${error}
`; } /** An admin page: the site's own look, with the console's sections and sign-out on top. */ export function adminPage(c: Context, ctx: AdminContext, title: string, body: Html): string { const tab = ([href, name]: (typeof SECTIONS)[number]) => html`${name}${ name === "notifications" && unread > 0 ? html` ${String(unread)}` : "" }`; const unread = ctx.kepr .query<{ n: number }, []>("SELECT COUNT(*) AS n FROM notifications WHERE read = 0") .get()?.n ?? 0; const handle = setting(ctx.kepr, "handle"); return page(ctx, { title: `${title} · admin`, description: "", path: c.req.path, body: html`Changes saved.
`} ${body}To revoke access, rotate the bucket key in Scaleway and update it under bucket.
`; } /** * The bucket as the sync last read it, and each repo's format and sync result: the sync checks the * format of every repo on every pass, so the console never waits on the bucket itself. */ function bucketStatus(ctx: AdminContext): Html { const error = setting(ctx.kepr, "bucket_error"); if (error) { return html`Bucket unreachable at the last sync: ${error}
`; } const readAt = Number(setting(ctx.kepr, "bucket_read_at") ?? 0); if (readAt === 0) return html`The sync has not read the bucket yet.
`; // The repos that listing found; Kepr keeps the rows of repos gone from the bucket. const seen = JSON.parse(setting(ctx.kepr, "bucket_repos") ?? "[]") as string[]; const errors = new Map(allRepos(ctx.kepr).map((repo) => [repo.name, repo.last_sync_error])); return html`Bucket read by the sync ${relativeTime(readAt)}: ${plural(seen.length, "repo")}.
No backup yet. Check the server log for backup errors.
`} ${!setting(ctx.kepr, "handle") && html`Set your handle to publish your profile →
`} ${attention.length === 0 && newest && html`All repositories are synced. A local backup is available.
`}No repositories yet. Offer one with Koh, then sync it here.
`}Running kepr ${version}: ${lines} lines of TypeScript.