@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
package/dist/platform/runtime.js
CHANGED
|
@@ -526,10 +526,41 @@ class PasswordAuth {
|
|
|
526
526
|
return { userId: input.id };
|
|
527
527
|
}
|
|
528
528
|
}
|
|
529
|
+
|
|
530
|
+
// packages/core/src/maintenance.ts
|
|
531
|
+
function scopeForDeletion(value) {
|
|
532
|
+
if (typeof value !== "string")
|
|
533
|
+
fail("INVALID_INPUT");
|
|
534
|
+
const parts = value.split("/");
|
|
535
|
+
if (parts.length !== 2)
|
|
536
|
+
fail("INVALID_INPUT");
|
|
537
|
+
return `${uuid(parts[0])}/${environment(parts[1])}`;
|
|
538
|
+
}
|
|
539
|
+
async function markEnvironmentDeleting(ctx) {
|
|
540
|
+
ctx.storage.sql.exec("CREATE TABLE IF NOT EXISTS _flared_deleted (id INTEGER PRIMARY KEY)");
|
|
541
|
+
ctx.storage.sql.exec("INSERT OR IGNORE INTO _flared_deleted VALUES (0)");
|
|
542
|
+
await ctx.storage.sync();
|
|
543
|
+
}
|
|
544
|
+
async function eraseEnvironment(ctx) {
|
|
545
|
+
ctx.facets.delete("app");
|
|
546
|
+
await ctx.storage.deleteAlarm();
|
|
547
|
+
await ctx.storage.deleteAll();
|
|
548
|
+
ctx.storage.sql.exec("CREATE TABLE _flared_deleted (id INTEGER PRIMARY KEY)");
|
|
549
|
+
ctx.storage.sql.exec("INSERT INTO _flared_deleted VALUES (1)");
|
|
550
|
+
await ctx.storage.sync();
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// packages/core/src/dashboard-live.ts
|
|
554
|
+
function dashboardIdentity(value) {
|
|
555
|
+
const v = value;
|
|
556
|
+
if (!v || typeof v.email !== "string" || v.email.length > 254 || !/^\S+@\S+\.\S+$/.test(v.email) || !Number.isSafeInteger(v.expires) || v.expires <= Date.now() || v.expires > Date.now() + 600000)
|
|
557
|
+
fail("UNAUTHORIZED");
|
|
558
|
+
return { email: v.email.toLowerCase(), expires: v.expires };
|
|
559
|
+
}
|
|
529
560
|
// packages/core/package.json
|
|
530
561
|
var package_default = {
|
|
531
562
|
name: "@flare/platform",
|
|
532
|
-
version: "0.3.
|
|
563
|
+
version: "0.3.8",
|
|
533
564
|
type: "module",
|
|
534
565
|
license: "MIT",
|
|
535
566
|
private: true,
|
|
@@ -596,29 +627,6 @@ var documents = new WeakMap;
|
|
|
596
627
|
// packages/flare/src/sdk/backup.ts
|
|
597
628
|
var SNAPSHOT_BYTES = 4 * 1048576;
|
|
598
629
|
|
|
599
|
-
// packages/core/src/maintenance.ts
|
|
600
|
-
function scopeForDeletion(value) {
|
|
601
|
-
if (typeof value !== "string")
|
|
602
|
-
fail("INVALID_INPUT");
|
|
603
|
-
const parts = value.split("/");
|
|
604
|
-
if (parts.length !== 2)
|
|
605
|
-
fail("INVALID_INPUT");
|
|
606
|
-
return `${uuid(parts[0])}/${environment(parts[1])}`;
|
|
607
|
-
}
|
|
608
|
-
async function markEnvironmentDeleting(ctx) {
|
|
609
|
-
ctx.storage.sql.exec("CREATE TABLE IF NOT EXISTS _flared_deleted (id INTEGER PRIMARY KEY)");
|
|
610
|
-
ctx.storage.sql.exec("INSERT OR IGNORE INTO _flared_deleted VALUES (0)");
|
|
611
|
-
await ctx.storage.sync();
|
|
612
|
-
}
|
|
613
|
-
async function eraseEnvironment(ctx) {
|
|
614
|
-
ctx.facets.delete("app");
|
|
615
|
-
await ctx.storage.deleteAlarm();
|
|
616
|
-
await ctx.storage.deleteAll();
|
|
617
|
-
ctx.storage.sql.exec("CREATE TABLE _flared_deleted (id INTEGER PRIMARY KEY)");
|
|
618
|
-
ctx.storage.sql.exec("INSERT INTO _flared_deleted VALUES (1)");
|
|
619
|
-
await ctx.storage.sync();
|
|
620
|
-
}
|
|
621
|
-
|
|
622
630
|
// packages/core/src/files.ts
|
|
623
631
|
var types = new Set([
|
|
624
632
|
"text/plain",
|
|
@@ -904,6 +912,9 @@ class BackupStore {
|
|
|
904
912
|
this.bucket = bucket;
|
|
905
913
|
this.files = files;
|
|
906
914
|
}
|
|
915
|
+
async completed(scope, id) {
|
|
916
|
+
return !!(await this.bucket.head(backupPrefix(scope, id) + "manifest.json"))?.customMetadata?.sha256;
|
|
917
|
+
}
|
|
907
918
|
async read(scope, id) {
|
|
908
919
|
const object = await this.bucket.get(backupPrefix(scope, id) + "manifest.json");
|
|
909
920
|
if (!object)
|
|
@@ -1039,6 +1050,86 @@ class BackupStore {
|
|
|
1039
1050
|
}
|
|
1040
1051
|
}
|
|
1041
1052
|
|
|
1053
|
+
class BackupInventory {
|
|
1054
|
+
storage;
|
|
1055
|
+
store;
|
|
1056
|
+
scope;
|
|
1057
|
+
constructor(storage, store, scope) {
|
|
1058
|
+
this.storage = storage;
|
|
1059
|
+
this.store = store;
|
|
1060
|
+
this.scope = scope;
|
|
1061
|
+
storage.sql.exec("CREATE TABLE IF NOT EXISTS backup_inventory (id INTEGER PRIMARY KEY,cursor TEXT,complete INTEGER NOT NULL)");
|
|
1062
|
+
storage.sql.exec("INSERT OR IGNORE INTO backup_inventory VALUES (1,NULL,0)");
|
|
1063
|
+
storage.sql.exec("CREATE TABLE IF NOT EXISTS backup_index (id TEXT PRIMARY KEY,at INTEGER NOT NULL,summary TEXT NOT NULL)");
|
|
1064
|
+
storage.sql.exec("CREATE INDEX IF NOT EXISTS backup_index_at ON backup_index(at DESC,id DESC)");
|
|
1065
|
+
}
|
|
1066
|
+
async record(summary) {
|
|
1067
|
+
this.storage.sql.exec("INSERT OR REPLACE INTO backup_index VALUES (?,?,?)", summary.id, summary.at, JSON.stringify(summary));
|
|
1068
|
+
await this.storage.sync();
|
|
1069
|
+
return summary;
|
|
1070
|
+
}
|
|
1071
|
+
async remove(id) {
|
|
1072
|
+
this.storage.sql.exec("DELETE FROM backup_index WHERE id=?", id);
|
|
1073
|
+
this.storage.sql.exec("UPDATE backup_inventory SET cursor=NULL,complete=0 WHERE id=1");
|
|
1074
|
+
await this.storage.sync();
|
|
1075
|
+
return this.store.remove(this.scope, id);
|
|
1076
|
+
}
|
|
1077
|
+
async list(cursor) {
|
|
1078
|
+
if (cursor && (typeof cursor !== "string" || cursor.length > 2048))
|
|
1079
|
+
fail("INVALID_CURSOR");
|
|
1080
|
+
let before;
|
|
1081
|
+
if (cursor) {
|
|
1082
|
+
try {
|
|
1083
|
+
before = JSON.parse(atob(cursor));
|
|
1084
|
+
} catch {
|
|
1085
|
+
fail("INVALID_CURSOR");
|
|
1086
|
+
}
|
|
1087
|
+
if (!before || before.scope !== this.scope || !Number.isSafeInteger(before.at) || before.at < -1)
|
|
1088
|
+
fail("INVALID_CURSOR");
|
|
1089
|
+
uuid(before.id);
|
|
1090
|
+
}
|
|
1091
|
+
const checkpoint = this.storage.sql.exec("SELECT cursor,complete FROM backup_inventory WHERE id=1").one();
|
|
1092
|
+
if (!checkpoint.complete) {
|
|
1093
|
+
const result = await this.store.list(this.scope, checkpoint.cursor == null ? undefined : String(checkpoint.cursor));
|
|
1094
|
+
for (const item of result.page)
|
|
1095
|
+
this.storage.sql.exec("INSERT OR REPLACE INTO backup_index VALUES (?,?,?)", item.id, "at" in item ? item.at : -1, JSON.stringify(item));
|
|
1096
|
+
this.storage.sql.exec("UPDATE backup_inventory SET cursor=?,complete=? WHERE id=1", result.cursor, result.cursor ? 0 : 1);
|
|
1097
|
+
await this.storage.sync();
|
|
1098
|
+
}
|
|
1099
|
+
const complete = !!this.storage.sql.exec("SELECT complete FROM backup_inventory WHERE id=1").one().complete;
|
|
1100
|
+
const rows = before ? this.storage.sql.exec("SELECT * FROM backup_index WHERE at<? OR (at=? AND id<?) ORDER BY at DESC,id DESC LIMIT 21", before.at, before.at, before.id).toArray() : this.storage.sql.exec("SELECT * FROM backup_index ORDER BY at DESC,id DESC LIMIT 21").toArray();
|
|
1101
|
+
const page = rows.slice(0, 20).map((r) => JSON.parse(String(r.summary)));
|
|
1102
|
+
const last = rows[19];
|
|
1103
|
+
const newest = complete ? this.storage.sql.exec("SELECT summary FROM backup_index WHERE at>=0 ORDER BY at DESC,id DESC LIMIT 1").toArray()[0] : undefined;
|
|
1104
|
+
const latest = newest ? JSON.parse(String(newest.summary)) : null;
|
|
1105
|
+
if (latest && !await this.store.completed(this.scope, latest.id)) {
|
|
1106
|
+
this.storage.sql.exec("DELETE FROM backup_index WHERE id=?", latest.id);
|
|
1107
|
+
this.storage.sql.exec("UPDATE backup_inventory SET cursor=NULL,complete=0 WHERE id=1");
|
|
1108
|
+
await this.storage.sync();
|
|
1109
|
+
return {
|
|
1110
|
+
page: page.filter((r) => r.id !== latest.id),
|
|
1111
|
+
cursor: null,
|
|
1112
|
+
inventory: "partial",
|
|
1113
|
+
latest: null,
|
|
1114
|
+
recoveryPoint: "unknown",
|
|
1115
|
+
ageMs: null
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
return {
|
|
1119
|
+
page,
|
|
1120
|
+
cursor: rows.length > 20 ? btoa(JSON.stringify({
|
|
1121
|
+
scope: this.scope,
|
|
1122
|
+
at: Number(last.at),
|
|
1123
|
+
id: String(last.id)
|
|
1124
|
+
})) : null,
|
|
1125
|
+
inventory: complete ? "complete" : "partial",
|
|
1126
|
+
latest,
|
|
1127
|
+
recoveryPoint: !complete ? "unknown" : !latest ? "missing" : Date.now() - latest.at > 86400000 ? "overdue" : "within-24h",
|
|
1128
|
+
ageMs: latest ? Math.max(0, Date.now() - latest.at) : null
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1042
1133
|
// packages/core/src/observability.ts
|
|
1043
1134
|
class InvocationSummaries {
|
|
1044
1135
|
sql;
|
|
@@ -7949,6 +8040,10 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
7949
8040
|
connection.close(1008, "Unauthorized");
|
|
7950
8041
|
return;
|
|
7951
8042
|
}
|
|
8043
|
+
if (state.dashboard && !await this.env.ADMIN_API.dashboardAllowed(this.config().scope, state.dashboard.email)) {
|
|
8044
|
+
connection.close(1008, "Unauthorized");
|
|
8045
|
+
return;
|
|
8046
|
+
}
|
|
7952
8047
|
connection.setState(state);
|
|
7953
8048
|
await this.scheduleExpiry();
|
|
7954
8049
|
connection.send('{"type":"ready"}');
|
|
@@ -7956,6 +8051,10 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
7956
8051
|
onMessage: async (connection, message) => {
|
|
7957
8052
|
await this.serial(async () => {
|
|
7958
8053
|
try {
|
|
8054
|
+
if (connection.state?.dashboard) {
|
|
8055
|
+
connection.close(1002, "Read-only channel");
|
|
8056
|
+
return;
|
|
8057
|
+
}
|
|
7959
8058
|
const auth = this.connectionAuth(connection), state = connection.state;
|
|
7960
8059
|
const now = Date.now();
|
|
7961
8060
|
if (!state.windowAt || now - state.windowAt >= 60000) {
|
|
@@ -8307,6 +8406,7 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8307
8406
|
await this.arm();
|
|
8308
8407
|
await this.recover();
|
|
8309
8408
|
}
|
|
8409
|
+
await this.dashboardHint();
|
|
8310
8410
|
return { hash: metadata.release.hash, unchanged: false };
|
|
8311
8411
|
}
|
|
8312
8412
|
async arm() {
|
|
@@ -8357,7 +8457,7 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8357
8457
|
}
|
|
8358
8458
|
}
|
|
8359
8459
|
async scheduleExpiry(exclude) {
|
|
8360
|
-
const times = [...this.sockets.getConnections()].filter((c) => c.id !== exclude && c.state?.auth != null).map((c) => c.state?.expires ?? Date.now());
|
|
8460
|
+
const times = [...this.sockets.getConnections()].filter((c) => c.id !== exclude && (c.state?.auth != null || !!c.state?.dashboard)).map((c) => c.state?.expires ?? Date.now());
|
|
8361
8461
|
if (!times.length)
|
|
8362
8462
|
await this.lifecycle.jobs.cancel("expire");
|
|
8363
8463
|
else
|
|
@@ -8368,8 +8468,31 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8368
8468
|
singleflight: true
|
|
8369
8469
|
});
|
|
8370
8470
|
}
|
|
8471
|
+
async dashboardHint() {
|
|
8472
|
+
const allowed = new Map;
|
|
8473
|
+
for (const c of this.sockets.getConnections()) {
|
|
8474
|
+
const identity = c.state?.dashboard;
|
|
8475
|
+
if (!identity)
|
|
8476
|
+
continue;
|
|
8477
|
+
try {
|
|
8478
|
+
if (identity.expires <= Date.now()) {
|
|
8479
|
+
c.close(1008, "Expired");
|
|
8480
|
+
continue;
|
|
8481
|
+
}
|
|
8482
|
+
if (!allowed.has(identity.email))
|
|
8483
|
+
allowed.set(identity.email, await this.env.ADMIN_API.dashboardAllowed(this.config().scope, identity.email));
|
|
8484
|
+
if (!allowed.get(identity.email))
|
|
8485
|
+
c.close(1008, "Revoked");
|
|
8486
|
+
else
|
|
8487
|
+
c.send('{"type":"invalidate"}');
|
|
8488
|
+
} catch {
|
|
8489
|
+
c.close(1008, "Unavailable");
|
|
8490
|
+
}
|
|
8491
|
+
}
|
|
8492
|
+
}
|
|
8371
8493
|
async broadcast() {
|
|
8372
|
-
|
|
8494
|
+
await this.dashboardHint();
|
|
8495
|
+
const connections = [...this.sockets.getConnections()].filter((c) => !c.state?.dashboard);
|
|
8373
8496
|
if (!connections.length)
|
|
8374
8497
|
return;
|
|
8375
8498
|
const cache = new SnapshotCache;
|
|
@@ -8439,7 +8562,7 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8439
8562
|
const state = c.state;
|
|
8440
8563
|
if (!state)
|
|
8441
8564
|
c.close(1008, "Expired");
|
|
8442
|
-
else if (state.auth) {
|
|
8565
|
+
else if (state.auth || state.dashboard) {
|
|
8443
8566
|
if (state.expires === null || state.expires <= Date.now())
|
|
8444
8567
|
c.close(1008, "Expired");
|
|
8445
8568
|
else
|
|
@@ -8560,6 +8683,7 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8560
8683
|
fail("UNAUTHORIZED");
|
|
8561
8684
|
const result = await this.passwords().public(input.operation, input.body ?? {}, input.authorization?.slice(7) ?? null);
|
|
8562
8685
|
this.closeRevokedSockets();
|
|
8686
|
+
await this.dashboardHint();
|
|
8563
8687
|
return result;
|
|
8564
8688
|
});
|
|
8565
8689
|
}
|
|
@@ -8696,6 +8820,8 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8696
8820
|
if (kind !== "query" || outcome !== "ok")
|
|
8697
8821
|
await this.prune();
|
|
8698
8822
|
this.summaries.record({ at: Date.now(), path, kind, status: outcome, ms });
|
|
8823
|
+
if (kind !== "query")
|
|
8824
|
+
await this.dashboardHint();
|
|
8699
8825
|
}
|
|
8700
8826
|
async event(path, kind, outcome, ms) {
|
|
8701
8827
|
await this.serial(async () => this.record(path, kind, outcome, ms));
|
|
@@ -8722,7 +8848,7 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8722
8848
|
const store = new BackupStore(this.env.BUNDLES, this.env.FILES), scope = this.config().scope;
|
|
8723
8849
|
const existing = await store.read(scope, id);
|
|
8724
8850
|
if (existing)
|
|
8725
|
-
return store.summary(existing);
|
|
8851
|
+
return this.backupInventory().record(store.summary(existing));
|
|
8726
8852
|
this.assertQuiet();
|
|
8727
8853
|
await this.finishActivation();
|
|
8728
8854
|
const data = await this.facetAdmin("backup-export");
|
|
@@ -8741,7 +8867,7 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8741
8867
|
size: Number(row.size),
|
|
8742
8868
|
createdAt: Number(row.createdAt)
|
|
8743
8869
|
}));
|
|
8744
|
-
return store.create({
|
|
8870
|
+
return this.backupInventory().record(await store.create({
|
|
8745
8871
|
format: 1,
|
|
8746
8872
|
id,
|
|
8747
8873
|
scope,
|
|
@@ -8749,7 +8875,10 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8749
8875
|
bundle,
|
|
8750
8876
|
data,
|
|
8751
8877
|
authUsers: this.passwords().backupUsers()
|
|
8752
|
-
}, files);
|
|
8878
|
+
}, files));
|
|
8879
|
+
}
|
|
8880
|
+
backupInventory() {
|
|
8881
|
+
return new BackupInventory(this.ctx.storage, new BackupStore(this.env.BUNDLES, this.env.FILES), this.config().scope);
|
|
8753
8882
|
}
|
|
8754
8883
|
async restoreBackup(input) {
|
|
8755
8884
|
const scope = this.config().scope, source = scopeForDeletion(input.source);
|
|
@@ -8813,7 +8942,7 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8813
8942
|
}
|
|
8814
8943
|
async admin(op, input) {
|
|
8815
8944
|
await this.lifecycle.start();
|
|
8816
|
-
|
|
8945
|
+
const result = await this.serial(async () => {
|
|
8817
8946
|
if (this.removed)
|
|
8818
8947
|
fail("NOT_FOUND");
|
|
8819
8948
|
if (op.startsWith("auth-")) {
|
|
@@ -8859,7 +8988,7 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8859
8988
|
if (op === "backup-create")
|
|
8860
8989
|
return this.createBackup(input.id);
|
|
8861
8990
|
if (op === "backup-list")
|
|
8862
|
-
return
|
|
8991
|
+
return this.backupInventory().list(input.cursor);
|
|
8863
8992
|
if (op === "backup-pin") {
|
|
8864
8993
|
const target = scopeForDeletion(input.target), scope = this.config().scope;
|
|
8865
8994
|
if (target === scope || target.split("/")[0] !== scope.split("/")[0])
|
|
@@ -8878,7 +9007,7 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8878
9007
|
if (op === "backup-delete") {
|
|
8879
9008
|
if (this.ctx.storage.sql.exec("SELECT 1 FROM backup_pins WHERE id=? LIMIT 1", input.id).toArray().length)
|
|
8880
9009
|
fail("BACKUP_BUSY");
|
|
8881
|
-
return
|
|
9010
|
+
return this.backupInventory().remove(input.id);
|
|
8882
9011
|
}
|
|
8883
9012
|
if (op === "backup-restore")
|
|
8884
9013
|
return this.restoreBackup(input);
|
|
@@ -8944,6 +9073,25 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8944
9073
|
};
|
|
8945
9074
|
fail("INVALID_OPERATION");
|
|
8946
9075
|
});
|
|
9076
|
+
if ([
|
|
9077
|
+
"auth-create",
|
|
9078
|
+
"auth-password",
|
|
9079
|
+
"auth-disable",
|
|
9080
|
+
"auth-revoke",
|
|
9081
|
+
"auth-configure",
|
|
9082
|
+
"edit",
|
|
9083
|
+
"file-upload",
|
|
9084
|
+
"file-delete",
|
|
9085
|
+
"job-cancel",
|
|
9086
|
+
"cron-set",
|
|
9087
|
+
"cron-cancel",
|
|
9088
|
+
"backup-create",
|
|
9089
|
+
"backup-delete",
|
|
9090
|
+
"backup-restore",
|
|
9091
|
+
"release-rollback"
|
|
9092
|
+
].includes(op))
|
|
9093
|
+
await this.serial(() => this.dashboardHint());
|
|
9094
|
+
return result;
|
|
8947
9095
|
}
|
|
8948
9096
|
async job(job) {
|
|
8949
9097
|
await this.lifecycle.start();
|
|
@@ -8957,6 +9105,7 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8957
9105
|
return await promise;
|
|
8958
9106
|
} finally {
|
|
8959
9107
|
this.runningJobs.delete(job.id);
|
|
9108
|
+
await this.serial(() => this.dashboardHint());
|
|
8960
9109
|
}
|
|
8961
9110
|
}
|
|
8962
9111
|
async executeJob(job) {
|
|
@@ -8993,7 +9142,7 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
8993
9142
|
}
|
|
8994
9143
|
async fileCommand(op, input) {
|
|
8995
9144
|
await this.lifecycle.start();
|
|
8996
|
-
|
|
9145
|
+
const result = await this.serial(async () => {
|
|
8997
9146
|
if (input?.origin && !fileOriginAllowed(input.origin, this.config().origins, this.env.DASHBOARD_URL))
|
|
8998
9147
|
fail("FORBIDDEN");
|
|
8999
9148
|
this.assertAvailable();
|
|
@@ -9013,6 +9162,9 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
9013
9162
|
return this.files().complete(input.id);
|
|
9014
9163
|
fail("INVALID_OPERATION");
|
|
9015
9164
|
});
|
|
9165
|
+
if (["reserve", "delete", "complete"].includes(op))
|
|
9166
|
+
await this.serial(() => this.dashboardHint());
|
|
9167
|
+
return result;
|
|
9016
9168
|
}
|
|
9017
9169
|
cronRows() {
|
|
9018
9170
|
return this.ctx.storage.sql.exec("SELECT name,id,path,cron,enabled,schedulerId FROM crons ORDER BY name LIMIT 20").toArray().map((row) => ({ ...row, enabled: Number(row.enabled) === 1 }));
|
|
@@ -9073,7 +9225,7 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
9073
9225
|
}
|
|
9074
9226
|
async scheduleCommand(op, input) {
|
|
9075
9227
|
await this.lifecycle.start();
|
|
9076
|
-
|
|
9228
|
+
const result = await this.serial(async () => {
|
|
9077
9229
|
if (op === "set")
|
|
9078
9230
|
return this.setCron(input);
|
|
9079
9231
|
if (op === "cancel")
|
|
@@ -9082,6 +9234,9 @@ class EnvironmentSupervisor extends DurableObject {
|
|
|
9082
9234
|
return this.cronRows();
|
|
9083
9235
|
fail("INVALID_OPERATION");
|
|
9084
9236
|
});
|
|
9237
|
+
if (op !== "list")
|
|
9238
|
+
await this.serial(() => this.dashboardHint());
|
|
9239
|
+
return result;
|
|
9085
9240
|
}
|
|
9086
9241
|
}
|
|
9087
9242
|
|
|
@@ -9155,6 +9310,30 @@ class AppCapability extends WorkerEntrypoint {
|
|
|
9155
9310
|
}
|
|
9156
9311
|
|
|
9157
9312
|
class RuntimeControl extends WorkerEntrypoint {
|
|
9313
|
+
async fetch(request) {
|
|
9314
|
+
try {
|
|
9315
|
+
const url = new URL(request.url);
|
|
9316
|
+
if (url.host !== "dashboard.internal" || request.headers.get("upgrade")?.toLowerCase() !== "websocket")
|
|
9317
|
+
fail("FORBIDDEN");
|
|
9318
|
+
const scope = scopeForDeletion(url.pathname.slice(1));
|
|
9319
|
+
const identity = dashboardIdentity(JSON.parse(request.headers.get("x-flared-dashboard") ?? "null"));
|
|
9320
|
+
if (!await this.env.ADMIN_API.dashboardAllowed(scope, identity.email))
|
|
9321
|
+
fail("FORBIDDEN");
|
|
9322
|
+
return getSupervisor(this.env, scope).fetch(new Request("https://dashboard.internal/", {
|
|
9323
|
+
headers: {
|
|
9324
|
+
upgrade: "websocket",
|
|
9325
|
+
"x-flared-identity": stableJson({
|
|
9326
|
+
auth: null,
|
|
9327
|
+
expires: identity.expires,
|
|
9328
|
+
dashboard: identity
|
|
9329
|
+
})
|
|
9330
|
+
}
|
|
9331
|
+
}));
|
|
9332
|
+
} catch (error) {
|
|
9333
|
+
const result = status(error);
|
|
9334
|
+
return Response.json({ error: result.code }, { status: result.status });
|
|
9335
|
+
}
|
|
9336
|
+
}
|
|
9158
9337
|
async command(op, args) {
|
|
9159
9338
|
return capture(async () => {
|
|
9160
9339
|
if (![
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e,r as t}from"./client.BeKNsjPN.js";import{S as n,_ as r,a as i,b as a,f as o,g as s,mt 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.D6YHScsr.js";import{o as _}from"./index.astro_astro_type_script_index_0_lang.BQ7pqT75.js";var v=e(t(),1),y=c();function b(e){let t=s(e.project,e.env),[c,b]=(0,v.useState)(``),[x,S]=(0,v.useState)(),C=p(t+`/auth-users`+(c?`?cursor=`+encodeURIComponent(c):``)),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,...o(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)(r,{children:[(0,y.jsx)(h,{children:(0,y.jsxs)(n,{children:[(0,y.jsx)(a,{children:`Login`}),(0,y.jsx)(a,{children:`User ID`}),(0,y.jsx)(a,{children:`Status`}),(0,y.jsx)(a,{children:`Created`}),(0,y.jsx)(a,{children:`Actions`})]})}),(0,y.jsx)(m,{children:C.data.users.map(e=>(0,y.jsxs)(n,{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:!c,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)(i,{modal:x,onClose:()=>S(void 0)})]})}export{b as default};
|