dsh-vault 1.2.0-rc.2 → 1.2.0-rc.4
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/lib/client.js +179 -61
- package/lib/client.js.map +1 -1
- package/lib/index.js +326 -13
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -17,80 +17,80 @@ window.__ModuleLoader__.load({
|
|
|
17
17
|
document.head.appendChild(tag);
|
|
18
18
|
}
|
|
19
19
|
var VaultSection_module_css_default = {
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"saveButton": "t_j5va_saveButton",
|
|
23
|
-
"emptyHint": "t_j5va_emptyHint",
|
|
20
|
+
"reportBox": "t_j5va_reportBox",
|
|
21
|
+
"modeAuto": "t_j5va_modeAuto",
|
|
24
22
|
"status": "t_j5va_status",
|
|
25
|
-
"kindIcon": "t_j5va_kindIcon",
|
|
26
|
-
"searchBox": "t_j5va_searchBox",
|
|
27
|
-
"intro": "t_j5va_intro",
|
|
28
|
-
"detailItem": "t_j5va_detailItem",
|
|
29
|
-
"policyField": "t_j5va_policyField",
|
|
30
|
-
"field": "t_j5va_field",
|
|
31
|
-
"pwFair": "t_j5va_pwFair",
|
|
32
|
-
"identity": "t_j5va_identity",
|
|
33
|
-
"checkField": "t_j5va_checkField",
|
|
34
|
-
"pwLabel": "t_j5va_pwLabel",
|
|
35
|
-
"dupGroup": "t_j5va_dupGroup",
|
|
36
|
-
"modeReadonly": "t_j5va_modeReadonly",
|
|
37
|
-
"badge": "t_j5va_badge",
|
|
38
|
-
"dupNames": "t_j5va_dupNames",
|
|
39
|
-
"badgeWarn": "t_j5va_badgeWarn",
|
|
40
|
-
"auditRow": "t_j5va_auditRow",
|
|
41
|
-
"genOptRow": "t_j5va_genOptRow",
|
|
42
23
|
"rowDue": "t_j5va_rowDue",
|
|
43
|
-
"
|
|
44
|
-
"detailBox": "t_j5va_detailBox",
|
|
45
|
-
"emptyBox": "t_j5va_emptyBox",
|
|
24
|
+
"section": "t_j5va_section",
|
|
46
25
|
"rowActions": "t_j5va_rowActions",
|
|
47
|
-
"
|
|
26
|
+
"totp": "t_j5va_totp",
|
|
27
|
+
"row": "t_j5va_row",
|
|
48
28
|
"reportTitle": "t_j5va_reportTitle",
|
|
49
|
-
"empty": "t_j5va_empty",
|
|
50
|
-
"pwWeak": "t_j5va_pwWeak",
|
|
51
|
-
"copied": "t_j5va_copied",
|
|
52
|
-
"scoreRing": "t_j5va_scoreRing",
|
|
53
|
-
"dangerButton": "t_j5va_dangerButton",
|
|
54
29
|
"pwBar": "t_j5va_pwBar",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
30
|
+
"addButton": "t_j5va_addButton",
|
|
31
|
+
"intro": "t_j5va_intro",
|
|
32
|
+
"reportLine": "t_j5va_reportLine",
|
|
33
|
+
"backupButton": "t_j5va_backupButton",
|
|
34
|
+
"pwMeter": "t_j5va_pwMeter",
|
|
35
|
+
"policyField": "t_j5va_policyField",
|
|
36
|
+
"badge": "t_j5va_badge",
|
|
37
|
+
"detailItem": "t_j5va_detailItem",
|
|
38
|
+
"identity": "t_j5va_identity",
|
|
39
|
+
"kindIcon": "t_j5va_kindIcon",
|
|
40
|
+
"dupMerge": "t_j5va_dupMerge",
|
|
41
|
+
"trashButton": "t_j5va_trashButton",
|
|
57
42
|
"sortButton": "t_j5va_sortButton",
|
|
43
|
+
"copied": "t_j5va_copied",
|
|
44
|
+
"auditIssues": "t_j5va_auditIssues",
|
|
58
45
|
"editorBody": "t_j5va_editorBody",
|
|
59
|
-
"trashButton": "t_j5va_trashButton",
|
|
60
46
|
"retryButton": "t_j5va_retryButton",
|
|
47
|
+
"genOptRow": "t_j5va_genOptRow",
|
|
48
|
+
"clearButton": "t_j5va_clearButton",
|
|
49
|
+
"kindFilter": "t_j5va_kindFilter",
|
|
50
|
+
"policyBar": "t_j5va_policyBar",
|
|
51
|
+
"pwFair": "t_j5va_pwFair",
|
|
52
|
+
"checkField": "t_j5va_checkField",
|
|
53
|
+
"error": "t_j5va_error",
|
|
54
|
+
"saveButton": "t_j5va_saveButton",
|
|
55
|
+
"rowMain": "t_j5va_rowMain",
|
|
56
|
+
"emptyBox": "t_j5va_emptyBox",
|
|
57
|
+
"editor": "t_j5va_editor",
|
|
58
|
+
"pinStar": "t_j5va_pinStar",
|
|
61
59
|
"title": "t_j5va_title",
|
|
62
|
-
"
|
|
63
|
-
"dupMerge": "t_j5va_dupMerge",
|
|
64
|
-
"reportLine": "t_j5va_reportLine",
|
|
65
|
-
"totp": "t_j5va_totp",
|
|
66
|
-
"reportSub": "t_j5va_reportSub",
|
|
67
|
-
"secretField": "t_j5va_secretField",
|
|
60
|
+
"searchBox": "t_j5va_searchBox",
|
|
68
61
|
"genOpts": "t_j5va_genOpts",
|
|
69
|
-
"
|
|
70
|
-
"
|
|
62
|
+
"toolbar": "t_j5va_toolbar",
|
|
63
|
+
"healthBar": "t_j5va_healthBar",
|
|
64
|
+
"badgeDanger": "t_j5va_badgeDanger",
|
|
65
|
+
"dangerButton": "t_j5va_dangerButton",
|
|
66
|
+
"pwLabel": "t_j5va_pwLabel",
|
|
71
67
|
"revealButton": "t_j5va_revealButton",
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
68
|
+
"badgeWarn": "t_j5va_badgeWarn",
|
|
69
|
+
"field": "t_j5va_field",
|
|
70
|
+
"dupNames": "t_j5va_dupNames",
|
|
71
|
+
"auditRow": "t_j5va_auditRow",
|
|
72
|
+
"totpRing": "t_j5va_totpRing",
|
|
73
|
+
"empty": "t_j5va_empty",
|
|
75
74
|
"lockedBanner": "t_j5va_lockedBanner",
|
|
76
|
-
"modeAuto": "t_j5va_modeAuto",
|
|
77
|
-
"clearButton": "t_j5va_clearButton",
|
|
78
|
-
"badgeDanger": "t_j5va_badgeDanger",
|
|
79
75
|
"modeAsk": "t_j5va_modeAsk",
|
|
76
|
+
"dueBadge": "t_j5va_dueBadge",
|
|
77
|
+
"deleteButton": "t_j5va_deleteButton",
|
|
78
|
+
"rowExpired": "t_j5va_rowExpired",
|
|
79
|
+
"secretField": "t_j5va_secretField",
|
|
80
80
|
"editorActions": "t_j5va_editorActions",
|
|
81
|
-
"policyBar": "t_j5va_policyBar",
|
|
82
|
-
"pwStrong": "t_j5va_pwStrong",
|
|
83
|
-
"healthBar": "t_j5va_healthBar",
|
|
84
|
-
"totpRing": "t_j5va_totpRing",
|
|
85
|
-
"section": "t_j5va_section",
|
|
86
|
-
"toolbar": "t_j5va_toolbar",
|
|
87
|
-
"srOnly": "t_j5va_srOnly",
|
|
88
81
|
"highBadge": "t_j5va_highBadge",
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
82
|
+
"reportSub": "t_j5va_reportSub",
|
|
83
|
+
"modeReadonly": "t_j5va_modeReadonly",
|
|
84
|
+
"list": "t_j5va_list",
|
|
85
|
+
"detailBox": "t_j5va_detailBox",
|
|
86
|
+
"dupGroup": "t_j5va_dupGroup",
|
|
87
|
+
"scoreRing": "t_j5va_scoreRing",
|
|
88
|
+
"badgeOk": "t_j5va_badgeOk",
|
|
89
|
+
"srOnly": "t_j5va_srOnly",
|
|
90
|
+
"footer": "t_j5va_footer",
|
|
91
|
+
"pwWeak": "t_j5va_pwWeak",
|
|
92
|
+
"pwStrong": "t_j5va_pwStrong",
|
|
93
|
+
"emptyHint": "t_j5va_emptyHint"
|
|
94
94
|
};
|
|
95
95
|
//#endregion
|
|
96
96
|
//#region src/client/VaultSection.tsx
|
|
@@ -231,7 +231,7 @@ window.__ModuleLoader__.load({
|
|
|
231
231
|
}
|
|
232
232
|
/** Render the Vault settings section. */
|
|
233
233
|
function VaultSection(props) {
|
|
234
|
-
const { t, config, setAccessMode, setAutoCapture, list, search, get, add, update, remove, trash, rotation, health, duplicates, duplicateGroups, merge, history, stats, backupStatus, backup, recent, restore, undeleteAll, totp, status, switchVault, listVaults, touch, verifyAll, breachCheck, generatePassword, strength, generateUsername } = props;
|
|
234
|
+
const { t, config, setAccessMode, setAutoCapture, list, search, get, add, update, remove, trash, rotation, health, duplicates, duplicateGroups, merge, history, stats, backupStatus, backup, recent, restore, undeleteAll, totp, status, switchVault, listVaults, touch, verifyAll, breachCheck, generatePassword, strength, generateUsername, templates, saveTemplate } = props;
|
|
235
235
|
const searchId = (0, react.useId)();
|
|
236
236
|
const [query, setQuery] = (0, react.useState)("");
|
|
237
237
|
const [state, setState] = (0, react.useState)({ status: "loading" });
|
|
@@ -255,6 +255,7 @@ window.__ModuleLoader__.load({
|
|
|
255
255
|
});
|
|
256
256
|
const [showGenOpts, setShowGenOpts] = (0, react.useState)(false);
|
|
257
257
|
const [pwStrength, setPwStrength] = (0, react.useState)(null);
|
|
258
|
+
const [tplList, setTplList] = (0, react.useState)([]);
|
|
258
259
|
const [kindFilter, setKindFilter] = (0, react.useState)("");
|
|
259
260
|
const [tagFilter, setTagFilter] = (0, react.useState)("");
|
|
260
261
|
const [expandedId, setExpandedId] = (0, react.useState)(null);
|
|
@@ -276,6 +277,43 @@ window.__ModuleLoader__.load({
|
|
|
276
277
|
const [audit, setAudit] = (0, react.useState)([]);
|
|
277
278
|
const [breach, setBreach] = (0, react.useState)(null);
|
|
278
279
|
const readonly = policy?.accessMode === "readonly";
|
|
280
|
+
(0, react.useEffect)(() => {
|
|
281
|
+
const url = form.url ?? "";
|
|
282
|
+
if (url.length === 0 || (form.icon ?? "").length > 0) return;
|
|
283
|
+
const host = url.replace(/^https?:\/\//i, "").split("/")[0].toLowerCase();
|
|
284
|
+
const map = {
|
|
285
|
+
github: "🐙",
|
|
286
|
+
gitlab: "🦊",
|
|
287
|
+
google: "🔎",
|
|
288
|
+
"google.com": "🔎",
|
|
289
|
+
amazon: "📦",
|
|
290
|
+
apple: "",
|
|
291
|
+
aws: "☁️",
|
|
292
|
+
azure: "☁️",
|
|
293
|
+
digitalocean: "🐳",
|
|
294
|
+
docker: "🐳",
|
|
295
|
+
npm: "📦",
|
|
296
|
+
figma: "🎨",
|
|
297
|
+
notion: "📝",
|
|
298
|
+
slack: "💬",
|
|
299
|
+
discord: "🎮",
|
|
300
|
+
twitter: "🐦",
|
|
301
|
+
facebook: "👥",
|
|
302
|
+
instagram: "📷",
|
|
303
|
+
linkedin: "💼",
|
|
304
|
+
youtube: "▶️",
|
|
305
|
+
netflix: "🎬",
|
|
306
|
+
spotify: "🎵",
|
|
307
|
+
dropbox: "📁",
|
|
308
|
+
drive: "🗂️"
|
|
309
|
+
};
|
|
310
|
+
const hostBase = host.split(".")[0] ?? "";
|
|
311
|
+
const icon = map[host] ?? map[hostBase] ?? "";
|
|
312
|
+
if (icon) setForm((previous) => ({
|
|
313
|
+
...previous,
|
|
314
|
+
icon
|
|
315
|
+
}));
|
|
316
|
+
}, [form.url, form.icon]);
|
|
279
317
|
(0, react.useEffect)(() => {
|
|
280
318
|
const pw = form.password ?? "";
|
|
281
319
|
if (pw.length === 0) {
|
|
@@ -290,6 +328,44 @@ window.__ModuleLoader__.load({
|
|
|
290
328
|
}, 250);
|
|
291
329
|
return () => window.clearTimeout(timer);
|
|
292
330
|
}, [form.password, strength]);
|
|
331
|
+
/** Apply a template's field values to the current form. */
|
|
332
|
+
function applyTemplate(name) {
|
|
333
|
+
const tpl = tplList.find((t) => t.name === name);
|
|
334
|
+
if (!tpl) return;
|
|
335
|
+
const next = {};
|
|
336
|
+
if (tpl.kind !== "builtin:custom") next.kind = tpl.kind.replace("builtin:", "");
|
|
337
|
+
for (const [key, value] of Object.entries(tpl.fields)) {
|
|
338
|
+
if (key === "password" || key === "otpSecret" || key === "apiKey" || key === "secret") continue;
|
|
339
|
+
next[key] = value;
|
|
340
|
+
}
|
|
341
|
+
setForm((previous) => ({
|
|
342
|
+
...previous,
|
|
343
|
+
...next
|
|
344
|
+
}));
|
|
345
|
+
}
|
|
346
|
+
/** Save the current form as a reusable template. */
|
|
347
|
+
async function saveAsTemplate() {
|
|
348
|
+
const name = window.prompt(t("tplNamePrompt"));
|
|
349
|
+
if (!name) return;
|
|
350
|
+
const fields = {};
|
|
351
|
+
for (const key of [
|
|
352
|
+
"username",
|
|
353
|
+
"email",
|
|
354
|
+
"phone",
|
|
355
|
+
"host",
|
|
356
|
+
"port",
|
|
357
|
+
"url",
|
|
358
|
+
"notes",
|
|
359
|
+
"icon",
|
|
360
|
+
"color"
|
|
361
|
+
]) {
|
|
362
|
+
const v = form[key];
|
|
363
|
+
if (typeof v === "string" && v.length > 0) fields[key] = v;
|
|
364
|
+
}
|
|
365
|
+
await saveTemplate(name.trim(), form.kind ?? "login", fields);
|
|
366
|
+
await templates().then(setTplList).catch(() => {});
|
|
367
|
+
setMessage(`${t("tplSaved")} ${name.trim()}`);
|
|
368
|
+
}
|
|
293
369
|
/** Switch the active vault and reload everything. */
|
|
294
370
|
async function switchVaultTo(name) {
|
|
295
371
|
setBusy(true);
|
|
@@ -363,13 +439,15 @@ window.__ModuleLoader__.load({
|
|
|
363
439
|
if (current) setLocked(value.locked);
|
|
364
440
|
}, () => {});
|
|
365
441
|
listVaults().then(setVaults).catch(() => {});
|
|
442
|
+
templates().then(setTplList).catch(() => {});
|
|
366
443
|
return () => {
|
|
367
444
|
current = false;
|
|
368
445
|
};
|
|
369
446
|
}, [
|
|
370
447
|
config,
|
|
371
448
|
status,
|
|
372
|
-
listVaults
|
|
449
|
+
listVaults,
|
|
450
|
+
templates
|
|
373
451
|
]);
|
|
374
452
|
const refresh = (0, react.useMemo)(() => async () => {
|
|
375
453
|
setState({ status: "loading" });
|
|
@@ -1482,6 +1560,32 @@ window.__ModuleLoader__.load({
|
|
|
1482
1560
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1483
1561
|
className: VaultSection_module_css_default.editorBody,
|
|
1484
1562
|
children: [
|
|
1563
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1564
|
+
className: VaultSection_module_css_default.genOpts,
|
|
1565
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1566
|
+
className: VaultSection_module_css_default.genOptRow,
|
|
1567
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("tplApply") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1568
|
+
value: "",
|
|
1569
|
+
onChange: (event) => {
|
|
1570
|
+
const v = event.target.value;
|
|
1571
|
+
if (v) applyTemplate(v);
|
|
1572
|
+
},
|
|
1573
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1574
|
+
value: "",
|
|
1575
|
+
children: "—"
|
|
1576
|
+
}), tplList.map((tpl) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1577
|
+
value: tpl.name,
|
|
1578
|
+
children: tpl.name
|
|
1579
|
+
}, tpl.name))]
|
|
1580
|
+
})]
|
|
1581
|
+
}), editor.status === "editing" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1582
|
+
type: "button",
|
|
1583
|
+
className: VaultSection_module_css_default.retryButton,
|
|
1584
|
+
onClick: () => void saveAsTemplate(),
|
|
1585
|
+
disabled: busy,
|
|
1586
|
+
children: t("tplSave")
|
|
1587
|
+
})]
|
|
1588
|
+
}),
|
|
1485
1589
|
FORM_FIELDS.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1486
1590
|
className: VaultSection_module_css_default.field,
|
|
1487
1591
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(field.label) }), field.key === "kind" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
@@ -1775,6 +1879,10 @@ window.__ModuleLoader__.load({
|
|
|
1775
1879
|
genOptSymbols: "Symbols",
|
|
1776
1880
|
genOptExcludeAmbiguous: "No ambiguous chars",
|
|
1777
1881
|
strengthLabel: "Strength",
|
|
1882
|
+
tplApply: "Apply template",
|
|
1883
|
+
tplSave: "Save as template",
|
|
1884
|
+
tplNamePrompt: "Template name:",
|
|
1885
|
+
tplSaved: "Template saved:",
|
|
1778
1886
|
httpSites: "HTTP sites",
|
|
1779
1887
|
healthStrength: "strength",
|
|
1780
1888
|
dueExpired: "EXPIRED",
|
|
@@ -1913,6 +2021,10 @@ window.__ModuleLoader__.load({
|
|
|
1913
2021
|
genOptSymbols: "符号",
|
|
1914
2022
|
genOptExcludeAmbiguous: "排除易混淆字符",
|
|
1915
2023
|
strengthLabel: "强度",
|
|
2024
|
+
tplApply: "套用模板",
|
|
2025
|
+
tplSave: "存为模板",
|
|
2026
|
+
tplNamePrompt: "模板名称:",
|
|
2027
|
+
tplSaved: "模板已保存:",
|
|
1916
2028
|
httpSites: "HTTP站点",
|
|
1917
2029
|
healthStrength: "强度",
|
|
1918
2030
|
dueExpired: "已过期",
|
|
@@ -2043,6 +2155,12 @@ window.__ModuleLoader__.load({
|
|
|
2043
2155
|
breachCheck: (online) => invoke("breachCheck", { online: online ?? true }),
|
|
2044
2156
|
generatePassword: (options) => invoke("generatePassword", { ...options ?? {} }),
|
|
2045
2157
|
strength: (password) => invoke("strength", { password }),
|
|
2158
|
+
templates: () => invoke("templates"),
|
|
2159
|
+
saveTemplate: (name, kind, fields) => invoke("saveTemplate", {
|
|
2160
|
+
name,
|
|
2161
|
+
kind,
|
|
2162
|
+
fields
|
|
2163
|
+
}),
|
|
2046
2164
|
generateUsername: () => invoke("generateUsername"),
|
|
2047
2165
|
duplicateGroups: () => invoke("duplicateGroups"),
|
|
2048
2166
|
merge: (fromId, toId, keepSource) => invoke("merge", {
|