@auersperg/flare 0.3.7 → 0.3.8
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/cli/{chunk-r3ceakrn.js → chunk-y6e2h40n.js} +187 -20
- package/dist/cli/flare.js +15 -4
- package/dist/platform/admin.js +80 -47
- package/dist/platform/dashboard/_astro/auth-view.EASEF9BN.js +1 -0
- package/dist/platform/dashboard/_astro/{cli-approval.C4-RBXQh.js → cli-approval.DwZVHdyo.js} +1 -1
- package/dist/platform/dashboard/_astro/common.TAClb6G9.js +1 -0
- package/dist/platform/dashboard/_astro/{data-view.B2afgdXn.js → data-view.BaHEU4Xa.js} +1 -1
- package/dist/platform/dashboard/_astro/{functions-view.B0jPBNNy.js → functions-view.BGOYuDwu.js} +1 -1
- package/dist/platform/dashboard/_astro/index.astro_astro_type_script_index_0_lang.Blg5hU9g.js +3 -0
- package/dist/platform/dashboard/_astro/{input-group.BziAwKdZ.js → input-group.DUp-h0xx.js} +1 -1
- package/dist/platform/dashboard/_astro/{sdk-view.DAd5aPbd.js → sdk-view.2ErPn4cO.js} +1 -1
- package/dist/platform/dashboard/_astro/{services-view.BvkS7FHr.js → services-view.iZujitxl.js} +1 -1
- package/dist/platform/dashboard/_astro/settings-view.CqK-mrlB.js +3 -0
- package/dist/platform/dashboard/index.html +1 -1
- package/dist/platform/manifest.json +33 -33
- package/dist/platform/runtime.js +214 -35
- package/package.json +1 -1
- package/dist/platform/dashboard/_astro/auth-view.DxyBDw7Z.js +0 -1
- package/dist/platform/dashboard/_astro/common.D6YHScsr.js +0 -1
- package/dist/platform/dashboard/_astro/index.astro_astro_type_script_index_0_lang.BQ7pqT75.js +0 -3
- package/dist/platform/dashboard/_astro/settings-view.BXt0dexJ.js +0 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/flare/src/cli/platform.mjs
|
|
3
|
-
import { readFile as
|
|
3
|
+
import { readFile as readFile4, mkdir as mkdir2, mkdtemp, writeFile as writeFile2, rm, cp } from "fs/promises";
|
|
4
4
|
import { join as join3 } from "path";
|
|
5
5
|
import { tmpdir } from "os";
|
|
6
6
|
import { randomBytes, randomUUID as randomUUID2 } from "crypto";
|
|
@@ -17,7 +17,7 @@ import { join } from "path";
|
|
|
17
17
|
import { fileURLToPath } from "url";
|
|
18
18
|
import { createHash } from "crypto";
|
|
19
19
|
var PLATFORM_FORMAT = 1;
|
|
20
|
-
var WRANGLER_VERSION = "4.
|
|
20
|
+
var WRANGLER_VERSION = "4.150.0";
|
|
21
21
|
var sha256 = (value) => createHash("sha256").update(value).digest("hex");
|
|
22
22
|
var manifestDigest = ({ digest: _digest, ...manifest }) => sha256(JSON.stringify(manifest));
|
|
23
23
|
async function payloadFiles(directory, prefix = "") {
|
|
@@ -46,7 +46,7 @@ async function loadPlatformRelease(directory = fileURLToPath(new URL("../platfor
|
|
|
46
46
|
} catch {
|
|
47
47
|
throw Error("Platform payload missing or invalid. Install a complete Flare npm release.");
|
|
48
48
|
}
|
|
49
|
-
if (manifest.format !== PLATFORM_FORMAT || !/^\d+\.\d+\.\d+$/.test(manifest.version ?? "") || manifest.protocol !== 1 || manifest.runtimeAbi !== 4 || manifest.stateFormat !== 1 || manifest.wranglerVersion
|
|
49
|
+
if (manifest.format !== PLATFORM_FORMAT || !/^\d+\.\d+\.\d+$/.test(manifest.version ?? "") || manifest.protocol !== 1 || manifest.runtimeAbi !== 4 || manifest.stateFormat !== 1 || ![WRANGLER_VERSION, "4.149.0"].includes(manifest.wranglerVersion) || !/^\d{4}-\d{2}-\d{2}$/.test(manifest.compatibilityDate ?? "") || !Array.isArray(manifest.upgradeFrom) || manifest.upgradeFrom.some((version) => !/^\d+\.\d+\.\d+$/.test(version) || Bun.semver.order(version, manifest.version) >= 0) || !Array.isArray(manifest.files) || !manifest.files.length || manifest.files.length > 1000 || !/^[a-f0-9]{64}$/.test(manifest.digest ?? "") || manifest.digest !== manifestDigest(manifest))
|
|
50
50
|
throw Error("Unsupported or damaged platform manifest");
|
|
51
51
|
const paths = new Set;
|
|
52
52
|
let size = 0;
|
|
@@ -84,6 +84,16 @@ function assertUpgrade(installed, target) {
|
|
|
84
84
|
if (installed.version === target.version && installed.digest !== target.digest)
|
|
85
85
|
throw Error("Same platform version has different bytes. Publish a new version; refusing an in-place replacement.");
|
|
86
86
|
}
|
|
87
|
+
function configurationDigest(value) {
|
|
88
|
+
const canonical = (item) => {
|
|
89
|
+
if (Array.isArray(item))
|
|
90
|
+
return item.map(canonical);
|
|
91
|
+
if (item && typeof item === "object")
|
|
92
|
+
return Object.fromEntries(Object.keys(item).sort().map((key) => [key, canonical(item[key])]));
|
|
93
|
+
return item;
|
|
94
|
+
};
|
|
95
|
+
return sha256(JSON.stringify(canonical(value)));
|
|
96
|
+
}
|
|
87
97
|
|
|
88
98
|
// packages/flare/src/cli/cloudflare-account.mjs
|
|
89
99
|
class CloudflareError extends Error {
|
|
@@ -271,10 +281,85 @@ class CloudflareAccount {
|
|
|
271
281
|
}
|
|
272
282
|
|
|
273
283
|
// packages/flare/src/cli/platform-state.mjs
|
|
274
|
-
import { mkdir, readFile as
|
|
284
|
+
import { mkdir, readFile as readFile3, writeFile, rename, chmod } from "fs/promises";
|
|
275
285
|
import { join as join2 } from "path";
|
|
276
286
|
import { homedir } from "os";
|
|
277
287
|
import { randomUUID } from "crypto";
|
|
288
|
+
|
|
289
|
+
// packages/flare/src/cli/platform-config.mjs
|
|
290
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
291
|
+
function validatePlatformConfig(value = {}) {
|
|
292
|
+
const object = (v) => v && typeof v === "object" && !Array.isArray(v);
|
|
293
|
+
if (!object(value) || Object.keys(value).some((k) => !["capabilities", "secrets"].includes(k)))
|
|
294
|
+
throw Error("Platform config accepts only capabilities and secrets references");
|
|
295
|
+
const capabilities = value.capabilities ?? {}, secrets = value.secrets ?? [];
|
|
296
|
+
if (!object(capabilities) || !Array.isArray(secrets) || secrets.length > 64)
|
|
297
|
+
throw Error("Invalid platform capabilities/secrets references");
|
|
298
|
+
const bindings = new Set;
|
|
299
|
+
const references = secrets.map((r) => {
|
|
300
|
+
if (!object(r) || Object.keys(r).sort().join() !== "binding,secret_name,store_id" || !/^APP_SECRET_[A-Z0-9_]{1,64}$/.test(r.binding) || !/^[a-f0-9]{32}$/.test(r.store_id) || typeof r.secret_name !== "string" || !/^[A-Za-z0-9_.-]{1,128}$/.test(r.secret_name) || bindings.has(r.binding))
|
|
301
|
+
throw Error("Invalid or duplicate native Secrets Store reference");
|
|
302
|
+
bindings.add(r.binding);
|
|
303
|
+
return {
|
|
304
|
+
binding: r.binding,
|
|
305
|
+
store_id: r.store_id,
|
|
306
|
+
secret_name: r.secret_name
|
|
307
|
+
};
|
|
308
|
+
}).sort((a, b) => a.binding.localeCompare(b.binding));
|
|
309
|
+
const result = {};
|
|
310
|
+
const used = new Set;
|
|
311
|
+
for (const scope of Object.keys(capabilities).sort()) {
|
|
312
|
+
const p = capabilities[scope];
|
|
313
|
+
if (!/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\/(dev|prod)$/.test(scope) || !object(p) || Object.keys(p).some((k) => !["outbound", "secrets"].includes(k)))
|
|
314
|
+
throw Error("Invalid capability scope");
|
|
315
|
+
const hosts = p.outbound ?? [], mapping = p.secrets ?? {};
|
|
316
|
+
if (!Array.isArray(hosts) || hosts.length > 20 || !object(mapping) || Object.keys(mapping).length > 16)
|
|
317
|
+
throw Error("Capability limits exceeded");
|
|
318
|
+
for (const host of hosts)
|
|
319
|
+
if (typeof host !== "string" || host.length > 253 || !/^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,63}$/.test(host) || /\.(localhost|local|internal)$/.test(host))
|
|
320
|
+
throw Error("Only exact public HTTPS hostnames are allowed");
|
|
321
|
+
for (const [name, binding] of Object.entries(mapping)) {
|
|
322
|
+
if (!/^[A-Z][A-Z0-9_]{0,63}$/.test(name) || !bindings.has(binding))
|
|
323
|
+
throw Error("Secret grant must reference an explicit APP_SECRET binding");
|
|
324
|
+
used.add(binding);
|
|
325
|
+
}
|
|
326
|
+
result[scope] = {
|
|
327
|
+
outbound: [...new Set(hosts)].sort(),
|
|
328
|
+
secrets: Object.fromEntries(Object.entries(mapping).sort())
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (references.some((r) => !used.has(r.binding)))
|
|
332
|
+
throw Error("Unused secret binding; grant it explicitly or remove it");
|
|
333
|
+
if (JSON.stringify(result).length > 16384)
|
|
334
|
+
throw Error("Capabilities exceed 16 KiB");
|
|
335
|
+
return { capabilities: result, secrets: references };
|
|
336
|
+
}
|
|
337
|
+
async function readPlatformConfig(path) {
|
|
338
|
+
const bytes = await readFile2(path);
|
|
339
|
+
if (bytes.length > 32768)
|
|
340
|
+
throw Error("Platform config exceeds 32 KiB");
|
|
341
|
+
return validatePlatformConfig(JSON.parse(bytes));
|
|
342
|
+
}
|
|
343
|
+
function accessConfiguration(app, policies) {
|
|
344
|
+
const {
|
|
345
|
+
created_at: _created,
|
|
346
|
+
updated_at: _updated,
|
|
347
|
+
policies: _embedded,
|
|
348
|
+
...config
|
|
349
|
+
} = app;
|
|
350
|
+
const clean = policies.map(({ created_at: _c, updated_at: _u, ...p }) => p).sort((a, b) => String(a.id).localeCompare(String(b.id)));
|
|
351
|
+
return { app: config, policies: clean };
|
|
352
|
+
}
|
|
353
|
+
async function accessSnapshot(cf, id) {
|
|
354
|
+
const app = await cf.api(`access/apps/${id}`);
|
|
355
|
+
const policies = await cf.list(`access/apps/${id}/policies`);
|
|
356
|
+
return {
|
|
357
|
+
app,
|
|
358
|
+
digest: configurationDigest(accessConfiguration(app, policies))
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// packages/flare/src/cli/platform-state.mjs
|
|
278
363
|
var leaseMilliseconds = 15 * 60 * 1000;
|
|
279
364
|
var identifier = /^[a-z][a-z0-9-]{0,19}$/;
|
|
280
365
|
var resourceName = /^[a-z][a-z0-9-]{0,62}$/;
|
|
@@ -288,6 +373,8 @@ function hostname(value) {
|
|
|
288
373
|
function validateState(state, account, name) {
|
|
289
374
|
if (!state || state.format !== 1 || state.account !== account || state.name !== name || !uuid.test(state.id ?? "") || !validName(name) || typeof state.owner !== "string" || !/^\S+@\S+\.\S+$/.test(state.owner) || state.owner.length > 254)
|
|
290
375
|
throw Error("Unsupported installation state or account/name mismatch");
|
|
376
|
+
if (state.config !== undefined)
|
|
377
|
+
validatePlatformConfig(state.config);
|
|
291
378
|
const prefix = `flare-${name}-${state.id.replaceAll("-", "").slice(0, 12)}`;
|
|
292
379
|
const operator = state.origin === "operator";
|
|
293
380
|
if (state.origin !== undefined && !operator)
|
|
@@ -407,7 +494,7 @@ class InstallationStore {
|
|
|
407
494
|
await chmod(this.file, 384);
|
|
408
495
|
}
|
|
409
496
|
async local() {
|
|
410
|
-
const source = await
|
|
497
|
+
const source = await readFile3(this.file, "utf8").catch((error) => {
|
|
411
498
|
if (error.code !== "ENOENT")
|
|
412
499
|
throw error;
|
|
413
500
|
return null;
|
|
@@ -505,15 +592,63 @@ function settingsDigest(settings) {
|
|
|
505
592
|
stable.bindings = [...settings.bindings ?? []].sort((a, b) => a.name.localeCompare(b.name));
|
|
506
593
|
return configurationDigest(stable);
|
|
507
594
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
595
|
+
|
|
596
|
+
// packages/flare/src/cli/platform-health.mjs
|
|
597
|
+
async function platformHealth(cf, state, fetcher) {
|
|
598
|
+
const checkedAt = Date.now();
|
|
599
|
+
const probe = async (fn) => {
|
|
600
|
+
try {
|
|
601
|
+
return await fn();
|
|
602
|
+
} catch {
|
|
603
|
+
return { status: "unavailable" };
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
const [runtime, dashboard, access] = await Promise.all([
|
|
607
|
+
probe(async () => {
|
|
608
|
+
const r = await fetcher(`https://${state.addresses.runtimeHost}/health?ready=1`, { redirect: "error", signal: AbortSignal.timeout(5000) });
|
|
609
|
+
if (!r.ok) {
|
|
610
|
+
await r.body?.cancel();
|
|
611
|
+
return { status: "unhealthy", httpStatus: r.status };
|
|
612
|
+
}
|
|
613
|
+
const d = await r.json();
|
|
614
|
+
const matches = d.readiness === "control-plane-ready" && d.installationId === state.id && d.platformPayload === state.installed?.digest && d.version === state.installed?.version;
|
|
615
|
+
return {
|
|
616
|
+
status: matches ? "healthy" : "mismatch",
|
|
617
|
+
version: d.version,
|
|
618
|
+
installationId: d.installationId
|
|
619
|
+
};
|
|
620
|
+
}),
|
|
621
|
+
probe(async () => {
|
|
622
|
+
const r = await fetcher(`https://${state.addresses.adminHost}/api/session`, { redirect: "manual", signal: AbortSignal.timeout(5000) });
|
|
623
|
+
const location = r.headers.get("location");
|
|
624
|
+
await r.body?.cancel();
|
|
625
|
+
const closed = [401, 403].includes(r.status) || [301, 302, 303, 307, 308].includes(r.status) && location && new URL(location, `https://${state.addresses.adminHost}`).origin === state.access?.issuer;
|
|
626
|
+
return {
|
|
627
|
+
status: closed ? "access-protected" : "not-verified-closed",
|
|
628
|
+
httpStatus: r.status,
|
|
629
|
+
applicationHealth: "not-checked-without-owner-session"
|
|
630
|
+
};
|
|
631
|
+
}),
|
|
632
|
+
probe(async () => {
|
|
633
|
+
if (!state.access)
|
|
634
|
+
return { status: "not-configured" };
|
|
635
|
+
const { app, digest } = await accessSnapshot(cf, state.access.id);
|
|
636
|
+
const host = app.domain === state.addresses.adminHost || app.destinations?.some((d) => d.type === "public" && d.uri === state.addresses.adminHost);
|
|
637
|
+
const matches = !state.access.snapshot || (state.access.snapshotFormat === 2 ? digest : configurationDigest(app)) === state.access.snapshot;
|
|
638
|
+
return {
|
|
639
|
+
status: host && app.aud === state.access.aud && matches ? "verified" : "drift",
|
|
640
|
+
snapshot: digest,
|
|
641
|
+
snapshotFormat: 2
|
|
642
|
+
};
|
|
643
|
+
})
|
|
644
|
+
]);
|
|
645
|
+
return {
|
|
646
|
+
status: state.operation ? "incomplete" : runtime.status === "healthy" && dashboard.status === "access-protected" && access.status === "verified" ? "ready" : "not-verified",
|
|
647
|
+
checkedAt,
|
|
648
|
+
runtime,
|
|
649
|
+
dashboard,
|
|
650
|
+
access
|
|
515
651
|
};
|
|
516
|
-
return sha256(JSON.stringify(canonical(value)));
|
|
517
652
|
}
|
|
518
653
|
|
|
519
654
|
// packages/flare/src/cli/platform.mjs
|
|
@@ -681,6 +816,7 @@ function workerConfiguration(state, release, role, bootstrap = false) {
|
|
|
681
816
|
return {
|
|
682
817
|
...common,
|
|
683
818
|
worker_loaders: [{ binding: "LOADER" }],
|
|
819
|
+
...state.config ? { secrets_store_secrets: validatePlatformConfig(state.config).secrets } : {},
|
|
684
820
|
durable_objects: {
|
|
685
821
|
bindings: [{ name: "SUPERVISORS", class_name: "EnvironmentSupervisor" }]
|
|
686
822
|
},
|
|
@@ -720,7 +856,10 @@ function workerConfiguration(state, release, role, bootstrap = false) {
|
|
|
720
856
|
],
|
|
721
857
|
vars: {
|
|
722
858
|
...common.vars,
|
|
723
|
-
DEV_TOKEN_ISSUER: "https://" + state.addresses.runtimeHost
|
|
859
|
+
DEV_TOKEN_ISSUER: "https://" + state.addresses.runtimeHost,
|
|
860
|
+
...state.config ? {
|
|
861
|
+
APP_CAPABILITIES: JSON.stringify(validatePlatformConfig(state.config).capabilities)
|
|
862
|
+
} : {}
|
|
724
863
|
}
|
|
725
864
|
};
|
|
726
865
|
}
|
|
@@ -731,10 +870,15 @@ async function confirm(options, state, plan, ask, log) {
|
|
|
731
870
|
throw Error("Account confirmation required; no cloud changes were made");
|
|
732
871
|
}
|
|
733
872
|
async function assertAccess(cf, state) {
|
|
734
|
-
const app = await cf
|
|
873
|
+
const { app, digest } = await accessSnapshot(cf, state.access.id);
|
|
735
874
|
const coversHost = app.domain === state.addresses.adminHost || app.destinations?.some((d) => d.type === "public" && d.uri === state.addresses.adminHost);
|
|
736
|
-
if (!coversHost || app.aud !== state.access.aud
|
|
737
|
-
throw Error("Access
|
|
875
|
+
if (!coversHost || app.aud !== state.access.aud)
|
|
876
|
+
throw Error("Access identity/host drift; explicit snapshot acceptance cannot change identities");
|
|
877
|
+
const expected = state.access.snapshot;
|
|
878
|
+
if (expected && (state.access.snapshotFormat === 2 ? digest : configurationDigest(app)) !== expected)
|
|
879
|
+
throw Error(`Access application/policy drift. Review Cloudflare settings, then explicitly accept snapshot ${digest} using --accept-access; no policies were changed.`);
|
|
880
|
+
state.access.snapshot = digest;
|
|
881
|
+
state.access.snapshotFormat = 2;
|
|
738
882
|
}
|
|
739
883
|
async function remoteWorker(cf, name) {
|
|
740
884
|
try {
|
|
@@ -1140,12 +1284,16 @@ async function runPlatform(options, dependencies = {}) {
|
|
|
1140
1284
|
const discovered = adoption ? await discoverOperator(cf, store, name) : await store.discover();
|
|
1141
1285
|
let state = discovered.state;
|
|
1142
1286
|
if (operation === "status")
|
|
1143
|
-
return state ?
|
|
1287
|
+
return state ? {
|
|
1288
|
+
...publicState(state, release.manifest, discovered.row),
|
|
1289
|
+
health: await platformHealth(cf, state, dependencies.fetch ?? globalThis.fetch)
|
|
1290
|
+
} : {
|
|
1144
1291
|
account,
|
|
1145
1292
|
installation: name,
|
|
1146
1293
|
status: "not-installed",
|
|
1147
1294
|
target: release.manifest.version
|
|
1148
1295
|
};
|
|
1296
|
+
const requestedConfig = options.config ? await readPlatformConfig(options.config) : null;
|
|
1149
1297
|
const inventory = await preflight(cf);
|
|
1150
1298
|
if (adoption && !state)
|
|
1151
1299
|
state = await prepareOperator(cf, inventory, release, options, dependencies);
|
|
@@ -1164,6 +1312,20 @@ async function runPlatform(options, dependencies = {}) {
|
|
|
1164
1312
|
runtimeHost: options["runtime-host"]
|
|
1165
1313
|
});
|
|
1166
1314
|
}
|
|
1315
|
+
const configChanged = requestedConfig && configurationDigest(requestedConfig) !== configurationDigest(validatePlatformConfig(state.config));
|
|
1316
|
+
if (state.operation && requestedConfig && configurationDigest(requestedConfig) !== state.operation.configDigest)
|
|
1317
|
+
throw Error("Pending config differs; resume with the original config or omit --config");
|
|
1318
|
+
let accessChanged = false;
|
|
1319
|
+
if (options["accept-access"]) {
|
|
1320
|
+
if (!/^[a-f0-9]{64}$/.test(options["accept-access"]) || operation !== "update" || state.operation)
|
|
1321
|
+
throw Error("Access snapshot acceptance requires an idle managed update and exact SHA256");
|
|
1322
|
+
const actual = await accessSnapshot(cf, state.access.id);
|
|
1323
|
+
if (actual.digest !== options["accept-access"])
|
|
1324
|
+
throw Error("Access changed since review; read platform status and review the new snapshot");
|
|
1325
|
+
accessChanged = state.access.snapshotFormat !== 2 || state.access.snapshot !== actual.digest;
|
|
1326
|
+
state.access.snapshot = actual.digest;
|
|
1327
|
+
state.access.snapshotFormat = 2;
|
|
1328
|
+
}
|
|
1167
1329
|
if (state.operation && (state.operation.kind !== operation || state.operation.targetDigest !== release.manifest.digest))
|
|
1168
1330
|
throw Error("A different platform operation is incomplete. Resume it with the original CLI release before changing versions.");
|
|
1169
1331
|
if (state.installed) {
|
|
@@ -1172,7 +1334,7 @@ async function runPlatform(options, dependencies = {}) {
|
|
|
1172
1334
|
if (!state.operation) {
|
|
1173
1335
|
await assertResources(cf, state, inventory);
|
|
1174
1336
|
await assertNoDrift(cf, state);
|
|
1175
|
-
if (state.installed.digest === release.manifest.digest)
|
|
1337
|
+
if (state.installed.digest === release.manifest.digest && !configChanged && !accessChanged)
|
|
1176
1338
|
return { ...publicState(state, release.manifest), unchanged: true };
|
|
1177
1339
|
if (operation === "install" || adoption)
|
|
1178
1340
|
throw Error("Already installed. Use flare platform update to change the platform version");
|
|
@@ -1186,6 +1348,8 @@ async function runPlatform(options, dependencies = {}) {
|
|
|
1186
1348
|
from: state.installed?.version ?? state.adoptedFrom?.version ?? null,
|
|
1187
1349
|
to: release.manifest.version,
|
|
1188
1350
|
digest: release.manifest.digest,
|
|
1351
|
+
configuration: requestedConfig ?? state.config ?? null,
|
|
1352
|
+
acceptedAccessSnapshot: accessChanged ? state.access.snapshot : null,
|
|
1189
1353
|
dashboard: "https://" + state.addresses.adminHost,
|
|
1190
1354
|
runtime: "https://" + state.addresses.runtimeHost,
|
|
1191
1355
|
steps: state.installed || adoption ? ["migrations", "runtime", "admin", "verify"] : steps,
|
|
@@ -1208,10 +1372,13 @@ async function runPlatform(options, dependencies = {}) {
|
|
|
1208
1372
|
if (options["dry-run"])
|
|
1209
1373
|
return plan;
|
|
1210
1374
|
await confirm(options, state, plan, ask, log);
|
|
1375
|
+
if (!state.operation && requestedConfig)
|
|
1376
|
+
state.config = requestedConfig;
|
|
1211
1377
|
state.operation ??= {
|
|
1212
1378
|
id: randomUUID2(),
|
|
1213
1379
|
kind: operation,
|
|
1214
1380
|
targetDigest: release.manifest.digest,
|
|
1381
|
+
configDigest: configurationDigest(validatePlatformConfig(state.config)),
|
|
1215
1382
|
completed: [],
|
|
1216
1383
|
phase: "catalog",
|
|
1217
1384
|
previousVersions: { ...state.versions },
|
|
@@ -1235,7 +1402,7 @@ async function runPlatform(options, dependencies = {}) {
|
|
|
1235
1402
|
await assertResources(cf, state, inventory);
|
|
1236
1403
|
await assertNoDrift(cf, state);
|
|
1237
1404
|
}
|
|
1238
|
-
await store.initialize(state, await
|
|
1405
|
+
await store.initialize(state, await readFile4(join3(release.directory, "migrations/0006_platform_installation.sql"), "utf8"));
|
|
1239
1406
|
}
|
|
1240
1407
|
await store.acquire(state, options.resume === true);
|
|
1241
1408
|
const scratch = await mkdtemp(join3(tmpdir(), "flare-platform-"));
|
package/dist/cli/flare.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// packages/flare/package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@auersperg/flare",
|
|
6
|
-
version: "0.3.
|
|
6
|
+
version: "0.3.8",
|
|
7
7
|
license: "MIT",
|
|
8
8
|
type: "module",
|
|
9
9
|
description: "Flare client, server declarations and development CLI",
|
|
@@ -107,6 +107,8 @@ else if (command === "platform") {
|
|
|
107
107
|
console.log(`flare platform install|status|update|adopt [--account <ID>] [--name <name>]
|
|
108
108
|
install: [--owner <email>] [--dashboard-host <host> --runtime-host <host>] [--subdomain <workers.dev prefix>]
|
|
109
109
|
adopt: --owner <email> (existing standard flare-admin/flare-runtime/flare-catalog; default installation only)
|
|
110
|
+
install/update: [--config <operator JSON>]
|
|
111
|
+
update: [--accept-access <reviewed snapshot SHA256>]
|
|
110
112
|
install/update/adopt: [--dry-run] [--confirm <account ID>] [--resume]
|
|
111
113
|
Uses the platform payload bundled with this CLI release. Never updates applications or runs a local dashboard. Requires separate Cloudflare account permissions; FLARE_DEPLOY_TOKEN is not an account token.`);
|
|
112
114
|
} else {
|
|
@@ -122,8 +124,17 @@ Uses the platform payload bundled with this CLI release. Never updates applicati
|
|
|
122
124
|
"subdomain",
|
|
123
125
|
"confirm",
|
|
124
126
|
"dry-run",
|
|
125
|
-
"resume"
|
|
126
|
-
|
|
127
|
+
"resume",
|
|
128
|
+
"config"
|
|
129
|
+
] : operation === "update" ? [
|
|
130
|
+
"account",
|
|
131
|
+
"name",
|
|
132
|
+
"confirm",
|
|
133
|
+
"dry-run",
|
|
134
|
+
"resume",
|
|
135
|
+
"config",
|
|
136
|
+
"accept-access"
|
|
137
|
+
] : ["account", "name"];
|
|
127
138
|
for (let i = 0;i < args.length; i++) {
|
|
128
139
|
const key = args[i]?.slice(2);
|
|
129
140
|
if (!args[i]?.startsWith("--") || !allowed.includes(key) || key in options)
|
|
@@ -136,7 +147,7 @@ Uses the platform payload bundled with this CLI release. Never updates applicati
|
|
|
136
147
|
options[key] = args[++i];
|
|
137
148
|
}
|
|
138
149
|
}
|
|
139
|
-
const { platform } = await import("./chunk-
|
|
150
|
+
const { platform } = await import("./chunk-y6e2h40n.js");
|
|
140
151
|
console.log(JSON.stringify(await platform(options), null, 2));
|
|
141
152
|
}
|
|
142
153
|
} catch (error) {
|
package/dist/platform/admin.js
CHANGED
|
@@ -1,49 +1,3 @@
|
|
|
1
|
-
// packages/core/src/retention.ts
|
|
2
|
-
var jobRetentionMs = 30 * 86400000;
|
|
3
|
-
var catalogDeadlines = new WeakMap;
|
|
4
|
-
async function collectCatalog(database, now = Date.now()) {
|
|
5
|
-
if (now < (catalogDeadlines.get(database) ?? 0))
|
|
6
|
-
return;
|
|
7
|
-
const next = now + 86400000;
|
|
8
|
-
const claim = await database.prepare("UPDATE maintenance SET next=? WHERE id=1 AND next<=? RETURNING id").bind(next, now).first();
|
|
9
|
-
catalogDeadlines.set(database, claim ? next : now + 60000);
|
|
10
|
-
if (!claim)
|
|
11
|
-
return;
|
|
12
|
-
const statements = [
|
|
13
|
-
database.prepare("DELETE FROM audit WHERE id IN (SELECT id FROM audit WHERE at<? ORDER BY at LIMIT 1000)").bind(now - 90 * 86400000),
|
|
14
|
-
database.prepare("DELETE FROM credentials WHERE hash IN (SELECT hash FROM credentials WHERE expiresAt<? ORDER BY expiresAt LIMIT 1000)").bind(now - 14 * 86400000),
|
|
15
|
-
database.prepare("DELETE FROM cli_logins WHERE id IN (SELECT id FROM cli_logins WHERE expiresAt<? ORDER BY expiresAt LIMIT 1000)").bind(now)
|
|
16
|
-
];
|
|
17
|
-
const result = await database.batch(statements);
|
|
18
|
-
if (result.some((value) => Number(value.meta.changes) >= 1000)) {
|
|
19
|
-
catalogDeadlines.set(database, now + 60000);
|
|
20
|
-
await database.prepare("UPDATE maintenance SET next=? WHERE id=1").bind(now + 60000).run();
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
// packages/core/package.json
|
|
24
|
-
var package_default = {
|
|
25
|
-
name: "@flare/platform",
|
|
26
|
-
version: "0.3.7",
|
|
27
|
-
type: "module",
|
|
28
|
-
license: "MIT",
|
|
29
|
-
private: true,
|
|
30
|
-
files: [
|
|
31
|
-
"dist",
|
|
32
|
-
"src",
|
|
33
|
-
"bootstrap",
|
|
34
|
-
"migrations",
|
|
35
|
-
"README.md"
|
|
36
|
-
],
|
|
37
|
-
dependencies: {
|
|
38
|
-
agents: "0.28.0"
|
|
39
|
-
},
|
|
40
|
-
repository: {
|
|
41
|
-
type: "git",
|
|
42
|
-
url: "https://github.com/auersperg/flare.git",
|
|
43
|
-
directory: "packages/core"
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
1
|
// packages/flare/src/sdk/protocol.ts
|
|
48
2
|
var LIMITS = Object.freeze({
|
|
49
3
|
inputBytes: 65536,
|
|
@@ -305,6 +259,69 @@ function projectCustomer(value) {
|
|
|
305
259
|
return value.trim();
|
|
306
260
|
}
|
|
307
261
|
|
|
262
|
+
// packages/core/src/maintenance.ts
|
|
263
|
+
function scopeForDeletion(value) {
|
|
264
|
+
if (typeof value !== "string")
|
|
265
|
+
fail("INVALID_INPUT");
|
|
266
|
+
const parts = value.split("/");
|
|
267
|
+
if (parts.length !== 2)
|
|
268
|
+
fail("INVALID_INPUT");
|
|
269
|
+
return `${uuid(parts[0])}/${environment(parts[1])}`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// packages/core/src/dashboard-live.ts
|
|
273
|
+
async function dashboardAllowed(catalog, scope, email) {
|
|
274
|
+
const project = scopeForDeletion(scope).split("/")[0];
|
|
275
|
+
const row = await catalog.prepare("SELECT m.role FROM members m JOIN projects p ON p.id=? JOIN environments e ON e.projectId=p.id AND e.env=? WHERE m.email=? AND NOT EXISTS (SELECT 1 FROM project_deletions d WHERE d.projectId=p.id)").bind(project, scope.split("/")[1], email).first();
|
|
276
|
+
return !!row && ["owner", "editor", "viewer"].includes(row.role);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// packages/core/src/retention.ts
|
|
280
|
+
var jobRetentionMs = 30 * 86400000;
|
|
281
|
+
var catalogDeadlines = new WeakMap;
|
|
282
|
+
async function collectCatalog(database, now = Date.now()) {
|
|
283
|
+
if (now < (catalogDeadlines.get(database) ?? 0))
|
|
284
|
+
return;
|
|
285
|
+
const next = now + 86400000;
|
|
286
|
+
const claim = await database.prepare("UPDATE maintenance SET next=? WHERE id=1 AND next<=? RETURNING id").bind(next, now).first();
|
|
287
|
+
catalogDeadlines.set(database, claim ? next : now + 60000);
|
|
288
|
+
if (!claim)
|
|
289
|
+
return;
|
|
290
|
+
const statements = [
|
|
291
|
+
database.prepare("DELETE FROM audit WHERE id IN (SELECT id FROM audit WHERE at<? ORDER BY at LIMIT 1000)").bind(now - 90 * 86400000),
|
|
292
|
+
database.prepare("DELETE FROM credentials WHERE hash IN (SELECT hash FROM credentials WHERE expiresAt<? ORDER BY expiresAt LIMIT 1000)").bind(now - 14 * 86400000),
|
|
293
|
+
database.prepare("DELETE FROM cli_logins WHERE id IN (SELECT id FROM cli_logins WHERE expiresAt<? ORDER BY expiresAt LIMIT 1000)").bind(now)
|
|
294
|
+
];
|
|
295
|
+
const result = await database.batch(statements);
|
|
296
|
+
if (result.some((value) => Number(value.meta.changes) >= 1000)) {
|
|
297
|
+
catalogDeadlines.set(database, now + 60000);
|
|
298
|
+
await database.prepare("UPDATE maintenance SET next=? WHERE id=1").bind(now + 60000).run();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// packages/core/package.json
|
|
302
|
+
var package_default = {
|
|
303
|
+
name: "@flare/platform",
|
|
304
|
+
version: "0.3.8",
|
|
305
|
+
type: "module",
|
|
306
|
+
license: "MIT",
|
|
307
|
+
private: true,
|
|
308
|
+
files: [
|
|
309
|
+
"dist",
|
|
310
|
+
"src",
|
|
311
|
+
"bootstrap",
|
|
312
|
+
"migrations",
|
|
313
|
+
"README.md"
|
|
314
|
+
],
|
|
315
|
+
dependencies: {
|
|
316
|
+
agents: "0.28.0"
|
|
317
|
+
},
|
|
318
|
+
repository: {
|
|
319
|
+
type: "git",
|
|
320
|
+
url: "https://github.com/auersperg/flare.git",
|
|
321
|
+
directory: "packages/core"
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
|
|
308
325
|
// packages/core/src/project-deletion.ts
|
|
309
326
|
function authorizeProjectDeletion(actor, project, input) {
|
|
310
327
|
if (actor.role !== "owner" || actor.projectId)
|
|
@@ -599,6 +616,19 @@ async function api(request, env, a) {
|
|
|
599
616
|
});
|
|
600
617
|
const which = environment(match[2]), scope = `${projectId}/${which}`, op = match[3] ?? "health";
|
|
601
618
|
if (request.method === "GET") {
|
|
619
|
+
if (op === "live") {
|
|
620
|
+
if (a.projectId || request.headers.get("origin") !== url.origin || request.headers.get("upgrade")?.toLowerCase() !== "websocket")
|
|
621
|
+
fail("FORBIDDEN");
|
|
622
|
+
return env.RUNTIME.fetch(new Request(`https://dashboard.internal/${scope}`, {
|
|
623
|
+
headers: {
|
|
624
|
+
upgrade: "websocket",
|
|
625
|
+
"x-flared-dashboard": JSON.stringify({
|
|
626
|
+
email: a.email,
|
|
627
|
+
expires: Date.now() + 600000
|
|
628
|
+
})
|
|
629
|
+
}
|
|
630
|
+
}));
|
|
631
|
+
}
|
|
602
632
|
if (["release", "release-bundle"].includes(op)) {
|
|
603
633
|
const hash = url.searchParams.get("hash");
|
|
604
634
|
if (!hash || !/^[a-f0-9]{64}$/.test(hash))
|
|
@@ -749,6 +779,9 @@ async function api(request, env, a) {
|
|
|
749
779
|
}
|
|
750
780
|
|
|
751
781
|
class AdminControl extends WorkerEntrypoint {
|
|
782
|
+
dashboardAllowed(scope, email) {
|
|
783
|
+
return dashboardAllowed(this.env.CATALOG, scope, email);
|
|
784
|
+
}
|
|
752
785
|
async readiness() {
|
|
753
786
|
const row = await this.env.CATALOG.prepare("SELECT installation_id FROM flare_installation WHERE singleton=1").first();
|
|
754
787
|
const owner = await this.env.CATALOG.prepare("SELECT email FROM members WHERE role='owner' LIMIT 1").first();
|
|
@@ -785,7 +818,7 @@ var admin_default = {
|
|
|
785
818
|
if (u.pathname.startsWith("/bootstrap/") || u.pathname.startsWith("/starter/"))
|
|
786
819
|
return new Response("Not found", { status: 404 });
|
|
787
820
|
const response = await env.ASSETS.fetch(request), headers = new Headers(response.headers);
|
|
788
|
-
headers.set("content-security-policy", "default-src 'self'; script-src 'self' " + { scripts: ["'sha256-xIb/Pa7FUshAu4ihwjchYcQpyCI8eS8kt40GpOAYCz8='", "'sha256-1BvDVWLr3QCC20uHhUDi27eDFVJpk1uMQJGIWKUks2I='", "'sha256-kmZE/W8I1iuTnl1b3yqzv9/DQZenPngd3NjSKMQl7y0='", "'sha256-Y8t0ntteBwH+PqGKUyjWW3CAk+BNJKSoQc0xZ68FjhM='", "'sha256-0O7iB6zr9x0r6X6eWYITC9V5q+yWCGAoFvcysBGbY9Q='", "'sha256-tQIkdAw2vqfQ1PjgUE4ML2A4pIV8SByQ1Seeehy6UIc='", "'sha256-4f86GpVBksx63uHEB4f15jGyhnVEOKjwqYxwnfDVOik='"], styles: ["'sha256-vv9IoKo7BSLbWcUHr3tNmfNVmm5L/9Cfn2H6LMk7/ow='", "'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='"] }.scripts.join(" ") + "; style-src 'self' " + { scripts: ["'sha256-xIb/Pa7FUshAu4ihwjchYcQpyCI8eS8kt40GpOAYCz8='", "'sha256-1BvDVWLr3QCC20uHhUDi27eDFVJpk1uMQJGIWKUks2I='", "'sha256-kmZE/W8I1iuTnl1b3yqzv9/DQZenPngd3NjSKMQl7y0='", "'sha256-Y8t0ntteBwH+PqGKUyjWW3CAk+BNJKSoQc0xZ68FjhM='", "'sha256-0O7iB6zr9x0r6X6eWYITC9V5q+yWCGAoFvcysBGbY9Q='", "'sha256-tQIkdAw2vqfQ1PjgUE4ML2A4pIV8SByQ1Seeehy6UIc='", "'sha256-4f86GpVBksx63uHEB4f15jGyhnVEOKjwqYxwnfDVOik='"], styles: ["'sha256-vv9IoKo7BSLbWcUHr3tNmfNVmm5L/9Cfn2H6LMk7/ow='", "'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='"] }.styles.join(" ") + "; connect-src 'self' " + env.RUNTIME_URL + " " + env.RUNTIME_URL.replace("https:", "wss:") + "; object-src 'none'; base-uri 'none'; frame-ancestors 'none'");
|
|
821
|
+
headers.set("content-security-policy", "default-src 'self'; script-src 'self' " + { scripts: ["'sha256-xIb/Pa7FUshAu4ihwjchYcQpyCI8eS8kt40GpOAYCz8='", "'sha256-1BvDVWLr3QCC20uHhUDi27eDFVJpk1uMQJGIWKUks2I='", "'sha256-kmZE/W8I1iuTnl1b3yqzv9/DQZenPngd3NjSKMQl7y0='", "'sha256-Y8t0ntteBwH+PqGKUyjWW3CAk+BNJKSoQc0xZ68FjhM='", "'sha256-0O7iB6zr9x0r6X6eWYITC9V5q+yWCGAoFvcysBGbY9Q='", "'sha256-tQIkdAw2vqfQ1PjgUE4ML2A4pIV8SByQ1Seeehy6UIc='", "'sha256-4f86GpVBksx63uHEB4f15jGyhnVEOKjwqYxwnfDVOik='"], styles: ["'sha256-vv9IoKo7BSLbWcUHr3tNmfNVmm5L/9Cfn2H6LMk7/ow='", "'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='"] }.scripts.join(" ") + "; style-src 'self' " + { scripts: ["'sha256-xIb/Pa7FUshAu4ihwjchYcQpyCI8eS8kt40GpOAYCz8='", "'sha256-1BvDVWLr3QCC20uHhUDi27eDFVJpk1uMQJGIWKUks2I='", "'sha256-kmZE/W8I1iuTnl1b3yqzv9/DQZenPngd3NjSKMQl7y0='", "'sha256-Y8t0ntteBwH+PqGKUyjWW3CAk+BNJKSoQc0xZ68FjhM='", "'sha256-0O7iB6zr9x0r6X6eWYITC9V5q+yWCGAoFvcysBGbY9Q='", "'sha256-tQIkdAw2vqfQ1PjgUE4ML2A4pIV8SByQ1Seeehy6UIc='", "'sha256-4f86GpVBksx63uHEB4f15jGyhnVEOKjwqYxwnfDVOik='"], styles: ["'sha256-vv9IoKo7BSLbWcUHr3tNmfNVmm5L/9Cfn2H6LMk7/ow='", "'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='"] }.styles.join(" ") + "; connect-src 'self' " + env.DASHBOARD_URL.replace("https:", "wss:") + " " + env.RUNTIME_URL + " " + env.RUNTIME_URL.replace("https:", "wss:") + "; object-src 'none'; base-uri 'none'; frame-ancestors 'none'");
|
|
789
822
|
headers.set("referrer-policy", "no-referrer");
|
|
790
823
|
headers.set("x-content-type-options", "nosniff");
|
|
791
824
|
headers.set("cache-control", response.ok && u.pathname.startsWith("/_astro/") ? "private, max-age=31536000, immutable" : "private, no-store");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,r as t}from"./client.BeKNsjPN.js";import{At as n,S as r,_ as i,a,b as o,f as s,g as c,n as l,p as u,pt as d,s as f,u as p,v as m,x as h,y as g}from"./common.TAClb6G9.js";import{o as _}from"./index.astro_astro_type_script_index_0_lang.Blg5hU9g.js";var v=e(t(),1),y=n();function b(e){let t=c(e.project,e.env),[n,b]=(0,v.useState)(``),[x,S]=(0,v.useState)(),C=p(t+`/auth-users`+(n?`?cursor=`+encodeURIComponent(n):``)),w=p(t+`/auth-settings`),T=e.session.role===`owner`,E=(n,r,i,a=[])=>{S({title:r,description:`Passwords are never displayed. Changes revoke existing sessions.`,productionId:e.env===`prod`?e.project.id:void 0,fields:a,submit:async r=>{try{await u(t+`/`+n,{...i,...s(a,e=>new FormData(r).get(e)),confirm:e.project.id})}catch(e){throw e instanceof Error&&e.message===`INVALID_PASSWORD`?Error(`Password must contain 6–128 characters. Spaces are allowed; the password is not trimmed.`):e instanceof Error&&e.message===`REGISTRATION_UNAVAILABLE`?Error(`This login is already registered. Choose another login or manage the existing user.`):e}C.refresh(),w.refresh()}})},D={name:`password`,type:`password`,required:!0,description:`6–128 characters.`};return(0,y.jsxs)(`div`,{className:`space-y-4 p-4`,children:[(0,y.jsx)(`h1`,{className:`page-title`,children:`Auth`}),(0,y.jsx)(f,{loading:C.loading||w.loading,error:C.error||w.error}),w.data&&(0,y.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,y.jsx)(_,{status:w.data.enabled?`Enabled`:`Disabled`}),(0,y.jsx)(_,{status:w.data.registration?`Public registration`:`Registration closed`}),T&&(0,y.jsx)(d,{variant:`outline`,onClick:()=>E(`auth-configure`,`Configure authentication`,{},[{name:`enabled`,type:`boolean`,value:w.data.enabled},{name:`registration`,type:`boolean`,value:w.data.registration}]),children:`Configure`}),T&&(0,y.jsx)(d,{onClick:()=>E(`auth-create`,`Create user`,{},[{name:`login`,type:`string`,required:!0},D]),children:`Create user`})]}),C.data&&(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(i,{children:[(0,y.jsx)(h,{children:(0,y.jsxs)(r,{children:[(0,y.jsx)(o,{children:`Login`}),(0,y.jsx)(o,{children:`User ID`}),(0,y.jsx)(o,{children:`Status`}),(0,y.jsx)(o,{children:`Created`}),(0,y.jsx)(o,{children:`Actions`})]})}),(0,y.jsx)(m,{children:C.data.users.map(e=>(0,y.jsxs)(r,{children:[(0,y.jsx)(g,{children:e.login}),(0,y.jsx)(g,{children:(0,y.jsx)(`code`,{children:e.id})}),(0,y.jsx)(g,{children:(0,y.jsx)(_,{status:e.disabled?`Blocked`:`Active`})}),(0,y.jsx)(g,{children:new Date(e.createdAt).toLocaleString()}),(0,y.jsx)(g,{children:(0,y.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:T&&(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(d,{variant:`outline`,size:`sm`,onClick:()=>E(`auth-password`,`Change user password`,{id:e.id},[D]),children:`Change password`}),(0,y.jsx)(d,{variant:`outline`,size:`sm`,onClick:()=>E(`auth-revoke`,`Revoke all sessions`,{id:e.id}),children:`Revoke sessions`}),(0,y.jsx)(d,{variant:`outline`,size:`sm`,onClick:()=>E(`auth-disable`,e.disabled?`Unblock user`:`Block user`,{id:e.id,disabled:!e.disabled}),children:e.disabled?`Unblock`:`Block`})]})})})]},e.id))})]}),!C.data.users.length&&(0,y.jsx)(l,{title:`No users`}),(0,y.jsxs)(`div`,{className:`flex gap-2`,children:[(0,y.jsx)(d,{variant:`outline`,disabled:!n,onClick:()=>b(``),children:`First page`}),(0,y.jsx)(d,{variant:`outline`,disabled:!C.data.next,onClick:()=>b(C.data.next),children:`Next page`})]})]}),x&&(0,y.jsx)(a,{modal:x,onClose:()=>S(void 0)})]})}export{b as default};
|
package/dist/platform/dashboard/_astro/{cli-approval.C4-RBXQh.js → cli-approval.DwZVHdyo.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,r as t}from"./client.BeKNsjPN.js";import{A as n,
|
|
1
|
+
import{a as e,r as t}from"./client.BeKNsjPN.js";import{A as n,At as r,O as i,k as a,m as o,p as s,pt as c,q as l,r as u,t as d}from"./common.TAClb6G9.js";var f=e(t(),1),p=r();function m(){let e=new URLSearchParams(location.search).get(`cli`)??``,[t,r]=(0,f.useState)(``),[m,h]=(0,f.useState)([]),[g,_]=(0,f.useState)(`new`),[v,y]=(0,f.useState)(``),[b,x]=(0,f.useState)(``),[S,C]=(0,f.useState)(!1),[w,T]=(0,f.useState)(!1);(0,f.useEffect)(()=>{let t=new AbortController;return Promise.all([s(`cli/approval?id=`+encodeURIComponent(e),void 0,t.signal),s(`projects`,void 0,t.signal)]).then(([e,t])=>{r(e.code),h(t),_(`new`)}).catch(e=>{t.signal.aborted||x(o(e))}),()=>t.abort()},[e]);async function E(){C(!0),x(``);try{let t=g;if(t===`new`){let e=await s(`projects`,{name:v,customer:`Applications`,origins:[]});t=e.id,h(t=>[...t,{id:e.id,name:v,customer:`Applications`}]),_(t)}await s(`cli/approval`,{id:e,project:t}),T(!0)}catch(e){x(o(e))}finally{C(!1)}}return(0,p.jsxs)(`main`,{className:`mx-auto max-w-lg p-8`,children:[(0,p.jsx)(`h1`,{className:`mb-4 text-xl font-semibold`,children:`Connect Flare CLI`}),w?(0,p.jsx)(`p`,{children:`Connected. Return to your terminal. You can close this tab.`}):(0,p.jsxs)(a,{children:[(0,p.jsx)(`p`,{children:`Approve only a login you just started. Compare this code with your terminal:`}),(0,p.jsx)(`strong`,{className:`text-2xl font-mono`,children:t||`Loading…`}),(0,p.jsx)(`p`,{children:`Grants this computer deployment access to the selected project, including production. The credential expires in 30 days and can be revoked in Settings.`}),(0,p.jsxs)(i,{children:[(0,p.jsx)(n,{children:`Project`}),(0,p.jsx)(d,{label:`CLI project`,value:g,onChange:_,items:[...m.map(e=>({value:e.id,label:e.name})),{value:`new`,label:`Create new project`}]})]}),g===`new`&&(0,p.jsxs)(i,{children:[(0,p.jsx)(n,{children:`New project name`}),(0,p.jsx)(l,{value:v,onChange:e=>y(e.target.value),maxLength:100})]}),b&&(0,p.jsx)(u,{children:b}),(0,p.jsx)(c,{disabled:S||!t||!g||g===`new`&&!v.trim(),onClick:E,children:`Approve connection`})]})]})}export{m as default};
|