@camstack/addon-admin-ui 1.2.430 → 1.2.432
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/assets/{AddonAssetIcon-BWUL7eDI.js → AddonAssetIcon-BUMe5IDK.js} +1 -1
- package/dist/assets/{Addons-2-Q9J_R-.js → Addons-DeuXr1KP.js} +1 -1
- package/dist/assets/{AdminPage-DMiMIHwu.js → AdminPage-BIr_CXzx.js} +1 -1
- package/dist/assets/{AdminSectionedPage-DKH0UpKh.js → AdminSectionedPage-BaIEUBzU.js} +1 -1
- package/dist/assets/{AiProfiles-DQ5YpTn5.js → AiProfiles-DXFjP3HN.js} +1 -1
- package/dist/assets/{BrokerForm-wEqc4cpx.js → BrokerForm-D73q6J9S.js} +1 -1
- package/dist/assets/{Build-DIaw3f01.js → Build-v9eZF0g2.js} +1 -1
- package/dist/assets/{Calendars-BpyELuD4.js → Calendars-DKauMIgJ.js} +1 -1
- package/dist/assets/{Cluster-CfJQLikz.js → Cluster-Bk29KZQL.js} +1 -1
- package/dist/assets/{ClusterStepModel-B_1igjPf.js → ClusterStepModel-C4t8EYyW.js} +1 -1
- package/dist/assets/{CompositionEditor-DYkQHem0.js → CompositionEditor-Bw5I7XNl.js} +1 -1
- package/dist/assets/{CompositionEditorPage-DDBZOcqy.js → CompositionEditorPage-DLat14IE.js} +1 -1
- package/dist/assets/{Data-7qDFkx-Q.js → Data-C54aVzn0.js} +1 -1
- package/dist/assets/{DeviceDetail-Dy2NTHbz.js → DeviceDetail-UyQq0okE.js} +1 -1
- package/dist/assets/{DeviceDoActionFields-DmKzugUt.js → DeviceDoActionFields-BZPhSBgg.js} +1 -1
- package/dist/assets/{Devices-COzPWkpB.js → Devices-C2gBbpLK.js} +1 -1
- package/dist/assets/{EmbedPlayerPage-CD4jAYaX.js → EmbedPlayerPage-BMNLT-PN.js} +1 -1
- package/dist/assets/Identity-J4MwstTs.js +1 -0
- package/dist/assets/{IntegrationDetail-CP91fqQF.js → IntegrationDetail-lkVpMjqt.js} +1 -1
- package/dist/assets/{IntegrationHub-DMx7fLHV.js → IntegrationHub-B9rzgT5H.js} +1 -1
- package/dist/assets/{Integrations-Blb_S3_f.js → Integrations-CmqbxDj8.js} +1 -1
- package/dist/assets/{Integrations-BzqzbXQ2.js → Integrations-DhQ3DIcI.js} +1 -1
- package/dist/assets/{Library-Cqg-Y18g.js → Library-BvJfjpJY.js} +1 -1
- package/dist/assets/{Locations-EQXaWxpY.js → Locations-CgI3IeYv.js} +1 -1
- package/dist/assets/{Logs-hQ_F2BI5.js → Logs-DYZ-Wx2b.js} +1 -1
- package/dist/assets/{MaskShapeCanvas-BkBmWJHu.js → MaskShapeCanvas-D5vpR-SP.js} +1 -1
- package/dist/assets/{MotionZonesSettings-Ctl81l__.js → MotionZonesSettings-BEGvKMSe.js} +1 -1
- package/dist/assets/{MyAccess-D8vfWKFy.js → MyAccess-CfsgcW14.js} +1 -1
- package/dist/assets/Network-CfV9qDQb.js +1 -0
- package/dist/assets/{NodeAddonsSettingsPanel-DMEeQbDW.js → NodeAddonsSettingsPanel-Bm5HTEFT.js} +1 -1
- package/dist/assets/{Notifications-BtrpoxXL.js → Notifications-QmfzpIs2.js} +1 -1
- package/dist/assets/{PrivacyMaskSettings-DEbHPS5X.js → PrivacyMaskSettings-Dd5t3M3f.js} +1 -1
- package/dist/assets/{Recordings-BrGa72HR.js → Recordings-Crol71OX.js} +1 -1
- package/dist/assets/{ScanNetworkModal-CTjVwV69.js → ScanNetworkModal-CPFtW5pZ.js} +1 -1
- package/dist/assets/{SceneMonitorEditor-DgNqz_fJ.js → SceneMonitorEditor-kP-ZQsfj.js} +1 -1
- package/dist/assets/{SessionsList-CzSpm8u5.js → SessionsList-CTV6uO5n.js} +1 -1
- package/dist/assets/{Showroom-DC0Rdsfa.js → Showroom-DWJgeOiz.js} +1 -1
- package/dist/assets/{System-ByMuYkyz.js → System-CRn_eq5a.js} +1 -1
- package/dist/assets/{Terminal-CLof2QSK.js → Terminal-Dh_sliYJ.js} +1 -1
- package/dist/assets/{Viewer-CLEBXqqR.js → Viewer-B4ctSRpW.js} +1 -1
- package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-CxVe8UPZ.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-DNje-LmA.js} +1 -1
- package/dist/assets/{addon-icons-context-B6iVhWXv.js → addon-icons-context-D5fUq03j.js} +1 -1
- package/dist/assets/{archive-WKa1b0o1.js → archive-D75KchjA.js} +1 -1
- package/dist/assets/{authed-media-B0bKjjhb.js → authed-media-D2AKdsuc.js} +1 -1
- package/dist/assets/{bell-off-DQeMLsEK.js → bell-off-C1XQJfLA.js} +1 -1
- package/dist/assets/{board-model-DIWD11Nh.js → board-model-DR9pUgyG.js} +1 -1
- package/dist/assets/{boxes-DMqqPR7T.js → boxes-L_1M15a4.js} +1 -1
- package/dist/assets/circle-slash-jUACStpz.js +1 -0
- package/dist/assets/client-DSjr7-vB.js +358 -0
- package/dist/assets/{cloud-upload-8Rtciaox.js → cloud-upload-CX-DdQts.js} +1 -1
- package/dist/assets/crop--XlYmzNa.js +1 -0
- package/dist/assets/{device-integration-icon-BRxzTJPF.js → device-integration-icon-Bcm7Z6jY.js} +1 -1
- package/dist/assets/device-picker-model-C6sGbx0i.js +1 -0
- package/dist/assets/{dist-BiqTDJU8.js → dist-CWmjwgsm.js} +1 -1
- package/dist/assets/dist-DvZ0rTuL.js +358 -0
- package/dist/assets/{eraser-AtBM6hp_.js → eraser-BGE9R8_a.js} +1 -1
- package/dist/assets/{hard-drive-download-COMXm0vT.js → hard-drive-download-Bg2SJyCf.js} +1 -1
- package/dist/assets/{history-CNH6Hwzl.js → history-G5nuVYvw.js} +1 -1
- package/dist/assets/{hostInit-Cn-rvYrx.js → hostInit-rwNOXVdE.js} +1 -1
- package/dist/assets/i18n-CxM-3m0e.js +1 -0
- package/dist/assets/icons-C3xAUFxL.js +1 -0
- package/dist/assets/{index-IBVizVRk.js → index-C4TCQi7u.js} +3 -3
- package/dist/assets/key-D4l0MPuJ.js +1 -0
- package/dist/assets/{key-round-DhUJVMTJ.js → key-round-By7HqlM-.js} +1 -1
- package/dist/assets/{layers-BNtkJA8b.js → layers-CrP3_-zE.js} +1 -1
- package/dist/assets/{linked-devices-model-DXpryeZj.js → linked-devices-model-DzomDthM.js} +1 -1
- package/dist/assets/{list-ordered-DE6semVl.js → list-ordered-Q101NLCk.js} +1 -1
- package/dist/assets/{location-owned-usage-4K4CCW-I.js → location-owned-usage-BHGw7ZrT.js} +1 -1
- package/dist/assets/{log-in-k_3OshGq.js → log-in-CVN3HrLY.js} +1 -1
- package/dist/assets/{memory-stick--pKqiId8.js → memory-stick-DNkEIVDl.js} +1 -1
- package/dist/assets/{message-square-plus-DD_p-LBd.js → message-square-plus-BM5D9v7J.js} +1 -1
- package/dist/assets/{mf-entry-bootstrap-0-b5dba7ed.js → mf-entry-bootstrap-0-22a1f3ed.js} +2 -2
- package/dist/assets/{mic-off-abkMqo-Z.js → mic-off-D6Lc5R7l.js} +1 -1
- package/dist/assets/{monitor-off-Btwz3hik.js → monitor-off-RKGlWndF.js} +1 -1
- package/dist/assets/{monitor-smartphone-CewWbhfo.js → monitor-smartphone-BaBns5JL.js} +1 -1
- package/dist/assets/mount-C6sGbx0i.js +1 -0
- package/dist/assets/{notification-schedule-field-DALe8QBw.js → notification-schedule-field-MLRs0icT.js} +1 -1
- package/dist/assets/{octagon-alert-B-H38qMt.js → octagon-alert-DYq78E92.js} +1 -1
- package/dist/assets/{player-overlays-CTjp5_0E.js → player-overlays-BtrsPwRp.js} +1 -1
- package/dist/assets/{recycle-om2m5njd.js → recycle-CAwzzZqf.js} +1 -1
- package/dist/assets/remoteEntry-CBD0Jyz7.js +1 -0
- package/dist/assets/{scan-eye-RPomUFSt.js → scan-eye-BpAY7eTH.js} +1 -1
- package/dist/assets/{scissors-oLO0ZxF2.js → scissors-Be9z9iTK.js} +1 -1
- package/dist/assets/{search-x-By7QfPjb.js → search-x-ExdMvYlD.js} +1 -1
- package/dist/assets/{send-CWuZ-VHG.js → send-NfLeCT7d.js} +1 -1
- package/dist/assets/{settings-2-DrkSvhIU.js → settings-2-AuxgmdTU.js} +1 -1
- package/dist/assets/{speaker-Bi1HndpO.js → speaker-DcnALnai.js} +1 -1
- package/dist/assets/src-D7fj-1-O.js +67 -0
- package/dist/assets/{timer-reset-CX_75T7d.js → timer-reset-b5RtzF8y.js} +1 -1
- package/dist/assets/{ungroup-CQ9vK9Rb.js → ungroup-B_FqAxhG.js} +1 -1
- package/dist/assets/{unlink-DRIRCsre.js → unlink-_FaVL_1F.js} +1 -1
- package/dist/assets/{usb-CSAqtgKZ.js → usb-S8PAfCjY.js} +1 -1
- package/dist/assets/{use-scenes-feed-B3oVDWHB.js → use-scenes-feed-CCiJNPg1.js} +1 -1
- package/dist/assets/{use-stationary-objects-D8Q3fSKJ.js → use-stationary-objects-U550m5tx.js} +1 -1
- package/dist/assets/useEventInvalidation-C6sGbx0i.js +1 -0
- package/dist/assets/{useNodeUrlSelection-CbhdLJkk.js → useNodeUrlSelection-Dr_4iDNb.js} +1 -1
- package/dist/assets/user-round-CJ5fRzDO.js +1 -0
- package/dist/assets/{user-x-fVEkryXF.js → user-x-CEMv46WL.js} +1 -1
- package/dist/assets/{video-C9XbLIGZ.js → video-fjSOa6Uz.js} +1 -1
- package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-CWUvAVp2.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-DD6rNG_X.js} +2 -2
- package/dist/index.html +10 -10
- package/dist/server/addon.js +178 -27
- package/package.json +1 -1
- package/dist/assets/Identity-BnBFoIrz.js +0 -1
- package/dist/assets/Network-BYFc8leF.js +0 -1
- package/dist/assets/circle-slash-HW5fnitA.js +0 -1
- package/dist/assets/client-Ck3kx2Z0.js +0 -358
- package/dist/assets/crop-BCuJfQIp.js +0 -1
- package/dist/assets/device-picker-model-DUsOqTKW.js +0 -1
- package/dist/assets/dist-D4jmeWXJ.js +0 -358
- package/dist/assets/file-up-DvSwIdQI.js +0 -1
- package/dist/assets/i18n-DWijAvai.js +0 -1
- package/dist/assets/icons-BzZe_Qg7.js +0 -1
- package/dist/assets/key-B62K9GgX.js +0 -1
- package/dist/assets/mount-DUsOqTKW.js +0 -1
- package/dist/assets/remoteEntry-Dmuue8--.js +0 -1
- package/dist/assets/src-CoNs4bys.js +0 -67
- package/dist/assets/useEventInvalidation-DUsOqTKW.js +0 -1
- package/dist/assets/user-round-DhADiv-t.js +0 -1
package/dist/server/addon.js
CHANGED
|
@@ -5388,7 +5388,7 @@ var ZodIssueCode = {
|
|
|
5388
5388
|
var ZodFirstPartyTypeKind;
|
|
5389
5389
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5390
5390
|
//#endregion
|
|
5391
|
-
//#region ../types/dist/sleep-
|
|
5391
|
+
//#region ../types/dist/sleep-bwsTj221.mjs
|
|
5392
5392
|
/**
|
|
5393
5393
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5394
5394
|
* window to float samples (D455).
|
|
@@ -6639,7 +6639,6 @@ function createHandle(raw, opts) {
|
|
|
6639
6639
|
const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
|
|
6640
6640
|
const readAddonStoreResult = raw.readAddonStoreResult;
|
|
6641
6641
|
const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
|
|
6642
|
-
const readDeviceStoreBatch = raw.readDeviceStoreBatch;
|
|
6643
6642
|
const overrides = {
|
|
6644
6643
|
readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
|
|
6645
6644
|
...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
|
|
@@ -6663,12 +6662,12 @@ function createHandle(raw, opts) {
|
|
|
6663
6662
|
};
|
|
6664
6663
|
} } : {},
|
|
6665
6664
|
readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
|
|
6666
|
-
|
|
6667
|
-
const rows = await readDeviceStoreBatch
|
|
6665
|
+
readDeviceStoreBatch: async (deviceIds) => {
|
|
6666
|
+
const rows = await raw.readDeviceStoreBatch(deviceIds);
|
|
6668
6667
|
const out = /* @__PURE__ */ new Map();
|
|
6669
6668
|
for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
|
|
6670
6669
|
return out;
|
|
6671
|
-
}
|
|
6670
|
+
},
|
|
6672
6671
|
writeAddonStore: async (patch) => {
|
|
6673
6672
|
await writeAddon(patch);
|
|
6674
6673
|
},
|
|
@@ -7495,7 +7494,7 @@ var BaseAddon = class {
|
|
|
7495
7494
|
return this._addonSecretTree;
|
|
7496
7495
|
}
|
|
7497
7496
|
deviceSecretTree() {
|
|
7498
|
-
this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
|
|
7497
|
+
this._deviceSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.deviceSettingsSchema()), this.secretStatePaths);
|
|
7499
7498
|
return this._deviceSecretTree;
|
|
7500
7499
|
}
|
|
7501
7500
|
/**
|
|
@@ -7571,42 +7570,135 @@ var BaseAddon = class {
|
|
|
7571
7570
|
this[timerOf] = timer;
|
|
7572
7571
|
}
|
|
7573
7572
|
/**
|
|
7574
|
-
* Wrap `ctx.settings.readAddonStore()` with a
|
|
7575
|
-
*
|
|
7576
|
-
* swap, or any race where the SqliteSettingsBackend is between shutdown and
|
|
7577
|
-
* re-initialize) doesn't propagate up into `initialize()` and leave the
|
|
7573
|
+
* Wrap `ctx.settings.readAddonStore()` with a retry budget so a transient
|
|
7574
|
+
* settings-store outage doesn't propagate up into `initialize()` and leave the
|
|
7578
7575
|
* addon permanently broken.
|
|
7579
7576
|
*
|
|
7580
|
-
*
|
|
7581
|
-
*
|
|
7582
|
-
* `
|
|
7577
|
+
* Two kinds of outage, two budgets:
|
|
7578
|
+
* - the store's own restart fingerprints (`SqliteSettingsBackend not
|
|
7579
|
+
* initialized`, `provider not available`): a short budget, then the addon
|
|
7580
|
+
* falls back to `{}` (defaults) - its first successful patch rehydrates;
|
|
7581
|
+
* - a TRANSPORT failure (the hub is restarting while this runner boots:
|
|
7582
|
+
* `discovery deadline exceeded`, `parent could not route`, a refused or
|
|
7583
|
+
* timed-out connection). Since D870 a runner's settings read THROWS instead
|
|
7584
|
+
* of answering `{}`, so without a budget a hub restart ends in a respawn
|
|
7585
|
+
* loop. A long, bounded budget; only after it the error propagates - the
|
|
7586
|
+
* addon never runs on defaults for a store it could not ask.
|
|
7587
|
+
* Anything else propagates immediately so real bugs surface.
|
|
7583
7588
|
*/
|
|
7584
7589
|
async readAddonStoreWithRetry() {
|
|
7585
7590
|
const settings = this.rawSettings();
|
|
7586
7591
|
if (!settings) return {};
|
|
7587
|
-
const delaysMs = [
|
|
7588
|
-
150,
|
|
7589
|
-
350,
|
|
7590
|
-
600,
|
|
7591
|
-
900
|
|
7592
|
-
];
|
|
7593
7592
|
let lastErr;
|
|
7594
|
-
for (let attempt = 0
|
|
7593
|
+
for (let attempt = 0;; attempt++) try {
|
|
7595
7594
|
const stored = await settings.readAddonStore() ?? {};
|
|
7596
7595
|
this.settingsStoreReady = true;
|
|
7597
7596
|
return stored;
|
|
7598
7597
|
} catch (err) {
|
|
7599
7598
|
lastErr = err;
|
|
7600
|
-
const
|
|
7601
|
-
if (
|
|
7602
|
-
|
|
7603
|
-
|
|
7599
|
+
const kind = classifyStoreError(err);
|
|
7600
|
+
if (kind === "permanent") throw err;
|
|
7601
|
+
const delay = (kind === "transport" ? TRANSPORT_RETRY_DELAYS_MS : RESTART_RETRY_DELAYS_MS)[attempt];
|
|
7602
|
+
if (delay === void 0) {
|
|
7603
|
+
if (kind === "transport") throw err;
|
|
7604
|
+
break;
|
|
7605
|
+
}
|
|
7606
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
7604
7607
|
}
|
|
7605
7608
|
this.settingsStoreReady = false;
|
|
7606
7609
|
this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
|
|
7607
7610
|
return {};
|
|
7608
7611
|
}
|
|
7609
7612
|
};
|
|
7613
|
+
/** The settings store's own restart fingerprints. */
|
|
7614
|
+
var RESTART_RETRY_DELAYS_MS = [
|
|
7615
|
+
150,
|
|
7616
|
+
350,
|
|
7617
|
+
600,
|
|
7618
|
+
900
|
|
7619
|
+
];
|
|
7620
|
+
/** A hub restarting under a booting runner: ~100 s in total. */
|
|
7621
|
+
var TRANSPORT_RETRY_DELAYS_MS = [
|
|
7622
|
+
150,
|
|
7623
|
+
350,
|
|
7624
|
+
600,
|
|
7625
|
+
900,
|
|
7626
|
+
2e3,
|
|
7627
|
+
5e3,
|
|
7628
|
+
1e4,
|
|
7629
|
+
2e4,
|
|
7630
|
+
3e4,
|
|
7631
|
+
3e4
|
|
7632
|
+
];
|
|
7633
|
+
var RESTART_FINGERPRINTS = ["SqliteSettingsBackend not initialized", "provider not available"];
|
|
7634
|
+
/** Node / socket error codes of a connection that did not happen or broke. */
|
|
7635
|
+
var TRANSPORT_CODES = new Set([
|
|
7636
|
+
"ECONNREFUSED",
|
|
7637
|
+
"ECONNRESET",
|
|
7638
|
+
"ETIMEDOUT",
|
|
7639
|
+
"EPIPE",
|
|
7640
|
+
"EHOSTUNREACH",
|
|
7641
|
+
"ENETUNREACH"
|
|
7642
|
+
]);
|
|
7643
|
+
/** tRPC error codes that mean "the other side is not answering now", not "your call is wrong". */
|
|
7644
|
+
var UNAVAILABLE_TRPC_CODES = new Set([
|
|
7645
|
+
"TIMEOUT",
|
|
7646
|
+
"SERVICE_UNAVAILABLE",
|
|
7647
|
+
"BAD_GATEWAY",
|
|
7648
|
+
"GATEWAY_TIMEOUT"
|
|
7649
|
+
]);
|
|
7650
|
+
/** The exact phrases OUR transport raises when it cannot reach the hub. */
|
|
7651
|
+
var TRANSPORT_PHRASES = [
|
|
7652
|
+
"discovery deadline exceeded",
|
|
7653
|
+
"parent could not route child unowned cap call",
|
|
7654
|
+
"UDS request timed out after",
|
|
7655
|
+
"channel closed"
|
|
7656
|
+
];
|
|
7657
|
+
/** Moleculer error `type`s of a call that could not be served right now. */
|
|
7658
|
+
var MOLECULER_TRANSIENT_TYPES = new Set([
|
|
7659
|
+
"REQUEST_TIMEOUT",
|
|
7660
|
+
"SERVICE_NOT_FOUND",
|
|
7661
|
+
"SERVICE_NOT_AVAILABLE",
|
|
7662
|
+
"REQUEST_REJECTED",
|
|
7663
|
+
"BROKER_DISCONNECTED"
|
|
7664
|
+
]);
|
|
7665
|
+
function stringProp(value, key) {
|
|
7666
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
7667
|
+
const found = Reflect.get(value, key);
|
|
7668
|
+
return typeof found === "string" ? found : void 0;
|
|
7669
|
+
}
|
|
7670
|
+
/** The code an error carries: its own, its cause's, or a tRPC `data.code`. */
|
|
7671
|
+
function errorCode(err) {
|
|
7672
|
+
const out = {};
|
|
7673
|
+
const cause = typeof err === "object" && err !== null ? Reflect.get(err, "cause") : void 0;
|
|
7674
|
+
const data = typeof err === "object" && err !== null ? Reflect.get(err, "data") : void 0;
|
|
7675
|
+
const node = stringProp(err, "code") ?? stringProp(cause, "code");
|
|
7676
|
+
if (node !== void 0) out.node = node;
|
|
7677
|
+
const trpc = stringProp(data, "code");
|
|
7678
|
+
if (trpc !== void 0) out.trpc = trpc;
|
|
7679
|
+
return out;
|
|
7680
|
+
}
|
|
7681
|
+
/**
|
|
7682
|
+
* Is a settings read failure worth retrying, and with which budget? Only
|
|
7683
|
+
* EXPLICIT signals count: a socket error code, a tRPC "unavailable" code, or
|
|
7684
|
+
* one of the exact phrases our own transport raises. A message that merely
|
|
7685
|
+
* contains "timed out" or "unreachable" is not evidence of anything, and
|
|
7686
|
+
* `settings store not wired` is a wiring bug that retrying cannot fix.
|
|
7687
|
+
*/
|
|
7688
|
+
function classifyStoreError(err) {
|
|
7689
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7690
|
+
if (RESTART_FINGERPRINTS.some((f) => message.includes(f))) return "restart";
|
|
7691
|
+
const code = errorCode(err);
|
|
7692
|
+
if (code.node !== void 0 && TRANSPORT_CODES.has(code.node)) return "transport";
|
|
7693
|
+
if (code.trpc !== void 0 && UNAVAILABLE_TRPC_CODES.has(code.trpc)) return "transport";
|
|
7694
|
+
if (typeof err === "object" && err !== null) {
|
|
7695
|
+
if (Reflect.get(err, "retryable") === true) return "transport";
|
|
7696
|
+
const type = stringProp(err, "type");
|
|
7697
|
+
if (type !== void 0 && MOLECULER_TRANSIENT_TYPES.has(type)) return "transport";
|
|
7698
|
+
}
|
|
7699
|
+
if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
|
|
7700
|
+
return "permanent";
|
|
7701
|
+
}
|
|
7610
7702
|
/**
|
|
7611
7703
|
* Collect the keys of every field marked `perNode: true`, recursing into
|
|
7612
7704
|
* layout containers (`group` fields and `sub-tabs` tabs) the same way
|
|
@@ -8179,7 +8271,7 @@ object({
|
|
|
8179
8271
|
params: record(string(), union([string(), number()])).optional()
|
|
8180
8272
|
});
|
|
8181
8273
|
//#endregion
|
|
8182
|
-
//#region ../types/dist/err-msg
|
|
8274
|
+
//#region ../types/dist/err-msg--2RrYiLO.mjs
|
|
8183
8275
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
8184
8276
|
DeviceType["Camera"] = "camera";
|
|
8185
8277
|
DeviceType["Hub"] = "hub";
|
|
@@ -8448,6 +8540,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
8448
8540
|
/** Virtual HA toggle (input_boolean.*) — distinguishable from a
|
|
8449
8541
|
* real Switch device for UI rendering / export adapters. */
|
|
8450
8542
|
DeviceRole["BinaryHelper"] = "binary-helper";
|
|
8543
|
+
/** A smart plug: a `switch` whose source says it is an outlet (HA
|
|
8544
|
+
* `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
|
|
8545
|
+
DeviceRole["Outlet"] = "outlet";
|
|
8451
8546
|
/** Generic motion / occupancy / moving event source. Distinct from
|
|
8452
8547
|
* the camera accessory PirSensor role: that one is a camera child;
|
|
8453
8548
|
* this is a standalone HA / 3rd-party motion sensor. */
|
|
@@ -8515,7 +8610,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
8515
8610
|
return DeviceRole;
|
|
8516
8611
|
}({});
|
|
8517
8612
|
//#endregion
|
|
8518
|
-
//#region ../types/dist/composition-
|
|
8613
|
+
//#region ../types/dist/composition-DOwQB_Ua.mjs
|
|
8519
8614
|
/**
|
|
8520
8615
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
8521
8616
|
*
|
|
@@ -15837,6 +15932,28 @@ id: union([string(), number()]) }), {
|
|
|
15837
15932
|
kind: "mutation",
|
|
15838
15933
|
auth: "admin"
|
|
15839
15934
|
});
|
|
15935
|
+
/** One table of the settings database. */
|
|
15936
|
+
var StorageTableSchema = object({
|
|
15937
|
+
/** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
|
|
15938
|
+
name: string(),
|
|
15939
|
+
/** The owner prefix of a scoped name (`recorder` for `recorder:addon-settings`), else null. */
|
|
15940
|
+
owner: string().nullable(),
|
|
15941
|
+
rows: number().int().nonnegative(),
|
|
15942
|
+
/** Bytes the table's pages occupy (`dbstat`); null when the engine cannot measure. */
|
|
15943
|
+
bytes: number().int().nonnegative().nullable(),
|
|
15944
|
+
/** Declared by an addon since this engine process started (a running addon claims it). */
|
|
15945
|
+
declaredThisBoot: boolean()
|
|
15946
|
+
});
|
|
15947
|
+
method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
|
|
15948
|
+
collection: string().min(1),
|
|
15949
|
+
expectRows: number().int().nonnegative()
|
|
15950
|
+
}), object({
|
|
15951
|
+
dropped: literal(true),
|
|
15952
|
+
rows: number().int().nonnegative()
|
|
15953
|
+
}), {
|
|
15954
|
+
kind: "mutation",
|
|
15955
|
+
auth: "admin"
|
|
15956
|
+
});
|
|
15840
15957
|
/**
|
|
15841
15958
|
* `keychain` — the hub's encrypted store of secret values (D787).
|
|
15842
15959
|
*
|
|
@@ -16378,6 +16495,16 @@ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}),
|
|
|
16378
16495
|
kind: "mutation",
|
|
16379
16496
|
auth: "admin",
|
|
16380
16497
|
caller: "required"
|
|
16498
|
+
}), method(object({
|
|
16499
|
+
ref: string().min(1),
|
|
16500
|
+
value: string().min(1)
|
|
16501
|
+
}), object({
|
|
16502
|
+
ref: string(),
|
|
16503
|
+
version: number().int()
|
|
16504
|
+
}), {
|
|
16505
|
+
kind: "mutation",
|
|
16506
|
+
auth: "admin",
|
|
16507
|
+
caller: "required"
|
|
16381
16508
|
}), method(object({ refs: array(string().min(1)).max(1e3) }), object({
|
|
16382
16509
|
deleted: array(string()).readonly(),
|
|
16383
16510
|
kept: array(object({
|
|
@@ -52823,6 +52950,12 @@ object({
|
|
|
52823
52950
|
/** Horizontal louver swing on/off. Null when the device has no
|
|
52824
52951
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
52825
52952
|
swingHorizontal: boolean().nullable(),
|
|
52953
|
+
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
52954
|
+
minTemp: number().nullable().optional(),
|
|
52955
|
+
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
52956
|
+
maxTemp: number().nullable().optional(),
|
|
52957
|
+
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
52958
|
+
tempStep: number().positive().nullable().optional(),
|
|
52826
52959
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
52827
52960
|
lastFetchedAt: number()
|
|
52828
52961
|
});
|
|
@@ -63730,6 +63863,12 @@ Object.freeze({
|
|
|
63730
63863
|
addonId: null,
|
|
63731
63864
|
access: "create"
|
|
63732
63865
|
},
|
|
63866
|
+
"keychainAdmin.importSecret": {
|
|
63867
|
+
capName: "keychain-admin",
|
|
63868
|
+
capScope: "system",
|
|
63869
|
+
addonId: null,
|
|
63870
|
+
access: "create"
|
|
63871
|
+
},
|
|
63733
63872
|
"keychainAdmin.listAudit": {
|
|
63734
63873
|
capName: "keychain-admin",
|
|
63735
63874
|
capScope: "system",
|
|
@@ -67516,6 +67655,18 @@ Object.freeze({
|
|
|
67516
67655
|
addonId: null,
|
|
67517
67656
|
access: "create"
|
|
67518
67657
|
},
|
|
67658
|
+
"storageAdmin.dropCollection": {
|
|
67659
|
+
capName: "storage-admin",
|
|
67660
|
+
capScope: "system",
|
|
67661
|
+
addonId: null,
|
|
67662
|
+
access: "delete"
|
|
67663
|
+
},
|
|
67664
|
+
"storageAdmin.listTables": {
|
|
67665
|
+
capName: "storage-admin",
|
|
67666
|
+
capScope: "system",
|
|
67667
|
+
addonId: null,
|
|
67668
|
+
access: "view"
|
|
67669
|
+
},
|
|
67519
67670
|
"storageEvictable.evict": {
|
|
67520
67671
|
capName: "storage-evictable",
|
|
67521
67672
|
capScope: "system",
|
|
@@ -72688,7 +72839,7 @@ var AdminUIAddon = class extends BaseAddon {
|
|
|
72688
72839
|
capability: adminUiCapability,
|
|
72689
72840
|
provider: {
|
|
72690
72841
|
getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
|
|
72691
|
-
getVersion: async () => ({ version: "1.2.
|
|
72842
|
+
getVersion: async () => ({ version: "1.2.432" })
|
|
72692
72843
|
}
|
|
72693
72844
|
}];
|
|
72694
72845
|
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{$ as e,B as t,G as n,J as r,L as i,S as a,a as o,b as s,o as c,q as l,t as u,x as d}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-CEHpkaJP.js";import{Hi as f,Hr as p,Ir as m,It as h,Nt as g,On as _,Pn as v,Qt as y,Tt as b,_i as x,ar as S,cn as C,cr as w,dt as T,fi as E,fn as D,ia as O,nr as k,on as A,ot as ee,pr as j,sn as M,un as N,yi as P,yn as F}from"./player-overlays-CTjp5_0E.js";import{n as I,r as L,t as te}from"./key-B62K9GgX.js";import{c as R,o as z,u as B}from"./video-C9XbLIGZ.js";import{t as V}from"./history-CNH6Hwzl.js";import{t as ne}from"./key-round-DhUJVMTJ.js";import{t as re}from"./monitor-smartphone-CewWbhfo.js";import{t as ie}from"./unlink-DRIRCsre.js";import{Si as ae,a as oe,bi as se,i as ce,o as le,s as ue,xi as de}from"./client-Ck3kx2Z0.js";import{$l as fe,Al as pe,An as me,Bl as he,Bn as H,Cn as ge,Dn as _e,Dr as ve,En as U,Er as ye,Fl as be,Ft as xe,G as Se,Hl as Ce,Ht as W,Il as we,Jl as Te,L as Ee,Ll as De,Ln as Oe,Ml as ke,N as Ae,Nl as je,Ol as Me,P as Ne,Pl as Pe,Ql as Fe,Qn as Ie,Rl as Le,Rn as Re,Tn as ze,Ul as Be,Ut as Ve,Vl as He,Wt as Ue,X as We,Xl as Ge,Yl as Ke,Z as qe,Zl as Je,Zn as G,ar as K,bn as Ye,en as Xe,ha as Ze,in as Qe,jl as $e,jn as et,jo as tt,kl as nt,kn as rt,nn as it,on as at,rn as ot,yu as q,z as st,zl as ct,zn as lt}from"./src-CoNs4bys.js";import{t as J}from"./i18n-DWijAvai.js";import{t as Y}from"./useTranslation-D3Is_4B6.js";import{C as ut,Q as dt,X as ft,Y as pt,Z as mt,c as ht,et as gt,f as _t,m as vt,s as yt,z as bt}from"./index-IBVizVRk.js";import{t as xt}from"./AdminSectionedPage-DKH0UpKh.js";import{t as St}from"./AdminPage-DMiMIHwu.js";import{A as Ct,C as wt,D as Tt,E as Et,F as Dt,M as Ot,N as X,O as kt,P as Z,S as At,T as jt,_ as Mt,a as Nt,b as Pt,c as Ft,d as It,f as Lt,h as Rt,i as zt,j as Bt,k as Vt,l as Ht,m as Ut,n as Wt,o as Gt,p as Kt,s as qt,t as Jt,u as Yt,v as Xt,w as Zt,x as Qt,y as $t}from"./SessionsList-CzSpm8u5.js";import{i as en,n as tn,r as nn,t as rn}from"./IntegrationHub-DMx7fLHV.js";import{n as an,t as on}from"./addon-display-names-BkwMrExv.js";e();function sn(){let{t:e}=Y(),t=G(),i=q.useUtils(),a=Dt(),{data:o,isLoading:s}=we(),c=n(()=>o??[],[o]),l=q.useQueries(e=>c.map(t=>e.userManagement.listExternalIdentities({userId:t.id}))),u=q.auth.listLoginMethods.useQuery(),[f,p]=r(null),m=Ce({onSuccess:()=>void i.userManagement.listExternalIdentities.invalidate(),onError:e=>p(`Remove failed: ${e.message}`)}),h=n(()=>new Map((u.data??[]).flatMap(e=>e.kind===`redirect`?[[e.id,e.label]]:[])),[u.data]),g=c.flatMap((e,t)=>l[t]?.data??[]),_=g.map(e=>({...jt(e,a),kind:h.get(e.provider)??e.provider})),v=async n=>{let r=g.find(e=>Zt(e)===n.id);r!==void 0&&await t({title:e(`access.externalIdentitiesSection.removeSignInLink`,`Remove sign-in link`),message:e(`access.externalIdentitiesSection.willNoLongerBeAble`,`"{{ownerName}}" will no longer be able to sign in with this {{kind}} account.`,{ownerName:n.ownerName,kind:n.kind}),confirmLabel:e(`common.remove`,`Remove`),variant:`danger`})&&(p(null),m.mutate({userId:r.userId,provider:r.provider,subject:r.subject}))};return d(X,{notice:Z(f),onDismissNotice:()=>{p(null)},id:`external-identities`,title:e(`access.externalIdentitiesSection.signInLinks`,`Sign-in links`),icon:B,description:e(`access.externalIdentitiesSection.externalAccountsSingleSignOn`,`External accounts (single sign-on) each user linked to their CamStack account. Only the owner can link one, from their profile; an admin can remove it.`),children:d(Vt,{rows:_,label:`sign-in links`,isLoading:s||l.some(e=>e.isLoading),loadError:l.find(e=>e.isError)?.error?.message??null,emptyText:`No user has linked a sign-in account.`,expiresNone:`—`,action:{label:e(`common.remove`,`Remove`),icon:ie,isAvailable:()=>!0,onRun:e=>void v(e),busyId:null,disabled:m.isPending}})})}e();function cn(){let{t:e}=Y(),t=G(),i=c(),a=Dt(),o=je(),[s,l]=r(null),u=Le({onSuccess:()=>void i.invalidateQueries({queryKey:[[`userManagement`,`listApiKeys`]]}),onError:e=>l(`Revoke failed: ${e.message}`)}),f=n(()=>(o.data??[]).map(e=>wt(e,a)),[o.data,a]),p=async n=>{await t({title:e(`access.legacyApiKeysSection.revoke`,`Revoke "{{name}}"?`,{name:n.name}),message:e(`access.legacyApiKeysSection.anyClientUsingThisKey`,`Any client using this key loses access immediately. This cannot be undone.`),confirmLabel:e(`access.legacyApiKeysSection.revoke2`,`Revoke`),variant:`danger`})&&(l(null),u.mutate({id:n.id}))};return d(X,{notice:Z(s),onDismissNotice:()=>{l(null)},id:`legacy-api-keys`,title:e(`access.legacyApiKeysSection.legacyApiKeys`,`Legacy API keys`),icon:V,description:e(`access.legacyApiKeysSection.keysFromBeforeScopedTokens`,`Keys from before scoped tokens. Nothing creates them any more; revoke them and issue a scoped token instead.`),children:d(Vt,{rows:f,label:e(`access.legacyApiKeysSection.legacyApiKeys2`,`legacy API keys`),isLoading:o.isLoading,loadError:o.isError?o.error?.message??`Request failed`:null,emptyText:`No legacy API keys.`,action:{label:e(`access.legacyApiKeysSection.revoke2`,`Revoke`),icon:h,isAvailable:()=>!0,onRun:e=>void p(e),busyId:u.isPending?u.variables?.id??null:null,disabled:u.isPending}})})}e();var ln={key:`access.nouns.linkedApp`};function un(e){if(e.type===`category`){let t=e.target??``;return t===`device`?J.t(`access.linkedAppsSection.allDevices`):J.t(`access.linkedAppsSection.allTarget`,{target:t})}if(e.type===`device`)return`device:[${(e.targets??[]).join(`,`)}]`;let t=e;return`${t.type}:${t.target??``}`}function dn(){let{t:e}=Y(),t=G(),i=c(),a=be(),[o,s]=r(null),l=()=>void i.invalidateQueries({queryKey:[[`userManagement`,`listOauthSessions`]]}),u=ct({onSuccess:l,onError:e=>s(`Revoke failed: ${e.message}`)}),f=Ct(`oauth-session`,ln,l),p=n(()=>(a.data??[]).map(e=>Et(e,e.scopes.map(un).join(`, `))),[a.data]),m=async n=>{await t({title:e(`access.linkedAppsSection.revokeTheLink`,`Revoke the "{{name}}" link?`,{name:n.name}),message:e(`access.linkedAppsSection.losesAccessForOnIts`,`{{name}} loses access for {{ownerName}} on its next call. Linking it again needs the account-link flow from the start.`,{name:n.name,ownerName:n.ownerName}),confirmLabel:e(`access.linkedAppsSection.revoke`,`Revoke`),variant:`danger`})&&(s(null),u.mutate({id:n.id}))},g=u.isPending?u.variables?.id??null:null;return d(X,{notice:Z(o,f.outcome),onDismissNotice:()=>{s(null),f.dismiss()},id:`linked-apps`,title:e(`access.linkedAppsSection.linkedApps`,`Linked apps`),icon:w,description:e(`access.linkedAppsSection.externalIntegrationsAlexaLinkedTo`,`External integrations (Alexa, …) linked to this hub through OAuth. Revoking one stops that integration on its next call. Already-revoked rows can be selected and removed from the list.`),children:d(Vt,{rows:p,label:e(`access.linkedAppsSection.linkedApps2`,`linked apps`),isLoading:a.isLoading,loadError:a.isError?a.error?.message??`Request failed`:null,emptyText:`No linked apps — an integration appears here once it completes the account-link flow.`,expiresNone:`—`,action:{label:e(`access.linkedAppsSection.revoke`,`Revoke`),icon:h,isAvailable:e=>e.status===`active`||e.status===`revoked`,onRun:e=>void m(e),busyId:g,disabled:u.isPending||f.busy},bulk:{revoke:f,noun:ln,isSelectable:e=>e.status===`active`||e.status===`revoked`,quickSelect:[{label:e(`access.linkedAppsSection.selectRevoked`,`Select revoked`),match:e=>e.status===`revoked`}]}})})}e();var fn={key:`access.nouns.session`};function pn(){let{t:e}=Y(),t=G(),i=q.useUtils(),a=Dt(),o=q.auth.listSessions.useQuery(),[s,c]=r(null),l=()=>void i.auth.listSessions.invalidate(),u=q.auth.revokeSessions.useMutation({onSuccess:l,onError:e=>c(`Sign out failed: ${e.message}`)}),f=Ct(`login-session`,fn,l),p=n(()=>(o.data??[]).map(e=>Tt(e,a,{client:e=>Xt(e).label,method:$t})),[o.data,a]),m=async n=>{await t({title:e(`access.loginSessionsSection.signOutThisSession`,`Sign out this session`),message:e(`access.loginSessionsSection.onWillHaveToSign`,`{{ownerName}} on {{name}} will have to sign in again.`,{ownerName:n.ownerName,name:n.name}),confirmLabel:e(`access.loginSessionsSection.signOut`,`Sign out`),variant:`danger`})&&(c(null),u.mutate({id:n.id}))};return d(X,{notice:Z(s,f.outcome),onDismissNotice:()=>{c(null),f.dismiss()},id:`login-sessions`,title:e(`access.loginSessionsSection.loginSessions`,`Login sessions`),icon:re,description:e(`access.loginSessionsSection.everyBrowserAndAppSigned`,`Every browser and app signed in to this hub, for every user. Signing one out ends it at once; it has to sign in again. Your own current session is marked and never included in a bulk sign-out.`),children:d(Vt,{rows:p,label:e(`common.sessionsLabel`),isLoading:o.isLoading,loadError:o.isError?o.error?.message??`Request failed`:null,emptyText:`No active sessions.`,action:{label:e(`access.loginSessionsSection.signOut`,`Sign out`),icon:z,isAvailable:e=>e.status===`active`,onRun:e=>void m(e),busyId:u.isPending&&u.variables!==void 0&&`id`in u.variables?u.variables.id:null,disabled:u.isPending||f.busy},bulk:{revoke:f,noun:fn,isSelectable:e=>e.status===`active`}})})}function mn({token:e,label:t,onClose:n}){let{t:r}=Y();return d(ze,{open:!0,onOpenChange:e=>!e&&n(),children:a(_e,{width:`md`,scroll:`body`,children:[d(me,{children:d(et,{children:r(`auth.tokenModals.token`,`Token: {{label}}`,{label:t})})}),a(U,{className:`space-y-3`,children:[d(`div`,{className:`rounded-md border border-warning/30 bg-warning/10 px-3 py-2 text-xs text-warning`,children:r(`auth.tokenModals.copyThisTokenNowOnce`,`Copy this token now. Once you close this dialog the secret cannot be displayed again.`)}),d(`div`,{className:`select-all break-all rounded-md border border-border bg-background p-2 font-mono text-[11px] text-foreground`,children:e})]}),a(rt,{children:[d(xe,{value:e,label:r(`common.copy`,`Copy`),srLabel:`token`}),d(H,{type:`button`,variant:`primary`,size:`sm`,onClick:n,children:r(`auth.tokenModals.done`,`Done`)})]})]})})}var hn={token:`newToken`,scoped:`newScoped`,admin:`newAdmin`};e();var gn=7,_n=90,vn=80;function yn(e){let t=Number(e);return Number.isInteger(t)&&t>=1&&t<=_n?t:null}function bn({open:e,users:n,onClose:i,onSubmitScoped:o,onSubmitAdmin:c,submitting:l,formError:u,awaitingProof:f}){let{t:p}=Y(),m=O(),{user:h}=bt(),g=h?.isAdmin===!0,_=g?null:h?.scopes??[],[v,y]=r(!1),[b,x]=r(n[0]?.id??``),[C,w]=r(``),[T,E]=r([{type:`capability`,target:``,access:[`view`,`create`]}]),[D,k]=r(``),[ee,j]=r(String(gn));t(()=>{e||(y(!1),x(n[0]?.id??``),w(``),E([{type:`capability`,target:``,access:[`view`,`create`]}]),k(``),j(String(gn)))},[e,n]);let N=C.trim(),P=Ne(T,m),F=yn(ee),I=!v&&b!==``&&N.length>0&&T.length>0&&P===null&&!l,L=v&&g&&N.length>0&&N.length<=vn&&F!==null&&!l&&!f,te=()=>{if(v){if(!L||F===null)return;c({name:N,expiresInDays:F});return}if(!I)return;let e=parseInt(D,10),t=Number.isFinite(e)&&e>0?Date.now()+e*864e5:void 0;o({userId:b,name:N,scopes:T,...t===void 0?{}:{expiresAt:t}})},R=v,z=v?M:A;return d(st,{open:e,onClose:i,title:v?p(`access.createTokenModal.createAdminToken`,`Create admin token`):p(`access.createTokenModal.createToken`,`Create token`),icon:v?M:ne,error:u,width:`md`,testId:`create-token-dialog`,footer:a(H,{type:`button`,variant:R?`danger`:`primary`,size:`sm`,onClick:te,disabled:v?!L:!I,children:[l||f?d(S,{className:`h-3.5 w-3.5 animate-spin`,"aria-hidden":!0}):d(z,{className:`h-3.5 w-3.5`,"aria-hidden":!0}),v?p(`access.createTokenModal.createAdminToken`,`Create admin token`):p(`access.createTokenModal.issueToken`,`Issue token`)]}),children:a(`div`,{className:`space-y-3`,children:[g?a(`label`,{className:`flex items-start gap-2 text-sm text-foreground`,children:[d(`input`,{type:`checkbox`,className:`mt-0.5`,checked:v,onChange:e=>y(e.target.checked),"data-testid":`token-as-admin`}),a(`span`,{children:[d(`span`,{className:`font-medium`,children:p(`access.createTokenModal.administrator`,`Administrator`)}),d(`span`,{className:`block text-[11px] text-foreground-subtle`,children:p(`access.createTokenModal.offScopedToASurface`,`Off = scoped to a surface. On = full admin for scripts (audited, max 90 days).`)})]})]}):null,v?d(`div`,{className:`space-y-1 rounded-md border border-danger/30 bg-danger/5 px-3 py-2 text-[11px] leading-relaxed text-foreground`,"data-testid":`admin-token-warning`,children:d(`p`,{children:d(pt,{i18nKey:`auth.adminTokenNoticeShort`,components:{strong:d(`strong`,{})}})})}):null,v?null:a(`div`,{className:`space-y-1`,children:[d(at,{htmlFor:`token-user`,children:p(`access.createTokenModal.onBehalfOfUser`,`On behalf of user`)}),d(Te,{id:`token-user`,value:b,onChange:e=>x(e.target.value),options:n.map(e=>({value:e.id,label:e.username}))})]}),d(Se,{label:p(`access.createTokenModal.name`,`Name`),required:!0,children:d(fe,{id:`token-name`,value:C,maxLength:vn,onChange:e=>w(e.target.value),placeholder:v?`nightly-backup`:`cloudflare-tunnel-route`,autoFocus:!0})}),v?a(`div`,{className:`space-y-1`,children:[d(at,{htmlFor:`token-admin-days`,children:p(`access.createTokenModal.expiresInDays`,`Expires in (days)`)}),d(fe,{id:`token-admin-days`,type:`number`,min:1,max:_n,value:ee,onChange:e=>j(e.target.value),state:F===null?`error`:`default`}),F===null?d(`p`,{className:`text-[11px] text-danger`,children:p(`auth.expiryInvalid`,{max:_n})}):d(`p`,{className:`text-[11px] text-foreground-subtle`,children:p(`auth.expiryHint`,{max:_n})})]}):a(s,{children:[a(`div`,{className:`space-y-1`,children:[d(at,{children:p(`access.createTokenModal.scopes`,`Scopes`)}),d(Ae,{value:T,onChange:E,clampToParent:_}),P&&d(`p`,{className:`text-[10px] text-danger`,children:P}),d(`p`,{className:`text-[10px] text-foreground-subtle`,children:p(`access.createTokenModal.eachScopePicksACap`,`Each scope picks a (cap or addon) target + one or more access flavours. The token can access ONLY what's listed.`)})]}),a(`div`,{className:`space-y-1`,children:[d(at,{htmlFor:`token-scoped-days`,children:p(`access.createTokenModal.expiresInDays`,`Expires in (days)`)}),d(fe,{id:`token-scoped-days`,type:`number`,min:1,value:D,onChange:e=>k(e.target.value),placeholder:`30`}),d(`p`,{className:`text-[10px] text-foreground-subtle`,children:p(`access.createTokenModal.leaveBlankForNonExpiring`,`Leave blank for non-expiring.`)})]})]})]})})}e();var xn={key:`access.nouns.token`};function Sn(e,t){return e.type===`device`?`device:[${qe(e.selector,t)}]`:`${e.type}:${e.target??``}`}function Cn({createRequested:e=!1,onCreateConsumed:i}){let{t:o}=Y(),s=O(),{user:l}=bt(),u=G(),f=c(),p=q.useUtils(),m=Dt(),{data:g,isLoading:_}=we(),v=n(()=>g??[],[g]),y=q.useQueries(e=>v.map(t=>e.userManagement.listScopedTokens({userId:t.id}))),b=q.auth.listAdminTokens.useQuery(void 0,{enabled:l?.isAdmin===!0}),{requestProof:x,dialog:S}=Mt(`Creating an admin token`),{state:C,openNew:w,close:T}=Ee({newKey:hn.token}),[E,D]=r(null),[k,A]=r(null),[ee,j]=r(!1),[M,N]=r(null),[P,F]=r(null);t(()=>{e&&(w(),i?.())},[e,w,i]);let I=()=>{f.invalidateQueries({queryKey:[[`userManagement`,`listScopedTokens`]]}),p.auth.listAdminTokens.invalidate(),p.auth.listAuthAudit.invalidate()},L=Me({onSuccess:({token:e,record:t})=>{I(),T(),A(null),N({token:e,label:t.name})},onError:e=>{D(`Create failed: ${e.message}`),A(`Create failed: ${e.message}`)}}),te=q.auth.createAdminToken.useMutation({onSuccess:({token:e,record:t})=>{I(),T(),A(null),j(!1),N({token:e,label:t.name})},onError:e=>{D(`Create failed: ${e.message}`),A(`Create failed: ${e.message}`),j(!1)}}),R=he({onSuccess:()=>{F(null),I()},onError:e=>{F(null),D(`Revoke failed: ${e.message}`)}}),z=q.auth.revokeAdminToken.useMutation({onSuccess:()=>{F(null),I()},onError:e=>{F(null),D(`Revoke failed: ${e.message}`)}}),B=Ot(xn,I),V=Date.now(),re=v.flatMap((e,t)=>(y[t]?.data??[]).map(t=>kt(t,e.id,m,t.scopes.map(e=>Sn(e,s)).join(`, `),V))),ie=l?.isAdmin===!0?(b.data??[]).map(e=>At(e,m,V)):[],ae=[...re,...ie],oe=y.find(e=>e.isError)?.error?.message??(b.isError?b.error?.message??`Request failed`:null),se=async e=>{let t=e.kind===Qt;await u({title:o(`access.tokensSection.revoke`,`Revoke "{{name}}"?`,{name:e.name}),message:t?o(`access.tokensSection.everyScriptUsingItLoses`,`Every script using it loses access immediately. This cannot be undone.`):o(`access.tokensSection.sScopedTokenStopsWorking`,`{{ownerName}}'s scoped token stops working on its next use. This cannot be undone.`,{ownerName:e.ownerName}),confirmLabel:o(`access.tokensSection.revoke2`,`Revoke`),variant:`danger`})&&(D(null),F(e.id),t?z.mutate({id:e.id}):R.mutate({id:e.id}))},ce=L.isPending||te.isPending;return a(`div`,{"data-testid":`tokens`,children:[S,d(X,{notice:Z(E,B.outcome),onDismissNotice:()=>{D(null),B.dismiss()},id:`tokens`,title:o(`access.tokensSection.tokens`,`Tokens`),icon:ne,description:o(`access.tokensSection.scopedTokensForANarrow`,`Scoped tokens for a narrow surface, and admin tokens for full-administrator scripts. Revoking one stops it on its next use.`),children:d(Vt,{rows:ae,label:o(`common.tokensLabel`),isLoading:_||y.some(e=>e.isLoading)||l?.isAdmin===!0&&b.isLoading,loadError:oe,emptyText:`No tokens.`,action:{label:o(`access.tokensSection.revoke2`,`Revoke`),icon:h,isAvailable:e=>e.status===`active`,onRun:e=>void se(e),busyId:P,disabled:B.busy||P!==null},bulk:{revoke:B,noun:xn,isSelectable:e=>e.status===`active`}})}),d(bn,{open:C.mode===`new`,users:v.map(e=>({id:e.id,username:e.username})),onClose:()=>{T(),A(null),j(!1)},submitting:ce,formError:k,awaitingProof:ee,onSubmitScoped:e=>L.mutate({userId:e.userId,name:e.name,scopes:e.scopes,expiresAt:e.expiresAt}),onSubmitAdmin:e=>{(async()=>{D(null),A(null),j(!0);let t=await x();j(!1),t!==null&&te.mutate({...e,reauthToken:t})})()}}),M&&d(mn,{token:M.token,label:M.label,onClose:()=>N(null)})]})}var wn={"share-link":`helpers.authAuditWording.bulkNoun.shareLink`,"admin-token":`helpers.authAuditWording.bulkNoun.adminToken`,"login-session":`helpers.authAuditWording.bulkNoun.loginSession`};function Q(e,t){let n=e[t];return typeof n==`string`&&n.length>0?n:null}function Tn(e,t){let n=Q(e,`tokenId`);return Q(e,`name`)??(n===null?void 0:t.get(n))??n??`?`}function En(e){return e.filter(e=>e!==null)}function Dn(e){let t=Q(e,`prefix`);return t===null?null:J.t(`helpers.authAuditWording.prefix`,{prefix:t})}function On(e,t){let n=e.detail??{};switch(e.action){case`admin-token-created`:{let e=n.expiresAt;return{title:J.t(`helpers.authAuditWording.tokenCreated`,{name:Tn(n,t)}),details:En([Dn(n),typeof e==`number`?J.t(`helpers.authAuditWording.expires`,{at:Pt(e)}):null])}}case`admin-token-used`:{let e=Q(n,`path`);return{title:J.t(`helpers.authAuditWording.tokenUsed`,{name:Tn(n,t)}),details:En([Dn(n),e===null?null:J.t(`helpers.authAuditWording.call`,{path:e})])}}case`admin-token-revoked`:return{title:J.t(`helpers.authAuditWording.tokenRevoked`,{name:Tn(n,t)}),details:En([Dn(n)])};case`credentials-revoked`:{let e=Q(n,`kind`)??`credential`,t=n.revoked,r=n.skipped,i=wn[e],a=i===void 0?`${e}s`:J.t(i);return{title:J.t(`helpers.authAuditWording.bulkRevoked`,{n:typeof t==`number`?String(t):`?`,noun:a}),details:En([typeof r==`number`&&r>0?J.t(`helpers.authAuditWording.skipped`,{count:r}):null,Q(n,`ids`)===null?null:J.t(`helpers.authAuditWording.ids`,{ids:Q(n,`ids`)??``})])}}case`second-factor-skipped`:{let e=Q(n,`method`),t=Q(n,`factors`);return{title:J.t(`helpers.authAuditWording.secondFactorSkipped`),details:En([e===null?null:J.t(`helpers.authAuditWording.signedInWith`,{method:$t(e)}),t===null?null:J.t(`helpers.authAuditWording.skippedFactors`,{factors:t.split(`,`).join(`, `)})])}}default:return{title:e.action,details:Object.entries(n).map(([e,t])=>`${e}: ${String(t)}`)}}}e();var kn=100;function An(){let{user:e}=bt();return e?.isAdmin?d(Mn,{}):null}function jn(){let{data:e}=we(),{data:t}=q.auth.listAdminTokens.useQuery();return n(()=>{let n=new Map;for(let t of e??[])n.set(t.id,t.username);let r=new Map;for(let e of t??[])r.set(e.id,e.name);return{userNameById:n,tokenNameById:r}},[e,t])}function Mn(){let{t:e}=Y(),t=q.auth.listAuthAudit.useQuery({limit:kn}),{userNameById:n,tokenNameById:r}=jn(),i=t.data??[];return a(`div`,{"data-testid":`auth-audit`,className:`rounded-lg border border-border bg-surface overflow-hidden`,children:[a(`div`,{className:`px-4 py-2.5 border-b border-border flex items-center gap-2`,children:[d(D,{className:`h-3.5 w-3.5 text-foreground-subtle`}),d(`div`,{className:`text-xs font-semibold text-foreground`,children:e(`auth.authAuditSection.authenticationAudit`,`Authentication audit`)}),d(`span`,{className:`ml-auto text-[10px] text-foreground-subtle`,children:e(`auth.authAuditSection.latest`,{count:kn})})]}),d(`div`,{className:`p-4`,children:t.isLoading?a(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[d(S,{className:`h-3.5 w-3.5 animate-spin`}),e(`auth.authAuditSection.loadingAuditTrail`,`Loading audit trail…`)]}):t.isError?a(`div`,{className:`text-[11px] text-danger`,children:[e(`auth.authAuditSection.couldNotLoadTheAudit`,`Could not load the audit trail:`),` `,t.error?.message??e(`auth.authAuditSection.requestFailed`,`Request failed`)]}):i.length===0?a(`div`,{className:`rounded border border-dashed border-border px-3 py-4 text-xs text-foreground-subtle text-center space-y-1`,children:[d(`div`,{children:e(`auth.authAuditSection.noAuthenticationEventsRecordedYet`,`No authentication events recorded yet.`)}),d(`div`,{className:`text-[11px] text-foreground-subtle/90`,children:e(`auth.authAuditSection.thisTrailRecordsAdminToken`,`This trail records admin-token create/use/revoke, trusted-network 2FA skips, and credential revokes (including single share-link revoke). Browser password logins are not listed here.`)})]}):d(`ul`,{className:`divide-y divide-border rounded-md border border-border max-h-[420px] overflow-y-auto`,children:i.map(e=>d(Nn,{record:e,userName:e.userId===void 0?null:n.get(e.userId)??e.userId,tokenNameById:r},e.id))})})]})}function Nn({record:e,userName:t,tokenNameById:n}){let r=On(e,n);return a(`li`,{"data-testid":`auth-audit-row`,className:`px-3 py-2 text-xs space-y-0.5`,children:[a(`div`,{className:`flex flex-wrap items-baseline gap-x-2`,children:[d(`span`,{className:`font-medium text-foreground`,children:r.title}),d(`span`,{className:`ml-auto text-[10px] text-foreground-subtle`,children:Pt(e.at)})]}),a(`div`,{className:`flex flex-wrap gap-x-3 text-[11px] text-foreground-subtle`,children:[t!==null&&d(`span`,{className:`text-foreground`,children:t}),e.ip!==void 0&&d(`span`,{className:`font-mono`,children:e.ip}),r.details.map(e=>d(`span`,{className:`font-mono`,children:e},e))]})]})}e();var $={loginSessions:`login-sessions`,linkedApps:`linked-apps`,tokens:`tokens`,scopedTokens:`scoped-tokens`,adminTokens:`admin-tokens`,shareLinks:`share-links`,legacyApiKeys:`legacy-api-keys`,externalIdentities:`external-identities`,audit:`auth-audit`},Pn={[$.scopedTokens]:$.tokens,[$.adminTokens]:$.tokens};function Fn(e){return[{id:$.loginSessions,label:J.t(`system.access.loginSessions`,`Login sessions`),icon:re,render:()=>d(pn,{})},{id:$.linkedApps,label:J.t(`system.access.linkedApps`,`Linked apps`),icon:w,render:()=>d(dn,{})},{id:$.tokens,label:J.t(`system.access.tokens`,`Tokens`),icon:ne,render:({createRequested:e,onCreateConsumed:t})=>d(Cn,{headerOwnsCreate:!0,createRequested:e,onCreateConsumed:t})},{id:$.shareLinks,label:J.t(`system.access.shareLinks`,`Share links`),icon:C,render:()=>d(Rt,{})},...e?[{id:$.legacyApiKeys,label:J.t(`system.access.legacyApiKeys`,`Legacy API keys`),icon:V,render:()=>d(cn,{})}]:[],{id:$.externalIdentities,label:J.t(`system.access.signInLinks`,`Sign-in links`),icon:B,render:()=>d(sn,{})},{id:$.audit,label:J.t(`system.access.audit`,`Audit`),icon:D,render:()=>d(An,{})}]}function In({createRequested:e=!1,onCreateConsumed:t}){let{t:r}=Y(),i=je(),o=(i.data??[]).length>0,s=n(()=>Fn(o),[o]),c=en({ids:n(()=>s.map(e=>e.id),[s]),answered:!i.isLoading}),l=dt(),[u]=gt(),f=u.get(`integration`),p=f===null?void 0:Pn[f];if(p!==void 0){let e=new URLSearchParams(u);return e.set(`integration`,p),d(mt,{to:`?${e.toString()}`,replace:!0})}let m=s.find(e=>e.id===c.selectedId)??s[0],h=m?.id??$.loginSessions;return i.isLoading?d(`div`,{className:`text-sm text-foreground-subtle`,"data-testid":`access-page`,children:r(`common.loading`,`Loading…`)}):a(`div`,{className:`flex flex-col gap-3`,"data-testid":`access-page`,children:[d(Xe,{appearance:`pills`,value:h,onValueChange:e=>{l(`?${vt(u,e,null,null).toString()}`)},children:d(it,{"aria-label":r(`system.access.credentialKinds`,`Credential kinds`),children:s.map(e=>a(ot,{value:e.id,"data-testid":`access-kind-${e.id}`,children:[d(e.icon,{className:`h-3.5 w-3.5 shrink-0`,"aria-hidden":!0}),e.label]},e.id))})}),m===void 0?null:d(`div`,{role:`region`,"aria-label":m.label,"data-testid":`access-kind-body`,children:m.render({createRequested:e&&m.id===$.tokens,onCreateConsumed:t??(()=>void 0)})},m.id)]})}function Ln(e,t){if(e===$.tokens)return a(H,{size:`sm`,type:`button`,"data-testid":`access-new-token`,onClick:t,children:[d(ne,{className:`h-3.5 w-3.5`,"aria-hidden":!0}),J.t(`system.access.newToken`,`New token`)]})}function Rn(){let[e,t]=r(!1);return{createRequested:e,requestCreate:()=>t(!0),clearCreate:()=>t(!1)}}e();var zn=[`oidcConnections`,`listConnections`],Bn=[`oidcConnections`,`connectionFormSchema`],Vn={displayName:`OpenID Connect`,icon:`shield-check`,issuerUrl:``,clientId:``,clientSecret:``,redirectUri:``,scopes:`openid profile email`,usernameClaim:`preferred_username`},Hn=/^[a-z0-9][a-z0-9-]{0,63}$/;function Un(e){return e instanceof Error?e.message:String(e)}function Wn(e,t){if(t)return typeof e==`string`&&e.length>0?e:void 0;if(de(e))return se;if(typeof e==`string`&&e.length>0)return e;ae(e)}function Gn(e){return typeof e!=`object`||!e?!1:Array.isArray(Reflect.get(e,`sections`))}function Kn({addonId:e}){let{t:f}=Y(),p=ee(),m=c(),g=G(),v=Ee({editKey:`oidcEdit`,newKey:`oidcNew`}),[y,b]=r(!1),[x,S]=r(null),[C,w]=r(``),[T,E]=r({...Vn}),D=l(null),O=o({queryKey:[...zn,e],queryFn:async()=>p.trpcClient.oidcConnections.listConnections.query({})}),k=o({queryKey:Bn,queryFn:async()=>p.trpcClient.oidcConnections.connectionFormSchema.query({})}),j=O.data??[],M=Gn(k.data)?k.data:void 0,N=v.state,P=N.mode===`edit`?j.find(e=>e.id===N.id)??null:null;t(()=>{if(v.state.mode===`edit`&&P!==null){let e=`edit:${P.id}`;if(D.current===e)return;D.current=e,S(null),w(P.id),E(P.formValues===void 0?{...Vn}:{...P.formValues});return}if(v.state.mode===`new`){if(D.current===`new`)return;D.current=`new`,S(null),w(``),E({...Vn})}},[P,v.state]);let F=i(async()=>{await m.invalidateQueries({queryKey:zn})},[m]),I=u({mutationFn:async e=>p.trpcClient.oidcConnections.deleteConnection.mutate({id:e}),onSuccess:async()=>F()}),L=i(async e=>{await g({title:f(`system.oidcSettingsConnections.deleteOidcConnection`,`Delete OIDC connection?`),message:f(`system.oidcSettingsConnections.removeTheLoginButtonAnd`,`Remove “{{displayName}}” ({{id}}). The login button and IdP credentials for this id are dropped.`,{displayName:e.displayName,id:e.id}),confirmLabel:f(`common.delete`,`Delete`),cancelLabel:f(`common.cancel`,`Cancel`),variant:`danger`})&&(await I.mutateAsync(e.id),v.state.mode===`edit`&&v.state.id===e.id&&(v.close(),D.current=null))},[g,I,v,f]),te=i(async()=>{let e=T.displayName,t=typeof e==`string`&&e.trim()!==``?e.trim():``;if(t===``){S(`Display name is required.`);return}let n=v.state.mode===`new`,r=n?C.trim():P?.id??``;if(n&&!Hn.test(r)){S(`Instance id must be a lowercase slug (e.g. authentik, google).`);return}if(r===``){S(`That connection is no longer available.`);return}let i=Wn(T.clientSecret,n),a=T.icon,o=T.issuerUrl,s=T.clientId,c=T.redirectUri,l=T.scopes,u=T.usernameClaim,d=u===`email`||u===`sub`?u:`preferred_username`;b(!0),S(null);try{n&&await p.trpcClient.oidcConnections.createConnection.mutate({id:r,displayName:t}),await p.trpcClient.oidcConnections.updateConnection.mutate({id:r,displayName:t,icon:typeof a==`string`?a:`shield-check`,issuerUrl:typeof o==`string`?o:``,clientId:typeof s==`string`?s:``,...i===void 0?{}:{clientSecret:i},redirectUri:typeof c==`string`?c:``,scopes:typeof l==`string`?l:`openid profile email`,usernameClaim:d}),await F(),v.close(),D.current=null}catch(e){S(Un(e))}finally{b(!1)}},[C,P?.id,v,T,F,p]),R=n(()=>[{key:`id`,header:f(`system.oidcSettingsConnections.id`,`Id`),sortValue:e=>e.id,render:e=>d(`span`,{className:`font-mono text-xs text-foreground`,children:e.id})},{key:`name`,header:f(`system.oidcSettingsConnections.name`,`Name`),sortValue:e=>e.displayName,render:e=>d(`span`,{className:`truncate font-medium text-foreground`,children:e.displayName})},{key:`issuer`,header:f(`system.oidcSettingsConnections.issuer`,`Issuer`),sortValue:e=>e.issuerUrl,render:e=>d(`span`,{className:`truncate text-xs text-foreground-muted`,children:e.issuerUrl===``?`—`:e.issuerUrl})},{key:`status`,header:f(`system.oidcSettingsConnections.status`,`Status`),notSortableReason:`Badge`,render:e=>d(K,{variant:e.configured?`success`:`warning`,children:e.configured?f(`system.oidcSettingsConnections.configured`,`Configured`):f(`system.oidcSettingsConnections.notConfigured`,`Not configured`)})},{key:`actions`,header:``,align:`right`,notSortableReason:`Row actions`,nowrap:!0,render:e=>a(`div`,{className:`flex justify-end gap-1`,children:[d(H,{type:`button`,size:`sm`,variant:`secondary`,onClick:t=>{t.stopPropagation(),v.openEdit(e.id)},children:f(`common.edit`,`Edit`)}),d(H,{type:`button`,size:`sm`,variant:`ghost`,"aria-label":f(`system.oidcSettingsConnections.delete`,`Delete {{displayName}}`,{displayName:e.displayName}),title:f(`common.delete`,`Delete`),onClick:t=>{t.stopPropagation(),L(e)},children:d(h,{className:`h-3.5 w-3.5 text-danger`})})]})}],[v,L,f]),z=v.state.mode===`new`,B=z||P!==null,V=z?`new`:P===null?`closed`:`edit:${P.id}:${P.configured?`cfg`:`empty`}`;return a(`div`,{className:`flex min-h-0 flex-col gap-3`,"data-testid":`oidc-settings-connections`,children:[a(`div`,{className:`flex flex-wrap items-center justify-between gap-2`,children:[a(`p`,{className:`text-sm text-foreground-muted`,children:[f(`system.oidcSettingsConnections.eachRowIsOneIdp`,`Each row is one IdP. Redirect URI:`),` `,d(`span`,{className:`font-mono text-xs`,children:`/addon/${e}/<id>/callback`})]}),a(H,{type:`button`,size:`sm`,"data-testid":`oidc-connections-add`,onClick:()=>v.openNew(),children:[d(_,{className:`h-3.5 w-3.5`}),f(`system.oidcSettingsConnections.addConnection`,`Add connection`)]})]}),O.isError?d(`p`,{className:`text-sm text-danger`,children:f(`system.oidcSettingsConnections.couldNotLoadConnections`,`Could not load connections.`)}):null,O.isPending?d(lt,{className:`h-32 w-full`}):d(Ve,{columns:R,rows:j,rowKey:e=>e.id,pageSize:25,emptyMessage:f(`system.oidcSettingsConnections.noOidcConnectionsYetAdd`,`No OIDC connections yet. Add one to register an IdP.`),onRowClick:e=>v.openEdit(e.id),defaultSort:{columnKey:`name`,dir:`asc`}}),d(st,{open:B,onClose:()=>{v.close(),S(null),D.current=null},title:z?f(`system.oidcSettingsConnections.addOidcConnection`,`Add OIDC connection`):f(`system.oidcSettingsConnections.edit`,`Edit {{value1}}`,{value1:P?.displayName??`connection`}),icon:A,width:`xl`,testId:`oidc-connection-editor`,error:x,footer:d(H,{type:`button`,size:`sm`,disabled:y||z&&C.trim()===``,onClick:()=>void te(),children:y?f(`system.oidcSettingsConnections.saving`,`Saving…`):f(`common.save`,`Save`)}),children:B?a(s,{children:[z?a(`div`,{children:[d(at,{htmlFor:`oidc-connection-id`,children:f(`system.oidcSettingsConnections.instanceId`,`Instance id`)}),d(fe,{id:`oidc-connection-id`,value:C,onChange:e=>w(e.target.value),placeholder:`authentik`,autoComplete:`off`}),d(`p`,{className:`mt-1 text-xs text-foreground-muted`,children:f(`system.oidcSettingsConnections.urlSlugAppearsInAddon`,`URL slug — appears in /addon/{{addonId}}/<id>/callback. Stable; renaming is not supported after save.`,{addonId:e})})]}):a(`p`,{className:`text-sm text-foreground-muted`,children:[f(`system.oidcSettingsConnections.instanceId2`,`Instance id:`),` `,d(`span`,{className:`font-mono`,children:P?.id})]}),M===void 0?d(`p`,{className:`text-sm text-foreground-muted`,children:f(`system.oidcSettingsConnections.loadingForm`,`Loading form…`)}):d(Ye,{schema:M,values:T,onChange:E,bare:!0},V)]}):null})]})}e();var qn=3e4,Jn=[{capName:`auth-provider`,stageBadge:null},{capName:`user-passkeys`,stageBadge:`Second factor`}],Yn={titleKey:`system.authentication.sectionTitle`,subtitleKey:`system.authentication.sectionSubtitle`,emptyTitleKey:`system.authentication.sectionEmptyTitle`,emptyDescriptionKey:`system.authentication.sectionEmptyDescription`,installLabelKey:`system.authentication.sectionInstall`};function Xn(e){return e===`local-auth`?{headline:`Username + password (local).`,icon:I,bullets:[`Users live in the hub’s SQLite database — no IdP roundtrip.`,`TOTP / 2FA is self-enrolled: each user enables it from their own profile page (My access → Two-factor authentication).`,`Scoped tokens, admin tokens and sessions are managed from Identity → Tokens & sessions.`],setupSteps:[`A fresh hub has one account, admin / changeme — the first sign-in asks for a new password. Add users from Users → New user.`,`Hand out either passwords or scoped tokens — both validate through this provider.`]}:e.startsWith(`auth-oidc`)?{headline:`OpenID Connect (Google, Microsoft, Okta, Keycloak, …).`,icon:R,bullets:[`Three-leg redirect flow with PKCE S256 + nonce.`,`id_token is signature-verified against the IdP’s JWKS — no implicit trust.`,`Explicit linking only: an IdP account signs in only the CamStack user who linked it from their profile (My access → Linked sign-in accounts). No auto-created users, no email matching.`,`The linked user’s role, scopes and two-factor authentication apply.`],setupSteps:[`Register a web app at your IdP and copy the client_id + client_secret.`,"Choose a stable instance id (slug). Set the IdP redirect URI to `https://<this-hub>/addon/auth-oidc/<id>/callback`.",`In Settings below, Add connection — enter the id, issuer, and credentials; Save.`,`Each user then links their IdP account from My access → Linked sign-in accounts.`]}:e.startsWith(`auth-saml`)?{headline:`SAML 2.0 — enterprise SSO (Azure AD, Okta, OneLogin).`,icon:R,bullets:[`Heavier handshake than OIDC — exchange metadata XML with your IdP.`,`Group → role mapping via SAML assertions.`]}:e.startsWith(`auth-webauthn`)||e.startsWith(`auth-passkey`)?{headline:`Passkeys / WebAuthn — passwordless second factor.`,icon:y,bullets:[`Enroll a YubiKey, Touch ID, or platform passkey per user.`,`Resistant to phishing — the browser binds the credential to your hub origin.`]}:e.startsWith(`auth-totp`)?{headline:`TOTP — second factor.`,icon:y,bullets:[`Time-based codes from an authenticator app (RFC 6238).`,`Compatible with Google Authenticator, Aegis, 1Password, Bitwarden, etc.`]}:{headline:`Authentication provider.`,icon:A,bullets:[`Generic authentication provider. No additional hints registered.`]}}function Zn(){let{t:e}=Y(),t=ve({capName:Jn[0].capName},{refetchInterval:qn}),r=ve({capName:Jn[1].capName},{refetchInterval:qn}),i=ye(void 0,{refetchInterval:qn}),a=n(()=>on(i.data??[]),[i.data]),o=n(()=>{let e=[],n=[t.data??[],r.data??[]];for(let[t,r]of Jn.entries())for(let i of n[t]??[])e.push({addonId:i.addonId,displayName:a.get(i.addonId)??i.addonId,isActive:i.isActive,capName:r.capName,stageBadge:r.stageBadge});return e},[t.data,r.data,a]),s=[{id:`authentication`,label:e(`system.authentication.authenticationProviders`,`Authentication providers`),install:{capability:Jn[0].capName,label:e(Yn.installLabelKey)},entries:o.map(t=>({id:t.addonId,name:t.displayName,fallbackIcon:A,...an(t.isActive),render:()=>d(tn,{addonId:t.addonId,displayName:t.displayName,capName:t.capName,isActive:t.isActive,badges:t.stageBadge===null?null:d(K,{variant:`info`,className:nn,children:t.stageBadge}),settingsContent:t.addonId.startsWith(`auth-oidc`)?d(Kn,{addonId:t.addonId}):void 0,panels:[{id:`about`,title:e(`system.authentication.about`,`About`),icon:Xn(t.addonId).icon,content:d(Qn,{addonId:t.addonId})}]})}))}];return d(rn,{embedded:!0,icon:A,title:e(Yn.titleKey),subtitle:e(Yn.subtitleKey),groups:s,isLoading:t.isLoading||r.isLoading,emptyTitle:e(Yn.emptyTitleKey),emptyDescription:e(Yn.emptyDescriptionKey)})}function Qn({addonId:e}){let{t}=Y(),n=Xn(e),r=n.icon;return a(`div`,{className:`space-y-3 rounded-lg border border-border bg-surface p-2 sm:p-4`,children:[a(`div`,{className:`flex items-start gap-2.5`,children:[d(r,{className:`h-4 w-4 mt-0.5 text-primary shrink-0`}),d(`div`,{className:`text-xs text-foreground font-medium`,children:n.headline})]}),d(`ul`,{className:`text-xs text-foreground-subtle space-y-1.5 pl-6 list-disc`,children:n.bullets.map((e,t)=>d(`li`,{children:e},t))}),n.setupSteps&&n.setupSteps.length>0&&a(`div`,{className:`rounded-md border border-border bg-surface-hover/40 px-3 py-2.5 space-y-1.5`,children:[a(`div`,{className:`flex items-center gap-1.5 text-[10px] font-semibold text-foreground-subtle uppercase tracking-wide`,children:[d(L,{className:`h-3 w-3`}),t(`system.authentication.setupSteps`,`Setup steps`)]}),d(`ol`,{className:`text-xs text-foreground space-y-0.5 pl-4 list-decimal`,children:n.setupSteps.map((e,t)=>d(`li`,{children:e},t))})]}),e===`local-auth`&&a(`div`,{className:`rounded-md border border-info/30 bg-info/5 px-3 py-2 flex items-start gap-2 text-[11px] text-foreground`,children:[d(j,{className:`h-3.5 w-3.5 mt-0.5 text-info shrink-0`}),d(`span`,{children:d(pt,{i18nKey:`system.authentication.totpNote`,components:{access:d(ft,{to:`/system/my-access`,className:`underline text-primary`}),users:d(ft,{to:`/system/identity?tab=users`,className:`underline text-primary`})}})})]})]})}function $n({isPending:e,error:t,what:n,onRetry:r,children:i}){let{t:o}=Y();return e?a(`div`,{className:`flex items-center gap-2 py-3 text-xs text-foreground-subtle`,role:`status`,"data-testid":`query-pending`,children:[d(S,{className:`h-3.5 w-3.5 animate-spin`,"aria-hidden":!0}),o(`certificates.queryBody.reading`,{what:n})]}):t===null?i:d(Oe,{title:o(`certificates.queryBody.couldNotRead`,`Could not read {{what}}`,{what:n}),message:t.message.length>0?t.message:o(`certificates.queryBody.requestFailed`,`Request failed`),onRetry:r})}function er(){let{t:e}=Y(),t=q.deviceCredentials.getDeviceCaStatus.useQuery({}),n=t.data;return d(X,{id:`device-ca`,title:e(`certificates.deviceCaCard.deviceCertificateAuthority`,`Device certificate authority`),icon:A,description:e(`certificates.deviceCaCard.signsTheCertificateEachPaired`,`Signs the certificate each paired device holds. It is created the first time a device is paired.`),children:d($n,{isPending:t.isPending,error:t.error,what:`the device CA`,onRetry:()=>void t.refetch(),children:n!==void 0&&a(`div`,{className:`space-y-1`,"data-testid":`device-ca-status`,children:[a(W,{label:e(`certificates.deviceCaCard.state`,`State`),children:[n.state===`ready`&&d(K,{variant:`success`,children:e(`certificates.deviceCaCard.ready`,`Ready`)}),n.state===`absent`&&d(K,{variant:`default`,children:e(`certificates.deviceCaCard.notCreatedYet`,`Not created yet`)}),n.state===`unavailable`&&d(K,{variant:`danger`,children:e(`certificates.deviceCaCard.unavailable`,`Unavailable`)})]}),n.state===`ready`&&a(s,{children:[d(W,{label:e(`certificates.deviceCaCard.subject`,`Subject`),valueClassName:`font-mono`,children:n.subject}),d(W,{label:e(`certificates.deviceCaCard.fingerprintSha256`,`Fingerprint (SHA-256)`),valueClassName:`font-mono`,children:n.fingerprintSha256}),d(W,{label:e(`certificates.deviceCaCard.validUntil`,`Valid until`),children:n.validTo})]}),n.state===`unavailable`&&d(W,{label:e(`certificates.deviceCaCard.reason`,`Reason`),children:d(`span`,{className:`text-danger`,children:n.reason})})]})})})}e();function tr(){let{t:e}=Y(),t=q.useUtils(),n=q.deviceCredentials.getDeviceCertificateSettings.useQuery({}),[i,o]=r(null),s=q.deviceCredentials.setDeviceCertificateSettings.useMutation({onSuccess:()=>{o(null),t.deviceCredentials.getDeviceCertificateSettings.invalidate()},onError:e=>o(`Could not save: ${e.message}`)});return d($n,{isPending:n.isPending,error:n.error,what:`the enrollment setting`,onRetry:()=>void n.refetch(),children:n.data!==void 0&&a(W,{label:e(`certificates.deviceSettingsRow.usersCanEnrollTheirOwn`,`Users can enroll their own devices`),actions:d(Qe,{checked:n.data.selfEnrollment===`allowed`,disabled:s.isPending,"aria-label":e(`certificates.deviceSettingsRow.usersCanEnrollTheirOwn`,`Users can enroll their own devices`),onCheckedChange:e=>s.mutate({selfEnrollment:e?`allowed`:`disabled`})}),children:[d(`span`,{className:`text-[11px] text-foreground-subtle`,children:n.data.selfEnrollment===`allowed`?e(`certificates.deviceSettingsRow.allowedAUserSignedIn`,`Allowed - a user signed in on a phone can keep it signed in. Pairing by QR works either way.`):e(`certificates.deviceSettingsRow.disabledOnlyAnAdminCan`,`Disabled - only an admin can pair a device, with a QR code.`)}),i!==null&&d(`span`,{className:`block text-[11px] text-danger`,children:i})]})})}e();function nr(e){let[n,i]=r(()=>Date.now());return t(()=>{let t=setInterval(()=>i(Date.now()),e);return()=>clearInterval(t)},[e]),n}e();var rr=80,ir=208;function ar({open:e,users:t,onClose:n,onPaired:i}){let{t:o}=Y(),s=q.deviceCredentials.startEnrollment.useMutation({onSuccess:i}),[c,l]=r(``),[u,f]=r(``),p=c===``?t[0]?.id??``:c,m=s.data,h=()=>{s.reset(),f(``),n()};return d(st,{open:e,onClose:h,title:o(`certificates.pairDeviceDialog.pairADevice`,`Pair a device`),icon:y,width:`md`,testId:`pair-device-dialog`,error:s.isError?s.error.message:null,footer:m===void 0?a(H,{type:`button`,onClick:()=>{let e=u.trim();p===``||e===``||s.mutate({userId:p,deviceName:e})},disabled:s.isPending||p===``||u.trim()===``,children:[s.isPending&&d(S,{className:`h-3.5 w-3.5 animate-spin`,"aria-hidden":!0}),o(`certificates.pairDeviceDialog.createPairingCode`,`Create pairing code`)]}):d(H,{type:`button`,variant:`outline`,onClick:h,children:o(`certificates.pairDeviceDialog.done`,`Done`)}),children:m===void 0?a(`div`,{className:`space-y-3`,children:[d(`p`,{className:`text-[11px] text-foreground-subtle`,children:o(`certificates.pairDeviceDialog.createsAOneTimeCode`,`Creates a one-time code for a phone you hold. Scan it with the CamStack app on that phone; the phone then stays signed in as the chosen user until you revoke it.`)}),d(Se,{label:o(`certificates.pairDeviceDialog.user`,`User`),required:!0,children:d(Te,{value:p,onChange:e=>l(e.target.value),options:t.map(e=>({value:e.id,label:e.username}))})}),d(Se,{label:o(`certificates.pairDeviceDialog.deviceName`,`Device name`),required:!0,description:o(`certificates.pairDeviceDialog.howThisPhoneIsListed`,`How this phone is listed, e.g. Anna's iPhone.`),children:d(fe,{value:u,maxLength:rr,onChange:e=>f(e.target.value),placeholder:o(`certificates.pairDeviceDialog.annaSIphone`,`Anna's iPhone`),autoFocus:!0})})]}):d(or,{enrollUrl:m.enrollUrl,expiresAt:m.expiresAt,hubKnown:m.hubOrigin!==null,onRestart:()=>s.reset()})})}function or({enrollUrl:e,expiresAt:t,hubKnown:n,onRestart:r}){let{t:i}=Y(),o=Gt(t,nr(1e3));return d(`div`,{className:`space-y-3`,"data-testid":`pair-issued`,children:a(`div`,{className:`flex flex-col items-center gap-3 sm:flex-row sm:items-start`,children:[d(`div`,{className:o.expired?`opacity-30`:void 0,children:d(ge,{value:e,size:ir,alt:i(`certificates.pairDeviceDialog.enrollmentQrCode`,`Enrollment QR code`)})}),a(`div`,{className:`min-w-0 flex-1 space-y-2`,children:[d(`p`,{className:`text-[11px] text-foreground-subtle`,children:i(`certificates.pairDeviceDialog.openTheCamstackAppOn`,`Open the CamStack app on the phone and scan this code. It works once.`)}),d(`div`,{className:`text-sm font-semibold tabular-nums text-foreground`,role:`timer`,"data-testid":`pair-countdown`,children:o.expired?i(`certificates.pairDeviceDialog.expiredCreateANewCode`,`Expired - create a new code`):i(`certificates.pairDeviceDialog.expiresIn`,`Expires in {{label}}`,{label:o.label})}),a(`div`,{className:`flex items-center gap-1 rounded-md border border-border bg-background px-2 py-1`,children:[d(`code`,{className:`min-w-0 flex-1 select-all break-all font-mono text-[11px] text-foreground`,children:e}),d(xe,{value:e,srLabel:`Enrollment link`})]}),!n&&a(`div`,{className:`flex items-start gap-2 rounded-md border border-warning/30 bg-warning/5 px-2.5 py-1.5 text-[11px] text-foreground`,children:[d(g,{className:`mt-0.5 h-3.5 w-3.5 shrink-0 text-warning`,"aria-hidden":!0}),d(`span`,{children:i(`certificates.pairDeviceDialog.theHubCouldNotName`,`The hub could not name an address for this link, so the app must already know it.`)})]}),o.expired&&d(H,{type:`button`,variant:`outline`,size:`sm`,onClick:r,children:i(`certificates.pairDeviceDialog.newCode`,`New code`)})]})]})})}e();var sr={key:`access.nouns.device`};function cr({pairOpen:e,onPairClose:t}){let{t:i}=Y(),o=G(),s=Ie(),c=q.useUtils(),l=q.deviceCredentials.listDeviceCredentials.useQuery({includeRevoked:!0}),u=we(),[p,m]=r(null),[g,_]=r(null),b=()=>void c.deviceCredentials.listDeviceCredentials.invalidate(),x=e=>t=>{_(null),m(`${e} failed: ${t.message}`)},S=()=>{_(null),b()},C=q.deviceCredentials.revokeDeviceCredential.useMutation({onSuccess:S,onError:x(`Revoke`)}),w=q.deviceCredentials.deleteDeviceCredential.useMutation({onSuccess:e=>{e.result!==`deleted`&&m(e.result===`live`?`Only a revoked device can be deleted: revoke it first.`:`That device no longer exists.`),S()},onError:x(`Delete`)}),T=q.deviceCredentials.renameDeviceCredential.useMutation({onSuccess:S,onError:x(`Rename`)}),E=Bt(sr,e=>C.mutateAsync({id:e}),b),D=E.busy||g!==null,O=n(()=>(l.data??[]).map(Wt),[l.data]),k=async e=>{await o({title:i(`certificates.devicesCard.revoke`,`Revoke "{{name}}"?`,{name:e.name}),message:i(`certificates.devicesCard.sDeviceStopsSigningIn`,`{{ownerName}}'s device stops signing in at once and its open sessions end. To use it again it has to be paired anew. This cannot be undone.`,{ownerName:e.ownerName}),confirmLabel:i(`certificates.devicesCard.revoke2`,`Revoke`),variant:`danger`})&&(m(null),_(e.id),C.mutate({id:e.id}))},A=async e=>{await o({title:i(`certificates.devicesCard.delete`,`Delete "{{name}}"?`,{name:e.name}),message:i(`certificates.devicesCard.isRemovedFromTheList`,`{{name}} is removed from the list. The audit trail keeps the revocation.`,{name:e.name}),confirmLabel:i(`common.delete`,`Delete`),variant:`danger`})&&(m(null),_(e.id),w.mutate({id:e.id}))},ee=async e=>{let t=(await s({title:i(`certificates.devicesCard.renameDevice`,`Rename device`),label:i(`certificates.devicesCard.deviceName`,`Device name`),defaultValue:e.name,confirmLabel:i(`certificates.devicesCard.rename`,`Rename`)}))?.trim()??``;t===``||t===e.name||(m(null),_(e.id),T.mutate({id:e.id,deviceName:t}))},j={label:i(`certificates.devicesCard.revoke2`,`Revoke`),icon:f,isAvailable:Nt,onRun:e=>void k(e),busyId:C.isPending?g:null,disabled:D},M=[{label:i(`certificates.devicesCard.rename`,`Rename`),icon:v,tone:`neutral`,isAvailable:()=>!0,onRun:e=>void ee(e),busyId:T.isPending?g:null,disabled:D},{label:i(`common.delete`,`Delete`),icon:h,isAvailable:zt,onRun:e=>void A(e),busyId:w.isPending?g:null,disabled:D}];return a(X,{id:`devices`,title:i(`certificates.devicesCard.devices`,`Devices`),icon:y,description:i(`certificates.devicesCard.phonesThatStaySignedIn`,`Phones that stay signed in with a certificate instead of a login. Revoking one signs it out at once; it has to be paired again.`),notice:Z(p,E.outcome),onDismissNotice:()=>{m(null),E.dismiss()},children:[d(tr,{}),d(Vt,{rows:O,label:i(`certificates.devicesCard.label`),isLoading:l.isPending,loadError:l.error===null?null:l.error.message||`Request failed`,emptyText:`No devices paired yet.`,expiresNone:`—`,action:j,extraActions:M,bulk:{revoke:E,noun:sr,isSelectable:Nt}}),d(ar,{open:e,users:u.data??[],onClose:t,onPaired:b})]})}var lr={ok:`text-foreground-subtle`,soon:`text-warning`,expired:`text-danger`,unknown:`text-foreground-subtle`};function ur(e){let t=e.find(e=>e.isHub);return t?.https.kind===`served`?t.https.status.caFingerprintSha256:null}function dr({https:e,mesh:t,now:n}){let{t:r}=Y(),i=tt().data?.caCertPem??``,o=()=>{if(i===``)return;let e=URL.createObjectURL(new Blob([i],{type:`application/x-pem-file`})),t=document.createElement(`a`);t.href=e,t.download=`camstack-ca.crt`,t.click(),URL.revokeObjectURL(e)},s=t?.ca,c=s?.state===`ready`?Date.parse(s.validTo):null,l=t?.listener;return a(`div`,{className:`space-y-1`,"data-testid":`ca-strip`,children:[d(W,{label:r(`certificates.caStrip.localHttpsCaSha256`,`Local HTTPS CA (SHA-256)`),valueClassName:`font-mono`,children:a(`span`,{className:`flex flex-wrap items-center gap-2`,children:[ur(e)??`—`,i!==``&&a(H,{type:`button`,variant:`ghost`,size:`sm`,onClick:o,children:[d(p,{className:`h-3 w-3`,"aria-hidden":!0}),r(`certificates.caStrip.downloadCa`,`Download CA`)]})]})}),d(W,{label:r(`certificates.caStrip.clusterCaSha256`,`Cluster CA (SHA-256)`),valueClassName:`font-mono`,children:s===void 0?`—`:s.state===`ready`?s.fingerprintSha256:d(`span`,{className:`text-danger`,children:r(`certificates.caStrip.unavailable`,{reason:s.reason})})}),s?.state===`ready`&&a(W,{label:r(`certificates.caStrip.clusterCaValidUntil`,`Cluster CA valid until`),children:[s.validTo,c!==null&&!Number.isNaN(c)&&a(`span`,{className:`ml-2 text-[10px] ${lr[Yt(c,n)]}`,children:[`(`,Ft(c,n),`)`]})]}),l!==void 0&&a(W,{label:r(`certificates.caStrip.nodeListener`,`Node listener`),children:[l.state===`open`&&d(K,{variant:`success`,children:r(`certificates.caStrip.open`,`Open`)}),l.state===`not-started`&&d(K,{variant:`default`,children:r(`certificates.caStrip.notStarted`,`Not started`)}),l.state===`closed`&&a(`span`,{className:`text-danger`,children:[r(`certificates.caStrip.closedSince`,{when:It(l.since)}),` `,l.reason]})]}),t!==void 0&&!t.registryAvailable&&a(`div`,{className:`flex items-start gap-2 rounded-md border border-warning/30 bg-warning/5 px-2.5 py-1.5 text-[11px] text-foreground`,children:[d(g,{className:`mt-0.5 h-3.5 w-3.5 shrink-0 text-warning`,"aria-hidden":!0}),d(`span`,{children:r(`certificates.caStrip.theCertificateRegistryIsNot`,`The certificate registry is not up, so the node list below is incomplete.`)})]})]})}function fr(e,t){let n=new Set;for(let t of e)n.add(t.nodeId);for(let e of t?.nodes??[])n.add(e.nodeId);return[...n].map(n=>{let r=e.find(e=>e.nodeId===n)??null,i=t?.nodes.find(e=>e.nodeId===n)??null;return{nodeId:n,isHub:r?.isHub??n===`hub`,online:r?.online??i?.online??!1,https:r,mesh:i}}).toSorted((e,t)=>Number(t.isHub)-Number(e.isHub)||e.nodeId.localeCompare(t.nodeId))}function pr(e){return e.isHub?J.t(`helpers.nodesModel.hubReissued`):e.mesh===null?J.t(`helpers.nodesModel.noCertRecorded`):e.online?null:J.t(`helpers.certificateModel.nodeOffline`)}function mr(e){return e.isHub||e.mesh===null?pr(e):null}var hr={ok:`text-foreground-subtle`,soon:`text-warning`,expired:`text-danger`,unknown:`text-foreground-subtle`},gr={enroll:`Issued at enrollment`,renewal:`Issued by a renewal`,boot:`Issued at boot`,pairing:`Issued at pairing`,observed:`Observed on its connection`},_r={uploaded:`Uploaded`,generated:`Generated`,disabled:`Disabled`};function vr(e){let t=e.https?.https;return t?.kind===`served`?qt(t.status.validTo):null}function yr({ms:e,now:t}){return e===null?d(`span`,{className:`text-foreground-subtle`,children:`—`}):a(`div`,{children:[d(`div`,{children:It(e)}),d(`div`,{className:`text-[10px] ${hr[Yt(e,t)]}`,children:Ft(e,t)})]})}function br({row:e}){let{t}=Y(),n=e.https?.https;return n===void 0?d(`span`,{className:`text-foreground-subtle`,children:`—`}):n.kind===`served`?a(`div`,{title:n.status.leafFingerprintSha256??``,children:[d(K,{variant:`success`,className:`text-[10px]`,children:t(`certificates.nodesColumns.served`,`Served`)}),d(`span`,{className:`ml-1.5 text-[10px] text-foreground-subtle`,children:_r[n.status.mode]}),d(`div`,{className:`mt-1 font-mono text-[10px] text-foreground-subtle`,children:Kt(n.status.leafFingerprintSha256)}),n.status.sans.length>0&&d(`div`,{className:`mt-0.5 max-w-[14rem] truncate font-mono text-[10px] text-foreground-subtle`,title:n.status.sans.join(`, `),children:n.status.sans.join(`, `)})]}):a(`div`,{className:`max-w-[16rem]`,children:[d(K,{variant:n.kind===`unreadable`?`danger`:`warning`,className:`text-[10px]`,children:n.kind===`unreadable`?t(`certificates.nodesColumns.unreadable`,`Unreadable`):t(`certificates.nodesColumns.notServed`,`Not served`)}),d(`div`,{className:`mt-1 whitespace-normal text-[10px] text-foreground-subtle`,children:n.reason})]})}function xr({row:e}){let{t}=Y(),n=e.mesh?.current??null;if(n===null)return d(`span`,{className:`text-foreground-subtle`,children:t(`certificates.nodesColumns.noneRecorded`,`None recorded`)});let r=e.mesh?.history.length??0;return a(`div`,{children:[d(`div`,{className:`font-mono text-[11px]`,title:n.serial,children:Ut(n.serial)}),d(`div`,{className:`font-mono text-[10px] text-foreground-subtle`,title:n.fingerprintSha256,children:Kt(n.fingerprintSha256)}),a(`div`,{className:`text-[10px] text-foreground-subtle`,children:[gr[n.issuedVia],r>0?` · ${t(`certificates.nodesColumns.earlier`,{count:r})}`:``]})]})}function Sr({row:e}){let{t}=Y(),n=e.mesh?.presentingCurrent??null;return n===null?d(`span`,{className:`text-foreground-subtle`,children:e.online?`—`:t(`certificates.nodesColumns.offline`,`Offline`)}):n?d(K,{variant:`success`,className:`text-[10px]`,children:t(`common.yes`,`Yes`)}):d(K,{variant:`warning`,className:`text-[10px]`,children:t(`certificates.nodesColumns.noPresentsAnother`,`No - presents another`)})}function Cr(e){let t=e.icon;return a(H,{type:`button`,variant:`ghost`,size:`sm`,className:e.danger?`text-danger hover:bg-danger/10`:void 0,disabled:e.reason!==null||e.busy,title:e.reason??void 0,"aria-label":e.ariaLabel,onClick:e.onClick,children:[e.spinning?d(S,{className:`h-3 w-3 animate-spin`,"aria-hidden":!0}):d(t,{className:`h-3 w-3`,"aria-hidden":!0}),e.label]})}function wr({reasons:e}){let{t}=Y();return a(Ge,{className:`inline-block`,children:[d(Fe,{children:d(`span`,{className:`cursor-help text-[11px] text-foreground-subtle`,"data-testid":`hub-managed-note`,children:t(`certificates.nodesColumns.managedByTheHub`,`Managed by the hub`)})}),d(Je,{children:d(`ul`,{className:`max-w-xs space-y-1 whitespace-normal text-left text-[11px]`,children:e.map(e=>d(`li`,{children:e},e))})})]})}function Tr({row:e,o:t}){let{t:n}=Y(),r=e.https===null?`No HTTPS inventory for this node.`:Lt(e.https).reason,i=pr(e),o=mr(e),s=n=>t.running?.nodeId===e.nodeId&&t.running.action===n,c=t=>!e.isHub||t===null,l=[r,i,o].filter((e,t,n)=>e!==null&&n.indexOf(e)===t);return a(`div`,{className:`flex w-44 flex-col items-end gap-1`,children:[a(`div`,{className:`flex flex-col items-end gap-0.5`,children:[c(r)&&d(Cr,{label:n(`certificates.nodesColumns.renewHttps`,`Renew HTTPS`),ariaLabel:n(`certificates.nodesColumns.renewTheCertificateOn`,`Renew the certificate on {{nodeId}}`,{nodeId:e.nodeId}),icon:F,reason:r,busy:t.busy,spinning:s(`renew-https`),danger:!1,onClick:()=>t.onRenewHttps(e)}),c(i)&&d(Cr,{label:s(`renew-identity`)?n(`certificates.nodesColumns.renewing`,`Renewing…`):n(`certificates.nodesColumns.renewIdentity`,`Renew identity`),ariaLabel:n(`certificates.nodesColumns.renewTheClusterCertificateOf`,`Renew the cluster certificate of {{nodeId}}`,{nodeId:e.nodeId}),icon:F,reason:i,busy:t.busy,spinning:s(`renew-identity`),danger:!1,onClick:()=>t.onRenewIdentity(e)}),c(o)&&d(Cr,{label:n(`certificates.nodesColumns.revokeIdentity`,`Revoke identity`),ariaLabel:n(`certificates.nodesColumns.revokeTheClusterCertificateOf`,`Revoke the cluster certificate of {{nodeId}}`,{nodeId:e.nodeId}),icon:f,reason:o,busy:t.busy,spinning:s(`revoke-identity`),danger:!0,onClick:()=>t.onRevokeIdentity(e)})]}),e.isHub&&l.length>0&&d(wr,{reasons:l}),!e.isHub&&r!==null&&e.https!==null&&d(`div`,{className:`w-full whitespace-normal text-right text-[10px] text-foreground-subtle`,"data-testid":`renew-reason-${e.nodeId}`,children:r}),!e.isHub&&i!==null&&d(`div`,{className:`w-full whitespace-normal text-right text-[10px] text-foreground-subtle`,"data-testid":`mesh-reason-${e.nodeId}`,children:i})]})}function Er(e){return[{key:`node`,header:J.t(`certificates.nodesColumns.node`,`Node`),sortValue:e=>e.isHub?``:e.nodeId.toLowerCase(),render:e=>a(`div`,{className:`flex items-center gap-1.5`,children:[d(`span`,{className:`font-medium text-foreground`,children:e.nodeId}),e.nodeId===`hub`&&d(K,{variant:`info`,className:`text-[10px]`,children:J.t(`common.hubWord`)}),!e.online&&d(K,{variant:`default`,className:`text-[10px]`,children:J.t(`common.offline`)})]})},{key:`https-state`,header:`HTTPS`,notSortableReason:`A state with a reason has no single order.`,render:e=>d(br,{row:e})},{key:`https-expires`,header:J.t(`certificates.nodesColumns.httpsExpires`,`HTTPS expires`),sortValue:e=>vr(e),render:t=>d(yr,{ms:vr(t),now:e.now})},{key:`identity`,header:J.t(`certificates.nodesColumns.identity`,`Identity`),sortValue:e=>e.mesh?.current?.serial??null,render:e=>d(xr,{row:e})},{key:`identity-expires`,header:J.t(`certificates.nodesColumns.identityExpires`,`Identity expires`),sortValue:e=>e.mesh?.current?.notAfter??null,render:t=>d(yr,{ms:t.mesh?.current?.notAfter??null,now:e.now})},{key:`presenting`,header:J.t(`certificates.nodesColumns.inUse`,`In use`),notSortableReason:`Offline nodes have no answer to order by.`,render:e=>d(Sr,{row:e})},{key:`actions`,header:J.t(`certificates.nodesColumns.actions`,`Actions`),align:`right`,notSortableReason:`Actions have no order.`,render:t=>d(Tr,{row:t,o:e})}]}e();function Dr(){let{t:e}=Y(),t=G(),i=q.useUtils(),o=nr(6e4),s=q.certificates.listHttpsCertificates.useQuery(),c=q.certificates.getMeshCertificates.useQuery(),[l,u]=r(null),[f,p]=r(null),[m,h]=r(null),g=()=>{h(null),i.certificates.listHttpsCertificates.invalidate(),i.certificates.getMeshCertificates.invalidate()},_=e=>(t,n)=>{h(null),u(`${n.nodeId}: ${e} - ${t.message}`)},v=q.certificates.renewHttpsOnNode.useMutation({onSuccess:(t,n)=>{p({tone:`success`,message:t.restartRequired?e(`certificates.nodesCard.newCertificateIssuedRestartThe`,`{{nodeId}}: new certificate issued. Restart the node to serve it.`,{nodeId:n.nodeId}):e(`certificates.nodesCard.newCertificateIssuedAndServed`,`{{nodeId}}: new certificate issued and served.`,{nodeId:n.nodeId})}),g()},onError:_(`renewal failed`)}),y=q.certificates.renewMeshOnNode.useMutation({onSuccess:e=>{p(Ht(e)),g()},onError:_(`renewal could not start`)}),b=q.certificates.revokeMeshOnNode.useMutation({onSuccess:e=>{p({tone:`success`,message:`${e.nodeId}: ${e.next}`}),g()},onError:_(`revoke failed`)}),x=v.isPending||y.isPending||b.isPending,S=(e,t)=>{u(null),p(null),h({nodeId:e,action:t})},C=async n=>{await t({title:e(`certificates.nodesCard.renewHttpsOn`,`Renew HTTPS on "{{nodeId}}"?`,{nodeId:n.nodeId}),message:n.isHub?e(`certificates.nodesCard.aNewCertificateIsIssued`,`A new certificate is issued from the local CA. Clients that trust the CA keep working.`):e(`certificates.nodesCard.theNodeMakesANew`,`The node makes a new key, the hub signs it and the node switches over without a restart. It keeps serving what it had if anything fails.`),confirmLabel:e(`certificates.nodesCard.renew`,`Renew`)})&&(S(n.nodeId,`renew-https`),v.mutate({nodeId:n.nodeId}))},w=e=>{S(e.nodeId,`renew-identity`),y.mutate({nodeId:e.nodeId})},T=async n=>{await t({title:e(`certificates.nodesCard.revokeTheIdentityOf`,`Revoke the identity of "{{nodeId}}"?`,{nodeId:n.nodeId}),message:e(`certificates.nodesCard.isDisconnectedAtOnceAnd`,`{{nodeId}} is disconnected at once and can no longer connect. To bring it back it must be paired again from System → Cluster → Agents. It stays in the node list until you forget it on the Cluster page. This cannot be undone.`,{nodeId:n.nodeId}),confirmLabel:e(`certificates.nodesCard.revokeAndDisconnect`,`Revoke and disconnect`),variant:`danger`})&&(S(n.nodeId,`revoke-identity`),b.mutate({nodeId:n.nodeId}))},E=Er({now:o,busy:x,running:m,onRenewHttps:e=>void C(e),onRenewIdentity:w,onRevokeIdentity:e=>void T(e)}),D=n(()=>fr(s.data??[],c.data),[s.data,c.data]),O=c.data?.registryAvailable??!0;return d(X,{id:`nodes`,title:e(`certificates.nodesCard.nodes`,`Nodes`),icon:N,description:e(`certificates.nodesCard.eachNodeHoldsTwoCertificates`,`Each node holds two certificates: one it serves over HTTPS, one that proves its identity to the hub. Renewing replaces a certificate without a gap; revoking an identity cuts the node off until it re-joins.`),notice:Z(l,f),onDismissNotice:()=>{u(null),p(null)},children:d($n,{isPending:s.isPending||c.isPending,error:s.error??c.error,what:`node certificates`,onRetry:()=>{s.refetch(),c.refetch()},children:a(`div`,{className:`space-y-3`,children:[d(dr,{https:s.data??[],mesh:c.data,now:o}),d(Ve,{columns:E,rows:D,rowKey:e=>e.nodeId,minWidthPx:960,maxBodyHeight:Ue,defaultSort:{columnKey:`node`,dir:`asc`},emptyMessage:O?e(`certificates.nodesCard.noNodesReported`,`No nodes reported.`):e(`certificates.nodesCard.theCertificateRegistryIsNot`,`The certificate registry is not available yet, so nodes cannot be listed.`)})]})})})}function Or({pairOpen:e,onPairClose:t}){return a(`div`,{className:`space-y-4`,"data-testid":`certificates-page`,children:[d(cr,{pairOpen:e,onPairClose:t}),d(er,{}),d(Dr,{})]})}function kr({userId:e,username:t,onClose:n}){let{t:r}=Y(),i=G(),o=q.useUtils(),s=Pe({userId:e}),c=q.auth.listLoginMethods.useQuery(),l=Ce({onSuccess:()=>void o.userManagement.listExternalIdentities.invalidate()}),u=new Map((c.data??[]).flatMap(e=>e.kind===`redirect`?[[e.id,e.label]]:[])),f=s.data??[],p=async(n,a)=>{await i({title:r(`users.externalIdentitiesModal.removeSignInLink`,`Remove sign-in link`),message:r(`users.externalIdentitiesModal.willNoLongerBeAble`,`"{{username}}" will no longer be able to sign in with this {{label}} account.`,{username:t,label:a}),confirmLabel:r(`common.remove`,`Remove`),variant:`danger`})&&l.mutate({userId:e,provider:n.provider,subject:n.subject})};return d(ze,{open:!0,onOpenChange:e=>!e&&n(),children:a(_e,{width:`md`,placement:`sheet-on-mobile`,scroll:`body`,"data-testid":`external-identities-modal`,children:[d(me,{children:a(et,{className:`flex items-center gap-2`,children:[d(w,{className:`h-4 w-4 text-primary`}),a(`span`,{children:[r(`users.externalIdentitiesModal.linkedSignInAccounts`,`Linked sign-in accounts`),a(`span`,{className:`mt-0.5 block text-[11px] font-normal text-foreground-subtle`,children:[r(`users.externalIdentitiesModal.user`,`User:`),` `,d(`span`,{className:`font-mono`,children:t})]})]})]})}),d(U,{className:`space-y-3`,children:s.isLoading?a(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[d(S,{className:`h-3.5 w-3.5 animate-spin`}),r(`common.loading`,`Loading…`)]}):f.length===0?d(`p`,{className:`text-xs text-foreground-subtle`,children:r(`users.externalIdentitiesModal.noLinkedAccountsOnlyThe`,`No linked accounts. Only the user can link one, from their own profile page.`)}):d(`ul`,{className:`divide-y divide-border rounded-md border border-border`,children:f.map(e=>{let t=u.get(e.provider),n=t??e.provider;return a(`li`,{className:`flex items-center gap-3 px-3 py-2 text-xs`,children:[a(`div`,{className:`flex-1 min-w-0`,children:[a(`div`,{className:`font-medium text-foreground truncate`,children:[n,t===void 0&&d(`span`,{className:`ml-2 rounded bg-foreground-subtle/15 px-1.5 py-0.5 text-[10px] font-normal text-foreground-subtle`,children:r(`users.externalIdentitiesModal.providerUnavailable`,`provider unavailable`)})]}),a(`div`,{className:`text-[11px] text-foreground-subtle truncate`,children:[e.label??e.subject,` ·`,` `,new Date(e.linkedAt).toLocaleDateString()]})]}),a(`button`,{onClick:()=>void p(e,n),disabled:l.isPending,className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] text-danger hover:bg-danger/10 disabled:opacity-50`,children:[d(ie,{className:`h-3 w-3`}),r(`common.remove`,`Remove`)]})]},`${e.provider}|${e.subject}`)})})}),d(rt,{children:d(H,{onClick:n,variant:`secondary`,children:r(`common.close`,`Close`)})})]})})}function Ar({userId:e,username:t,onClose:n}){let{t:r}=Y(),i=c(),o=G(),l=ke({userId:e}),u=l.data,f=()=>i.invalidateQueries({queryKey:[[`userManagement`,`getTotpStatus`]]}),p=$e({onSuccess:()=>{f(),n()}}),m=l.isLoading?`loading`:u?.enabled?`enrolled`:`not_enrolled`;return d(ze,{open:!0,onOpenChange:e=>!e&&n(),children:a(_e,{width:`md`,placement:`sheet-on-mobile`,scroll:`body`,children:[d(me,{children:a(et,{className:`flex items-center gap-2`,children:[d(y,{className:`h-4 w-4 text-primary`}),a(`span`,{children:[r(`users.totpModal.twoFactorAuthentication`,`Two-factor authentication`),a(`span`,{className:`mt-0.5 block text-[11px] font-normal text-foreground-subtle`,children:[r(`users.totpModal.user`,`User:`),` `,d(`span`,{className:`font-mono`,children:t})]})]})]})}),a(U,{className:`space-y-4`,children:[m===`loading`&&a(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[d(S,{className:`h-3.5 w-3.5 animate-spin`}),r(`users.totpModal.checkingStatus`,`Checking status…`)]}),m===`not_enrolled`&&a(`div`,{className:`rounded-md border border-border bg-surface-hover/30 px-3 py-2.5 flex items-start gap-2.5 text-xs text-foreground-subtle`,children:[d(j,{className:`h-4 w-4 mt-0.5 shrink-0`}),a(`div`,{children:[d(`div`,{className:`font-medium text-foreground`,children:r(`users.totpModal.2faNotEnrolled`,`2FA not enrolled`)}),d(`p`,{className:`mt-0.5`,children:r(`users.totpModal.onlyTheUserThemselvesCan`,`Only the user themselves can enable 2FA, from their own profile page.`)})]})]}),m===`enrolled`&&u&&a(s,{children:[a(`div`,{className:`rounded-md border border-emerald-500/30 bg-emerald-500/5 px-3 py-2.5 flex items-start gap-2.5`,children:[d(E,{className:`h-4 w-4 mt-0.5 text-emerald-500 shrink-0`}),a(`div`,{className:`text-xs text-foreground space-y-0.5`,children:[d(`div`,{className:`font-medium`,children:r(`users.totpModal.2faIsActive`,`2FA is active`)}),d(`div`,{className:`text-foreground-subtle`,children:r(`common.enrolledOn`,{when:u.confirmedAt?new Date(u.confirmedAt).toLocaleString():`—`})})]})]}),d(`div`,{className:`text-[11px] text-foreground-subtle`,children:r(`users.totpModal.removingHereClearsTheServer`,`Removing here clears the server-side secret. Use this only when the user has lost access — they'll have to re-enroll from their profile page afterwards.`)}),a(`div`,{className:`flex justify-end gap-2`,children:[d(H,{onClick:n,variant:`secondary`,children:r(`common.close`,`Close`)}),a(`button`,{onClick:async()=>{await o({title:r(`users.totpModal.remove2fa`,`Remove 2FA`),message:r(`users.totpModal.removeTwoFactorAuthenticationFor`,`Remove two-factor authentication for "{{username}}"? They'll be able to log in with just their password until they re-enroll from their own profile page.`,{username:t}),confirmLabel:r(`common.remove`,`Remove`),variant:`danger`})&&p.mutate({userId:e})},disabled:p.isPending,className:`rounded bg-danger px-3 py-1.5 text-xs font-medium text-white hover:bg-danger/90 disabled:opacity-50 inline-flex items-center gap-1`,children:[p.isPending?d(S,{className:`h-3 w-3 animate-spin`}):d(T,{className:`h-3 w-3`}),r(`users.totpModal.remove2fa`,`Remove 2FA`)]})]})]})]})]})})}e();function jr({userId:e,username:t,onClose:n}){let{t:i}=Y(),{user:o,logout:s}=bt(),c=o?.id===e,l=G(),u=q.useUtils(),f=q.auth.listSessions.useQuery({userId:e}),[p,m]=r(null),h=q.auth.revokeSessions.useMutation({onSuccess:(e,t)=>{if(c&&`userId`in t){s();return}u.auth.listSessions.invalidate()},onError:e=>m(`Sign out failed: ${e.message}`)}),g=async e=>{await l({title:i(`users.userSessionsModal.signOutThisSession`,`Sign out this session`),message:i(`users.userSessionsModal.onWillHaveToSign`,`"{{username}}" on {{label}} will have to sign in again.`,{username:t,label:Xt(e.client).label}),confirmLabel:i(`users.userSessionsModal.signOut`,`Sign out`),variant:`danger`})&&(m(null),h.mutate({id:e.id}))},_=async()=>{await l({title:i(`users.userSessionsModal.signOutEverywhere`,`Sign out everywhere`),message:c?i(`users.userSessionsModal.everySessionOfYourOwn`,`Every session of your own account ends, this browser included: you will be signed out here too.`):i(`users.userSessionsModal.everySessionOfEndsAt`,`Every session of "{{username}}" ends at once; each device has to sign in again.`,{username:t}),confirmLabel:i(`users.userSessionsModal.signOutEverywhere`,`Sign out everywhere`),variant:`danger`})&&(m(null),h.mutate({userId:e}))},v=h.isPending&&h.variables!==void 0&&`id`in h.variables?h.variables.id:null;return d(ze,{open:!0,onOpenChange:e=>!e&&n(),children:a(_e,{width:`lg`,placement:`sheet-on-mobile`,scroll:`body`,"data-testid":`user-sessions-modal`,children:[d(me,{children:a(et,{className:`flex items-center gap-2`,children:[d(re,{className:`h-4 w-4 text-primary`}),a(`span`,{children:[i(`users.userSessionsModal.activeSessions`,`Active sessions`),a(`span`,{className:`mt-0.5 block text-[11px] font-normal text-foreground-subtle`,children:[i(`users.userSessionsModal.user`,`User:`),` `,d(`span`,{className:`font-mono`,children:t})]})]})]})}),a(U,{className:`space-y-3`,children:[p!==null&&d(`div`,{className:`text-[11px] text-danger`,children:p}),d(Jt,{sessions:f.data??[],isLoading:f.isLoading,loadError:f.isError?f.error?.message??`Request failed`:null,emptyText:`No active sessions.`,onRevoke:e=>void g(e),revokingId:v})]}),a(rt,{className:`justify-between`,children:[a(`button`,{onClick:()=>void _(),disabled:h.isPending||(f.data??[]).length===0,className:`inline-flex items-center gap-1.5 rounded border border-danger/40 px-2.5 py-1 text-[11px] font-medium text-danger hover:bg-danger/10 disabled:opacity-50`,children:[h.isPending&&v===null?d(S,{className:`h-3 w-3 animate-spin`}):d(z,{className:`h-3 w-3`}),i(`users.userSessionsModal.signOutEverywhere`,`Sign out everywhere`)]}),d(H,{onClick:n,variant:`secondary`,children:i(`common.close`,`Close`)})]})]})})}function Mr({icon:e,label:t,count:n,all:r,total:i}){let{t:o}=Y();return a(`div`,{className:`flex items-center gap-2 text-[11px]`,children:[d(e,{className:`h-3 w-3 shrink-0 text-foreground-subtle`}),d(`span`,{className:`min-w-0 flex-1 text-foreground-subtle`,children:t}),d(`span`,{className:n===0?`text-foreground-subtle`:`font-medium text-foreground`,children:n===0?o(`common.none`):r?o(`users.scopePreview.all`,`all {{total}}`,{total:i}):o(`users.scopePreview.of`,`{{count}} of {{total}}`,{count:n,total:i})})]})}function Nr({scopes:e,isAdmin:t,invalidReason:n}){let{t:r}=Y(),i=ee(),c=o({queryKey:[`auth`,`previewScope`,e,t],enabled:n===null,queryFn:()=>i.trpcClient.auth.previewScope.query({scopes:[...e],isAdmin:t})}),{data:l}=Ze({projection:`slim`}),u=l?.length??0;if(n!==null)return d(`div`,{className:`rounded-lg border border-border bg-surface p-3`,children:a(`p`,{className:`flex items-center gap-1.5 text-[11px] text-warning`,children:[d(M,{className:`h-3 w-3 shrink-0`}),n]})});if(c.isLoading)return a(`div`,{className:`flex items-center gap-2 rounded-lg border border-border bg-surface p-3 text-[11px] text-foreground-subtle`,children:[d(S,{className:`h-3 w-3 animate-spin`}),r(`users.scopePreview.resolving`,`Resolving…`)]});if(c.isError||!c.data)return d(`div`,{className:`rounded-lg border border-border bg-surface p-3`,children:d(`p`,{className:`text-[11px] text-danger`,children:r(`users.scopePreview.couldNotResolveThisGrant`,`Could not resolve this grant set. Saving is still allowed — the server enforces the same rules either way.`)})});let f=c.data;return d(`div`,{className:`space-y-2 rounded-lg border border-border bg-surface p-3`,children:f.isAdmin?a(`p`,{className:`text-[11px] text-foreground`,children:[d(`span`,{className:`font-medium`,children:r(`users.scopePreview.administrator`,`Administrator`)}),` `,r(`users.scopePreview.unrestricted`)]}):a(s,{children:[d(Mr,{icon:m,label:r(`users.scopePreview.devicesTheyCanSee`,`Devices they can see`),count:f.device.view.deviceCount,all:f.device.view.all,total:u}),d(Mr,{icon:v,label:r(`users.scopePreview.devicesTheyCanOperate`,`Devices they can operate`),count:f.device.create.deviceCount,all:f.device.create.all,total:u}),d(Mr,{icon:h,label:r(`users.scopePreview.devicesTheyCanDelete`,`Devices they can delete`),count:f.device.delete.deviceCount,all:f.device.delete.all,total:u}),a(`div`,{className:`border-t border-border pt-2 text-[10px] text-foreground-subtle`,children:[r(`users.scopePreview.systemAccess`,`System access:`),` `,f.system.length===0?r(`common.none`):f.system.join(`, `),f.capabilities.length>0&&a(s,{children:[` `,r(`users.scopePreview.extraCapabilities`,`· Extra capabilities:`),` `,f.capabilities.map(e=>e.name).join(`, `)]}),f.addons.length>0&&a(s,{children:[` `,r(`users.scopePreview.addons`,`· Addons:`),` `,f.addons.map(e=>e.name).join(`, `)]})]}),f.device.view.deviceCount===0&&d(`p`,{className:`text-[10px] text-warning`,children:r(`users.scopePreview.thisUserWillNotBe`,`This user will not be able to see any device.`)})]})})}e();function Pr(e){let t=le(e);return t===null?{mode:e.length===0?`role`:`advanced`,roleId:null,selector:{kind:`all`},includeLinked:!0,advancedScopes:e}:{mode:`role`,roleId:t.roleId,selector:t.selector,includeLinked:t.includeLinked,advancedScopes:e}}function Fr(e){return e.mode===`advanced`?e.advancedScopes:e.roleId===null?[]:oe({roleId:e.roleId,selector:e.selector,includeLinked:e.includeLinked})}function Ir({user:e,onClose:t,onSubmit:i,submitting:o,callerScopes:c}){let{t:l}=Y(),u=O(),f=n(()=>Pr(e.scopes??[]),[e.scopes]),[p,m]=r(f),[h,g]=r(f.mode===`advanced`),_=n(()=>Fr(p),[p]),v=!e.isAdmin&&p.mode===`role`&&p.roleId===null,y=n(()=>e.isAdmin?null:p.mode===`role`&&p.roleId===null?l(`users.userAccessModal.pickAccess`):Ne(_,u),[e.isAdmin,p.mode,p.roleId,_]),b=e=>m({...p,...e});return d(ze,{open:!0,onOpenChange:e=>!e&&t(),children:a(_e,{width:`lg`,placement:`sheet-on-mobile`,scroll:`body`,className:`md:max-w-2xl`,children:[a(me,{children:[d(et,{children:l(`users.userAccessModal.title`,{name:e.username})}),d(`p`,{className:`text-[10px] text-foreground-subtle`,children:e.isAdmin?l(`users.userAccessModal.thisUserIsAnAdministrator`,`This user is an administrator — they can do everything, and the settings below are ignored.`):l(`users.userAccessModal.chooseWhatTheyCanDo`,`Choose what they can do, then which devices it applies to.`)})]}),a(U,{className:`space-y-4`,children:[v&&a(`div`,{className:`flex items-start gap-2 rounded-lg border border-warning/40 bg-warning/10 p-3`,children:[d(M,{className:`mt-0.5 h-3.5 w-3.5 shrink-0 text-warning`}),a(`p`,{className:`text-[11px] text-foreground`,children:[d(`span`,{className:`font-medium`,children:l(`users.userAccessModal.noAccess`,`No access.`)}),` `,d(`span`,{className:`text-foreground-subtle`,children:l(`users.userAccessModal.cannotSeeAnything`,{name:e.username})})]})]}),a(`section`,{className:`space-y-2`,children:[d(`h3`,{className:`text-[11px] font-semibold uppercase tracking-wide text-foreground-subtle`,children:l(`users.userAccessModal.1WhatCanTheyDo`,`1 · What can they do`)}),d(`div`,{className:`space-y-1.5`,children:ce.map(e=>{let t=p.mode===`role`&&p.roleId===e.id;return a(`label`,{className:`flex cursor-pointer items-start gap-2 rounded-lg border p-2.5 transition-colors ${t?`border-primary bg-primary/5`:`border-border hover:bg-foreground-subtle/5`}`,children:[d(`input`,{type:`radio`,name:`access-role`,checked:t,onChange:()=>b({mode:`role`,roleId:e.id}),className:`mt-0.5 border-border focus:ring-primary`}),a(`span`,{className:`min-w-0`,children:[d(`span`,{className:`block text-[12px] font-medium text-foreground`,children:e.label}),d(`span`,{className:`mt-0.5 block text-[10px] text-foreground-subtle`,children:e.description})]})]},e.id)})}),a(`button`,{type:`button`,onClick:()=>{if(!h){m({...p,mode:`advanced`,advancedScopes:_}),g(!0);return}g(!1),m(Pr(p.advancedScopes))},className:`inline-flex items-center gap-1 rounded px-1.5 py-1 text-[10px] text-foreground-subtle transition-colors hover:bg-foreground-subtle/10 hover:text-foreground`,children:[d(h?P:x,{className:`h-3 w-3`}),l(`users.userAccessModal.advancedEditRawGrants`,`Advanced — edit raw grants`)]}),h&&a(`div`,{className:`rounded-lg border border-border p-2`,children:[d(`p`,{className:`mb-2 text-[10px] text-foreground-subtle`,children:l(`users.userAccessModal.oneRowPerGrantAnything`,`One row per grant. Anything expressible here overrides the three roles above; close this section to go back to them.`)}),d(Ae,{value:p.advancedScopes,onChange:e=>b({mode:`advanced`,advancedScopes:e}),clampToParent:c,emptyHint:d(s,{children:d(pt,{i18nKey:`users.userAccessModal.noGrants`,values:{name:e.username},components:{b:d(`span`,{className:`font-medium text-foreground`})}})})})]})]}),p.mode===`role`&&a(`section`,{className:`space-y-2`,children:[d(`h3`,{className:`text-[11px] font-semibold uppercase tracking-wide text-foreground-subtle`,children:l(`users.userAccessModal.2OnWhichDevices`,`2 · On which devices`)}),d(We,{value:p.selector,onChange:e=>b({selector:e}),includeLinked:p.includeLinked,onIncludeLinkedChange:e=>b({includeLinked:e}),clampToDeviceIds:null})]}),a(`section`,{className:`space-y-2`,children:[d(`h3`,{className:`text-[11px] font-semibold uppercase tracking-wide text-foreground-subtle`,children:p.mode===`role`?l(`users.userAccessModal.3Result`,`3 · Result`):l(`users.userAccessModal.2Result`,`2 · Result`)}),d(Nr,{scopes:_,isAdmin:e.isAdmin,invalidReason:y})]})]}),a(rt,{children:[d(H,{variant:`outline`,size:`sm`,onClick:t,children:l(`common.cancel`,`Cancel`)}),d(H,{size:`sm`,onClick:()=>i([..._]),disabled:o||y!==null,children:o?d(S,{className:`h-3 w-3 animate-spin`}):l(`users.userAccessModal.saveAccess`,`Save access`)})]})]})})}e();var Lr=8;function Rr(){return{username:``,password:``,isAdmin:!1}}function zr(e){return{id:e.id,username:e.username,password:``,isAdmin:e.isAdmin}}function Br(e){return e instanceof Error?e.message:J.t(`admin.system.users.genericError`)}function Vr(){let{t:e}=Y(),n=c(),{user:i}=bt(),{state:o,openNew:l,openEdit:u,close:f}=Ee(),[p,m]=r(null),[g,x]=r(null),[C,T]=r(!1),{data:E,isLoading:D,isError:O,error:k,refetch:ee}=we(),j=G(),M=Ie(),N=()=>n.invalidateQueries({queryKey:[[`userManagement`,`listUsers`]]}),P=nt(),F=pe({onSuccess:N}),I=Be({onSuccess:N}),L=De({onSuccess:N}),R=He({onSuccess:()=>{N(),B(null)}}),[z,B]=r(null),[V,ne]=r(null),[ie,ae]=r(null),[oe,se]=r(null),ce=E??[],le=o.mode===`edit`?ce.find(e=>e.id===o.id)??null:null,ue=()=>{m(null),x(null),T(!1),P.reset(),I.reset(),f()};t(()=>{if(o.mode===`closed`){m(null),x(null),T(!1);return}if(o.mode===`new`){m(e=>e!==null&&e.id===void 0?e:Rr());return}if(le===null){D||f();return}m(e=>e?.id===le.id?e:zr(le))},[o,le,D,f]);let de=o.mode===`new`&&p!==null&&p.id===void 0||o.mode===`edit`&&p!==null&&le!==null;async function me(t){await j({title:e(`admin.system.users.deleteUser`,`Delete user`),message:e(`admin.system.users.permanentlyDeleteUserThisCannot`,`Permanently delete user "{{username}}"? This cannot be undone — any active sessions will be revoked on next request.`,{username:t.username}),confirmLabel:e(`common.delete`,`Delete`),variant:`danger`})&&(F.mutate({id:t.id}),o.mode===`edit`&&o.id===t.id&&ue())}async function he(t){let n=await M({title:e(`admin.system.users.resetPassword`,`Reset password`),message:e(`admin.system.users.setANewPasswordFor`,`Set a new password for "{{username}}".`,{username:t.username}),label:e(`admin.system.users.newPassword`,`New password`),inputType:`password`,placeholder:e(`admin.system.users.atLeast8Characters`,`At least 8 characters`),confirmLabel:e(`admin.system.users.continue`,`Continue`),validate:e=>e.length<Lr?J.t(`admin.system.users.passwordTooShort`,{min:Lr}):null});n===null||n.length===0||await j({title:e(`admin.system.users.resetPassword`,`Reset password`),message:e(`admin.system.users.resetThePasswordForThey`,`Reset the password for "{{username}}"? They will need to use the new password on their next login.`,{username:t.username}),confirmLabel:e(`common.reset`,`Reset`)})&&L.mutate({id:t.id,newPassword:n})}function ge(e,t){t!==e.isAdmin&&I.mutate({id:e.id,isAdmin:t})}async function _e(){if(p!==null){x(null);try{if(p.id===void 0){let e=p.username.trim();if(e===``||p.password.length<Lr)return;await P.mutateAsync({username:e,password:p.password,isAdmin:p.isAdmin})}else await I.mutateAsync({id:p.id,isAdmin:p.isAdmin});N(),ue()}catch(e){x(Br(e))}}}function ve(e){return e?new Date(typeof e==`number`?e:String(e)).toLocaleDateString(`en-GB`,{day:`2-digit`,month:`short`,year:`numeric`}):`—`}let U=P.isPending||I.isPending,ye=p!==null&&p.id===void 0&&p.username.trim()!==``&&p.password.length>=Lr,be=p!==null&&p.id!==void 0,xe=U||(p?.id===void 0?!ye:!be),Ce=e=>F.isPending&&F.variables?.id===e.id||I.isPending&&I.variables?.id===e.id||L.isPending&&L.variables?.id===e.id,W=[{key:`username`,header:e(`admin.system.users.username`,`Username`),sortValue:e=>e.username,render:e=>d(`span`,{className:`font-medium text-foreground`,"data-testid":`user-open-${e.id}`,children:e.username})},{key:`isAdmin`,header:e(`admin.system.users.admin`,`Admin`),nowrap:!0,sortValue:e=>+!!e.isAdmin,render:t=>d(`span`,{onClick:e=>e.stopPropagation(),children:a(`label`,{className:`inline-flex items-center gap-1.5 text-xs cursor-pointer`,children:[d(`input`,{type:`checkbox`,checked:t.isAdmin,onChange:e=>ge(t,e.target.checked),disabled:Ce(t),className:`rounded border-border focus:ring-primary disabled:opacity-50`}),d(`span`,{className:t.isAdmin?`text-primary font-medium`:`text-foreground-subtle`,children:t.isAdmin?e(`admin.system.users.admin`,`Admin`):e(`admin.system.users.regular`,`Regular`)})]})})},{key:`scopes`,header:e(`admin.system.users.access`,`Access`),render:t=>t.isAdmin?d(`span`,{className:`text-[10px] italic text-foreground-subtle`,children:e(`admin.system.users.everythingAdmin`,`everything (admin)`)}):d(`span`,{onClick:e=>e.stopPropagation(),children:a(H,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>B(t),title:e(`admin.system.users.configureAccess`,`Configure access`),children:[d(A,{className:`h-3 w-3 shrink-0 text-foreground-subtle`}),d(Hr,{scopes:t.scopes??[]})]})})},{key:`createdAt`,header:e(`admin.system.users.created`,`Created`),nowrap:!0,sortValue:e=>e.createdAt,render:e=>d(`span`,{className:`text-foreground-subtle`,children:ve(e.createdAt)})},{key:`actions`,header:e(`admin.system.users.actions`,`Actions`),align:`right`,nowrap:!0,notSortableReason:`Row actions`,render:t=>{let n=Ce(t),r=t.id!==i?.id&&t.totpEnabled;return a(`div`,{className:`flex items-center justify-end gap-1`,onClick:e=>e.stopPropagation(),children:[a(H,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>u(t.id),disabled:n,title:e(`admin.system.users.editUser`,`Edit user`),children:[d(v,{className:`h-3 w-3`}),e(`common.edit`,`Edit`)]}),r&&a(H,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>ne(t),disabled:n,title:e(`admin.system.users.removeTwoFactorAuthentication`,`Remove two-factor authentication`),children:[d(y,{className:`h-3 w-3`}),e(`admin.system.users.remove2fa`,`Remove 2FA`)]}),a(H,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>ae(t),disabled:n,title:e(`admin.system.users.linkedSignInAccounts`,`Linked sign-in accounts`),children:[d(w,{className:`h-3 w-3`}),e(`admin.system.users.signInLinks`,`Sign-in links`)]}),a(H,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>se(t),disabled:n,title:e(`admin.system.users.activeSessions`,`Active sessions`),children:[d(re,{className:`h-3 w-3`}),e(`admin.system.users.sessions`,`Sessions`)]}),a(H,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>void he(t),disabled:n,title:e(`admin.system.users.resetPassword`,`Reset password`),children:[d(te,{className:`h-3 w-3`}),e(`admin.system.users.resetPassword`,`Reset password`)]}),a(H,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>void me(t),disabled:n,title:e(`admin.system.users.deleteUser`,`Delete user`),className:`text-danger hover:bg-danger/10`,children:[n?d(S,{className:`h-3 w-3 animate-spin`}):d(h,{className:`h-3 w-3`}),e(`common.delete`,`Delete`)]})]})}}];return a(St,{embedded:!0,icon:b,title:e(`admin.system.users.users`,`Users`),subtitle:e(`admin.system.users.hubAccountsAdminFlagAccess`,`Hub accounts, admin flag, access scopes, sessions and sign-in links.`),actions:a(H,{size:`sm`,type:`button`,onClick:()=>{x(null),l()},children:[d(_,{className:`h-3.5 w-3.5`}),e(`admin.system.users.createUser`,`Create User`)]}),children:[O&&d(Oe,{title:e(`admin.system.users.couldNotLoadUsers`,`Could not load users`),message:Br(k),onRetry:()=>void ee()}),D&&a(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[d(S,{className:`h-3.5 w-3.5 animate-spin`}),e(`common.loading`,`Loading…`)]}),!D&&!O&&ce.length===0&&d(Re,{icon:b,title:e(`admin.system.users.noUsersYet`,`No users yet`),description:e(`admin.system.users.createAUserToGrant`,`Create a user to grant hub access.`)}),!D&&!O&&ce.length>0&&d(Ve,{columns:W,rows:ce,rowKey:e=>e.id,minWidthPx:560,maxBodyHeight:`max-h-[60vh]`,onRowClick:e=>u(e.id)}),d(st,{open:de,onClose:ue,title:p?.id===void 0?e(`admin.system.users.createUser`,`Create User`):e(`admin.system.users.edit`,`Edit {{value1}}`,{value1:p.username||`user`}),icon:b,error:g,testId:`user-form-dialog`,footer:d(H,{size:`sm`,type:`button`,disabled:xe,onClick:()=>void _e(),children:U?a(s,{children:[d(S,{className:`h-3.5 w-3.5 animate-spin`}),e(`admin.system.users.saving`,`Saving…`)]}):p?.id===void 0?e(`admin.system.users.create`,`Create`):e(`common.save`,`Save`)}),children:p!==null&&a(`div`,{className:`space-y-3`,children:[d(Se,{label:e(`admin.system.users.username`,`Username`),required:p.id===void 0,children:d(fe,{value:p.username,onChange:e=>m({...p,username:e.target.value}),placeholder:`john`,autoFocus:p.id===void 0,disabled:p.id!==void 0,"data-testid":`user-form-username`})}),p.id===void 0&&d(Se,{label:e(`admin.system.users.password`,`Password`),required:!0,description:e(`admin.system.users.atLeastCharacters`,`At least {{value1}} characters.`,{value1:String(Lr)}),children:d(Ke,{value:p.password,onValueChange:e=>m({...p,password:e}),shown:C,onToggleShown:()=>T(e=>!e),placeholder:`••••••••`,"data-testid":`user-form-password`})}),d(Se,{label:e(`admin.system.users.admin`,`Admin`),description:e(`admin.system.users.unrestrictedAccessToEveryCapability`,`Unrestricted access to every capability and device.`),children:a(`label`,{className:`inline-flex items-center gap-2 text-xs text-foreground cursor-pointer`,children:[d(`input`,{type:`checkbox`,checked:p.isAdmin,onChange:e=>m({...p,isAdmin:e.target.checked}),className:`rounded border-border focus:ring-primary`,"data-testid":`user-form-admin`}),d(`span`,{children:e(`admin.system.users.admin`,`Admin`)})]})}),p.id!==void 0&&d(`p`,{className:`text-[11px] text-foreground-subtle`,children:e(`admin.system.users.toChangeThePasswordUse`,`To change the password, use Reset password on the user's row. Access scopes are configured from the Access column.`)})]})}),V&&d(Ar,{userId:V.id,username:V.username,onClose:()=>ne(null)}),ie&&d(kr,{userId:ie.id,username:ie.username,onClose:()=>ae(null)}),oe&&d(jr,{userId:oe.id,username:oe.username,onClose:()=>se(null)}),z&&d(Ur,{user:z,onClose:()=>B(null),onSubmit:e=>R.mutate({userId:z.id,scopes:e}),submitting:R.isPending})]})}function Hr({scopes:e}){let{t}=Y(),n=O();if(e.length===0)return d(`span`,{className:`font-medium text-warning`,children:t(`admin.system.users.noAccess`,`No access`)});let r=le(e);return r===null?d(`span`,{className:`text-foreground-subtle`,children:t(`users.customGrants`,{count:e.length})}):a(`span`,{className:`text-foreground-subtle`,children:[d(`span`,{className:`font-medium text-foreground`,children:ue(r.roleId).label}),` ·`,` `,qe(r.selector,n)]})}function Ur(e){let{user:t}=bt(),n=t?.isAdmin===!0;return d(Ir,{...e,callerScopes:n?null:t?.scopes??[]})}e();var Wr={"api-keys":$.tokens,"linked-sessions":$.linkedApps},Gr=`certificates`;function Kr(e){return a(H,{size:`sm`,type:`button`,"data-testid":`certificates-pair-device`,onClick:e,children:[d(y,{className:`h-3.5 w-3.5`,"aria-hidden":!0}),J.t(`systemPage.identity.pairADevice`,`Pair a device`)]})}function qr(){let{t:e}=Y(),[t]=gt(),n=Rn(),[i,a]=r(!1),o=Wr[t.get(`tab`)??``];if(o!==void 0)return d(mt,{to:_t(`access`,{integration:o}),replace:!0});let s=ut(`/system/identity`,{t:e,isAdmin:!0})??{icon:k,title:e(`system.identity`,`Identity & Access`)},c=t.get(`tab`)??`users`,l=t.get(`integration`),u=c===ht.access.tab?Ln(l,n.requestCreate):c===`certificates`?Kr(()=>a(!0)):void 0;return d(xt,{icon:s.icon,title:s.title,defaultSection:`users`,sectionsLabel:`${s.title} sections`,actions:u,sections:[{id:`users`,label:e(`systemPage.identity.users`,`Users`),content:d(Vr,{})},{id:ht.access.tab,label:e(`systemPage.identity.tokensSessions`,`Tokens & sessions`),content:d(In,{createRequested:n.createRequested,onCreateConsumed:n.clearCreate}),ownedParams:yt.concat([hn.token,hn.scoped,hn.admin])},{id:Gr,label:e(`systemPage.identity.certificates`,`Certificates`),content:d(Or,{pairOpen:i,onPairClose:()=>a(!1)})},{id:ht.authentication.tab,label:e(`systemPage.identity.authentication`,`Authentication`),content:d(Zn,{}),ownedParams:yt}]})}export{qr as IdentityPage};
|