diff --git a/skill/erika/SKILL.md b/skill/erika/SKILL.md index 27984d6..bf3b72e 100644 --- a/skill/erika/SKILL.md +++ b/skill/erika/SKILL.md @@ -90,21 +90,37 @@ Strong success criteria let you loop independently. Weak criteria ("make it work - Do not pass code strings directly to execution tools (e.g., `bun -e 'console.log("hello world")'`). - Always write scripts to dedicated files before executing them. -## 9. Database +## 9. Commit Messages + +**Use Conventional Commits with a scope.** + +When committing changes, use the format: +``` +type(scope): short imperative summary +``` + +Examples: +- `feat(form): add submission link webhook placeholder` +- `fix(auth): reject missing discord ids` +- `test(webhook): cover update template preview` + +Prefer common types such as `feat`, `fix`, `test`, `docs`, `refactor`, `chore`, and `style`. Use a concise scope that names the affected area, such as `form`, `auth`, `webhook`, `leaderboard`, or `ui`. + +## 10. Database **Use standard tools for external databases.** - Assume the use of an external database for persistent storage. - Use Drizzle ORM exclusively for database interactions. -## 10. Explicit Approval for Plans +## 11. Explicit Approval for Plans **Achieve alignment before execution.** - Ensure the implementation plan is completely clear and mutually understood. - Do not begin executing the plan until explicit approval (e.g., "let's do it") is given by the user. -## 11. Communication Style +## 12. Communication Style **Be concise and professional. No fluff.** @@ -114,204 +130,16 @@ Strong success criteria let you loop independently. Weak criteria ("make it work --- -## Project Overview +## Reference Files -Erika is a Next.js website for the content creator "Erika!". It has two main features: +Load only the reference files relevant to the task: -1. **Linktree** (`/`) — A landing page showing profile info, social links, and live follower counts from YouTube, Roblox, Discord, and TikTok. -2. **Custom Form System** (`/form`, `/admin/form`) — A full form builder where admins create forms with various question types, and users submit responses via Discord OAuth login. - -### Tech Stack - -- **Runtime**: Bun -- **Framework**: Next.js (App Router) -- **Database**: PostgreSQL via Drizzle ORM -- **Cache**: Redis (Bun's built-in `RedisClient`) -- **Auth**: NextAuth.js with Discord OAuth provider -- **UI**: ShadCN components + Tailwind CSS -- **Fonts**: Anuphan (Thai), Geist Sans (Latin) -- **Deployment**: Docker Compose + Nginx reverse proxy - ---- - -## Environment Variables (`.env`) - -| Variable | Purpose | +| Task Area | Reference | |---|---| -| `DATABASE_URL` | PostgreSQL connection string | -| `NEXTAUTH_SECRET` | Secret for signing NextAuth sessions | -| `NEXTAUTH_URL` | Canonical URL of the site (e.g. `https://erika.sudloh.com`) | -| `REDIS_URL` | Redis connection string, used for caching follower counts and tracking unique visitors | -| `BASE_URL` | Public base URL, used in metadata/SEO | -| `DISCORD_CLIENT_ID` / `DISCORD_CLIENT_SECRET` | Discord OAuth app credentials for user login | -| `ADMIN_DISCORD_IDS` | **Comma-separated list of Discord user IDs that have admin access.** These users can access `/admin/form` to create, edit, and delete forms. Any Discord user NOT in this list is treated as a regular user. | -| `CRON_SECRET` | Secret key to authorize the automated `/api/cron/tiktok` polling endpoint | -| `DISCORD_BOT_TOKEN` | Bot token used to fetch Discord server member count | -| `DISCORD_GUILD_ID` | Discord server ID to fetch member count from | -| `YOUTUBE_API_KEY` / `YOUTUBE_CHANNEL_ID` | YouTube Data API credentials to fetch subscriber count | -| `ROBLOX_USER_ID` | Roblox user ID to fetch follower count | -| `TIKTOK_CLIENT_KEY` / `TIKTOK_CLIENT_SECRET` | TikTok Official API credentials | -| `TIKTOK_ACCESS_TOKEN` / `TIKTOK_REFRESH_TOKEN` / `TIKTOK_OPEN_ID` | TikTok OAuth tokens (obtained via one-time flow at `/api/auth/tiktok`) | +| Project purpose, stack, and environment variables | `references/project.md` | +| Shared libraries, follower fetchers, and database schema files | `references/libraries-and-data.md` | +| App Router routes, server actions, and API endpoints | `references/routes-and-actions.md` | +| UI components, config, Docker, and infrastructure files | `references/components-and-infra.md` | +| Auth model, access control, and performance-sensitive behavior | `references/behavior.md` | ---- - -## File & Directory Reference - -### `lib/` — Shared Libraries - -| File | Purpose | -|---|---| -| `lib/config.ts` | **Linktree configuration for the root page (`/`).** Defines the profile (name, description, avatar) and the list of social links (Roblox, Discord, TikTok, YouTube) displayed on the landing page. Also defines `config_p2` which adds the Form link. This does NOT configure the form system. | -| `lib/icons.tsx` | SVG icon components for social platforms (Roblox, Discord, TikTok, YouTube, Form) | -| `lib/redis.ts` | Redis client singleton using Bun's built-in `RedisClient`. Used for caching follower counts (1hr TTL) and tracking unique visitors. | -| `lib/auth.ts` | Shared session, admin, and form role-access authorization helpers | -| `lib/discord.ts` | Shared Discord API utilities for guild roles/member profiles with in-memory TTL caching | -| `lib/sanitize-html.ts` | Sanitizes admin-authored rich text before storage/rendering | -| `lib/utils.ts` | Utility functions (e.g. `cn()` for className merging) | - -### `lib/followers/` — Follower Count Fetchers - -| File | Purpose | -|---|---| -| `lib/followers/index.ts` | Aggregates all platform follower counts with Redis caching (1hr TTL). Exports `getFollowerCounts()` used by the root page. | -| `lib/followers/discord.ts` | Fetches Discord server member count via bot API | -| `lib/followers/youtube.ts` | Fetches YouTube subscriber count via YouTube Data API | -| `lib/followers/roblox.ts` | Fetches Roblox follower count via public API | -| `lib/followers/tiktok.ts` | Fetches TikTok follower count and profile via TikTok Official API. Auto-refreshes expired tokens. | - -### `db/` — Database Layer - -| File | Purpose | -|---|---| -| `db/index.ts` | Drizzle ORM client initialization, exports `db` | -| `db/schema.ts` | Barrel file re-exporting all schemas | -| `db/schema/account.ts` | NextAuth account tables (`user`, `account`, `session`, `verificationToken`) in the `account` PostgreSQL schema. Users have a `discordId` field. | -| `db/schema/form.ts` | Form system tables in the `form` PostgreSQL schema: `forms` (form metadata, webhook config, Discord role access control), `questions` (question definitions with types: text/textarea/radio/checkbox), `submissions` (user submissions with edit history), `answers` (individual answers) | - -### `app/actions/` — Server Actions - -| File | Purpose | -|---|---| -| `app/actions/discord.ts` | Admin-protected server action for loading Discord server roles in the form settings UI | - -### `app/` — Routes - -#### Root & Global - -| File | Purpose | -|---|---| -| `app/layout.tsx` | Root layout. Sets up fonts (Anuphan + Geist), theme provider, tooltip provider, toaster, page progress bar, and SEO metadata. | -| `app/page.tsx` | **Linktree landing page (`/`).** Displays profile avatar (from TikTok API or fallback), social links with live follower counts, unique visitor counter, and a video background. | -| `app/globals.css` | Global CSS styles | - -#### `/form` — Public Form System (user-facing) - -| File | Purpose | -|---|---| -| `app/form/page.tsx` | Lists all **open** forms for users to fill out | -| `app/form/layout.tsx` | Form section layout with header, login state, and theme toggle. Shows a login dialog if the user is not authenticated. | -| `app/form/client.tsx` | Client-side form components (user avatar dropdown, sign out, etc.) | -| `app/form/[id]/page.tsx` | Individual form submission page where users answer questions. Enforces Discord role access control (redirects to not-found if unauthorized). | -| `app/form/[id]/layout.tsx` | Layout for individual form pages | - -#### `/admin` — Admin Panel (admin-only) - -| File | Purpose | -|---|---| -| `app/admin/layout.tsx` | **Centralized admin auth guard.** Checks `getServerSession` against `ADMIN_DISCORD_IDS` env var for ALL `/admin/*` routes. Redirects non-admins to `/`. | -| `app/admin/page.tsx` | **Admin dashboard homepage (`/admin`).** Overview cards linking to Form and Backend sections. Wrapped with `AdminShell` sidebar. | - -#### `/admin/form` — Admin Form Management - -| File | Purpose | -|---|---| -| `app/admin/form/page.tsx` | **Admin form list.** Shows all forms (open and closed) with create/edit/delete actions. Wrapped with `AdminShell` sidebar. | -| `app/admin/form/actions.ts` | Server actions for form CRUD (create, update settings, delete). All protected by admin auth check. | -| `app/admin/form/[id]/layout.tsx` | **Admin form editor layout.** Has its own form-specific sidebar (Editor/Responses/Extra). The admin-level sidebar is NOT shown here. | -| `app/admin/form/[id]/page.tsx` | Admin form editor page | -| `app/admin/form/[id]/client.tsx` | Client-side admin form editor components (question builder, drag-and-drop, settings) | -| `app/admin/form/[id]/result/` | Admin view of form submission results | -| `app/admin/form/[id]/extra/` | Additional admin form settings (Discord webhook templates, Role-based Access Control via Shadcn DropdownMenu) | - -#### `/admin/backend` — Backend Settings - -| File | Purpose | -|---|---| -| `app/admin/backend/page.tsx` | **Backend settings page (`/admin/backend`).** Auto-discovers platform files from `lib/followers/` using `fs.readdir`. Reads cached counts from Redis. Wrapped with `AdminShell` sidebar. | -| `app/admin/backend/client.tsx` | Client component rendering platform cards with cached counts and individual refetch buttons. | -| `app/admin/backend/actions.ts` | Server action `refetchPlatform()` — dynamically imports the platform module, calls the fetcher, and updates Redis cache per-platform. Adding a new `.ts` file to `lib/followers/` auto-registers it here. | - -#### `/discord` — Discord Redirect - -| File | Purpose | -|---|---| -| `app/discord/page.tsx` | Simple redirect to the Discord server invite link | - -#### `/api` — API Routes - -| File | Purpose | -|---|---| -| `app/api/auth/[...nextauth]/route.ts` | NextAuth.js configuration with Discord OAuth provider. Exports `authOptions`. | -| `app/api/auth/tiktok/route.ts` | Initiates TikTok OAuth flow (one-time setup to get tokens) | -| `app/api/auth/tiktok/callback/route.ts` | TikTok OAuth callback handler | -| `app/api/cron/tiktok/route.ts` | TikTok polling endpoint. Checks TikWM for new organic videos and notifies Discord. Triggered every 5 mins by the `erika-cron` Docker container. | -| `app/api/upload/route.ts` | File upload endpoint (admin-only, checks `ADMIN_DISCORD_IDS`) | - -#### `app/actions/` — Server Actions - -| File | Purpose | -|---|---| -| `app/actions/questions.ts` | Server actions for question CRUD within a form. Admin-protected. | -| `app/actions/submissions.ts` | Server actions for form submissions (submit, edit, view results). | - -### `components/` — UI Components - -| File | Purpose | -|---|---| -| `components/admin-shell.tsx` | **Shared admin sidebar layout.** Client component with `SidebarProvider` + `Sidebar` (Dashboard, Form, Backend links). Used by `/admin`, `/admin/form`, `/admin/backend`. NOT used by `/admin/form/[id]` which has its own form-specific sidebar. | -| `components/LinkCard.tsx` | Social link card component with follower count badges | -| `components/ShaderBackground.tsx` | WebGL shader background effect | -| `components/delete-buttons.tsx` | Delete confirmation dialogs for forms, submissions, and bulk delete. Uses AlertDialog/Dialog from ShadCN. | -| `components/login-dialog.tsx` | Discord OAuth login dialog | -| `components/rich-text-editor.tsx` | Rich text editor for form descriptions | -| `components/page-progress.tsx` | Page navigation progress bar | -| `components/theme-toggle.tsx` | Light/dark theme toggle | -| `components/theme-provider.tsx` | next-themes provider wrapper | -| `components/ui/` | ShadCN component library (button, card, table, badge, sidebar, etc.) | - -### Config & Infrastructure - -| File | Purpose | -|---|---| -| `drizzle.config.ts` | Drizzle Kit config — points to schema, outputs to `drizzle/`, uses schemas: `public`, `account`, `form` | -| `docker-compose.yml` | Runs the app, Nginx reverse proxy, and a self-pinging `erika-cron` container via Docker Compose with `network_mode: host` | -| `proxy.ts` | Replaces `middleware.ts` in modern Next.js. Currently a pass-through for `/form/:path*` routes — auth is handled via dialog in the layout, not here. | -| `nginx.conf` | Nginx config for reverse proxying to the Next.js app | -| `next.config.ts` | Next.js configuration | -| `Dockerfile` | Container build config | - ---- - -## Auth Model - -1. **User login**: Discord OAuth via NextAuth.js. Users log in at `/form` to submit forms. -2. **Admin check**: Server-side only. Compares the logged-in user's `discordId` (from session) against the comma-separated `ADMIN_DISCORD_IDS` env var. The primary guard is **centralized** in `app/admin/layout.tsx` which protects all `/admin/*` routes. Additional per-action checks exist in: - - `app/admin/form/actions.ts` (form CRUD actions) - - `app/actions/questions.ts` (question CRUD actions) - - `app/actions/submissions.ts` (admin submission deletion actions) - - `app/api/upload/route.ts` (file uploads) -3. **Form role access check**: `lib/auth.ts` contains the shared role-access check. Public form pages use it before rendering restricted forms, and `submitForm()` repeats the same check before mutating data. -4. **No middleware.ts**: Modern Next.js replaces `middleware.ts` with `proxy.ts`. The project has a `proxy.ts` that matches `/form/:path*` but currently just passes through. Auth checks are done per-page/per-action in server components and server actions. - ---- - -## Optimizations & Performance - -The following optimizations have been implemented to keep CPU and RAM usage extremely low while maintaining speed. **Do not remove or bypass these without good reason:** - -1. **DB Connection Pooling**: `db/index.ts` uses a `globalThis` singleton to prevent connection leaks during dev hot-reloads, with a strict max pool size (`max: 5`) to reduce idle RAM. -2. **Session Caching**: The NextAuth session callback uses an in-memory `Map` with a TTL to cache the `discordId` lookup. This prevents a DB hit on *every single request* that checks the session. -3. **Discord API Caching**: `lib/discord.ts` aggressively caches Discord API responses (roles, profiles) in memory with a 60s TTL. This avoids massive delays (30s-50s) on admin pages caused by redundant external API requests. -4. **Redis Batching**: The unique visitor tracking (`app/page.tsx`) only runs the `scard` (count) command if `sadd` (add IP) actually added a new IP. -5. **Single Docker Replica**: The project runs on a **single replica** (`replicas: 1` in docker-compose.yml). Given Bun's performance, 1 replica is sufficient and drastically reduces the total RAM footprint compared to running 3 replicas. -6. **Nginx Streaming**: Nginx must have `proxy_buffering off;` (or specifically disabled for the Next.js upstream) so that Next.js App Router streaming (React Server Components and Server Actions) works without hanging or causing 504 timeouts. +Keep detailed project maps in these references rather than expanding `SKILL.md`. diff --git a/skill/erika/references/behavior.md b/skill/erika/references/behavior.md new file mode 100644 index 0000000..6a66ad1 --- /dev/null +++ b/skill/erika/references/behavior.md @@ -0,0 +1,21 @@ +# Behavior Notes + +## Auth Model + +1. **User login**: Discord OAuth via NextAuth.js. Users log in at `/form` to submit forms. +2. **Admin check**: Server-side only. The logged-in user's `discordId` is compared against `ADMIN_DISCORD_IDS`. +3. **Centralized admin guard**: `app/admin/layout.tsx` protects all `/admin/*` routes. +4. **Action-level admin checks**: Important mutations also check authorization in `app/admin/form/actions.ts`, `app/actions/questions.ts`, `app/actions/submissions.ts`, and `app/api/upload/route.ts`. +5. **Form role access**: `lib/auth.ts` handles role access. Public form pages check before rendering restricted forms, and `submitForm()` checks again before mutation. +6. **No `middleware.ts`**: Next.js uses `proxy.ts`; auth checks live in server components and server actions. + +## Performance-Sensitive Behavior + +Do not remove or bypass these without good reason: + +1. `db/index.ts` uses a `globalThis` singleton and small pool size to prevent dev hot-reload connection leaks and reduce idle RAM. +2. The NextAuth session callback caches `discordId` lookups in memory with a TTL. +3. `lib/discord.ts` caches Discord API roles/profiles in memory for 60 seconds to avoid slow repeated external calls. +4. `app/page.tsx` batches Redis unique visitor tracking and only counts when `sadd` adds a new IP. +5. Docker Compose runs a single app replica; this is intentional for low RAM usage. +6. Nginx must disable proxy buffering for the Next.js upstream so App Router streaming and Server Actions do not hang. diff --git a/skill/erika/references/components-and-infra.md b/skill/erika/references/components-and-infra.md new file mode 100644 index 0000000..f5af75a --- /dev/null +++ b/skill/erika/references/components-and-infra.md @@ -0,0 +1,27 @@ +# Components And Infrastructure + +## Components + +| File | Purpose | +|---|---| +| `components/admin-shell.tsx` | Shared admin sidebar layout for `/admin`, `/admin/form`, and `/admin/backend`; not used by `/admin/form/[id]` | +| `components/LinkCard.tsx` | Social link card with follower count badges | +| `components/space-background.tsx` | Space/video-style visual background | +| `components/delete-buttons.tsx` | Delete confirmation dialogs for forms, submissions, and bulk deletion | +| `components/login-dialog.tsx` | Discord OAuth login dialog | +| `components/rich-text-editor.tsx` | Rich text editor for form descriptions | +| `components/page-progress.tsx` | Page navigation progress bar | +| `components/theme-toggle.tsx` | Light/dark theme toggle | +| `components/theme-provider.tsx` | `next-themes` provider wrapper | +| `components/ui/` | ShadCN component library | + +## Config And Infrastructure + +| File | Purpose | +|---|---| +| `drizzle.config.ts` | Drizzle Kit config for schemas and migration output | +| `docker-compose.yml` | App, Nginx reverse proxy, and self-pinging cron service | +| `proxy.ts` | Modern Next.js replacement for `middleware.ts`; currently passes through `/form/:path*` | +| `nginx.conf` | Nginx reverse proxy config | +| `next.config.ts` | Next.js configuration | +| `Dockerfile` | Container build config | diff --git a/skill/erika/references/libraries-and-data.md b/skill/erika/references/libraries-and-data.md new file mode 100644 index 0000000..e2564a7 --- /dev/null +++ b/skill/erika/references/libraries-and-data.md @@ -0,0 +1,32 @@ +# Libraries And Data + +## `lib/` Shared Libraries + +| File | Purpose | +|---|---| +| `lib/config.ts` | Linktree config for `/`: profile, social links, and `config_p2` with Form link. This does not configure the form system. | +| `lib/icons.tsx` | SVG icon components for social platforms and Form | +| `lib/redis.ts` | Redis singleton using Bun `RedisClient`; used for follower counts and unique visitors | +| `lib/auth.ts` | Shared session, admin, and form role-access authorization helpers | +| `lib/discord.ts` | Discord API utilities for guild roles/member profiles with in-memory TTL caching | +| `lib/sanitize-html.ts` | Sanitizes admin-authored rich text before storage/rendering | +| `lib/utils.ts` | Utility helpers such as `cn()` | + +## `lib/followers/` Fetchers + +| File | Purpose | +|---|---| +| `lib/followers/index.ts` | Aggregates all platform follower counts with Redis caching; exports `getFollowerCounts()` | +| `lib/followers/discord.ts` | Fetches Discord server member count via bot API | +| `lib/followers/youtube.ts` | Fetches YouTube subscriber count via YouTube Data API | +| `lib/followers/roblox.ts` | Fetches Roblox follower count via public API | +| `lib/followers/tiktok.ts` | Fetches TikTok follower count/profile and refreshes expired tokens | + +## `db/` Database Layer + +| File | Purpose | +|---|---| +| `db/index.ts` | Drizzle ORM client initialization; exports `db` | +| `db/schema.ts` | Barrel file re-exporting all schemas | +| `db/schema/account.ts` | NextAuth account tables in the `account` PostgreSQL schema; users have `discordId` | +| `db/schema/form.ts` | Form schema: forms, questions, submissions, and answers in the `form` PostgreSQL schema | diff --git a/skill/erika/references/project.md b/skill/erika/references/project.md new file mode 100644 index 0000000..a9ac99c --- /dev/null +++ b/skill/erika/references/project.md @@ -0,0 +1,36 @@ +# Erika Project + +Erika is a Next.js website for the content creator "Erika!". It has two main features: + +1. **Linktree** (`/`) - A landing page showing profile info, social links, and live follower counts from YouTube, Roblox, Discord, and TikTok. +2. **Custom Form System** (`/form`, `/admin/form`) - A full form builder where admins create forms with various question types, and users submit responses via Discord OAuth login. + +## Tech Stack + +- **Runtime**: Bun +- **Framework**: Next.js App Router +- **Database**: PostgreSQL via Drizzle ORM +- **Cache**: Redis using Bun's built-in `RedisClient` +- **Auth**: NextAuth.js with Discord OAuth provider +- **UI**: ShadCN components + Tailwind CSS +- **Fonts**: Anuphan for Thai, Geist Sans for Latin +- **Deployment**: Docker Compose + Nginx reverse proxy + +## Environment Variables + +| Variable | Purpose | +|---|---| +| `DATABASE_URL` | PostgreSQL connection string | +| `NEXTAUTH_SECRET` | Secret for signing NextAuth sessions | +| `NEXTAUTH_URL` | Canonical URL of the site, such as `https://erika.sudloh.com` | +| `REDIS_URL` | Redis connection string, used for follower-count caching and unique visitors | +| `BASE_URL` | Public base URL, used in metadata/SEO and generated links | +| `DISCORD_CLIENT_ID` / `DISCORD_CLIENT_SECRET` | Discord OAuth app credentials for user login | +| `ADMIN_DISCORD_IDS` | Comma-separated Discord user IDs with admin access | +| `CRON_SECRET` | Secret for the automated `/api/cron/tiktok` polling endpoint | +| `DISCORD_BOT_TOKEN` | Bot token used to fetch Discord server member count | +| `DISCORD_GUILD_ID` | Discord server ID to fetch member count from | +| `YOUTUBE_API_KEY` / `YOUTUBE_CHANNEL_ID` | YouTube Data API credentials for subscriber count | +| `ROBLOX_USER_ID` | Roblox user ID to fetch follower count | +| `TIKTOK_CLIENT_KEY` / `TIKTOK_CLIENT_SECRET` | TikTok Official API credentials | +| `TIKTOK_ACCESS_TOKEN` / `TIKTOK_REFRESH_TOKEN` / `TIKTOK_OPEN_ID` | TikTok OAuth tokens from one-time setup at `/api/auth/tiktok` | diff --git a/skill/erika/references/routes-and-actions.md b/skill/erika/references/routes-and-actions.md new file mode 100644 index 0000000..69af49a --- /dev/null +++ b/skill/erika/references/routes-and-actions.md @@ -0,0 +1,70 @@ +# Routes And Actions + +## Root And Global + +| File | Purpose | +|---|---| +| `app/layout.tsx` | Root layout with fonts, theme provider, tooltip provider, toaster, page progress, and SEO metadata | +| `app/page.tsx` | Linktree landing page with avatar, social links, follower counts, unique visitor count, and video background | +| `app/globals.css` | Global CSS styles | + +## `/form` User-Facing Forms + +| File | Purpose | +|---|---| +| `app/form/page.tsx` | Lists open forms for users | +| `app/form/layout.tsx` | Form layout with header, login state, theme toggle, and login dialog | +| `app/form/client.tsx` | Client-side form components and submission UX | +| `app/form/[id]/page.tsx` | Individual submission page; enforces Discord role access | +| `app/form/[id]/layout.tsx` | Layout for individual form pages | + +## `/admin` Admin Panel + +| File | Purpose | +|---|---| +| `app/admin/layout.tsx` | Centralized admin guard for all `/admin/*` routes | +| `app/admin/page.tsx` | Admin dashboard homepage with Form and Backend cards | + +## `/admin/form` Form Management + +| File | Purpose | +|---|---| +| `app/admin/form/page.tsx` | Admin form list with create/edit/delete actions | +| `app/admin/form/actions.ts` | Admin-protected form CRUD and settings actions | +| `app/admin/form/[id]/layout.tsx` | Form editor layout with form-specific sidebar | +| `app/admin/form/[id]/page.tsx` | Admin form editor page | +| `app/admin/form/[id]/client.tsx` | Client-side question builder, drag-and-drop, and settings | +| `app/admin/form/[id]/result/` | Admin form submission results | +| `app/admin/form/[id]/extra/` | Discord webhook templates and role-based access control settings | + +## `/admin/backend` Backend Settings + +| File | Purpose | +|---|---| +| `app/admin/backend/page.tsx` | Backend settings page; auto-discovers `lib/followers/` platform files and reads cached counts | +| `app/admin/backend/client.tsx` | Platform cards with cached counts and refetch buttons | +| `app/admin/backend/actions.ts` | `refetchPlatform()` dynamically imports fetchers and updates Redis cache | + +## `/discord` + +| File | Purpose | +|---|---| +| `app/discord/page.tsx` | Redirects to the Discord server invite | + +## `/api` + +| File | Purpose | +|---|---| +| `app/api/auth/[...nextauth]/route.ts` | NextAuth.js route with Discord OAuth provider | +| `app/api/auth/tiktok/route.ts` | Starts TikTok OAuth setup | +| `app/api/auth/tiktok/callback/route.ts` | TikTok OAuth callback handler | +| `app/api/cron/tiktok/route.ts` | TikTok polling endpoint; checks TikWM and notifies Discord | +| `app/api/upload/route.ts` | Admin-only file upload endpoint | + +## `app/actions/` Server Actions + +| File | Purpose | +|---|---| +| `app/actions/discord.ts` | Admin-protected Discord role loading | +| `app/actions/questions.ts` | Admin-protected question CRUD within a form | +| `app/actions/submissions.ts` | Form submission, update, deletion, and webhook notification actions |