From a3176494c9aa967bacd061b421411f26ab25edd3 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Tue, 23 Jun 2026 09:17:52 -0400 Subject: [PATCH] Patcher backend refinements, dep cleanup, regenerated types - Move default_category_for_non_nested validator onto RomFileSchema so top-level files default to category "game" (the v2 patcher's base-file filter relies on this). - Use Annotated Body() in the patch endpoint; check patcher output via anyio async Path. - Drop the now-unused client-side rom-patcher and vite-plugin-static-copy (patching is server-side); simplify the Storybook plugin filter. - Regenerate frontend OpenAPI types. Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/endpoints/responses/rom.py | 12 +- backend/endpoints/roms/patch.py | 2 +- backend/utils/rom_patcher/package-lock.json | 4 +- backend/utils/rom_patcher/package.json | 2 +- backend/utils/rom_patcher/patcher.py | 4 +- frontend/.storybook/main.ts | 12 +- frontend/package-lock.json | 203 ------------------ frontend/package.json | 1 - frontend/src/__generated__/index.ts | 3 +- .../models/ActivityEntrySchema.ts | 19 ++ ..._update_platform_api_platforms__id__put.ts | 11 - .../models/DeviceHeartbeatPayload.ts | 9 + .../__generated__/models/MetadataMediaType.ts | 2 +- 13 files changed, 48 insertions(+), 236 deletions(-) create mode 100644 frontend/src/__generated__/models/ActivityEntrySchema.ts delete mode 100644 frontend/src/__generated__/models/Body_update_platform_api_platforms__id__put.ts create mode 100644 frontend/src/__generated__/models/DeviceHeartbeatPayload.ts diff --git a/backend/endpoints/responses/rom.py b/backend/endpoints/responses/rom.py index 8ceecf011..24a35d98d 100644 --- a/backend/endpoints/responses/rom.py +++ b/backend/endpoints/responses/rom.py @@ -195,6 +195,12 @@ class RomFileSchema(BaseModel): category: RomFileCategory | None audio_meta: RomFileAudioMetaSchema | None = None + @model_validator(mode="after") + def default_category_for_non_nested(self) -> RomFileSchema: + if self.category is None and self.is_top_level: + self.category = RomFileCategory.GAME + return self + class SoundtrackTrackMetaSchema(BaseModel): model_config = ConfigDict(from_attributes=True) @@ -204,12 +210,6 @@ class SoundtrackTrackMetaSchema(BaseModel): file_size_bytes: int audio_meta: RomFileAudioMetaSchema | None = None - @model_validator(mode="after") - def default_category_for_non_nested(self) -> RomFileSchema: - if self.category is None and self.is_top_level: - self.category = RomFileCategory.GAME - return self - class RomMetadataSchema(BaseModel): model_config = ConfigDict(from_attributes=True) diff --git a/backend/endpoints/roms/patch.py b/backend/endpoints/roms/patch.py index 67ee2ff9b..0c8ca2713 100644 --- a/backend/endpoints/roms/patch.py +++ b/backend/endpoints/roms/patch.py @@ -51,7 +51,7 @@ class PatchResponse(BaseModel): async def patch_rom( request: Request, id: Annotated[int, PathVar(description="ROM file ID (the base game file).", ge=1)], - patch_request: PatchRequest = Body(...), + patch_request: Annotated[PatchRequest, Body()], ): """Apply a patch to a ROM file server-side and return the patched file. diff --git a/backend/utils/rom_patcher/package-lock.json b/backend/utils/rom_patcher/package-lock.json index 664850cff..fc242110e 100644 --- a/backend/utils/rom_patcher/package-lock.json +++ b/backend/utils/rom_patcher/package-lock.json @@ -1,10 +1,10 @@ { - "name": "romm-backend-rom-patcher", + "name": "romm-patcher", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "romm-backend-rom-patcher", + "name": "romm-patcher", "license": "AGPL-3.0-only", "dependencies": { "rom-patcher": "github:marcrobledo/RomPatcher.js#v3.2.1" diff --git a/backend/utils/rom_patcher/package.json b/backend/utils/rom_patcher/package.json index 9c344f918..315e2bad9 100644 --- a/backend/utils/rom_patcher/package.json +++ b/backend/utils/rom_patcher/package.json @@ -1,5 +1,5 @@ { - "name": "romm-backend-rom-patcher", + "name": "romm-patcher", "private": true, "description": "Isolated RomPatcher.js install used by the backend server-side patch endpoint.", "license": "AGPL-3.0-only", diff --git a/backend/utils/rom_patcher/patcher.py b/backend/utils/rom_patcher/patcher.py index 5249afd2a..5ba7cbed9 100644 --- a/backend/utils/rom_patcher/patcher.py +++ b/backend/utils/rom_patcher/patcher.py @@ -8,6 +8,8 @@ import asyncio import json from pathlib import Path +from anyio import Path as AnyioPath + PATCHER_SCRIPT = Path(__file__).parent / "patcher.js" SUPPORTED_PATCH_EXTENSIONS = frozenset( @@ -46,5 +48,5 @@ async def apply_patch(rom_path: Path, patch_path: Path, output_path: Path) -> No message = stderr.decode(errors="replace").strip() raise PatcherError(message) - if not output_path.exists(): + if not await AnyioPath(output_path).exists(): raise PatcherError("Patcher did not produce an output file") diff --git a/frontend/.storybook/main.ts b/frontend/.storybook/main.ts index 29213f290..218365dca 100644 --- a/frontend/.storybook/main.ts +++ b/frontend/.storybook/main.ts @@ -22,16 +22,12 @@ const config: StorybookConfig = { "@": fileURLToPath(new URL("../src", import.meta.url)), "@v2": fileURLToPath(new URL("../src/v2", import.meta.url)), }; - // The main app's Vite config registers VitePWA and viteStaticCopy; both - // are app-build concerns (service worker, ROM patcher assets) and have + // The main app's Vite config registers VitePWA; this is an + // app-build concern (service worker, ROM patcher assets) and has // no place in Storybook. Flatten the plugin tree (vite plugins can be - // arrays of plugins) and strip anything PWA or static-copy related. + // arrays of plugins) and strip anything PWA related. function isBlocked(name: string) { - return ( - name.startsWith("vite-plugin-pwa") || - name === "vite-plugin-static-copy" || - name.startsWith("vite-plugin-static-copy") - ); + return name.startsWith("vite-plugin-pwa"); } function keep(plugin: unknown): unknown[] { if (!plugin) return []; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3464d9d31..6b71504c3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -61,7 +61,6 @@ "vite": "^8.0.16", "vite-plugin-mkcert": "^1.17.8", "vite-plugin-pwa": "^1.3.0", - "vite-plugin-static-copy": "^3.4.0", "vite-plugin-vuetify": "^2.0.4", "vitest": "^4.1.5", "vue-tsc": "^2.2.8" @@ -6025,33 +6024,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -6302,19 +6274,6 @@ "node": ">=6.0.0" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/birpc": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", @@ -6350,19 +6309,6 @@ "node": "18 || 20 || >=22" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -6553,44 +6499,6 @@ "node": ">= 16" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -7783,19 +7691,6 @@ "node": ">=10" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -8443,19 +8338,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-boolean-object": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", @@ -9978,16 +9860,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -10252,19 +10124,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", - "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -10737,32 +10596,6 @@ "license": "MIT", "peer": true }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/recast": { "version": "0.23.11", "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", @@ -11792,19 +11625,6 @@ "node": ">=14.0.0" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/token-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", @@ -12361,29 +12181,6 @@ } } }, - "node_modules/vite-plugin-static-copy": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-3.4.0.tgz", - "integrity": "sha512-ekryzCw0ouAOE8tw4RvVL/dfqguXzumsV3FBKoKso4MQ1MUUrUXtl5RI4KpJQUNGqFEsg9kxl4EvDl02YtA9VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.6.0", - "p-map": "^7.0.4", - "picocolors": "^1.1.1", - "tinyglobby": "^0.2.15" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/sapphi-red" - }, - "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, "node_modules/vite-plugin-vuetify": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/vite-plugin-vuetify/-/vite-plugin-vuetify-2.1.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index 2be70314a..a24aae320 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -88,7 +88,6 @@ "vite": "^8.0.16", "vite-plugin-mkcert": "^1.17.8", "vite-plugin-pwa": "^1.3.0", - "vite-plugin-static-copy": "^3.4.0", "vite-plugin-vuetify": "^2.0.4", "vitest": "^4.1.5", "vue-tsc": "^2.2.8" diff --git a/frontend/src/__generated__/index.ts b/frontend/src/__generated__/index.ts index 24c343a27..b6272fd01 100644 --- a/frontend/src/__generated__/index.ts +++ b/frontend/src/__generated__/index.ts @@ -3,6 +3,7 @@ /* tslint:disable */ /* eslint-disable */ +export type { ActivityEntrySchema } from './models/ActivityEntrySchema'; export type { AddFirmwareResponse } from './models/AddFirmwareResponse'; export type { Body_add_collection_api_collections_post } from './models/Body_add_collection_api_collections_post'; export type { Body_add_firmware_api_firmware_post } from './models/Body_add_firmware_api_firmware_post'; @@ -25,7 +26,6 @@ export type { Body_token_api_token_post } from './models/Body_token_api_token_po export type { Body_track_save_api_saves__id__track_post } from './models/Body_track_save_api_saves__id__track_post'; export type { Body_untrack_save_api_saves__id__untrack_post } from './models/Body_untrack_save_api_saves__id__untrack_post'; export type { Body_update_collection_api_collections__id__put } from './models/Body_update_collection_api_collections__id__put'; -export type { Body_update_platform_api_platforms__id__put } from './models/Body_update_platform_api_platforms__id__put'; export type { Body_update_rom_api_roms__id__put } from './models/Body_update_rom_api_roms__id__put'; export type { Body_update_save_api_saves__id__put } from './models/Body_update_save_api_saves__id__put'; export type { Body_update_save_visibility_api_saves__id__visibility_put } from './models/Body_update_save_visibility_api_saves__id__visibility_put'; @@ -61,6 +61,7 @@ export type { DeviceAuthTokenPayload } from './models/DeviceAuthTokenPayload'; export type { DeviceAuthTokenResponse } from './models/DeviceAuthTokenResponse'; export type { DeviceCreatePayload } from './models/DeviceCreatePayload'; export type { DeviceCreateResponse } from './models/DeviceCreateResponse'; +export type { DeviceHeartbeatPayload } from './models/DeviceHeartbeatPayload'; export type { DeviceSchema } from './models/DeviceSchema'; export type { DeviceSyncSchema } from './models/DeviceSyncSchema'; export type { DeviceUpdatePayload } from './models/DeviceUpdatePayload'; diff --git a/frontend/src/__generated__/models/ActivityEntrySchema.ts b/frontend/src/__generated__/models/ActivityEntrySchema.ts new file mode 100644 index 000000000..d289380a8 --- /dev/null +++ b/frontend/src/__generated__/models/ActivityEntrySchema.ts @@ -0,0 +1,19 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ActivityEntrySchema = { + user_id: number; + username: string; + avatar_path: string; + rom_id: number; + rom_name: string; + rom_cover_path?: string; + screenshot_path?: string; + platform_slug: string; + platform_name: string; + device_id: string; + device_type: string; + started_at: string; +}; + diff --git a/frontend/src/__generated__/models/Body_update_platform_api_platforms__id__put.ts b/frontend/src/__generated__/models/Body_update_platform_api_platforms__id__put.ts deleted file mode 100644 index a3f9ed5c3..000000000 --- a/frontend/src/__generated__/models/Body_update_platform_api_platforms__id__put.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type Body_update_platform_api_platforms__id__put = { - /** - * Custom platform name. - */ - custom_name?: (string | null); -}; - diff --git a/frontend/src/__generated__/models/DeviceHeartbeatPayload.ts b/frontend/src/__generated__/models/DeviceHeartbeatPayload.ts new file mode 100644 index 000000000..66db5cd1c --- /dev/null +++ b/frontend/src/__generated__/models/DeviceHeartbeatPayload.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DeviceHeartbeatPayload = { + rom_id: number; + device_id: string; +}; + diff --git a/frontend/src/__generated__/models/MetadataMediaType.ts b/frontend/src/__generated__/models/MetadataMediaType.ts index 076a998da..72306f31d 100644 --- a/frontend/src/__generated__/models/MetadataMediaType.ts +++ b/frontend/src/__generated__/models/MetadataMediaType.ts @@ -2,4 +2,4 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export type MetadataMediaType = 'bezel' | 'box2d' | 'box2d_back' | 'box3d' | 'miximage' | 'miximage_v2' | 'physical' | 'screenshot' | 'title_screen' | 'marquee' | 'logo' | 'fanart' | 'video' | 'video_normalized' | 'manual'; +export type MetadataMediaType = 'bezel' | 'box2d' | 'box2d_back' | 'box2d_side' | 'box3d' | 'miximage' | 'miximage_v2' | 'physical' | 'screenshot' | 'title_screen' | 'marquee' | 'logo' | 'fanart' | 'video' | 'video_normalized' | 'manual';