108 lines
5.1 KiB
Markdown
108 lines
5.1 KiB
Markdown
---
|
|
phase: 42-wallpapers-social-format-conversion-voice
|
|
plan: 03
|
|
subsystem: api
|
|
tags: [sharp, ffmpeg-static, xlsx, csv-parse, file-type, convert-renderer, multipart, magic-byte, content-jobs, typescript]
|
|
|
|
# Dependency graph
|
|
requires:
|
|
- phase: 42-wallpapers-social-format-conversion-voice
|
|
plan: 01
|
|
provides: ConvertBundle type, convert case in renderContent switch, converter-capabilities service
|
|
- phase: 40-content-job-infra
|
|
provides: contentJobStore, contentJobRunner, async job pattern
|
|
provides:
|
|
- server/src/services/renderers/convert-renderer.ts — full format conversion router (sharp/ffmpeg/xlsx/AI-bridge)
|
|
- server/src/routes/convert.ts — POST /api/companies/:companyId/convert (multipart, 202)
|
|
- server/src/routes/convert.ts — GET /api/system/converters (capability map)
|
|
- convert route mounted in app.ts
|
|
affects:
|
|
- 42-06 (UI wiring — ConvertPanel calls these endpoints)
|
|
|
|
# Tech tracking
|
|
tech-stack:
|
|
added: []
|
|
patterns:
|
|
- ffmpeg-static stdin/stdout pipe pattern for audio/video conversion
|
|
- csv-parse/sync for synchronous CSV parsing with column headers
|
|
- xlsx.read/write for XLSX ↔ JSON ↔ CSV round-trips
|
|
- fileTypeFromBuffer magic-byte MIME validation with text-based extension allowlist
|
|
- multer memoryStorage + runSingleFileUpload wrapper (follows chat-files.ts pattern)
|
|
|
|
key-files:
|
|
created:
|
|
- server/src/routes/convert.ts
|
|
modified:
|
|
- server/src/services/renderers/convert-renderer.ts
|
|
- server/src/app.ts
|
|
|
|
key-decisions:
|
|
- "ffmpegPath cast as unknown as string required — ffmpeg-static typings return string|null but binary is always present; matches documented Pitfall 3 from research"
|
|
- "TEXT_BASED_EXTENSIONS allowlist prevents false 422 rejections for CSV/JSON/SVG which have no magic bytes"
|
|
- "sourceMime resolved from extension map rather than multer claim to avoid browser MIME inconsistencies"
|
|
- "StorageService passed through to convertRoutes for future asset storage but not used in job dispatch path (job runner handles storage)"
|
|
|
|
# Metrics
|
|
duration: 3min
|
|
completed: 2026-04-04
|
|
---
|
|
|
|
# Phase 42 Plan 03: Format Conversion Renderer and Multipart Route
|
|
|
|
**Full convert-renderer.ts routing sharp/ffmpeg/xlsx/AI-bridge by format pair, multipart upload route with magic-byte MIME validation returning 202, and GET /api/system/converters capability endpoint**
|
|
|
|
## Performance
|
|
|
|
- **Duration:** 3 min
|
|
- **Started:** 2026-04-04T22:12:21Z
|
|
- **Completed:** 2026-04-04T22:15:16Z
|
|
- **Tasks:** 2
|
|
- **Files modified:** 3 (1 replaced, 1 created, 1 modified)
|
|
|
|
## Accomplishments
|
|
|
|
- Replaced stub convert-renderer.ts with full implementation routing four format categories:
|
|
- Image pairs (PNG/JPG/WebP/GIF/SVG) → sharp with density:300 for SVG input
|
|
- Audio/video pairs → ffmpeg-static via stdin/stdout pipe (`ffmpegPath as unknown as string`)
|
|
- Data pairs (CSV/JSON/XLSX) → csv-parse/sync + xlsx for all six conversion combinations
|
|
- All other pairs → AI-bridge via puterChatComplete with system prompt
|
|
- Created convert.ts route with multer memoryStorage, fileTypeFromBuffer magic-byte validation, and 202 job dispatch
|
|
- Implemented MIME mismatch detection: returns 422 with `{ error, actualMime, claimedMime }` for misnamed binary files
|
|
- Text-based formats (CSV, JSON, SVG, TXT, HTML, MD, XML) allowed through without magic-byte check (they return null)
|
|
- Wired GET /api/system/converters endpoint returning converterCapabilitiesService result
|
|
- Mounted convertRoutes in app.ts after contentJobRoutes
|
|
|
|
## Task Commits
|
|
|
|
Each task was committed atomically:
|
|
|
|
1. **Task 1: Implement convert renderer with format routing** - `d5f7586d` (feat)
|
|
2. **Task 2: Create multipart convert route with MIME validation and wire to app.ts** - `84f97a43` (feat)
|
|
|
|
## Files Created/Modified
|
|
|
|
- `server/src/services/renderers/convert-renderer.ts` - Full implementation: isImageFormat/isAVFormat/isDataFormat helpers, convertImage/convertAV/convertData/convertViaAiBridge functions, renderConvert entry point
|
|
- `server/src/routes/convert.ts` - Multipart upload route: multer, fileTypeFromBuffer MIME validation, content job dispatch, converter capability endpoint
|
|
- `server/src/app.ts` - Import and mount convertRoutes after contentJobRoutes
|
|
|
|
## Decisions Made
|
|
|
|
- `ffmpegPath as unknown as string` cast used in spawn — ffmpeg-static typings declare `string | null` but the binary is always present when the package is installed; matches Pitfall 3 documented in 42-RESEARCH.md
|
|
- `TEXT_BASED_EXTENSIONS` allowlist prevents false 422 rejection for text-based formats whose `fileTypeFromBuffer` returns null (no magic bytes)
|
|
- Source MIME resolved from extension map rather than multer's `file.mimetype` to avoid browser-reported MIME inconsistencies (e.g. Chrome reports `text/csv` but Safari may report `application/csv`)
|
|
|
|
## Deviations from Plan
|
|
|
|
None — plan executed exactly as written.
|
|
|
|
## Known Stubs
|
|
|
|
None — all conversion paths are fully implemented.
|
|
|
|
## Self-Check: PASSED
|
|
|
|
- `server/src/services/renderers/convert-renderer.ts` — FOUND
|
|
- `server/src/routes/convert.ts` — FOUND
|
|
- Task 1 commit `d5f7586d` — FOUND
|
|
- Task 2 commit `84f97a43` — FOUND
|
|
- `tsc --noEmit` — zero errors
|