diff --git a/apps/matrix/apps/web/package.json b/apps/matrix/apps/web/package.json index 1d852c2a3..85ef0874b 100644 --- a/apps/matrix/apps/web/package.json +++ b/apps/matrix/apps/web/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "matrix-js-sdk": "^37.1.0", + "@matrix-org/matrix-sdk-crypto-wasm": "^13.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "@manacore/shared-auth": "workspace:*", @@ -41,8 +42,5 @@ "lucide-svelte": "^0.509.0", "date-fns": "^4.1.0", "svelte-i18n": "^4.0.1" - }, - "overrides": { - "@matrix-org/matrix-sdk-crypto-nodejs": "npm:empty-npm-package@1.0.0" } } diff --git a/apps/matrix/apps/web/src/app.css b/apps/matrix/apps/web/src/app.css index c4976f67c..0d82a0a39 100644 --- a/apps/matrix/apps/web/src/app.css +++ b/apps/matrix/apps/web/src/app.css @@ -16,7 +16,7 @@ } body { - @apply bg-base-100 text-base-content; + @apply bg-background text-foreground; } } diff --git a/apps/matrix/apps/web/src/lib/components/chat/Message.svelte b/apps/matrix/apps/web/src/lib/components/chat/Message.svelte index e6a180563..14dcd92e5 100644 --- a/apps/matrix/apps/web/src/lib/components/chat/Message.svelte +++ b/apps/matrix/apps/web/src/lib/components/chat/Message.svelte @@ -12,17 +12,32 @@ FileIcon, Play, Image as ImageIcon, + Lock, + AlertTriangle, } from 'lucide-svelte'; interface Props { message: SimpleMessage; showAvatar?: boolean; showTimestamp?: boolean; + showEncryptionBadge?: boolean; onReply?: (message: SimpleMessage) => void; onEdit?: (message: SimpleMessage) => void; } - let { message, showAvatar = true, showTimestamp = false, onReply, onEdit }: Props = $props(); + let { + message, + showAvatar = true, + showTimestamp = false, + showEncryptionBadge = false, + onReply, + onEdit, + }: Props = $props(); + + // Check if message is a decryption error (body starts with "Unable to decrypt:") + let isDecryptionError = $derived( + message.body.startsWith('Unable to decrypt:') || message.body.includes('** Unable to decrypt') + ); let showActions = $state(false); let imageLoading = $state(true); @@ -78,15 +93,15 @@ {#if showTimestamp}
-
- {formattedDate()} -
+
+ {formattedDate()} +
{/if}
{#if showAvatar && !message.isOwn} -
-
- {initials} -
+
+ {initials}
{/if}
@@ -111,9 +126,12 @@ {message.isOwn ? 'Du' : message.senderName} - {formattedTime} + {formattedTime} {#if message.edited} - (bearbeitet) + (bearbeitet) + {/if} + {#if showEncryptionBadge} + {/if}
{/if} @@ -121,28 +139,36 @@ {#if message.replyTo && message.replyToBody}
- - {message.replyToBody} + + {message.replyToBody}
{/if}
{#if message.redacted} -

Nachricht wurde gelöscht

+

Nachricht wurde gelöscht

+ {:else if isDecryptionError} + +
+ + + Nachricht kann nicht entschlüsselt werden. Möglicherweise fehlen Schlüssel. + +
{:else if message.type === 'm.image' && thumbnailUrl}
{#if imageLoading} -
- +
+
{/if} {#if imageError} -
-

Bild konnte nicht geladen werden

+
+

Bild konnte nicht geladen werden

{:else}

{message.media?.filename || message.body}

-

+

{formatFileSize(message.media?.size)} {#if message.media?.mimetype} • {message.media.mimetype.split('/')[1]?.toUpperCase()} {/if}

- + {:else if message.type === 'm.emote'} -

* {message.senderName} {message.body}

+

* {message.senderName} {message.body}

{:else if message.type === 'm.notice'} -

{message.body}

+

{message.body}

{:else}

{message.body}

{/if} {#if !showAvatar} -
+ + +
+
+ Geräte-ID + {deviceId || 'Unbekannt'} +
+
+ + +
+
+ +
+

+ {keyBackupEnabled ? 'Schlüssel-Backup aktiv' : 'Kein Schlüssel-Backup'} +

+

+ {keyBackupEnabled + ? 'Deine Nachrichten werden gesichert' + : 'Richte ein Backup ein, um Nachrichten wiederherzustellen'} +

+
+
+ {#if keyBackupEnabled} + + {:else} + + {/if} +
+
+ {/if} +
+ + + +
+
+

+ + Erscheinungsbild +

+

Theme-Einstellungen folgen bald...

-
-
-

+
+
+

- Notifications + Benachrichtigungen

-

Notification settings coming soon...

-
-
- - -
-
-

- - Security -

-

- End-to-end encryption settings coming in Phase 2... -

+

Benachrichtigungseinstellungen folgen bald...

-
-
- -
+
+

+ + + (verificationDialogOpen = false)} +/> + (recoveryDialogOpen = false)} +/> diff --git a/apps/matrix/apps/web/vite.config.ts b/apps/matrix/apps/web/vite.config.ts index 857f7aa9a..3a18c889b 100644 --- a/apps/matrix/apps/web/vite.config.ts +++ b/apps/matrix/apps/web/vite.config.ts @@ -7,12 +7,19 @@ export default defineConfig({ server: { port: 5180, strictPort: true, + headers: { + // Required for WASM module loading + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Embedder-Policy': 'require-corp', + }, }, define: { global: 'globalThis', }, optimizeDeps: { include: ['buffer', 'events'], + // WASM modules cannot be pre-bundled + exclude: ['@matrix-org/matrix-sdk-crypto-wasm'], esbuildOptions: { define: { global: 'globalThis', @@ -22,4 +29,10 @@ export default defineConfig({ ssr: { noExternal: ['@manacore/shared-*', '@matrix/shared'], }, + worker: { + format: 'es', + }, + build: { + target: 'esnext', + }, }); diff --git a/packages/bot-services/src/ai/ai.service.ts b/packages/bot-services/src/ai/ai.service.ts index 676483a5d..224c880b8 100644 --- a/packages/bot-services/src/ai/ai.service.ts +++ b/packages/bot-services/src/ai/ai.service.ts @@ -9,6 +9,9 @@ import { SYSTEM_PROMPTS, VISION_MODELS, NON_CHAT_MODELS, + OllamaVersionResponse, + OllamaTagsResponse, + OllamaChatResponse, } from './types'; @Injectable() @@ -36,7 +39,7 @@ export class AiService implements OnModuleInit { const response = await fetch(`${this.config.baseUrl}/api/version`, { signal: AbortSignal.timeout(5000), }); - const data = await response.json(); + const data = (await response.json()) as OllamaVersionResponse; this.logger.log(`Ollama connected: v${data.version}`); return true; } catch (error) { @@ -50,7 +53,7 @@ export class AiService implements OnModuleInit { async listModels(): Promise { try { const response = await fetch(`${this.config.baseUrl}/api/tags`); - const data = await response.json(); + const data = (await response.json()) as OllamaTagsResponse; return data.models || []; } catch (error) { this.logger.error('Failed to list models:', error); @@ -97,18 +100,22 @@ export class AiService implements OnModuleInit { throw new Error(`Ollama API error: ${response.status}`); } - const data = await response.json(); + const data = (await response.json()) as OllamaChatResponse; const meta = { model, evalCount: data.eval_count, evalDuration: data.eval_duration, tokensPerSecond: - data.eval_count && data.eval_duration ? (data.eval_count / data.eval_duration) * 1e9 : undefined, + data.eval_count && data.eval_duration + ? (data.eval_count / data.eval_duration) * 1e9 + : undefined, }; if (meta.tokensPerSecond) { - this.logger.debug(`Generated ${meta.evalCount} tokens at ${meta.tokensPerSecond.toFixed(1)} t/s`); + this.logger.debug( + `Generated ${meta.evalCount} tokens at ${meta.tokensPerSecond.toFixed(1)} t/s` + ); } return { @@ -140,7 +147,10 @@ export class AiService implements OnModuleInit { ...session.history, ]; - const result = await this.chat(messages, { ...options, model: options?.model ?? session.model }); + const result = await this.chat(messages, { + ...options, + model: options?.model ?? session.model, + }); // Add assistant response to history session.history.push({ role: 'assistant', content: result.content }); @@ -175,14 +185,16 @@ export class AiService implements OnModuleInit { throw new Error(`Ollama API error: ${response.status}`); } - const data = await response.json(); + const data = (await response.json()) as OllamaChatResponse; const meta = { model: selectedModel, evalCount: data.eval_count, evalDuration: data.eval_duration, tokensPerSecond: - data.eval_count && data.eval_duration ? (data.eval_count / data.eval_duration) * 1e9 : undefined, + data.eval_count && data.eval_duration + ? (data.eval_count / data.eval_duration) * 1e9 + : undefined, }; return { diff --git a/packages/bot-services/src/ai/types.ts b/packages/bot-services/src/ai/types.ts index 67befc0aa..8b720eb08 100644 --- a/packages/bot-services/src/ai/types.ts +++ b/packages/bot-services/src/ai/types.ts @@ -122,6 +122,30 @@ Passe deinen Stil an die gewünschte Textart an.`, */ export const VISION_MODELS = ['llava', 'llava:7b', 'llava:13b', 'bakllava', 'moondream']; +/** + * Ollama API response types + */ +export interface OllamaVersionResponse { + version: string; +} + +export interface OllamaTagsResponse { + models: OllamaModel[]; +} + +export interface OllamaChatResponse { + model: string; + message?: { + role: string; + content: string; + }; + eval_count?: number; + eval_duration?: number; + total_duration?: number; + load_duration?: number; + prompt_eval_count?: number; +} + /** * Models excluded from comparison (specialized, not for general chat) */ diff --git a/packages/bot-services/src/clock/clock.service.ts b/packages/bot-services/src/clock/clock.service.ts index a52b24a70..f92d163ab 100644 --- a/packages/bot-services/src/clock/clock.service.ts +++ b/packages/bot-services/src/clock/clock.service.ts @@ -36,7 +36,12 @@ export class ClockService { // ===== API Helper ===== - private async apiCall(endpoint: string, method: string = 'GET', token?: string, body?: unknown): Promise { + private async apiCall( + endpoint: string, + method = 'GET', + token?: string, + body?: unknown + ): Promise { const headers: Record = { 'Content-Type': 'application/json', }; @@ -56,7 +61,7 @@ export class ClockService { throw new Error(`Clock API error: ${response.status} - ${errorText}`); } - return response.json(); + return response.json() as Promise; } // ===== Health ===== @@ -167,7 +172,10 @@ export class ClockService { return finishedAt.toDateString() === today.toDateString(); }); - const totalMinutes = finishedToday.reduce((sum, t) => sum + Math.floor(t.durationSeconds / 60), 0); + const totalMinutes = finishedToday.reduce( + (sum, t) => sum + Math.floor(t.durationSeconds / 60), + 0 + ); return { totalMinutes, diff --git a/services/mana-search/docker-compose.dev.yml b/services/mana-search/docker-compose.dev.yml index e06a32dc6..efbd90549 100644 --- a/services/mana-search/docker-compose.dev.yml +++ b/services/mana-search/docker-compose.dev.yml @@ -13,8 +13,8 @@ services: ports: - "8080:8080" # Exposed for development volumes: - - ./searxng/settings.yml:/etc/searxng/settings.yml:ro - - ./searxng/limiter.toml:/etc/searxng/limiter.toml:ro + - ./searxng/settings.yml:/etc/searxng/settings.yml + - ./searxng/limiter.toml:/etc/searxng/limiter.toml environment: SEARXNG_BASE_URL: http://localhost:8080 SEARXNG_SECRET: dev-secret-change-in-production diff --git a/services/mana-search/searxng/limiter.toml b/services/mana-search/searxng/limiter.toml index 5e69c9f3e..90e88b747 100644 --- a/services/mana-search/searxng/limiter.toml +++ b/services/mana-search/searxng/limiter.toml @@ -2,14 +2,8 @@ # Documentation: https://docs.searxng.org/admin/settings/limiter.html [botdetection.ip_limit] -# Enable link token for bot detection -link_token = true - -# Maximum searches per minute per IP -limit = 60 - -# Burst limit (requests before rate limiting kicks in) -burst = 20 +# Disable link token for API usage +link_token = false [botdetection.ip_lists] # Allow internal Docker network IPs (no rate limiting for internal services) @@ -22,6 +16,3 @@ pass_ip = [ "127.0.0.1", "::1", ] - -# Block known bad actors (add IPs as needed) -block_ip = []