From 27e2f196ebc5c3f534c53957fb9d58e128db3bc8 Mon Sep 17 00:00:00 2001 From: Sean O'Connor Date: Mon, 7 Sep 2026 19:36:14 -0400 Subject: [PATCH] Initial commit of Vellum, an event photo product for guest uploads, host moderation, and original-quality galleries. Co-authored-by: Cursor --- .env.example | 22 + .gitignore | 11 + AGENTS.md | 17 + README.md | 48 + apps/web/components.json | 25 + apps/web/next-env.d.ts | 6 + apps/web/next.config.ts | 32 + apps/web/package.json | 52 + apps/web/postcss.config.mjs | 5 + apps/web/src/app/admin/layout.tsx | 39 + apps/web/src/app/admin/page.tsx | 98 ++ apps/web/src/app/admin/platform-codes.tsx | 110 ++ apps/web/src/app/admin/platform-users.tsx | 84 + .../settings/deployment-settings-form.tsx | 95 ++ apps/web/src/app/admin/settings/page.tsx | 33 + apps/web/src/app/api/auth/[...all]/route.ts | 4 + apps/web/src/app/api/trpc/[trpc]/route.ts | 34 + apps/web/src/app/apple-icon.png | Bin 0 -> 80022 bytes .../src/app/dashboard/create-event-dialog.tsx | 106 ++ .../app/dashboard/events/[id]/event-audit.tsx | 36 + .../app/dashboard/events/[id]/event-notes.tsx | 55 + .../dashboard/events/[id]/event-people.tsx | 144 ++ .../events/[id]/event-settings-form.tsx | 197 +++ .../dashboard/events/[id]/moderation-grid.tsx | 279 +++ .../src/app/dashboard/events/[id]/page.tsx | 122 ++ apps/web/src/app/dashboard/layout.tsx | 59 + apps/web/src/app/dashboard/page.tsx | 90 + .../src/app/dashboard/people/group-people.tsx | 156 ++ apps/web/src/app/dashboard/people/page.tsx | 39 + apps/web/src/app/e/[slug]/guest-gallery.tsx | 103 ++ apps/web/src/app/e/[slug]/guest-upload.tsx | 298 ++++ apps/web/src/app/e/[slug]/page.tsx | 59 + .../forgot-password/forgot-password-form.tsx | 82 + apps/web/src/app/forgot-password/page.tsx | 26 + apps/web/src/app/globals.css | 220 +++ apps/web/src/app/icon.svg | 8 + apps/web/src/app/invitations/[token]/page.tsx | 66 + .../app/invitations/[token]/redeem-button.tsx | 33 + apps/web/src/app/layout.tsx | 67 + apps/web/src/app/not-found.tsx | 16 + apps/web/src/app/opengraph-image.tsx | 60 + apps/web/src/app/page.tsx | 84 + apps/web/src/app/reset-password/page.tsx | 29 + .../reset-password/reset-password-form.tsx | 120 ++ apps/web/src/app/sign-in/page.tsx | 49 + apps/web/src/app/sign-in/sign-in-form.tsx | 100 ++ apps/web/src/app/sign-up/page.tsx | 52 + apps/web/src/app/sign-up/sign-up-form.tsx | 137 ++ apps/web/src/components/brand-mark.tsx | 41 + apps/web/src/components/dashboard-tab-bar.tsx | 92 + apps/web/src/components/event-workspace.tsx | 46 + apps/web/src/components/group-switcher.tsx | 49 + apps/web/src/components/sign-out-button.tsx | 24 + apps/web/src/components/site-header-bar.tsx | 99 ++ apps/web/src/components/site-header.tsx | 35 + apps/web/src/components/theme-provider.tsx | 11 + apps/web/src/components/theme-toggle.tsx | 31 + apps/web/src/components/ui/alert.tsx | 75 + apps/web/src/components/ui/badge.tsx | 48 + apps/web/src/components/ui/button.tsx | 66 + apps/web/src/components/ui/card.tsx | 102 ++ apps/web/src/components/ui/dialog.tsx | 168 ++ apps/web/src/components/ui/dropdown-menu.tsx | 268 +++ apps/web/src/components/ui/empty.tsx | 103 ++ apps/web/src/components/ui/field.tsx | 238 +++ apps/web/src/components/ui/input.tsx | 18 + apps/web/src/components/ui/label.tsx | 23 + apps/web/src/components/ui/progress.tsx | 30 + apps/web/src/components/ui/select.tsx | 191 +++ apps/web/src/components/ui/separator.tsx | 27 + apps/web/src/components/ui/sheet.tsx | 147 ++ apps/web/src/components/ui/skeleton.tsx | 13 + apps/web/src/components/ui/sonner.tsx | 49 + apps/web/src/components/ui/spinner.tsx | 10 + apps/web/src/components/ui/switch.tsx | 32 + apps/web/src/components/ui/tabs.tsx | 89 + apps/web/src/components/ui/textarea.tsx | 17 + apps/web/src/instrumentation.ts | 15 + apps/web/src/lib/auth-client.ts | 3 + apps/web/src/lib/brand.ts | 3 + apps/web/src/lib/photo-status.test.ts | 25 + apps/web/src/lib/photo-status.ts | 30 + apps/web/src/lib/slug.test.ts | 12 + apps/web/src/lib/slug.ts | 10 + apps/web/src/lib/upload.ts | 46 + apps/web/src/lib/utils.ts | 13 + apps/web/src/server/api/root.ts | 26 + apps/web/src/server/api/routers/event.ts | 102 ++ apps/web/src/server/api/routers/group.ts | 327 ++++ apps/web/src/server/api/routers/guest.ts | 115 ++ apps/web/src/server/api/routers/invites.ts | 39 + apps/web/src/server/api/routers/manager.ts | 725 ++++++++ apps/web/src/server/api/routers/photos.ts | 154 ++ apps/web/src/server/api/routers/platform.ts | 247 +++ apps/web/src/server/api/routers/viewer.ts | 92 + apps/web/src/server/api/trpc.ts | 154 ++ apps/web/src/server/audit.ts | 25 + apps/web/src/server/auth.ts | 62 + apps/web/src/server/cookies.ts | 41 + apps/web/src/server/entitlements.ts | 70 + apps/web/src/server/invites.ts | 171 ++ apps/web/src/server/membership.ts | 153 ++ apps/web/src/server/permissions.test.ts | 60 + apps/web/src/server/permissions.ts | 209 +++ apps/web/src/server/public-app-url.ts | 60 + apps/web/src/server/rate-limit.ts | 59 + apps/web/src/server/roles.ts | 126 ++ apps/web/src/server/seed-auth.ts | 91 + apps/web/src/server/settings.ts | 25 + apps/web/src/server/tokens.ts | 13 + apps/web/src/trpc/react.tsx | 36 + apps/web/src/trpc/server.ts | 17 + apps/web/tsconfig.json | 23 + apps/worker/package.json | 23 + apps/worker/src/heic-convert.d.ts | 7 + apps/worker/src/index.ts | 176 ++ apps/worker/tsconfig.json | 5 + bun.lock | 1499 +++++++++++++++++ bunfig.toml | 2 + docker-compose.yml | 59 + docker/garage.toml | 22 + package.json | 35 + packages/contracts/package.json | 20 + packages/contracts/src/index.ts | 216 +++ packages/contracts/tsconfig.json | 7 + packages/database/drizzle.config.ts | 15 + .../database/drizzle/0000_yummy_whizzer.sql | 113 ++ .../drizzle/0001_groups_roles_guests.sql | 207 +++ .../database/drizzle/meta/0000_snapshot.json | 834 +++++++++ packages/database/drizzle/meta/_journal.json | 20 + packages/database/package.json | 29 + packages/database/src/auth-schema.ts | 105 ++ packages/database/src/db.ts | 56 + packages/database/src/index.ts | 3 + packages/database/src/migrate.ts | 16 + packages/database/src/schema.ts | 491 ++++++ packages/database/src/seed.ts | 173 ++ packages/database/tsconfig.json | 7 + packages/email/package.json | 22 + packages/email/src/index.ts | 216 +++ packages/email/tsconfig.json | 7 + packages/storage/package.json | 22 + packages/storage/src/index.ts | 15 + packages/storage/src/keys.test.ts | 19 + packages/storage/src/keys.ts | 15 + packages/storage/src/s3.ts | 200 +++ packages/storage/tsconfig.json | 7 + tsconfig.base.json | 14 + turbo.json | 42 + 149 files changed, 13847 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 README.md create mode 100644 apps/web/components.json create mode 100644 apps/web/next-env.d.ts create mode 100644 apps/web/next.config.ts create mode 100644 apps/web/package.json create mode 100644 apps/web/postcss.config.mjs create mode 100644 apps/web/src/app/admin/layout.tsx create mode 100644 apps/web/src/app/admin/page.tsx create mode 100644 apps/web/src/app/admin/platform-codes.tsx create mode 100644 apps/web/src/app/admin/platform-users.tsx create mode 100644 apps/web/src/app/admin/settings/deployment-settings-form.tsx create mode 100644 apps/web/src/app/admin/settings/page.tsx create mode 100644 apps/web/src/app/api/auth/[...all]/route.ts create mode 100644 apps/web/src/app/api/trpc/[trpc]/route.ts create mode 100644 apps/web/src/app/apple-icon.png create mode 100644 apps/web/src/app/dashboard/create-event-dialog.tsx create mode 100644 apps/web/src/app/dashboard/events/[id]/event-audit.tsx create mode 100644 apps/web/src/app/dashboard/events/[id]/event-notes.tsx create mode 100644 apps/web/src/app/dashboard/events/[id]/event-people.tsx create mode 100644 apps/web/src/app/dashboard/events/[id]/event-settings-form.tsx create mode 100644 apps/web/src/app/dashboard/events/[id]/moderation-grid.tsx create mode 100644 apps/web/src/app/dashboard/events/[id]/page.tsx create mode 100644 apps/web/src/app/dashboard/layout.tsx create mode 100644 apps/web/src/app/dashboard/page.tsx create mode 100644 apps/web/src/app/dashboard/people/group-people.tsx create mode 100644 apps/web/src/app/dashboard/people/page.tsx create mode 100644 apps/web/src/app/e/[slug]/guest-gallery.tsx create mode 100644 apps/web/src/app/e/[slug]/guest-upload.tsx create mode 100644 apps/web/src/app/e/[slug]/page.tsx create mode 100644 apps/web/src/app/forgot-password/forgot-password-form.tsx create mode 100644 apps/web/src/app/forgot-password/page.tsx create mode 100644 apps/web/src/app/globals.css create mode 100644 apps/web/src/app/icon.svg create mode 100644 apps/web/src/app/invitations/[token]/page.tsx create mode 100644 apps/web/src/app/invitations/[token]/redeem-button.tsx create mode 100644 apps/web/src/app/layout.tsx create mode 100644 apps/web/src/app/not-found.tsx create mode 100644 apps/web/src/app/opengraph-image.tsx create mode 100644 apps/web/src/app/page.tsx create mode 100644 apps/web/src/app/reset-password/page.tsx create mode 100644 apps/web/src/app/reset-password/reset-password-form.tsx create mode 100644 apps/web/src/app/sign-in/page.tsx create mode 100644 apps/web/src/app/sign-in/sign-in-form.tsx create mode 100644 apps/web/src/app/sign-up/page.tsx create mode 100644 apps/web/src/app/sign-up/sign-up-form.tsx create mode 100644 apps/web/src/components/brand-mark.tsx create mode 100644 apps/web/src/components/dashboard-tab-bar.tsx create mode 100644 apps/web/src/components/event-workspace.tsx create mode 100644 apps/web/src/components/group-switcher.tsx create mode 100644 apps/web/src/components/sign-out-button.tsx create mode 100644 apps/web/src/components/site-header-bar.tsx create mode 100644 apps/web/src/components/site-header.tsx create mode 100644 apps/web/src/components/theme-provider.tsx create mode 100644 apps/web/src/components/theme-toggle.tsx create mode 100644 apps/web/src/components/ui/alert.tsx create mode 100644 apps/web/src/components/ui/badge.tsx create mode 100644 apps/web/src/components/ui/button.tsx create mode 100644 apps/web/src/components/ui/card.tsx create mode 100644 apps/web/src/components/ui/dialog.tsx create mode 100644 apps/web/src/components/ui/dropdown-menu.tsx create mode 100644 apps/web/src/components/ui/empty.tsx create mode 100644 apps/web/src/components/ui/field.tsx create mode 100644 apps/web/src/components/ui/input.tsx create mode 100644 apps/web/src/components/ui/label.tsx create mode 100644 apps/web/src/components/ui/progress.tsx create mode 100644 apps/web/src/components/ui/select.tsx create mode 100644 apps/web/src/components/ui/separator.tsx create mode 100644 apps/web/src/components/ui/sheet.tsx create mode 100644 apps/web/src/components/ui/skeleton.tsx create mode 100644 apps/web/src/components/ui/sonner.tsx create mode 100644 apps/web/src/components/ui/spinner.tsx create mode 100644 apps/web/src/components/ui/switch.tsx create mode 100644 apps/web/src/components/ui/tabs.tsx create mode 100644 apps/web/src/components/ui/textarea.tsx create mode 100644 apps/web/src/instrumentation.ts create mode 100644 apps/web/src/lib/auth-client.ts create mode 100644 apps/web/src/lib/brand.ts create mode 100644 apps/web/src/lib/photo-status.test.ts create mode 100644 apps/web/src/lib/photo-status.ts create mode 100644 apps/web/src/lib/slug.test.ts create mode 100644 apps/web/src/lib/slug.ts create mode 100644 apps/web/src/lib/upload.ts create mode 100644 apps/web/src/lib/utils.ts create mode 100644 apps/web/src/server/api/root.ts create mode 100644 apps/web/src/server/api/routers/event.ts create mode 100644 apps/web/src/server/api/routers/group.ts create mode 100644 apps/web/src/server/api/routers/guest.ts create mode 100644 apps/web/src/server/api/routers/invites.ts create mode 100644 apps/web/src/server/api/routers/manager.ts create mode 100644 apps/web/src/server/api/routers/photos.ts create mode 100644 apps/web/src/server/api/routers/platform.ts create mode 100644 apps/web/src/server/api/routers/viewer.ts create mode 100644 apps/web/src/server/api/trpc.ts create mode 100644 apps/web/src/server/audit.ts create mode 100644 apps/web/src/server/auth.ts create mode 100644 apps/web/src/server/cookies.ts create mode 100644 apps/web/src/server/entitlements.ts create mode 100644 apps/web/src/server/invites.ts create mode 100644 apps/web/src/server/membership.ts create mode 100644 apps/web/src/server/permissions.test.ts create mode 100644 apps/web/src/server/permissions.ts create mode 100644 apps/web/src/server/public-app-url.ts create mode 100644 apps/web/src/server/rate-limit.ts create mode 100644 apps/web/src/server/roles.ts create mode 100644 apps/web/src/server/seed-auth.ts create mode 100644 apps/web/src/server/settings.ts create mode 100644 apps/web/src/server/tokens.ts create mode 100644 apps/web/src/trpc/react.tsx create mode 100644 apps/web/src/trpc/server.ts create mode 100644 apps/web/tsconfig.json create mode 100644 apps/worker/package.json create mode 100644 apps/worker/src/heic-convert.d.ts create mode 100644 apps/worker/src/index.ts create mode 100644 apps/worker/tsconfig.json create mode 100644 bun.lock create mode 100644 bunfig.toml create mode 100644 docker-compose.yml create mode 100644 docker/garage.toml create mode 100644 package.json create mode 100644 packages/contracts/package.json create mode 100644 packages/contracts/src/index.ts create mode 100644 packages/contracts/tsconfig.json create mode 100644 packages/database/drizzle.config.ts create mode 100644 packages/database/drizzle/0000_yummy_whizzer.sql create mode 100644 packages/database/drizzle/0001_groups_roles_guests.sql create mode 100644 packages/database/drizzle/meta/0000_snapshot.json create mode 100644 packages/database/drizzle/meta/_journal.json create mode 100644 packages/database/package.json create mode 100644 packages/database/src/auth-schema.ts create mode 100644 packages/database/src/db.ts create mode 100644 packages/database/src/index.ts create mode 100644 packages/database/src/migrate.ts create mode 100644 packages/database/src/schema.ts create mode 100644 packages/database/src/seed.ts create mode 100644 packages/database/tsconfig.json create mode 100644 packages/email/package.json create mode 100644 packages/email/src/index.ts create mode 100644 packages/email/tsconfig.json create mode 100644 packages/storage/package.json create mode 100644 packages/storage/src/index.ts create mode 100644 packages/storage/src/keys.test.ts create mode 100644 packages/storage/src/keys.ts create mode 100644 packages/storage/src/s3.ts create mode 100644 packages/storage/tsconfig.json create mode 100644 tsconfig.base.json create mode 100644 turbo.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e96ce14 --- /dev/null +++ b/.env.example @@ -0,0 +1,22 @@ +DATABASE_URL=postgres://album:album@localhost:5439/album +NEXT_PUBLIC_APP_URL=http://localhost:3000 +BETTER_AUTH_SECRET=album-development-secret-change-me +BETTER_AUTH_URL=http://localhost:3000 + +EMAIL_PROVIDER=mailpit +EMAIL_FROM=Album +SMTP_HOST=127.0.0.1 +SMTP_PORT=1027 +RESEND_API_KEY= +RESEND_FROM=Album + +S3_ENDPOINT=http://127.0.0.1:3900 +S3_PUBLIC_ENDPOINT=http://127.0.0.1:3900 +S3_REGION=garage +S3_BUCKET=album +S3_ACCESS_KEY=GK0123456789abcdef0123456789abcdef +S3_SECRET_KEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +S3_FORCE_PATH_STYLE=true + +WORKER_CONCURRENCY=1 +WORKER_MIN_INTERVAL_MS=250 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2cc9fe8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +node_modules +.turbo +.next +dist +.env +.env.local +*.log +*.tsbuildinfo +coverage +.secrets/ +.DS_Store diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..7a1dd13 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,17 @@ +# Vellum repository guidance + +- Use Bun exclusively for dependency installation and project scripts. Do not + introduce npm, pnpm, or Yarn lockfiles or commands. +- Keep event-owned database reads and writes scoped by `event_id` (or the + hosting `user_id` for event records). +- Store timestamps as timezone-aware values. +- Do not import `@album/database` into client components. +- Cross-application payloads belong in `@album/contracts`, not in duplicated + local interfaces. +- Never log contributor names, emails, or object keys in bulk. Log photo and + event ids only. +- Send testing email only to Mailpit through the non-production mail + configuration. Never send tests through a production provider. +- Guest gallery queries must return approved photos only. +- Uploads go directly to object storage via short-lived presigned PUTs. Do not + stream original files through Next.js. diff --git a/README.md b/README.md new file mode 100644 index 0000000..70f4305 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# Vellum + +Event photo collection for a wedding day — and for any gathering that needs +the same guest-upload, host-moderate, public-gallery loop. + +Guests open a shareable event link, optionally add a name, email, and note, +and upload photos. Event people approve what appears in the gallery. +Originals stay full quality in S3-compatible object storage. + +## Repository + +```text +apps/ + web/ Public site, guest pages, dashboard, platform admin + worker/ image variants, EXIF/GPS strip, HEIC conversion +packages/ + contracts/ shared Zod payloads + database/ Drizzle schema, migrations, seed + storage/ S3/Garage client, object keys, presign + email/ Mailpit (dev) / Resend (prod) for auth and guest mail +``` + +## Quick start + +Requirements: Bun 1.3+ and Docker. + +```bash +cp .env.example .env +bun install +bun run docker:up +bun run db:migrate +bun run auth:seed +bun run db:seed +bun run dev +``` + +The app runs at `http://localhost:3000`. Garage S3 is at +`http://localhost:3900`. Mailpit is at `http://localhost:8027`. + +- Public: `/` (listed events) and `/e/demo` +- Dashboard: `/dashboard` +- Platform: `/admin` + +Example accounts (password `host`, admin password `admin`): + +- `admin@example.com` — platform super-admin +- `host@example.com` / `partner@example.com` — event owners +- `manager@example.com` — event manager diff --git a/apps/web/components.json b/apps/web/components.json new file mode 100644 index 0000000..2a42785 --- /dev/null +++ b/apps/web/components.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "radix-nova", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "menuColor": "default", + "menuAccent": "subtle", + "registries": {} +} diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts new file mode 100644 index 0000000..c4b7818 --- /dev/null +++ b/apps/web/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +import "./.next/dev/types/routes.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts new file mode 100644 index 0000000..c530ab3 --- /dev/null +++ b/apps/web/next.config.ts @@ -0,0 +1,32 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + output: "standalone", + poweredByHeader: false, + transpilePackages: [ + "@album/contracts", + "@album/database", + "@album/email", + "@album/storage", + ], + async headers() { + const securityHeaders = [ + { key: "X-Content-Type-Options", value: "nosniff" }, + { key: "X-Frame-Options", value: "DENY" }, + { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, + { + key: "Permissions-Policy", + value: "camera=(), geolocation=(), microphone=(), payment=()", + }, + ]; + if (process.env.NODE_ENV === "production") { + securityHeaders.push({ + key: "Strict-Transport-Security", + value: "max-age=31536000; includeSubDomains", + }); + } + return [{ source: "/(.*)", headers: securityHeaders }]; + }, +}; + +export default nextConfig; diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..5e20d57 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,52 @@ +{ + "name": "@album/web", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "bun --env-file=../../.env x next dev --turbopack", + "build": "bun --env-file=../../.env x next build", + "start": "bun --env-file=../../.env x next start", + "auth:seed": "bun --env-file=../../.env src/server/seed-auth.ts", + "typecheck": "tsc --noEmit", + "lint": "tsc --noEmit" + }, + "dependencies": { + "@album/contracts": "workspace:*", + "@album/database": "workspace:*", + "@album/email": "workspace:*", + "@album/storage": "workspace:*", + "@tanstack/react-query": "^5.90.2", + "@trpc/client": "^11.4.3", + "@trpc/react-query": "^11.4.3", + "@trpc/server": "^11.4.3", + "better-auth": "1.6.24", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cn": "^0.2.6", + "dotenv": "^16.5.0", + "drizzle-orm": "^0.45.2", + "lucide-react": "^0.468.0", + "next": "16.2.12", + "next-themes": "^0.4.6", + "radix-ui": "^1.6.7", + "react": "19.2.8", + "react-dom": "19.2.8", + "server-only": "^0.0.1", + "shadcn": "^4.21.0", + "sonner": "^2.0.7", + "superjson": "^2.2.2", + "tailwind-merge": "^3.3.0", + "tw-animate-css": "^1.4.0", + "zod": "^3.25.67" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.10", + "@types/bun": "^1.3.14", + "@types/node": "^22.15.32", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "postcss": "^8.5.22", + "tailwindcss": "^4.1.10", + "typescript": "^5.8.3" + } +} diff --git a/apps/web/postcss.config.mjs b/apps/web/postcss.config.mjs new file mode 100644 index 0000000..c2ddf74 --- /dev/null +++ b/apps/web/postcss.config.mjs @@ -0,0 +1,5 @@ +export default { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; diff --git a/apps/web/src/app/admin/layout.tsx b/apps/web/src/app/admin/layout.tsx new file mode 100644 index 0000000..3b84367 --- /dev/null +++ b/apps/web/src/app/admin/layout.tsx @@ -0,0 +1,39 @@ +import { redirect } from "next/navigation"; +import { headers } from "next/headers"; +import Link from "next/link"; +import { auth } from "@/server/auth"; +import { getPlatformRole } from "@/server/roles"; +import { Button } from "@/components/ui/button"; + +import { DashboardTabBar } from "@/components/dashboard-tab-bar"; + +export default async function AdminLayout({ + children, +}: { + children: React.ReactNode; +}) { + const session = await auth.api.getSession({ headers: await headers() }); + if (!session) redirect("/sign-in?callbackURL=/admin"); + const role = await getPlatformRole(session.user.id); + if (!role) redirect("/dashboard"); + + return ( + <> +
+
+ + + +
+
{children}
+
+ + + ); +} diff --git a/apps/web/src/app/admin/page.tsx b/apps/web/src/app/admin/page.tsx new file mode 100644 index 0000000..50e1db4 --- /dev/null +++ b/apps/web/src/app/admin/page.tsx @@ -0,0 +1,98 @@ +import { createServerCaller } from "@/trpc/server"; +import { getPlatformRole } from "@/server/roles"; +import { auth } from "@/server/auth"; +import { headers } from "next/headers"; +import { Badge } from "@/components/ui/badge"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { PlatformUsers } from "./platform-users"; +import { PlatformCodes } from "./platform-codes"; + +export default async function AdminPage() { + const session = await auth.api.getSession({ headers: await headers() }); + const role = session ? await getPlatformRole(session.user.id) : null; + const caller = await createServerCaller(); + const [groups, events, audit] = await Promise.all([ + caller.platform.groups(), + caller.platform.events(), + caller.platform.audit(), + ]); + const canManageUsers = role === "super_admin" || role === "admin"; + + return ( +
+
+

Platform

+

+ Full access for deployment operators. +

+
+
+ + + Groups + {groups.length} groups + + + {groups.map((group) => ( +
+ {group.name} + + {group.quota.unlimited + ? "unlimited" + : `${group.quota.used}/${group.quota.eventLimit ?? 0}`} + {group.quota.complimentary ? " · comp" : ""} + +
+ ))} +
+
+ + + Events + {events.length} events + + + {events.map((event) => ( +
+ {event.title} +
+ {event.status} + {event.listed ? listed : null} +
+
+ ))} +
+
+
+ {canManageUsers ? : null} + {canManageUsers ? ( + ({ id: group.id, name: group.name }))} /> + ) : null} + + + Audit + + +
    + {audit.slice(0, 40).map((row) => ( +
  • + + {row.action} · {row.subjectType} + + + {new Date(row.createdAt).toLocaleString()} + +
  • + ))} +
