150 lines
4.4 KiB
TypeScript
150 lines
4.4 KiB
TypeScript
/**
|
|
* Builds the Oak application and, when run directly, starts the recurring
|
|
* jobs and listens on {@link LISTEN_HOST}:{@link PORT}, having first signed
|
|
* the users out of other instances if they may no longer sign in there
|
|
* (`FEDERATION.outgoing`). A request goes
|
|
* through the error middleware, the compression of what is answered, 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 federationRouter from "./routes/federation.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 oauthRouter from "./routes/oauth.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 wellKnownRouter from "./routes/well-known.ts";
|
|
import wsRouter from "./routes/ws.ts";
|
|
|
|
import {
|
|
ALLOWED_ORIGINS,
|
|
FEDERATION,
|
|
LISTEN_HOST,
|
|
PORT,
|
|
PUBLIC_URL,
|
|
} from "./config.ts";
|
|
import { compressMiddleware } from "./middleware/compress.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";
|
|
import {
|
|
outboxJob,
|
|
withdrawAllGrants,
|
|
} from "./services/identity-provider-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(compressMiddleware);
|
|
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,
|
|
federationRouter,
|
|
oauthRouter,
|
|
wellKnownRouter,
|
|
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) {
|
|
if (!FEDERATION.outgoing) {
|
|
const withdrawn = await withdrawAllGrants();
|
|
if (withdrawn > 0) {
|
|
console.log(`Signed users out of other instances (${withdrawn} grants)`);
|
|
}
|
|
}
|
|
startJobs([
|
|
digestJob,
|
|
communityJob,
|
|
pingJob,
|
|
typingExpiryJob,
|
|
rateLimitSweepJob,
|
|
outboxJob,
|
|
]);
|
|
await app.listen({ hostname: LISTEN_HOST, port: PORT });
|
|
}
|
|
|
|
export { app };
|