Commit Graph
19 Commits
Author SHA1 Message Date
ajandClaude Opus 5.5 f93b0d6273 feat(data): library save model
Saving a nest drawing to the drawing library now has a Linux-testable model,
DrawingLibrarySaveSession, ahead of the desktop dialog.

Prefill starts from the drawing's name and customer (the nest's customer when
the drawing has none), the nest's material, grade, thickness and units, and the
linked revision's cut type and notes when the drawing came from this server's
library, otherwise the operator's last-used cut type.

Duplicates follow D4: active drawings whose normalized customer, name, material
and grade and exact thickness, units and cut type match. The server only
narrows (a blank filter means "any" there, and LIKE does not collapse
whitespace), so every key is checked again on the client. The default is the
linked drawing when it matches, else the only match; several matches require
the operator to choose, and a linked drawing that no longer matches is offered
but never the default, so a thickness change cannot silently rewrite it.

Writes follow NestSaveSession: a new drawing's id is allocated before the
upload, and a save that fails without a definite answer stays pending. The
next save first looks for the revision it would have produced and adopts it
when the package hash and details match; a not-stored new drawing is retried
under the same id, never onto another drawing, and a revision someone else
saved meanwhile is a conflict. A definite refusal (409, old server, invalid
request) leaves nothing pending. On success the drawing's link points at the
saved revision; nothing else in the nest copy changes (D2).

RemoteDrawingLibrary now implements IDrawingLibraryStore, the read/write
surface the session needs, so tests can use an in-memory store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:19 -04:00
ajandClaude Opus 5.5 8b63c042b5 test(server): drawing library end to end
Proves Slice 1's contract through the production routes and the real client:
a code-built drawing is packaged, uploaded, found by cut type, material, grade
and thickness among decoys, downloaded and read back with identical program
text; a stale If-Match update raises a conflict and leaves the current
revision, its package hash and the history unchanged. Archive, restore,
metadata edits and duplicate ids are exercised the same way.

Documents the /api/drawings contract, schema v4 and the fact that the backup
manifest and restore check do not yet verify drawings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:03 -04:00
ajandClaude Opus 5.5 76133193df feat(server): drawing library schema and routes
The server becomes the system of record for shared drawings (cut type,
thickness, material and grade, customer, notes). Schema v4 adds `drawings`
(current row) and `drawing_revisions` (immutable) to the same SQLite file under
the existing monitor; the nest tables are untouched and a v3 upgrade keeps every
nest row byte-identical.

`/api/drawings` follows the nest revision contract: client-allocated ids,
If-Match on every existing-drawing write (428/400/409 with no state change),
archive instead of delete, and one transaction for promotion plus snapshot.
Geometry is an opaque package (a zero-plate .nest) the server never parses.
Enum fields accept single names only, because the JSON enum converter would
otherwise accept numbers and comma combinations. Material, grade and customer
filters match SharedListNames keys so spacing and case cannot split a drawing
family; thickness is stored and compared at four decimals.

Shared bounds live in OpenNest.Data so the client can refuse the same requests
before sending. If-Match parsing moves to one helper used by nests and drawings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:02 -04:00
aj fae37dd1d0 fix(data): retry absent pending uploads without changing identity 2026-10-11 17:15:16 -04:00
aj 028832debc fix(data): reconcile ambiguous nest uploads before retry 2026-10-11 17:10:26 -04:00
aj 914c197181 feat(server): restore historical nest as new revision 2026-10-11 16:49:12 -04:00
aj 5598804942 feat(server): preserve deleted nests with guarded recovery 2026-10-11 16:49:12 -04:00
aj d216bcd406 feat(server): version guarded metadata edits 2026-10-11 16:49:12 -04:00
aj 11471e3c71 test(server): exercise version history through real client 2026-10-11 16:39:48 -04:00
aj a47b414392 feat(server): preserve file-save nest revisions 2026-10-11 16:37:28 -04:00
aj eff148c24e fix(test): unpool direct transient server databases 2026-10-08 20:03:10 -04:00
aj 648ffbca65 fix(test): own and close private server database before cleanup 2026-10-08 19:55:56 -04:00
aj 4d94273a00 fix(test): release transient SQLite handles for Windows server tests 2026-10-08 19:49:09 -04:00
aj eace08f351 fix(server): reject NUL characters in nest query search
SQLite LIKE stops matching at an embedded NUL, so search=Alpha%00x
behaved like 'Alpha' followed by a wildcard and returned rows that do
not contain the whole search text. The shared query validation now
rejects NUL, so the server answers 400 and the client throws before
sending.
2026-10-02 20:16:00 -04:00
aj 646d4c3975 feat(server): sort nest queries by allowlisted columns
The query accepts sort=<column>&order=asc|desc over a fixed allowlist
(saved, name, customer, status, material, dates, thickness, plate and
part counts, made by, comments, file size). Text columns sort with
NOCASE, ties break by id in the same direction so pages partition the
matches, and unknown or numeric sort values and other orders return 400.
The client sends the sort as its camelCase name.
2026-10-02 19:57:35 -04:00
aj 1fadf6c4d3 feat(server): add bounded nest query with SQL-side search
GET /api/nests/query filters name, customer, material, made by, comments
and status (stored and display names) with an escaped LIKE parameter,
orders newest saved first with an id tie-break, and returns one page of
at most 500 records plus the total match count, read in one database
hold. Unknown, repeated or out-of-range parameters return 400.

RemoteNestRepository.QueryAsync validates the same bounds before
sending, rejects oversized pages, and reports a 404 from an older server
as a server that needs updating. GET /api/nests stays the full
enumeration used by backup, restore checks and the container smoke.
2026-10-02 19:52:35 -04:00
aj 142c77a27f fix(server): serialize SQLite access and validate storage health 2026-10-02 16:31:46 -04:00
aj 5d8874a9e7 fix(server): reject malformed multipart uploads with 400 2026-10-02 15:48:55 -04:00
aj b17fe1a318 test(server): cover HTTP storage contracts with isolated SQLite 2026-10-02 15:37:44 -04:00