From 7ab206fe96fe98bfc91e99fed1fd475fe0ce0046 Mon Sep 17 00:00:00 2001 From: AlacrisDevs Date: Sat, 14 Feb 2026 13:09:45 +0200 Subject: [PATCH] MEga push vol idk, chat function updates, docker fixes --- .dockerignore | 3 +- .env.example | 9 +- Dockerfile | 38 +- docker-compose.yml | 26 +- .../components/chat-layout/ChatArea.svelte | 2 - src/lib/components/matrix/MemberList.svelte | 6 +- .../matrix/MentionAutocomplete.svelte | 35 +- src/lib/components/matrix/MessageInput.svelte | 300 ++---- src/lib/components/matrix/MessageList.svelte | 300 +----- .../components/matrix/RoomInfoPanel.svelte | 216 +++-- .../matrix/RoomSettingsModal.svelte | 8 +- src/lib/components/matrix/StartDMModal.svelte | 67 +- .../components/matrix/TypingIndicator.svelte | 25 +- .../components/matrix/UserProfileModal.svelte | 106 ++- .../message/MessageContainer.svelte | 68 +- .../message/parts/MessageActions.svelte | 165 ++-- .../message/parts/MessageReactions.svelte | 2 +- .../message/parts/MessageReadReceipts.svelte | 2 +- .../settings/SettingsGeneral.svelte | 18 +- src/lib/components/ui/Avatar.svelte | 11 +- src/lib/components/ui/EmojiPicker.svelte | 55 +- src/lib/components/ui/MatrixAvatar.svelte | 26 + src/lib/components/ui/Twemoji.svelte | 7 +- src/lib/components/ui/index.ts | 1 + src/lib/matrix/client.ts | 8 +- src/lib/matrix/sync.ts | 2 +- src/lib/stores/avatarCache.ts | 105 +++ src/lib/stores/matrix.ts | 17 +- src/lib/utils/twemoji.ts | 64 +- src/routes/[orgSlug]/+layout.svelte | 2 +- src/routes/[orgSlug]/chat/+page.svelte | 857 +++++++++--------- .../058_fix_handle_new_org_trigger.sql | 19 + synapse/data/homeserver.db | Bin 2011136 -> 2125824 bytes synapse/data/homeserver.db-shm | Bin 32768 -> 32768 bytes synapse/data/homeserver.db-wal | Bin 1260752 -> 53592 bytes 35 files changed, 1226 insertions(+), 1344 deletions(-) create mode 100644 src/lib/components/ui/MatrixAvatar.svelte create mode 100644 src/lib/stores/avatarCache.ts create mode 100644 supabase/migrations/058_fix_handle_new_org_trigger.sql diff --git a/.dockerignore b/.dockerignore index eadde3a..a5cc8bb 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,6 +11,7 @@ README.md node_modules build **/.env -**/.env.* +**/.env.local +**/.env.*.local *.log .DS_Store diff --git a/.env.example b/.env.example index 9227b0d..9758dc6 100644 --- a/.env.example +++ b/.env.example @@ -1,18 +1,25 @@ +# ── Supabase ── PUBLIC_SUPABASE_URL=your_supabase_url PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key # Service role key — required for admin operations (invite emails, etc.) # Find it in Supabase Dashboard → Settings → API → service_role key SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key +# ── Google ── GOOGLE_API_KEY=your_google_api_key # Google Service Account for Calendar push (create/update/delete events) # Paste the full JSON key file contents, or base64-encode it # The calendar must be shared with the service account email (with "Make changes to events" permission) GOOGLE_SERVICE_ACCOUNT_KEY= -# Matrix / Synapse integration +# ── Matrix / Synapse (optional — chat is not yet enabled) ── # The homeserver URL where your Synapse instance is running MATRIX_HOMESERVER_URL=https://matrix.example.com # Synapse Admin API shared secret or admin access token # Used to auto-provision Matrix accounts for users MATRIX_ADMIN_TOKEN= + +# ── Docker / Production ── +# Public URL of the app — required by SvelteKit node adapter for CSRF protection +# Set this to your actual domain in production (e.g. https://app.example.com) +ORIGIN=http://localhost:3000 diff --git a/Dockerfile b/Dockerfile index e559c0a..07c25c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,43 +1,59 @@ -# Build stage +# ── Build stage ── FROM node:22-alpine AS builder WORKDIR /app -# Copy package files +# Build args needed by $env/static/public at build time +ARG PUBLIC_SUPABASE_URL +ARG PUBLIC_SUPABASE_ANON_KEY + +# Copy package files first for better layer caching COPY package*.json ./ -# Install all dependencies (including dev) +# Install all dependencies (including dev) needed for the build RUN npm ci # Copy source files COPY . . -# Build the application +# Build the SvelteKit application RUN npm run build -# Prune dev dependencies -RUN npm prune --production +# ── Production dependencies stage ── +FROM node:22-alpine AS deps -# Production stage +WORKDIR /app + +COPY package*.json ./ + +# Install only production dependencies +RUN npm ci --omit=dev + +# ── Runtime stage ── FROM node:22-alpine WORKDIR /app -# Copy built application +# Copy built application and production deps COPY --from=builder /app/build build/ -COPY --from=builder /app/node_modules node_modules/ +COPY --from=deps /app/node_modules node_modules/ COPY package.json . # Expose port EXPOSE 3000 -# Set environment +# Set environment defaults ENV NODE_ENV=production ENV PORT=3000 ENV HOST=0.0.0.0 +# SvelteKit node adapter needs ORIGIN for CSRF protection +# Override at runtime via docker-compose or -e flag +ENV ORIGIN=http://localhost:3000 +# Allow file uploads up to 10 MB +ENV BODY_SIZE_LIMIT=10485760 # Health check -HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ +HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1 # Run the application diff --git a/docker-compose.yml b/docker-compose.yml index fcd0c2a..78e8025 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,31 +3,26 @@ services: build: context: . dockerfile: Dockerfile + args: + - PUBLIC_SUPABASE_URL=${PUBLIC_SUPABASE_URL} + - PUBLIC_SUPABASE_ANON_KEY=${PUBLIC_SUPABASE_ANON_KEY} ports: - "3000:3000" + env_file: .env environment: - NODE_ENV=production - PORT=3000 - HOST=0.0.0.0 - # Supabase - - PUBLIC_SUPABASE_URL=${PUBLIC_SUPABASE_URL} - - PUBLIC_SUPABASE_ANON_KEY=${PUBLIC_SUPABASE_ANON_KEY} - # Google - - GOOGLE_API_KEY=${GOOGLE_API_KEY} - - GOOGLE_SERVICE_ACCOUNT_KEY=${GOOGLE_SERVICE_ACCOUNT_KEY} - # Matrix - - MATRIX_HOMESERVER_URL=${MATRIX_HOMESERVER_URL} - - MATRIX_ADMIN_TOKEN=${MATRIX_ADMIN_TOKEN} - # Email (Resend) - - RESEND_API_KEY=${RESEND_API_KEY} - - RESEND_FROM_EMAIL=${RESEND_FROM_EMAIL} + # SvelteKit node adapter CSRF — set to your public URL in production + - ORIGIN=${ORIGIN:-http://localhost:3000} + - BODY_SIZE_LIMIT=10485760 restart: unless-stopped healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"] + test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health" ] interval: 30s timeout: 3s retries: 3 - start_period: 5s + start_period: 10s # Development mode with hot reload dev: @@ -39,8 +34,7 @@ services: volumes: - .:/app - /app/node_modules + env_file: .env environment: - NODE_ENV=development - - PUBLIC_SUPABASE_URL=${PUBLIC_SUPABASE_URL} - - PUBLIC_SUPABASE_ANON_KEY=${PUBLIC_SUPABASE_ANON_KEY} command: npm run dev -- --host diff --git a/src/lib/components/chat-layout/ChatArea.svelte b/src/lib/components/chat-layout/ChatArea.svelte index bef8966..0561557 100644 --- a/src/lib/components/chat-layout/ChatArea.svelte +++ b/src/lib/components/chat-layout/ChatArea.svelte @@ -268,8 +268,6 @@ {onEdit} {onDelete} {onReply} - {onLoadMore} - isLoading={isLoadingMore} /> - import { Avatar } from "$lib/components/ui"; + import { MatrixAvatar } from "$lib/components/ui"; import UserProfileModal from "./UserProfileModal.svelte"; import type { RoomMember } from "$lib/matrix/types"; import { userPresence } from "$lib/stores/matrix"; @@ -66,8 +66,8 @@ class="w-full flex items-center gap-3 px-4 py-2 hover:bg-light/5 transition-colors text-left" onclick={() => handleMemberClick(member)} > - - import { Avatar } from '$lib/components/ui'; - import type { RoomMember } from '$lib/matrix/types'; + import { MatrixAvatar } from "$lib/components/ui"; + import type { RoomMember } from "$lib/matrix/types"; interface Props { members: RoomMember[]; @@ -23,11 +23,12 @@ // Filter members based on query const filteredMembers = $derived( members - .filter(m => - m.name.toLowerCase().includes(query.toLowerCase()) || - m.userId.toLowerCase().includes(query.toLowerCase()) + .filter( + (m) => + m.name.toLowerCase().includes(query.toLowerCase()) || + m.userId.toLowerCase().includes(query.toLowerCase()), ) - .slice(0, 8) + .slice(0, 8), ); // Reset selection when query changes @@ -40,22 +41,23 @@ if (filteredMembers.length === 0) return; switch (e.key) { - case 'ArrowDown': + case "ArrowDown": e.preventDefault(); selectedIndex = (selectedIndex + 1) % filteredMembers.length; break; - case 'ArrowUp': + case "ArrowUp": e.preventDefault(); - selectedIndex = (selectedIndex - 1 + filteredMembers.length) % filteredMembers.length; + selectedIndex = + (selectedIndex - 1 + filteredMembers.length) % filteredMembers.length; break; - case 'Enter': - case 'Tab': + case "Enter": + case "Tab": e.preventDefault(); if (filteredMembers[selectedIndex]) { onSelect(filteredMembers[selectedIndex]); } break; - case 'Escape': + case "Escape": e.preventDefault(); onClose(); break; @@ -76,11 +78,14 @@ {#each filteredMembers as member, i} @@ -556,35 +469,47 @@ {#if replyTo && !editingMessage}
+ reply
-

