- Updated index.html with improved meta tags for SEO, including author and theme color. - Added a feedback dialog in ImprintContainer.vue for user feedback submission. - Refactored LoginForm.vue to utilize a utility for cookie management, simplifying profile persistence. - Introduced new routes and schemas for feedback in the router and server, enhancing SEO and user experience. - Improved ChatView.vue with better error handling and command table display. - Implemented feedback API endpoints in server routes for managing user feedback submissions and admin access. These changes collectively improve the application's SEO, user interaction, and feedback management capabilities.
74 lines
2.2 KiB
JavaScript
74 lines
2.2 KiB
JavaScript
import crypto from 'crypto';
|
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
import { join } from 'path';
|
|
|
|
const CHAT_USERS_FILE_NAME = 'chat-users.json';
|
|
|
|
function ensureLogsDir(baseDir) {
|
|
const logsDir = join(baseDir, '../logs');
|
|
if (!existsSync(logsDir)) {
|
|
mkdirSync(logsDir, { recursive: true });
|
|
}
|
|
return logsDir;
|
|
}
|
|
|
|
function getChatUsersPath(baseDir) {
|
|
return join(ensureLogsDir(baseDir), CHAT_USERS_FILE_NAME);
|
|
}
|
|
|
|
function sha256(value) {
|
|
return crypto.createHash('sha256').update(value).digest('hex');
|
|
}
|
|
|
|
export function ensureChatUsersFile(baseDir) {
|
|
const usersPath = getChatUsersPath(baseDir);
|
|
if (existsSync(usersPath)) return;
|
|
writeFileSync(usersPath, '[]\n', 'utf-8');
|
|
console.warn(
|
|
`[Auth] ${CHAT_USERS_FILE_NAME} wurde neu erstellt. Bitte mindestens einen Admin-User mit Passwort-Hash konfigurieren.`
|
|
);
|
|
}
|
|
|
|
export function loadChatUsers(baseDir) {
|
|
ensureChatUsersFile(baseDir);
|
|
const usersPath = getChatUsersPath(baseDir);
|
|
const raw = readFileSync(usersPath, 'utf-8').trim();
|
|
if (!raw) return [];
|
|
|
|
let users = [];
|
|
try {
|
|
users = JSON.parse(raw);
|
|
} catch (error) {
|
|
throw new Error(`Ungültige ${CHAT_USERS_FILE_NAME}: ${error.message}`);
|
|
}
|
|
|
|
if (!Array.isArray(users)) {
|
|
throw new Error(`${CHAT_USERS_FILE_NAME} muss ein Array sein`);
|
|
}
|
|
|
|
return users
|
|
.filter((entry) => entry && typeof entry.username === 'string')
|
|
.map((entry) => ({
|
|
username: entry.username.trim(),
|
|
passwordHash: typeof entry.passwordHash === 'string' ? entry.passwordHash.trim() : '',
|
|
rights: Array.isArray(entry.rights) ? entry.rights.map((r) => String(r).toLowerCase()) : []
|
|
}))
|
|
.filter((entry) => entry.username && entry.passwordHash);
|
|
}
|
|
|
|
export function verifyChatUser(baseDir, username, password) {
|
|
if (!username || !password) return null;
|
|
const normalizedUser = String(username).trim();
|
|
const passwordHash = sha256(password);
|
|
const user = loadChatUsers(baseDir).find(
|
|
(entry) => entry.username.toLowerCase() === normalizedUser.toLowerCase() && entry.passwordHash === passwordHash
|
|
);
|
|
|
|
if (!user) return null;
|
|
|
|
return {
|
|
username: user.username,
|
|
rights: new Set(user.rights)
|
|
};
|
|
}
|