- Introduced new HTTP authentication and page management APIs, allowing for the creation, retrieval, updating, and deletion of HTTP auth profiles and pages. - Added validation for auth and page fields to ensure proper configuration and error handling. - Implemented a mechanism for resolving auth credentials from various sources, including inline definitions, KV store, and secrets. - Enhanced workflow validation to include checks for HTTP triggers, ensuring proper auth and response configurations. - Updated the web interface to include new routes for managing HTTP auth profiles and pages, improving user experience and accessibility.
151 lines
3.5 KiB
JavaScript
151 lines
3.5 KiB
JavaScript
import { randomUUID } from "node:crypto";
|
|
import { db } from "./db.js";
|
|
|
|
const MAX_NAME_LENGTH = 128;
|
|
const NAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
const ALLOWED_MIME = new Set(["html", "json"]);
|
|
|
|
function nowIso() {
|
|
return new Date().toISOString();
|
|
}
|
|
|
|
/**
|
|
* @param {unknown} name
|
|
* @returns {string}
|
|
*/
|
|
export function assertPageName(name) {
|
|
if (typeof name !== "string" || !NAME_RE.test(name)) {
|
|
const err = new Error("invalid page name");
|
|
err.statusCode = 400;
|
|
throw err;
|
|
}
|
|
if (name.length > MAX_NAME_LENGTH) {
|
|
const err = new Error(`page name must be at most ${MAX_NAME_LENGTH} characters`);
|
|
err.statusCode = 400;
|
|
throw err;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* @param {unknown} mime
|
|
* @returns {"html" | "json"}
|
|
*/
|
|
export function assertMime(mime) {
|
|
const m = String(mime ?? "");
|
|
if (!ALLOWED_MIME.has(m)) {
|
|
const err = new Error('mime must be "html" or "json"');
|
|
err.statusCode = 400;
|
|
throw err;
|
|
}
|
|
return /** @type {"html" | "json"} */ (m);
|
|
}
|
|
|
|
/**
|
|
* @param {unknown} status
|
|
* @returns {number}
|
|
*/
|
|
export function assertHttpStatus(status, fallback = 200) {
|
|
if (status == null || status === "") return fallback;
|
|
const n = Number(status);
|
|
if (!Number.isInteger(n) || n < 100 || n > 599) {
|
|
const err = new Error("status must be an HTTP status code (100-599)");
|
|
err.statusCode = 400;
|
|
throw err;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
function publicPage(row) {
|
|
return {
|
|
id: row.id,
|
|
name: row.name,
|
|
content: row.content,
|
|
mime: row.mime,
|
|
status: row.status,
|
|
created_at: row.created_at,
|
|
updated_at: row.updated_at,
|
|
};
|
|
}
|
|
|
|
export async function listHttpPages() {
|
|
const rows = await db("http_pages")
|
|
.select("*")
|
|
.orderBy("name", "asc");
|
|
return rows.map(publicPage);
|
|
}
|
|
|
|
/**
|
|
* @param {string} name
|
|
*/
|
|
export async function getHttpPageByName(name) {
|
|
const row = await db("http_pages").where({ name: assertPageName(name) }).first();
|
|
return row ? publicPage(row) : null;
|
|
}
|
|
|
|
/**
|
|
* @param {string} id
|
|
*/
|
|
export async function getHttpPageById(id) {
|
|
const row = await db("http_pages").where({ id }).first();
|
|
return row ? publicPage(row) : null;
|
|
}
|
|
|
|
/**
|
|
* @param {{ name: string, content: string, mime: string, status?: number }} opts
|
|
*/
|
|
export async function upsertHttpPage({ name, content, mime, status }) {
|
|
const pageName = assertPageName(name);
|
|
const pageMime = assertMime(mime);
|
|
const pageStatus = assertHttpStatus(status, 200);
|
|
if (typeof content !== "string") {
|
|
const err = new Error("content must be a string");
|
|
err.statusCode = 400;
|
|
throw err;
|
|
}
|
|
const now = nowIso();
|
|
const existing = await db("http_pages").where({ name: pageName }).first();
|
|
|
|
if (existing) {
|
|
await db("http_pages")
|
|
.where({ id: existing.id })
|
|
.update({
|
|
content,
|
|
mime: pageMime,
|
|
status: pageStatus,
|
|
updated_at: now,
|
|
});
|
|
return getHttpPageById(existing.id);
|
|
}
|
|
|
|
const id = randomUUID();
|
|
await db("http_pages").insert({
|
|
id,
|
|
name: pageName,
|
|
content,
|
|
mime: pageMime,
|
|
status: pageStatus,
|
|
created_at: now,
|
|
updated_at: now,
|
|
});
|
|
return getHttpPageById(id);
|
|
}
|
|
|
|
/**
|
|
* @param {string} id
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
export async function deleteHttpPage(id) {
|
|
const n = await db("http_pages").where({ id }).del();
|
|
return n > 0;
|
|
}
|
|
|
|
/**
|
|
* Content-Type for a page mime.
|
|
* @param {"html" | "json" | string} mime
|
|
*/
|
|
export function contentTypeForMime(mime) {
|
|
if (mime === "html") return "text/html; charset=utf-8";
|
|
return "application/json; charset=utf-8";
|
|
}
|