Commit Graph
72 Commits
Author SHA1 Message Date
anthonyandClaude Sonnet 5 598781f417 feat: register ICO/HEIC/HEIF converters in the worker and pass iconSize through
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:16:25 +02:00
anthonyandClaude Sonnet 5 67bdfbf3de feat: wire ICO/HEIC/HEIF converters and iconSize validation into the API
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:15:14 +02:00
anthonyandClaude Sonnet 5 be43eac13a feat: thread iconSize through jobRepository
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:13:15 +02:00
anthonyandClaude Sonnet 5 1ff660a3d2 feat: add iconSize column to ConversionJob
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:12:32 +02:00
anthonyandClaude Sonnet 5 8795f65055 feat: add source-only HEIC/HEIF image converter
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:10:41 +02:00
anthonyandClaude Sonnet 5 05ef7dfca1 feat: add bidirectional ICO image converter
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:09:56 +02:00
anthonyandClaude Sonnet 5 39b296f5d3 feat: add ico MIME type and heif->heic extension alias
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:08:46 +02:00
anthony 2b2d8a9e6f Add implementation plan for HEIC/HEIF/ICO image format support
9 tasks covering mime.js, the ico/heic converter modules, a new
iconSize column on ConversionJob, app.js/worker.js wiring, and the
frontend size picker. Every code snippet (icojs encode/decode, the
heic-convert ESM import, prisma db execute/migrate status flags) was
verified against the actual installed/resolved packages rather than
guessed.
2026-07-31 09:54:06 +02:00
anthony fc217d8aae Add HEIC/HEIF test fixtures for upcoming heic-convert-based decoding
Reuses libheif's own LGPL/MIT-licensed with-alpha-512x512.heic sample
under both a .heic and .heif filename, verified to decode correctly
via heic-convert (the library the image format work will use). No
suitably-licensed HEVC/mif1-branded .heif-distinct sample exists that
is compatible with the pinned libheif-js version, and file-type detects
both extensions identically regardless of container brand, so the
duplicate content is sufficient to exercise the heif source-format
code path.
2026-07-31 09:48:11 +02:00
anthony 587ace3366 package.json > create deploy script 2026-07-31 09:31:13 +02:00
anthony bac93c853a Add design spec for HEIC/HEIF (source-only) and ICO image formats
Drops HDR from scope: the installed sharp/libvips build has it fully
disabled and no viable library exists for either direction.
2026-07-31 09:30:26 +02:00
anthony b40a7d340f Add support for csv + xlsx 2026-07-31 09:17:08 +02:00
anthony 4c408d83e9 Markdown conversion doc + CLAUDE.md improve 2026-07-31 08:40:04 +02:00
anthonyandClaude Sonnet 5 756e875fd9 feat: add md<->txt passthrough document converters
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 08:34:51 +02:00
anthonyandClaude Sonnet 5 70c0c5ce6b feat: add md<->docx document converters
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 08:33:31 +02:00
anthonyandClaude Sonnet 5 82533ca436 feat: add md<->pdf document converters
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 08:31:53 +02:00
anthonyandClaude Sonnet 5 1595c5e0af feat: add md<->html document converters
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 08:31:00 +02:00
anthonyandClaude Sonnet 5 28b6edac82 feat: add MIME plumbing for markdown (.md) format
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 08:29:55 +02:00
anthony 7d61209bd8 CLAUDE.md > add precisions for o2switch that need precompiled binaries 2026-07-31 08:28:14 +02:00
anthony 02ae3bc44b docs: add design spec for Markdown (.md) document conversion
Adds md<->html, md<->txt, md<->docx, md<->pdf via markdown-it and turndown.
2026-07-31 08:20:29 +02:00
anthony 15baa25c43 package.json > fix prisma generate for o2switch 2026-07-31 08:14:56 +02:00
anthonyandClaude Sonnet 5 e4507ca8a0 Fix broken migration fallback in CLAUDE.md; add defensive null guards
The documented shadow-database-free fallback for generating migration SQL
(--from-migrations ... --to-schema-datamodel ...) was never actually tested
and fails for the same P3014 reason `migrate dev` does, since
--from-migrations also requires --shadow-database-url internally. Verified
against the local dev DB that --from-schema-datasource (live introspection,
no shadow DB) diffed against --to-schema-datamodel (static file read) works
in both the no-op case and a real ADD COLUMN case, and documented that
instead. Also corrected the inaccurate claim that this matched Task 3's
approach (Task 3 used --from-empty) and moved the suggested SQL output path
out of the repo root into the OS temp directory.

