@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.
Files changed (21) hide show
  1. package/dist/cli/{chunk-r3ceakrn.js → chunk-y6e2h40n.js} +187 -20
  2. package/dist/cli/flare.js +15 -4
  3. package/dist/platform/admin.js +80 -47
  4. package/dist/platform/dashboard/_astro/auth-view.EASEF9BN.js +1 -0
  5. package/dist/platform/dashboard/_astro/{cli-approval.C4-RBXQh.js → cli-approval.DwZVHdyo.js} +1 -1
  6. package/dist/platform/dashboard/_astro/common.TAClb6G9.js +1 -0
  7. package/dist/platform/dashboard/_astro/{data-view.B2afgdXn.js → data-view.BaHEU4Xa.js} +1 -1
  8. package/dist/platform/dashboard/_astro/{functions-view.B0jPBNNy.js → functions-view.BGOYuDwu.js} +1 -1
  9. package/dist/platform/dashboard/_astro/index.astro_astro_type_script_index_0_lang.Blg5hU9g.js +3 -0
  10. package/dist/platform/dashboard/_astro/{input-group.BziAwKdZ.js → input-group.DUp-h0xx.js} +1 -1
  11. package/dist/platform/dashboard/_astro/{sdk-view.DAd5aPbd.js → sdk-view.2ErPn4cO.js} +1 -1
  12. package/dist/platform/dashboard/_astro/{services-view.BvkS7FHr.js → services-view.iZujitxl.js} +1 -1
  13. package/dist/platform/dashboard/_astro/settings-view.CqK-mrlB.js +3 -0
  14. package/dist/platform/dashboard/index.html +1 -1
  15. package/dist/platform/manifest.json +33 -33
  16. package/dist/platform/runtime.js +214 -35
  17. package/package.json +1 -1
  18. package/dist/platform/dashboard/_astro/auth-view.DxyBDw7Z.js +0 -1
  19. package/dist/platform/dashboard/_astro/common.D6YHScsr.js +0 -1
  20. package/dist/platform/dashboard/_astro/index.astro_astro_type_script_index_0_lang.BQ7pqT75.js +0 -3
  21. package/dist/platform/dashboard/_astro/settings-view.BXt0dexJ.js +0 -3
@@ -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.7",
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
- const connections = [...this.sockets.getConnections()];
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
- return this.serial(async () => {
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 new BackupStore(this.env.BUNDLES, this.env.FILES).list(this.config().scope, input.cursor);
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 new BackupStore(this.env.BUNDLES, this.env.FILES).remove(this.config().scope, input.id);
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
- return this.serial(async () => {
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
- return this.serial(async () => {
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@auersperg/flare",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Flare client, server declarations and development CLI",
@@ -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};