Initial commit: The Collective Hub planning documentation
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
# Development Implementation Plan
|
||||
|
||||
## How to Use This
|
||||
|
||||
This is a step-by-step build order for Phase 1 and beyond. Each step lists what to build, what it depends on, and how to verify it works.
|
||||
|
||||
**Follow the order.** Each step builds on the previous one. Don't skip ahead.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Foundation
|
||||
|
||||
### Step 1: Initialize SvelteKit Project
|
||||
|
||||
**What to do:**
|
||||
- Create a new SvelteKit project with `create-svelte` (choose TypeScript, ESLint, Prettier)
|
||||
- Set up the directory structure as outlined in the architecture plan
|
||||
- Configure `svelte.config.js` with the Node adapter for production builds
|
||||
- Create a basic `+page.svelte` that says "Hello World"
|
||||
- Create a `Dockerfile` for production builds
|
||||
|
||||
**Depends on:** Nothing.
|
||||
|
||||
**Verify:** `npm run dev` starts. Browser shows "Hello World". `docker build` succeeds.
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Set Up Postgres and Drizzle
|
||||
|
||||
**What to do:**
|
||||
- Install `drizzle-orm`, `drizzle-kit`, and `postgres` (the npm package)
|
||||
- Create `drizzle.config.ts`
|
||||
- Create `src/lib/server/db/index.ts` with the Postgres connection
|
||||
- Create `src/lib/server/db/schema.ts` with the `sites` table only (start simple)
|
||||
- Run `drizzle-kit push` to create the table in Postgres
|
||||
- Insert a test site row manually: `INSERT INTO sites (slug, name) VALUES ('test-site', 'Test Site');`
|
||||
|
||||
**Depends on:** Step 1. A running Postgres instance (local Docker or remote).
|
||||
|
||||
**Verify:** `drizzle-kit studio` shows the table. A simple script can query the test site.
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Create Core Tables
|
||||
|
||||
**What to do:**
|
||||
- Add all Phase 1 tables to `schema.ts`: `sites`, `users`, `memberships`, `siteSettings`
|
||||
- Define types, enums, indexes, and foreign keys
|
||||
- Run `drizzle-kit push` to create all tables
|
||||
- Optionally create a seed script for a test site
|
||||
|
||||
**Depends on:** Step 2.
|
||||
|
||||
**Verify:** All tables exist in the database. Relationships are correct in Drizzle Studio.
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Implement Site Resolver
|
||||
|
||||
**What to do:**
|
||||
- Create `src/lib/server/site-resolver.ts`
|
||||
- Export a function `getSiteBySlug(slug: string)` that queries the `sites` table and includes `siteSettings`
|
||||
- In `src/hooks.server.ts`, read `SITE_SLUG` from env, call the resolver, attach site to `locals`
|
||||
- Augment `app.d.ts` so `locals.site` is typed
|
||||
- In `src/routes/+layout.server.ts`, load site data from locals and return it
|
||||
- The homepage `+page.svelte` should display the site name
|
||||
|
||||
**Depends on:** Step 3.
|
||||
|
||||
**Verify:** Set `SITE_SLUG=test-site` in `.env`. Homepage shows "Test Site". Change the slug, restart, page breaks cleanly.
|
||||
|
||||
---
|
||||
|
||||
### Step 5: Set Up Better Auth with Discord
|
||||
|
||||
**What to do:**
|
||||
- Install Better Auth and follow its SvelteKit setup guide
|
||||
- Configure Discord OAuth provider
|
||||
- Create `src/lib/server/auth.ts` with the Better Auth configuration
|
||||
- Add the Better Auth API route: `src/routes/api/auth/[...betterAuth]/+server.ts`
|
||||
- Create a `/login` page with a "Login with Discord" button
|
||||
- On successful login, upsert the user into the `users` table
|
||||
|
||||
**Depends on:** Step 3 (users table must exist).
|
||||
|
||||
**Verify:** Visit `/login`, click the Discord button, authorize, get redirected back. User record appears in the `users` table.
|
||||
|
||||
---
|
||||
|
||||
### Step 6: Implement Owner Bootstrap
|
||||
|
||||
**What to do:**
|
||||
- In the auth callback or post-login hook, check if `user.discordId === process.env.OWNER_DISCORD_ID`
|
||||
- If match: upsert a membership row with role `owner` for the current site
|
||||
- In `src/hooks.server.ts`, after auth, load the user's membership for the current site and attach to `locals`
|
||||
- Create an admin auth guard: a `+layout.server.ts` in `/admin` that checks `locals.membership`
|
||||
|
||||
**Depends on:** Step 4 (site resolver), Step 5 (auth).
|
||||
|
||||
**Verify:** Log in with the Discord account matching `OWNER_DISCORD_ID`. Membership row with `owner` role appears. Navigate to `/admin` — accessible. Log in with a different Discord account — `/admin` redirects to `/login` with an error message.
|
||||
|
||||
---
|
||||
|
||||
### Step 7: Build Admin Layout
|
||||
|
||||
**What to do:**
|
||||
- Create `src/routes/admin/+layout.svelte` with sidebar navigation and top bar
|
||||
- Create `src/routes/admin/+layout.server.ts` with the auth guard
|
||||
- Create a minimal `src/routes/admin/+page.svelte` dashboard
|
||||
|
||||
**Depends on:** Step 6.
|
||||
|
||||
**Verify:** Owner logs in, sees admin layout with nav sidebar. Non-owner cannot access.
|
||||
|
||||
---
|
||||
|
||||
### Step 8: Build Admin Settings Page
|
||||
|
||||
**What to do:**
|
||||
- Create `src/routes/admin/settings/+page.svelte`
|
||||
- Form fields: site name, tagline
|
||||
- Load current values from `siteSettings.settings` JSON
|
||||
- Create a form action (or API endpoint) that updates the JSON
|
||||
- Add basic form validation and success/error feedback
|
||||
|
||||
**Depends on:** Step 7.
|
||||
|
||||
**Verify:** Owner can edit site name and tagline. Changes persist after reload. Public homepage reflects changes.
|
||||
|
||||
---
|
||||
|
||||
### Step 9: Build Public Homepage
|
||||
|
||||
**What to do:**
|
||||
- Update `src/routes/+page.server.ts` to load site settings as a flat object
|
||||
- Build `src/routes/+page.svelte` with sections:
|
||||
- Hero (site name, tagline, CTA button if configured)
|
||||
- About (text from settings if configured)
|
||||
- Apply basic CSS styling with CSS custom properties (hardcode dark theme defaults for now)
|
||||
- Handle empty states: sections hide when no content configured
|
||||
|
||||
**Depends on:** Step 4 (site resolver), Step 8 (settings exist).
|
||||
|
||||
**Verify:** Public homepage shows site name and content. Changing settings in admin updates the public page.
|
||||
|
||||
---
|
||||
|
||||
### Step 10: Add Theme CSS Variables
|
||||
|
||||
**What to do:**
|
||||
- In the root layout, generate CSS custom properties from theme settings
|
||||
- Fall back to sensible defaults if no theme configured
|
||||
- Apply variables to the public homepage
|
||||
- Admin panel can use a fixed theme (don't need the public theme in admin)
|
||||
|
||||
**Depends on:** Step 9.
|
||||
|
||||
**Verify:** Changing accent color in admin (once branding page is built) changes the public page's button color. Default theme looks clean.
|
||||
|
||||
---
|
||||
|
||||
### Step 11: Set Up CDN and Asset Upload
|
||||
|
||||
**What to do:**
|
||||
- Configure CDN storage client (Bunny CDN or S3-compatible)
|
||||
- Create `src/lib/server/cdn.ts` with upload, delete, and URL construction helpers
|
||||
- Create an image upload API endpoint: `src/routes/api/assets/+server.ts`
|
||||
- Implement webp conversion and optimization (using `sharp` or similar)
|
||||
- File validation: accepted types (PNG, JPEG, WebP input), max size
|
||||
- Auto-generate CDN keys: `sites/{siteSlug}/{type}/{uuid}.webp`
|
||||
- Create asset records in the `assets` table on successful upload
|
||||
- Create asset library page: `src/routes/admin/assets/+page.svelte`
|
||||
|
||||
**Depends on:** Step 4 (site resolver for siteSlug), Step 7 (admin layout).
|
||||
|
||||
**Verify:** Upload an image via admin. Asset record appears in database. File exists in CDN bucket. Asset library page shows uploaded files. Image is served correctly via CDN URL.
|
||||
|
||||
---
|
||||
|
||||
### Step 12: Set Up Migration Automation
|
||||
|
||||
**What to do:**
|
||||
- Add `RUN_MIGRATIONS` env var check in app startup
|
||||
- If `RUN_MIGRATIONS=true`, run `drizzle-kit migrate` on startup
|
||||
- If `RUN_MIGRATIONS=false` (or unset), skip migrations entirely
|
||||
- Log migration status on startup for observability
|
||||
- Document which deployment is the migration runner
|
||||
|
||||
**Depends on:** Step 2 (Drizzle configured).
|
||||
|
||||
**Verify:** Start app with `RUN_MIGRATIONS=true` — migrations run. Start with `RUN_MIGRATIONS=false` — migrations are skipped. Both deployments work against the same database.
|
||||
|
||||
---
|
||||
|
||||
### Step 13: Implement Super Admin Access
|
||||
|
||||
**What to do:**
|
||||
- Parse `SUPER_ADMIN_DISCORD_IDS` env var (comma-separated) on startup
|
||||
- In the auth hook / membership check, compare user's Discord ID against the super admin list
|
||||
- If match: bypass site-scoped membership checks, grant full admin access
|
||||
- Attach super admin flag to `locals` for conditional UI (e.g., "View All Sites" link)
|
||||
|
||||
**Depends on:** Step 6 (owner bootstrap / membership logic).
|
||||
|
||||
**Verify:** Log in with a Discord ID in `SUPER_ADMIN_DISCORD_IDS`. Access any site's admin panel. Log in with a non-super-admin ID — restricted to their own site.
|
||||
|
||||
---
|
||||
|
||||
### Step 14: Admin Branding Page
|
||||
|
||||
**What to do:**
|
||||
- Create `src/routes/admin/branding/+page.svelte`
|
||||
- Color pickers for accent, background, text colors
|
||||
- Theme preset dropdown (Dark, Light, Custom)
|
||||
- Logo and background selectors: pick from asset library (uploaded in Step 11)
|
||||
|
||||
**Depends on:** Step 8 (settings save pattern), Step 11 (asset library).
|
||||
|
||||
**Verify:** Owner can change colors. Public page reflects changes immediately. Logo and background can be selected from uploaded assets.
|
||||
|
||||
---
|
||||
|
||||
### Step 15: Dockerize and Deploy
|
||||
|
||||
**What to do:**
|
||||
- Finalize Dockerfile (multi-stage build for Node)
|
||||
- Create `docker-compose.yml` for local testing with Postgres
|
||||
- Set up first Coolify deployment (designate as migration runner: `RUN_MIGRATIONS=true`)
|
||||
- Configure all environment variables in Coolify
|
||||
- Deploy and verify public site is accessible
|
||||
|
||||
**Depends on:** Step 9 (working public site), Step 12 (migration automation).
|
||||
|
||||
**Verify:** Site is live at the configured URL. Login works. Admin works. Migrations ran on startup.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 Onward
|
||||
|
||||
Once Phase 1 is fully working, continue with:
|
||||
|
||||
1. Nav links + social links admin pages and public rendering
|
||||
2. Homepage content editor (hero title, subtitle, about text, CTA)
|
||||
3. Events: schema, admin CRUD, public render
|
||||
4. Super admin dashboard (Phase 4)
|
||||
5. Role management (Phase 5)
|
||||
|
||||
Detailed steps for Phases 2-5 should be written once Phase 1 is complete and any lessons learned are incorporated.
|
||||
|
||||
---
|
||||
|
||||
## Development Rules
|
||||
|
||||
- **One migration per schema change.** Don't batch unrelated changes into one migration.
|
||||
- **Test with multiple SITE_SLUG values locally.** Use different `.env` files or a script that switches them.
|
||||
- **Commit often.** Small, focused commits are easier to reason about.
|
||||
- **Migrations run automatically on startup, gated by `RUN_MIGRATIONS`.** Only one deployment runs them. All others skip.
|
||||
- **Keep the public site SSR-only.** No client-side data fetching for public pages.
|
||||
- **Admin pages can use client-side interactivity.** Forms, file uploads, etc. can use Svelte's client-side features.
|
||||
- **All uploaded images are converted to webp.** No raw user files stored on CDN.
|
||||
Reference in New Issue
Block a user