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.

This commit is contained in:
Torsten Schulz (local)
2026-03-19 13:48:09 +01:00
parent 83110659db
commit bcb3b5b71f
3 changed files with 86 additions and 30 deletions

View File

@@ -2,7 +2,7 @@
<div class="chat-input-container">
<input
v-model="message"
type="text"
:type="inputType"
:placeholder="inputPlaceholder"
@keyup.enter="sendMessage"
/>
@@ -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 = '';
}

View File

@@ -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

View File

@@ -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 <username> - 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;
}