Commit Graph

44 Commits

Author SHA1 Message Date
Jakub Zych
55314e41f8 fix(admin): datetime popover clock, unsaved confirm, inferred list dates
BM UAT on v0.1.1 showed a date-only calendar for datetime fields, a stuck discard dialog, and raw ISO timestamps when columns.yaml omitted type. The picker now edits time in the popover, confirm sits above the calendar, and omitted time.Time / Date / TimeOfDay columns compile as datetime / date / time.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-03 05:58:24 +02:00
Jakub Zych
516f9c9025 fix(12.2): close code-review blockers on uploads, JSON caps, and pivot fill
Keep form save behind in-flight uploads, make retries idempotent via X-Upload-Id, cap remaining JSON bodies, and surface pending pivot type errors instead of zeroing them.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-02 23:10:48 +02:00
Jakub Zych
c8d41a68c6 test(12.2-05): cover every Go behaviour of phase 12.2
- lagoon: Date and TimeOfDay through JSON, text and real DATE/TIME
  columns; Fill text fallback without changing earlier conversions;
  required on zero dates; deferred_bindings shape, store isolation and
  envelope; PurgeDeferred cut-off, after-commit blobs, SKIP LOCKED,
  skipped types and the deferred:purge command
- attach: Store limits, extensions, MIME patterns, default lists, key
  shape and blob cleanup; IsAllowedImage formats, polyglots, ceiling
- conga and pact: framework purge schedule entry and forged jobs; the
  six relation child hook interfaces
- cabana: fileupload and datepicker compile, upload, remove, caption,
  reorder and bounds; deferred commit order, rollback, applied-only and
  concurrent saves; relation contracts, forms, CRUD, deferral, schema
2026-10-02 20:34:23 +02:00
Jakub Zych
9d2b1c1848 test(12.2-05): prove relation child and protected file scoping through the router
- acme.deferred fixture plugin over testdata/deferred (test-only), two
  controllers, recording Form and Relation hooks, two admins
- TestRelationChildScope*: every child route answers 404 for another
  parent, a hidden parent and another admin's pending child, changes
  nothing; undeclared toolbar buttons 403 before SQL; pivot whitelist
- TestProtectedFile*: foreign, pending and public files 404; only jpeg,
  png, gif and webp inline; nosniff, no-store and sandbox CSP everywhere
