From bcb3b5b71ff32e2c704fd2e6250e2eee1e1bcde2 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 19 Mar 2026 13:48:09 +0100 Subject: [PATCH] Implement login functionality in ChatInput and chat store. Update input handling to support username and password prompts, enhance message sending logic, and ensure proper command handling during login. Adjust broadcast logic to manage login states and provide appropriate feedback to users. --- client/src/components/ChatInput.vue | 31 +++++++++++---- client/src/stores/chat.js | 27 ++++++++++++-- server/broadcast.js | 58 ++++++++++++++++++++--------- 3 files changed, 86 insertions(+), 30 deletions(-) diff --git a/client/src/components/ChatInput.vue b/client/src/components/ChatInput.vue index f606301..b450f95 100644 --- a/client/src/components/ChatInput.vue +++ b/client/src/components/ChatInput.vue @@ -2,7 +2,7 @@
@@ -47,11 +47,22 @@ const chatStore = useChatStore(); const message = ref(''); const showSmileys = ref(false); const hasConversation = computed(() => !!chatStore.currentConversation); -const inputPlaceholder = computed(() => - hasConversation.value +const isAwaitingUsername = computed(() => chatStore.awaitingLoginUsername); +const isAwaitingPassword = computed(() => chatStore.awaitingLoginPassword); + +const inputPlaceholder = computed(() => { + if (isAwaitingUsername.value) { + return 'Admin-Username eingeben'; + } + if (isAwaitingPassword.value) { + return 'Admin-Passwort eingeben'; + } + return hasConversation.value ? 'Nachricht senden oder /Befehl eingeben' - : 'Nur /Befehle eingeben (z.B. /login, /stat help)' -); + : 'Nur /Befehle eingeben (z.B. /login, /stat help)'; +}); + +const inputType = computed(() => (isAwaitingPassword.value ? 'password' : 'text')); // Smiley-Definitionen (wie im Original) const smileys = { @@ -81,7 +92,10 @@ function sendMessage() { const trimmed = message.value.trim(); if (!trimmed) return; const isCommand = trimmed.startsWith('/'); - if (!isCommand && !hasConversation.value) { + const canSendPlain = + hasConversation.value || isAwaitingUsername.value || isAwaitingPassword.value; + + if (!isCommand && !canSendPlain) { chatStore.errorMessage = 'Ohne aktive Konversation sind nur /Befehle erlaubt.'; setTimeout(() => { if (chatStore.errorMessage === 'Ohne aktive Konversation sind nur /Befehle erlaubt.') { @@ -90,8 +104,9 @@ function sendMessage() { }, 4000); return; } - - chatStore.sendMessage(chatStore.currentConversation, trimmed); + + const suppressLocal = isCommand || isAwaitingUsername.value || isAwaitingPassword.value; + chatStore.sendMessage(chatStore.currentConversation, trimmed, { suppressLocal }); message.value = ''; } diff --git a/client/src/stores/chat.js b/client/src/stores/chat.js index fa720be..36f4fb6 100644 --- a/client/src/stores/chat.js +++ b/client/src/stores/chat.js @@ -22,6 +22,8 @@ export const useChatStore = defineStore('chat', () => { const unreadChatsCount = ref(0); const errorMessage = ref(null); const remainingSecondsToTimeout = ref(1800); + const awaitingLoginUsername = ref(false); + const awaitingLoginPassword = ref(false); const searchData = ref({ nameIncludes: '', minAge: null, @@ -293,7 +295,20 @@ export const useChatStore = defineStore('chat', () => { break; case 'commandResult': { const lines = Array.isArray(data.lines) ? data.lines : []; - // Command output is global and must not pollute per-conversation history. + const kind = data.kind || 'info'; + + if (kind === 'loginPromptUsername') { + awaitingLoginUsername.value = true; + awaitingLoginPassword.value = false; + } else if (kind === 'loginPromptPassword') { + awaitingLoginUsername.value = false; + awaitingLoginPassword.value = true; + } else if (kind === 'loginSuccess' || kind === 'loginError' || kind === 'loginAbort' || kind === 'loginLogout') { + awaitingLoginUsername.value = false; + awaitingLoginPassword.value = false; + } + + // Command-Ausgaben immer global anzeigen, nicht im Chatverlauf. errorMessage.value = lines.join(' | '); setTimeout(() => { errorMessage.value = null; @@ -360,7 +375,7 @@ export const useChatStore = defineStore('chat', () => { }); } - function sendMessage(toUserName, message) { + function sendMessage(toUserName, message, options = {}) { if (!socket.value || !socket.value.connected) { console.error('Socket.IO nicht verbunden'); return; @@ -375,8 +390,10 @@ export const useChatStore = defineStore('chat', () => { messageId }); - // Lokal hinzufügen (außer bei Commands, die serverseitig beantwortet werden) - if (!isCommand) { + const suppressLocal = !!options.suppressLocal || isCommand || awaitingLoginUsername.value || awaitingLoginPassword.value; + + // Lokal hinzufügen (außer bei Commands oder sensiblen Eingaben wie Login) + if (!isCommand && !suppressLocal) { messages.value.push({ from: userName.value, message: trimmed, @@ -673,6 +690,8 @@ export const useChatStore = defineStore('chat', () => { remainingSecondsToTimeout, errorMessage, searchData, + awaitingLoginUsername, + awaitingLoginPassword, // Computed currentConversationWith, // Actions diff --git a/server/broadcast.js b/server/broadcast.js index a0b6847..5758e66 100644 --- a/server/broadcast.js +++ b/server/broadcast.js @@ -320,9 +320,9 @@ export function setupBroadcast(io, __dirname) { downloadCountries(); setInterval(downloadCountries, 24 * 60 * 60 * 1000); // Täglich aktualisieren - function sendCommandResult(socket, lines) { + function sendCommandResult(socket, lines, kind = 'info') { const payload = Array.isArray(lines) ? lines : [String(lines)]; - socket.emit('commandResult', { lines: payload }); + socket.emit('commandResult', { lines: payload, kind }); } function hasRight(client, right) { @@ -536,15 +536,21 @@ export function setupBroadcast(io, __dirname) { function executeCommand(socket, client, rawInput) { const input = rawInput.trim(); - if (client.pendingChatLogin) { + if (!input.startsWith('/')) return false; + + const parts = input.split(/\s+/); + const command = parts[0].toLowerCase(); + + // Laufender Login-Dialog: Nur Eingaben ohne Slash als Username/Passwort behandeln. + if (client.pendingChatLogin && !input.startsWith('/')) { if (client.pendingChatLogin.step === 'username') { const enteredUser = input; if (!enteredUser) { - sendCommandResult(socket, 'Username darf nicht leer sein. Bitte Username eingeben:'); + sendCommandResult(socket, 'Username darf nicht leer sein. Bitte Username eingeben:', 'loginPromptUsername'); return true; } client.pendingChatLogin = { step: 'password', username: enteredUser }; - sendCommandResult(socket, 'Passwort eingeben:'); + sendCommandResult(socket, 'Passwort eingeben:', 'loginPromptPassword'); return true; } @@ -553,32 +559,33 @@ export function setupBroadcast(io, __dirname) { const auth = verifyChatUser(username, input); client.pendingChatLogin = null; if (!auth) { - sendCommandResult(socket, 'Login fehlgeschlagen. Benutzername oder Passwort falsch.'); + sendCommandResult(socket, 'Login fehlgeschlagen. Benutzername oder Passwort falsch.', 'loginError'); return true; } client.chatAuth = auth; sendCommandResult( socket, - `Login erfolgreich als ${auth.username}. Rechte: ${Array.from(auth.rights).join(', ') || 'keine'}` + `Login erfolgreich als ${auth.username}. Rechte: ${Array.from(auth.rights).join(', ') || 'keine'}`, + 'loginSuccess' ); return true; } + } else if (client.pendingChatLogin && input.startsWith('/')) { + // Ein neuer /Befehl bricht den Login-Vorgang ab. + client.pendingChatLogin = null; + sendCommandResult(socket, 'Login-Vorgang abgebrochen.', 'loginAbort'); + // und läuft unten als normaler Befehl weiter } - if (!input.startsWith('/')) return false; - - const parts = input.split(/\s+/); - const command = parts[0].toLowerCase(); - if (command === '/login') { const username = (parts[1] || '').trim(); if (username) { client.pendingChatLogin = { step: 'password', username }; - sendCommandResult(socket, 'Passwort eingeben:'); + sendCommandResult(socket, 'Passwort eingeben:', 'loginPromptPassword'); } else { client.pendingChatLogin = { step: 'username', username: '' }; - sendCommandResult(socket, 'Username eingeben:'); + sendCommandResult(socket, 'Username eingeben:', 'loginPromptUsername'); } return true; } @@ -591,20 +598,35 @@ export function setupBroadcast(io, __dirname) { socket, wasLoggedIn ? 'Admin/Command-Login wurde abgemeldet.' - : 'Es war kein Admin/Command-Login aktiv.' + : 'Es war kein Admin/Command-Login aktiv.', + 'loginLogout' ); return true; } if (command === '/whoami-rights') { if (!client.chatAuth) { - sendCommandResult(socket, 'Nicht per Command-Login angemeldet.'); + sendCommandResult(socket, 'Nicht per Command-Login angemeldet.', 'whoami'); return true; } sendCommandResult(socket, [ `Angemeldet als: ${client.chatAuth.username}`, `Rechte: ${Array.from(client.chatAuth.rights).join(', ') || 'keine'}` - ]); + ], 'whoami'); + return true; + } + + if (command === '/help' || command === '/?') { + sendCommandResult(socket, [ + 'Verfügbare Befehle:', + '/login [username] - Admin-/Command-Login starten', + '/logout-admin - Admin-/Command-Login beenden', + '/whoami-rights - Aktuelle Admin-Rechte anzeigen', + '/stat help - Hilfe zu Statistikbefehlen', + '/all-stats - Zusammenfassung wichtiger Statistiken', + '/kick - Benutzer aus dem Chat werfen', + '/help oder /? - Diese Hilfe' + ], 'help'); return true; } @@ -623,7 +645,7 @@ export function setupBroadcast(io, __dirname) { return true; } - sendCommandResult(socket, `Unbekannter Befehl: ${command}`); + sendCommandResult(socket, `Unbekannter Befehl: ${command}`, 'unknown'); return true; }