51 lines
1.8 KiB
JavaScript
51 lines
1.8 KiB
JavaScript
import { getConfig } from "../common/config.js";
|
|
import { fail, sessionRoute } from "../common/http.js";
|
|
import {
|
|
listKeys as defaultList,
|
|
generateKey as defaultGenerate,
|
|
revokeKey as defaultRevoke,
|
|
} from "../modules/core/apiKey.js";
|
|
|
|
const requireUser = (fn) => sessionRoute("apikeys", fn);
|
|
|
|
// Internal modules have no `app` row and issue no billable calls, so no key.
|
|
const mintableModules = () =>
|
|
Object.entries(getConfig().appModules)
|
|
.filter(([, m]) => !m?.internal)
|
|
.map(([type]) => type);
|
|
|
|
export function apiKeyHandlers({
|
|
listKeys = defaultList,
|
|
generateKey = defaultGenerate,
|
|
revokeKey = defaultRevoke,
|
|
} = {}) {
|
|
return {
|
|
whoami: requireUser(async (req, res) => {
|
|
res.json({ username: req.user.username, canViewJson: req.user.canViewJson, modules: mintableModules() });
|
|
}),
|
|
|
|
list: requireUser(async (req, res) => {
|
|
res.json(await listKeys(req.user.id));
|
|
}),
|
|
|
|
create: requireUser(async (req, res) => {
|
|
const { type, label, expiresTs } = req.body || {};
|
|
if (!mintableModules().includes(type)) {
|
|
return fail(res, 400, "bad_request", "unknown module");
|
|
}
|
|
if (expiresTs != null && (typeof expiresTs !== "string" || Number.isNaN(Date.parse(expiresTs)))) {
|
|
return fail(res, 400, "bad_request", "invalid expiresTs (expected an ISO date string)");
|
|
}
|
|
// Returns the raw key ONCE — the client must surface it immediately.
|
|
res.json(await generateKey(req.user.id, type, { label: label || null, expiresTs: expiresTs || null }));
|
|
}),
|
|
|
|
revoke: requireUser(async (req, res) => {
|
|
const id = parseInt(req.params.id, 10);
|
|
if (!Number.isInteger(id)) return fail(res, 400, "bad_request", "invalid id");
|
|
const ok = await revokeKey(req.user.id, id);
|
|
res.status(ok ? 200 : 404).json({ ok });
|
|
}),
|
|
};
|
|
}
|