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

@@ -44,9 +44,9 @@ router.post("/api/avatars/me", authMiddleware, async (ctx) => {
}
updateClientAvatar(authPayload.userId, mime);
const user = getUserById(authPayload.userId);
const { passwordHash: _, ...publicUser } = getUserById(authPayload.userId);
ctx.response.status = 200;
ctx.response.body = { success: true, data: user };
ctx.response.body = { success: true, data: publicUser };
});
router.get("/api/avatars/:userId", async (ctx) => {

View File

@@ -38,7 +38,7 @@ router.get("/dumps/:dumpId/comments", async (ctx) => {
// POST /api/dumps/:dumpId/comments — auth required
router.post("/dumps/:dumpId/comments", authMiddleware, async (ctx) => {
const userId = ctx.state.user.userId as string;
const userId = ctx.state.user.userId;
const dump = getDump(ctx.params.dumpId, userId);
const body = await ctx.request.body.json();
if (!isCreateCommentRequest(body)) {
@@ -62,8 +62,8 @@ router.post("/dumps/:dumpId/comments", authMiddleware, async (ctx) => {
// PATCH /api/comments/:commentId — auth required
router.patch("/comments/:commentId", authMiddleware, async (ctx) => {
const userId = ctx.state.user.userId as string;
const isAdmin = (ctx.state.user.isAdmin ?? false) as boolean;
const userId = ctx.state.user.userId;
const isAdmin = ctx.state.user.isAdmin ?? false;
const body = await ctx.request.body.json();
if (!isUpdateCommentRequest(body)) {
throw new APIException(
@@ -85,8 +85,8 @@ router.patch("/comments/:commentId", authMiddleware, async (ctx) => {
// DELETE /api/comments/:commentId — auth required
router.delete("/comments/:commentId", authMiddleware, (ctx) => {
const userId = ctx.state.user.userId as string;
const isAdmin = (ctx.state.user.isAdmin ?? false) as boolean;
const userId = ctx.state.user.userId;
const isAdmin = ctx.state.user.isAdmin ?? false;
const { dumpId, isPrivate } = deleteComment(
ctx.params.commentId,
userId,

View File

@@ -6,6 +6,7 @@ import {
type FollowStatus,
type PaginatedData,
} from "../model/interfaces.ts";
import { parsePagination } from "../lib/pagination.ts";
import {
followPlaylist,
followUser,
@@ -22,26 +23,16 @@ const router = new Router({ prefix: "/api/follows" });
// GET /api/follows/status
router.get("/status", authMiddleware, (ctx) => {
const status = getFollowStatus(ctx.state.user.userId as string);
const status = getFollowStatus(ctx.state.user.userId);
const body: APIResponse<FollowStatus> = { success: true, data: status };
ctx.response.body = body;
});
// GET /api/follows/feed/users?page=&limit=
router.get("/feed/users", authMiddleware, (ctx) => {
const page = Math.max(
1,
parseInt(ctx.request.url.searchParams.get("page") ?? "1") || 1,
);
const limit = Math.min(
Math.max(
1,
parseInt(ctx.request.url.searchParams.get("limit") ?? "20") || 20,
),
100,
);
const { page, limit } = parsePagination(ctx.request.url.searchParams);
const { items, total } = getFollowedUsersDumpFeed(
ctx.state.user.userId as string,
ctx.state.user.userId,
page,
limit,
);
@@ -56,19 +47,9 @@ router.get("/feed/users", authMiddleware, (ctx) => {
// GET /api/follows/feed/playlists?page=&limit=
router.get("/feed/playlists", authMiddleware, (ctx) => {
const page = Math.max(
1,
parseInt(ctx.request.url.searchParams.get("page") ?? "1") || 1,
);
const limit = Math.min(
Math.max(
1,
parseInt(ctx.request.url.searchParams.get("limit") ?? "20") || 20,
),
100,
);
const { page, limit } = parsePagination(ctx.request.url.searchParams);
const { items, total } = getFollowedPlaylistsDumpFeed(
ctx.state.user.userId as string,
ctx.state.user.userId,
page,
limit,
);
@@ -83,25 +64,25 @@ router.get("/feed/playlists", authMiddleware, (ctx) => {
// POST /api/follows/users/:userId
router.post("/users/:userId", authMiddleware, (ctx) => {
followUser(ctx.state.user.userId as string, ctx.params.userId);
followUser(ctx.state.user.userId, ctx.params.userId);
ctx.response.status = 204;
});
// DELETE /api/follows/users/:userId
router.delete("/users/:userId", authMiddleware, (ctx) => {
unfollowUser(ctx.state.user.userId as string, ctx.params.userId);
unfollowUser(ctx.state.user.userId, ctx.params.userId);
ctx.response.status = 204;
});
// POST /api/follows/playlists/:playlistId
router.post("/playlists/:playlistId", authMiddleware, (ctx) => {
followPlaylist(ctx.state.user.userId as string, ctx.params.playlistId);
followPlaylist(ctx.state.user.userId, ctx.params.playlistId);
ctx.response.status = 204;
});
// DELETE /api/follows/playlists/:playlistId
router.delete("/playlists/:playlistId", authMiddleware, (ctx) => {
unfollowPlaylist(ctx.state.user.userId as string, ctx.params.playlistId);
unfollowPlaylist(ctx.state.user.userId, ctx.params.playlistId);
ctx.response.status = 204;
});

View File

@@ -19,7 +19,7 @@ router.post("/", authMiddleware, async (ctx: AuthContext) => {
router.get("/:token", async (ctx) => {
try {
await validateInvite(ctx.params.token);
ctx.response.body = { success: true };
ctx.response.body = { success: true, data: null };
} catch {
throw new APIException(
APIErrorCode.NOT_FOUND,

View File

@@ -2,9 +2,9 @@ import { Router } from "@oak/oak";
import {
APIErrorCode,
APIException,
type AuthPayload,
type PaginatedData,
} from "../model/interfaces.ts";
import { parsePagination } from "../lib/pagination.ts";
import { type AuthContext, authMiddleware } from "../middleware/auth.ts";
import {
getNotificationsForUser,
@@ -19,17 +19,7 @@ router.get("/", authMiddleware, (ctx: AuthContext) => {
if (!ctx.state.user) {
throw new APIException(APIErrorCode.UNAUTHORIZED, 401, "Not authenticated");
}
const page = Math.max(
1,
parseInt(ctx.request.url.searchParams.get("page") ?? "1") || 1,
);
const limit = Math.min(
Math.max(
1,
parseInt(ctx.request.url.searchParams.get("limit") ?? "20") || 20,
),
100,
);
const { page, limit } = parsePagination(ctx.request.url.searchParams);
const { items, total } = getNotificationsForUser(
ctx.state.user.userId,
page,
@@ -56,7 +46,7 @@ router.post("/read-all", authMiddleware, (ctx: AuthContext) => {
// PATCH /api/notifications/:id/read
router.patch("/:id/read", authMiddleware, (ctx) => {
const user = ctx.state.user as AuthPayload;
const user = ctx.state.user;
if (!user) {
throw new APIException(APIErrorCode.UNAUTHORIZED, 401, "Not authenticated");
}

View File

@@ -48,7 +48,7 @@ router.post("/register", async (ctx) => {
// Mark invite as used only after the user row is committed
try {
await redeemInvite(body.inviteToken);
redeemInvite(body.inviteToken);
} catch (err) {
console.error("[register] redeemInvite failed (user created):", err);
}
@@ -123,11 +123,13 @@ router.get("/me", authMiddleware, (ctx: AuthContext) => {
);
}
const user = getUserById(ctx.state.user.userId);
const { passwordHash: _, ...publicUser } = getUserById(
ctx.state.user.userId,
);
ctx.response.body = {
success: true,
data: user,
data: publicUser,
};
} catch (err) {
console.error(err);

View File

@@ -16,7 +16,10 @@ import {
} from "../services/vote-service.ts";
import { getUnreadCount } from "../services/notification-service.ts";
import { getUserById } from "../services/user-service.ts";
import { APIException } from "../model/interfaces.ts";
import {
APIException,
type ClientToServerMessage,
} from "../model/interfaces.ts";
const router = new Router();
@@ -78,7 +81,7 @@ router.get("/ws", async (ctx) => {
});
socket.addEventListener("message", (event) => {
let msg: { type: string; dumpId?: string };
let msg: ClientToServerMessage;
try {
msg = JSON.parse(event.data as string);
} catch {
@@ -109,7 +112,7 @@ router.get("/ws", async (ctx) => {
function handleVote(
client: WsClient,
dumpId: string | undefined,
dumpId: string,
action: "cast" | "remove",
): void {
const { socket } = client;
@@ -121,11 +124,6 @@ function handleVote(
return;
}
if (!dumpId) {
socket.send(JSON.stringify({ type: "error", message: "Missing dumpId" }));
return;
}
try {
const newCount = action === "cast"
? castVote(dumpId, client.userId)