93 lines
4.0 KiB
TypeScript
93 lines
4.0 KiB
TypeScript
// Isolated browser QA server. Never opens or changes the user's database.
|
|
import { sendDueReminders } from "../src/reminders/worker";
|
|
import type { DiscordFetch } from "../src/sharing/routes";
|
|
import { fixture } from "../src/habits/test-fixture";
|
|
import { createApi } from "../src/api";
|
|
import index from "../src/index.html";
|
|
|
|
const f = fixture();
|
|
const origin = "http://127.0.0.1:3107";
|
|
const previewCookie = "minabot_dashboard_preview_session";
|
|
const sharingPreview = process.env.SHARE_PREVIEW === "1";
|
|
let sharedImage: Blob | null = null;
|
|
let reminderMessage: string | null = null;
|
|
const previewDiscord: DiscordFetch = async (url, init) => {
|
|
if (url.endsWith("/users/@me/channels")) return Response.json({ id: "523456789012345678" });
|
|
if (init?.method === "POST") {
|
|
if (init.body instanceof FormData) sharedImage = init.body.get("files[0]") as Blob;
|
|
else reminderMessage = JSON.parse(String(init.body)).content;
|
|
return Response.json({ id: "423456789012345678" });
|
|
}
|
|
return Response.json({ type: 0, id: "223456789012345678", guild_id: "323456789012345678", name: "preview-only" });
|
|
};
|
|
if (sharingPreview) {
|
|
f.setTime("2026-08-01T12:00:00Z");
|
|
const reading = await f.json("/habits", "POST", { name: "A few pages, every day", method: "count", target: 10, unit: "pages", color: "#90647e" }, 201);
|
|
const walking = await f.json("/habits", "POST", { name: "An afternoon walk", method: "manual", schedule: { type: "weekdays", days: [1, 3, 5] }, color: "#397f76" }, 201);
|
|
for (let i = 0; i < 35; i++) {
|
|
const date = new Date(Date.UTC(2026, 7, 1 + i)).toISOString().slice(0, 10);
|
|
f.setTime(`${date}T12:00:00Z`);
|
|
if (i % 4 !== 0) await f.json(`/habits/${reading.id}/days/${date}/progress`, "PUT", { count: i % 3 === 0 ? 5 : 10 });
|
|
if ([1, 3, 5].includes(new Date(`${date}T12:00:00Z`).getUTCDay()) && i % 4 !== 0) await f.json(`/habits/${walking.id}/days/${date}/progress`, "PUT", { done: true });
|
|
}
|
|
}
|
|
const app = createApi(
|
|
f.db,
|
|
{
|
|
origin,
|
|
clientId: "",
|
|
clientSecret: "",
|
|
cookieSecret: "preview-only-secret-at-least-32-characters",
|
|
},
|
|
undefined,
|
|
() => Date.parse("2026-09-04T12:00:00Z"),
|
|
sharingPreview ? previewDiscord : undefined,
|
|
sharingPreview ? { token: "preview-bot-token", channelId: "223456789012345678" } : undefined,
|
|
);
|
|
let reminderRunning = false;
|
|
if (sharingPreview) setInterval(async () => {
|
|
if (reminderRunning) return; reminderRunning = true;
|
|
try { await sendDueReminders(f.db, { token: "preview-bot-token", origin }, previewDiscord, () => Date.parse("2026-09-04T12:00:00Z")); }
|
|
finally { reminderRunning = false; }
|
|
}, 1000).unref();
|
|
const server = Bun.serve({
|
|
hostname: "127.0.0.1",
|
|
port: 3107,
|
|
routes: {
|
|
"/__preview/reminder": () => new Response(reminderMessage ?? "No reminder sent"),
|
|
"/__preview/shared-image": () => sharingPreview && sharedImage ? new Response(sharedImage) : new Response(null, { status: 404 }),
|
|
"/__preview/login": () =>
|
|
new Response(null, {
|
|
status: 302,
|
|
headers: {
|
|
Location: "/",
|
|
"Set-Cookie": `${previewCookie}=${"a".repeat(43)}; Path=/; HttpOnly; SameSite=Lax`,
|
|
},
|
|
}),
|
|
"/api/*": async (request) => {
|
|
// Translate only the preview cookie; never use or overwrite a real session.
|
|
const cookie = request.headers
|
|
.get("cookie")
|
|
?.split(";")
|
|
.map((value) => value.trim())
|
|
.find((value) => value.startsWith(`${previewCookie}=`));
|
|
const headers = new Headers(request.headers);
|
|
headers.set(
|
|
"cookie",
|
|
cookie ? cookie.replace(previewCookie, "minabot_session") : "",
|
|
);
|
|
const response = await app.fetch(new Request(request, { headers }));
|
|
const setCookie = response.headers.get("set-cookie");
|
|
if (setCookie)
|
|
response.headers.set(
|
|
"set-cookie",
|
|
setCookie.replace("minabot_session", previewCookie),
|
|
);
|
|
return response;
|
|
},
|
|
"/*": index,
|
|
},
|
|
development: true,
|
|
});
|
|
console.log(`Disposable dashboard preview: ${server.url}__preview/login`);
|