2026-10-02 20:10:10 +02:00
Jakub Zych
fe9e8baaf1 feat(12.2-03): defer relation work on unsaved records and add child file routes
- record id 0 with X-Session-Key manages deferrable relations: create, link, unlink, delete and pivot edits are held in deferred_bindings
- the record's create save applies relation bindings with the file bindings; an ineligible link is a 422 on the relation-manager field
- child forms upload files through .../records/{child}/files/{field} keyed by X-Child-Session-Key; the child save commits them
- boot refuses a deferrable relation with create whose related model no plugin lists in Models()
2026-10-02 19:08:16 +02:00
Jakub Zych
afb05b6ee4 feat(12.2-03): add parent-scoped child show, update, delete and pivot routes
- loadChild finds a child with one query carrying the parent predicate; a foreign child is 404
- GET/PUT .../records/{child} and POST .../delete (all or nothing) per relation kind
- hasMany link adopts NULL-key rows and unlink clears the key; pending created children are never candidates
- link accepts pivot values for one id through the pivot.form whitelist; GET/PUT .../pivot/{child}
- Link and Unlink share linkRelated/unlinkRelated for the deferred commit
2026-10-02 18:44:34 +02:00
Jakub Zych
48a5b8045a feat(12.2-03): add hasMany relation contracts, relation forms and child create
- RelationContract gains Kind (empty is belongsToMany) and ForeignKey, with kind-aware boot checks
- manage.form, view.form and pivot.form compile against the related or pivot model; $/ paths resolve inside the plugin
- view toolbarButtons accept create|update|delete|link|unlink, each the capability of its routes
- POST .../relations/{name}/records creates a child through the manage form; the server sets the hasMany key
- relation schema carries kind, deferrable and the localized forms; 17 new relation message keys in en and pl
2026-10-02 18:37:13 +02:00
Jakub Zych
67d4c7ff13 feat(12.2-02): add the datepicker field with server-side bounds and date list columns
- type: datepicker compiles the D-20 keys; format maps to displayFormat with WinterCMS's momentFormat table
- boot fails when the mode does not match the column's Go type (time.Time, lagoon.Date, lagoon.TimeOfDay)
- datepicker is a writable scalar field; minDate and maxDate are rechecked on save
- columns.yaml accepts type: date and type: time; Scanner/Valuer structs are columns, not relations
- conformance fixture carries date and datetime fields; README, forms and lists docs
2026-10-02 18:19:04 +02:00
Jakub Zych
e54fd257ee feat(12.2-02): add file removal, caption, reorder and protected downloads
- DELETE, PUT and POST reorder under .../{id}/files/{field}, each scoped by one parent query (404 for a foreign file)
- protected download and thumb routes: is_public=false only, nosniff, private no-store, sandbox CSP, inline only for jpeg/png/gif/webp
- the save applies deferred removals, replaces attachOne files and rechecks maxFiles and required
- blobs of deleted files are removed after commit
- swagger2openapi emits binary content for file responses
- admin OpenAPI, TS types, conformance, README and attachments docs
2026-10-02 18:11:56 +02:00
Jakub Zych
044e0450ef feat(12.2-02): add the fileupload field with deferred uploads committed on save
- type: fileupload compiles the D-08 keys and binds to the model's attach.Relation at boot
- X-Session-Key (cabana.SessionKeyHeader) carries the form session key; RecordInput.SessionKey
- GET and POST .../{id}/files/{field}: list with pending uploads, multipart upload into attach.Store
- the create and update save attaches the session's pending files in its transaction
- swagger2openapi folds formData parameters into a multipart requestBody
- admin OpenAPI, TS types, conformance cases, README and forms docs
2026-10-02 18:04:34 +02:00
Jakub Zych
4ae272e3cc fix(09): WR-19 expose the write transaction to hooks and scopes through TxFromContext 2026-10-01 21:37:13 +02:00
Jakub Zych
8479defe53 fix(09): WR-17 merge an admin's own permissions over the role's, honouring denies 2026-10-01 21:33:07 +02:00
Jakub Zych
629fac4d29 fix(09): WR-16 resolve model columns through embedded structs and explicit column tags 2026-10-01 21:31:46 +02:00
Jakub Zych
2d96adf775 fix(09): WR-10 keep cabana's own backend guard when it is activated twice on one application 2026-10-01 21:31:46 +02:00
Jakub Zych
299d220b51 fix(09): WR-14 let logout revoke an expired token that is still refreshable and always clear the cookie 2026-10-01 21:23:42 +02:00
Jakub Zych
c9bb14944a fix(09): WR-13 read admin passwords from a prompt or stdin and deprecate the --password flag 2026-10-01 21:20:20 +02:00
Jakub Zych
331351a73c fix(09): WR-11 reject ambiguous admin logins and cross-field login or email collisions 2026-10-01 21:17:50 +02:00
Jakub Zych
eb8c727790 fix(09): WR-12 time a missing admin login at the configured bcrypt cost 2026-10-01 21:17:37 +02:00
Jakub Zych
3f476164f9 fix(09): WR-10 fail boot when another plugin already owns the backend guard 2026-10-01 21:15:05 +02:00
Jakub Zych
8b48eea4e1 fix(09): WR-08 search non-text list columns as text instead of failing in SQL 2026-10-01 21:11:38 +02:00
Jakub Zych
b026aec162 fix(09): WR-07 keep relation column order from the YAML itself, not a fixed indentation 2026-10-01 21:10:09 +02:00
Jakub Zych
889250c8fb fix(09): WR-06 default a relation list to its first sortable column 2026-10-01 21:07:54 +02:00
Jakub Zych
9bca815b1b fix(09): WR-05 refuse relation link and unlink the panel does not declare 2026-10-01 21:07:11 +02:00
Jakub Zych
b8084080cb fix(09): WR-04 apply a form field's required flag only in the contexts that can supply it 2026-10-01 21:05:33 +02:00
Jakub Zych
f2ab93f291 fix(09): WR-03 refuse writes that the compiled list and form do not declare 2026-10-01 21:04:31 +02:00
Jakub Zych
28aa073de0 fix(09): WR-02 drop a denied main menu item and never link it to a controller the admin cannot open 2026-10-01 20:59:26 +02:00
Jakub Zych
b4b8b5df64 fix(09): WR-01 match wildcard required permissions and treat several codes as any, like Winter 2026-10-01 20:58:16 +02:00
Jakub Zych
037dc53030 feat(lagoon): per-query collation for OrderBy, drop the database locale check
- lagoon.OrderBy takes variadic lagoon.OrderOption values; lagoon.Collate(name)
  emits a validated, double-quoted COLLATE clause (e.g. "pl-x-icu")
