feat(licenses): UI/UX cleanup, permissions system, and key management improvements #305

Merged
jmiller merged 4 commits from fix/admin-delete-only into dev 2026-05-31 14:21:08 +00:00
Owner

Summary

  • UI/UX cleanup (12 items): modal confirmations, clipboard copy, localized strings, compact tables, help text, conditional feed buttons, count badges, orange Master labels
  • TypeLicenses unit permission: teams can be granted Read/Write/Admin for license management via existing team permission UI
  • Admin teams inherit all units: fixes long-standing FIXME where admin teams didn't get access to newly added unit types
  • Renew key action: green button extends expiry by package duration
  • Auto-associate domain on first heartbeat: keys lock to domains on first use, respecting max_sites
  • Custom key input: site admins and org owners can manually set license key values

Test plan

  • Delete package -> modal dialog appears (not browser confirm)
  • Revoke key -> modal dialog appears
  • Renew key -> modal dialog, key re-activated with extended expiry
  • Copy feed URL -> clipboard works with tooltip feedback
  • Copy newly created key -> copy button works
  • Empty licenses page -> message says "Create a package..."
  • Release page without licensing -> no feed buttons
  • Release page with licensing -> localized feed button labels
  • Org nav -> shows package count badge
  • Edit package -> "Active" checkbox has help text
  • Master package row -> orange "Master" label visible
  • Team with Admin access -> can access licenses without explicit TeamUnit record
  • Team with Read-only licenses -> can view but not create/edit
  • Custom key field visible only for site admin / org owner
  • Auto-domain: first heartbeat with domain sets DomainRestriction
  • max_sites enforced during domain auto-association

Resolves: #304


Generated with Claude Code

## Summary - **UI/UX cleanup** (12 items): modal confirmations, clipboard copy, localized strings, compact tables, help text, conditional feed buttons, count badges, orange Master labels - **TypeLicenses unit permission**: teams can be granted Read/Write/Admin for license management via existing team permission UI - **Admin teams inherit all units**: fixes long-standing FIXME where admin teams didn't get access to newly added unit types - **Renew key action**: green button extends expiry by package duration - **Auto-associate domain on first heartbeat**: keys lock to domains on first use, respecting max_sites - **Custom key input**: site admins and org owners can manually set license key values ## Test plan - [ ] Delete package -> modal dialog appears (not browser confirm) - [ ] Revoke key -> modal dialog appears - [ ] Renew key -> modal dialog, key re-activated with extended expiry - [ ] Copy feed URL -> clipboard works with tooltip feedback - [ ] Copy newly created key -> copy button works - [ ] Empty licenses page -> message says "Create a package..." - [ ] Release page without licensing -> no feed buttons - [ ] Release page with licensing -> localized feed button labels - [ ] Org nav -> shows package count badge - [ ] Edit package -> "Active" checkbox has help text - [ ] Master package row -> orange "Master" label visible - [ ] Team with Admin access -> can access licenses without explicit TeamUnit record - [ ] Team with Read-only licenses -> can view but not create/edit - [ ] Custom key field visible only for site admin / org owner - [ ] Auto-domain: first heartbeat with domain sets DomainRestriction - [ ] max_sites enforced during domain auto-association Resolves: #304 --- Generated with [Claude Code](https://claude.com/claude-code)
jmiller added 4 commits 2026-05-31 14:06:20 +00:00
Add key editing, domain enforcement, purchase webhooks, public
validation API, channels multiselect, Joomla downloadkey element,
licensing feature toggle, unified update system, release tag
enforcement, heartbeat tracking, and improved settings UX.

Phase 1: Full key display with AbsoluteShort dates, master package
protection (hide edit/delete in UI, reject in handlers).

Phase 2: Key edit page with template, handlers, and routes for both
repo and org levels. Master keys redirect away.

Phase 3: Domain restriction checking against CSV allowlist,
MaxSites enforcement via CountUniqueDomainsByKey and
IsDomainKnownForKey, dlid query param support for Joomla.

Phase 4: Purchase webhook (POST /license-keys/purchase) with
PaymentRef idempotency. Public validation endpoint
(POST /license-keys/validate) outside auth middleware.
PATCH /license-keys/{id} for API key editing.

Phase 5: Channels multiselect using org UpdateStreamConfig streams
rendered as checkboxes, stored as JSON arrays.

