SPEC_LWOPAN_MIGRATION.md
Lwopan migration into apps.siao.ai — spec
⚠️ Built, and partly superseded by
SPEC_ACCOUNTS.md. The migration shipped. But everything here about accounts is now wrong: user stories 1–4 (register with email/password, a password for this site, magic-login links) and 21 (leave NextAuth's session as-is) describe features that were built and have since been removed. AnswerHub has no credential store — identity comes fromaccounts.siao.ai, and itsUserrow is a profile keyed bylogtoSub. The Prisma models named below for accounts (Account,Session,VerificationToken,TwoFactorToken,TwoFactorConfirmation) no longer exist. Everything about submissions, blog, comments, likes and admin moderation still holds.
Status: built (Lwopan migration complete; later renamed to AnswerHub).
Originally drafted from a /grill-me session. Lives in the
apps-siao-ai repo (~/siao-ai/apps-siao-ai/), which currently has a
working nav homepage (see SPEC_APPS.md) with a lwopan card linking
to /lwopan (currently 404s). No issue tracker exists yet for this
project — this file is the spec until one does. Full decision trail:
NOTES_APPS.md ch.4 (grill session), source app explored at
~/Lwopan/learning-platform.
Naming note: "Lwopan" (羅盤) is a placeholder — the app will be renamed later, deliberately deferred so it doesn't block this work. This spec uses "Lwopan"/"lwopan" throughout to match the current code and directory names; treat every occurrence as "whatever it ends up being called."
Problem Statement
~/Lwopan/learning-platform is a fully-featured, never-deployed
Next.js learning platform (帳號系統, 作業分享/submissions, 技術博客/blog,
知識庫/knowledge base, admin review) sitting outside the siao.ai
project family as its own standalone repo with its own Tailwind design
system built around a Liquid Glass aesthetic the owner has since
decided to abandon. It isn't reachable at any siao.ai subdomain, has
no path into the apps.siao.ai nav homepage's /lwopan card (which
currently 404s by design), and its frontend doesn't match the direction
the owner wants going forward.
Solution
Merge learning-platform's backend logic — Prisma schema, NextAuth
setup, API routes, business logic — into the apps-siao-ai Next.js app
as-is, under a /lwopan route namespace (pages and API routes both
prefixed, e.g. app/lwopan/..., app/lwopan/api/...), so future apps
mounted the same way don't collide on route names. Rebuild the frontend
from scratch against this backend: Liquid Glass is dropped entirely,
and the migration is the trigger for apps-siao-ai adopting Tailwind
CSS + Radix UI (overturning the nav homepage's earlier "plain CSS, no
Tailwind" decision — see Implementation Decisions). Ship this as one
single migration covering every existing feature area, not a phased
rollout, with the same high testing bar the nav homepage got — but stop
at "builds and tests pass locally"; going live on apps.siao.ai is a
separately-decided, deliberately deferred step (same posture the owner
already took for the nav homepage itself, see NOTES_APPS.md ch.2–3).
User Stories
Account system (foundation for everything else)
- As a visitor, I want to register an account with email/password, so that I can participate in the platform.
- As a visitor, I want to sign in with Google, so that I don't need a separate password for this site.
- As a user, I want to receive and use a magic-login email link, so that I can sign in without typing a password.
- As a user, I want email verification before my account is fully active, so that the platform can trust the email I registered with.
- As a user, I want to optionally enable two-factor authentication, so that my account has an extra layer of protection.
- As a user, I want to edit my profile (avatar, bio, social links), so that other users can learn about me.
- As a user, I want to upload a profile avatar image, so that my profile isn't a blank placeholder.
- As a user, I want to view another user's public profile, so that I can see their submissions and posts.
- As an admin, I want a role-based permission system (
USERvs admin roles), so that moderation actions are restricted to the right people. - As an admin, I want to ban a user account, so that abusive accounts stop being able to upload content.
Submissions (作業分享)
- As a user, I want to upload a submission (an assignment/solution), so that I can share it with other learners.
- As a user, I want to bulk-upload multiple submissions at once, so that I don't have to repeat the single-upload flow for each one.
- As a visitor, I want to browse and open individual submissions by their slug, so that I can read the content.
- As a user, I want to like and comment on a submission, so that I can engage with other users' work.
- As an admin, I want to review submissions before or after they're public, so that I can moderate content quality.
Blog (技術博客)
- As a visitor, I want to read blog posts written in MDX with syntax highlighting and math rendering, so that technical content displays correctly.
- As a user (author), I want to write and publish blog posts, so that I can share technical knowledge.
Knowledge base (知識庫)
- As a visitor, I want to browse the knowledge base's structured content, so that I can learn systematically rather than searching ad hoc.
Search
- As a visitor, I want to search across submissions/posts/knowledge content, so that I can find relevant material quickly. (Search backend — Algolia — is not configured this round; the UI and query logic port over, but live search results depend on a future session wiring up real Algolia credentials.)
Platform integration
- As the owner, I want
/lwopan's pages and API routes fully namespaced under that prefix, so that a second app mounted ontoapps.siao.ailater can't collide with Lwopan's routes. - As the owner, I want Lwopan's NextAuth session left exactly as it
is today (its own session/cookie, not integrated with any other
siao.ailogin), so that a future platform-wide shared-login design isn't accidentally foreclosed or half-implemented by this migration. - As the owner, I want the whole migration to build and pass its test
suite locally, so that I know the port is correct — without it
needing to be live on
apps.siao.aiyet. - As the owner, I want uploaded files (avatars, submission uploads)
to keep working exactly as they do today (local disk under
public/uploads/), so that this migration doesn't quietly break a working feature by trying to also swap storage backends in the same pass.
Implementation Decisions
- Merge, not proxy.
learning-platform's code becomes part of theapps-siao-aiNext.js app and deploy — one process, one build, same posture as the nav homepage's original "single app, internal routes" architecture (SPEC_APPS.md). No separate container, no reverse proxy to a standalone Lwopan deployment. - Full scope, one pass. Every existing feature area (account system, submissions, blog, knowledge base, admin review) is migrated together — not phased by module. Owner's explicit choice, overriding the recommendation to phase around the account system as a foundation-first slice.
- Route namespace: both pages and API routes live under
/lwopan(e.g.app/lwopan/...for pages,app/lwopan/api/...for API routes). NextAuth's own routes, cookie path, and any callback URLs move under this prefix too. This is namespace isolation against future apps mounted the same way, not a cosmetic URL choice. - Styling: Tailwind CSS + Radix UI, replacing
apps-siao-ai's current plain-CSS nav homepage styling. This decision overturnsSPEC_APPS.md's Implementation Decisions section (plain CSS, no Tailwind) — that choice was scoped to the small, expected-to-be- rewritten nav slice, and doesn't hold once a large, permanent Radix-UI-heavy app is being merged in. The nav homepage's own markup may end up touched incidentally when the shared layout/shell is introduced; that's expected, not scope creep. - Liquid Glass: dropped entirely. No glassmorphism, no
lib/liquid-glassport, noLiquidGlassFilter/LiquidGlassSwitchcomponents. The rest of the frontend (every page, every component tree) is rebuilt fresh against Tailwind + Radix, not a 1:1 visual port of the old design. - Backend logic ports as-is: Prisma schema (User, Account, Session,
Submission, BlogPost, Comment, Like, and related models), NextAuth
configuration, API route handlers, validation logic (
lib/validators,lib/validations), auth logic (lib/auth), storage logic (lib/storage), MDX handling (lib/mdx). These move over with their behavior intact; only their location (under the/lwopannamespace) and their calling frontend change. - Database: SQLite, including production — not PostgreSQL, despite
learning-platform's README describing a SQLite-dev/Postgres-prod split that was never actually implemented. Matches the pattern already used by Forgejo on the same host. Single-file DB, no additional service to run alongside it. - No data migration. The existing
prisma/dev.db(SQLite, ~26MB) is local development/test data from an app that was never deployed publicly — confirmed with the owner. The migrated app starts with a fresh, empty database on the new schema;dev.dbis not carried over. - External services — only bring over what's actually configured
today: Google OAuth and SMTP email (both have real values in
learning-platform/.env). Algolia (search) and GA (analytics) stay unconfigured — their integration code can port over, but without live credentials, so those features are effectively inert until a future session supplies real keys.uploadthingis listed as a dependency inlearning-platform/package.jsonbut is not referenced anywhere in its code (confirmed via search) — it is dead weight and should not be carried into the merged app at all. - File storage: local disk, unchanged. Avatar and submission
uploads keep writing to
public/uploads/...viafs/promisesexactly aslearning-platformdoes today. This needs a persistent Docker volume mount once deployed (mirroring Forgejo's./data:/datapattern ingit-siao-ai/docker-compose.yml) so uploaded files survive a container rebuild — but swapping to a real object-storage backend (S3-compatible or similar) is explicitly out of scope for this migration and belongs to a later "update the app itself" pass, not this port. - Auth scope: left exactly as-is, not narrowed or widened. The
owner's longer-term intent is a shared login across the whole
siao.aifamily, but this migration does not attempt that — Lwopan's NextAuth setup (its own session, its own cookie) ports over unchanged. Don't scope this down to "explicitly /lwopan-only forever" either; the platform-wide design is an open question for a future, separate session. - Deployment scope for this spec: local only. "Done" for this
migration means it builds and its test suite passes against a local
dev/production server, same verification bar the nav homepage met
before its own deploy was deferred (
NOTES_APPS.mdch.2–3). Actually wiring this into the self-hosted server (Docker Compose service update, volume mounts, cloudflared, whatevergit-siao-aichanges are needed) is out of scope here, consistent with the owner's standing "目前不上線" decision forapps.siao.aigenerally.
Testing Decisions
- Good tests here assert on externally-observable behavior: what a request/response pair or a rendered page actually does, not on internal function calls or component internals.
- Full coverage, same high bar as the nav homepage — explicit owner choice (overriding a suggestion to test only core flows and treat admin/2FA/bulk-upload as lower priority). Every feature area listed under User Stories needs test coverage, including admin review, 2FA, and bulk upload — not just the primary happy paths.
- Seam: the rendered page and the API route's actual HTTP response,
under the
/lwopanprefix — matching the convention already established forapps-siao-ai's nav homepage (tests/homepage.spec.tsin that repo asserts against rendered DOM and route status codes, not internal modules). No new, lower-level seam is introduced by this migration; each ported feature gets tests at this same boundary. Confirmed with the owner before writing this spec. - Prior art:
apps-siao-ai/tests/homepage.spec.tsand itsplaywright.config.ts(dev-server-drivenwebServer,desktopChrome project, axe accessibility checks) — extend this suite rather than starting a second one. - Features gated on unconfigured external services (Algolia search results, GA) should have their integration code exercised in a way that doesn't require live credentials (e.g. asserting the query is built/dispatched correctly, or skipping/marking pending with a clear reason) rather than being left completely untested.
Out of Scope
- Renaming "Lwopan" to its eventual real name.
- A platform-wide shared login across
siao.ai— Lwopan's auth ports over exactly as it is today; the shared-login design is a distinct, future, explicitly separate decision. - Migrating or preserving
prisma/dev.db's existing data. - Configuring Algolia (search) or Google Analytics with real credentials — the code paths can port over, but going live with these services is future work.
- Carrying over the unused
uploadthingdependency. - Any file-storage backend swap (S3-compatible or otherwise) — local disk + a Docker volume mount is the extent of storage work here.
- Actually deploying
/lwopan(orapps.siao.aigenerally) to the live server — this spec's "done" is a local build + full test pass, not a production rollout. - Liquid Glass in any form, and any 1:1 visual parity with
learning-platform's old design — the frontend is a from-scratch rebuild against Tailwind + Radix, not a faithful visual port. - Phased/incremental rollout by feature module — this is one migration covering everything, per the owner's explicit choice.
Further Notes
- Source app for this migration:
~/Lwopan/learning-platform(Next.js 15, TypeScript, Prisma 6 + SQLite, NextAuth v5 beta, Tailwind 4, Radix UI, Framer Motion,@monaco-editor/react, MDX via@next/mdx/next-mdx-remote/shiki/rehype-pretty-code). 68 commits, confirmed with the owner to have never been deployed publicly. ~/Lwopan/liquid-glass-toolkit(a sibling directory, separate fromlearning-platform's ownlib/liquid-glassmodule) remains permanently excluded from everything in thesiao.aifamily — seeNOTES_APPS.mdch.1 andSPEC_APPS.md. Not relevant to this migration beyond the reminder that neither Liquid Glass source is coming over.- This spec assumes
apps-siao-ai's existing nav homepage (lwopancard →/lwopan) stays as the entry point; once this migration lands, that card's target resolves to real content instead of a 404.