@nauticana/sail 1.1.42 → 1.1.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/package.json +1 -1
- package/src/model/appdata.ts +8 -0
- package/src/service/auth.service.ts +11 -11
- package/src/service/billing.service.ts +7 -0
- package/src/service/rest_url.ts +1 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A shared Angular component library for building CRUD-based admin frontends. Provides table management, form handling, navigation, authentication, two-factor authentication, and trusted device management — all driven by metadata from a [keel](https://github.com/nauticana/keel) Go backend.
|
|
4
4
|
|
|
5
|
-
> **Compatibility:** sail and keel are versioned in lock-step. The current line is **sail v1.1.x ↔ keel v1.2.x**; sail v1.1.42 adopts keel v1.2.118 (RFC 3339 timestamps, `X-Time-Zone`), sail v1.1.41 adopts keel v1.2.117 (scheduled plan changes), sail v1.1.39 and v1.1.40 adopt keel v1.2.115 (usage captions, provider-managed subscriptions, the connect disconnect route), sail v1.1.37 adopts keel v1.2.114 (scopes validated against a constant list), sail v1.1.36 adopts keel v1.2.113 (API key expiry, caller-aware scope policy), sail v1.1.35 adopts keel v1.2.112 (minor-unit money, the `currency` catalog, OAuth grant routes), sail v1.1.33 adopts keel v1.2.106 (`prompt` on the login page, new-device step-up, sign-in sessions), sail v1.1.32 adopts keel v1.2.101 (the OAuth connect callback ticket), sail v1.1.31 adopts keel v1.2.100 (tenant single sign-on, DNS and HTTP-file domain verification), sail v1.1.30 adopts keel v1.2.99 (password routes; a password change signs out), sail v1.1.29 adopts keel v1.2.94 (signup: confirmation signs in, partner setup, sign-in hand-off code), sail v1.1.28 adopts keel v1.2.93 (absolute `return` for the OAuth hand-off), sail v1.1.18 adopts keel v1.2.58 (refresh-token rotation, `/public/logout`, setup intents, the realtime hub), the agency components in sail v1.1.14 require keel v1.2.41 (sail v1.1.9 needs keel v1.2.16 for the OAuth-connect layer). Earlier lines: **sail v0.5.x ↔ keel v0.5.x**, **sail v0.6.x / v0.7.x ↔ keel v0.7.x**, **sail v0.8.x ↔ keel v0.8.x**, **sail v0.9.x ↔ keel v0.9.x**. Newer sail releases extend the contract — older keel servers reject unknown endpoints with HTTP 404 / 400. The v0.8.x line additionally ships the `table_action` framework (per-table custom buttons surfaced in `TableList` / `TableSearch` / `TableEdit` / `TableDetail`); see the [Migrating to v0.7.0 §5 — TableAction](#migrating-to-v070--payout-user-payment-methods-table-actions) section for the seed shape (basis `table_action` + `authorization_object` + `authorization_object_action` rows) and the [keel/README Table Actions](https://github.com/nauticana/keel#table-actions) section for backend wiring via `handler.WrapTableAction`.
|
|
5
|
+
> **Compatibility:** sail and keel are versioned in lock-step. The current line is **sail v1.1.x ↔ keel v1.2.x**; sail v1.1.43 adopts keel v1.2.125 (plan-change overages), sail v1.1.42 adopts keel v1.2.118 (RFC 3339 timestamps, `X-Time-Zone`), sail v1.1.41 adopts keel v1.2.117 (scheduled plan changes), sail v1.1.39 and v1.1.40 adopt keel v1.2.115 (usage captions, provider-managed subscriptions, the connect disconnect route), sail v1.1.37 adopts keel v1.2.114 (scopes validated against a constant list), sail v1.1.36 adopts keel v1.2.113 (API key expiry, caller-aware scope policy), sail v1.1.35 adopts keel v1.2.112 (minor-unit money, the `currency` catalog, OAuth grant routes), sail v1.1.33 adopts keel v1.2.106 (`prompt` on the login page, new-device step-up, sign-in sessions), sail v1.1.32 adopts keel v1.2.101 (the OAuth connect callback ticket), sail v1.1.31 adopts keel v1.2.100 (tenant single sign-on, DNS and HTTP-file domain verification), sail v1.1.30 adopts keel v1.2.99 (password routes; a password change signs out), sail v1.1.29 adopts keel v1.2.94 (signup: confirmation signs in, partner setup, sign-in hand-off code), sail v1.1.28 adopts keel v1.2.93 (absolute `return` for the OAuth hand-off), sail v1.1.18 adopts keel v1.2.58 (refresh-token rotation, `/public/logout`, setup intents, the realtime hub), the agency components in sail v1.1.14 require keel v1.2.41 (sail v1.1.9 needs keel v1.2.16 for the OAuth-connect layer). Earlier lines: **sail v0.5.x ↔ keel v0.5.x**, **sail v0.6.x / v0.7.x ↔ keel v0.7.x**, **sail v0.8.x ↔ keel v0.8.x**, **sail v0.9.x ↔ keel v0.9.x**. Newer sail releases extend the contract — older keel servers reject unknown endpoints with HTTP 404 / 400. The v0.8.x line additionally ships the `table_action` framework (per-table custom buttons surfaced in `TableList` / `TableSearch` / `TableEdit` / `TableDetail`); see the [Migrating to v0.7.0 §5 — TableAction](#migrating-to-v070--payout-user-payment-methods-table-actions) section for the seed shape (basis `table_action` + `authorization_object` + `authorization_object_action` rows) and the [keel/README Table Actions](https://github.com/nauticana/keel#table-actions) section for backend wiring via `handler.WrapTableAction`.
|
|
6
6
|
|
|
7
7
|
**Recent additions:** `BaseAuthService.acceptToken(jwt)` — adopt an externally-minted JWT (registration / SSO-handoff flows) and run the full post-login sequence (store under the canonical `jwt` key, load appdata, init routes); apps must use this instead of writing `localStorage` directly. `BaseRestService.analytic<T>(endpoint, params?)` — GET a keel `analytic/<endpoint>` report and return its rows. `passwordPolicyValidator` + `BaseAuthService.ensurePasswordPolicy()` — validate passwords against keel's policy (via `SailGuiConfig.passwordPolicyUrl`); see Configuration reference.
|
|
8
8
|
|
|
@@ -29,7 +29,7 @@ A shared Angular component library for building CRUD-based admin frontends. Prov
|
|
|
29
29
|
| **Services** | `BaseAuthService` (OTP / social / push / deleteAccount / logoutEverywhere / profile: `getProfile`, `updateProfile`, `request`+`confirmEmailChange`, `request`+`confirmPhoneChange`), `BillingService`, `AgencyService`, `BackendService`, `PayoutService`, `UserPaymentMethodService`, `PartnerDocumentService`, `NotificationInboxService`, `loadScript()`, `authInterceptor`, `apiResponseInterceptor` |
|
|
30
30
|
| **Abstracts** | `BaseTable`, `BaseForm`, `BaseView`, `AbstractEditableView`, `BaseAsync`, `BaseRestService` |
|
|
31
31
|
| **Config** | `SAIL_GUI_CONFIG`, `SailGuiConfig`, `configureRestUrls()` |
|
|
32
|
-
| **Models** | `ApplicationData`, `TableDefinition`, `SiudAction`, `ApplicationMenu`, `ConstantValue`, `UserAccount`, `RestReport`, `ReportParam`, `TrustedDevice`, `PublicPlan`, `PlanPrice`, `PaymentMethod`, `Subscription`, `PlanChangeResult`, `Invoice`, `CheckoutRequest`/`CheckoutResponse`, `PortalResponse`, `UsageMeter`, `ChargeResult`, `UserPaymentMethod`, `TableAction`, `TableActionParameter`, `ReusableAccount`, `PayoutOnboardingSession`, `BankInfoFormValue`, `Currency`, `Country`, `OtpRequest`/`OtpResponse`, `SignupConsent`, `ConsentState`, `ConsentOption`, `SocialProvider`, `PushPlatform`, `InboxMessage`/`InboxPage`, `PartnerDocument`/`DocumentUploadRequest`, 2FA types, etc. |
|
|
32
|
+
| **Models** | `ApplicationData`, `TableDefinition`, `SiudAction`, `ApplicationMenu`, `ConstantValue`, `UserAccount`, `RestReport`, `ReportParam`, `TrustedDevice`, `PublicPlan`, `PlanPrice`, `PaymentMethod`, `Subscription`, `PlanChangeResult`, `PlanOverage`, `Invoice`, `CheckoutRequest`/`CheckoutResponse`, `PortalResponse`, `UsageMeter`, `ChargeResult`, `UserPaymentMethod`, `TableAction`, `TableActionParameter`, `ReusableAccount`, `PayoutOnboardingSession`, `BankInfoFormValue`, `Currency`, `Country`, `OtpRequest`/`OtpResponse`, `SignupConsent`, `ConsentState`, `ConsentOption`, `SocialProvider`, `PushPlatform`, `InboxMessage`/`InboxPage`, `PartnerDocument`/`DocumentUploadRequest`, 2FA types, etc. |
|
|
33
33
|
| **Decorators** | `@IsSailString()`, `@IsSailNumeric()` (class-validator based) |
|
|
34
34
|
| **Utils** | `titleCase()`, `fromPriceLabel()`, `formatMinor()`, `CurrencyService`, `sailMoney` pipe |
|
|
35
35
|
|
|
@@ -473,6 +473,7 @@ export class PricingPage {
|
|
|
473
473
|
| `getSubscription()` | `GET /api/billing/subscription` | `Observable<Subscription>` |
|
|
474
474
|
| `cancelSubscription()` | `POST /api/billing/subscription/cancel` | `Observable<void>` |
|
|
475
475
|
| `changePlan(planId)` | `POST /api/billing/subscription/change` | `Observable<PlanChangeResult>` |
|
|
476
|
+
| `checkChange(planId)` | `POST /api/billing/subscription/change/check` | `Observable<{ overages: PlanOverage[] }>` |
|
|
476
477
|
| `setSeats(seats)` | `POST /api/billing/subscription/seats` | `Observable<void>` |
|
|
477
478
|
| `listInvoices()` | `GET /api/billing/invoices` | `Observable<Invoice[]>` |
|
|
478
479
|
| `listPaymentMethods()` | `GET /api/billing/payment-methods` | `Observable<PaymentMethod[]>` |
|
|
@@ -655,7 +656,7 @@ An action with `parameters` (keel v1.2.75 `table_action_parameter`) opens a dial
|
|
|
655
656
|
|
|
656
657
|
### Plan change vs first-time checkout (v1.0.1)
|
|
657
658
|
|
|
658
|
-
First-time checkout uses `<sail-checkout-button>` (provider-hosted). For an **existing** subscription, upgrade/downgrade is a distinct path — `BillingService.changePlan(planId)` routes to keel's `SubscriptionLifecycle.ChangePlan` via an endpoint the app exposes and answers `{ status: 'changed' | 'scheduled' }`: a scheduled change sets `Subscription.scheduledAt` and `scheduledPlanId`, and re-selecting the current plan cancels it. Wire the picker's selection to whichever path applies:
|
|
659
|
+
First-time checkout uses `<sail-checkout-button>` (provider-hosted). For an **existing** subscription, upgrade/downgrade is a distinct path — `BillingService.changePlan(planId)` routes to keel's `SubscriptionLifecycle.ChangePlan` via an endpoint the app exposes and answers `{ status: 'changed' | 'scheduled' }`: a scheduled change sets `Subscription.scheduledAt` and `scheduledPlanId`, and re-selecting the current plan cancels it. A paid target the partner exceeds (more live-counted resources than it allows) is refused with 409; call `checkChange(planId)` first to list each `PlanOverage` to remove. Wire the picker's selection to whichever path applies:
|
|
659
660
|
|
|
660
661
|
```typescript
|
|
661
662
|
onPlanPicked(planId: string) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nauticana/sail",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.44",
|
|
4
4
|
"description": "Metadata-driven Angular component library for CRUD admin frontends backed by the keel Go backend. Tables, forms, navigation, OTP/2FA/social auth, billing, payout onboarding.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"exports": {
|
package/src/model/appdata.ts
CHANGED
|
@@ -416,6 +416,14 @@ export interface PlanChangeResult {
|
|
|
416
416
|
status: 'changed' | 'scheduled';
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
+
// A live-counted resource the partner holds more of than the target plan allows (keel `QuotaOverage`).
|
|
420
|
+
export interface PlanOverage {
|
|
421
|
+
resource: string;
|
|
422
|
+
caption: string;
|
|
423
|
+
count: number;
|
|
424
|
+
limit: number;
|
|
425
|
+
}
|
|
426
|
+
|
|
419
427
|
// Mirrors keel `billing.Invoice`; `status` is keel's one-character invoice status code.
|
|
420
428
|
export interface Invoice {
|
|
421
429
|
id: string;
|
|
@@ -82,6 +82,8 @@ export abstract class BaseAuthService extends BaseRestService {
|
|
|
82
82
|
* so the login screen doesn't keep the previous user's route tree. */
|
|
83
83
|
private preLoginRoutes: Routes | null = null;
|
|
84
84
|
private landing: string | null = null;
|
|
85
|
+
// A signed-in page's own address, read before Angular's first navigation can send an unknown menu route to login.
|
|
86
|
+
private initialUrl = localStorage.getItem('jwt') ? window.location.pathname + window.location.search : null;
|
|
85
87
|
|
|
86
88
|
// Lazy getters, not field initializers: fields would snapshot during super(),
|
|
87
89
|
// before a subclass constructor can call configureRestUrls().
|
|
@@ -881,19 +883,17 @@ export abstract class BaseAuthService extends BaseRestService {
|
|
|
881
883
|
}
|
|
882
884
|
|
|
883
885
|
/**
|
|
884
|
-
* Where to land after routes are (re)built
|
|
885
|
-
*
|
|
886
|
-
*
|
|
886
|
+
* Where to land after routes are (re)built: a `setLanding()` URL, else the
|
|
887
|
+
* address the page loaded with (first build) or is on, unless that is the
|
|
888
|
+
* root or a `/login` route, which go to the dashboard.
|
|
887
889
|
*/
|
|
888
890
|
private postLoginTarget(): string {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
const preserve = !!path && path !== '/' && !path.startsWith('/login');
|
|
896
|
-
return preserve ? path + window.location.search : '/dashboard';
|
|
891
|
+
const landing = this.landing;
|
|
892
|
+
const url = this.initialUrl ?? window.location.pathname + window.location.search;
|
|
893
|
+
this.landing = this.initialUrl = null;
|
|
894
|
+
if (landing) return landing;
|
|
895
|
+
const path = url.split('?')[0];
|
|
896
|
+
return path !== '/' && !path.startsWith('/login') ? url : '/dashboard';
|
|
897
897
|
}
|
|
898
898
|
|
|
899
899
|
async initRoutes() {
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
Invoice,
|
|
9
9
|
PaymentMethod,
|
|
10
10
|
PlanChangeResult,
|
|
11
|
+
PlanOverage,
|
|
11
12
|
PortalResponse,
|
|
12
13
|
PublicPlan,
|
|
13
14
|
SetupIntentResponse,
|
|
@@ -61,6 +62,12 @@ export class BillingService extends BaseRestService {
|
|
|
61
62
|
return this.http.post<PlanChangeResult>(this.url(RestURL.changePlanURL), { planId });
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
/** What to remove before changing to `planId`; empty when the change is allowed.
|
|
66
|
+
* `changePlan` refuses with 409 carrying the same overages. */
|
|
67
|
+
checkChange(planId: string): Observable<{ overages: PlanOverage[] }> {
|
|
68
|
+
return this.http.post<{ overages: PlanOverage[] }>(this.url(RestURL.checkChangeURL), { planId });
|
|
69
|
+
}
|
|
70
|
+
|
|
64
71
|
/** Set the seat quantity on the current active/trial subscription. */
|
|
65
72
|
setSeats(seats: number): Observable<void> {
|
|
66
73
|
return this.http.post<void>(this.url(RestURL.seatsURL), { seats });
|
package/src/service/rest_url.ts
CHANGED
|
@@ -32,6 +32,7 @@ export const RestURL = {
|
|
|
32
32
|
subscriptionURL: '/api/billing/subscription',
|
|
33
33
|
cancelSubURL: '/api/billing/subscription/cancel',
|
|
34
34
|
changePlanURL: '/api/billing/subscription/change',
|
|
35
|
+
checkChangeURL: '/api/billing/subscription/change/check',
|
|
35
36
|
seatsURL: '/api/billing/subscription/seats',
|
|
36
37
|
invoicesURL: '/api/billing/invoices',
|
|
37
38
|
paymentMethodsURL: '/api/billing/payment-methods',
|