spb/autoscholar
Public
1import { allSettings, getEnv } from "@autoscholar/core";2import { auditEvents } from "@autoscholar/db";3import { desc } from "drizzle-orm";4import { fmtDateTime } from "@/lib/format";5import { db } from "@/lib/server/db";6import { currentSession } from "@/lib/server/session";7import { SettingsControls } from "./SettingsControls";89export const dynamic = "force-dynamic";1011export default async function SettingsPage() {12 const d = db();13 const env = getEnv();14 const settings = await allSettings(d);15 const audit = await d.select().from(auditEvents).orderBy(desc(auditEvents.createdAt)).limit(100);16 const sess = await currentSession();17 return (18 <div className="space-y-8">19 <header>20 <p className="eyebrow">Founder controls</p>21 <h1 className="title-2 mt-1">Settings & security</h1>22 </header>23 <SettingsControls24 settings={{25 dispatchPaused: settings["dispatch.paused"] === true,26 publishingHalted: settings["publishing.halted"] === true,27 haltedModes: (settings["publishing.haltedModes"] as string[]) ?? [],28 }}29 />30 <section className="surface p-4">31 <h2 className="title-3">Environment and providers</h2>32 <dl className="mt-2 grid grid-cols-2 gap-x-6 gap-y-1 text-xs sm:grid-cols-4">33 <dt className="muted">Publishing (env)</dt>34 <dd>{env.PUBLISHING_ENABLED === 1 ? "enabled" : "DISABLED"}</dd>35 <dt className="muted">Dispatch (env)</dt>36 <dd>{env.DISPATCH_ENABLED === 1 ? "enabled" : "DISABLED"}</dd>37 <dt className="muted">Staging mode</dt>38 <dd>{env.STAGING_MODE === 1 ? "on (noindex, synthetic sample allowed)" : "off"}</dd>39 <dt className="muted">Research model</dt>40 <dd className="font-mono">{env.ANTHROPIC_MODEL}</dd>41 <dt className="muted">Reviewer model</dt>42 <dd className="font-mono">{env.ANTHROPIC_REVIEWER_MODEL}</dd>43 <dt className="muted">Default caps</dt>44 <dd>45 ${env.DEFAULT_MAX_BUDGET_USD} · {env.DEFAULT_MAX_RUNTIME_MINUTES} min46 </dd>47 <dt className="muted">Lease TTL</dt>48 <dd>{env.LEASE_TTL_SECONDS}s</dd>49 <dt className="muted">Enrollment secret</dt>50 <dd>{env.WORKER_ENROLLMENT_SECRET ? "configured" : "not set"}</dd>51 <dt className="muted">Anthropic key on control plane</dt>52 <dd>53 {process.env.ANTHROPIC_API_KEY54 ? "present (not needed here)"55 : "absent (expected: keys live on workers)"}56 </dd>57 <dt className="muted">Session</dt>58 <dd>59 {sess?.username} · expires {fmtDateTime(sess?.expiresAt)}60 </dd>61 </dl>62 </section>63 <section className="surface p-4">64 <h2 className="title-3">Audit log (latest 100)</h2>65 <table className="table mt-2">66 <thead>67 <tr>68 <th>When</th>69 <th>Actor</th>70 <th>Action</th>71 <th>Target</th>72 <th>Detail</th>73 </tr>74 </thead>75 <tbody>76 {audit.length === 0 && (77 <tr>78 <td colSpan={5} className="muted py-6 text-center">79 Empty.80 </td>81 </tr>82 )}83 {audit.map((a) => (84 <tr key={a.id}>85 <td className="subtle whitespace-nowrap text-xs">{fmtDateTime(a.createdAt)}</td>86 <td className="text-xs">87 {a.actorType}88 {a.actorId ? `:${a.actorId.slice(0, 8)}` : ""}89 </td>90 <td className="font-mono text-xs">{a.action}</td>91 <td className="text-xs">92 {a.targetType} {a.targetId?.slice(0, 8)}93 </td>94 <td className="muted max-w-md truncate text-xs">{JSON.stringify(a.detail)}</td>95 </tr>96 ))}97 </tbody>98 </table>99 </section>100 </div>101 );102}103