Files
STCourses/packages/feature-learner/src/server/feature.ts
T

40 lines
1.6 KiB
TypeScript

import type { AuthService, Feature, RouteHandler, ServerContext } from "@stc/contracts";
import { createAuth } from "./auth";
import { runMigrations } from "./migrate";
import { json } from "./http";
import { adminRoutes } from "./admin";
import { libraryRoutes } from "./catalog";
import { lessonRoutes } from "./lesson";
import { userDataRoutes } from "./userdata";
/**
* The learner feature: auth, library, reader, notes, progress and exercises.
* Returns the routes plus the `AuthService` that replaces the server's anonymous one.
*/
export function learnerFeature(ctx: ServerContext): Feature & { auth: AuthService } {
const ready = runMigrations(ctx.db);
ready.catch((e) => console.error("[learner] migrations failed", e));
const auth = createAuth(ctx, ready);
const routes: Feature["routes"] = {
"/api/auth/*": {
GET: (req, rc) => auth.handler(req, rc.user),
POST: (req, rc) => auth.handler(req, rc.user),
},
"/api/me": { GET: async (_req, rc) => json({ user: rc.user }) },
"/api/learner/providers": {
GET: async () => json({ emailPassword: true, providers: auth.providers(), sso: await auth.hasSso() }),
},
...adminRoutes(ctx, auth.service.invalidate),
...libraryRoutes(ctx),
...lessonRoutes(ctx),
...userDataRoutes(ctx),
};
// Every handler waits for the learner migrations (a no-op once Phase 2 runs them in migrate()).
for (const methods of Object.values(routes)) {
for (const [m, h] of Object.entries(methods)) {
(methods as Record<string, RouteHandler>)[m] = async (req, rc) => { await ready; return h!(req, rc); };
}
}
return { name: "learner", routes, auth: auth.service };
}