+
+
+
+ ); +} diff --git a/apps/web/src/app/admin/platform-codes.tsx b/apps/web/src/app/admin/platform-codes.tsx new file mode 100644 index 0000000..e8c69bd --- /dev/null +++ b/apps/web/src/app/admin/platform-codes.tsx @@ -0,0 +1,110 @@ +"use client"; + +import { useState } from "react"; +import { toast } from "sonner"; +import { api } from "@/trpc/react"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; + +export function PlatformCodes({ + groups, +}: { + groups: { id: string; name: string }[]; +}) { + const [code, setCode] = useState(null); + const [groupId, setGroupId] = useState(groups[0]?.id ?? ""); + const createCode = api.platform.createCode.useMutation({ + onSuccess: (result) => { + setCode(result.code); + toast.success("Code created"); + }, + onError: (error) => toast.error(error.message), + }); + const grant = api.platform.grantEntitlement.useMutation({ + onSuccess: () => toast.success("Complimentary unlimited granted"), + onError: (error) => toast.error(error.message), + }); + + return ( + + + Invites and entitlements + + One-time or reusable codes, and complimentary event access. + + + +
+ + +
+ {code ? ( +

+ Share once: {code} +

+ ) : null} + {groups.length > 0 ? ( +
+ + +
+ ) : null} +
+
+ ); +} diff --git a/apps/web/src/app/admin/platform-users.tsx b/apps/web/src/app/admin/platform-users.tsx new file mode 100644 index 0000000..420681c --- /dev/null +++ b/apps/web/src/app/admin/platform-users.tsx @@ -0,0 +1,84 @@ +"use client"; + +import { useState } from "react"; +import { toast } from "sonner"; +import type { PlatformRole } from "@album/contracts"; +import { api } from "@/trpc/react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Badge } from "@/components/ui/badge"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; + +const roles: Array = [ + "none", + "viewer", + "moderator", + "admin", + "super_admin", +]; + +export function PlatformUsers() { + const [query, setQuery] = useState(""); + const users = api.platform.users.useQuery({ query: query || undefined }); + const setRole = api.platform.setPlatformRole.useMutation({ + onSuccess: async () => { + toast.success("Role updated"); + await users.refetch(); + }, + onError: (error) => toast.error(error.message), + }); + + return ( + + + Users + Grant platform roles. + + + setQuery(event.target.value)} + /> +
    + {(users.data ?? []).map((row) => ( +
  • +
    +

    {row.name}

    +

    {row.email}

    +
    +
    + {row.platformRole ? ( + {row.platformRole} + ) : null} + +
    +
  • + ))} +
