refactor: replace all explicit any types with proper TypeScript types

Backend resolvers: typed enrichArticle/enrichVideo/enrichModel with DB
and $inferSelect types, SQL<unknown>[] for conditions arrays, proper
enum casts for status/role fields, $inferInsert for .set() updates,
typed raw SQL result rows in gamification, ReplyLike interface for
ctx.reply in auth. Frontend: typed catch blocks with Error/interface
casts, isActiveLink param, adminGetUser response, tags filter callback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 19:25:04 +01:00
parent 8313664d70
commit e236ced12a
30 changed files with 392 additions and 375 deletions

View File

@@ -2,7 +2,7 @@ import { GraphQLError } from "graphql";
import { builder } from "../builder";
import { RecordingType, AdminRecordingListType } from "../types/index";
import { recordings, recording_plays } from "../../db/schema/index";
import { eq, and, desc, ilike, count } from "drizzle-orm";
import { eq, and, desc, ilike, count, type SQL } from "drizzle-orm";
import { slugify } from "../../lib/slugify";
import { awardPoints, checkAchievements } from "../../lib/gamification";
import { requireAdmin } from "../../lib/acl";
@@ -21,7 +21,7 @@ builder.queryField("recordings", (t) =>
if (!ctx.currentUser) throw new GraphQLError("Unauthorized");
const conditions = [eq(recordings.user_id, ctx.currentUser.id)];
if (args.status) conditions.push(eq(recordings.status, args.status as any));
if (args.status) conditions.push(eq(recordings.status, args.status as "draft" | "published"));
if (args.linkedVideoId) conditions.push(eq(recordings.linked_video, args.linkedVideoId));
const limit = args.limit || 50;
@@ -115,7 +115,7 @@ builder.mutationField("createRecording", (t) =>
user_id: ctx.currentUser.id,
tags: args.tags || [],
linked_video: args.linkedVideoId || null,
status: (args.status as any) || "draft",
status: (args.status as "draft" | "published") || "draft",
public: false,
})
.returning();
@@ -174,7 +174,7 @@ builder.mutationField("updateRecording", (t) =>
const updated = await ctx.db
.update(recordings)
.set(updates as any)
.set(updates as Partial<typeof recordings.$inferInsert>)
.where(eq(recordings.id, args.id))
.returning();
@@ -355,9 +355,9 @@ builder.queryField("adminListRecordings", (t) =>
const limit = args.limit ?? 50;
const offset = args.offset ?? 0;
const conditions: any[] = [];
const conditions: SQL<unknown>[] = [];
if (args.search) conditions.push(ilike(recordings.title, `%${args.search}%`));
if (args.status) conditions.push(eq(recordings.status, args.status as any));
if (args.status) conditions.push(eq(recordings.status, args.status as "draft" | "published"));
const where = conditions.length > 0 ? and(...conditions) : undefined;
const [rows, totalRows] = await Promise.all([