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