@stacksjs/ts-cloud 0.11.6 → 0.12.0
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/aws/index.js +1 -1
- package/dist/bin/{chunk-znnawt0k.js → chunk-71t1gmsq.js} +1 -1
- package/dist/bin/{chunk-4xhj37nj.js → chunk-8ewncfce.js} +1 -1
- package/dist/bin/{chunk-8khgprf6.js → chunk-9kf0np8j.js} +1 -1
- package/dist/bin/chunk-cxt74wsj.js +1 -0
- package/dist/bin/{chunk-j447p8t4.js → chunk-g6baf9c5.js} +84 -84
- package/dist/bin/{chunk-zsf6pccz.js → chunk-q5cmfpzt.js} +1 -1
- package/dist/bin/{chunk-r6b3pq31.js → chunk-rtecnmts.js} +1 -1
- package/dist/bin/{chunk-5nsqz5n3.js → chunk-rv18092e.js} +1 -1
- package/dist/bin/{chunk-yw6f160x.js → chunk-ta7sh0cc.js} +1 -1
- package/dist/bin/{chunk-cbsys5v8.js → chunk-tfbs8k8v.js} +1 -1
- package/dist/bin/{chunk-g0fm1xcs.js → chunk-xkfrqqcs.js} +1 -1
- package/dist/bin/cli.js +122 -116
- package/dist/bin/dashboard-server.js +1 -1
- package/dist/cdn/cloudflare-rules.d.ts +37 -0
- package/dist/chunk-13qjvbqh.js +2 -0
- package/dist/{chunk-nk7sav4r.js → chunk-152he7m3.js} +1 -1
- package/dist/{chunk-dr4pj8hp.js → chunk-3mgj3hrh.js} +1 -1
- package/dist/chunk-6kpbveb8.js +5 -0
- package/dist/{chunk-aw0cgv5w.js → chunk-86ss83s2.js} +1 -1
- package/dist/chunk-a84gmpyh.js +1 -0
- package/dist/{chunk-ywjkyg8p.js → chunk-b087qsyf.js} +99 -99
- package/dist/{chunk-zam6ecam.js → chunk-h42zck7h.js} +2 -2
- package/dist/{chunk-zz52xje8.js → chunk-jm5mg890.js} +1 -1
- package/dist/{chunk-kasdm9tx.js → chunk-m8fs0233.js} +1 -1
- package/dist/chunk-nms5q44e.js +1 -0
- package/dist/{chunk-aw0spg20.js → chunk-nzqxdkch.js} +2 -2
- package/dist/{chunk-rd38qdnf.js → chunk-qm32ytbr.js} +1 -1
- package/dist/{chunk-q0rc1ax9.js → chunk-t0xgmhd6.js} +1 -1
- package/dist/{chunk-q9q6cmt0.js → chunk-t3t9ymec.js} +1 -1
- package/dist/{chunk-5vt93kxx.js → chunk-y2v22hkv.js} +1 -1
- package/dist/deploy/index.js +1 -1
- package/dist/dns/index.js +1 -1
- package/dist/drivers/index.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +45 -45
- package/dist/operations/drained-sites.d.ts +56 -0
- package/dist/operations/drained-sites.test.d.ts +1 -0
- package/dist/pages/cloudflare-pages.d.ts +227 -0
- package/dist/pages/cloudflare-pages.test.d.ts +1 -0
- package/dist/pages/index.d.ts +2 -0
- package/dist/protection/index.js +1 -1
- package/dist/push/index.js +1 -1
- package/dist/spend/index.js +1 -1
- package/dist/ui/404.html +1 -1
- package/dist/ui/accept-invitation.html +1 -1
- package/dist/ui/access-denied.html +1 -1
- package/dist/ui/account/automation.html +5 -5
- package/dist/ui/account/security.html +1 -1
- package/dist/ui/applications/compose.html +5 -5
- package/dist/ui/applications/new.html +2 -2
- package/dist/ui/data/backups.html +6 -6
- package/dist/ui/data/services.html +6 -6
- package/dist/ui/data/volumes.html +6 -6
- package/dist/ui/index.html +6 -6
- package/dist/ui/infrastructure/topology.html +5 -5
- package/dist/ui/integrations.html +2 -2
- package/dist/ui/operations/alerts.html +6 -6
- package/dist/ui/operations/configuration.html +6 -6
- package/dist/ui/operations/jobs.html +6 -6
- package/dist/ui/operations/maintenance.html +5 -5
- package/dist/ui/operations/observability.html +6 -6
- package/dist/ui/operations/previews.html +6 -6
- package/dist/ui/operations/queue.html +6 -6
- package/dist/ui/operations/regions.html +5 -5
- package/dist/ui/operations/releases.html +6 -6
- package/dist/ui/operations/spend.html +6 -6
- package/dist/ui/operations/workloads.html +6 -6
- package/dist/ui/security.html +2 -2
- package/dist/ui/server/actions.html +6 -6
- package/dist/ui/server/activity.html +2 -2
- package/dist/ui/server/capacity.html +6 -6
- package/dist/ui/server/database.html +6 -6
- package/dist/ui/server/deployments.html +6 -6
- package/dist/ui/server/diagnostics.html +1 -1
- package/dist/ui/server/firewall.html +6 -6
- package/dist/ui/server/fleet.html +6 -6
- package/dist/ui/server/logs.html +6 -6
- package/dist/ui/server/metrics.html +6 -6
- package/dist/ui/server/security.html +2 -2
- package/dist/ui/server/services.html +2 -2
- package/dist/ui/server/sites.html +6 -6
- package/dist/ui/server/ssh-keys.html +6 -6
- package/dist/ui/server/team.html +6 -6
- package/dist/ui/server/terminal.html +2 -2
- package/dist/ui/serverless/alarms.html +6 -6
- package/dist/ui/serverless/assets.html +2 -2
- package/dist/ui/serverless/cost.html +2 -2
- package/dist/ui/serverless/data.html +6 -6
- package/dist/ui/serverless/deployments.html +2 -2
- package/dist/ui/serverless/firewall.html +1 -1
- package/dist/ui/serverless/functions.html +5 -5
- package/dist/ui/serverless/logs.html +6 -6
- package/dist/ui/serverless/metrics.html +2 -2
- package/dist/ui/serverless/queues.html +6 -6
- package/dist/ui/serverless/secrets.html +6 -6
- package/dist/ui/serverless/traces.html +6 -6
- package/dist/ui/serverless.html +6 -6
- package/package.json +4 -3
- package/dist/bin/chunk-wkx799z2.js +0 -1
- package/dist/chunk-95e6aeqz.js +0 -1
- package/dist/chunk-p10xt3jb.js +0 -1
- package/dist/chunk-z2qqwy7e.js +0 -2
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect sites whose files are still on a box that no longer serves them.
|
|
3
|
+
*
|
|
4
|
+
* `site:move` deliberately never deletes anything on the source: it stops the
|
|
5
|
+
* units, drops the gateway route, and leaves the whole tree in place. That
|
|
6
|
+
* leftover tree IS the rollback — the one thing that makes a bad cutover
|
|
7
|
+
* recoverable, and the reason the operation can promise reversibility "until the
|
|
8
|
+
* source server is destroyed".
|
|
9
|
+
*
|
|
10
|
+
* Which makes destroying that server the moment the promise expires, and nothing
|
|
11
|
+
* about the box says so. It has no running units for the site, so it looks idle;
|
|
12
|
+
* the config has moved on; the operator is tidying up. Terminating it is exactly
|
|
13
|
+
* the right thing to do once the move is verified, and exactly the wrong thing
|
|
14
|
+
* to do before — and those two look identical from outside.
|
|
15
|
+
*
|
|
16
|
+
* So: before a teardown, ask the box whether it is holding anyone's rollback.
|
|
17
|
+
*
|
|
18
|
+
* @see https://github.com/stacksjs/ts-cloud/issues/167
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Report every one of this project's site trees on the box and whether anything
|
|
22
|
+
* is still running for it.
|
|
23
|
+
*
|
|
24
|
+
* Read-only by construction — it lists and queries, and that is all. A scan that
|
|
25
|
+
* could change the box would be a poor thing to run immediately before deciding
|
|
26
|
+
* whether to keep it.
|
|
27
|
+
*
|
|
28
|
+
* A directory is only counted when it holds `releases/`, so a stray folder under
|
|
29
|
+
* `/var/www` is not mistaken for a deployed site and does not block a teardown
|
|
30
|
+
* the operator genuinely wants.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildDrainedSiteScanScript(slug: string, wwwRoot?: string): string[];
|
|
33
|
+
export interface DrainedSite {
|
|
34
|
+
name: string;
|
|
35
|
+
/** Human-readable size of the tree left behind, e.g. `1.2G`. */
|
|
36
|
+
size: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Sites the box still holds but no longer runs.
|
|
40
|
+
*
|
|
41
|
+
* A site with something active is NOT drained — it is simply a site on a box
|
|
42
|
+
* being torn down, which is ordinary and already covered by the teardown's own
|
|
43
|
+
* confirmation. The interesting case is files with nothing running: either a
|
|
44
|
+
* completed `site:move` whose rollback this is, or a site that has been stopped
|
|
45
|
+
* and forgotten. Both are worth a sentence before the disk goes away.
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseDrainedSites(output: string | undefined): DrainedSite[];
|
|
48
|
+
/**
|
|
49
|
+
* What the operator sees instead of a teardown.
|
|
50
|
+
*
|
|
51
|
+
* Names the sites, says what the files are FOR, and gives the two ways forward.
|
|
52
|
+
* The flag is deliberately its own thing rather than `--force`: `--force` exists
|
|
53
|
+
* so a teardown can run unattended, and a CI job that skips a prompt must not
|
|
54
|
+
* also silently discard the only copy of a rollback.
|
|
55
|
+
*/
|
|
56
|
+
export declare function formatDrainedSiteRefusal(sites: readonly DrainedSite[], slug: string, flag: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploy a built static site to Cloudflare Pages.
|
|
3
|
+
*
|
|
4
|
+
* Pages is the one Cloudflare product that is not reachable through the zone
|
|
5
|
+
* API the rest of this codebase talks to: projects are account-scoped, and
|
|
6
|
+
* uploads go through a separate content-addressed flow rather than a single
|
|
7
|
+
* PUT. The shape of that flow drives this module:
|
|
8
|
+
*
|
|
9
|
+
* - **Files are addressed by hash, not by path.** A deployment is a manifest
|
|
10
|
+
* mapping `/path` to a hash; the bytes are uploaded separately, and only for
|
|
11
|
+
* hashes the account has never seen. A redeploy of a site whose CSS did not
|
|
12
|
+
* change re-uploads nothing for that file, which is why `deployDirectory`
|
|
13
|
+
* checks `/pages/assets/check-missing` before sending anything.
|
|
14
|
+
* - **The hash is Cloudflare's own, not a plain digest.** It is a blake3 of
|
|
15
|
+
* the base64 payload concatenated with the file extension, truncated to 32
|
|
16
|
+
* hex characters. Getting this wrong does not error — it uploads fine and
|
|
17
|
+
* then serves 404s, because the manifest points at a hash the asset store
|
|
18
|
+
* does not hold under that name.
|
|
19
|
+
* - **Upload auth is a separate, short-lived JWT**, fetched from
|
|
20
|
+
* `/pages/projects/{name}/upload-token`. The account API token cannot be
|
|
21
|
+
* used against the asset endpoints.
|
|
22
|
+
*
|
|
23
|
+
* A custom domain is attached with {@link attachCustomDomain}, which is
|
|
24
|
+
* idempotent, and still needs a CNAME in DNS — see {@link pagesDnsRecord}.
|
|
25
|
+
*/
|
|
26
|
+
import type { DnsRecord } from '../dns/types';
|
|
27
|
+
/** Cloudflare's asset-upload ceiling. Larger files are rejected outright. */
|
|
28
|
+
export declare const PAGES_MAX_FILE_BYTES: number;
|
|
29
|
+
export interface CloudflarePagesProject {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
subdomain: string;
|
|
33
|
+
domains: string[];
|
|
34
|
+
production_branch?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface CloudflarePagesDeployment {
|
|
37
|
+
id: string;
|
|
38
|
+
url: string;
|
|
39
|
+
environment: string;
|
|
40
|
+
project_name: string;
|
|
41
|
+
}
|
|
42
|
+
export interface CloudflarePagesOptions {
|
|
43
|
+
/** Account-scoped API token with the `Cloudflare Pages: Edit` permission. */
|
|
44
|
+
apiToken: string;
|
|
45
|
+
/** Account the project lives under. */
|
|
46
|
+
accountId: string;
|
|
47
|
+
}
|
|
48
|
+
/** One file in a deployment, already hashed. */
|
|
49
|
+
export interface PagesAsset {
|
|
50
|
+
/** Site-absolute path, e.g. `/guide/index.html`. */
|
|
51
|
+
path: string;
|
|
52
|
+
/** Cloudflare asset hash (32 hex chars). */
|
|
53
|
+
hash: string;
|
|
54
|
+
/** Base64 of the file's bytes. */
|
|
55
|
+
base64: string;
|
|
56
|
+
/** Extension without the dot, e.g. `html`. Empty when there is none. */
|
|
57
|
+
extension: string;
|
|
58
|
+
/** Byte length of the original file, for reporting and limit checks. */
|
|
59
|
+
size: number;
|
|
60
|
+
}
|
|
61
|
+
export interface DeployDirectoryResult {
|
|
62
|
+
deployment: CloudflarePagesDeployment;
|
|
63
|
+
/** Every asset in the manifest. */
|
|
64
|
+
total: number;
|
|
65
|
+
/** Assets whose bytes had to be sent, the rest already being in the store. */
|
|
66
|
+
uploaded: number;
|
|
67
|
+
/** Bytes actually transferred. */
|
|
68
|
+
uploadedBytes: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Cloudflare Pages, scoped to one account.
|
|
72
|
+
*
|
|
73
|
+
* Mirrors {@link CloudflareProvider}: a thin authenticated `request`, and
|
|
74
|
+
* methods that each map onto one API concern.
|
|
75
|
+
*/
|
|
76
|
+
export declare class CloudflarePagesProvider {
|
|
77
|
+
readonly name = "cloudflare-pages";
|
|
78
|
+
private readonly apiToken;
|
|
79
|
+
private readonly accountId;
|
|
80
|
+
constructor(options: CloudflarePagesOptions);
|
|
81
|
+
/**
|
|
82
|
+
* Make an authenticated request against the account-scoped Pages API.
|
|
83
|
+
*
|
|
84
|
+
* @param method - HTTP method
|
|
85
|
+
* @param endpoint - Path below `/accounts/{id}`, starting with a slash
|
|
86
|
+
* @param body - JSON body, or a `FormData` for multipart endpoints
|
|
87
|
+
* @returns The unwrapped `result`
|
|
88
|
+
* @throws {Error} When Cloudflare reports failure
|
|
89
|
+
*/
|
|
90
|
+
private request;
|
|
91
|
+
/**
|
|
92
|
+
* Look up a project by name.
|
|
93
|
+
*
|
|
94
|
+
* @param name - Project name
|
|
95
|
+
* @returns The project, or `null` when it does not exist
|
|
96
|
+
*/
|
|
97
|
+
getProject(name: string): Promise<CloudflarePagesProject | null>;
|
|
98
|
+
/**
|
|
99
|
+
* Create a direct-upload project, or return the existing one.
|
|
100
|
+
*
|
|
101
|
+
* Direct upload means ts-cloud pushes the built output itself rather than
|
|
102
|
+
* Cloudflare cloning and building the repository. That keeps the build in
|
|
103
|
+
* your CI, where the toolchain and secrets already are.
|
|
104
|
+
*
|
|
105
|
+
* @param name - Project name; also the `<name>.pages.dev` subdomain
|
|
106
|
+
* @param productionBranch - Branch treated as production. @default 'main'
|
|
107
|
+
* @returns The project, whether created here or already present
|
|
108
|
+
*/
|
|
109
|
+
ensureProject(name: string, productionBranch?: string): Promise<CloudflarePagesProject>;
|
|
110
|
+
/**
|
|
111
|
+
* Short-lived JWT for the asset endpoints.
|
|
112
|
+
*
|
|
113
|
+
* The account API token is not accepted there, so this is fetched per deploy
|
|
114
|
+
* rather than held.
|
|
115
|
+
*
|
|
116
|
+
* @param project - Project name
|
|
117
|
+
*/
|
|
118
|
+
private uploadToken;
|
|
119
|
+
/**
|
|
120
|
+
* Ask which of these hashes the asset store does not already hold.
|
|
121
|
+
*
|
|
122
|
+
* @param jwt - Upload token
|
|
123
|
+
* @param hashes - Every hash in the manifest
|
|
124
|
+
* @returns The subset whose bytes still need sending
|
|
125
|
+
*/
|
|
126
|
+
private missingHashes;
|
|
127
|
+
/**
|
|
128
|
+
* Upload one batch of assets.
|
|
129
|
+
*
|
|
130
|
+
* @param jwt - Upload token
|
|
131
|
+
* @param assets - At most {@link UPLOAD_BATCH_SIZE} assets
|
|
132
|
+
*/
|
|
133
|
+
private uploadBatch;
|
|
134
|
+
/**
|
|
135
|
+
* Create a deployment from an already-hashed manifest.
|
|
136
|
+
*
|
|
137
|
+
* @param project - Project name
|
|
138
|
+
* @param assets - Every file in the site
|
|
139
|
+
* @param branch - Branch to record. Matching the production branch publishes
|
|
140
|
+
* to the live domain; anything else becomes a preview deployment.
|
|
141
|
+
* @returns The created deployment
|
|
142
|
+
*/
|
|
143
|
+
createDeployment(project: string, assets: PagesAsset[], branch?: string): Promise<CloudflarePagesDeployment>;
|
|
144
|
+
/**
|
|
145
|
+
* Build, upload and deploy a directory.
|
|
146
|
+
*
|
|
147
|
+
* The whole flow: hash every file, ask what is missing, upload only that,
|
|
148
|
+
* then create the deployment.
|
|
149
|
+
*
|
|
150
|
+
* @param options.project - Project name
|
|
151
|
+
* @param options.directory - Directory to publish; becomes the site root
|
|
152
|
+
* @param options.branch - Branch to record on the deployment
|
|
153
|
+
* @param options.onProgress - Called as batches complete, for CLI output
|
|
154
|
+
* @returns The deployment and what it cost to send
|
|
155
|
+
* @throws {Error} When the directory is empty or a file exceeds the size limit
|
|
156
|
+
*/
|
|
157
|
+
deployDirectory(options: {
|
|
158
|
+
project: string;
|
|
159
|
+
directory: string;
|
|
160
|
+
branch?: string;
|
|
161
|
+
onProgress?: (uploaded: number, total: number) => void;
|
|
162
|
+
}): Promise<DeployDirectoryResult>;
|
|
163
|
+
/** Custom domains already attached to a project. */
|
|
164
|
+
listCustomDomains(project: string): Promise<string[]>;
|
|
165
|
+
/**
|
|
166
|
+
* Attach a custom domain, or leave it attached.
|
|
167
|
+
*
|
|
168
|
+
* Idempotent so a deploy pipeline can call it on every run rather than
|
|
169
|
+
* guarding with a lookup that races.
|
|
170
|
+
*
|
|
171
|
+
* @param project - Project name
|
|
172
|
+
* @param domain - Hostname to serve, e.g. `buddy.sh`
|
|
173
|
+
* @returns Whether this call is what attached it
|
|
174
|
+
*/
|
|
175
|
+
attachCustomDomain(project: string, domain: string): Promise<boolean>;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The DNS record that points a custom domain at a Pages project.
|
|
179
|
+
*
|
|
180
|
+
* Pages serves from a `*.pages.dev` host, so the zone needs a CNAME. Two things
|
|
181
|
+
* are easy to get wrong here:
|
|
182
|
+
*
|
|
183
|
+
* - **The target is the project's `subdomain`, not `<name>.pages.dev`.**
|
|
184
|
+
* Cloudflare only grants the bare name when it is free; a project named
|
|
185
|
+
* `buddy` whose name was taken is served from `buddy-5yo.pages.dev`.
|
|
186
|
+
* Deriving the host from the project name instead of reading `subdomain`
|
|
187
|
+
* produces a CNAME to a host that does not exist.
|
|
188
|
+
* - **It must be proxied.** An unproxied CNAME reaches Cloudflare's edge but
|
|
189
|
+
* arrives without the routing metadata Pages uses to pick a project, and
|
|
190
|
+
* answers 404.
|
|
191
|
+
*
|
|
192
|
+
* A CNAME at the apex is legal here only because Cloudflare flattens it.
|
|
193
|
+
*
|
|
194
|
+
* @param hostname - Public hostname, e.g. `buddy.sh` or `www.buddy.sh`
|
|
195
|
+
* @param subdomain - The project's `subdomain`, e.g. `buddy-5yo.pages.dev`
|
|
196
|
+
* @returns A record ready for `CloudflareProvider.upsertRecord`
|
|
197
|
+
*/
|
|
198
|
+
export declare function pagesDnsRecord(hostname: string, subdomain: string): DnsRecord;
|
|
199
|
+
/**
|
|
200
|
+
* Cloudflare's asset hash: BLAKE3 of `<base64><extension>`, first 32 hex chars.
|
|
201
|
+
*
|
|
202
|
+
* The extension is part of the input, so the same bytes saved as `.html` and
|
|
203
|
+
* as `.txt` hash differently — which is deliberate, since the asset store
|
|
204
|
+
* serves content type from the key.
|
|
205
|
+
*
|
|
206
|
+
* BLAKE3 specifically, and it has to come from a library: Bun's `CryptoHasher`
|
|
207
|
+
* ships blake2b but not blake3. Substituting a different digest is not a
|
|
208
|
+
* degradation that shows up as an error — the upload succeeds and the site
|
|
209
|
+
* then serves 404s, because the manifest names a key the store does not hold.
|
|
210
|
+
*
|
|
211
|
+
* @param base64 - Base64 of the file's bytes
|
|
212
|
+
* @param extension - Extension without the dot
|
|
213
|
+
*/
|
|
214
|
+
export declare function pagesAssetHash(base64: string, extension: string): Promise<string>;
|
|
215
|
+
/**
|
|
216
|
+
* Walk a directory into a deployable manifest.
|
|
217
|
+
*
|
|
218
|
+
* Dotfiles are included: a built site legitimately contains `.well-known`, and
|
|
219
|
+
* bunpress writes its output under a `.bunpress` root. Cloudflare's own
|
|
220
|
+
* reserved paths are skipped, since uploading them as assets shadows the
|
|
221
|
+
* configuration they are meant to be.
|
|
222
|
+
*
|
|
223
|
+
* @param directory - Site root
|
|
224
|
+
* @returns One entry per file, hashed and base64-encoded
|
|
225
|
+
* @throws {Error} When a file exceeds {@link PAGES_MAX_FILE_BYTES}
|
|
226
|
+
*/
|
|
227
|
+
export declare function collectAssets(directory: string): Promise<PagesAsset[]>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { CloudflarePagesProvider, collectAssets, PAGES_MAX_FILE_BYTES, pagesAssetHash, pagesDnsRecord, } from './cloudflare-pages';
|
|
2
|
+
export type { CloudflarePagesDeployment, CloudflarePagesOptions, CloudflarePagesProject, DeployDirectoryResult, PagesAsset, } from './cloudflare-pages';
|
package/dist/protection/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{$k as z,_k as y,al as A,bl as B,cl as C,dl as D,el as E,fl as F,gl as G,hl as H,il as I,jl as J,kl as K,ll as L,ml as M}from"../chunk-1qdne0bf.js";import{nl as s,ol as t,pl as u,ql as v,rl as w,sl as x}from"../chunk-hn40z11p.js";import{Al as r,tl as k,ul as l,vl as m,wl as n,xl as o,yl as p,zl as q}from"../chunk-x3hay61c.js";import{Ns as a,Os as b,Ps as c,Qs as d,Rs as e,Ss as f,Ts as g,Us as h,Vs as i,Ws as j}from"../chunk-
|
|
1
|
+
import{$k as z,_k as y,al as A,bl as B,cl as C,dl as D,el as E,fl as F,gl as G,hl as H,il as I,jl as J,kl as K,ll as L,ml as M}from"../chunk-1qdne0bf.js";import{nl as s,ol as t,pl as u,ql as v,rl as w,sl as x}from"../chunk-hn40z11p.js";import{Al as r,tl as k,ul as l,vl as m,wl as n,xl as o,yl as p,zl as q}from"../chunk-x3hay61c.js";import{Ns as a,Os as b,Ps as c,Qs as d,Rs as e,Ss as f,Ts as g,Us as h,Vs as i,Ws as j}from"../chunk-jm5mg890.js";import"../chunk-13qjvbqh.js";export{b as CHAIN_HEADER,H as DEFAULT_ATTACK_MODE_HOURS,k as DEFAULT_DDOS_THRESHOLDS,e as DEFAULT_RECURSION_LIMITS,a as DEPTH_HEADER,G as MAX_CONTROL_HOURS,F as PROTECTION_SETTING,I as ProtectionControlStore,y as RATE_LIMIT_KEY_SEPARATOR,C as RateLimiter,h as RecursionGuard,c as TRACE_HEADER,L as applyControlsToDdos,K as applyControlsToRateLimits,r as applyMitigationFactor,E as defaultRateLimitRules,J as describePosture,s as escapeSecLang,d as functionFingerprint,z as globMatches,u as globToRegex,g as inspectInvocation,t as isSafePattern,l as isValidCidr,M as mergeControlsIntoConfig,f as parseChain,q as planMitigation,i as propagationHeaders,D as rateLimitHeaders,B as rateLimitKey,j as recursionBlockedResponse,p as renderDdosInstallScript,o as renderNftablesRuleset,n as renderSysctlFile,v as renderWafConfig,w as renderWafInstallScript,A as ruleMatches,m as sysctlHardening,x as wafEventTelemetry};
|
package/dist/push/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"../chunk-
|
|
1
|
+
import"../chunk-13qjvbqh.js";import*as h from"node:http2";import{createSign as b}from"node:crypto";var f="api.push.apple.com",k="api.sandbox.push.apple.com",S=2700000;class R{config;token=null;tokenGeneratedAt=0;client=null;host;constructor(e){this.config=e,this.host=e.production?f:k}generateToken(){let e=Math.floor(Date.now()/1000);if(this.token&&Date.now()-this.tokenGeneratedAt<S)return this.token;let t={alg:"ES256",kid:this.config.keyId},s={iss:this.config.teamId,iat:e},r=Buffer.from(JSON.stringify(t)).toString("base64url"),n=Buffer.from(JSON.stringify(s)).toString("base64url"),i=`${r}.${n}`,o=b("SHA256");o.update(i);let a=o.sign(this.config.privateKey),l=this.derToRaw(a).toString("base64url");return this.token=`${i}.${l}`,this.tokenGeneratedAt=Date.now(),this.token}derToRaw(e){let t=2;if(e[t]!==2)throw Error("Invalid DER signature: expected 0x02 for R");t++;let s=e[t];t++;let r=e.subarray(t,t+s);if(t+=s,e[t]!==2)throw Error("Invalid DER signature: expected 0x02 for S");t++;let n=e[t];t++;let i=e.subarray(t,t+n);if(r[0]===0&&r.length===33)r=r.subarray(1);if(i[0]===0&&i.length===33)i=i.subarray(1);let o=Buffer.alloc(64);return r.copy(o,32-r.length),i.copy(o,64-i.length),o}async getClient(){if(this.client&&!this.client.destroyed)return this.client;return new Promise((e,t)=>{this.client=h.connect(`https://${this.host}`),this.client.on("error",(s)=>{t(s)}),this.client.on("connect",()=>{e(this.client)}),this.client.on("close",()=>{this.client=null})})}buildPayload(e){let t={};if(e.title||e.body||e.subtitle){if(t.alert={},e.title)t.alert.title=e.title;if(e.subtitle)t.alert.subtitle=e.subtitle;if(e.body)t.alert.body=e.body}if(e.badge!==void 0)t.badge=e.badge;if(e.sound!==void 0)t.sound=e.sound;if(e.category)t.category=e.category;if(e.threadId)t["thread-id"]=e.threadId;if(e.contentAvailable)t["content-available"]=1;if(e.mutableContent)t["mutable-content"]=1;if(e.targetContentId)t["target-content-id"]=e.targetContentId;let s={aps:t};if(e.data)Object.assign(s,e.data);return s}async send(e){try{let t=await this.getClient(),s=this.generateToken(),r=JSON.stringify(this.buildPayload(e)),n={":method":"POST",":path":`/3/device/${e.deviceToken}`,authorization:`bearer ${s}`,"apns-topic":this.config.bundleId,"apns-push-type":e.pushType||"alert","apns-priority":String(e.priority||10)};if(e.expiration!==void 0)n["apns-expiration"]=String(e.expiration);if(e.collapseId)n["apns-collapse-id"]=e.collapseId;return new Promise((i)=>{let o=t.request(n),a="",d=0,l;o.on("response",(c)=>{d=c[":status"],l=c["apns-id"]}),o.on("data",(c)=>{a+=c.toString()}),o.on("end",()=>{if(d===200)i({success:!0,deviceToken:e.deviceToken,apnsId:l,statusCode:d});else{let c="Unknown error",g,u;if(a)try{let p=JSON.parse(a);g=p.reason,u=p.timestamp,c=g||c}catch{c=a}i({success:!1,deviceToken:e.deviceToken,apnsId:l,statusCode:d,error:c,reason:g,timestamp:u})}}),o.on("error",(c)=>{i({success:!1,deviceToken:e.deviceToken,error:c.message})}),o.write(r),o.end()})}catch(t){return{success:!1,deviceToken:e.deviceToken,error:t.message}}}async sendBatch(e,t){let s=t?.concurrency||10,r=[];for(let n=0;n<e.length;n+=s){let i=e.slice(n,n+s),o=await Promise.all(i.map((a)=>this.send(a)));r.push(...o)}return{sent:r.filter((n)=>n.success).length,failed:r.filter((n)=>!n.success).length,results:r}}async sendSimple(e,t,s,r){return this.send({deviceToken:e,title:t,body:s,data:r})}async sendSilent(e,t){return this.send({deviceToken:e,contentAvailable:!0,pushType:"background",priority:5,data:t})}close(){if(this.client)this.client.close(),this.client=null;this.token=null,this.tokenGeneratedAt=0}}import{createSign as T}from"node:crypto";var C="https://fcm.googleapis.com/v1/projects",y="https://oauth2.googleapis.com/token",P=3300000;class m{config;accessToken=null;tokenExpiresAt=0;constructor(e){this.config=e}static fromServiceAccount(e){return new m({projectId:e.project_id,clientEmail:e.client_email,privateKey:e.private_key})}generateJWT(){let e=Math.floor(Date.now()/1000),t=e+3600,s={alg:"RS256",typ:"JWT"},r={iss:this.config.clientEmail,sub:this.config.clientEmail,aud:y,iat:e,exp:t,scope:"https://www.googleapis.com/auth/firebase.messaging"},n=Buffer.from(JSON.stringify(s)).toString("base64url"),i=Buffer.from(JSON.stringify(r)).toString("base64url"),o=`${n}.${i}`,a=T("SHA256");a.update(o);let d=a.sign(this.config.privateKey,"base64url");return`${o}.${d}`}async getAccessToken(){if(this.accessToken&&Date.now()<this.tokenExpiresAt)return this.accessToken;let e=this.generateJWT(),t=await fetch(y,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:jwt-bearer",assertion:e}).toString()});if(!t.ok){let r=await t.text();throw Error(`Failed to get access token: ${r}`)}let s=await t.json();return this.accessToken=s.access_token,this.tokenExpiresAt=Date.now()+P,this.accessToken}buildMessage(e){let t={};if(e.token)t.token=e.token;else if(e.topic)t.topic=e.topic;else if(e.condition)t.condition=e.condition;if(e.title||e.body||e.imageUrl){if(t.notification={},e.title)t.notification.title=e.title;if(e.body)t.notification.body=e.body;if(e.imageUrl)t.notification.image=e.imageUrl}if(e.data&&Object.keys(e.data).length>0)t.data=e.data;if(e.android){if(t.android={priority:e.android.priority||"high"},e.android.ttl)t.android.ttl=`${e.android.ttl}s`;if(e.android.collapseKey)t.android.collapse_key=e.android.collapseKey;if(e.android.directBootOk)t.android.direct_boot_ok=e.android.directBootOk;let s={};if(e.android.channelId)s.channel_id=e.android.channelId;if(e.android.icon)s.icon=e.android.icon;if(e.android.color)s.color=e.android.color;if(e.android.sound)s.sound=e.android.sound;if(e.android.clickAction)s.click_action=e.android.clickAction;if(e.android.tag)s.tag=e.android.tag;if(e.android.visibility)s.visibility=e.android.visibility;if(e.android.notificationCount!==void 0)s.notification_count=e.android.notificationCount;if(Object.keys(s).length>0)t.android.notification=s}if(e.webpush)t.webpush=e.webpush;if(e.apns)t.apns=e.apns;if(e.fcmOptions)t.fcm_options=e.fcmOptions;return{message:t}}async send(e){try{let t=await this.getAccessToken(),s=this.buildMessage(e),r=await fetch(`${C}/${this.config.projectId}/messages:send`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify(s)}),n=await r.json();if(r.ok)return{success:!0,messageId:n.name};else return{success:!1,error:n.error?.message||"Unknown error",errorCode:n.error?.status}}catch(t){return{success:!1,error:t.message}}}async sendBatch(e,t,s){let r=s?.concurrency||10,n=[];for(let i=0;i<e.length;i+=r){let a=e.slice(i,i+r).map(async(l)=>({...await this.send({...t,token:l}),token:l})),d=await Promise.all(a);n.push(...d)}return{sent:n.filter((i)=>i.success).length,failed:n.filter((i)=>!i.success).length,results:n}}async sendToTopic(e,t){return this.send({...t,topic:e})}async sendToCondition(e,t){return this.send({...t,condition:e})}async sendSimple(e,t,s,r){return this.send({token:e,title:t,body:s,data:r})}async sendSilent(e,t){return this.send({token:e,data:t,android:{priority:"high"}})}async subscribeToTopic(e,t){try{let s=await this.getAccessToken(),r=await fetch("https://iid.googleapis.com/iid/v1:batchAdd",{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({to:`/topics/${t}`,registration_tokens:e})});if(r.ok)return{success:!0};else return{success:!1,error:(await r.json()).error?.message||"Failed to subscribe"}}catch(s){return{success:!1,error:s.message}}}async unsubscribeFromTopic(e,t){try{let s=await this.getAccessToken(),r=await fetch("https://iid.googleapis.com/iid/v1:batchRemove",{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({to:`/topics/${t}`,registration_tokens:e})});if(r.ok)return{success:!0};else return{success:!1,error:(await r.json()).error?.message||"Failed to unsubscribe"}}catch(s){return{success:!1,error:s.message}}}}export{R as APNsClient,m as FCMClient};
|
package/dist/spend/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{$j as _,Aj as z,Bj as A,Cj as B,Dj as C,Ej as D,Fj as E,Gj as F,Hj as G,Ij as H,Jj as I,Jk as qa,Kj as J,Kk as ra,Lj as K,Lk as sa,Mj as L,Mk as ta,Nj as M,Nk as ua,Oj as N,Ok as va,Pj as O,Pk as wa,Qj as P,Qk as xa,Rj as Q,Rk as ya,Sj as R,Sk as za,Tj as S,Tk as Aa,Uj as T,Uk as Ba,Vj as U,Wj as V,Xj as W,Yj as X,Yk as Ca,Zj as Y,Zk as Da,_j as Z,ak as aa,bj as a,bk as ba,cj as b,ck as ca,dj as c,dk as da,ej as d,ek as ea,fj as e,fk as fa,gj as f,gk as ga,hj as g,hk as ha,ij as h,ik as ia,jj as i,jk as ja,kj as j,kk as ka,lj as k,lk as la,mj as l,mk as ma,nj as m,nk as na,oj as n,ok as oa,pj as o,pk as pa,qj as p,rj as q,sj as r,tj as s,uj as t,vj as u,wj as v,xj as w,yj as x,zj as y}from"../chunk-
|
|
1
|
+
import{$j as _,Aj as z,Bj as A,Cj as B,Dj as C,Ej as D,Fj as E,Gj as F,Hj as G,Ij as H,Jj as I,Jk as qa,Kj as J,Kk as ra,Lj as K,Lk as sa,Mj as L,Mk as ta,Nj as M,Nk as ua,Oj as N,Ok as va,Pj as O,Pk as wa,Qj as P,Qk as xa,Rj as Q,Rk as ya,Sj as R,Sk as za,Tj as S,Tk as Aa,Uj as T,Uk as Ba,Vj as U,Wj as V,Xj as W,Yj as X,Yk as Ca,Zj as Y,Zk as Da,_j as Z,ak as aa,bj as a,bk as ba,cj as b,ck as ca,dj as c,dk as da,ej as d,ek as ea,fj as e,fk as fa,gj as f,gk as ga,hj as g,hk as ha,ij as h,ik as ia,jj as i,jk as ja,kj as j,kk as ka,lj as k,lk as la,mj as l,mk as ma,nj as m,nk as na,oj as n,ok as oa,pj as o,pk as pa,qj as p,rj as q,sj as r,tj as s,uj as t,vj as u,wj as v,xj as w,yj as x,zj as y}from"../chunk-86ss83s2.js";import"../chunk-x3hay61c.js";import"../chunk-jm5mg890.js";import"../chunk-13qjvbqh.js";export{Z as AnomalyConfigStore,za as AwsSpendTransport,Aa as ComputeSpendTransport,x as CounterTracker,u as DEFAULT_METER_MAPPINGS,l as DEFAULT_PRICE_BOOK,B as DEFAULT_THRESHOLDS,_ as DETECTABLE_SIGNALS,c as ENFORCEMENT_ACTIONS,K as ENFORCEMENT_DESCRIPTIONS,d as ENFORCEMENT_SEVERITY,ea as FOCUS_VERSION,b as METERS,a as METER_KEYS,e as MICRO_CENTS,G as MIN_PROJECTION_CONFIDENCE,pa as RecordingSpendTransport,X as SENSITIVITY_THRESHOLDS,xa as SPEND_CYCLE_SECONDS,ka as SPEND_GATE_SETTING,ua as SPEND_LEASE_SETTING,ba as SignalSource,ma as SpendCapError,la as SpendGate,va as SpendLoopLease,ta as SpendNotificationRouter,wa as SpendRunner,ia as SpendService,C as SpendStore,qa as URGENT_SPEND_EVENTS,L as USER_VISIBLE_ACTIONS,z as aggregateDeltas,W as anomalyOptionsForSignal,na as assertSpendAllows,q as budgetWindow,Ba as compositeSpendTransport,oa as createEnforcementHandlers,U as detectAnomalies,V as detectLatestAnomaly,I as evaluateBudget,T as ewma,v as findMapping,g as findPriceEntry,ra as formatCents,F as formatTimeToExhaustion,H as governingLimitCents,Ca as handleSpendRequest,s as hourBucket,t as hourBucketsBetween,P as isOperationAllowed,m as isValidTimeZone,da as lookbackHoursForSignal,Q as median,R as medianAbsoluteDeviation,J as mergeDecisions,k as mergePriceBooks,y as meterTelemetry,ca as optionsForSignal,E as percentOfLimit,M as planEnforcement,r as previousBudgetWindow,h as priceUsage,i as priceUsageLines,D as projectSpend,S as robustZScore,j as roundCents,Y as routeMatches,N as runEnforcement,A as scopeKey,aa as signalDefinition,sa as spendNotificationText,Da as spendOpenApiPaths,ya as startSpendLoop,ha as streamFocusJsonl,O as strongestActiveAction,f as tieredMicroCents,ga as toFocusJsonl,fa as toFocusRecords,w as usageDeltaKey,p as utcInstantOfLocal,ja as zeroFill,o as zoneOffsetMs,n as zonedParts};
|
package/dist/ui/404.html
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="
|
|
1
|
+
<!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt7l77jbzlapbv"><meta name="title" content="stx Project"><meta name="description" content="A website built with stx templating engine"><meta property="og:title" content="stx Project"><meta property="og:description" content="A website built with stx templating engine"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="stx Project"><meta name="twitter:description" content="A website built with stx templating engine"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ts-cloud - Accept invitation</title><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='%235a8be0'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_stx/style.448ab192.css"><style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.616c25f3.css"></head><body class="auth-page"><main class="auth-card" id="main-content"><a class="brand" href="/"><span class="dot"></span><span>ts-cloud</span></a><div><span class="context-label">Organization invitation</span><h1>Join your team</h1><p>Choose the dashboard username and password you will use after this invitation is accepted.</p></div><form id="accept-invitation-form" class="auth-form"><div class="field-block"><label for="invite-name">Name</label><input id="invite-name" name="name" autocomplete="name" placeholder="Chris Breuer"></div><div class="field-block"><label for="invite-username">Username</label><input id="invite-username" name="username" autocomplete="username" minlength="2" maxlength="32" required></div><div class="field-block"><label for="invite-password">Password</label><input id="invite-password" name="password" type="password" autocomplete="new-password" minlength="12" required><small>At least 12 characters.</small></div><button class="btn" type="submit">Accept invitation</button><div id="invitation-status" class="auth-status" role="status"></div></form><p class="note">If this link expired or was already used, ask an organization owner to resend the invitation.</p></main> <script data-stx-scoped data-stx-run="always">
|
|
2
2
|
;(function() {
|
|
3
3
|
'use strict';
|
|
4
4
|
(() => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="
|
|
1
|
+
<!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt7l77jbzlapbv"><meta name="title" content="Access unavailable · ts-cloud"><meta name="description" content="A website built with stx templating engine"><meta property="og:title" content="Access unavailable · ts-cloud"><meta property="og:description" content="A website built with stx templating engine"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="Access unavailable · ts-cloud"><meta name="twitter:description" content="A website built with stx templating engine"> <script data-stx-runtime src="/_stx/runtime.c696d17a.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Access unavailable · ts-cloud</title><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='%235a8be0'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_stx/style.448ab192.css"><style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.906ee17c.css"></head><body data-stx-auto><a data-stx-scope="stx_scope_nav_50" class="skip-link" href="#main-content">Skip to content</a><button class="nav-toggle-button" type="button" aria-label="Open navigation" aria-controls="primary-navigation" aria-expanded="false"><span></span><span></span><span></span></button><button class="nav-scrim" type="button" aria-label="Close navigation"></button><aside class="app-sidebar" id="primary-navigation" aria-label="Primary navigation"><a class="brand" href="/?env=production"><span class="dot"></span><span>ts-cloud</span></a><div class="sidebar-context"><span class="context-label">Project</span><strong>ts-cloud</strong></div><div class="environment-picker" aria-label="Environment"><span class="context-label">Environment</span><details><summary class="environment-current production"><span>PROD</span><strong>production</strong></summary></details></div><button class="command-trigger" type="button" aria-keyshortcuts="Meta+K Control+K"><span>Search resources</span><kbd>⌘ K</kbd></button><div class="nav-groups"><section class="nav-group"><h2>Overview</h2><a class="" href="/?env=production">Dashboard</a><a href="/server/activity?env=production">Activity</a></section><section class="nav-group"><h2>Applications</h2><a class="" href="/server/sites?env=production">Services</a><a class="" href="/server/deployments?env=production">Deployments</a><a class="" href="/server/logs?env=production">Logs</a><a class="" href="/integrations?env=production">Git integrations</a><a class="" href="/applications/new?env=production">Create application</a><a class="" href="/applications/compose?env=production">Compose applications</a></section><section class="nav-group"><h2>Operations</h2><a class="" href="/operations/queue?env=production">Deployment queue</a><a class="" href="/operations/previews?env=production">Preview environments</a><a class="" href="/operations/releases?env=production">Releases & promotion</a><a class="" href="/operations/regions?env=production">Regions & failover</a><a class="" href="/operations/maintenance?env=production">Maintenance & DR</a><a class="" href="/operations/workloads?env=production">Workloads</a><a class="" href="/operations/observability?env=production">Observability</a><a class="" href="/operations/alerts?env=production">Alerts & health</a><a class="" href="/operations/spend?env=production">Spend & budgets</a><a href="/server/metrics?env=production">Metrics</a><a href="/server/services?env=production">Runtime services</a><a class="" href="/operations/jobs?env=production">Workers & schedules</a><a class="" href="/operations/configuration?env=production">Environment & secrets</a><a class="" href="/data/backups?env=production">Backups & recovery</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/actions?env=production">Run operations</a></section><section class="nav-group"><h2>Infrastructure</h2><a class="" href="/infrastructure/topology?env=production">Infrastructure map</a><a class="" href="/data/services?env=production">Data services</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/firewall?env=production">Network & firewall</a><a href="/server/ssh-keys?env=production">SSH keys</a><a class="" href="/server/fleet?env=production">Fleet inventory</a><a class="" href="/server/capacity?env=production">Capacity & placement</a><a href="/security?env=production">Security posture</a><a href="/server/terminal?env=production">Terminal</a></section><section class="nav-group"><h2>Organization</h2><a class="" href="/server/team?env=production">People & access</a><a class="" href="/account/automation?env=production">API & automation</a></section><section class="nav-group"><h2>Account</h2><a class="" href="/account/security?env=production">Sign-in & security</a></section></div><form method="post" action="/api/logout" class="signout"><button type="submit">Sign out</button></form></aside><dialog class="command-palette" aria-labelledby="command-title"><div class="command-search"><span aria-hidden="true">⌕</span><input type="search" placeholder="Search services, domains, deployments…" aria-label="Search resources" autocomplete="off"><button type="button" class="command-close" aria-label="Close search">Esc</button></div><h2 id="command-title" class="sr-only">Search resources and navigation</h2><div class="command-status" role="status">Start typing to search this project.</div><section class="saved-views" aria-labelledby="saved-views-title"><div class="saved-views-heading"><h3 id="saved-views-title">Saved views</h3><form class="save-view-form"><label class="sr-only" for="save-view-name">Saved view name</label><input id="save-view-name" maxlength="64" placeholder="Name this view" autocomplete="off"><button type="submit">Save current filters</button></form></div><div class="saved-view-list"></div></section><form class="tag-result-form"><label for="tag-result-name">Tag selected resource</label><input id="tag-result-name" maxlength="32" placeholder="customer-facing" pattern="[A-Za-z0-9][A-Za-z0-9 -]{0,31}" autocomplete="off"><input class="tag-color" type="color" value="#5a8be0" aria-label="Tag color"><button type="submit">Add tag</button></form><div class="command-results" role="listbox" aria-label="Search results"></div><footer><span>↑ ↓ navigate</span><span>Enter open</span><span>Esc close</span></footer></dialog><div class="context-bar" aria-label="Current scope"><div class="context-path"><div class="context-breadcrumb"><span>ts-cloud</span><i>/</i><strong>production</strong></div><div class="context-filters" aria-label="Active filters"></div></div><span class="scope-safety production">Production scope</span></div> <script data-stx-scoped data-stx-run="always">
|
|
2
2
|
(function() {
|
|
3
3
|
var { batch, computed, configureFetch, defineEmits, defineExpose, definePageMeta, defineProps, defineSlots, defineStore, derived, effect, goBack, goForward, inject, invalidateRoute, isDerived, isSignal, navigate, nextTick, onBeforeMount, onBeforeUnmount, onDestroy, onMount, onMounted, onUnmounted, peek, provide, reactive, ref, refresh, registerStoresClient, setRouteParams, state, untrack, useAsync, useClickOutside, useColorMode, useCookie, useCounter, useDark, useDebounce, useDebouncedValue, useEventListener, useFetch, useFocus, useHead, useId, useInterval, useLocalStorage, useMediaQuery, useMutation, useOptimistic, usePreferredContrast, usePreferredDark, usePreferredLight, usePreferredReducedMotion, useQuery, useReactiveProp, useRef, useRoute, useRouteParam, useRouteParams, useScrollLock, useSearchParams, useSeoMeta, useSessionStorage, useSlots, useStore, useThrottle, useTimeout, useToggle, useWebSocket, watch, watchEffect, watchMultiple, withDefaults } = window.stx;
|
|
4
4
|
var __destroyHooks = [];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="
|
|
2
|
-
function
|
|
1
|
+
<!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt7l77jbzlapbv"><meta name="title" content="stx Project"><meta name="description" content="A website built with stx templating engine"><meta property="og:title" content="stx Project"><meta property="og:description" content="A website built with stx templating engine"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="stx Project"><meta name="twitter:description" content="A website built with stx templating engine"> <script data-stx-runtime src="/_stx/runtime.c696d17a.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ts-cloud - API & automation</title><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='%235a8be0'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_stx/style.448ab192.css"> <script data-stx-scoped>
|
|
2
|
+
function __stx_setup_1787597169246_0() {
|
|
3
3
|
const { batch, computed, configureFetch, defineEmits, defineExpose, definePageMeta, defineProps, defineSlots, defineStore, derived, effect, goBack, goForward, inject, invalidateRoute, isDerived, isSignal, navigate, nextTick, onBeforeMount, onBeforeUnmount, onDestroy, onMount, onMounted, onUnmounted, peek, provide, reactive, ref, refresh, registerStoresClient, setRouteParams, state, untrack, useAsync, useClickOutside, useColorMode, useCookie, useCounter, useDark, useDebounce, useDebouncedValue, useEventListener, useFetch, useFocus, useHead, useId, useInterval, useLocalStorage, useMediaQuery, useMutation, useOptimistic, usePreferredContrast, usePreferredDark, usePreferredLight, usePreferredReducedMotion, useQuery, useReactiveProp, useRef, useRoute, useRouteParam, useRouteParams, useScrollLock, useSearchParams, useSeoMeta, useSessionStorage, useSlots, useStore, useThrottle, useTimeout, useToggle, useWebSocket, watch, watchEffect, watchMultiple, withDefaults } = window.stx;
|
|
4
4
|
|
|
5
5
|
const accounts = state([]);
|
|
@@ -189,9 +189,9 @@ onMount(() => {
|
|
|
189
189
|
|
|
190
190
|
return { accounts, project, environments, resources, message, messageKind, reveal, accountSlug, accountName, accountRole, accountScopeType, accountScopeId, selectedAccount, tokenName, tokenCapabilities, tokenScopeType, tokenScopeId, tokenExpiry, password, mfaCode, capabilities, setMessage, setScope, scopeLabel, tokenState, toggleCapability, load, selectAccount, stepUp, createAccount, createToken, tokenAction, disableAccount, curlExample, cliExample, copy };
|
|
191
191
|
}
|
|
192
|
-
window.__stx_latestSetup=
|
|
193
|
-
if(window.stx)window.stx._latestSetup=
|
|
194
|
-
</script> <style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.906ee17c.css"></head><body data-stx="
|
|
192
|
+
window.__stx_latestSetup=__stx_setup_1787597169246_0;
|
|
193
|
+
if(window.stx)window.stx._latestSetup=__stx_setup_1787597169246_0;
|
|
194
|
+
</script> <style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.906ee17c.css"></head><body data-stx="__stx_setup_1787597169246_0"><a data-stx-scope="stx_scope_nav_1" class="skip-link" href="#main-content">Skip to content</a><button class="nav-toggle-button" type="button" aria-label="Open navigation" aria-controls="primary-navigation" aria-expanded="false"><span></span><span></span><span></span></button><button class="nav-scrim" type="button" aria-label="Close navigation"></button><aside class="app-sidebar" id="primary-navigation" aria-label="Primary navigation"><a class="brand" href="/?env=production"><span class="dot"></span><span>ts-cloud</span></a><div class="sidebar-context"><span class="context-label">Project</span><strong>ts-cloud</strong></div><div class="environment-picker" aria-label="Environment"><span class="context-label">Environment</span><details><summary class="environment-current production"><span>PROD</span><strong>production</strong></summary></details></div><button class="command-trigger" type="button" aria-keyshortcuts="Meta+K Control+K"><span>Search resources</span><kbd>⌘ K</kbd></button><div class="nav-groups"><section class="nav-group"><h2>Overview</h2><a class="" href="/?env=production">Dashboard</a><a href="/server/activity?env=production">Activity</a></section><section class="nav-group"><h2>Applications</h2><a class="" href="/server/sites?env=production">Services</a><a class="" href="/server/deployments?env=production">Deployments</a><a class="" href="/server/logs?env=production">Logs</a><a class="" href="/integrations?env=production">Git integrations</a><a class="" href="/applications/new?env=production">Create application</a><a class="" href="/applications/compose?env=production">Compose applications</a></section><section class="nav-group"><h2>Operations</h2><a class="" href="/operations/queue?env=production">Deployment queue</a><a class="" href="/operations/previews?env=production">Preview environments</a><a class="" href="/operations/releases?env=production">Releases & promotion</a><a class="" href="/operations/regions?env=production">Regions & failover</a><a class="" href="/operations/maintenance?env=production">Maintenance & DR</a><a class="" href="/operations/workloads?env=production">Workloads</a><a class="" href="/operations/observability?env=production">Observability</a><a class="" href="/operations/alerts?env=production">Alerts & health</a><a class="" href="/operations/spend?env=production">Spend & budgets</a><a href="/server/metrics?env=production">Metrics</a><a href="/server/services?env=production">Runtime services</a><a class="" href="/operations/jobs?env=production">Workers & schedules</a><a class="" href="/operations/configuration?env=production">Environment & secrets</a><a class="" href="/data/backups?env=production">Backups & recovery</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/actions?env=production">Run operations</a></section><section class="nav-group"><h2>Infrastructure</h2><a class="" href="/infrastructure/topology?env=production">Infrastructure map</a><a class="" href="/data/services?env=production">Data services</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/firewall?env=production">Network & firewall</a><a href="/server/ssh-keys?env=production">SSH keys</a><a class="" href="/server/fleet?env=production">Fleet inventory</a><a class="" href="/server/capacity?env=production">Capacity & placement</a><a href="/security?env=production">Security posture</a><a href="/server/terminal?env=production">Terminal</a></section><section class="nav-group"><h2>Organization</h2><a class="" href="/server/team?env=production">People & access</a><a class="active" href="/account/automation?env=production">API & automation</a></section><section class="nav-group"><h2>Account</h2><a class="" href="/account/security?env=production">Sign-in & security</a></section></div><form method="post" action="/api/logout" class="signout"><button type="submit">Sign out</button></form></aside><dialog class="command-palette" aria-labelledby="command-title"><div class="command-search"><span aria-hidden="true">⌕</span><input type="search" placeholder="Search services, domains, deployments…" aria-label="Search resources" autocomplete="off"><button type="button" class="command-close" aria-label="Close search">Esc</button></div><h2 id="command-title" class="sr-only">Search resources and navigation</h2><div class="command-status" role="status">Start typing to search this project.</div><section class="saved-views" aria-labelledby="saved-views-title"><div class="saved-views-heading"><h3 id="saved-views-title">Saved views</h3><form class="save-view-form"><label class="sr-only" for="save-view-name">Saved view name</label><input id="save-view-name" maxlength="64" placeholder="Name this view" autocomplete="off"><button type="submit">Save current filters</button></form></div><div class="saved-view-list"></div></section><form class="tag-result-form"><label for="tag-result-name">Tag selected resource</label><input id="tag-result-name" maxlength="32" placeholder="customer-facing" pattern="[A-Za-z0-9][A-Za-z0-9 -]{0,31}" autocomplete="off"><input class="tag-color" type="color" value="#5a8be0" aria-label="Tag color"><button type="submit">Add tag</button></form><div class="command-results" role="listbox" aria-label="Search results"></div><footer><span>↑ ↓ navigate</span><span>Enter open</span><span>Esc close</span></footer></dialog><div class="context-bar" aria-label="Current scope"><div class="context-path"><div class="context-breadcrumb"><span>ts-cloud</span><i>/</i><strong>production</strong></div><div class="context-filters" aria-label="Active filters"></div></div><span class="scope-safety production">Production scope</span></div> <script data-stx-scoped data-stx-run="always">
|
|
195
195
|
(function() {
|
|
196
196
|
var { batch, computed, configureFetch, defineEmits, defineExpose, definePageMeta, defineProps, defineSlots, defineStore, derived, effect, goBack, goForward, inject, invalidateRoute, isDerived, isSignal, navigate, nextTick, onBeforeMount, onBeforeUnmount, onDestroy, onMount, onMounted, onUnmounted, peek, provide, reactive, ref, refresh, registerStoresClient, setRouteParams, state, untrack, useAsync, useClickOutside, useColorMode, useCookie, useCounter, useDark, useDebounce, useDebouncedValue, useEventListener, useFetch, useFocus, useHead, useId, useInterval, useLocalStorage, useMediaQuery, useMutation, useOptimistic, usePreferredContrast, usePreferredDark, usePreferredLight, usePreferredReducedMotion, useQuery, useReactiveProp, useRef, useRoute, useRouteParam, useRouteParams, useScrollLock, useSearchParams, useSeoMeta, useSessionStorage, useSlots, useStore, useThrottle, useTimeout, useToggle, useWebSocket, watch, watchEffect, watchMultiple, withDefaults } = window.stx;
|
|
197
197
|
var __destroyHooks = [];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="
|
|
1
|
+
<!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt7l77jbzlapbv"><meta name="title" content="stx Project"><meta name="description" content="A website built with stx templating engine"><meta property="og:title" content="stx Project"><meta property="og:description" content="A website built with stx templating engine"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="stx Project"><meta name="twitter:description" content="A website built with stx templating engine"> <script data-stx-runtime src="/_stx/runtime.c696d17a.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ts-cloud - Sign-in & security</title><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='%235a8be0'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_stx/style.448ab192.css"><style> .security-layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(280px, .7fr); gap: 18px; align-items: start; } .security-stack { display: grid; gap: 18px; } .security-summary { display: grid; gap: 10px; } .security-summary .kv { padding: 8px 0; } .session-list { display: grid; gap: 10px; } .session-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 14px; align-items: center; padding: 13px; border: 1px solid var(--panel-br); border-radius: 11px; } .session-copy { display: grid; gap: 4px; min-width: 0; } .session-copy strong, .session-copy span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .session-copy span { color: var(--txt3); font-size: 11px; } .security-form { display: grid; gap: 13px; } .security-form label { display: grid; gap: 6px; color: var(--txt3); font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; } .security-form input { width: 100%; } .security-status { min-height: 20px; color: var(--txt2); font-size: 12px; } .mfa-secret { display: block; margin: 9px 0; padding: 10px; overflow-wrap: anywhere; border: 1px solid var(--panel-br); border-radius: 9px; background: rgba(255,255,255,.025); color: var(--accent); font: 700 12px var(--mono); } .recovery-codes { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 10px; } .recovery-codes code { padding: 7px; border: 1px solid var(--panel-br); border-radius: 7px; text-align: center; } .hidden { display: none !important; } @media (max-width: 900px) { .security-layout { grid-template-columns: 1fr; } } </style><style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.222ed683.css"></head><body data-stx-auto><a data-stx-scope="stx_scope_nav_3" class="skip-link" href="#main-content">Skip to content</a><button class="nav-toggle-button" type="button" aria-label="Open navigation" aria-controls="primary-navigation" aria-expanded="false"><span></span><span></span><span></span></button><button class="nav-scrim" type="button" aria-label="Close navigation"></button><aside class="app-sidebar" id="primary-navigation" aria-label="Primary navigation"><a class="brand" href="/?env=production"><span class="dot"></span><span>ts-cloud</span></a><div class="sidebar-context"><span class="context-label">Project</span><strong>Project</strong></div><div class="environment-picker" aria-label="Environment"><span class="context-label">Environment</span><details><summary class="environment-current production"><span>PROD</span><strong>production</strong></summary></details></div><button class="command-trigger" type="button" aria-keyshortcuts="Meta+K Control+K"><span>Search resources</span><kbd>⌘ K</kbd></button><div class="nav-groups"><section class="nav-group"><h2>Overview</h2><a class="" href="/?env=production">Dashboard</a><a href="/server/activity?env=production">Activity</a></section><section class="nav-group"><h2>Applications</h2><a class="" href="/server/sites?env=production">Services</a><a class="" href="/server/deployments?env=production">Deployments</a><a class="" href="/server/logs?env=production">Logs</a><a class="" href="/integrations?env=production">Git integrations</a><a class="" href="/applications/new?env=production">Create application</a><a class="" href="/applications/compose?env=production">Compose applications</a></section><section class="nav-group"><h2>Operations</h2><a class="" href="/operations/queue?env=production">Deployment queue</a><a class="" href="/operations/previews?env=production">Preview environments</a><a class="" href="/operations/releases?env=production">Releases & promotion</a><a class="" href="/operations/regions?env=production">Regions & failover</a><a class="" href="/operations/maintenance?env=production">Maintenance & DR</a><a class="" href="/operations/workloads?env=production">Workloads</a><a class="" href="/operations/observability?env=production">Observability</a><a class="" href="/operations/alerts?env=production">Alerts & health</a><a class="" href="/operations/spend?env=production">Spend & budgets</a><a href="/server/metrics?env=production">Metrics</a><a href="/server/services?env=production">Runtime services</a><a class="" href="/operations/jobs?env=production">Workers & schedules</a><a class="" href="/operations/configuration?env=production">Environment & secrets</a><a class="" href="/data/backups?env=production">Backups & recovery</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/actions?env=production">Run operations</a></section><section class="nav-group"><h2>Infrastructure</h2><a class="" href="/infrastructure/topology?env=production">Infrastructure map</a><a class="" href="/data/services?env=production">Data services</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/firewall?env=production">Network & firewall</a><a href="/server/ssh-keys?env=production">SSH keys</a><a class="" href="/server/fleet?env=production">Fleet inventory</a><a class="" href="/server/capacity?env=production">Capacity & placement</a><a href="/security?env=production">Security posture</a><a href="/server/terminal?env=production">Terminal</a></section><section class="nav-group"><h2>Organization</h2><a class="" href="/server/team?env=production">People & access</a><a class="" href="/account/automation?env=production">API & automation</a></section><section class="nav-group"><h2>Account</h2><a class="active" href="/account/security?env=production">Sign-in & security</a></section></div><form method="post" action="/api/logout" class="signout"><button type="submit">Sign out</button></form></aside><dialog class="command-palette" aria-labelledby="command-title"><div class="command-search"><span aria-hidden="true">⌕</span><input type="search" placeholder="Search services, domains, deployments…" aria-label="Search resources" autocomplete="off"><button type="button" class="command-close" aria-label="Close search">Esc</button></div><h2 id="command-title" class="sr-only">Search resources and navigation</h2><div class="command-status" role="status">Start typing to search this project.</div><section class="saved-views" aria-labelledby="saved-views-title"><div class="saved-views-heading"><h3 id="saved-views-title">Saved views</h3><form class="save-view-form"><label class="sr-only" for="save-view-name">Saved view name</label><input id="save-view-name" maxlength="64" placeholder="Name this view" autocomplete="off"><button type="submit">Save current filters</button></form></div><div class="saved-view-list"></div></section><form class="tag-result-form"><label for="tag-result-name">Tag selected resource</label><input id="tag-result-name" maxlength="32" placeholder="customer-facing" pattern="[A-Za-z0-9][A-Za-z0-9 -]{0,31}" autocomplete="off"><input class="tag-color" type="color" value="#5a8be0" aria-label="Tag color"><button type="submit">Add tag</button></form><div class="command-results" role="listbox" aria-label="Search results"></div><footer><span>↑ ↓ navigate</span><span>Enter open</span><span>Esc close</span></footer></dialog><div class="context-bar" aria-label="Current scope"><div class="context-path"><div class="context-breadcrumb"><span>Project</span><i>/</i><strong>production</strong></div><div class="context-filters" aria-label="Active filters"></div></div><span class="scope-safety production">Production scope</span></div> <script data-stx-scoped data-stx-run="always">
|
|
2
2
|
(function() {
|
|
3
3
|
var { batch, computed, configureFetch, defineEmits, defineExpose, definePageMeta, defineProps, defineSlots, defineStore, derived, effect, goBack, goForward, inject, invalidateRoute, isDerived, isSignal, navigate, nextTick, onBeforeMount, onBeforeUnmount, onDestroy, onMount, onMounted, onUnmounted, peek, provide, reactive, ref, refresh, registerStoresClient, setRouteParams, state, untrack, useAsync, useClickOutside, useColorMode, useCookie, useCounter, useDark, useDebounce, useDebouncedValue, useEventListener, useFetch, useFocus, useHead, useId, useInterval, useLocalStorage, useMediaQuery, useMutation, useOptimistic, usePreferredContrast, usePreferredDark, usePreferredLight, usePreferredReducedMotion, useQuery, useReactiveProp, useRef, useRoute, useRouteParam, useRouteParams, useScrollLock, useSearchParams, useSeoMeta, useSessionStorage, useSlots, useStore, useThrottle, useTimeout, useToggle, useWebSocket, watch, watchEffect, watchMultiple, withDefaults } = window.stx;
|
|
4
4
|
var __destroyHooks = [];
|