- remove the exported CheckLocale and the ICU pl-PL check from Open and Use
- framework test containers and per-test databases are plain PostgreSQL
- lagoon README, root README and docs pages drop the locale requirement;
  queries-and-pagination gains a "Sorting with a collation" section
  backed by ExampleCollate
2026-10-01 09:51:03 +02:00
Jakub Zych
44bd1446f5 feat(11.1-04): add the Backend section, the remaining Services pages and the concept map links
- docs/backend: admin controllers, forms, lists and filters, relation
  manager, users and permissions, settings, partials and widgets, admin SPA
- docs/services: storage, outbound HTTP, realtime, Web Push, search, parity
  testing and the Frontend and AJAX (not provided) page
- Examples for cabana (with testdata/docs YAML), fetchguard, lighthouse and
  its centrifugo driver, flare, beachcomber and typesense, tide; lighthouse
  and beachcomber TestDocs* regions run on their Postgres harnesses
- concept map rows link their guide pages and the not-provided rows the
  Frontend and AJAX page; index lists Backend, Database and Services
- TestDocsRequiredPages asserts the D-08 section order
2026-09-30 23:18:35 +02:00
Jakub Zych
f7b6b0c313 fix(11-08): make cabana writes commit-safe 2026-09-30 20:14:23 +02:00
Jakub Zych
719ed719b4 fix(10.1): WR-06 honour the widget field's context on the action route
widgetAction derives the form from the request (create without record_id,
update with one) and answers 404 when the field's context hides the
widget on that form, reusing contextAllows as the save path does. An
update-only action can no longer run with a nil record through a direct
POST.
2026-09-29 10:00:08 +02:00
Jakub Zych
7b72bf4be4 fix(10.1): WR-05 drop widgets the admin may not run from the form schema
formSchema now filters type: widget fields by the action's permissions,
the same D-12 filtering listSchema applies to toolbarActions, so an admin
without the action permission no longer gets a button that always
answers 403, and the action name is not revealed. The filtered fields are
a new slice, so the cached schema is never modified.
2026-09-29 09:58:27 +02:00
Jakub Zych
0bdb6ebcac fix(10.1): WR-04 judge action fill values by their JSON encoding
isJSONScalar now encodes each value and keeps it only when the encoding
is a string, number, boolean or null, so a named scalar whose MarshalJSON
writes an array or object, NaN and the infinities are dropped. writeJSON
encodes into a buffer before the status line, so an encode failure is a
logged 500 with the generic envelope instead of a 200 with a truncated
body.
2026-09-29 09:56:44 +02:00
Jakub Zych
7333f450ad fix(10.1): WR-03 walk the whole partial view model before rendering
refusedViewModel compared only the top-level type with the controller's
model. It now walks the type through pointers, slices, arrays, maps,
struct fields and the results of exported methods, and the values held
in interface-typed members, refusing the controller's model, any other
GORM model (TableName, a gorm tag, gorm.Model, gorm.DeletedAt) and
html/template's trusted content types anywhere in that structure.
2026-09-29 09:54:35 +02:00
Jakub Zych
e60e69745e fix(10.1): CR-01 answer a value that does not fit its column with a 422
lagoon.Fill now returns a *lagoon.FillTypeError naming the key when a
requested value cannot be stored in its column (a fraction, exponent or
overflow for an integer field, or a value of the wrong type). The admin
save path maps it to a validation_failed 422 on that field instead of a
500 CapabilityError; genuine capability failures keep the 500.
2026-09-29 09:44:14 +02:00
Jakub Zych
7eed4acd87 test(10.1-04): cover the Phase 10.1 extension point Go code
- acme fixture plugin under modules/cabana/testdata/extension (gadgets
  controller, header and form partials, lookup widget, JS and CSS)
