first rewrite commit

This commit is contained in:
fuomag9
2025-10-31 20:08:28 +01:00
parent 85d3917f08
commit 315192fb54
605 changed files with 10913 additions and 47794 deletions

View File

@@ -0,0 +1,194 @@
import bcrypt from "bcryptjs";
import db, { nowIso } from "../db";
import { logAuditEvent } from "../audit";
import { applyCaddyConfig } from "../caddy";
export type AccessListEntry = {
id: number;
username: string;
created_at: string;
updated_at: string;
};
export type AccessList = {
id: number;
name: string;
description: string | null;
entries: AccessListEntry[];
created_at: string;
updated_at: string;
};
export type AccessListInput = {
name: string;
description?: string | null;
users?: { username: string; password: string }[];
};
function parseAccessList(row: any): AccessList {
const entries = db
.prepare(
`SELECT id, username, created_at, updated_at
FROM access_list_entries
WHERE access_list_id = ?
ORDER BY username ASC`
)
.all(row.id) as AccessListEntry[];
return {
id: row.id,
name: row.name,
description: row.description,
entries,
created_at: row.created_at,
updated_at: row.updated_at
};
}
export function listAccessLists(): AccessList[] {
const rows = db
.prepare(
`SELECT id, name, description, created_at, updated_at
FROM access_lists
ORDER BY name ASC`
)
.all();
return rows.map(parseAccessList);
}
export function getAccessList(id: number): AccessList | null {
const row = db
.prepare(
`SELECT id, name, description, created_at, updated_at
FROM access_lists WHERE id = ?`
)
.get(id);
if (!row) {
return null;
}
return parseAccessList(row);
}
export async function createAccessList(input: AccessListInput, actorUserId: number) {
const now = nowIso();
const tx = db.transaction(() => {
const result = db
.prepare(
`INSERT INTO access_lists (name, description, created_at, updated_at, created_by)
VALUES (?, ?, ?, ?, ?)`
)
.run(input.name.trim(), input.description ?? null, now, now, actorUserId);
const accessListId = Number(result.lastInsertRowid);
if (input.users) {
const insert = db.prepare(
`INSERT INTO access_list_entries (access_list_id, username, password_hash, created_at, updated_at)
VALUES (?, ?, ?, ?, ?)`
);
for (const account of input.users) {
const hash = bcrypt.hashSync(account.password, 10);
insert.run(accessListId, account.username, hash, now, now);
}
}
logAuditEvent({
userId: actorUserId,
action: "create",
entityType: "access_list",
entityId: accessListId,
summary: `Created access list ${input.name}`
});
return accessListId;
});
const id = tx();
await applyCaddyConfig();
return getAccessList(id)!;
}
export async function updateAccessList(
id: number,
input: { name?: string; description?: string | null },
actorUserId: number
) {
const existing = getAccessList(id);
if (!existing) {
throw new Error("Access list not found");
}
const now = nowIso();
db.prepare(
`UPDATE access_lists SET name = ?, description = ?, updated_at = ? WHERE id = ?`
).run(input.name ?? existing.name, input.description ?? existing.description, now, id);
logAuditEvent({
userId: actorUserId,
action: "update",
entityType: "access_list",
entityId: id,
summary: `Updated access list ${input.name ?? existing.name}`
});
await applyCaddyConfig();
return getAccessList(id)!;
}
export async function addAccessListEntry(
accessListId: number,
entry: { username: string; password: string },
actorUserId: number
) {
const list = getAccessList(accessListId);
if (!list) {
throw new Error("Access list not found");
}
const now = nowIso();
const hash = bcrypt.hashSync(entry.password, 10);
db.prepare(
`INSERT INTO access_list_entries (access_list_id, username, password_hash, created_at, updated_at)
VALUES (?, ?, ?, ?, ?)`
).run(accessListId, entry.username, hash, now, now);
logAuditEvent({
userId: actorUserId,
action: "create",
entityType: "access_list_entry",
entityId: accessListId,
summary: `Added user ${entry.username} to access list ${list.name}`
});
await applyCaddyConfig();
return getAccessList(accessListId)!;
}
export async function removeAccessListEntry(accessListId: number, entryId: number, actorUserId: number) {
const list = getAccessList(accessListId);
if (!list) {
throw new Error("Access list not found");
}
db.prepare("DELETE FROM access_list_entries WHERE id = ?").run(entryId);
logAuditEvent({
userId: actorUserId,
action: "delete",
entityType: "access_list_entry",
entityId: entryId,
summary: `Removed entry from access list ${list.name}`
});
await applyCaddyConfig();
return getAccessList(accessListId)!;
}
export async function deleteAccessList(id: number, actorUserId: number) {
const existing = getAccessList(id);
if (!existing) {
throw new Error("Access list not found");
}
db.prepare("DELETE FROM access_lists WHERE id = ?").run(id);
logAuditEvent({
userId: actorUserId,
action: "delete",
entityType: "access_list",
entityId: id,
summary: `Deleted access list ${existing.name}`
});
await applyCaddyConfig();
}