zuplo 6.70.70 → 6.70.71
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/docs/ai-gateway/getting-started.mdx +2 -1
- package/docs/ai-gateway/integrations/ai-sdk.mdx +17 -0
- package/docs/ai-gateway/introduction.mdx +5 -5
- package/docs/ai-gateway/providers.mdx +2 -0
- package/docs/analytics/access-and-entitlements.md +71 -0
- package/docs/analytics/overview.md +63 -0
- package/docs/analytics/reference/metrics-glossary.md +105 -0
- package/docs/analytics/reference/url-parameters.md +66 -0
- package/docs/analytics/shared-controls.md +121 -0
- package/docs/analytics/tabs/agents.md +88 -0
- package/docs/analytics/tabs/consumers.md +73 -0
- package/docs/analytics/tabs/graphql.md +77 -0
- package/docs/analytics/tabs/mcp.md +80 -0
- package/docs/analytics/tabs/origins.md +82 -0
- package/docs/analytics/tabs/requests.md +96 -0
- package/docs/articles/ci-cd-github/basic-deployment.mdx +10 -1
- package/docs/articles/ci-cd-github/deploy-and-test.mdx +14 -1
- package/docs/articles/ci-cd-github/local-testing.mdx +3 -1
- package/docs/articles/ci-cd-github/pr-preview-environments.mdx +36 -4
- package/docs/articles/custom-ci-cd-github.mdx +11 -2
- package/docs/articles/monetization/api-access.mdx +184 -0
- package/docs/articles/monetization/meters.mdx +4 -4
- package/docs/articles/monetization/monetization-policy.md +4 -1
- package/docs/articles/monetization/private-plans.md +3 -4
- package/docs/articles/monetization/stripe-integration.md +9 -0
- package/docs/articles/monetization/subscription-lifecycle.md +12 -11
- package/docs/articles/monorepo-deployment.mdx +20 -2
- package/docs/cli/deploy.mdx +32 -0
- package/docs/cli/deploy.partial.mdx +32 -0
- package/docs/concepts/api-keys.md +2 -2
- package/docs/dev-portal/zudoku/components/callout.mdx +11 -18
- package/docs/dev-portal/zudoku/configuration/search.md +36 -0
- package/docs/dev-portal/zudoku/configuration/site.md +38 -0
- package/docs/dev-portal/zudoku/customization/colors-theme.mdx +51 -40
- package/docs/errors/rate-limit-exceeded.mdx +30 -3
- package/docs/policies/_index.md +2 -0
- package/docs/policies/data-loss-prevention-inbound/doc.md +116 -0
- package/docs/policies/data-loss-prevention-inbound/intro.md +15 -0
- package/docs/policies/data-loss-prevention-inbound/schema.json +220 -0
- package/docs/policies/data-loss-prevention-outbound/doc.md +116 -0
- package/docs/policies/data-loss-prevention-outbound/intro.md +18 -0
- package/docs/policies/data-loss-prevention-outbound/schema.json +220 -0
- package/docs/programmable-api/background-dispatcher.mdx +6 -8
- package/docs/programmable-api/zone-cache.mdx +1 -1
- package/docs/rate-limiting/combining-policies.mdx +293 -0
- package/docs/rate-limiting/dynamic-rate-limiting.mdx +240 -0
- package/docs/rate-limiting/getting-started.mdx +339 -0
- package/docs/rate-limiting/how-it-works.md +225 -0
- package/docs/rate-limiting/monitoring-and-troubleshooting.mdx +243 -0
- package/docs/{articles → rate-limiting}/per-user-rate-limits-using-db.mdx +39 -27
- package/package.json +4 -4
- package/docs/concepts/rate-limiting.md +0 -246
|
@@ -52,6 +52,190 @@ curl \
|
|
|
52
52
|
--header "Authorization: Bearer $ZAPI_KEY"
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
## Bucket monetization configuration
|
|
56
|
+
|
|
57
|
+
Each bucket has an optional `MonetizationConfiguration` that holds bucket-wide
|
|
58
|
+
behavior — multi-subscription support, plan display order, plan-level overrides,
|
|
59
|
+
and the default payment grace period. The configuration is read by the runtime
|
|
60
|
+
and the Developer Portal; it is not stored in OpenMeter.
|
|
61
|
+
|
|
62
|
+
### Read
|
|
63
|
+
|
|
64
|
+
```shell
|
|
65
|
+
curl \
|
|
66
|
+
https://dev.zuplo.com/v3/metering/$BUCKET_ID/monetization-configuration \
|
|
67
|
+
--header "Authorization: Bearer $ZAPI_KEY"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
When no configuration row exists for the bucket, the endpoint returns a default
|
|
71
|
+
body with `multipleSubscriptionsEnabled: false`, an empty `planOrder`, empty
|
|
72
|
+
`planSettings`, and `maxPaymentOverdueDays: 3`.
|
|
73
|
+
|
|
74
|
+
### Upsert
|
|
75
|
+
|
|
76
|
+
```shell
|
|
77
|
+
curl \
|
|
78
|
+
https://dev.zuplo.com/v3/metering/$BUCKET_ID/monetization-configuration \
|
|
79
|
+
--request PUT \
|
|
80
|
+
--header "Authorization: Bearer $ZAPI_KEY" \
|
|
81
|
+
--header "Content-Type: application/json" \
|
|
82
|
+
--data @- << EOF
|
|
83
|
+
{
|
|
84
|
+
"multipleSubscriptionsEnabled": false,
|
|
85
|
+
"planOrder": ["free", "starter", "pro", "enterprise"],
|
|
86
|
+
"planSettings": {
|
|
87
|
+
"pro": { "visiblePhases": ["default"] }
|
|
88
|
+
},
|
|
89
|
+
"maxPaymentOverdueDays": 7
|
|
90
|
+
}
|
|
91
|
+
EOF
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
| Field | Type | Description |
|
|
95
|
+
| ------------------------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
96
|
+
| `multipleSubscriptionsEnabled` | `boolean` | Stored on the bucket. Reserved for future multi-subscription rules; today the Developer Portal create-subscription path enforces a single active subscription per customer regardless of this flag. |
|
|
97
|
+
| `planOrder` | `string[]` | Ordered list of plan keys; drives pricing-page sort and upgrade/downgrade direction during plan changes |
|
|
98
|
+
| `planSettings` | `object` | Per-plan overrides keyed by plan key. The supported sub-key today is `visiblePhases` — an array of phase keys that should appear on the pricing page |
|
|
99
|
+
| `maxPaymentOverdueDays` | `integer` | Bucket-default payment grace period. Must be ≥ 0. Defaults to `3` when not set |
|
|
100
|
+
|
|
101
|
+
The request body must include at least one of these fields. All four fields are
|
|
102
|
+
optional in the request — the upsert preserves any field you don't send.
|
|
103
|
+
|
|
104
|
+
`planOrder` is consumed when a customer changes plans through the Developer
|
|
105
|
+
Portal: a target plan whose index is greater than or equal to the current plan's
|
|
106
|
+
index is treated as an upgrade (immediate timing); a lower index is treated as a
|
|
107
|
+
downgrade (next-billing-cycle timing). Plans not listed in `planOrder` default
|
|
108
|
+
to upgrade timing.
|
|
109
|
+
|
|
110
|
+
`maxPaymentOverdueDays` is the lowest-precedence default for the payment grace
|
|
111
|
+
period. See
|
|
112
|
+
[Subscription and payment validation](./monetization-policy.md#subscription-and-payment-validation)
|
|
113
|
+
for the full precedence chain (customer metadata → plan metadata → bucket
|
|
114
|
+
configuration → built-in default).
|
|
115
|
+
|
|
116
|
+
### Delete
|
|
117
|
+
|
|
118
|
+
```shell
|
|
119
|
+
curl \
|
|
120
|
+
https://dev.zuplo.com/v3/metering/$BUCKET_ID/monetization-configuration \
|
|
121
|
+
--request DELETE \
|
|
122
|
+
--header "Authorization: Bearer $ZAPI_KEY"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
After deletion, GET returns the default body again.
|
|
126
|
+
|
|
127
|
+
## Stripe setup and billing readiness
|
|
128
|
+
|
|
129
|
+
Most users connect Stripe through the
|
|
130
|
+
[Zuplo Portal](./stripe-integration.md#connecting-your-stripe-account). For
|
|
131
|
+
automated provisioning — CI scripts, infrastructure-as-code, or self-hosted
|
|
132
|
+
control planes — the same flow is available via these API endpoints.
|
|
133
|
+
|
|
134
|
+
### Install the Stripe app
|
|
135
|
+
|
|
136
|
+
Connect a Stripe account to a bucket and create the default billing profile in
|
|
137
|
+
one call:
|
|
138
|
+
|
|
139
|
+
```shell
|
|
140
|
+
curl \
|
|
141
|
+
https://dev.zuplo.com/v3/metering/$BUCKET_ID/setup/stripe \
|
|
142
|
+
--request POST \
|
|
143
|
+
--header "Authorization: Bearer $ZAPI_KEY" \
|
|
144
|
+
--header "Content-Type: application/json" \
|
|
145
|
+
--data @- << EOF
|
|
146
|
+
{
|
|
147
|
+
"apiKey": "rk_test_...",
|
|
148
|
+
"name": "Stripe Billing Profile",
|
|
149
|
+
"taxEnabled": false,
|
|
150
|
+
"taxEnforced": false,
|
|
151
|
+
"country": "US"
|
|
152
|
+
}
|
|
153
|
+
EOF
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The endpoint validates the Stripe key prefix against the bucket's environment:
|
|
157
|
+
|
|
158
|
+
- Working-copy and preview buckets accept `sk_test_*` or `rk_test_*`
|
|
159
|
+
- Production buckets accept `sk_live_*` or `rk_live_*`
|
|
160
|
+
|
|
161
|
+
The response returns the installed `appId`. The endpoint fails with a
|
|
162
|
+
`409 Conflict` if a Stripe app is already installed for the bucket.
|
|
163
|
+
|
|
164
|
+
### Read the current Stripe setup
|
|
165
|
+
|
|
166
|
+
```shell
|
|
167
|
+
curl \
|
|
168
|
+
https://dev.zuplo.com/v3/metering/$BUCKET_ID/setup/stripe \
|
|
169
|
+
--header "Authorization: Bearer $ZAPI_KEY"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Returns the connected Stripe app summary and the billing profiles linked to it.
|
|
173
|
+
|
|
174
|
+
### Create an additional billing profile
|
|
175
|
+
|
|
176
|
+
To attach more billing profiles to the same Stripe app:
|
|
177
|
+
|
|
178
|
+
```shell
|
|
179
|
+
curl \
|
|
180
|
+
https://dev.zuplo.com/v3/metering/$BUCKET_ID/setup/stripe/$STRIPE_APP_ID/billing-profile \
|
|
181
|
+
--request POST \
|
|
182
|
+
--header "Authorization: Bearer $ZAPI_KEY" \
|
|
183
|
+
--header "Content-Type: application/json" \
|
|
184
|
+
--data @- << EOF
|
|
185
|
+
{
|
|
186
|
+
"name": "EU Billing Profile",
|
|
187
|
+
"taxEnabled": true,
|
|
188
|
+
"taxEnforced": false,
|
|
189
|
+
"country": "DE"
|
|
190
|
+
}
|
|
191
|
+
EOF
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Check billing readiness
|
|
195
|
+
|
|
196
|
+
A lightweight check for tooling that gates deploys on Stripe being connected:
|
|
197
|
+
|
|
198
|
+
```shell
|
|
199
|
+
curl \
|
|
200
|
+
https://dev.zuplo.com/v3/metering/$BUCKET_ID/billing-readiness \
|
|
201
|
+
--header "Authorization: Bearer $ZAPI_KEY"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Response:
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"hasStripeApp": true,
|
|
209
|
+
"stripeAppId": "app_01H...",
|
|
210
|
+
"hasDefaultBillingProfile": true,
|
|
211
|
+
"defaultBillingProfileId": "bp_01H..."
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Use this in setup wizards to gate the UI on whether Stripe is connected.
|
|
216
|
+
|
|
217
|
+
### Update a connected app
|
|
218
|
+
|
|
219
|
+
Rotate the Stripe key on an existing app, or update its name and metadata:
|
|
220
|
+
|
|
221
|
+
```shell
|
|
222
|
+
curl \
|
|
223
|
+
https://dev.zuplo.com/v3/metering/$BUCKET_ID/apps/$APP_ID \
|
|
224
|
+
--request PUT \
|
|
225
|
+
--header "Authorization: Bearer $ZAPI_KEY" \
|
|
226
|
+
--header "Content-Type: application/json" \
|
|
227
|
+
--data @- << EOF
|
|
228
|
+
{
|
|
229
|
+
"type": "stripe",
|
|
230
|
+
"name": "Stripe Billing Profile",
|
|
231
|
+
"secretAPIKey": "rk_test_..."
|
|
232
|
+
}
|
|
233
|
+
EOF
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The same key-prefix validation applies — a live key is rejected on a
|
|
237
|
+
non-production bucket and vice versa.
|
|
238
|
+
|
|
55
239
|
## API Reference
|
|
56
240
|
|
|
57
241
|
For complete API operations, see the API Reference documentation:
|
|
@@ -58,10 +58,10 @@ carries the quantity:
|
|
|
58
58
|
`subject` identifies _who_ consumed the subscription's entitlements on this
|
|
59
59
|
request — typically the API key's consumer name, the end-user id, or another
|
|
60
60
|
stable per-actor identifier. It is **not** the subscription id (use the
|
|
61
|
-
`subscription` field for that) and
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
`subscription` field for that) and does not route billing. Its purpose is to let
|
|
62
|
+
you break down usage within a subscription so you can see which key, user, or
|
|
63
|
+
agent drove the consumption — a single subscription will commonly emit events
|
|
64
|
+
with many different `subject` values. See
|
|
65
65
|
[Monetization Policy](./monetization-policy.md) for how usage is recorded.
|
|
66
66
|
|
|
67
67
|
:::
|
|
@@ -145,7 +145,10 @@ below it:
|
|
|
145
145
|
|
|
146
146
|
1. **Customer metadata** — `zuplo_max_payment_overdue_days` on the customer
|
|
147
147
|
2. **Plan metadata** — `zuplo_max_payment_overdue_days` on the plan
|
|
148
|
-
3. **
|
|
148
|
+
3. **Bucket configuration** —
|
|
149
|
+
[`maxPaymentOverdueDays`](./api-access.mdx#bucket-monetization-configuration)
|
|
150
|
+
on the bucket's monetization configuration
|
|
151
|
+
4. **Default** — `3` days
|
|
149
152
|
|
|
150
153
|
Set the value to `0` to block requests immediately when payment is overdue.
|
|
151
154
|
|
|
@@ -99,10 +99,9 @@ Save the returned `id` — you need it to publish and invite users.
|
|
|
99
99
|
|
|
100
100
|
:::note
|
|
101
101
|
|
|
102
|
-
The plan `id` is a 26-character ULID
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
require the `id`, not the `key`.
|
|
102
|
+
The plan `id` is a 26-character ULID. It's distinct from the human-friendly
|
|
103
|
+
`key` field. Use the `id` (not the `key`) when calling `/publish` and
|
|
104
|
+
`/plan-invites`.
|
|
106
105
|
|
|
107
106
|
:::
|
|
108
107
|
|
|
@@ -45,6 +45,15 @@ specifically Customers, Checkout Sessions, Customer Portal Sessions, Invoices,
|
|
|
45
45
|
and Tax Calculations. See
|
|
46
46
|
[What Zuplo creates in Stripe](#what-zuplo-creates-in-stripe) for the full list.
|
|
47
47
|
|
|
48
|
+
:::tip
|
|
49
|
+
|
|
50
|
+
To script the connection — for CI, infrastructure-as-code, or self-hosted
|
|
51
|
+
control planes — use the
|
|
52
|
+
[Stripe setup API endpoints](./api-access.mdx#stripe-setup-and-billing-readiness)
|
|
53
|
+
instead of the Portal flow.
|
|
54
|
+
|
|
55
|
+
:::
|
|
56
|
+
|
|
48
57
|
### Test mode vs. live mode
|
|
49
58
|
|
|
50
59
|
Connect with a Stripe **test** key (`sk_test_...`) first to validate your
|
|
@@ -67,8 +67,9 @@ curl -X POST https://dev.zuplo.com/v3/metering/{bucketId}/subscriptions \
|
|
|
67
67
|
-H "Authorization: Bearer {API_KEY}" \
|
|
68
68
|
-H "Content-Type: application/json" \
|
|
69
69
|
-d '{
|
|
70
|
-
"plan": { "key": "pro" },
|
|
71
|
-
"
|
|
70
|
+
"plan": { "key": "pro", "version": 1 },
|
|
71
|
+
"customerKey": "user_external_id",
|
|
72
|
+
"timing": "immediate"
|
|
72
73
|
}'
|
|
73
74
|
```
|
|
74
75
|
|
|
@@ -216,14 +217,15 @@ curl -X POST https://dev.zuplo.com/v3/metering/{bucketId}/subscriptions/{subscri
|
|
|
216
217
|
-H "Content-Type: application/json" \
|
|
217
218
|
-d '{
|
|
218
219
|
"timing": "immediate",
|
|
219
|
-
"plan": { "key": "enterprise" }
|
|
220
|
+
"plan": { "key": "enterprise", "version": 1 }
|
|
220
221
|
}'
|
|
221
222
|
```
|
|
222
223
|
|
|
223
224
|
`timing` accepts `"immediate"`, `"next_billing_cycle"`, or an RFC 3339
|
|
224
225
|
timestamp. Optional fields include `startingPhase`, `name`, `description`,
|
|
225
|
-
`metadata`, `alignment`, and `billingAnchor`.
|
|
226
|
-
|
|
226
|
+
`metadata`, `alignment`, and `billingAnchor`. The response includes both the
|
|
227
|
+
closed-out (`current`) and newly-started (`next`) subscriptions. To preview the
|
|
228
|
+
proration credit before committing, call
|
|
227
229
|
`POST /v3/metering/{bucketId}/subscriptions/{subscriptionId}/change/estimate-credit`
|
|
228
230
|
with the same body.
|
|
229
231
|
|
|
@@ -310,9 +312,7 @@ behavior does not apply.
|
|
|
310
312
|
curl -X POST https://dev.zuplo.com/v3/metering/{bucketId}/subscriptions/{subscriptionId}/cancel \
|
|
311
313
|
-H "Authorization: Bearer {API_KEY}" \
|
|
312
314
|
-H "Content-Type: application/json" \
|
|
313
|
-
-d '{
|
|
314
|
-
"timing": "next_billing_cycle"
|
|
315
|
-
}'
|
|
315
|
+
-d '{ "timing": "next_billing_cycle" }'
|
|
316
316
|
```
|
|
317
317
|
|
|
318
318
|
`timing` controls when the cancellation takes effect:
|
|
@@ -341,9 +341,10 @@ curl -X POST https://dev.zuplo.com/v3/metering/{bucketId}/subscriptions/{subscri
|
|
|
341
341
|
-H "Authorization: Bearer {API_KEY}"
|
|
342
342
|
```
|
|
343
343
|
|
|
344
|
-
This removes the pending cancellation. The subscription continues as normal.
|
|
345
|
-
|
|
346
|
-
|
|
344
|
+
This removes the pending cancellation. The subscription continues as normal.
|
|
345
|
+
|
|
346
|
+
If the subscription has already ended, create a new subscription rather than
|
|
347
|
+
restoring the old one.
|
|
347
348
|
|
|
348
349
|
## Subscriptions per customer
|
|
349
350
|
|
|
@@ -114,7 +114,9 @@ jobs:
|
|
|
114
114
|
run: npm install
|
|
115
115
|
|
|
116
116
|
- name: Deploy to Zuplo
|
|
117
|
-
run:
|
|
117
|
+
run:
|
|
118
|
+
npx zuplo deploy --api-key "$ZUPLO_API_KEY" --environment "${{
|
|
119
|
+
github.ref_name }}"
|
|
118
120
|
env:
|
|
119
121
|
ZUPLO_API_KEY: ${{ secrets.ZUPLO_API_KEY }}
|
|
120
122
|
```
|
|
@@ -171,8 +173,12 @@ jobs:
|
|
|
171
173
|
- name: Deploy to Zuplo
|
|
172
174
|
id: deploy
|
|
173
175
|
shell: bash
|
|
176
|
+
env:
|
|
177
|
+
# The PR's source branch — not the pull/<number>/merge ref that
|
|
178
|
+
# pull_request events check out
|
|
179
|
+
ENVIRONMENT: ${{ github.head_ref }}
|
|
174
180
|
run: |
|
|
175
|
-
OUTPUT=$(npx zuplo deploy --api-key "$ZUPLO_API_KEY" 2>&1)
|
|
181
|
+
OUTPUT=$(npx zuplo deploy --api-key "$ZUPLO_API_KEY" --environment "$ENVIRONMENT" 2>&1)
|
|
176
182
|
echo "$OUTPUT"
|
|
177
183
|
DEPLOYMENT_URL=$(echo "$OUTPUT" | grep -oP 'Deployed to \K(https://[^ ]+)')
|
|
178
184
|
echo "url=$DEPLOYMENT_URL" >> $GITHUB_OUTPUT
|
|
@@ -230,6 +236,18 @@ jobs:
|
|
|
230
236
|
--wait
|
|
231
237
|
```
|
|
232
238
|
|
|
239
|
+
:::caution
|
|
240
|
+
|
|
241
|
+
The deploy step passes `--environment` with the PR's source branch name
|
|
242
|
+
(`github.head_ref`). Workflows triggered by `pull_request` check out the PR
|
|
243
|
+
merge ref (`refs/pull/<number>/merge`), so without `--environment` the CLI names
|
|
244
|
+
the environment after that ref instead of your branch — and any other deploy of
|
|
245
|
+
the same branch creates a second environment with a different URL. See
|
|
246
|
+
[PR Preview Environments](./ci-cd-github/pr-preview-environments.mdx) for
|
|
247
|
+
details.
|
|
248
|
+
|
|
249
|
+
:::
|
|
250
|
+
|
|
233
251
|
### Secrets and environment variables
|
|
234
252
|
|
|
235
253
|
Store your Zuplo API key as a GitHub Actions secret:
|
package/docs/cli/deploy.mdx
CHANGED
|
@@ -122,6 +122,38 @@ zuplo deploy --project my-project
|
|
|
122
122
|
zuplo deploy --project my-project --environment my-env-name
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
+
### Deploying from CI/CD
|
|
126
|
+
|
|
127
|
+
Without `--environment`, the CLI names the environment after the current git
|
|
128
|
+
branch. CI systems usually check out a detached HEAD, in which case the CLI
|
|
129
|
+
resolves the branch from the remote branches that contain the checked-out
|
|
130
|
+
commit. Two cases break this inference:
|
|
131
|
+
|
|
132
|
+
- On GitHub Actions `pull_request` events, the checkout is the pull request
|
|
133
|
+
merge ref (`refs/pull/<number>/merge`) — a commit that doesn't exist on any
|
|
134
|
+
branch — so the environment is named after that ref instead of your branch.
|
|
135
|
+
- When the checked-out commit exists on more than one branch, the first match
|
|
136
|
+
wins, which may not be the branch that triggered the build.
|
|
137
|
+
|
|
138
|
+
Always pass `--environment` explicitly in CI so that every trigger deploys the
|
|
139
|
+
same, predictably named environment:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# GitHub Actions: github.head_ref is the source branch on pull_request
|
|
143
|
+
# events; github.ref_name is the branch on push events
|
|
144
|
+
zuplo deploy --project my-project --environment "$BRANCH_NAME"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Deploying the same environment name always updates the same environment and
|
|
148
|
+
keeps its URL stable across deploys. This matters whenever an external system
|
|
149
|
+
must match the URL exactly — an OIDC token audience, a webhook registration, or
|
|
150
|
+
an allowlist. Capture the URL from the deploy output (`Deployed to
|
|
151
|
+
https://...`) rather than constructing it from the branch name: the URL
|
|
152
|
+
hostname uses a normalized, truncated form of the environment name plus a
|
|
153
|
+
unique identifier. See
|
|
154
|
+
[Branch-Based Deployments](../articles/branch-based-deployments.mdx) for the
|
|
155
|
+
naming rules.
|
|
156
|
+
|
|
125
157
|
## Polling timeout
|
|
126
158
|
|
|
127
159
|
By default, the deploy command polls the status of the deployment every second
|
|
@@ -25,6 +25,38 @@ zuplo deploy --project my-project
|
|
|
25
25
|
zuplo deploy --project my-project --environment my-env-name
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
### Deploying from CI/CD
|
|
29
|
+
|
|
30
|
+
Without `--environment`, the CLI names the environment after the current git
|
|
31
|
+
branch. CI systems usually check out a detached HEAD, in which case the CLI
|
|
32
|
+
resolves the branch from the remote branches that contain the checked-out
|
|
33
|
+
commit. Two cases break this inference:
|
|
34
|
+
|
|
35
|
+
- On GitHub Actions `pull_request` events, the checkout is the pull request
|
|
36
|
+
merge ref (`refs/pull/<number>/merge`) — a commit that doesn't exist on any
|
|
37
|
+
branch — so the environment is named after that ref instead of your branch.
|
|
38
|
+
- When the checked-out commit exists on more than one branch, the first match
|
|
39
|
+
wins, which may not be the branch that triggered the build.
|
|
40
|
+
|
|
41
|
+
Always pass `--environment` explicitly in CI so that every trigger deploys the
|
|
42
|
+
same, predictably named environment:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# GitHub Actions: github.head_ref is the source branch on pull_request
|
|
46
|
+
# events; github.ref_name is the branch on push events
|
|
47
|
+
zuplo deploy --project my-project --environment "$BRANCH_NAME"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Deploying the same environment name always updates the same environment and
|
|
51
|
+
keeps its URL stable across deploys. This matters whenever an external system
|
|
52
|
+
must match the URL exactly — an OIDC token audience, a webhook registration, or
|
|
53
|
+
an allowlist. Capture the URL from the deploy output (`Deployed to
|
|
54
|
+
https://...`) rather than constructing it from the branch name: the URL
|
|
55
|
+
hostname uses a normalized, truncated form of the environment name plus a
|
|
56
|
+
unique identifier. See
|
|
57
|
+
[Branch-Based Deployments](../articles/branch-based-deployments.mdx) for the
|
|
58
|
+
naming rules.
|
|
59
|
+
|
|
28
60
|
## Polling timeout
|
|
29
61
|
|
|
30
62
|
By default, the deploy command polls the status of the deployment every second
|
|
@@ -50,8 +50,8 @@ After successful validation, the policy populates `request.user`:
|
|
|
50
50
|
- `request.user.data` contains the consumer's metadata (plan, customerId, etc.)
|
|
51
51
|
|
|
52
52
|
This lets downstream policies and handlers make authorization decisions, apply
|
|
53
|
-
per-consumer [rate limits](
|
|
54
|
-
backend.
|
|
53
|
+
per-consumer [rate limits](../rate-limiting/how-it-works.md), or forward
|
|
54
|
+
identity to your backend.
|
|
55
55
|
|
|
56
56
|
:::note
|
|
57
57
|
|
|
@@ -193,24 +193,17 @@ Or pass any React node as the `icon` to override the default for the chosen `typ
|
|
|
193
193
|
## Customize colors
|
|
194
194
|
|
|
195
195
|
Each callout type is driven by a single CSS variable that determines the icon, border tint, and
|
|
196
|
-
background tint via `color-mix`. Override any of them in your
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
.dark {
|
|
208
|
-
--callout-tip: oklch(0.78 0.18 160);
|
|
209
|
-
--callout-sparkles: oklch(0.75 0.2 320);
|
|
210
|
-
}
|
|
211
|
-
`,
|
|
212
|
-
},
|
|
213
|
-
};
|
|
196
|
+
background tint via `color-mix`. Override any of them in your stylesheet to recolor a type globally:
|
|
197
|
+
|
|
198
|
+
```css title=styles.css
|
|
199
|
+
:root {
|
|
200
|
+
--callout-tip: oklch(0.65 0.18 160);
|
|
201
|
+
--callout-sparkles: oklch(0.6 0.22 320);
|
|
202
|
+
}
|
|
203
|
+
.dark {
|
|
204
|
+
--callout-tip: oklch(0.78 0.18 160);
|
|
205
|
+
--callout-sparkles: oklch(0.75 0.2 320);
|
|
206
|
+
}
|
|
214
207
|
```
|
|
215
208
|
|
|
216
209
|
All available variables:
|
|
@@ -167,3 +167,39 @@ your [Dev Portal Configuration](./overview.md):
|
|
|
167
167
|
// ...
|
|
168
168
|
}
|
|
169
169
|
```
|
|
170
|
+
|
|
171
|
+
### Customizing Inkeep
|
|
172
|
+
|
|
173
|
+
Any other [base settings](https://docs.inkeep.com/cloud/ui-components/common-settings/base) can be
|
|
174
|
+
set alongside the required fields, for example `filters` to scope results or `transformSource` to
|
|
175
|
+
customize how sources are displayed.
|
|
176
|
+
|
|
177
|
+
You can also pass
|
|
178
|
+
[`searchSettings`](https://docs.inkeep.com/cloud/ui-components/common-settings/search),
|
|
179
|
+
[`aiChatSettings`](https://docs.inkeep.com/cloud/ui-components/common-settings/ai-chat), and
|
|
180
|
+
`modalSettings` to customize the respective parts of the search experience. They are merged with
|
|
181
|
+
Zudoku's defaults and passed through to Inkeep as-is, so any option Inkeep supports can be used —
|
|
182
|
+
including ones added after this Dev Portal version was released.
|
|
183
|
+
|
|
184
|
+
For example, to categorize results into tabs based on their URL:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
{
|
|
188
|
+
// ...
|
|
189
|
+
search: {
|
|
190
|
+
type: "inkeep",
|
|
191
|
+
// ...required fields from above
|
|
192
|
+
transformSource: (source) => {
|
|
193
|
+
if (!source.url.includes("/blog/")) return source;
|
|
194
|
+
return { ...source, tabs: [...(source.tabs ?? []), "Blog"] };
|
|
195
|
+
},
|
|
196
|
+
searchSettings: {
|
|
197
|
+
tabs: [["All", { isAlwaysVisible: true }], "Blog"],
|
|
198
|
+
},
|
|
199
|
+
aiChatSettings: {
|
|
200
|
+
aiAssistantName: "My Assistant",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
// ...
|
|
204
|
+
}
|
|
205
|
+
```
|
|
@@ -127,6 +127,44 @@ export const NotFound = () => (
|
|
|
127
127
|
|
|
128
128
|
## Layout
|
|
129
129
|
|
|
130
|
+
### Collapsible Sidebar
|
|
131
|
+
|
|
132
|
+
The navigation sidebar is collapsible by default. A small toggle button on the sidebar's right
|
|
133
|
+
border lets users hide and reveal it. Configure the behavior under `site.sidebar`:
|
|
134
|
+
|
|
135
|
+
```tsx title=zudoku.config.tsx
|
|
136
|
+
{
|
|
137
|
+
site: {
|
|
138
|
+
sidebar: {
|
|
139
|
+
collapsible: true, // default: true. Set to false to disable the toggle entirely.
|
|
140
|
+
toggleVisibility: "always", // "always" (default) or "hover" — show button only when hovering the sidebar's right edge
|
|
141
|
+
togglePosition: "bottom", // "top", "center", or "bottom" (default)
|
|
142
|
+
},
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
For finer vertical placement, override the `--sidebar-toggle-y` CSS variable in your stylesheet:
|
|
148
|
+
|
|
149
|
+
```css
|
|
150
|
+
:root {
|
|
151
|
+
--sidebar-toggle-y: 30%;
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The toggle button carries `aria-expanded="true"` when the sidebar is open and `"false"` when
|
|
156
|
+
collapsed. Combine it with the `[data-sidebar-toggle]` selector to position the button differently
|
|
157
|
+
per state:
|
|
158
|
+
|
|
159
|
+
```css
|
|
160
|
+
[data-sidebar-toggle][aria-expanded="true"] {
|
|
161
|
+
--sidebar-toggle-y: 20%;
|
|
162
|
+
}
|
|
163
|
+
[data-sidebar-toggle][aria-expanded="false"] {
|
|
164
|
+
--sidebar-toggle-y: 80%;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
130
168
|
### Banner
|
|
131
169
|
|
|
132
170
|
Add a banner message to the top of the page:
|