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) <noreply@anthropic.com>
This commit is contained in:
Georges-Antoine Assi
2026-06-23 09:17:52 -04:00
parent ececc2ec91
commit a3176494c9
13 changed files with 48 additions and 236 deletions

View File

@@ -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)

View File

@@ -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.

View File

@@ -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"

View File

@@ -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",

View File

@@ -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")

View File

@@ -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 [];

View File

@@ -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",

View File

@@ -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"

View File

@@ -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';

View File

@@ -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;
};

View File

@@ -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);
};

View File

@@ -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;
};

View File

@@ -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';