/** * Builds the Oak application and, when run directly, starts the recurring * jobs and listens on {@link LISTEN_HOST}:{@link PORT}. A request goes * through the error middleware, the OG tags and CORS, then the routers, one * per resource of the API (`API.md` lists them), and last the built frontend, * which answers every path no router did. * * @module */ import { Application } from "@oak/oak"; import { oakCors } from "@tajpouria/cors"; import attachmentsRouter from "./routes/attachments.ts"; import avatarsRouter from "./routes/avatars.ts"; import categoriesRouter from "./routes/categories.ts"; import chatRouter from "./routes/chat.ts"; import communityRouter from "./routes/community.ts"; import commentsRouter from "./routes/comments.ts"; import dumpsRouter from "./routes/dumps.ts"; import filesRouter from "./routes/files.ts"; import followsRouter from "./routes/follows.ts"; import invitesRouter from "./routes/invites.ts"; import meRouter from "./routes/me.ts"; import notificationsRouter from "./routes/notifications.ts"; import passwordResetsRouter from "./routes/password-resets.ts"; import playlistsRouter from "./routes/playlists.ts"; import remoteRouter from "./routes/remote.ts"; import searchRouter from "./routes/search.ts"; import sessionsRouter from "./routes/sessions.ts"; import thumbnailsRouter from "./routes/thumbnails.ts"; import waveformsRouter from "./routes/waveforms.ts"; import unsubscribeRouter from "./routes/unsubscribe.ts"; import usersRouter from "./routes/users.ts"; import wsRouter from "./routes/ws.ts"; import { ALLOWED_ORIGINS, LISTEN_HOST, PORT, PUBLIC_URL } from "./config.ts"; import { errorMiddleware } from "./middleware/error.ts"; import { ogMiddleware } from "./middleware/og.ts"; import { routeStaticFilesFrom } from "./lib/static.ts"; import { startJobs } from "./lib/scheduler.ts"; import { rateLimitSweepJob } from "./lib/rate-limit.ts"; import { digestJob } from "./services/digest-service.ts"; import { communityJob } from "./services/community-service.ts"; import { pingJob, typingExpiryJob } from "./services/ws-service.ts"; /** * The whole server, exported so the tests' helpers can send it requests * without listening. */ const app: Application = new Application(); const cors = oakCors({ origin: ALLOWED_ORIGINS }); app.use(errorMiddleware); app.use(ogMiddleware); // oakCors calls next() without awaiting it when there is no origin, which // corrupts the response in Oak's async chain. So it only sees requests from an // allowed origin, and the others get no CORS headers. app.use(async (ctx, next) => { const origin = ctx.request.headers.get("origin"); if (origin && ALLOWED_ORIGINS.includes(origin)) { return await cors(ctx, next); } await next(); }); for ( const router of [ meRouter, followsRouter, notificationsRouter, avatarsRouter, usersRouter, sessionsRouter, passwordResetsRouter, unsubscribeRouter, dumpsRouter, filesRouter, thumbnailsRouter, waveformsRouter, commentsRouter, playlistsRouter, categoriesRouter, searchRouter, chatRouter, communityRouter, invitesRouter, attachmentsRouter, remoteRouter, wsRouter, ] ) { app.use(router.routes(), router.allowedMethods()); } app.use(routeStaticFilesFrom([ `${Deno.cwd()}/dist`, `${Deno.cwd()}/public`, ])); app.addEventListener( "listen", () => console.log(`Server listening on ${PUBLIC_URL}`), ); app.addEventListener( "error", (e) => console.log(`Uncaught error: ${e.message}`), ); if (import.meta.main) { startJobs([ digestJob, communityJob, pingJob, typingExpiryJob, rateLimitSweepJob, ]); await app.listen({ hostname: LISTEN_HOST, port: PORT }); } export { app };