docs: plan production furni asset fix
This commit is contained in:
1 parent
44b4b3b45c
commit
a81af2d71a
1 file changed
+126
@@ -0,0 +1,126 @@
|
||||
# Production Furni Assets Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Make every furni import write the icon, Nitro bundle, and FurnitureData entry into the directories served by the production client under `/var/www/Gamedata`.
|
||||
|
||||
**Architecture:** Extend the central furni asset resolver with a separate Gamedata layout while preserving CMS-local and Nitro-Files targets. Both import paths consume the same resolved targets; FurnitureData resolves the same Gamedata root independently so metadata and binary assets stay aligned.
|
||||
|
||||
**Tech Stack:** TypeScript 7, Node.js filesystem APIs, Vitest 4, Next.js 16.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Preserve all existing `nitro_files_root`, `furni_*_dir`, and `furni_data_mirror_path` behavior.
|
||||
- Auto-detect `/var/www/Gamedata` only when the directory exists; allow `gamedata_root` to override it.
|
||||
- Do not copy source SWFs into the Gamedata tree.
|
||||
- Report live mirror failures in the import warnings.
|
||||
- Use test-first development and run the production build before completion.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Resolve the production Gamedata layout
|
||||
|
||||
**Files:**
|
||||
- Create: `src/lib/services/furni-asset-dirs.test.ts`
|
||||
- Modify: `src/lib/services/furni-asset-dirs.ts`
|
||||
- Modify: `src/lib/services/furni-data.ts`
|
||||
- Modify: `src/app/admin/settings/cms-settings-config.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `getGamedataRoot(): Promise<string>`.
|
||||
- Produces: Gamedata mirror entries from `getFurniAssetWriteTargets()` with `<root>/icons` and `<root>/bundled/furniture`.
|
||||
- Consumes: `siteSettings.get("gamedata_root", "")` and `existsSync(DEFAULT_GAMEDATA_ROOT)`.
|
||||
|
||||
- [ ] **Step 1: Write failing resolver tests**
|
||||
|
||||
Mock `siteSettings.get` and `existsSync`, then assert that a configured Gamedata root produces:
|
||||
|
||||
```ts
|
||||
expect(targets.mirrorDirs).toContainEqual({
|
||||
swfDir: targets.swfDir,
|
||||
iconDir: path.join(gamedataRoot, "icons"),
|
||||
nitroDir: path.join(gamedataRoot, "bundled/furniture"),
|
||||
});
|
||||
```
|
||||
|
||||
Also assert that an absent unconfigured root adds no Gamedata mirror and that a duplicate primary destination is de-duplicated.
|
||||
|
||||
- [ ] **Step 2: Run the resolver test and verify RED**
|
||||
|
||||
Run: `pnpm test src/lib/services/furni-asset-dirs.test.ts`
|
||||
|
||||
Expected: FAIL because `gamedata_root` is not read and the Gamedata mirror is absent.
|
||||
|
||||
- [ ] **Step 3: Implement the Gamedata resolver**
|
||||
|
||||
Add `DEFAULT_GAMEDATA_ROOT`, `getGamedataRoot()`, and a pure Gamedata mapping that uses the primary `swfDir` while mapping icons and Nitro bundles to the live locations. Merge this candidate with the existing Nitro-Files candidate and remove identical directory triples.
|
||||
|
||||
- [ ] **Step 4: Extend FurnitureData and the settings form**
|
||||
|
||||
Make `getFurnitureDataWritePaths()` include `<gamedata_root>/config/FurnitureData.json`, after any explicit `furni_data_mirror_path`. Add a documented `gamedata_root` text setting with `/var/www/Gamedata` as the placeholder.
|
||||
|
||||
- [ ] **Step 5: Run resolver tests and verify GREEN**
|
||||
|
||||
Run: `pnpm test src/lib/services/furni-asset-dirs.test.ts`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 6: Commit Task 1**
|
||||
|
||||
```text
|
||||
fix: map furni imports to production gamedata
|
||||
```
|
||||
|
||||
### Task 2: Mirror manual Nitro uploads
|
||||
|
||||
**Files:**
|
||||
- Create: `src/lib/services/upload-import.test.ts`
|
||||
- Modify: `src/lib/services/upload-import.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `getFurniAssetWriteTargets(): Promise<FurniAssetWriteTargets>`.
|
||||
- Produces: manual upload copies at every unique `mirrorDirs[].iconDir` and `mirrorDirs[].nitroDir`.
|
||||
|
||||
- [ ] **Step 1: Write a failing manual-upload test**
|
||||
|
||||
Mock the database and metadata dependencies, provide temporary primary and Gamedata directories, call `uploadSingleFurni`, and assert:
|
||||
|
||||
```ts
|
||||
await expect(fs.readFile(path.join(liveNitroDir, "chair.nitro"))).resolves.toEqual(nitroBuffer);
|
||||
await expect(fs.readFile(path.join(liveIconDir, "chair_icon.png"))).resolves.toEqual(iconBuffer);
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the upload test and verify RED**
|
||||
|
||||
Run: `pnpm test src/lib/services/upload-import.test.ts`
|
||||
|
||||
Expected: FAIL because manual uploads currently write only to the primary CMS directories.
|
||||
|
||||
- [ ] **Step 3: Implement manual mirroring**
|
||||
|
||||
Resolve all write targets, create their directories through the existing `ensureDirectories()`, and copy the successfully written primary Nitro and optional icon files to each unique mirror. Catch each copy error separately and append a warning containing the destination path.
|
||||
|
||||
- [ ] **Step 4: Run the upload test and verify GREEN**
|
||||
|
||||
Run: `pnpm test src/lib/services/upload-import.test.ts`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 5: Run focused and full verification**
|
||||
|
||||
Run:
|
||||
|
||||
```text
|
||||
pnpm test src/lib/services/furni-asset-dirs.test.ts src/lib/services/upload-import.test.ts src/lib/services/furni-import.test.ts
|
||||
pnpm typecheck
|
||||
pnpm test
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Expected: every command exits with status 0.
|
||||
|
||||
- [ ] **Step 6: Commit Task 2**
|
||||
|
||||
```text
|
||||
fix: mirror manual furni uploads to live assets
|
||||
```
|
||||
Reference in new issue
Block a user