feat: launch readiness — PWA, API hardening, memory leak fixes, sticker removal

- Add PWA infrastructure: vite-plugin-pwa, manifest, service worker,
  SW update prompt component, placeholder icons, Apple meta tags
- Harden API client: 401 auto-logout, AbortController timeouts
  (30s standard, 120s uploads), onUnauthorized callback
- Fix memory leaks: clear voice user status on leave, clean up all
  Maps (channelToSpaceMap, permissions, etc.) on removeSpace
- Upgrade error boundary to Aether Drift design with Try Again button,
  collapsible stack trace, and componentDidCatch logging
- Configure desktop icon paths in electron-builder.yml
- Remove sticker feature (server routes, schema, types, UI components)
- Fix Docker build: use **/node_modules in .dockerignore to prevent
  COPY from clobbering pnpm-installed workspace dependencies
- Add vite-env.d.ts declarations for noise suppressor wasm imports
- Exclude test files from tsc build via tsconfig
This commit is contained in:
Jannis Braun
2026-03-15 15:41:22 +01:00
parent b08f40feb7
commit 4d230711fc
36 changed files with 2740 additions and 1283 deletions
-26
View File
@@ -70,7 +70,6 @@ export const messages = sqliteTable('messages', {
userId: text('user_id').notNull().references(() => users.id),
replyToId: text('reply_to_id'),
content: text('content'),
stickerId: text('sticker_id'),
editedAt: integer('edited_at'),
createdAt: integer('created_at').notNull(),
}, (table) => ({
@@ -113,7 +112,6 @@ export const dmMessages = sqliteTable('dm_messages', {
userId: text('user_id').notNull().references(() => users.id),
replyToId: text('reply_to_id'),
content: text('content'),
stickerId: text('sticker_id'),
editedAt: integer('edited_at'),
createdAt: integer('created_at').notNull(),
}, (table) => ({
@@ -215,30 +213,6 @@ export const userSpaceLayout = sqliteTable('user_space_layout', {
updatedAt: integer('updated_at').notNull(),
});
export const stickerPacks = sqliteTable('sticker_packs', {
id: text('id').primaryKey(),
spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
name: text('name').notNull(),
description: text('description'),
createdBy: text('created_by').notNull().references(() => users.id),
createdAt: integer('created_at').notNull(),
});
export const stickers = sqliteTable('stickers', {
id: text('id').primaryKey(),
packId: text('pack_id').notNull().references(() => stickerPacks.id, { onDelete: 'cascade' }),
spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
name: text('name').notNull(),
tags: text('tags').default(''),
filename: text('filename').notNull(),
mimetype: text('mimetype').notNull(),
size: integer('size').notNull(),
width: integer('width'),
height: integer('height'),
uploadedBy: text('uploaded_by').notNull().references(() => users.id),
createdAt: integer('created_at').notNull(),
});
export const instanceSettings = sqliteTable('instance_settings', {
id: integer('id').primaryKey().default(1),
instanceName: text('instance_name').default('Backspace'),