Also brings markFailed's errorMessage/errorLog params in line with markDone's
existing ?? null guard, and adds orderBy to findExpiredJobs to match
findPendingJobs, for consistency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 02:05:37 +02:00
anthonyandClaude Sonnet 5 3c0de7e742 docs: fill migration-workflow gaps found in final review
CLAUDE.md:
- Add the one-time `prisma migrate resolve --applied 0_init` baseline
  step that must run before the first `migrate deploy` against any
  environment (o2switch production included) whose conversion_jobs
  table predates Prisma. Without it, migrate deploy tries to
  CREATE TABLE against a table that already exists and fails, leaving
  migration history stuck.
- Document the actual, tested result of `prisma migrate dev` against
  the local dev DB: it fails with P3014 because convert_user lacks
  CREATE DATABASE/DROP DATABASE privileges needed for the shadow
  database. Document the verified fallback (`migrate diff
  --from-migrations ... --to-schema-datamodel ...` + manual migration
  folder + `migrate resolve --applied`) as the supported way to create
  new migrations here.

README.md:
- Local development step 2 referenced the deleted db/schema.sql;
  replaced with the real `prisma migrate deploy` invocation, which
  creates conversion_jobs fresh on an empty local database. Reordered
  so `npm install` runs first, since the migrate command needs
  node_modules/@prisma/client.
- Deployment on o2switch: added the same one-time baseline step
  (clearly marked, not to be repeated) before the first migrate
  deploy on that server, and added `prisma migrate deploy` to the
  "every subsequent deployment" checklist, after npm install and
  before restarting the app.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 01:54:23 +02:00
anthonyandClaude Sonnet 5 c42fe68b36 fix: isolate per-job cleanup errors and guard markDone against undefined
runCleanup previously had no try/catch around each expired job, so one
job's markCleaned/delete failure would abort the whole pass, leaving
later expired jobs' files undeleted for that run. Each iteration is now
wrapped in a try/catch that logs and continues; the returned count only
reflects jobs that actually completed the delete+markCleaned sequence.

markDone now guards outputPath/outputMimeType/outputSizeBytes/
conversionDurationSeconds with `?? null`, matching the guard createJob
already has on `quality` — Prisma treats `undefined` in a data object as
"leave the column alone" rather than binding NULL like the old raw SQL
did. Currently unreachable in practice since the worker always passes
real values, but keeps the repository defensive and consistent.

Added a cleanup.test.js case that monkey-patches
prisma.conversionJob.update to reject for one job's cleanedAt update,
asserting a later expired job in the same batch still gets cleaned.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 01:54:14 +02:00
anthonyandClaude Sonnet 5 fd757df3cd fix: move @prisma/client to production dependencies
src/db.js imports @prisma/client at runtime, but it was listed under
devDependencies (likely merged with the `prisma` devDependency during
Task 1). A production install that omits dev dependencies would crash
on startup with a missing-module error. `prisma` (the CLI) stays a
devDependency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 01:54:06 +02:00
anthony 9a49edce52 README.md > Update o2switch 2026-07-31 01:47:18 +02:00
anthony 40006e1719 Remove the mariadb dependency and the superseded schema.sql 2026-07-31 01:37:08 +02:00
anthonyandClaude Sonnet 5 4c9fe8f616 Wire the app, worker, and cleanup entry points onto Prisma Client
Task 4 rewrote src/db.js and src/jobs/jobRepository.js to use Prisma
instead of the hand-rolled mariadb pool. This updates every remaining
call site (app.js, server.js, worker.js, cleanup.js) and the 5 test
files that still referenced getPool/closePool/pool.query, so the app
and full test suite compile and run against Prisma Client.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 01:32:54 +02:00
anthony 3a04da3180 Rewrite the data access layer onto Prisma Client 2026-07-31 01:27:18 +02:00
anthony 439453be38 Baseline Prisma Migrate against the existing conversion_jobs table
Marks 0_init as applied via prisma migrate resolve so Prisma's
migration history is in sync with the pre-existing conversion_jobs
table, without running any SQL against the table itself.
2026-07-31 01:22:43 +02:00
anthonyandClaude Sonnet 5 852f7bc8bc Document a third accepted Prisma DDL exception (storage engine)
Task 3 found Prisma's generated migration SQL never emits an
explicit ENGINE=... clause, same root cause as the already-accepted
collation exception (no schema-level attribute exists). No practical
impact: the baseline never executes this SQL against the real DB,
and any fresh deploy still gets InnoDB from the server default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 01:17:15 +02:00
anthonyandClaude Sonnet 5 5e0b832fce Add buildDatabaseUrl and a CLI helper to print it
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 01:11:35 +02:00
anthonyandClaude Sonnet 5 1ebff25603 Fix Task 1 schema.prisma for exact DDL fidelity and drop --force
Review of Task 1 (d86245a) found two issues:

