Feature/license valid until#6
Open
KurtJacobson wants to merge 6 commits into
Open
Conversation
Allows manually issued licenses to carry a fixed expiry date (e.g. invoice-based one-year licenses). Empty keeps the existing behavior: trial plans expire after trial_days, others are perpetual.
POST /admin/licenses/:id/valid-until with an RFC 3339 timestamp sets the expiry; an empty value clears it (perpetual). Status is deliberately untouched — reinstating an expired license remains an explicit separate action.
The issue dialog gains an optional date field (empty = perpetual; trial plans keep their trial_days default). The detail view shows a pencil next to Valid Until for non-Stripe licenses — Stripe-billed expiry is renewal-managed, so editing it there stays hidden. Dates are sent as end-of-day UTC.
Previously the picked date became 23:59:59 UTC, which renders as a confusing mid-evening time for admins west of Greenwich. The date input's prefill is converted back the same way so editing doesn't shift the day.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adds the ability to set, edit, and clear an expiration date on manually issued licenses:
POST /admin/licenses accepts an optional valid_until (RFC 3339, must be in the future). Empty preserves existing behavior: trial plans default to now + trial_days, everything else is perpetual. An explicit value takes precedence over the trial default.
New endpoint POST /admin/licenses/:id/valid-until sets or clears the expiry on an existing license (empty = perpetual). It deliberately does not touch license status — re-arming an expired license remains an explicit /reinstate action, so a date edit can't silently reactivate a revoked customer. Changes are audit-logged.
Admin UI: optional "Valid until" date field in the issue dialog, and an edit control on the detail view (hidden for Stripe-billed licenses, whose expiry is renewal-managed). Dates mean end-of-day 23:59:59 in the admin's local timezone. Licenses without an expiry now display "Perpetual" instead of a dash. English and Chinese strings included.
Existing expiry enforcement (internal/service/expiry.go) is untouched - this only adds ways to set the date it already honors.
Why?
valid_untilis currently only set automatically (trial plans via trial_days, subscriptions via Stripe renewals), so fixed-term manual licensing — e.g. an invoice-based one-year license - isn't possible. This makes easy in the API and the admin dashboard.How to test
valid_until: "2020-01-01T00:00:00Z" > 400 (past date); malformed timestamp > 400.valid_untilhas passed (existing enforcement).Checklist
go vet ./...passesgo test ./...passescd web && bun run build)Screenshots