+
+
+ ); +} diff --git a/apps/web/src/app/admin/settings/deployment-settings-form.tsx b/apps/web/src/app/admin/settings/deployment-settings-form.tsx new file mode 100644 index 0000000..109ff6c --- /dev/null +++ b/apps/web/src/app/admin/settings/deployment-settings-form.tsx @@ -0,0 +1,95 @@ +"use client"; + +import { useState } from "react"; +import { toast } from "sonner"; +import type { EventCreatePolicy } from "@album/contracts"; +import { api } from "@/trpc/react"; +import { Button } from "@/components/ui/button"; +import { Switch } from "@/components/ui/switch"; +import { Input } from "@/components/ui/input"; +import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; + +export function DeploymentSettingsForm({ + openSignup, + eventCreatePolicy, + defaultEventLimit, +}: { + openSignup: boolean; + eventCreatePolicy: EventCreatePolicy; + defaultEventLimit: number; +}) { + const [open, setOpen] = useState(openSignup); + const [policy, setPolicy] = useState(eventCreatePolicy); + const [limit, setLimit] = useState(String(defaultEventLimit)); + const update = api.platform.updateSettings.useMutation({ + onSuccess: () => toast.success("Settings saved"), + onError: (error) => toast.error(error.message), + }); + + return ( + + + Access + + Open signup is for accounts. Event creation can still require an invite + or an administrator. + + + +
{ + event.preventDefault(); + update.mutate({ + openSignup: open, + eventCreatePolicy: policy, + defaultEventLimit: Number(limit), + }); + }} + > + + + Open signups + + + + Event creation + + + + Default event limit for new groups + setLimit(event.target.value)} + /> + + + +
+
+
+ ); +} diff --git a/apps/web/src/app/admin/settings/page.tsx b/apps/web/src/app/admin/settings/page.tsx new file mode 100644 index 0000000..54f06b7 --- /dev/null +++ b/apps/web/src/app/admin/settings/page.tsx @@ -0,0 +1,33 @@ +import { redirect } from "next/navigation"; +import { headers } from "next/headers"; +import { auth } from "@/server/auth"; +import { getPlatformRole } from "@/server/roles"; +import { hasPlatformPermission } from "@/server/roles"; +import { PLATFORM_PERMISSIONS } from "@/server/permissions"; +import { createServerCaller } from "@/trpc/server"; +import { DeploymentSettingsForm } from "./deployment-settings-form"; + +export default async function AdminSettingsPage() { + const session = await auth.api.getSession({ headers: await headers() }); + const role = session ? await getPlatformRole(session.user.id) : null; + if (!hasPlatformPermission(role, PLATFORM_PERMISSIONS.SETTINGS_MANAGE)) { + redirect("/admin"); + } + const caller = await createServerCaller(); + const settings = await caller.platform.settings(); + return ( +
+
+

Deployment settings

+

+ Signup policy and event creation rules for this install. +

+
+ +
+ ); +} diff --git a/apps/web/src/app/api/auth/[...all]/route.ts b/apps/web/src/app/api/auth/[...all]/route.ts new file mode 100644 index 0000000..f8b0608 --- /dev/null +++ b/apps/web/src/app/api/auth/[...all]/route.ts @@ -0,0 +1,4 @@ +import { toNextJsHandler } from "better-auth/next-js"; +import { auth } from "@/server/auth"; + +export const { GET, POST } = toNextJsHandler(auth); diff --git a/apps/web/src/app/api/trpc/[trpc]/route.ts b/apps/web/src/app/api/trpc/[trpc]/route.ts new file mode 100644 index 0000000..64bc284 --- /dev/null +++ b/apps/web/src/app/api/trpc/[trpc]/route.ts @@ -0,0 +1,34 @@ +import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; +import { appRouter } from "@/server/api/root"; +import { createTRPCContext } from "@/server/api/trpc"; +import { publicAppOrigin } from "@/server/public-app-url"; + +async function handler(request: Request) { + let context: Awaited> | undefined; + const response = await fetchRequestHandler({ + endpoint: "/api/trpc", + req: request, + router: appRouter, + createContext: async () => { + context = await createTRPCContext({ + headers: request.headers, + requestOrigin: publicAppOrigin(request.url), + }); + return context; + }, + }); + if (context?.setCookies.length) { + const headers = new Headers(response.headers); + for (const cookie of context.setCookies) { + headers.append("Set-Cookie", cookie); + } + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers, + }); + } + return response; +} + +export { handler as GET, handler as POST }; diff --git a/apps/web/src/app/apple-icon.png b/apps/web/src/app/apple-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..82c0c7d4690e649812bfb52aa3f55089ec59e60d GIT binary patch literal 80022 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 literal 0 HcmV?d00001 diff --git a/apps/web/src/app/dashboard/create-event-dialog.tsx b/apps/web/src/app/dashboard/create-event-dialog.tsx new file mode 100644 index 0000000..908bca1 --- /dev/null +++ b/apps/web/src/app/dashboard/create-event-dialog.tsx @@ -0,0 +1,106 @@ +"use client"; + +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import { toast } from "sonner"; +import { api } from "@/trpc/react"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Spinner } from "@/components/ui/spinner"; + +export function CreateEventDialog() { + const router = useRouter(); + const [open, setOpen] = useState(false); + const [title, setTitle] = useState(""); + const [description, setDescription] = useState(""); + const [inviteCode, setInviteCode] = useState(""); + const createEvent = api.manager.createEvent.useMutation({ + onSuccess: (event) => { + toast.success("Event created"); + setOpen(false); + setTitle(""); + setDescription(""); + setInviteCode(""); + router.push(`/dashboard/events/${event.id}`); + router.refresh(); + }, + onError: (error) => { + toast.error(error.message); + }, + }); + + return ( + + + + + +
{ + event.preventDefault(); + createEvent.mutate({ + title, + description: description.trim() || undefined, + inviteCode: inviteCode.trim() || undefined, + }); + }} + > + + Create event + + Guests will use a shareable link after you publish it. + + + + + Title + setTitle(event.target.value)} + placeholder="Maya and Jonah" + /> + + + Description +