diff --git a/.env.example b/.env.example index 073d0b7d..926b5d86 100644 --- a/.env.example +++ b/.env.example @@ -81,3 +81,6 @@ LIGHTWEIGHT_DB_PORT= LIGHTWEIGHT_DB_USER= LIGHTWEIGHT_DB_PASSWORD= LIGHTWEIGHT_DB_DATABASE= + +# If you want to add a prefix to API paths, such as /api, you can set that here +LIGHTWEIGHT_API_PATH_PREFIX= diff --git a/backend/src/api/archives.ts b/backend/src/api/archives.ts index 3c1ae702..9494d194 100644 --- a/backend/src/api/archives.ts +++ b/backend/src/api/archives.ts @@ -3,15 +3,15 @@ import moment from "moment-timezone"; import { GuildArchives } from "../data/GuildArchives"; import { notFound } from "./responses"; -export function initArchives(app: express.Express) { +export function initArchives(router: express.Router) { const archives = new GuildArchives(null); // Legacy redirect - app.get("/spam-logs/:id", (req: Request, res: Response) => { + router.get("/spam-logs/:id", (req: Request, res: Response) => { res.redirect("/archives/" + req.params.id); }); - app.get("/archives/:id", async (req: Request, res: Response) => { + router.get("/archives/:id", async (req: Request, res: Response) => { const archive = await archives.find(req.params.id); if (!archive) return notFound(res); diff --git a/backend/src/api/auth.ts b/backend/src/api/auth.ts index 85e42ecb..543dab0f 100644 --- a/backend/src/api/auth.ts +++ b/backend/src/api/auth.ts @@ -51,8 +51,8 @@ function simpleDiscordAPIRequest(bearerToken, path): Promise { }); } -export function initAuth(app: express.Express) { - app.use(passport.initialize()); +export function initAuth(router: express.Router) { + router.use(passport.initialize()); passport.serializeUser((user, done) => done(null, user)); passport.deserializeUser((user, done) => done(null, user as IPassportApiUser)); @@ -110,8 +110,8 @@ export function initAuth(app: express.Express) { ), ); - app.get("/auth/login", passport.authenticate("oauth2")); - app.get( + router.get("/auth/login", passport.authenticate("oauth2")); + router.get( "/auth/oauth-callback", passport.authenticate("oauth2", { failureRedirect: "/", session: false }), (req: Request, res: Response) => { @@ -122,7 +122,7 @@ export function initAuth(app: express.Express) { } }, ); - app.post("/auth/validate-key", async (req: Request, res: Response) => { + router.post("/auth/validate-key", async (req: Request, res: Response) => { const key = req.body.key; if (!key) { return res.status(400).json({ error: "No key supplied" }); @@ -135,14 +135,14 @@ export function initAuth(app: express.Express) { res.json({ valid: true, userId }); }); - app.post("/auth/logout", ...apiTokenAuthHandlers(), async (req: Request, res: Response) => { + router.post("/auth/logout", ...apiTokenAuthHandlers(), async (req: Request, res: Response) => { await apiLogins.expireApiKey(req.user!.apiKey); return ok(res); }); // API route to refresh the given API token's expiry time // The actual refreshing happens in the api-token passport strategy above, so we just return 200 OK here - app.post("/auth/refresh", ...apiTokenAuthHandlers(), (req, res) => { + router.post("/auth/refresh", ...apiTokenAuthHandlers(), (req, res) => { return ok(res); }); } diff --git a/backend/src/api/docs.ts b/backend/src/api/docs.ts index 51cb107f..8b742b5c 100644 --- a/backend/src/api/docs.ts +++ b/backend/src/api/docs.ts @@ -97,10 +97,10 @@ function formatZodConfigSchema(schema: z.ZodTypeAny) { return "unknown"; } -export function initDocs(app: express.Express) { +export function initDocs(router: express.Router) { const docsPluginNames = Object.keys(guildPluginInfo).filter((k) => guildPluginInfo[k].showInDocs); - app.get("/docs/plugins", (req: express.Request, res: express.Response) => { + router.get("/docs/plugins", (req: express.Request, res: express.Response) => { res.json( docsPluginNames.map((pluginName) => { const info = guildPluginInfo[pluginName]; @@ -113,7 +113,7 @@ export function initDocs(app: express.Express) { ); }); - app.get("/docs/plugins/:pluginName", (req: express.Request, res: express.Response) => { + router.get("/docs/plugins/:pluginName", (req: express.Request, res: express.Response) => { const name = req.params.pluginName; const baseInfo = guildPluginInfo[name]; if (!baseInfo) { diff --git a/backend/src/api/guilds/index.ts b/backend/src/api/guilds/index.ts index 1f37aca8..3da1384a 100644 --- a/backend/src/api/guilds/index.ts +++ b/backend/src/api/guilds/index.ts @@ -3,12 +3,12 @@ import { apiTokenAuthHandlers } from "../auth"; import { initGuildsImportExportAPI } from "./importExport"; import { initGuildsMiscAPI } from "./misc"; -export function initGuildsAPI(app: express.Express) { +export function initGuildsAPI(router: express.Router) { const guildRouter = express.Router(); guildRouter.use(...apiTokenAuthHandlers()); initGuildsMiscAPI(guildRouter); initGuildsImportExportAPI(guildRouter); - app.use("/guilds", guildRouter); + router.use("/guilds", guildRouter); } diff --git a/backend/src/api/start.ts b/backend/src/api/start.ts index 4e8985b0..2e18dbf1 100644 --- a/backend/src/api/start.ts +++ b/backend/src/api/start.ts @@ -10,6 +10,8 @@ import { initGuildsAPI } from "./guilds/index"; import { clientError, error, notFound } from "./responses"; import { startBackgroundTasks } from "./tasks"; +const apiPathPrefix = env.HOST_MODE === "lightweight" ? env.LIGHTWEIGHT_API_PATH_PREFIX || "" : "/api"; + const app = express(); app.use( @@ -24,16 +26,20 @@ app.use( ); app.use(multer().none()); +const rootRouter = express.Router(); + initAuth(app); initGuildsAPI(app); initArchives(app); initDocs(app); // Default route -app.get("/", (req, res) => { +rootRouter.get("/", (req, res) => { res.json({ status: "cookies", with: "milk" }); }); +app.use(apiPathPrefix, rootRouter); + // Error response // eslint-disable-next-line @typescript-eslint/no-unused-vars app.use((err, req, res, next) => { diff --git a/backend/src/env.ts b/backend/src/env.ts index 4ec20e08..33d6e310 100644 --- a/backend/src/env.ts +++ b/backend/src/env.ts @@ -47,6 +47,8 @@ const envType = z.object({ LIGHTWEIGHT_DB_PASSWORD: z.string().optional(), LIGHTWEIGHT_DB_DATABASE: z.string().optional(), + LIGHTWEIGHT_API_PATH_PREFIX: z.string().optional(), + HOST_MODE: z.enum(["development", "standalone", "lightweight"]).optional().default("lightweight"), DEBUG: z .string() diff --git a/docker/development/nginx/default.conf b/docker/development/nginx/default.conf index c0fb4240..5444a0b1 100644 --- a/docker/development/nginx/default.conf +++ b/docker/development/nginx/default.conf @@ -15,9 +15,6 @@ server { } location /api { - # Remove /api/ from the beginning when passing the path to the API process - rewrite /api(/.*)$ $1 break; - resolver 127.0.0.11; proxy_pass $backend_upstream$uri$is_args$args; proxy_redirect off; diff --git a/docker/production/nginx/default.conf b/docker/production/nginx/default.conf index 52001d38..dee3c84f 100644 --- a/docker/production/nginx/default.conf +++ b/docker/production/nginx/default.conf @@ -15,9 +15,6 @@ server { } location /api { - # Remove /api/ from the beginning when passing the path to the API process - rewrite /api(/.*)$ $1 break; - resolver 127.0.0.11; proxy_pass $backend_upstream$uri$is_args$args; proxy_redirect off;