1import { join } from "node:path";2import type { Hono } from "hono";3import { createSetupApp } from "./admin/setup.ts";4import { startStorageLoop } from "./admin/storage.ts";5import { openBucket } from "./bucket/reader.ts";6import { startSyncWorker } from "./bucket/sync.ts";7import { openMirrors } from "./mirror/store.ts";8import { warmAtlas } from "./pages/atlas.ts";9import { createApp } from "./server/app.ts";10import { startBackupLoop } from "./server/backup.ts";11import { CONFIG_FILE, type Config, ConfigError, loadConfig } from "./server/config.ts";12import { oneTimeCode } from "./server/connect.ts";13import { openKeprDb } from "./server/db.ts";14import { startMailLoop } from "./server/mail.ts";151617const DATA_DIR = "data";181920function start(config: Config): Hono["fetch"] {21 const kepr = openKeprDb(config.dataDir);22 23 const bucket = openBucket(config.bucket);24 startBackupLoop(kepr, config);25 startMailLoop(kepr, config);26 const mirrorDir = join(config.dataDir, "mirror");27 openMirrors(mirrorDir);28 29 const syncer = startSyncWorker(config.dataDir, config.bucket, (repo) =>30 warmAtlas({ kepr, mirrorDir }, repo),31 );32 startStorageLoop({ bucket, kepr, mirrorDir });33 return createApp(config, kepr, bucket, CONFIG_FILE, syncer).fetch;34}3536try {37 let fetch: Hono["fetch"];38 if (await Bun.file(CONFIG_FILE).exists()) {39 fetch = start(await loadConfig(CONFIG_FILE));40 } else {41 const code = oneTimeCode();42 fetch = createSetupApp({43 code,44 configPath: CONFIG_FILE,45 dataDir: DATA_DIR,46 done: (config) => {47 fetch = start(config);48 },49 }).fetch;50 console.log(51 `kepr: no ${CONFIG_FILE} yet. Open /setup in a browser and enter the code ${code.slice(0, 4)}-${code.slice(4)}`,52 );53 }54 55 const server = Bun.serve({ fetch: (request, bun) => fetch(request, bun) });56 console.log(`kepr listening on ${server.url}`);57} catch (error) {58 if (!(error instanceof ConfigError)) throw error;59 console.error(`kepr: ${error.message}`);60 process.exit(1);61}