v3: added site-wide categories, added admin category management, various visual fixes
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 2m52s

This commit is contained in:
khannurien
2026-06-28 16:23:44 +00:00
parent c8c7b05c25
commit fae25f3e6c
44 changed files with 2144 additions and 399 deletions

View File

@@ -6,13 +6,15 @@ export type Permission =
| "dump:moderate"
| "comment:moderate"
| "playlist:moderate"
| "user:manage";
| "user:manage"
| "category:manage";
const ALL_PERMISSIONS: readonly Permission[] = [
"dump:moderate",
"comment:moderate",
"playlist:moderate",
"user:manage",
"category:manage",
];
// Roles are fixed in code. `admin` is a superset of every permission (current

33
api/lib/reserved-slugs.ts Normal file
View File

@@ -0,0 +1,33 @@
// Top-level path segments that already have meaning in the SPA router
// (see src/App.tsx) plus a few reserved for infrastructure. Category slugs
// live at the URL root (e.g. /music), so they must never collide with these.
export const RESERVED_SLUGS: readonly string[] = [
"~",
"dumps",
"users",
"playlists",
"search",
"login",
"register",
"notifications",
"reset-password",
"manage",
"api",
"assets",
];
// Lowercase alphanumerics, hyphen-separated, no leading/trailing/double hyphen.
export const CATEGORY_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
export function isValidSlugFormat(slug: string): boolean {
return CATEGORY_SLUG_RE.test(slug);
}
export function isReservedSlug(slug: string): boolean {
return RESERVED_SLUGS.includes(slug);
}
/** A slug is available when it is well-formed and not reserved. */
export function isSlugAvailable(slug: string): boolean {
return isValidSlugFormat(slug) && !isReservedSlug(slug);
}