Add chat nick to admin users

This commit is contained in:
rajchales-monito
2026-07-29 22:33:22 +02:00
parent cac3deb11a
commit 16e44c5f79
4 changed files with 94 additions and 24 deletions
+25 -14
View File
@@ -128,6 +128,7 @@ function initDb() {
company_id INTEGER NOT NULL REFERENCES companies(id),
username TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL,
chat_nick TEXT,
password_hash TEXT,
is_system INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
@@ -237,6 +238,7 @@ function initDb() {
ensureColumn("messages", "translation_status", "TEXT");
ensureColumn("messages", "translation_error", "TEXT");
ensureColumn("admin_users", "password_hash", "TEXT");
ensureColumn("admin_users", "chat_nick", "TEXT");
ensureColumn("admin_users", "is_system", "INTEGER NOT NULL DEFAULT 0");
ensureColumn("admin_users", "updated_at", "TEXT");
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_messages_client_id ON messages(conversation_id, client_message_id) WHERE client_message_id IS NOT NULL");
@@ -272,13 +274,14 @@ function seedDefaults() {
for (const user of configuredUsers()) {
db.prepare(`
INSERT INTO admin_users (company_id, username, display_name, is_system, updated_at)
VALUES (?, ?, ?, 1, CURRENT_TIMESTAMP)
INSERT INTO admin_users (company_id, username, display_name, chat_nick, is_system, updated_at)
VALUES (?, ?, ?, ?, 1, CURRENT_TIMESTAMP)
ON CONFLICT(username) DO UPDATE SET
display_name = excluded.display_name,
chat_nick = excluded.chat_nick,
is_system = 1,
updated_at = CURRENT_TIMESTAMP
`).run(companyId, user.username, user.displayName || user.username);
`).run(companyId, user.username, user.displayName || user.username, user.chatNick || user.displayName || user.username);
}
}
@@ -326,17 +329,18 @@ function configuredUsers() {
const username = process.env[`USER_${i}`];
const password = process.env[`PASS_${i}`];
const displayName = process.env[`NAME_${i}`] || username;
const chatNick = process.env[`NICK_${i}`] || displayName;
if (!username && !password) break;
if (username && password) users.push({ username, password, displayName });
if (username && password) users.push({ username, password, displayName, chatNick });
}
return users;
}
function adminProfile(username) {
const configured = configuredUsers().find((user) => user.username === username);
if (configured) return { username: configured.username, displayName: configured.displayName };
const row = db.prepare("SELECT username, display_name FROM admin_users WHERE username = ?").get(username);
return row ? { username: row.username, displayName: row.display_name } : { username, displayName: username };
if (configured) return { username: configured.username, displayName: configured.displayName, chatNick: configured.chatNick };
const row = db.prepare("SELECT username, display_name, chat_nick FROM admin_users WHERE username = ?").get(username);
return row ? { username: row.username, displayName: row.display_name, chatNick: row.chat_nick || row.display_name } : { username, displayName: username, chatNick: username };
}
async function adminLogin(req, res) {
@@ -346,12 +350,12 @@ async function adminLogin(req, res) {
const configured = configuredUsers().find((u) => u.username === username && u.password === password);
const dbUser = configured ? null : db.prepare("SELECT * FROM admin_users WHERE username = ? AND password_hash IS NOT NULL").get(username);
const match = configured || (dbUser && verifyPassword(password, dbUser.password_hash)
? { username: dbUser.username, displayName: dbUser.display_name }
? { username: dbUser.username, displayName: dbUser.display_name, chatNick: dbUser.chat_nick || dbUser.display_name }
: null);
if (!match) return json(res, 401, { error: "bad_credentials" });
const token = signSession(match.username);
res.setHeader("Set-Cookie", `${COOKIE_NAME}=${token}; HttpOnly; SameSite=Lax; Path=/; Max-Age=${60 * 60 * 24 * 14}`);
return json(res, 200, { ok: true, user: { username: match.username, displayName: match.displayName } });
return json(res, 200, { ok: true, user: { username: match.username, displayName: match.displayName, chatNick: match.chatNick } });
}
function adminLogout(res) {
@@ -485,14 +489,15 @@ async function createAdminUser(req, res) {
const companyId = ensureCompany();
const username = normalizeAdminUsername(body.username);
const displayName = normalizeDisplayName(body.displayName) || username;
const chatNick = normalizeDisplayName(body.chatNick) || displayName;
const password = String(body.password || "");
if (!username) return json(res, 400, { error: "bad_username" });
if (!validAdminPassword(password)) return json(res, 400, { error: "bad_password" });
try {
db.prepare(`
INSERT INTO admin_users (company_id, username, display_name, password_hash, is_system, updated_at)
VALUES (?, ?, ?, ?, 0, CURRENT_TIMESTAMP)
`).run(companyId, username, displayName, hashPassword(password));
INSERT INTO admin_users (company_id, username, display_name, chat_nick, password_hash, is_system, updated_at)
VALUES (?, ?, ?, ?, ?, 0, CURRENT_TIMESTAMP)
`).run(companyId, username, displayName, chatNick, hashPassword(password));
return json(res, 201, { users: listAdminUsers() });
} catch (error) {
if (String(error.message || "").includes("UNIQUE")) return json(res, 409, { error: "username_exists" });
@@ -507,11 +512,16 @@ async function updateAdminUser(req, res, url) {
if (!user) return json(res, 404, { error: "user_not_found" });
const displayName = normalizeDisplayName(body.displayName);
const chatNick = normalizeDisplayName(body.chatNick);
const password = String(body.password || "");
if (displayName) {
if (user.is_system) return json(res, 400, { error: "system_user_profile_env" });
db.prepare("UPDATE admin_users SET display_name = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?").run(displayName, id);
}
if (chatNick) {
if (user.is_system) return json(res, 400, { error: "system_user_profile_env" });
db.prepare("UPDATE admin_users SET chat_nick = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?").run(chatNick, id);
}
if (password) {
if (user.is_system) return json(res, 400, { error: "system_user_password_env" });
if (!validAdminPassword(password)) return json(res, 400, { error: "bad_password" });
@@ -532,13 +542,14 @@ function deleteAdminUser(req, res, url) {
function listAdminUsers() {
return db.prepare(`
SELECT id, username, display_name, password_hash IS NOT NULL AS has_password, is_system, created_at, updated_at
SELECT id, username, display_name, chat_nick, password_hash IS NOT NULL AS has_password, is_system, created_at, updated_at
FROM admin_users
ORDER BY username COLLATE NOCASE ASC
`).all().map((user) => ({
id: user.id,
username: user.username,
displayName: user.display_name,
chatNick: user.chat_nick || user.display_name,
hasPassword: Boolean(user.has_password || user.is_system),
isSystem: Boolean(user.is_system),
createdAt: user.created_at,
@@ -774,7 +785,7 @@ async function createAdminMessage(req, res, url) {
if (!attachmentsAreAllowed(body.attachments || [])) return json(res, 400, { error: "only_images_allowed" });
const conversation = findConversation(publicIdFrom(url));
if (!conversation) return json(res, 404, { error: "conversation_not_found" });
const messageId = insertMessage(conversation.id, "admin", req.adminUser.displayName || req.adminUser.username, String(body.message || "").trim());
const messageId = insertMessage(conversation.id, "admin", req.adminUser.chatNick || req.adminUser.displayName || req.adminUser.username, String(body.message || "").trim());
saveAttachments(conversation.id, messageId, body.attachments || []);
await translateStoredMessage(conversation.id, messageId, "admin", body);
bumpConversation(conversation.id, conversation.status === "new" ? "open" : conversation.status);