v3: code quality pass

This commit is contained in:
khannurien
2026-03-24 18:47:05 +00:00
parent cd4076343b
commit c293f3e706
39 changed files with 1464 additions and 1555 deletions

View File

@@ -4,12 +4,7 @@ import {
type Comment,
} from "../model/interfaces.ts";
import { type SQLOutputValue } from "node:sqlite";
import {
type CommentRow,
commentRowToApi,
db,
isCommentRow,
} from "../model/db.ts";
import { commentRowToApi, db, isCommentRow } from "../model/db.ts";
import { notifyMentions } from "./notification-service.ts";
const SELECT_COLS =
@@ -23,7 +18,14 @@ function fetchComment(commentId: string): Comment {
if (!row || !isCommentRow(row as Record<string, SQLOutputValue>)) {
throw new APIException(APIErrorCode.NOT_FOUND, 404, "Comment not found");
}
return commentRowToApi(row as CommentRow);
if (!isCommentRow(row)) {
throw new APIException(
APIErrorCode.SERVER_ERROR,
500,
"Malformed comment data",
);
}
return commentRowToApi(row);
}
export function getComments(dumpId: string): Comment[] {
@@ -31,15 +33,14 @@ export function getComments(dumpId: string): Comment[] {
`SELECT ${SELECT_COLS} FROM comments c JOIN users u ON c.user_id = u.id
WHERE c.dump_id = ? ORDER BY c.created_at ASC;`,
).all(dumpId);
const typed = rows as Parameters<typeof isCommentRow>[0][];
if (!typed.every(isCommentRow)) {
if (!rows.every(isCommentRow)) {
throw new APIException(
APIErrorCode.SERVER_ERROR,
500,
"Malformed comment data",
);
}
return typed.map(commentRowToApi);
return rows.map(commentRowToApi);
}
export function createComment(

View File

@@ -448,11 +448,11 @@ export function getVotedDumpsByUser(
const dumpCols = SELECT_COLS_ALIASED;
let totalRow: { count: number } | undefined;
let rawRows: unknown[];
let rows: unknown[];
if (requestingUserId === userId) {
// Own profile: include private dumps the user themselves voted on and owns.
rawRows = db.prepare(
rows = db.prepare(
`SELECT ${dumpCols}
FROM dumps d
INNER JOIN votes v ON d.id = v.dump_id
@@ -465,7 +465,7 @@ export function getVotedDumpsByUser(
WHERE v.user_id = ? AND (d.is_private = 0 OR d.user_id = ?);`,
).get(userId, userId) as { count: number } | undefined;
} else {
rawRows = db.prepare(
rows = db.prepare(
`SELECT ${dumpCols}
FROM dumps d
INNER JOIN votes v ON d.id = v.dump_id
@@ -479,7 +479,6 @@ export function getVotedDumpsByUser(
).get(userId) as { count: number } | undefined;
}
const rows = rawRows as Parameters<typeof isDumpRow>[0][];
if (!rows.every(isDumpRow)) {
throw new APIException(
APIErrorCode.SERVER_ERROR,

View File

@@ -114,12 +114,12 @@ export function getFollowStatus(followerId: string): FollowStatus {
const rawUserRows = db.prepare(
`SELECT id, follower_id, followed_user_id, followed_playlist_id, created_at
FROM follows WHERE follower_id = ? AND followed_user_id IS NOT NULL;`,
).all(followerId) as Parameters<typeof isFollowRow>[0][];
).all(followerId);
const rawPlaylistRows = db.prepare(
`SELECT id, follower_id, followed_user_id, followed_playlist_id, created_at
FROM follows WHERE follower_id = ? AND followed_playlist_id IS NOT NULL;`,
).all(followerId) as Parameters<typeof isFollowRow>[0][];
).all(followerId);
if (!rawUserRows.every(isFollowRow) || !rawPlaylistRows.every(isFollowRow)) {
throw new APIException(
@@ -207,8 +207,7 @@ export function getFollowedPlaylistsDumpFeed(
AND d.is_private = 0;`,
).get(followerId) as { count: number } | undefined;
const playlistFeedRows = rawRows as Parameters<typeof isDumpRow>[0][];
if (!playlistFeedRows.every(isDumpRow)) {
if (!rawRows.every(isDumpRow)) {
throw new APIException(
APIErrorCode.SERVER_ERROR,
500,
@@ -216,7 +215,7 @@ export function getFollowedPlaylistsDumpFeed(
);
}
return {
items: playlistFeedRows.map(dumpRowToApi),
items: rawRows.map(dumpRowToApi),
total: totalRow?.count ?? 0,
};
}
@@ -246,7 +245,7 @@ export function getFollowedPlaylistsByUser(
AND p.is_public = 1
ORDER BY f.created_at DESC
LIMIT ? OFFSET ?;`,
).all(userId, limit, offset) as Parameters<typeof isPlaylistRow>[0][];
).all(userId, limit, offset);
if (!rawRows.every(isPlaylistRow)) {
throw new APIException(

View File

@@ -57,7 +57,7 @@ export function getNotificationsForUser(
const offset = (page - 1) * limit;
const rawRows = db.prepare(
`SELECT * FROM notifications WHERE user_id = ? ORDER BY created_at DESC LIMIT ? OFFSET ?;`,
).all(userId, limit, offset) as Parameters<typeof isNotificationRow>[0][];
).all(userId, limit, offset);
const totalRow = db.prepare(
`SELECT COUNT(*) as count FROM notifications WHERE user_id = ?;`,
@@ -195,6 +195,7 @@ export function notifyUserFollowersNewDump(
sendToUser(row.follower_id, {
type: "notification_created",
notification: {
id: crypto.randomUUID(),
userId: row.follower_id,
type: "user_dump_posted",
data,

View File

@@ -1,4 +1,3 @@
import type { SQLOutputValue } from "node:sqlite";
import {
APIErrorCode,
APIException,
@@ -29,7 +28,7 @@ import {
import { makeSlug, UUID_RE } from "../lib/slugify.ts";
const DUMP_SELECT_COLS =
"id, kind, title, slug, comment, user_id, created_at, url, rich_content, file_name, file_mime, file_size, vote_count, is_private";
"id, kind, title, slug, comment, user_id, created_at, updated_at, url, rich_content, file_name, file_mime, file_size, vote_count, is_private";
const PLAYLIST_SELECT = `p.*, u.username as owner_username,
(SELECT COUNT(*) FROM playlist_dumps pd WHERE pd.playlist_id = p.id) as dump_count
@@ -340,7 +339,7 @@ export function getPlaylistMembershipsForDump(
LEFT JOIN playlist_dumps pd ON pd.playlist_id = p.id AND pd.dump_id = ?
WHERE p.user_id = ?
ORDER BY p.created_at DESC;`,
).all(dumpId, userId) as Array<Record<string, SQLOutputValue>>;
).all(dumpId, userId);
return rows.map((row) => {
if (!isPlaylistRow(row)) {

View File

@@ -3,6 +3,7 @@ import type {
Dump,
OnlineUser,
Playlist,
ServerToClientMessage,
User,
} from "../model/interfaces.ts";
@@ -51,13 +52,13 @@ export function getOnlineUsers(): OnlineUser[] {
return Array.from(seen.values());
}
function send(socket: WebSocket, data: unknown): void {
function send(socket: WebSocket, data: ServerToClientMessage): void {
if (socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify(data));
}
}
export function sendToUser(userId: string, data: unknown): void {
export function sendToUser(userId: string, data: ServerToClientMessage): void {
for (const client of clients) {
if (client.userId === userId) {
send(client.socket, data);
@@ -109,7 +110,7 @@ export function broadcastVoteUpdate(
function sendToPlaylistAudience(
playlist: Pick<Playlist, "isPublic" | "userId">,
data: unknown,
data: ServerToClientMessage,
): void {
for (const client of clients) {
if (playlist.isPublic || client.userId === playlist.userId) {