40 lines
1.5 KiB
TypeScript
40 lines
1.5 KiB
TypeScript
// Self-hosted entry: Bun.serve behind our reverse proxy (spec §4.10). Keep it thin.
|
|
import { join, resolve } from "node:path";
|
|
import { createApp } from "./app";
|
|
import { SERVER_IDLE_TIMEOUT_S, clientIp, readConfig } from "./config";
|
|
import { DirStorage } from "./storage-dir";
|
|
|
|
const config = readConfig(process.env);
|
|
const storage = config.storageDir ? await DirStorage.open(config.storageDir, config.storageMaxBytes) : null;
|
|
|
|
if (storage) {
|
|
const sweep = () =>
|
|
storage
|
|
.sweep()
|
|
.then((n) => n > 0 && console.log(`swept ${n} expired cover(s)`))
|
|
.catch((err) => console.error("sweep failed:", err));
|
|
await sweep();
|
|
setInterval(sweep, 24 * 60 * 60 * 1000);
|
|
}
|
|
|
|
const assetsRoot = resolve(config.assetsDir);
|
|
let server: ReturnType<typeof Bun.serve>;
|
|
|
|
const app = createApp({
|
|
storage,
|
|
publicUrl: config.publicUrl,
|
|
// `relativePath` is already checked by resolveAssetPath (no dot-segments, no dotfiles).
|
|
assets: async (relativePath) => {
|
|
const file = Bun.file(join(assetsRoot, relativePath));
|
|
return (await file.exists()) ? new Response(file) : null;
|
|
},
|
|
clientIp: (req) => clientIp(req, config.trustProxy, server.requestIP(req)?.address),
|
|
});
|
|
|
|
server = Bun.serve({ port: config.port, hostname: config.hostname, idleTimeout: SERVER_IDLE_TIMEOUT_S, fetch: app });
|
|
console.log(
|
|
`mangabaka-seeder on http://${server.hostname}:${server.port} · storage: ${
|
|
storage ? `${config.storageDir} (${storage.usedBytes()} of ${config.storageMaxBytes} bytes)` : "off"
|
|
}`,
|
|
);
|