addition of new dashboard

This commit is contained in:
2026-08-20 15:08:32 +00:00
parent 0622018d95
commit bf59249ed7
161 changed files with 13170 additions and 119 deletions

View File

@@ -0,0 +1,95 @@
import { jest } from "@jest/globals";
import express from "express";
import request from "supertest";
import { getConfig } from "../common/config.js";
import { apiKeyHandlers } from "./apiKeys.js";
// getConfig() is a mutable singleton — point its enabled modules at a known set.
// `dashboard` is enabled but internal, so it must NOT appear as a mintable module.
beforeAll(() => {
getConfig().appModules = { core: {}, archive: {}, dashboard: { internal: true } };
});
function makeApp({ user = { id: 1, username: "dev" }, store } = {}) {
const app = express();
app.use((req, _res, next) => {
if (user) req.user = user;
next();
});
const h = apiKeyHandlers(store);
app.get("/api/dashboard/auth/whoami", h.whoami);
app.get("/api/dashboard/auth/keys", h.list);
app.post("/api/dashboard/auth/keys", express.json(), h.create);
app.delete("/api/dashboard/auth/keys/:id", h.revoke);
return app;
}
const okStore = () => ({
listKeys: jest.fn(async () => [{ id: 1, appType: "core", prefix: "ab12cd34", label: "ci" }]),
generateKey: jest.fn(async (_uid, type) => ({ id: 9, key: "rawsecret", prefix: "rawsecre", appType: type })),
revokeKey: jest.fn(async (_uid, id) => id === 9),
});
describe("API key management routes", () => {
it("401s when not logged in", async () => {
const res = await request(makeApp({ user: null, store: okStore() })).get("/api/dashboard/auth/keys");
expect(res.status).toBe(401);
expect(res.body.error.code).toBe("unauthorized");
});
it("whoami returns username + mintable modules (excludes internal dashboard)", async () => {
const res = await request(makeApp({ store: okStore() })).get("/api/dashboard/auth/whoami");
expect(res.status).toBe(200);
expect(res.body.username).toBe("dev");
expect(res.body.modules).toEqual(expect.arrayContaining(["core", "archive"]));
expect(res.body.modules).not.toContain("dashboard");
});
it("rejects generating a key for an internal module (dashboard)", async () => {
const store = okStore();
const res = await request(makeApp({ store })).post("/api/dashboard/auth/keys").send({ type: "dashboard" });
expect(res.status).toBe(400);
expect(store.generateKey).not.toHaveBeenCalled();
});
it("lists the user's keys (metadata only)", async () => {
const store = okStore();
const res = await request(makeApp({ store })).get("/api/dashboard/auth/keys");
expect(res.status).toBe(200);
expect(res.body[0]).toMatchObject({ prefix: "ab12cd34" });
expect(store.listKeys).toHaveBeenCalledWith(1);
});
it("generates a key for an enabled module and returns the raw key once", async () => {
const store = okStore();
const res = await request(makeApp({ store }))
.post("/api/dashboard/auth/keys")
.send({ type: "core", label: "ci" });
expect(res.status).toBe(200);
expect(res.body.key).toBe("rawsecret");
expect(store.generateKey).toHaveBeenCalledWith(1, "core", { label: "ci", expiresTs: null });
});
it("rejects generating a key for a module that is not enabled", async () => {
const store = okStore();
const res = await request(makeApp({ store })).post("/api/dashboard/auth/keys").send({ type: "nope" });
expect(res.status).toBe(400);
expect(store.generateKey).not.toHaveBeenCalled();
});
it("rejects a malformed expiresTs with 400 (not a DB-level 500)", async () => {
const store = okStore();
const res = await request(makeApp({ store }))
.post("/api/dashboard/auth/keys")
.send({ type: "core", expiresTs: "not-a-date" });
expect(res.status).toBe(400);
expect(store.generateKey).not.toHaveBeenCalled();
});
it("revokes a key (200 when deleted, 404 when not)", async () => {
const store = okStore();
const app = makeApp({ store });
expect((await request(app).delete("/api/dashboard/auth/keys/9")).status).toBe(200);
expect((await request(app).delete("/api/dashboard/auth/keys/5")).status).toBe(404);
});
});