diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6abd13a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +version: '3.8' +services: + postgres: + image: postgres:16-alpine + container_name: collective-hub-db + environment: + POSTGRES_USER: hub_dev + POSTGRES_PASSWORD: hub_dev_password + POSTGRES_DB: collective_hub + ports: + - '5432:5432' + volumes: + - pgdata:/var/lib/postgresql/data + +volumes: + pgdata: diff --git a/src/app.d.ts b/src/app.d.ts index 44d2070..9a0c5e5 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -18,6 +18,7 @@ declare global { email: string | null; } | null; membership: Membership | null; + isSuperAdmin: boolean; } // interface PageData {} // interface PageState {} diff --git a/src/hooks.server.ts b/src/hooks.server.ts index a811bdb..31f7cdd 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -4,13 +4,40 @@ import { env } from '$env/dynamic/private'; import { svelteKitHandler } from 'better-auth/svelte-kit'; import { auth } from '$lib/server/auth'; import { getSiteBySlug } from '$lib/server/site-resolver'; +import { runMigrations } from '$lib/server/db/migrate'; + +// ─── Migration Automation ──────────────────────────────────────────────────── +// +// Runs database migrations on startup if RUN_MIGRATIONS=true. +// Only the designated migration-runner deployment sets this to true. +// All other deployments (RUN_MIGRATIONS=false or unset) skip migrations. +// +// This runs as a module-level initialization before the server accepts +// any requests. Migrations are skipped entirely during the Vite build +// phase (`building` is true). + +if (!building) { + const shouldRun = env.RUN_MIGRATIONS === 'true'; + + if (shouldRun) { + console.log('🚀 RUN_MIGRATIONS=true — running database migrations…'); + // Top-level await is fine here — Node.js supports it in ES modules, + // and SvelteKit's server entry is an ES module. + await runMigrations(); + } else { + console.log('⏭️ RUN_MIGRATIONS is not "true" — skipping migrations.'); + } +} + +// ─── Request Handler ───────────────────────────────────────────────────────── /** * Root server hook — runs on every request. * * Order of operations: - * 1. Resolve the current site from SITE_SLUG env var → attach to event.locals - * 2. Delegate to Better Auth's svelteKitHandler (handles /api/auth/* routes, + * 1. (Startup only) Run DB migrations if RUN_MIGRATIONS=true + * 2. Resolve the current site from SITE_SLUG env var → attach to event.locals + * 3. Delegate to Better Auth's svelteKitHandler (handles /api/auth/* routes, * passes through for all other routes) */ export const handle: Handle = async ({ event, resolve }) => { @@ -27,6 +54,62 @@ export const handle: Handle = async ({ event, resolve }) => { event.locals.siteSlug = slug; event.locals.siteSettings = siteContext.settings; + // --- Deactivated Site Guard (503) --- + // If the site is deactivated (isActive=false), block public access with a 503. + // Admin paths (/admin/*) and the login page (/login) are allowed through so + // that site owners/admins can still log in and reactivate the site. + if (event.locals.site && !event.locals.site.isActive) { + const pathname = event.url.pathname; + const isAdminPath = pathname.startsWith('/admin'); + const isLoginPath = pathname.startsWith('/login'); + + if (!isAdminPath && !isLoginPath) { + const bg = event.locals.siteSettings?.theme?.backgroundColor ?? '#1a1a2e'; + const fg = event.locals.siteSettings?.theme?.textColor ?? '#eaeaea'; + const name = event.locals.site.name; + + return new Response( + ` + +
+ + +This site is currently deactivated. Please check back later.
+Run npm run db:seed to create the default "local-dev" site.