v3: code quality pass, various bug fixes
This commit is contained in:
@@ -2,6 +2,20 @@
|
||||
* Backend
|
||||
*/
|
||||
|
||||
// ── Validation constants (shared with frontend via src/config/api.ts) ──────────
|
||||
export const VALIDATION = {
|
||||
USERNAME_MIN: 1,
|
||||
USERNAME_MAX: 32,
|
||||
PASSWORD_MIN: 8,
|
||||
PASSWORD_MAX: 128,
|
||||
DUMP_TITLE_MAX: 200,
|
||||
DUMP_COMMENT_MAX: 5000,
|
||||
PLAYLIST_TITLE_MAX: 100,
|
||||
PLAYLIST_DESCRIPTION_MAX: 2000,
|
||||
COMMENT_BODY_MAX: 5000,
|
||||
USER_DESCRIPTION_MAX: 2000,
|
||||
} as const;
|
||||
|
||||
export interface RichContent {
|
||||
type: string;
|
||||
url: string;
|
||||
@@ -75,19 +89,42 @@ export function isLoginUserRequest(obj: unknown): obj is LoginUserRequest {
|
||||
export function isRegisterUserRequest(
|
||||
obj: unknown,
|
||||
): obj is RegisterUserRequest {
|
||||
return !!obj && typeof obj === "object" &&
|
||||
"username" in obj && typeof obj.username === "string" &&
|
||||
"password" in obj && typeof obj.password === "string" &&
|
||||
"inviteToken" in obj && typeof obj.inviteToken === "string";
|
||||
if (
|
||||
!obj || typeof obj !== "object" ||
|
||||
!("username" in obj) || typeof obj.username !== "string" ||
|
||||
!("password" in obj) || typeof obj.password !== "string" ||
|
||||
!("inviteToken" in obj) || typeof obj.inviteToken !== "string"
|
||||
) return false;
|
||||
const { username, password } = obj as RegisterUserRequest;
|
||||
return /^[a-zA-Z0-9_]{1,32}$/.test(username) &&
|
||||
password.length >= VALIDATION.PASSWORD_MIN &&
|
||||
password.length <= VALIDATION.PASSWORD_MAX;
|
||||
}
|
||||
|
||||
export function isUpdateUserRequest(obj: unknown): obj is UpdateUserRequest {
|
||||
return !!obj && typeof obj === "object" &&
|
||||
(!("username" in obj) || typeof obj.username === "string") &&
|
||||
(!("password" in obj) || typeof obj.password === "string") &&
|
||||
(!("isAdmin" in obj) || typeof obj.isAdmin === "boolean") &&
|
||||
(!("description" in obj) || typeof obj.description === "string" ||
|
||||
obj.description === null);
|
||||
if (!obj || typeof obj !== "object") return false;
|
||||
const o = obj as Record<string, unknown>;
|
||||
if ("username" in o) {
|
||||
if (typeof o.username !== "string") return false;
|
||||
if (!/^[a-zA-Z0-9_]{1,32}$/.test(o.username as string)) return false;
|
||||
}
|
||||
if ("password" in o) {
|
||||
if (typeof o.password !== "string") return false;
|
||||
const len = (o.password as string).length;
|
||||
if (len < VALIDATION.PASSWORD_MIN || len > VALIDATION.PASSWORD_MAX) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ("isAdmin" in o && typeof o.isAdmin !== "boolean") return false;
|
||||
if (
|
||||
"description" in o && typeof o.description !== "string" &&
|
||||
o.description !== null
|
||||
) return false;
|
||||
if (
|
||||
typeof o.description === "string" &&
|
||||
(o.description as string).length > VALIDATION.USER_DESCRIPTION_MAX
|
||||
) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export interface AuthResponse {
|
||||
@@ -200,7 +237,9 @@ export function isCreateCommentRequest(
|
||||
): obj is CreateCommentRequest {
|
||||
if (!obj || typeof obj !== "object") return false;
|
||||
const o = obj as Record<string, unknown>;
|
||||
return typeof o.body === "string" && (o.body as string).trim().length > 0 &&
|
||||
return typeof o.body === "string" &&
|
||||
(o.body as string).trim().length > 0 &&
|
||||
(o.body as string).length <= VALIDATION.COMMENT_BODY_MAX &&
|
||||
(!("parentId" in o) || typeof o.parentId === "string" ||
|
||||
o.parentId === null);
|
||||
}
|
||||
@@ -214,7 +253,9 @@ export function isUpdateCommentRequest(
|
||||
): obj is UpdateCommentRequest {
|
||||
if (!obj || typeof obj !== "object") return false;
|
||||
const o = obj as Record<string, unknown>;
|
||||
return typeof o.body === "string" && (o.body as string).trim().length > 0;
|
||||
return typeof o.body === "string" &&
|
||||
(o.body as string).trim().length > 0 &&
|
||||
(o.body as string).length <= VALIDATION.COMMENT_BODY_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,21 +304,43 @@ export interface ReorderPlaylistRequest {
|
||||
export function isCreatePlaylistRequest(
|
||||
obj: unknown,
|
||||
): obj is CreatePlaylistRequest {
|
||||
return !!obj && typeof obj === "object" &&
|
||||
"title" in obj && typeof obj.title === "string" &&
|
||||
(!("description" in obj) || typeof obj.description === "string" ||
|
||||
obj.description === null) &&
|
||||
"isPublic" in obj && typeof obj.isPublic === "boolean";
|
||||
if (
|
||||
!obj || typeof obj !== "object" ||
|
||||
!("title" in obj) || typeof obj.title !== "string" ||
|
||||
!("isPublic" in obj) || typeof obj.isPublic !== "boolean"
|
||||
) return false;
|
||||
const o = obj as Record<string, unknown>;
|
||||
if ((o.title as string).length === 0 || (o.title as string).length > VALIDATION.PLAYLIST_TITLE_MAX) return false;
|
||||
if (
|
||||
"description" in o && typeof o.description !== "string" &&
|
||||
o.description !== null
|
||||
) return false;
|
||||
if (
|
||||
typeof o.description === "string" &&
|
||||
(o.description as string).length > VALIDATION.PLAYLIST_DESCRIPTION_MAX
|
||||
) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isUpdatePlaylistRequest(
|
||||
obj: unknown,
|
||||
): obj is UpdatePlaylistRequest {
|
||||
return !!obj && typeof obj === "object" &&
|
||||
(!("title" in obj) || typeof obj.title === "string") &&
|
||||
(!("description" in obj) || typeof obj.description === "string" ||
|
||||
obj.description === null) &&
|
||||
(!("isPublic" in obj) || typeof obj.isPublic === "boolean");
|
||||
if (!obj || typeof obj !== "object") return false;
|
||||
const o = obj as Record<string, unknown>;
|
||||
if ("title" in o) {
|
||||
if (typeof o.title !== "string") return false;
|
||||
if ((o.title as string).length === 0 || (o.title as string).length > VALIDATION.PLAYLIST_TITLE_MAX) return false;
|
||||
}
|
||||
if (
|
||||
"description" in o && typeof o.description !== "string" &&
|
||||
o.description !== null
|
||||
) return false;
|
||||
if (
|
||||
typeof o.description === "string" &&
|
||||
(o.description as string).length > VALIDATION.PLAYLIST_DESCRIPTION_MAX
|
||||
) return false;
|
||||
if ("isPublic" in o && typeof o.isPublic !== "boolean") return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isReorderPlaylistRequest(
|
||||
@@ -301,12 +364,20 @@ export interface CreateUrlDumpRequest {
|
||||
export function isCreateUrlDumpRequest(
|
||||
obj: unknown,
|
||||
): obj is CreateUrlDumpRequest {
|
||||
return !!obj &&
|
||||
typeof obj === "object" &&
|
||||
"url" in obj && typeof obj.url === "string" &&
|
||||
(!("comment" in obj) ||
|
||||
typeof obj.comment === "string" || obj.comment === null) &&
|
||||
(!("isPrivate" in obj) || typeof obj.isPrivate === "boolean");
|
||||
if (
|
||||
!obj || typeof obj !== "object" ||
|
||||
!("url" in obj) || typeof obj.url !== "string"
|
||||
) return false;
|
||||
const o = obj as Record<string, unknown>;
|
||||
if (
|
||||
"comment" in o && typeof o.comment !== "string" && o.comment !== null
|
||||
) return false;
|
||||
if (
|
||||
typeof o.comment === "string" &&
|
||||
(o.comment as string).length > VALIDATION.DUMP_COMMENT_MAX
|
||||
) return false;
|
||||
if ("isPrivate" in o && typeof o.isPrivate !== "boolean") return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export interface UpdateDumpRequest {
|
||||
@@ -316,12 +387,18 @@ export interface UpdateDumpRequest {
|
||||
}
|
||||
|
||||
export function isUpdateDumpRequest(obj: unknown): obj is UpdateDumpRequest {
|
||||
return !!obj &&
|
||||
typeof obj === "object" &&
|
||||
(!("url" in obj) || typeof obj.url === "string" || obj.url === null) &&
|
||||
(!("comment" in obj) ||
|
||||
typeof obj.comment === "string" || obj.comment === null) &&
|
||||
(!("isPrivate" in obj) || typeof obj.isPrivate === "boolean");
|
||||
if (!obj || typeof obj !== "object") return false;
|
||||
const o = obj as Record<string, unknown>;
|
||||
if ("url" in o && typeof o.url !== "string" && o.url !== null) return false;
|
||||
if (
|
||||
"comment" in o && typeof o.comment !== "string" && o.comment !== null
|
||||
) return false;
|
||||
if (
|
||||
typeof o.comment === "string" &&
|
||||
(o.comment as string).length > VALIDATION.DUMP_COMMENT_MAX
|
||||
) return false;
|
||||
if ("isPrivate" in o && typeof o.isPrivate !== "boolean") return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user