@lukoweb/apitogo-plugin-dev-portal-billing 0.2.50 → 0.2.52
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/dist/{AccountPlansPage-DTVIVeRh.mjs → AccountPlansPage-CeUizdWk.mjs} +4 -4
- package/dist/{CheckoutPage-6nFq9GKO.mjs → CheckoutPage-CwSElFUk.mjs} +2 -2
- package/dist/{ContactSalesPage-Imh39E8z.mjs → ContactSalesPage-q7Dxxtx6.mjs} +2 -2
- package/dist/{CredentialsPage-BOVn3PpF.mjs → CredentialsPage-B2jPo6Go.mjs} +37 -27
- package/dist/{PricingCard-D-owYlf3.mjs → PricingCard-CKdq1oWn.mjs} +1 -1
- package/dist/{PricingPage-BTPQ-ntp.mjs → PricingPage-DnQQfy9L.mjs} +5 -5
- package/dist/{SubscriptionsPage-29Xs4XvM.mjs → SubscriptionsPage-msO31jv-.mjs} +26 -24
- package/dist/{UsagePage-U27bvmbF.mjs → UsagePage-BF4QgrTc.mjs} +4 -4
- package/dist/{account-links-BUeCO2Du.mjs → account-links-D_vS0GJn.mjs} +2 -164
- package/dist/{billing-state-DjUXQYSr.mjs → billing-state-DbU5Lpoz.mjs} +1 -1
- package/dist/{demo-session-B6WAunKV.mjs → demo-session-rN2Gj4lM.mjs} +2 -1
- package/dist/index.d.mts +19 -2
- package/dist/index.mjs +35 -17
- package/dist/{ChoosePlanNotice-DITJNrh1.mjs → key-plan-note-DTWeFhn2.mjs} +20 -1
- package/dist/useApiKeys-CVgXfUop.mjs +166 -0
- package/dist/{useDevPortalApiBaseUrl-Dk00H8xy.mjs → useDevPortalApiBaseUrl-BstWzITX.mjs} +1 -1
- package/dist/{useMySubscriptions-Bdo2HFrP.mjs → useMySubscriptions-BtxAXA1m.mjs} +2 -2
- package/dist/{usePlans-DVGt2Myv.mjs → usePlans-DPGFWcVo.mjs} +1 -1
- package/dist/{useUsage-DOUibue4.mjs → useUsage--5sZub3M.mjs} +1 -1
- package/package.json +3 -3
- /package/dist/{pricing-config-CfhRX9tk.mjs → pricing-config-02m4dyFf.mjs} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as PricingCard } from "./PricingCard-
|
|
1
|
+
import { f as parseManifestPlans, n as useDevPortalPreviewMode, r as useIsDemoSession, s as isActiveSubscription, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
2
|
+
import { t as PricingCard } from "./PricingCard-CKdq1oWn.mjs";
|
|
3
3
|
import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
4
|
-
import { t as useMySubscriptions } from "./useMySubscriptions-
|
|
5
|
-
import { t as usePlans } from "./usePlans-
|
|
4
|
+
import { t as useMySubscriptions } from "./useMySubscriptions-BtxAXA1m.mjs";
|
|
5
|
+
import { t as usePlans } from "./usePlans-DPGFWcVo.mjs";
|
|
6
6
|
import { Head } from "@lukoweb/apitogo/components";
|
|
7
7
|
import { CreditCardIcon } from "@lukoweb/apitogo/icons";
|
|
8
8
|
import { useSearchParams } from "@lukoweb/apitogo/router";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { o as PRICING_PATH, t as ACCOUNT_PLANS_PATH } from "./paths-ujy9wjVK.mjs";
|
|
2
|
-
import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-
|
|
3
|
-
import { t as FREE_CHECKOUT_MARKER } from "./billing-state-
|
|
2
|
+
import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
3
|
+
import { t as FREE_CHECKOUT_MARKER } from "./billing-state-DbU5Lpoz.mjs";
|
|
4
4
|
import { ShieldIcon } from "@lukoweb/apitogo/icons";
|
|
5
5
|
import { Link, useNavigate, useSearchParams } from "@lukoweb/apitogo/router";
|
|
6
6
|
import { useEffect } from "react";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { o as PRICING_PATH } from "./paths-ujy9wjVK.mjs";
|
|
2
|
-
import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl
|
|
2
|
+
import { l as isPlanCustomPriced, n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
3
3
|
import "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
4
|
-
import { t as usePlans } from "./usePlans-
|
|
4
|
+
import { t as usePlans } from "./usePlans-DPGFWcVo.mjs";
|
|
5
5
|
import { Link, useSearchParams } from "@lukoweb/apitogo/router";
|
|
6
6
|
import { useState } from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { t as ACCOUNT_PLANS_PATH } from "./paths-ujy9wjVK.mjs";
|
|
2
|
-
import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-
|
|
2
|
+
import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
3
3
|
import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
4
|
-
import { r as createDemoCredentials } from "./demo-session-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { r as createDemoCredentials, t as DEMO_GATEWAY_KEY_HEADER } from "./demo-session-rN2Gj4lM.mjs";
|
|
5
|
+
import { n as useApiKeys } from "./useApiKeys-CVgXfUop.mjs";
|
|
6
|
+
import { n as apiReferencePath, r as ApiKeysPanel } from "./account-links-D_vS0GJn.mjs";
|
|
7
|
+
import { n as ChoosePlanNotice, t as keyPlanNote } from "./key-plan-note-DTWeFhn2.mjs";
|
|
7
8
|
import { Head } from "@lukoweb/apitogo/components";
|
|
8
9
|
import { KeyRoundIcon } from "@lukoweb/apitogo/icons";
|
|
9
10
|
import { Link as Link$1 } from "@lukoweb/apitogo/router";
|
|
@@ -58,11 +59,12 @@ const CredentialsPage = () => {
|
|
|
58
59
|
const isDemoSession = useIsDemoSession();
|
|
59
60
|
const { data: credentials, isLoading, error } = useCredentials();
|
|
60
61
|
const { data: apiKeys, isLoading: keysLoading, error: keysError } = useApiKeys();
|
|
61
|
-
const { gatewayBaseUrl: configuredGatewayBaseUrl, links } = useDevPortalBillingConfig();
|
|
62
|
+
const { gatewayBaseUrl: configuredGatewayBaseUrl, links, keyHeader: configuredKeyHeader } = useDevPortalBillingConfig();
|
|
62
63
|
const apiReferenceLink = apiReferencePath(links);
|
|
63
64
|
const gatewayBaseUrl = credentials?.gatewayBaseUrl?.trim() || apiKeys?.gatewayBaseUrl?.trim() || configuredGatewayBaseUrl?.trim() || zudokuConfig.__meta?.gatewayBaseUrl?.trim() || null;
|
|
64
|
-
const header = apiKeys?.header
|
|
65
|
+
const header = configuredKeyHeader?.trim() || apiKeys?.header || DEMO_GATEWAY_KEY_HEADER;
|
|
65
66
|
const plans = apiKeys?.plans ?? [];
|
|
67
|
+
const planNote = isDemoSession ? null : keyPlanNote(plans);
|
|
66
68
|
return /* @__PURE__ */ jsxs("div", {
|
|
67
69
|
className: "mx-auto max-w-screen-lg px-4 pb-16 pt-(--padding-content-top) lg:px-8",
|
|
68
70
|
children: [
|
|
@@ -128,29 +130,37 @@ const CredentialsPage = () => {
|
|
|
128
130
|
}) : null,
|
|
129
131
|
(isDemoSession || !isPreview) && auth.isAuthenticated && !error && !keysError && !isLoading && !keysLoading ? /* @__PURE__ */ jsxs("div", {
|
|
130
132
|
className: "space-y-6",
|
|
131
|
-
children: [
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
133
|
+
children: [
|
|
134
|
+
planNote ? /* @__PURE__ */ jsx("div", {
|
|
135
|
+
role: "status",
|
|
136
|
+
className: "rounded-xl border border-primary/30 bg-primary/5 p-4 text-sm",
|
|
137
|
+
children: planNote.text
|
|
138
|
+
}) : null,
|
|
139
|
+
plans.length === 0 ? /* @__PURE__ */ jsx(ChoosePlanNotice, {}) : plans.map((plan) => /* @__PURE__ */ jsx(ApiKeysPanel, {
|
|
140
|
+
plan,
|
|
141
|
+
header,
|
|
142
|
+
readOnly: isDemoSession
|
|
143
|
+
}, plan.planSku)),
|
|
144
|
+
/* @__PURE__ */ jsxs("article", {
|
|
145
|
+
className: "rounded-xl border bg-card/60 p-6 space-y-4",
|
|
146
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
147
|
+
className: "space-y-1",
|
|
148
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
149
|
+
className: "text-lg font-medium",
|
|
150
|
+
children: "Gateway URL"
|
|
151
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
152
|
+
className: "text-sm text-muted-foreground",
|
|
153
|
+
children: "Base URL for API requests through the gateway."
|
|
154
|
+
})]
|
|
155
|
+
}), gatewayBaseUrl ? /* @__PURE__ */ jsx(Value, {
|
|
156
|
+
className: "max-w-full w-full",
|
|
157
|
+
value: gatewayBaseUrl
|
|
158
|
+
}) : /* @__PURE__ */ jsx("p", {
|
|
143
159
|
className: "text-sm text-muted-foreground",
|
|
144
|
-
children: "
|
|
160
|
+
children: "Available after your API is deployed to the gateway."
|
|
145
161
|
})]
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
value: gatewayBaseUrl
|
|
149
|
-
}) : /* @__PURE__ */ jsx("p", {
|
|
150
|
-
className: "text-sm text-muted-foreground",
|
|
151
|
-
children: "Available after your API is deployed to the gateway."
|
|
152
|
-
})]
|
|
153
|
-
})]
|
|
162
|
+
})
|
|
163
|
+
]
|
|
154
164
|
}) : null,
|
|
155
165
|
/* @__PURE__ */ jsxs("div", {
|
|
156
166
|
className: "mt-8 flex flex-wrap gap-3",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as CHECKOUT_PATH, r as CONTACT_SALES_PATH } from "./paths-ujy9wjVK.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { d as listPlanFeatureLines, i as formatPlanDisplayPrice, l as isPlanCustomPriced, o as formatPlanPriceUnit, s as isActiveSubscription, u as isPlanFree } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
3
3
|
import { Button } from "@lukoweb/apitogo/components";
|
|
4
4
|
import { Link as Link$1 } from "@lukoweb/apitogo/router";
|
|
5
5
|
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@lukoweb/apitogo/ui/AlertDialog.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as PricingCard } from "./PricingCard-
|
|
1
|
+
import { f as parseManifestPlans, h as pickFeaturedPlanSku, n as useDevPortalPreviewMode } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
2
|
+
import { t as PricingCard } from "./PricingCard-CKdq1oWn.mjs";
|
|
3
3
|
import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
4
|
-
import { t as usePlans } from "./usePlans-
|
|
5
|
-
import { t as isPreviewPricingEnabled } from "./pricing-config-
|
|
6
|
-
import { a as isPricingLoading, n as buildCompareRows } from "./billing-state-
|
|
4
|
+
import { t as usePlans } from "./usePlans-DPGFWcVo.mjs";
|
|
5
|
+
import { t as isPreviewPricingEnabled } from "./pricing-config-02m4dyFf.mjs";
|
|
6
|
+
import { a as isPricingLoading, n as buildCompareRows } from "./billing-state-DbU5Lpoz.mjs";
|
|
7
7
|
import { Head, Heading } from "@lukoweb/apitogo/components";
|
|
8
8
|
import { Navigate } from "@lukoweb/apitogo/router";
|
|
9
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { i as CREDENTIALS_PATH, t as ACCOUNT_PLANS_PATH } from "./paths-ujy9wjVK.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { a as formatPlanPrice, p as parseMonthlyQuotaCalls, s as isActiveSubscription, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
3
3
|
import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
4
|
-
import { t as useMySubscriptions } from "./useMySubscriptions-
|
|
5
|
-
import { t as usePlans } from "./usePlans-
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
4
|
+
import { t as useMySubscriptions } from "./useMySubscriptions-BtxAXA1m.mjs";
|
|
5
|
+
import { t as usePlans } from "./usePlans-DPGFWcVo.mjs";
|
|
6
|
+
import { n as useApiKeys } from "./useApiKeys-CVgXfUop.mjs";
|
|
7
|
+
import { t as useUsage } from "./useUsage--5sZub3M.mjs";
|
|
8
|
+
import { i as hasPaymentProvider, r as checkoutConfirmingText } from "./billing-state-DbU5Lpoz.mjs";
|
|
9
|
+
import { n as ChoosePlanNotice, t as keyPlanNote } from "./key-plan-note-DTWeFhn2.mjs";
|
|
9
10
|
import { Head, Spinner } from "@lukoweb/apitogo/components";
|
|
10
11
|
import { Link as Link$1, useSearchParams } from "@lukoweb/apitogo/router";
|
|
11
12
|
import { useEffect } from "react";
|
|
12
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13
14
|
import { useMutation } from "@lukoweb/apitogo/react-query";
|
|
14
15
|
import { useAuth } from "@lukoweb/apitogo/hooks";
|
|
15
16
|
import { Button as Button$1 } from "@lukoweb/apitogo/ui/Button.js";
|
|
@@ -47,6 +48,8 @@ const SubscriptionsPage = () => {
|
|
|
47
48
|
const { data: catalog } = usePlans();
|
|
48
49
|
const billingPortal = useBillingPortal();
|
|
49
50
|
const { data: usage } = useUsage("30d");
|
|
51
|
+
const { data: apiKeys } = useApiKeys();
|
|
52
|
+
const planNote = keyPlanNote(apiKeys?.plans ?? []);
|
|
50
53
|
const planBySku = new Map((catalog?.plans ?? []).map((plan) => [plan.sku, plan]));
|
|
51
54
|
const subscriptions = me?.subscriptions ?? [];
|
|
52
55
|
const activeSubscription = subscriptions.find((sub) => isActiveSubscription(sub.status));
|
|
@@ -81,9 +84,18 @@ const SubscriptionsPage = () => {
|
|
|
81
84
|
className: "mb-6 rounded-xl border border-border bg-card p-4 text-sm",
|
|
82
85
|
children: checkoutConfirmingText(checkoutPaid)
|
|
83
86
|
}) : null,
|
|
84
|
-
checkoutSuccess && activeSubscription ? /* @__PURE__ */
|
|
87
|
+
checkoutSuccess && activeSubscription ? /* @__PURE__ */ jsx("div", {
|
|
85
88
|
className: "mb-6 rounded-xl border border-green-500/30 bg-green-500/10 p-4 text-sm",
|
|
86
|
-
children: [
|
|
89
|
+
children: planNote ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
90
|
+
planNote.text,
|
|
91
|
+
" ",
|
|
92
|
+
/* @__PURE__ */ jsx(Link$1, {
|
|
93
|
+
to: CREDENTIALS_PATH,
|
|
94
|
+
className: "font-medium underline",
|
|
95
|
+
children: "Create the key"
|
|
96
|
+
}),
|
|
97
|
+
"."
|
|
98
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
87
99
|
"Your subscription is active.",
|
|
88
100
|
" ",
|
|
89
101
|
/* @__PURE__ */ jsx(Link$1, {
|
|
@@ -92,7 +104,7 @@ const SubscriptionsPage = () => {
|
|
|
92
104
|
children: "View your API credentials"
|
|
93
105
|
}),
|
|
94
106
|
"."
|
|
95
|
-
]
|
|
107
|
+
] })
|
|
96
108
|
}) : null,
|
|
97
109
|
isLoading ? /* @__PURE__ */ jsx("p", {
|
|
98
110
|
className: "text-muted-foreground",
|
|
@@ -109,14 +121,9 @@ const SubscriptionsPage = () => {
|
|
|
109
121
|
className: "mt-2 text-2xl font-bold",
|
|
110
122
|
children: activePlan?.displayName ?? activeSubscription.planSku
|
|
111
123
|
}),
|
|
112
|
-
activePlan ? /* @__PURE__ */
|
|
124
|
+
activePlan ? /* @__PURE__ */ jsx("p", {
|
|
113
125
|
className: "mt-1 text-sm text-muted-foreground",
|
|
114
|
-
children:
|
|
115
|
-
formatPlanPrice(activePlan),
|
|
116
|
-
" ·",
|
|
117
|
-
" ",
|
|
118
|
-
formatBillingPeriod(activePlan.billingPeriod)
|
|
119
|
-
]
|
|
126
|
+
children: formatPlanPrice(activePlan)
|
|
120
127
|
}) : /* @__PURE__ */ jsx("p", {
|
|
121
128
|
className: "mt-1 text-sm text-muted-foreground",
|
|
122
129
|
children: "This plan is no longer offered. Your access continues until you cancel or change plan."
|
|
@@ -164,14 +171,9 @@ const SubscriptionsPage = () => {
|
|
|
164
171
|
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
|
|
165
172
|
className: "font-semibold",
|
|
166
173
|
children: plan?.displayName ?? subscription.planSku
|
|
167
|
-
}), plan ? /* @__PURE__ */
|
|
174
|
+
}), plan ? /* @__PURE__ */ jsx("p", {
|
|
168
175
|
className: "mt-1 text-sm text-muted-foreground",
|
|
169
|
-
children:
|
|
170
|
-
formatPlanPrice(plan),
|
|
171
|
-
" ·",
|
|
172
|
-
" ",
|
|
173
|
-
formatBillingPeriod(plan.billingPeriod)
|
|
174
|
-
]
|
|
176
|
+
children: formatPlanPrice(plan)
|
|
175
177
|
}) : null] }), /* @__PURE__ */ jsx("span", {
|
|
176
178
|
className: "rounded-full bg-muted px-3 py-1 text-xs font-medium text-muted-foreground",
|
|
177
179
|
children: subscription.status
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { m as parseQuotaPeriodDays, p as parseMonthlyQuotaCalls, s as isActiveSubscription } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
2
2
|
import "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
3
|
-
import { t as useMySubscriptions } from "./useMySubscriptions-
|
|
4
|
-
import { t as usePlans } from "./usePlans-
|
|
5
|
-
import { t as useUsage } from "./useUsage
|
|
3
|
+
import { t as useMySubscriptions } from "./useMySubscriptions-BtxAXA1m.mjs";
|
|
4
|
+
import { t as usePlans } from "./usePlans-DPGFWcVo.mjs";
|
|
5
|
+
import { t as useUsage } from "./useUsage--5sZub3M.mjs";
|
|
6
6
|
import { Head } from "@lukoweb/apitogo/components";
|
|
7
7
|
import { Link as Link$1 } from "@lukoweb/apitogo/router";
|
|
8
8
|
import { useState } from "react";
|
|
@@ -1,176 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
3
|
-
import { n as createDemoApiKeys, t as DEMO_GATEWAY_KEY_HEADER } from "./demo-session-B6WAunKV.mjs";
|
|
1
|
+
import { i as useRevokeApiKey, r as useCreateApiKey, t as API_KEY_CAP_REACHED } from "./useApiKeys-CVgXfUop.mjs";
|
|
4
2
|
import { Spinner } from "@lukoweb/apitogo/components";
|
|
5
3
|
import { KeyRoundIcon, TrashIcon } from "@lukoweb/apitogo/icons";
|
|
6
4
|
import { useState } from "react";
|
|
7
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
-
import { useMutation, useQuery, useQueryClient } from "@lukoweb/apitogo/react-query";
|
|
9
|
-
import { useAuth } from "@lukoweb/apitogo/hooks";
|
|
10
6
|
import { Button as Button$1 } from "@lukoweb/apitogo/ui/Button.js";
|
|
11
7
|
import { Alert, AlertTitle } from "@lukoweb/apitogo/ui/Alert.js";
|
|
12
8
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@lukoweb/apitogo/ui/Dialog.js";
|
|
13
9
|
import { Input } from "@lukoweb/apitogo/ui/Input.js";
|
|
14
10
|
import { Secret } from "@lukoweb/apitogo/ui/Secret.js";
|
|
15
11
|
|
|
16
|
-
//#region src/utils/playground-key.ts
|
|
17
|
-
const STORAGE_KEY = "apitogo-playground-api-key";
|
|
18
|
-
const PLAYGROUND_KEY_IDENTITY_ID = "api-key";
|
|
19
|
-
const storage = () => {
|
|
20
|
-
try {
|
|
21
|
-
return typeof localStorage === "undefined" ? void 0 : localStorage;
|
|
22
|
-
} catch {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* Remembers a key the moment it is created: the plans backend shows a key's secret only then, so this is the one
|
|
28
|
-
* chance to offer it to the playground later. Only the last key is kept, in this browser only.
|
|
29
|
-
*/
|
|
30
|
-
function rememberPlaygroundKey(entry) {
|
|
31
|
-
if (!entry.key) return;
|
|
32
|
-
const value = {
|
|
33
|
-
id: entry.id,
|
|
34
|
-
key: entry.key,
|
|
35
|
-
header: entry.header || DEMO_GATEWAY_KEY_HEADER,
|
|
36
|
-
keyPrefix: entry.keyPrefix
|
|
37
|
-
};
|
|
38
|
-
try {
|
|
39
|
-
storage()?.setItem(STORAGE_KEY, JSON.stringify(value));
|
|
40
|
-
} catch {}
|
|
41
|
-
}
|
|
42
|
-
function readPlaygroundKey() {
|
|
43
|
-
try {
|
|
44
|
-
const raw = storage()?.getItem(STORAGE_KEY);
|
|
45
|
-
if (!raw) return void 0;
|
|
46
|
-
const parsed = JSON.parse(raw);
|
|
47
|
-
return parsed.id && parsed.key && parsed.header && parsed.keyPrefix ? parsed : void 0;
|
|
48
|
-
} catch {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
/** A revoked key stops working, so it is no longer offered. */
|
|
53
|
-
function forgetPlaygroundKey(keyId) {
|
|
54
|
-
if (readPlaygroundKey()?.id !== keyId) return;
|
|
55
|
-
try {
|
|
56
|
-
storage()?.removeItem(STORAGE_KEY);
|
|
57
|
-
} catch {}
|
|
58
|
-
}
|
|
59
|
-
/** The "Try it" identity for the remembered key, sent in the gateway's key header. */
|
|
60
|
-
function playgroundKeyIdentities() {
|
|
61
|
-
const remembered = readPlaygroundKey();
|
|
62
|
-
if (!remembered) return [];
|
|
63
|
-
return [{
|
|
64
|
-
id: PLAYGROUND_KEY_IDENTITY_ID,
|
|
65
|
-
label: `Your API key (${remembered.keyPrefix}…)`,
|
|
66
|
-
authorizationFields: { headers: [remembered.header] },
|
|
67
|
-
authorizeRequest: (request) => {
|
|
68
|
-
request.headers.set(remembered.header, remembered.key);
|
|
69
|
-
return request;
|
|
70
|
-
}
|
|
71
|
-
}];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
//#endregion
|
|
75
|
-
//#region src/hooks/useApiKeys.ts
|
|
76
|
-
/** Thrown when the developer has hit their plan's per-developer key cap. */
|
|
77
|
-
const API_KEY_CAP_REACHED = "API_KEY_CAP_REACHED";
|
|
78
|
-
function apiKeysQueryKey(apiBaseUrl) {
|
|
79
|
-
return ["dev-portal-api-keys", apiBaseUrl];
|
|
80
|
-
}
|
|
81
|
-
function useApiKeys() {
|
|
82
|
-
const auth = useAuth();
|
|
83
|
-
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
84
|
-
const isPreview = useDevPortalPreviewMode();
|
|
85
|
-
const isDemoSession = useIsDemoSession();
|
|
86
|
-
const { previewPlans } = useDevPortalBillingConfig();
|
|
87
|
-
const liveQuery = useQuery({
|
|
88
|
-
queryKey: apiKeysQueryKey(apiBaseUrl),
|
|
89
|
-
enabled: auth.isAuthenticated && !isPreview && !isDemoSession && Boolean(apiBaseUrl),
|
|
90
|
-
queryFn: async () => {
|
|
91
|
-
const response = await fetch(`${apiBaseUrl}/api/v1/api-keys`, {
|
|
92
|
-
method: "GET",
|
|
93
|
-
headers: { Accept: "application/json" },
|
|
94
|
-
credentials: "include"
|
|
95
|
-
});
|
|
96
|
-
if (response.status === 404) throw new Error("NO_ENTITLEMENT");
|
|
97
|
-
if (!response.ok) throw new Error("Failed to load API keys.");
|
|
98
|
-
return await response.json();
|
|
99
|
-
},
|
|
100
|
-
retry: (failureCount, error) => error instanceof Error && error.message === "NO_ENTITLEMENT" ? false : failureCount < 2
|
|
101
|
-
});
|
|
102
|
-
if (isDemoSession && auth.isAuthenticated) return {
|
|
103
|
-
...liveQuery,
|
|
104
|
-
data: createDemoApiKeys(previewPlans),
|
|
105
|
-
error: null,
|
|
106
|
-
isError: false,
|
|
107
|
-
isFetching: false,
|
|
108
|
-
isLoading: false,
|
|
109
|
-
isPending: false,
|
|
110
|
-
isSuccess: true,
|
|
111
|
-
status: "success"
|
|
112
|
-
};
|
|
113
|
-
return liveQuery;
|
|
114
|
-
}
|
|
115
|
-
function useCreateApiKey() {
|
|
116
|
-
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
117
|
-
const queryClient = useQueryClient();
|
|
118
|
-
return useMutation({
|
|
119
|
-
mutationFn: async ({ name, planSku }) => {
|
|
120
|
-
if (!apiBaseUrl) throw new Error("Dev portal API is not configured.");
|
|
121
|
-
const response = await fetch(`${apiBaseUrl}/api/v1/api-keys`, {
|
|
122
|
-
method: "POST",
|
|
123
|
-
headers: {
|
|
124
|
-
Accept: "application/json",
|
|
125
|
-
"Content-Type": "application/json"
|
|
126
|
-
},
|
|
127
|
-
credentials: "include",
|
|
128
|
-
body: JSON.stringify({
|
|
129
|
-
name,
|
|
130
|
-
planSku
|
|
131
|
-
})
|
|
132
|
-
});
|
|
133
|
-
if (response.status === 409) throw new Error(API_KEY_CAP_REACHED);
|
|
134
|
-
if (!response.ok) throw new Error(await readProblemDetail(response) ?? "Failed to create API key.");
|
|
135
|
-
return await response.json();
|
|
136
|
-
},
|
|
137
|
-
onSuccess: async (created) => {
|
|
138
|
-
rememberPlaygroundKey({
|
|
139
|
-
...created,
|
|
140
|
-
header: queryClient.getQueryData(apiKeysQueryKey(apiBaseUrl))?.header
|
|
141
|
-
});
|
|
142
|
-
await queryClient.invalidateQueries({ queryKey: apiKeysQueryKey(apiBaseUrl) });
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
function useRevokeApiKey() {
|
|
147
|
-
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
148
|
-
const queryClient = useQueryClient();
|
|
149
|
-
return useMutation({
|
|
150
|
-
mutationFn: async (keyId) => {
|
|
151
|
-
if (!apiBaseUrl) throw new Error("Dev portal API is not configured.");
|
|
152
|
-
const response = await fetch(`${apiBaseUrl}/api/v1/api-keys/${keyId}`, {
|
|
153
|
-
method: "DELETE",
|
|
154
|
-
headers: { Accept: "application/json" },
|
|
155
|
-
credentials: "include"
|
|
156
|
-
});
|
|
157
|
-
if (!response.ok && response.status !== 204) throw new Error(await readProblemDetail(response) ?? "Failed to revoke API key.");
|
|
158
|
-
},
|
|
159
|
-
onSuccess: async (_result, keyId) => {
|
|
160
|
-
forgetPlaygroundKey(keyId);
|
|
161
|
-
await queryClient.invalidateQueries({ queryKey: apiKeysQueryKey(apiBaseUrl) });
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
async function readProblemDetail(response) {
|
|
166
|
-
try {
|
|
167
|
-
return (await response.json()).detail?.trim() || null;
|
|
168
|
-
} catch {
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
//#endregion
|
|
174
12
|
//#region src/components/ApiKeysPanel.tsx
|
|
175
13
|
/**
|
|
176
14
|
* Lists gateway API keys for one plan with create and revoke. The plaintext key is returned by the
|
|
@@ -368,4 +206,4 @@ function apiReferencePath(links) {
|
|
|
368
206
|
}
|
|
369
207
|
|
|
370
208
|
//#endregion
|
|
371
|
-
export {
|
|
209
|
+
export { apiReferencePath as n, ApiKeysPanel as r, accountLayoutProps as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as
|
|
1
|
+
import { _ as rateLimitCell, g as quotaCell } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/utils/billing-state.ts
|
|
4
4
|
/** Whether the pricing page has no plans to show yet because the answer is still on its way. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
//#region src/utils/demo-session.ts
|
|
2
2
|
const DEMO_SUBSCRIPTION_KEY = "demo-subscription-key";
|
|
3
|
-
|
|
3
|
+
/** The platform gateway's default key header (API-120). */
|
|
4
|
+
const DEMO_GATEWAY_KEY_HEADER = "X-API-Key";
|
|
4
5
|
const DEMO_MAX_KEYS = 5;
|
|
5
6
|
function resolveDemoPlans(plans) {
|
|
6
7
|
if (plans && plans.length > 0) return plans.map((plan) => ({
|
package/dist/index.d.mts
CHANGED
|
@@ -53,7 +53,12 @@ type DevPortalBillingConfig = {
|
|
|
53
53
|
api?: string;
|
|
54
54
|
}; /** Address of the plans backend; the portal's generated wrapper passes it. The plans request starts from it early. */
|
|
55
55
|
apiBaseUrl?: string; /** Live gateway URL (publish bake or explicit plugin option). */
|
|
56
|
-
gatewayBaseUrl?: string | null;
|
|
56
|
+
gatewayBaseUrl?: string | null;
|
|
57
|
+
/**
|
|
58
|
+
* The header API users send their key in, as the portal build knows it from the gateway config. Wins over the plans
|
|
59
|
+
* backend's answer, which only knows the platform default.
|
|
60
|
+
*/
|
|
61
|
+
keyHeader?: string; /** Local preview plan catalog (from `apitogo.config.json` `plans`). */
|
|
57
62
|
previewPlans?: Array<{
|
|
58
63
|
sku: string;
|
|
59
64
|
displayName: string;
|
|
@@ -236,6 +241,18 @@ declare function useCreateApiKey(): _tanstack_react_query0.UseMutationResult<Dev
|
|
|
236
241
|
}, unknown>;
|
|
237
242
|
declare function useRevokeApiKey(): _tanstack_react_query0.UseMutationResult<void, Error, string, unknown>;
|
|
238
243
|
//#endregion
|
|
244
|
+
//#region src/hooks/useGatewayCall.d.ts
|
|
245
|
+
/** How a caller reaches the API: the gateway address and the header the key goes in. */
|
|
246
|
+
type GatewayCall = {
|
|
247
|
+
/** The gateway's base URL, without a trailing slash; null before the gateway is deployed. */baseUrl: string | null; /** The header API users send their key in. */
|
|
248
|
+
keyHeader: string;
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* The real gateway address and key header, as the portal build knows them (API-120), so a page's code sample works
|
|
252
|
+
* when a visitor pastes it with their key.
|
|
253
|
+
*/
|
|
254
|
+
declare function useGatewayCall(): GatewayCall;
|
|
255
|
+
//#endregion
|
|
239
256
|
//#region src/hooks/usePlans.d.ts
|
|
240
257
|
declare function usePlans(): UseQueryResult<DevPortalPlanCatalog>;
|
|
241
258
|
//#endregion
|
|
@@ -258,4 +275,4 @@ declare const toPlansPreviewItems: (plans: DevPortalPlan[]) => PlansPreviewItem[
|
|
|
258
275
|
*/
|
|
259
276
|
declare const usePlansPreview: () => PlansPreviewItem[];
|
|
260
277
|
//#endregion
|
|
261
|
-
export { ACCOUNT_PLANS_PATH, API_KEY_CAP_REACHED, AccountPlansPage, ApiKeysPanel, CHECKOUT_PATH, CONTACT_SALES_PATH, CREDENTIALS_PATH, type DevPortalPlan, type DevPortalPlanCatalog, OverviewDashboardPage, PRICING_PATH, type PlansPreviewItem, SUBSCRIPTIONS_PATH, USAGE_PATH, devPortalBillingPlugin, toPlansPreviewItems, useApiKeys, useCreateApiKey, usePlans, usePlansPreview, useRevokeApiKey };
|
|
278
|
+
export { ACCOUNT_PLANS_PATH, API_KEY_CAP_REACHED, AccountPlansPage, ApiKeysPanel, CHECKOUT_PATH, CONTACT_SALES_PATH, CREDENTIALS_PATH, type DevPortalPlan, type DevPortalPlanCatalog, type GatewayCall, OverviewDashboardPage, PRICING_PATH, type PlansPreviewItem, SUBSCRIPTIONS_PATH, USAGE_PATH, devPortalBillingPlugin, toPlansPreviewItems, useApiKeys, useCreateApiKey, useGatewayCall, usePlans, usePlansPreview, useRevokeApiKey };
|
package/dist/index.mjs
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import { t as AccountPlansPage } from "./AccountPlansPage-
|
|
1
|
+
import { t as AccountPlansPage } from "./AccountPlansPage-CeUizdWk.mjs";
|
|
2
2
|
import { a as LOGIN_NAV_PATH, c as USAGE_PATH, i as CREDENTIALS_PATH, n as CHECKOUT_PATH, o as PRICING_PATH, r as CONTACT_SALES_PATH, s as SUBSCRIPTIONS_PATH, t as ACCOUNT_PLANS_PATH } from "./paths-ujy9wjVK.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import "./PricingCard-
|
|
5
|
-
import { t as DevPortalBillingProvider } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
6
|
-
import { t as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { n as
|
|
10
|
-
import { t as
|
|
3
|
+
import { d as listPlanFeatureLines, f as parseManifestPlans, h as pickFeaturedPlanSku, i as formatPlanDisplayPrice, o as formatPlanPriceUnit, s as isActiveSubscription, v as resolveDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
4
|
+
import "./PricingCard-CKdq1oWn.mjs";
|
|
5
|
+
import { n as useDevPortalBillingConfig, t as DevPortalBillingProvider } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
6
|
+
import { t as DEMO_GATEWAY_KEY_HEADER } from "./demo-session-rN2Gj4lM.mjs";
|
|
7
|
+
import { t as useMySubscriptions } from "./useMySubscriptions-BtxAXA1m.mjs";
|
|
8
|
+
import { n as startPlansPrefetch, t as usePlans } from "./usePlans-DPGFWcVo.mjs";
|
|
9
|
+
import { a as playgroundKeyIdentities, i as useRevokeApiKey, n as useApiKeys, r as useCreateApiKey, t as API_KEY_CAP_REACHED } from "./useApiKeys-CVgXfUop.mjs";
|
|
10
|
+
import { n as useUsageRecent, t as useUsage } from "./useUsage--5sZub3M.mjs";
|
|
11
|
+
import { r as ApiKeysPanel, t as accountLayoutProps } from "./account-links-D_vS0GJn.mjs";
|
|
12
|
+
import { t as isPreviewPricingEnabled } from "./pricing-config-02m4dyFf.mjs";
|
|
11
13
|
import { Head, Link } from "@lukoweb/apitogo/components";
|
|
12
14
|
import { ArrowRightIcon, KeyRoundIcon, StarsIcon } from "@lukoweb/apitogo/icons";
|
|
13
15
|
import { Outlet } from "@lukoweb/apitogo/router";
|
|
14
16
|
import { Suspense, lazy } from "react";
|
|
15
17
|
import manifestCatalog from "virtual:apitogo-local-manifest";
|
|
18
|
+
import zudokuConfig from "virtual:zudoku-config";
|
|
16
19
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
17
20
|
import { useAuth } from "@lukoweb/apitogo/hooks";
|
|
18
21
|
import { Button as Button$1 } from "@lukoweb/apitogo/ui/Button.js";
|
|
@@ -363,13 +366,13 @@ async function getSignedInIdentities(apiBaseUrl, fetchImpl = fetch) {
|
|
|
363
366
|
|
|
364
367
|
//#endregion
|
|
365
368
|
//#region src/DevPortalBillingPlugin.tsx
|
|
366
|
-
const AccountPlansPage$1 = lazy(() => import("./AccountPlansPage-
|
|
367
|
-
const CheckoutPage = lazy(() => import("./CheckoutPage-
|
|
368
|
-
const ContactSalesPage = lazy(() => import("./ContactSalesPage-
|
|
369
|
-
const CredentialsPage = lazy(() => import("./CredentialsPage-
|
|
370
|
-
const PricingPage = lazy(() => import("./PricingPage-
|
|
371
|
-
const SubscriptionsPage = lazy(() => import("./SubscriptionsPage-
|
|
372
|
-
const UsagePage = lazy(() => import("./UsagePage-
|
|
369
|
+
const AccountPlansPage$1 = lazy(() => import("./AccountPlansPage-CeUizdWk.mjs").then((n) => n.n));
|
|
370
|
+
const CheckoutPage = lazy(() => import("./CheckoutPage-CwSElFUk.mjs"));
|
|
371
|
+
const ContactSalesPage = lazy(() => import("./ContactSalesPage-q7Dxxtx6.mjs"));
|
|
372
|
+
const CredentialsPage = lazy(() => import("./CredentialsPage-B2jPo6Go.mjs"));
|
|
373
|
+
const PricingPage = lazy(() => import("./PricingPage-DnQQfy9L.mjs"));
|
|
374
|
+
const SubscriptionsPage = lazy(() => import("./SubscriptionsPage-msO31jv-.mjs"));
|
|
375
|
+
const UsagePage = lazy(() => import("./UsagePage-BF4QgrTc.mjs"));
|
|
373
376
|
function resolvePricingEnabled(config) {
|
|
374
377
|
if (config.__meta?.pricingEnabled !== void 0) return config.__meta.pricingEnabled;
|
|
375
378
|
return isPreviewPricingEnabled();
|
|
@@ -474,6 +477,21 @@ const devPortalBillingPlugin = createPlugin((options = {}) => {
|
|
|
474
477
|
};
|
|
475
478
|
});
|
|
476
479
|
|
|
480
|
+
//#endregion
|
|
481
|
+
//#region src/hooks/useGatewayCall.ts
|
|
482
|
+
/**
|
|
483
|
+
* The real gateway address and key header, as the portal build knows them (API-120), so a page's code sample works
|
|
484
|
+
* when a visitor pastes it with their key.
|
|
485
|
+
*/
|
|
486
|
+
function useGatewayCall() {
|
|
487
|
+
const { gatewayBaseUrl, keyHeader } = useDevPortalBillingConfig();
|
|
488
|
+
const baseUrl = gatewayBaseUrl?.trim() || zudokuConfig.__meta?.gatewayBaseUrl?.trim() || "";
|
|
489
|
+
return {
|
|
490
|
+
baseUrl: baseUrl ? baseUrl.replace(/\/+$/, "") : null,
|
|
491
|
+
keyHeader: keyHeader?.trim() || DEMO_GATEWAY_KEY_HEADER
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
477
495
|
//#endregion
|
|
478
496
|
//#region src/hooks/usePlansPreview.ts
|
|
479
497
|
/** Maps plans to landing `PlansPreviewSection` cards, highlighting the same plan as /pricing. */
|
|
@@ -499,4 +517,4 @@ const usePlansPreview = () => {
|
|
|
499
517
|
};
|
|
500
518
|
|
|
501
519
|
//#endregion
|
|
502
|
-
export { ACCOUNT_PLANS_PATH, API_KEY_CAP_REACHED, AccountPlansPage, ApiKeysPanel, CHECKOUT_PATH, CONTACT_SALES_PATH, CREDENTIALS_PATH, OverviewDashboardPage, PRICING_PATH, SUBSCRIPTIONS_PATH, USAGE_PATH, devPortalBillingPlugin, toPlansPreviewItems, useApiKeys, useCreateApiKey, usePlans, usePlansPreview, useRevokeApiKey };
|
|
520
|
+
export { ACCOUNT_PLANS_PATH, API_KEY_CAP_REACHED, AccountPlansPage, ApiKeysPanel, CHECKOUT_PATH, CONTACT_SALES_PATH, CREDENTIALS_PATH, OverviewDashboardPage, PRICING_PATH, SUBSCRIPTIONS_PATH, USAGE_PATH, devPortalBillingPlugin, toPlansPreviewItems, useApiKeys, useCreateApiKey, useGatewayCall, usePlans, usePlansPreview, useRevokeApiKey };
|
|
@@ -31,4 +31,23 @@ const ChoosePlanNotice = ({ className = "rounded-xl border border-dashed p-8 tex
|
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
//#endregion
|
|
34
|
-
|
|
34
|
+
//#region src/utils/key-plan-note.ts
|
|
35
|
+
/**
|
|
36
|
+
* An API user may hold several plans at once, each with its own keys and limits (API-121). After buying a plan, their
|
|
37
|
+
* app still calls with the key of the plan they had, so it keeps that plan's limits. When a plan has no key yet while
|
|
38
|
+
* another plan they hold has one, this says so and what to do; otherwise null.
|
|
39
|
+
*/
|
|
40
|
+
function keyPlanNote(plans) {
|
|
41
|
+
const withKeys = plans.find((plan) => plan.activeCount > 0);
|
|
42
|
+
const withoutKey = plans.find((plan) => plan.activeCount === 0 && plan.canCreate);
|
|
43
|
+
if (!withKeys || !withoutKey) return null;
|
|
44
|
+
const fresh = withoutKey.displayName || withoutKey.planSku;
|
|
45
|
+
const old = withKeys.displayName || withKeys.planSku;
|
|
46
|
+
return {
|
|
47
|
+
planSku: withoutKey.planSku,
|
|
48
|
+
text: `You're on ${fresh} now. Your ${old} key still has ${old} limits. Create a ${fresh} key and use it in your app to get ${fresh} limits.`
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
//#endregion
|
|
53
|
+
export { ChoosePlanNotice as n, keyPlanNote as t };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
2
|
+
import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
3
|
+
import { n as createDemoApiKeys, t as DEMO_GATEWAY_KEY_HEADER } from "./demo-session-rN2Gj4lM.mjs";
|
|
4
|
+
import { useMutation, useQuery, useQueryClient } from "@lukoweb/apitogo/react-query";
|
|
5
|
+
import { useAuth } from "@lukoweb/apitogo/hooks";
|
|
6
|
+
|
|
7
|
+
//#region src/utils/playground-key.ts
|
|
8
|
+
const STORAGE_KEY = "apitogo-playground-api-key";
|
|
9
|
+
const PLAYGROUND_KEY_IDENTITY_ID = "api-key";
|
|
10
|
+
const storage = () => {
|
|
11
|
+
try {
|
|
12
|
+
return typeof localStorage === "undefined" ? void 0 : localStorage;
|
|
13
|
+
} catch {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Remembers a key the moment it is created: the plans backend shows a key's secret only then, so this is the one
|
|
19
|
+
* chance to offer it to the playground later. Only the last key is kept, in this browser only.
|
|
20
|
+
*/
|
|
21
|
+
function rememberPlaygroundKey(entry) {
|
|
22
|
+
if (!entry.key) return;
|
|
23
|
+
const value = {
|
|
24
|
+
id: entry.id,
|
|
25
|
+
key: entry.key,
|
|
26
|
+
header: entry.header || DEMO_GATEWAY_KEY_HEADER,
|
|
27
|
+
keyPrefix: entry.keyPrefix
|
|
28
|
+
};
|
|
29
|
+
try {
|
|
30
|
+
storage()?.setItem(STORAGE_KEY, JSON.stringify(value));
|
|
31
|
+
} catch {}
|
|
32
|
+
}
|
|
33
|
+
function readPlaygroundKey() {
|
|
34
|
+
try {
|
|
35
|
+
const raw = storage()?.getItem(STORAGE_KEY);
|
|
36
|
+
if (!raw) return void 0;
|
|
37
|
+
const parsed = JSON.parse(raw);
|
|
38
|
+
return parsed.id && parsed.key && parsed.header && parsed.keyPrefix ? parsed : void 0;
|
|
39
|
+
} catch {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** A revoked key stops working, so it is no longer offered. */
|
|
44
|
+
function forgetPlaygroundKey(keyId) {
|
|
45
|
+
if (readPlaygroundKey()?.id !== keyId) return;
|
|
46
|
+
try {
|
|
47
|
+
storage()?.removeItem(STORAGE_KEY);
|
|
48
|
+
} catch {}
|
|
49
|
+
}
|
|
50
|
+
/** The "Try it" identity for the remembered key, sent in the gateway's key header. */
|
|
51
|
+
function playgroundKeyIdentities() {
|
|
52
|
+
const remembered = readPlaygroundKey();
|
|
53
|
+
if (!remembered) return [];
|
|
54
|
+
return [{
|
|
55
|
+
id: PLAYGROUND_KEY_IDENTITY_ID,
|
|
56
|
+
label: `Your API key (${remembered.keyPrefix}…)`,
|
|
57
|
+
authorizationFields: { headers: [remembered.header] },
|
|
58
|
+
authorizeRequest: (request) => {
|
|
59
|
+
request.headers.set(remembered.header, remembered.key);
|
|
60
|
+
return request;
|
|
61
|
+
}
|
|
62
|
+
}];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/hooks/useApiKeys.ts
|
|
67
|
+
/** Thrown when the developer has hit their plan's per-developer key cap. */
|
|
68
|
+
const API_KEY_CAP_REACHED = "API_KEY_CAP_REACHED";
|
|
69
|
+
function apiKeysQueryKey(apiBaseUrl) {
|
|
70
|
+
return ["dev-portal-api-keys", apiBaseUrl];
|
|
71
|
+
}
|
|
72
|
+
function useApiKeys() {
|
|
73
|
+
const auth = useAuth();
|
|
74
|
+
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
75
|
+
const isPreview = useDevPortalPreviewMode();
|
|
76
|
+
const isDemoSession = useIsDemoSession();
|
|
77
|
+
const { previewPlans } = useDevPortalBillingConfig();
|
|
78
|
+
const liveQuery = useQuery({
|
|
79
|
+
queryKey: apiKeysQueryKey(apiBaseUrl),
|
|
80
|
+
enabled: auth.isAuthenticated && !isPreview && !isDemoSession && Boolean(apiBaseUrl),
|
|
81
|
+
queryFn: async () => {
|
|
82
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/api-keys`, {
|
|
83
|
+
method: "GET",
|
|
84
|
+
headers: { Accept: "application/json" },
|
|
85
|
+
credentials: "include"
|
|
86
|
+
});
|
|
87
|
+
if (response.status === 404) throw new Error("NO_ENTITLEMENT");
|
|
88
|
+
if (!response.ok) throw new Error("Failed to load API keys.");
|
|
89
|
+
return await response.json();
|
|
90
|
+
},
|
|
91
|
+
retry: (failureCount, error) => error instanceof Error && error.message === "NO_ENTITLEMENT" ? false : failureCount < 2
|
|
92
|
+
});
|
|
93
|
+
if (isDemoSession && auth.isAuthenticated) return {
|
|
94
|
+
...liveQuery,
|
|
95
|
+
data: createDemoApiKeys(previewPlans),
|
|
96
|
+
error: null,
|
|
97
|
+
isError: false,
|
|
98
|
+
isFetching: false,
|
|
99
|
+
isLoading: false,
|
|
100
|
+
isPending: false,
|
|
101
|
+
isSuccess: true,
|
|
102
|
+
status: "success"
|
|
103
|
+
};
|
|
104
|
+
return liveQuery;
|
|
105
|
+
}
|
|
106
|
+
function useCreateApiKey() {
|
|
107
|
+
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
108
|
+
const queryClient = useQueryClient();
|
|
109
|
+
const { keyHeader } = useDevPortalBillingConfig();
|
|
110
|
+
return useMutation({
|
|
111
|
+
mutationFn: async ({ name, planSku }) => {
|
|
112
|
+
if (!apiBaseUrl) throw new Error("Dev portal API is not configured.");
|
|
113
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/api-keys`, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: {
|
|
116
|
+
Accept: "application/json",
|
|
117
|
+
"Content-Type": "application/json"
|
|
118
|
+
},
|
|
119
|
+
credentials: "include",
|
|
120
|
+
body: JSON.stringify({
|
|
121
|
+
name,
|
|
122
|
+
planSku
|
|
123
|
+
})
|
|
124
|
+
});
|
|
125
|
+
if (response.status === 409) throw new Error(API_KEY_CAP_REACHED);
|
|
126
|
+
if (!response.ok) throw new Error(await readProblemDetail(response) ?? "Failed to create API key.");
|
|
127
|
+
return await response.json();
|
|
128
|
+
},
|
|
129
|
+
onSuccess: async (created) => {
|
|
130
|
+
rememberPlaygroundKey({
|
|
131
|
+
...created,
|
|
132
|
+
header: keyHeader?.trim() || queryClient.getQueryData(apiKeysQueryKey(apiBaseUrl))?.header
|
|
133
|
+
});
|
|
134
|
+
await queryClient.invalidateQueries({ queryKey: apiKeysQueryKey(apiBaseUrl) });
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
function useRevokeApiKey() {
|
|
139
|
+
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
140
|
+
const queryClient = useQueryClient();
|
|
141
|
+
return useMutation({
|
|
142
|
+
mutationFn: async (keyId) => {
|
|
143
|
+
if (!apiBaseUrl) throw new Error("Dev portal API is not configured.");
|
|
144
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/api-keys/${keyId}`, {
|
|
145
|
+
method: "DELETE",
|
|
146
|
+
headers: { Accept: "application/json" },
|
|
147
|
+
credentials: "include"
|
|
148
|
+
});
|
|
149
|
+
if (!response.ok && response.status !== 204) throw new Error(await readProblemDetail(response) ?? "Failed to revoke API key.");
|
|
150
|
+
},
|
|
151
|
+
onSuccess: async (_result, keyId) => {
|
|
152
|
+
forgetPlaygroundKey(keyId);
|
|
153
|
+
await queryClient.invalidateQueries({ queryKey: apiKeysQueryKey(apiBaseUrl) });
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
async function readProblemDetail(response) {
|
|
158
|
+
try {
|
|
159
|
+
return (await response.json()).detail?.trim() || null;
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
//#endregion
|
|
166
|
+
export { playgroundKeyIdentities as a, useRevokeApiKey as i, useApiKeys as n, useCreateApiKey as r, API_KEY_CAP_REACHED as t };
|
|
@@ -250,4 +250,4 @@ function useDevPortalPreviewMode() {
|
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
//#endregion
|
|
253
|
-
export {
|
|
253
|
+
export { rateLimitCell as _, formatPlanPrice as a, isPlaceholderDevPortalApiUrl as c, listPlanFeatureLines as d, parseManifestPlans as f, quotaCell as g, pickFeaturedPlanSku as h, formatPlanDisplayPrice as i, isPlanCustomPriced as l, parseQuotaPeriodDays as m, useDevPortalPreviewMode as n, formatPlanPriceUnit as o, parseMonthlyQuotaCalls as p, useIsDemoSession as r, isActiveSubscription as s, useDevPortalApiBaseUrl as t, isPlanFree as u, resolveDevPortalApiBaseUrl as v };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-
|
|
1
|
+
import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
2
2
|
import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
3
|
-
import { i as createDemoMeResponse } from "./demo-session-
|
|
3
|
+
import { i as createDemoMeResponse } from "./demo-session-rN2Gj4lM.mjs";
|
|
4
4
|
import { useQuery } from "@lukoweb/apitogo/react-query";
|
|
5
5
|
import { useAuth } from "@lukoweb/apitogo/hooks";
|
|
6
6
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as isPlaceholderDevPortalApiUrl, f as parseManifestPlans, n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
2
2
|
import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
3
3
|
import { useQuery } from "@lukoweb/apitogo/react-query";
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-
|
|
1
|
+
import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BstWzITX.mjs";
|
|
2
2
|
import { useQuery } from "@lukoweb/apitogo/react-query";
|
|
3
3
|
import { useAuth } from "@lukoweb/apitogo/hooks";
|
|
4
4
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lukoweb/apitogo-plugin-dev-portal-billing",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.52",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zuplo/zudoku",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"react": "19.2.4",
|
|
47
47
|
"react-dom": "19.2.4",
|
|
48
48
|
"tsdown": "0.20.3",
|
|
49
|
-
"@lukoweb/apitogo": "0.2.
|
|
50
|
-
"@lukoweb/apitogo-module-user-panel": "0.2.
|
|
49
|
+
"@lukoweb/apitogo": "0.2.52",
|
|
50
|
+
"@lukoweb/apitogo-module-user-panel": "0.2.52"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"react": ">=19.2.0",
|
|
File without changes
|