- TestPhase101FormExtensionSchema, TestPhase101PartialSchema and
  TestPhase101Toolbar: every widget, partial and toolbar boot rule
- TestPhase101PartialSanitizer: tag, attribute and URL allowlist, escaping,
  per-request trans, size/node/depth caps and the view-model guard
- TestPhase101Assets: exact-key asset hits, revalidation, SPA fall-through,
  boot path checks and ?v= schema URLs
- TestPhase101Actions (PostgreSQL): scoping, fill filter, strict body,
  action permission, error mapping, CSRF header, toolbar and partial routes
- TestPhase101BoardwalkExports: ContentType and SetSecurityHeaders
2026-09-29 02:48:12 +02:00
Jakub Zych
9df9fae930 feat(10.1-02): render header and form partials through an allowlisted node renderer
- partialNodes rebuilds the server node tree with h() under the server's tag, attribute and URL lists
- PartialHost owns the skeleton, empty and failure states and keeps nodes visible on refetch
- type: partial is a valueless group-labelled field rendered on create and update
- ListView shows headerPartial above the list card and refetches it after bulk delete
- summer-partial and summer-stats style kit in main.css, documented in the cabana README; dist rebuilt
2026-09-29 02:10:18 +02:00
Jakub Zych
771d2ccce0 feat(10.1-01): render header and form partials into an allowlisted node tree
- fields.yaml type: partial with a bare path name and config_list.yaml
  headerPartial resolve to {ConfigDir}/_{name}.htm, parsed at boot; the
  controller must implement pact.AdminPartialData
- html/template render against a curated view model, then x/net/html
  ParseFragment and a tag, attribute and URL allowlist with 64 KiB, 2000-node
  and depth-32 caps; the model type and trusted template types are refused
- GET .../partials/{name} with optional ?id= loaded through the form scope
- golang.org/x/net becomes a direct requirement (D-18), no new module
2026-09-28 23:52:50 +02:00
Jakub Zych
8b1cb244de feat(10.1-01): serve controller JS/CSS and run registered toolbar actions
- boardwalk exports ContentType and SetSecurityHeaders
- pact.AdminClientAssets files are read and hashed at boot and served by exact
  key under {prefix}/assets/{vendor}/{plugin}/ with nosniff, CSP, CORP,
  no-cache and an ETag; a miss falls through to the SPA
- list and form schemas carry assets URLs with a ?v= hash
- toolbar.buttons resolves create, delete and registered actions after decode;
  toolbarActions is permission-filtered per admin
- POST .../toolbar/{action} behind requireAjax and action permissions
2026-09-28 23:41:17 +02:00
Jakub Zych
f9281949a6 feat(10.1-01): run registered widget actions through a cabana-owned route
- pact: AdminClientAssets, AdminAction, AdminActionInput, AdminActionResult,
  HasAdminActions and AdminPartialData contracts
- fields.yaml type: widget with widget, action and fill keys; boot checks the
  plugin tag prefix, the registered action and writable scalar fill fields
- POST .../widgets/{field} behind requireAjax, controller and action
  permissions, scoped non-locking record read and a server-side fill filter
- typed OpenAPI operation, inventories and an acme conformance case
2026-09-28 23:35:00 +02:00
Jakub Zych
1bd34948a9 docs(modules): rewrite cabana, wire, towel, festival, compass, backpack READMEs 2026-09-28 15:43:23 +02:00
Jakub Zych
680a61cde2 docs(10.2-02): document framework modules
- Add concise code-derived onboarding for all nested modules\n- Name a real entry point and current consumers per package
2026-09-28 13:07:52 +02:00
Jakub Zych
57e7b56983 test(10.2-01): retarget moved package fixtures
- Resolve CLI and module-local fixtures from modules/ paths
- Keep root admin assets reachable from nested package tests
2026-09-28 12:56:15 +02:00
Jakub Zych
5e50b166ef refactor(10.2-01): nest framework packages under modules
- Move remaining beach packages and embedded admin assets\n- Rewrite framework, example, build, and gate paths
2026-09-28 02:21:02 +02:00