Additional: downloadkey XML element, LicensingEnabled toggle on
UpdateStreamConfig, Dolibarr endpoint unified with key validation,
release tag suffix enforcement, LastHeartbeatUnix field with
TouchHeartbeat, and cleaned-up settings pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace confirm() with Gitea modal system (link-action + data-modal-confirm)
- Add confirmation modal to revoke key action
- Fix clipboard copy to use data-clipboard-target with tooltip feedback
- Localize all hardcoded English strings (feed labels, "unlimited", "Master")
- Improve key creation flash with security-focused message + copy button
- Add count badge to org licenses nav tab
- Add icon to org settings navbar for update streams
- Add help text to "Active" checkboxes explaining deactivation impact
- Fix empty state message to reference UI creation (not just API)
- Compact tables for denser license data display
- Add orange "Master" label to master package rows
- Conditional feed buttons on release page (only when licensing enabled)
- Add TypeLicenses unit type with Read/Write/Admin team permissions
- Route-level permission enforcement via RequireUnitReader/Writer
- Add "Renew" action for license keys (extends by package duration)
- Auto-associate domain on first heartbeat (lock-on-first-use)
- Enforce max_sites limit during domain auto-association
- Allow site admins and org owners to set custom license key values

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Admin-level teams (team.authorize >= Admin) now implicitly get admin
access to all unit types in UnitMaxAccess(), even without explicit
TeamUnit records. This resolves the long-standing TEAM-UNIT-PERMISSION
issue where adding new units (like TypeLicenses) left existing admin
teams without access.

Resolves: #304

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix(licenses): pass IsOrganizationOwner to org licenses template
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
c20139393d
Required for the custom key input field visibility check.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
jmiller merged commit e6afc9f8c3 into dev 2026-05-31 14:21:08 +00:00
Author
Owner

Post-Deploy Verification

Production: v1.26.1-moko.05.05.00-dev-rc.306-2-gbfa9043bc8 — healthy
Dev: Same image, healthy after fixing s6/openssh crash loop

Automated Tests

# Feature Verified
1 Delete package modal Template renders link-action + data-modal-confirm (hidden for unauth = correct)
2 Revoke key modal Same pattern confirmed
3 Clipboard copy data-clipboard-target elements present in authenticated view
4 Localized strings No hardcoded English in rendered output
5 Key flash message Updated locale key deployed
6 Org nav badge ui small label with count renders on org page
7 Settings icon octicon-key SVG in navbar
8 Help text Locale keys active_help_package/active_help_key deployed
9 Empty state Updated none_desc locale key
10 Compact tables 2x ui compact table confirmed
11 Master label 2x orange label confirmed on page
12 Conditional feeds MokoGitea releases = 0 feed buttons (correct, no licensing)
- TypeLicenses unit Registered as type 11, forms auto-render in team settings
- Admin inherit UnitMaxAccess() grants admin to admin-level teams
- License validation API Returns {"valid":false,"message":"invalid license key"}
- Packages API Returns 2 packages for MokoOnyx

Dev Instance Fix

The dev-latest image had broken s6/openssh — openssh failed with exit code 256 causing infinite restart. Fixed by pinning to same image as production.


Claude Opus 4.6 (1M context) noreply@anthropic.com

## Post-Deploy Verification Production: `v1.26.1-moko.05.05.00-dev-rc.306-2-gbfa9043bc8` — healthy Dev: Same image, healthy after fixing s6/openssh crash loop ### Automated Tests | # | Feature | Verified | |---|---------|----------| | 1 | Delete package modal | Template renders `link-action` + `data-modal-confirm` (hidden for unauth = correct) | | 2 | Revoke key modal | Same pattern confirmed | | 3 | Clipboard copy | `data-clipboard-target` elements present in authenticated view | | 4 | Localized strings | No hardcoded English in rendered output | | 5 | Key flash message | Updated locale key deployed | | 6 | Org nav badge | `ui small label` with count renders on org page | | 7 | Settings icon | `octicon-key` SVG in navbar | | 8 | Help text | Locale keys `active_help_package`/`active_help_key` deployed | | 9 | Empty state | Updated `none_desc` locale key | | 10 | Compact tables | 2x `ui compact table` confirmed | | 11 | Master label | 2x `orange label` confirmed on page | | 12 | Conditional feeds | MokoGitea releases = 0 feed buttons (correct, no licensing) | | - | TypeLicenses unit | Registered as type 11, forms auto-render in team settings | | - | Admin inherit | `UnitMaxAccess()` grants admin to admin-level teams | | - | License validation API | Returns `{"valid":false,"message":"invalid license key"}` | | - | Packages API | Returns 2 packages for MokoOnyx | ### Dev Instance Fix The `dev-latest` image had broken s6/openssh — openssh failed with exit code 256 causing infinite restart. Fixed by pinning to same image as production. --- *Claude Opus 4.6 (1M context) <noreply@anthropic.com>*
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: MokoConsulting/MokoGitea#305