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
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 2m52s
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { randomBytes, scryptSync } from "node:crypto";
|
||||
import { DatabaseSync, type SQLOutputValue } from "node:sqlite";
|
||||
import {
|
||||
type Category,
|
||||
type Comment,
|
||||
Dump,
|
||||
isRole,
|
||||
@@ -352,6 +353,40 @@ export function playlistRowToApi(row: PlaylistRow): Playlist {
|
||||
};
|
||||
}
|
||||
|
||||
// ── Categories ────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface CategoryRow {
|
||||
id: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
position: number;
|
||||
created_at: string;
|
||||
updated_at: string | null;
|
||||
[key: string]: SQLOutputValue;
|
||||
}
|
||||
|
||||
export function isCategoryRow(obj: unknown): obj is CategoryRow {
|
||||
return !!obj && typeof obj === "object" &&
|
||||
"id" in obj && typeof obj.id === "string" &&
|
||||
"slug" in obj && typeof obj.slug === "string" &&
|
||||
"name" in obj && typeof obj.name === "string" &&
|
||||
"position" in obj && typeof obj.position === "number" &&
|
||||
"created_at" in obj && typeof obj.created_at === "string" &&
|
||||
"updated_at" in obj &&
|
||||
(typeof obj.updated_at === "string" || obj.updated_at === null);
|
||||
}
|
||||
|
||||
export function categoryRowToApi(row: CategoryRow): Category {
|
||||
return {
|
||||
id: row.id,
|
||||
slug: row.slug,
|
||||
name: row.name,
|
||||
position: row.position,
|
||||
createdAt: new Date(row.created_at),
|
||||
updatedAt: row.updated_at ? new Date(row.updated_at) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export interface FollowRow {
|
||||
id: string;
|
||||
follower_id: string;
|
||||
|
||||
@@ -33,6 +33,54 @@ export interface Dump {
|
||||
commentCount: number;
|
||||
isPrivate: boolean;
|
||||
thumbnailMime?: string;
|
||||
categoryIds?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Categories
|
||||
*/
|
||||
|
||||
export interface Category {
|
||||
id: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
position: number;
|
||||
createdAt: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export interface CreateCategoryRequest {
|
||||
slug: string;
|
||||
name: string;
|
||||
position?: number;
|
||||
}
|
||||
|
||||
export interface UpdateCategoryRequest {
|
||||
slug?: string;
|
||||
name?: string;
|
||||
position?: number;
|
||||
}
|
||||
|
||||
export function isCreateCategoryRequest(
|
||||
obj: unknown,
|
||||
): obj is CreateCategoryRequest {
|
||||
if (!obj || typeof obj !== "object") return false;
|
||||
const o = obj as Record<string, unknown>;
|
||||
if (typeof o.slug !== "string") return false;
|
||||
if (typeof o.name !== "string") return false;
|
||||
if ("position" in o && typeof o.position !== "number") return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isUpdateCategoryRequest(
|
||||
obj: unknown,
|
||||
): obj is UpdateCategoryRequest {
|
||||
if (!obj || typeof obj !== "object") return false;
|
||||
const o = obj as Record<string, unknown>;
|
||||
if ("slug" in o && typeof o.slug !== "string") return false;
|
||||
if ("name" in o && typeof o.name !== "string") return false;
|
||||
if ("position" in o && typeof o.position !== "number") return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -390,10 +438,15 @@ export function isReorderPlaylistRequest(
|
||||
* Request DTOs
|
||||
*/
|
||||
|
||||
function isStringArray(value: unknown): value is string[] {
|
||||
return Array.isArray(value) && value.every((v) => typeof v === "string");
|
||||
}
|
||||
|
||||
export interface CreateUrlDumpRequest {
|
||||
url: string;
|
||||
comment?: string;
|
||||
isPrivate?: boolean;
|
||||
categoryIds?: string[];
|
||||
}
|
||||
|
||||
export function isCreateUrlDumpRequest(
|
||||
@@ -412,6 +465,7 @@ export function isCreateUrlDumpRequest(
|
||||
(o.comment as string).length > VALIDATION.DUMP_COMMENT_MAX
|
||||
) return false;
|
||||
if ("isPrivate" in o && typeof o.isPrivate !== "boolean") return false;
|
||||
if ("categoryIds" in o && !isStringArray(o.categoryIds)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -420,6 +474,7 @@ export interface UpdateDumpRequest {
|
||||
title?: string;
|
||||
comment?: string;
|
||||
isPrivate?: boolean;
|
||||
categoryIds?: string[];
|
||||
}
|
||||
|
||||
export function isUpdateDumpRequest(obj: unknown): obj is UpdateDumpRequest {
|
||||
@@ -439,6 +494,7 @@ export function isUpdateDumpRequest(obj: unknown): obj is UpdateDumpRequest {
|
||||
(o.comment as string).length > VALIDATION.DUMP_COMMENT_MAX
|
||||
) return false;
|
||||
if ("isPrivate" in o && typeof o.isPrivate !== "boolean") return false;
|
||||
if ("categoryIds" in o && !isStringArray(o.categoryIds)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user