From 74bf5962d9c4163b905befa098fdf7358da5588b Mon Sep 17 00:00:00 2001 From: Neal Shah Date: Wed, 15 Jul 2026 00:53:18 -0400 Subject: [PATCH] fix type errors --- .../drivers/subdomain/SubdomainDriver.ts | 12 +++--- src/backend/drivers/workers/WorkerDriver.ts | 19 ++++----- .../localworker/LocalWorkerService.ts | 15 +------ .../stores/subdomain/SubdomainStore.ts | 41 +++++++++++++++---- 4 files changed, 49 insertions(+), 38 deletions(-) diff --git a/src/backend/drivers/subdomain/SubdomainDriver.ts b/src/backend/drivers/subdomain/SubdomainDriver.ts index f60d0fea8..7fd69a3d4 100644 --- a/src/backend/drivers/subdomain/SubdomainDriver.ts +++ b/src/backend/drivers/subdomain/SubdomainDriver.ts @@ -244,9 +244,11 @@ export class SubdomainDriver extends PuterDriver { if (object.domain !== undefined) patch.domain = object.domain != null ? String(object.domain) : null; - const updated = await this.stores.subdomain.update(row.uuid, patch, { - userId: row.user_id, - }); + const updated = await this.stores.subdomain.update( + String(row.uuid), + patch, + { userId: row.user_id as number }, + ); const [shaped] = await this.#hydrateRows( updated ? [updated as Record] : [], ); @@ -351,8 +353,8 @@ export class SubdomainDriver extends PuterDriver { } await this.#checkWriteAccess(row, actor); - await this.stores.subdomain.deleteByUuid(row.uuid, { - userId: row.user_id, + await this.stores.subdomain.deleteByUuid(String(row.uuid), { + userId: row.user_id as number, }); try { diff --git a/src/backend/drivers/workers/WorkerDriver.ts b/src/backend/drivers/workers/WorkerDriver.ts index e285ca37d..8c1f2514f 100644 --- a/src/backend/drivers/workers/WorkerDriver.ts +++ b/src/backend/drivers/workers/WorkerDriver.ts @@ -25,6 +25,7 @@ import { Context } from '../../core/context.js'; import { HttpError, type LegacyErrorCodes } from '../../core/http/HttpError.js'; import { assertVerifiedEmail } from '../../core/http/verifiedEmail.js'; import type { FSEntry } from '../../stores/fs/FSEntry.js'; +import type { SubdomainRow } from '../../stores/subdomain/SubdomainStore.js'; import { PuterDriver } from '../types.js'; import { loadFileInput } from '../util/fileInput.js'; @@ -313,7 +314,7 @@ export class WorkerDriver extends PuterDriver { const actor = this.#requireActor(); const workerName = args.workerName as string | undefined; - let rows: Array>; + let rows: SubdomainRow[]; if (typeof workerName === 'string' && workerName.length > 0) { const sub = await this.stores.subdomain.getBySubdomain( `${WORKER_SUBDOMAIN_PREFIX}${workerName}`, @@ -495,7 +496,7 @@ export class WorkerDriver extends PuterDriver { } #checkWorkerWriteAccess( - row: Record, + row: SubdomainRow, actor: Actor & { user: { id: number } }, errorStatus: number, errorMessage: string, @@ -738,14 +739,12 @@ export class WorkerDriver extends PuterDriver { ); } - async #listWorkerRowsForEntry( - entry: FSEntry, - ): Promise>> { + async #listWorkerRowsForEntry(entry: FSEntry): Promise { const workerSubs = await this.stores.subdomain.listByUserIdAndPrefix( entry.userId, WORKER_SUBDOMAIN_PREFIX, ); - return workerSubs.filter((r: Record) => { + return workerSubs.filter((r) => { return ( String(r.root_dir_id) === String(entry.id) || String(r.root_dir_id) === String(entry.uuid) || @@ -757,17 +756,17 @@ export class WorkerDriver extends PuterDriver { async #listWorkerRowsUnderPath( userId: number, parentPath: string, - ): Promise>> { + ): Promise { const workerSubs = await this.stores.subdomain.listByUserIdAndPrefix( userId, WORKER_SUBDOMAIN_PREFIX, ); const rootDirIds = workerSubs - .map((r: Record) => r.root_dir_id) + .map((r) => r.root_dir_id) .filter((id): id is number => typeof id === 'number'); const entriesById = await this.stores.fsEntry.getEntriesByIds(rootDirIds); - return workerSubs.filter((row: Record) => { + return workerSubs.filter((row) => { const rootDirId = row.root_dir_id; if (typeof rootDirId !== 'number') return false; const entry = entriesById.get(rootDirId); @@ -784,7 +783,7 @@ export class WorkerDriver extends PuterDriver { } async #deleteWorkerForSourceRow( - row: Record, + row: SubdomainRow, userId: number, ): Promise { const workerFullName = String(row.subdomain ?? ''); diff --git a/src/backend/services/localworker/LocalWorkerService.ts b/src/backend/services/localworker/LocalWorkerService.ts index f74660368..d82c6d2e4 100644 --- a/src/backend/services/localworker/LocalWorkerService.ts +++ b/src/backend/services/localworker/LocalWorkerService.ts @@ -4,6 +4,7 @@ import { Actor } from '../../core'; import { loadFileInput } from '../../drivers/util/fileInput'; import { getWorkerPreamble } from '../../drivers/workers/WorkerDriver'; import { puterStores } from '../../stores'; +import type { SubdomainRow } from '../../stores/subdomain/SubdomainStore'; import { LayerInstances } from '../../types'; import { PuterService } from '../types'; @@ -15,20 +16,6 @@ const MAX_SOURCE_SIZE = 10 * 1024 * 1024; // 10 MB const WORKER_IDLE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes const IDLE_SWEEP_INTERVAL_MS = 60 * 1000; // sweep cadence -interface SubdomainRow { - id: number; - uuid: string; - ts: number | string; // system timestamp - subdomain: string; // immutable name - user_id: number; // owner - app_owner: number | null; // owning app, if any - protected: 0 | 1; // access gate - database_id: string | null; // Cloudflare D1 binding - root_dir_id: number | null; // editable - associated_app_id: string | null; // editable - domain: string | null; // custom domain, editable -} - const activeWorkers = new Map(); // workerName -> last dispatch/deploy time (ms). Drives the idle sweep. const lastAccess = new Map(); diff --git a/src/backend/stores/subdomain/SubdomainStore.ts b/src/backend/stores/subdomain/SubdomainStore.ts index 91c90c81d..8b9362a30 100644 --- a/src/backend/stores/subdomain/SubdomainStore.ts +++ b/src/backend/stores/subdomain/SubdomainStore.ts @@ -20,6 +20,29 @@ import { v4 as uuidv4 } from 'uuid'; import { PuterStore } from '../types'; +/** + * A row from the `subdomains` table (the shape `getBySubdomain` / `getByUuid` + * resolve to). Kept alongside the store so callers share one definition instead + * of redeclaring it locally. + */ +export interface SubdomainRow { + id: number; + uuid: string; + ts: number | string; // system timestamp + subdomain: string; // immutable name + user_id: number; // owner + app_owner: number | null; // owning app, if any + protected: 0 | 1; // access gate + database_id: string | null; // Cloudflare D1 binding + root_dir_id: number | null; // editable + associated_app_id: string | null; // editable + domain: string | null; // custom domain, editable + // `SELECT *` may surface columns not modelled above (and callers still + // treat rows as `Record` in places). The index signature + // keeps the named fields strongly typed while staying Record-compatible. + [key: string]: unknown; +} + // Columns that may not be set through an `update` patch map. Defence-in-depth // against future callers (admin routes, extensions, new REST handlers) that // might forward `req.body` straight into the store: the driver's update @@ -71,7 +94,7 @@ export class SubdomainStore extends PuterStore { userId?: number | undefined; primary?: boolean; } = {}, - ) { + ): Promise { const where = userId !== undefined ? 'WHERE `uuid` = ? AND `user_id` = ?' @@ -81,10 +104,10 @@ export class SubdomainStore extends PuterStore { const rows = primary ? await this.clients.db.pread(sql, params) : await this.clients.db.read(sql, params); - return rows[0] ?? null; + return (rows[0] as unknown as SubdomainRow) ?? null; } - async getBySubdomain(subdomain: string) { + async getBySubdomain(subdomain: string): Promise { if (!subdomain) return null; const cacheKey = this.#cacheKey(subdomain); @@ -92,7 +115,7 @@ export class SubdomainStore extends PuterStore { const raw = await this.clients.redis.get(cacheKey); if (raw === NEGATIVE_CACHE_MARKER) return null; if (raw) { - const parsed = JSON.parse(raw); + const parsed = JSON.parse(raw) as SubdomainRow | null; if (parsed) return parsed; } } catch { @@ -103,7 +126,7 @@ export class SubdomainStore extends PuterStore { 'SELECT * FROM `subdomains` WHERE `subdomain` = ? LIMIT 1', [subdomain], ); - const row = rows[0] ?? null; + const row = (rows[0] as unknown as SubdomainRow | undefined) ?? null; if (row) { this.clients.redis @@ -173,7 +196,7 @@ export class SubdomainStore extends PuterStore { userId: number, prefix: string, extra: { appId?: number } = {}, - ) { + ): Promise { if (!userId || prefix == null) return []; const like = `${prefix}%`; @@ -190,7 +213,7 @@ export class SubdomainStore extends PuterStore { ); } - return rows; + return rows as unknown as SubdomainRow[]; } // -- Writes ------------------------------------------------------- @@ -248,14 +271,14 @@ export class SubdomainStore extends PuterStore { async update( uuid: string, - patch: unknown, + patch: Record, { userId, }: { userId?: number | undefined; } = {}, ) { - const allowed: unknown = {}; + const allowed: Record = {}; for (const [k, v] of Object.entries(patch)) { if (READ_ONLY_COLUMNS.has(k)) continue; allowed[k] = v;