+

Replying to {replyTo.senderName}

-

{replyTo.content}

+

{replyTo.content}

{/if} -
+ + {#if showEmojiPicker} +
+
+ { + message += emoji; + inputRef?.focus(); + }} + onClose={() => (showEmojiPicker = false)} + /> +
+
+ {/if} + +
@@ -656,13 +561,13 @@ /> {/if} - +
- + {#if message && hasEmoji(message)} - - - {#if showEmojiPicker} -
- { - message += emoji; - inputRef?.focus(); - }} - onClose={() => (showEmojiPicker = false)} - position={{ x: 0, y: 0 }} - /> -
- {/if}
- + {#if message.length > 1000} -
- {message.length} / 4000 +
+ + {message.length} / 4000 +
{/if}
diff --git a/src/lib/components/matrix/MessageList.svelte b/src/lib/components/matrix/MessageList.svelte index 734be0f..b84e7ef 100644 --- a/src/lib/components/matrix/MessageList.svelte +++ b/src/lib/components/matrix/MessageList.svelte @@ -1,7 +1,5 @@ @@ -324,97 +157,23 @@
- - {#if onLoadMore} -
- -
- {/if} - - {#if allVisibleMessages.length === 0}
- forum - - -

No messages yet

-

Be the first to send a message!

-
- {:else if virtualizer && enableVirtualization} - -
- {#each virtualItems as virtualRow (virtualRow.key)} - {@const message = allVisibleMessages[virtualRow.index]} - {@const previousMessage = - virtualRow.index > 0 - ? allVisibleMessages[virtualRow.index - 1] - : null} - {@const isGrouped = shouldGroup(message, previousMessage)} - {@const showDateSeparator = needsDateSeparator( - message, - previousMessage, - )} - -
- - {#if showDateSeparator} -
-
- - {formatDateSeparator(message.timestamp)} - -
-
- {/if} - - onReact?.(message.eventId, emoji)} - onToggleReaction={( - emoji: string, - reactionEventId: string | null, - ) => onToggleReaction?.(message.eventId, emoji, reactionEventId)} - onEdit={() => onEdit?.(message)} - onDelete={() => onDelete?.(message.eventId)} - onReply={() => onReply?.(message)} - onScrollToMessage={scrollToMessage} - replyPreview={message.replyTo - ? getReplyPreview(message.replyTo) - : null} - /> -
- {/each} +

No messages yet

+

Be the first to send a message!

{:else} - -
+
{#each allVisibleMessages as message, i (message.eventId)} {@const previousMessage = i > 0 ? allVisibleMessages[i - 1] : null} {@const isGrouped = shouldGroup(message, previousMessage)} @@ -425,12 +184,12 @@ {#if showDateSeparator} -
-
- +
+
+ {formatDateSeparator(message.timestamp)} -
+
{/if} @@ -455,24 +214,19 @@ {/if}
- + {#if !shouldAutoScroll && allVisibleMessages.length > 0} {/if}
diff --git a/src/lib/components/matrix/RoomInfoPanel.svelte b/src/lib/components/matrix/RoomInfoPanel.svelte index 8b75321..d78e1cf 100644 --- a/src/lib/components/matrix/RoomInfoPanel.svelte +++ b/src/lib/components/matrix/RoomInfoPanel.svelte @@ -1,5 +1,5 @@ -
+
-
-

Room Info

+
+

Room Info

-
+
- +
-

{room.name}

+

{room.name}

{#if room.topic} -

{room.topic}

+

{room.topic}

{/if} - - + Settings + + +
-
-
-

{room.memberCount}

-

Members

+
+
+

{room.memberCount}

+

Members

-
-

- {room.isEncrypted ? "🔒" : "🔓"} -

-

+

+ + {room.isEncrypted ? "lock" : "lock_open"} + +

{room.isEncrypted ? "Encrypted" : "Not Encrypted"}

-
-

+
+

Details

- -
+
- Room ID + Room ID {room.roomId}
- Type - Type + {room.isDirect ? "Direct Message" : "Room"}
{#if room.lastActivity}
- Last Activity - {formatDate(room.lastActivity)} + Last Activity + {formatDate(room.lastActivity)}
{/if}
@@ -194,20 +157,29 @@ {#if admins.length > 0} -
+

Admins ({admins.length})

-
    +
      {#each admins as member}
    • - - {member.name} - 👑 + + {member.name} + shield_person
    • {/each}
    @@ -215,41 +187,55 @@ {/if} {#if moderators.length > 0} -
    +

    Moderators ({moderators.length})

    -
      +
        {#each moderators as member}
      • - - {member.name} - 🛡️ + + {member.name} + shield
      • {/each}
    {/if} -
    -

    +
    +

    Members ({regularMembers.length})

    -
      +
        {#each regularMembers.slice(0, 20) as member}
      • - - {member.name} + + {member.name}
      • {/each} {#if regularMembers.length > 20} -
      • +
      • +{regularMembers.length - 20} more members
      • {/if} diff --git a/src/lib/components/matrix/RoomSettingsModal.svelte b/src/lib/components/matrix/RoomSettingsModal.svelte index a0c6bf6..04510d6 100644 --- a/src/lib/components/matrix/RoomSettingsModal.svelte +++ b/src/lib/components/matrix/RoomSettingsModal.svelte @@ -1,5 +1,5 @@ {#if userNames.length > 0} -
        - -
        - - - +
        +
        + + +
        {formatTypingText(userNames)}
        diff --git a/src/lib/components/matrix/UserProfileModal.svelte b/src/lib/components/matrix/UserProfileModal.svelte index 9091c8b..395cebb 100644 --- a/src/lib/components/matrix/UserProfileModal.svelte +++ b/src/lib/components/matrix/UserProfileModal.svelte @@ -1,12 +1,12 @@
        (showActions = true)} onmouseleave={() => (showActions = false)} @@ -77,32 +77,22 @@ {#if replyPreview && message.replyTo} {/each} - +
        {#if showEmojiPicker} - onReact?.(emoji)} - onClose={() => (showEmojiPicker = false)} - /> +
        + { + onReact?.(emoji); + showEmojiPicker = false; + }} + onClose={() => (showEmojiPicker = false)} + /> +
        {/if}
        -
        +
        - + - + {#if isOwnMessage} {/if} - -
        + +
        {#if showContextMenu}
        e.stopPropagation()} > {#if isOwnMessage} -
        +
        {/if} diff --git a/src/lib/components/message/parts/MessageReactions.svelte b/src/lib/components/message/parts/MessageReactions.svelte index bc6fce7..3919905 100644 --- a/src/lib/components/message/parts/MessageReactions.svelte +++ b/src/lib/components/message/parts/MessageReactions.svelte @@ -56,7 +56,7 @@ {#if reactions.size > 0} -
        +
        {#each [...reactions.entries()] as [emoji, userMap]} {@const hasReacted = userMap.has(currentUserId)} {@const reactionEventId = getUserReactionEventId(emoji)} diff --git a/src/lib/components/message/parts/MessageReadReceipts.svelte b/src/lib/components/message/parts/MessageReadReceipts.svelte index e418aef..1025034 100644 --- a/src/lib/components/message/parts/MessageReadReceipts.svelte +++ b/src/lib/components/message/parts/MessageReadReceipts.svelte @@ -15,7 +15,7 @@ {#if receipts.length > 0}
        Read by diff --git a/src/lib/components/settings/SettingsGeneral.svelte b/src/lib/components/settings/SettingsGeneral.svelte index 8089f6b..111c3be 100644 --- a/src/lib/components/settings/SettingsGeneral.svelte +++ b/src/lib/components/settings/SettingsGeneral.svelte @@ -742,18 +742,28 @@
        - +