🐛 fix(matrix-web): handle Matrix SSO loginToken callback

Add loginWithLoginToken function to exchange Matrix SSO loginToken for credentials.
The app layout now detects the loginToken URL parameter and completes the SSO flow.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2026-02-01 15:02:47 +01:00
parent 9e9db590dc
commit dc0d425f61
53 changed files with 1550 additions and 230 deletions

View file

@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
import { MatrixService } from './matrix.service';
import { AnalyticsModule } from '../analytics/analytics.module';
import { UsersModule } from '../users/users.module';
import { TranscriptionModule } from '@manacore/bot-services';
import { TranscriptionModule, SessionModule, CreditModule } from '@manacore/bot-services';
@Module({
imports: [
@ -11,6 +11,8 @@ import { TranscriptionModule } from '@manacore/bot-services';
TranscriptionModule.register({
sttUrl: process.env.STT_URL || 'http://localhost:3020',
}),
SessionModule.forRoot(),
CreditModule.forRoot(),
],
providers: [MatrixService],
exports: [MatrixService],

View file

@ -9,7 +9,7 @@ import {
} from '@manacore/matrix-bot-common';
import { AnalyticsService } from '../analytics/analytics.service';
import { UsersService } from '../users/users.service';
import { TranscriptionService } from '@manacore/bot-services';
import { TranscriptionService, SessionService, CreditService } from '@manacore/bot-services';
@Injectable()
export class MatrixService extends BaseMatrixService {
@ -28,7 +28,9 @@ export class MatrixService extends BaseMatrixService {
configService: ConfigService,
private analyticsService: AnalyticsService,
private usersService: UsersService,
private readonly transcriptionService: TranscriptionService
private readonly transcriptionService: TranscriptionService,
private sessionService: SessionService,
private creditService: CreditService
) {
super(configService);
this.reportRoomId = this.configService.get<string>('matrix.reportRoomId') || '';
@ -48,7 +50,7 @@ export class MatrixService extends BaseMatrixService {
roomId: string,
_event: MatrixRoomEvent,
message: string,
_sender: string
sender: string
): Promise<void> {
// Check for keyword commands first
const keywordCommand = this.keywordDetector.detect(message);
@ -58,8 +60,8 @@ export class MatrixService extends BaseMatrixService {
if (!message.startsWith('!')) return;
const [command] = message.slice(1).split(' ');
await this.handleCommand(roomId, command.toLowerCase());
const [command, ...args] = message.slice(1).split(' ');
await this.handleCommand(roomId, command.toLowerCase(), sender, args.join(' '));
}
protected override async handleAudioMessage(
@ -86,13 +88,25 @@ export class MatrixService extends BaseMatrixService {
}
}
private async handleCommand(roomId: string, command: string) {
private async handleCommand(roomId: string, command: string, sender: string, args: string) {
switch (command) {
case 'help':
case 'start':
await this.sendHelp(roomId);
break;
case 'login':
await this.handleLogin(roomId, sender, args);
break;
case 'logout':
await this.handleLogout(roomId, sender);
break;
case 'status':
await this.handleStatus(roomId, sender);
break;
case 'stats':
await this.sendStats(roomId);
break;
@ -121,7 +135,12 @@ export class MatrixService extends BaseMatrixService {
private async sendHelp(roomId: string) {
const helpText = `**📊 ManaCore Stats Bot (DSGVO-konform)**
**Befehle:**
**Account:**
- \`!login email passwort\` - Anmelden
- \`!logout\` - Abmelden
- \`!status\` - Account Status
**Statistiken:**
- \`!stats\` - Übersicht aller Apps (30 Tage)
- \`!today\` - Heutige Statistiken
- \`!week\` - Wochenstatistiken
@ -177,6 +196,53 @@ Daten von Umami Analytics (self-hosted).`;
await this.sendMessage(roomId, report);
}
private async handleLogin(roomId: string, sender: string, args: string) {
const parts = args.split(' ');
if (parts.length < 2 || !parts[0] || !parts[1]) {
await this.sendMessage(roomId, 'Verwendung: `!login email passwort`');
return;
}
const [email, password] = parts;
const result = await this.sessionService.login(sender, email, password);
if (result.success) {
const token = this.sessionService.getToken(sender);
if (token) {
const balance = await this.creditService.getBalance(token);
await this.sendMessage(roomId,
`✅ Erfolgreich angemeldet als **${email}**\n⚡ Credits: ${balance.balance.toFixed(2)}`);
} else {
await this.sendMessage(roomId, `✅ Erfolgreich angemeldet als **${email}**`);
}
} else {
await this.sendMessage(roomId, `❌ Anmeldung fehlgeschlagen: ${result.error}`);
}
}
private async handleLogout(roomId: string, sender: string) {
this.sessionService.logout(sender);
await this.sendMessage(roomId, '👋 Erfolgreich abgemeldet.');
}
private async handleStatus(roomId: string, sender: string) {
const loggedIn = this.sessionService.isLoggedIn(sender);
const session = this.sessionService.getSession(sender);
const token = this.sessionService.getToken(sender);
let response = '**📊 Stats Bot Status**\n\n';
if (loggedIn && session && token) {
const balance = await this.creditService.getBalance(token);
response += `👤 Angemeldet als: ${session.email}\n`;
response += `⚡ Credits: ${balance.balance.toFixed(2)}\n`;
} else {
response += `❌ Nicht angemeldet\n`;
response += `Nutze \`!login email passwort\` zum Anmelden.`;
}
await this.sendMessage(roomId, response);
}
// Public method for scheduled reports
async sendScheduledReport(report: string) {
if (!this.reportRoomId) {