1. prisma/schema.prisma didn't reproduce db/schema.sql's DDL exactly.
   Add @db.DateTime(0) to createdAt/updatedAt/expiresAt/cleanedAt (Prisma
   otherwise defaults to DATETIME(3)), and explicit map: names on the
   uuid unique constraint and status/expiresAt indexes so they match the
   real table's uniq_uuid/idx_status/idx_expires_at. Verified via
   `prisma migrate diff --from-empty --to-schema-datamodel`, which now
   shows DATETIME(0)/CURRENT_TIMESTAMP(0) and the correct names. Table
   collation and updated_at's ON UPDATE CURRENT_TIMESTAMP remain
   documented, accepted gaps with no schema-level fix in Prisma's mysql
   provider.

2. The original @prisma/client@6/prisma@6 pin used --force without
   diagnosing why. Reinstalled with `npm install @prisma/client@6
   prisma@6 --save-dev` (no --force) directly against this project's
   node_modules: it completed cleanly with no conflicts, confirming
   --force was unnecessary.

`prisma validate`, `prisma generate`, and the full test suite
(69 passed, 4 pre-existing failures unrelated to this change) all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 01:07:34 +02:00
anthonyandClaude Sonnet 5 3f1280be33 Fix Task 1 schema in the Prisma plan for exact DDL fidelity
Task 1's reviewer found the original schema.prisma didn't reproduce
db/schema.sql's DDL (DATETIME(3) vs DATETIME, auto-generated index
names, no explicit precision on defaults). Verified the real DDL via
SHOW CREATE TABLE and empirically confirmed @db.DateTime(0) plus
explicit map: names close the gap. Two remaining differences (table
collation, no DB-level ON UPDATE for updatedAt) are Prisma/MySQL
provider limitations with no schema-level fix; documented as
accepted, per user decision, since the baseline never executes this
SQL against the real database and all writes go through Prisma.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 01:04:23 +02:00
anthony d86245a2b1 Add Prisma schema and client generation 2026-07-31 00:50:23 +02:00
anthonyandClaude Sonnet 5 827b21ada0 Ignore .superpowers/ scratch directory
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 00:40:00 +02:00
anthonyandClaude Sonnet 5 be40e2cf6c Add implementation plan for Prisma ORM and migrations
Task-by-task plan to replace the mariadb pool/repository with
Prisma Client and baseline Prisma Migrate against the existing
conversion_jobs table without data loss.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 00:15:17 +02:00
anthonyandClaude Sonnet 5 fff7d63461 Add design spec for Prisma ORM and migrations
Plans the replacement of the raw mariadb pool and hand-written
repository with Prisma Client, plus a no-data-loss Prisma Migrate
baseline against the existing conversion_jobs table.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 23:59:30 +02:00
anthony 802422b01f package.json > fix build 2026-07-30 23:41:11 +02:00
anthonyandClaude Sonnet 5 c7ae536654 Add implementation plan for per-format image compression level
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 23:32:01 +02:00
anthonyandClaude Sonnet 5 44563edfcf feat: add per-format compression controls to the frontend
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 23:30:53 +02:00
anthonyandClaude Sonnet 5 4cb9d76176 feat: validate and persist per-file quality in POST /api/jobs
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 23:30:23 +02:00
anthonyandClaude Sonnet 5 d827f55e46 feat: pass job quality through to converters in the worker
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 23:30:16 +02:00
anthonyandClaude Sonnet 5 fd99453e38 feat: opt-in JPEG-embed quality for image-to-PDF conversion
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 23:30:08 +02:00
anthonyandClaude Sonnet 5 57d7dac2b5 feat: support quality/compressionLevel options in image converters
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 23:03:39 +02:00
anthonyandClaude Sonnet 5 8caab530e7 feat: add quality column to conversion_jobs
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 23:02:48 +02:00
anthonyandClaude Sonnet 5 b826338a61 Add design spec for per-format image compression level
Covers data model, backend threading through registry/image/imageToPdf/worker, frontend controls, validation, and testing for a new quality/compressionLevel option on image conversions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 22:26:00 +02:00
anthony f8e46ae955 fix package dependances 2026-07-30 22:07:02 +02:00
anthonyandClaude Sonnet 5 ff5a257a0e feat: add auto-increment id, size/duration tracking, and soft cleanup to conversion_jobs
Rename the old CHAR(36) id to uuid (still used for public URLs and file
naming) and add a real auto-increment id as the primary key. Track
input/output file size and conversion duration per job. Cleanup no
longer deletes rows; it marks cleaned_at and skips already-cleaned
expired jobs on later runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 22:06:32 +02:00
anthony 24f7ab2625 Add Posthog tracking + fix extension download + doc 2026-07-30 21:26:52 +02:00
anthony 028170f103 fix for o2switch (pm2 + doc + vite) 2026-07-30 16:04:34 +02:00
anthony 8fcf49a7f0 fix for o2switch 2026-07-30 15:24:02 +02:00
anthonyandClaude Sonnet 5 1e28f9837a test: add end-to-end upload-convert-download coverage for image and document families
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:55:31 +02:00
anthonyandClaude Sonnet 5 21cca36595 feat: add React frontend and serve it from Express
Also fixes a Windows-specific bug in worker.js and cleanup.js: the
"run only if executed directly" guard compared import.meta.url against
`file://${process.argv[1]}`, which never matches on Windows (backslash
path separators, missing extra slash before the drive letter). main()
silently never ran, so the worker process started and exited
immediately without ever polling. Caught during this task's manual
verification of the full upload-convert-download flow. Fixed with
node:url's pathToFileURL, which builds a correct file:// URL cross-platform.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:54:27 +02:00
anthonyandClaude Sonnet 5 0761f1b76d feat: add cleanup script and o2switch deployment docs
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:22:48 +02:00
anthonyandClaude Sonnet 5 360e0ee7a6 feat: add worker process with bounded concurrency and per-job timeout
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:21:42 +02:00
anthonyandClaude Sonnet 5 ceccc1d1af feat: add server entry point
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:20:25 +02:00
anthonyandClaude Sonnet 5 ba5756e122 feat: add job status and download endpoints
Also adds vitest.config.js with fileParallelism: false. Every DB-backed
test file wipes and reseeds the shared conversion_jobs table in
beforeEach; running test files in parallel (Vitest's default) let one
file's DELETE race another file's just-inserted row against the same
live MariaDB instance, causing intermittent cross-file failures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:17:55 +02:00
anthonyandClaude Sonnet 5 280b249b05 feat: add POST /api/jobs and GET /api/formats endpoints
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:15:13 +02:00
anthonyandClaude Sonnet 5 5cf2ae4d59 feat: validate declared source format against sniffed magic bytes
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:14:16 +02:00
anthonyandClaude Sonnet 5 05ff96b8b3 feat: add best-effort PDF source converters (txt, html, docx)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:12:43 +02:00
anthonyandClaude Sonnet 5 16b4f28b42 feat: add DOCX/TXT/HTML document converters
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:10:34 +02:00
anthonyandClaude Sonnet 5 a049be2fc8 feat: add image to PDF converter
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:08:51 +02:00
anthonyandClaude Sonnet 5 5a990106b0 feat: add image converter family (sharp)
Regenerates the sample.png test fixture via sharp itself: the
hand-encoded base64 PNG from the plan had valid magic bytes (enough to
fool file-type's signature check) but was malformed past the header,
which libpng rejected as soon as sharp actually tried to decode it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:07:26 +02:00
anthonyandClaude Sonnet 5 aeebc47379 feat: add converter registry
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:06:12 +02:00
anthonyandClaude Sonnet 5 4b7ad9ef4e feat: add conversion_jobs repository
Also configures the MariaDB pool with timezone: 'auto', since the
default 'local' mode sends dates without timezone conversion and
silently broke expires_at comparisons whenever the app host and DB
server clocks differ (caught by the findExpiredJobs test).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:03:04 +02:00
anthonyandClaude Sonnet 5 f7c7e547d2 feat: add MIME sniffing and output MIME lookup
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 09:00:45 +02:00
anthonyandClaude Sonnet 5 d0faa162dc feat: add storage path helpers
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 14:12:13 +02:00
anthonyandClaude Sonnet 5 a5ab05303f feat: add MariaDB schema and connection pool
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 14:09:46 +02:00
anthonyandClaude Sonnet 5 378ca762a2 feat: project scaffolding and env config loader
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 14:06:56 +02:00
anthonyandClaude Sonnet 5 fe93139c6e Add implementation plan for file converter v1 (core + Images + Documents)
18 bite-sized TDD tasks from project scaffolding through the React
frontend and an end-to-end pipeline test, following the approved design
spec.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 13:46:16 +02:00
anthonyandClaude Sonnet 5 020fa199f8 Add design spec for file converter v1 (core + Images + Documents)
Covers architecture, MariaDB job model, conversion registry pattern,
o2switch deployment (Passenger + pm2 worker + cron cleanup), and
anti-abuse/error-handling strategy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 14:35:42 +02:00