From a09c389edea8219f3c019c1b794fdf580512dff0 Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Mon, 1 Dec 2025 14:14:08 +0100 Subject: [PATCH] fix(picture): resolve hydration error and CORS issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ssr.noExternal config for @manacore packages with .svelte.ts files to ensure Svelte 5 runes are properly processed - Fix CORS configuration to return Access-Control-Allow-Origin header by using callback function for origin validation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../apps/web/src/routes/(app)/+layout.svelte | 14 ++++++++ apps/picture/apps/backend/src/main.ts | 36 ++++++++++++++----- .../apps/web/src/routes/app/+layout.svelte | 14 ++++++++ apps/picture/apps/web/vite.config.ts | 4 +++ apps/presi/apps/web/src/routes/+layout.svelte | 13 +++++++ .../zitare/apps/web/src/routes/+layout.svelte | 13 +++++++ 6 files changed, 85 insertions(+), 9 deletions(-) diff --git a/apps/manadeck/apps/web/src/routes/(app)/+layout.svelte b/apps/manadeck/apps/web/src/routes/(app)/+layout.svelte index bd73f2e96..ac1729a68 100644 --- a/apps/manadeck/apps/web/src/routes/(app)/+layout.svelte +++ b/apps/manadeck/apps/web/src/routes/(app)/+layout.svelte @@ -13,8 +13,12 @@ import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui'; import { THEME_DEFINITIONS } from '@manacore/shared-theme'; import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n'; + import { getPillAppItems } from '@manacore/shared-branding'; import { setLocale, supportedLocales } from '$lib/i18n'; + // App switcher items + const appItems = getPillAppItems('manadeck'); + let { children } = $props(); let isSidebarMode = $state(false); @@ -63,6 +67,9 @@ ); let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale)); + // User email for user dropdown + let userEmail = $derived(authStore.user?.email); + // Navigation shortcuts (Ctrl+1-5) const navRoutes = navItems.map((item) => item.href); @@ -181,7 +188,14 @@ showLanguageSwitcher={true} {languageItems} {currentLanguageLabel} + showLogout={true} primaryColor="#6366f1" + showAppSwitcher={true} + {appItems} + {userEmail} + settingsHref="/settings" + manaHref="/mana" + profileHref="/profile" /> diff --git a/apps/picture/apps/backend/src/main.ts b/apps/picture/apps/backend/src/main.ts index f3b12d1e3..abca24e44 100644 --- a/apps/picture/apps/backend/src/main.ts +++ b/apps/picture/apps/backend/src/main.ts @@ -1,20 +1,32 @@ import { NestFactory } from '@nestjs/core'; import { ValidationPipe } from '@nestjs/common'; +import { NestExpressApplication } from '@nestjs/platform-express'; +import { join } from 'path'; import { AppModule } from './app.module'; async function bootstrap() { - const app = await NestFactory.create(AppModule); + const app = await NestFactory.create(AppModule); // Enable CORS for mobile and web apps + const allowedOrigins = [ + 'http://localhost:3000', + 'http://localhost:5173', + 'http://localhost:5174', + 'http://localhost:5175', + 'http://localhost:8081', + 'exp://localhost:8081', + 'http://localhost:3001', // Mana Core Auth + ]; + app.enableCors({ - origin: [ - 'http://localhost:3000', - 'http://localhost:5173', - 'http://localhost:5174', - 'http://localhost:8081', - 'exp://localhost:8081', - 'http://localhost:3001', // Mana Core Auth - ], + origin: (origin, callback) => { + // Allow requests with no origin (like mobile apps or curl) + if (!origin || allowedOrigins.includes(origin)) { + callback(null, origin || '*'); + } else { + callback(null, false); + } + }, methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], credentials: true, }); @@ -28,6 +40,12 @@ async function bootstrap() { }) ); + // Serve static files from uploads directory (for local development) + const uploadsPath = process.env.LOCAL_STORAGE_PATH || join(process.cwd(), 'uploads'); + app.useStaticAssets(uploadsPath, { + prefix: '/uploads/', + }); + // Set global prefix for API routes app.setGlobalPrefix('api'); diff --git a/apps/picture/apps/web/src/routes/app/+layout.svelte b/apps/picture/apps/web/src/routes/app/+layout.svelte index 37403a29e..5480a991e 100644 --- a/apps/picture/apps/web/src/routes/app/+layout.svelte +++ b/apps/picture/apps/web/src/routes/app/+layout.svelte @@ -7,6 +7,7 @@ import type { PillNavItem, PillNavElement, PillDropdownItem } from '@manacore/shared-ui'; import { THEME_DEFINITIONS } from '@manacore/shared-theme'; import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n'; + import { getPillAppItems } from '@manacore/shared-branding'; import { setLocale, supportedLocales } from '$lib/i18n'; import KeyboardShortcutsModal from '$lib/components/ui/KeyboardShortcutsModal.svelte'; import { theme } from '$lib/stores/theme'; @@ -14,6 +15,9 @@ import { viewMode, setViewMode, type ViewMode } from '$lib/stores/view'; import { browser } from '$app/environment'; + // App switcher items + const appItems = getPillAppItems('picture'); + let { children } = $props(); // PillNav state @@ -110,6 +114,9 @@ ); let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale)); + // User email for user dropdown + let userEmail = $derived(authStore.user?.email); + // Elements (divider + view mode tabs) let elements: PillNavElement[] = $derived([ { type: 'divider' as const }, @@ -225,7 +232,14 @@ showLanguageSwitcher={true} {languageItems} {currentLanguageLabel} + showLogout={true} primaryColor="#3b82f6" + showAppSwitcher={true} + {appItems} + {userEmail} + settingsHref="/app/settings" + manaHref="/app/mana" + profileHref="/app/profile" /> {/if} diff --git a/apps/picture/apps/web/vite.config.ts b/apps/picture/apps/web/vite.config.ts index 1a028b6b8..bee9c8a60 100644 --- a/apps/picture/apps/web/vite.config.ts +++ b/apps/picture/apps/web/vite.config.ts @@ -8,4 +8,8 @@ export default defineConfig({ port: 5175, strictPort: true, }, + ssr: { + // Process @manacore packages that contain .svelte.ts files with runes + noExternal: ['@manacore/shared-theme', '@manacore/shared-auth'], + }, }); diff --git a/apps/presi/apps/web/src/routes/+layout.svelte b/apps/presi/apps/web/src/routes/+layout.svelte index 364acc7b2..95b6ea3de 100644 --- a/apps/presi/apps/web/src/routes/+layout.svelte +++ b/apps/presi/apps/web/src/routes/+layout.svelte @@ -13,9 +13,13 @@ isNavCollapsed as collapsedStore, } from '$lib/stores/navigation'; import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n'; + import { getPillAppItems } from '@manacore/shared-branding'; import { setLocale, supportedLocales } from '$lib/i18n'; import '../app.css'; + // App switcher items + const appItems = getPillAppItems('presi'); + let { children } = $props(); let loading = $state(true); @@ -53,6 +57,9 @@ ); let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale)); + // User email for user dropdown + let userEmail = $derived(auth.user?.email); + // Navigation items for Presi const navItems: PillNavItem[] = [ { href: '/', label: 'Decks', icon: 'document' }, @@ -192,6 +199,12 @@ showLogout={true} onLogout={handleLogout} primaryColor="#64748b" + showAppSwitcher={true} + {appItems} + {userEmail} + settingsHref="/settings" + manaHref="/mana" + profileHref="/profile" /> diff --git a/apps/zitare/apps/web/src/routes/+layout.svelte b/apps/zitare/apps/web/src/routes/+layout.svelte index 49df3f13e..5c1ebac5b 100644 --- a/apps/zitare/apps/web/src/routes/+layout.svelte +++ b/apps/zitare/apps/web/src/routes/+layout.svelte @@ -13,10 +13,14 @@ isNavCollapsed as collapsedStore, } from '$lib/stores/navigation'; import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n'; + import { getPillAppItems } from '@manacore/shared-branding'; import { setLocale, supportedLocales } from '$lib/i18n'; import ToastContainer from '$lib/components/ToastContainer.svelte'; import '../app.css'; + // App switcher items + const appItems = getPillAppItems('zitare'); + let { children } = $props(); let loading = $state(true); @@ -59,6 +63,9 @@ ); let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale)); + // User email for user dropdown + let userEmail = $derived(authStore.user?.email); + // Navigation items for Zitare const navItems: PillNavItem[] = [ { href: '/', label: 'Start', icon: 'home' }, @@ -194,6 +201,12 @@ showLogout={authStore.isAuthenticated} onLogout={handleLogout} primaryColor="#f59e0b" + showAppSwitcher={true} + {appItems} + {userEmail} + settingsHref="/settings" + manaHref="/mana" + profileHref="/profile" />