w-web-perm 1.1.12 → 1.1.13
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/README.md +158 -123
- package/dist/css/app.f09c7433.css +1 -0
- package/dist/index.tmp +1 -1
- package/dist/js/app-legacy.8af87052.js +1 -0
- package/dist/js/app.8af87052.js +1 -0
- package/dist/js/{chunk-vendors.3e9e3ef1.js → chunk-vendors-legacy.ece5a361.js} +1 -1
- package/dist/js/{chunk-vendors-legacy.3e9e3ef1.js → chunk-vendors.ece5a361.js} +1 -1
- package/dist/w-web-perm.umd.js +2 -2
- package/dist/w-web-perm.umd.js.map +1 -1
- package/docs/WWebPerm.html +84 -4
- package/docs/global.html +23023 -1125
- package/docs/index.html +2 -2
- package/docs/server_WWebPerm.mjs.html +232 -32
- package/docs/server_cascadeWrite.mjs.html +225 -0
- package/docs/server_dataChannel.mjs.html +2 -2
- package/docs/server_keyCheck.mjs.html +353 -0
- package/docs/server_lockSave.mjs.html +2 -2
- package/docs/server_maskLog.mjs.html +2 -2
- package/docs/server_procCore.mjs.html +180 -41
- package/docs/server_staLogs_resolveShellWorker.mjs.html +2 -2
- package/docs/server_syncKeep.mjs.html +311 -0
- package/docs/server_tools_authRefsAudit.mjs.html +247 -0
- package/docs/server_tools_authRefsClean.mjs.html +335 -0
- package/docs/server_tools_fromTools.mjs.html +797 -0
- package/docs/server_tools_isMain.mjs.html +103 -0
- package/docs/server_tools_nameDupReport.mjs.html +280 -0
- package/docs/server_validateRows.mjs.html +2 -2
- package/docs/server_wrapInjected.mjs.html +3 -3
- package/docs/src_fetchJson.mjs.html +2 -2
- package/docs/src_js_cvKpToArr.mjs.html +2 -2
- package/docs/src_js_cvKpToObj.mjs.html +44 -33
- package/docs/src_js_draftSync.mjs.html +142 -0
- package/docs/src_js_eachOwn.mjs.html +3 -3
- package/docs/src_js_editorRefRows.mjs.html +356 -0
- package/docs/src_js_externalRows.mjs.html +338 -0
- package/docs/src_js_externalUi.mjs.html +230 -0
- package/docs/src_js_findRefRows.mjs.html +107 -0
- package/docs/src_js_findTouchedDup.mjs.html +150 -0
- package/docs/src_js_fmtLossyMsg.mjs.html +2 -2
- package/docs/src_js_fromAudit.mjs.html +489 -0
- package/docs/src_js_getAddImpact.mjs.html +269 -0
- package/docs/src_js_getAuthWarn.mjs.html +463 -0
- package/docs/src_js_getCascadePreview.mjs.html +258 -0
- package/docs/src_js_getDelRefCount.mjs.html +210 -0
- package/docs/src_js_getEffMode.mjs.html +92 -0
- package/docs/src_js_getKpLossy.mjs.html +47 -52
- package/docs/src_js_getNameErrs.mjs.html +266 -0
- package/docs/src_js_getOwn.mjs.html +3 -3
- package/docs/src_js_getPropKey.mjs.html +109 -0
- package/docs/src_js_getRuleEnable.mjs.html +2 -2
- package/docs/src_js_isAuthMode.mjs.html +4 -4
- package/docs/src_js_joinNames.mjs.html +2 -2
- package/docs/src_js_legendFit.mjs.html +2 -2
- package/docs/src_js_pairRowsToSnapshot.mjs.html +156 -0
- package/docs/src_js_refAudit.mjs.html +359 -0
- package/docs/src_js_refCascade.mjs.html +572 -0
- package/docs/src_js_relDialogSend.mjs.html +199 -0
- package/docs/src_js_rowDigest.mjs.html +265 -0
- package/docs/src_plugins_dataReloadGuard.mjs.html +109 -0
- package/docs/src_plugins_reuseDeletedIds.mjs.html +2 -2
- package/docs/src_plugins_submitGuard.mjs.html +2 -2
- package/g_authRefsClean.mjs +79 -0
- package/g_fixFromType.mjs +92 -0
- package/g_initialData.mjs +3 -3
- package/g_purgeFrom.mjs +127 -0
- package/package.json +1 -1
- package/server/WWebPerm.mjs +230 -30
- package/server/cascadeWrite.mjs +153 -0
- package/server/keyCheck.mjs +281 -0
- package/server/procCore.mjs +178 -39
- package/server/procLang.mjs +270 -3
- package/server/syncKeep.mjs +239 -0
- package/server/tools/authRefsAudit.mjs +175 -0
- package/server/tools/authRefsClean.mjs +263 -0
- package/server/tools/fromTools.mjs +725 -0
- package/server/tools/isMain.mjs +31 -0
- package/server/tools/nameDupReport.mjs +208 -0
- package/server/wrapInjected.mjs +1 -1
- package/src/components/LayoutContentGrups.vue +396 -75
- package/src/components/LayoutContentPemis.vue +395 -75
- package/src/components/LayoutContentTargets.vue +261 -25
- package/src/components/LayoutContentUsers.vue +334 -97
- package/src/components/VeCgrups.vue +88 -129
- package/src/components/VeCpemis.vue +86 -129
- package/src/components/VeCrules.vue +15 -5
- package/src/components/VeGrupBlngUsers.vue +202 -89
- package/src/components/VePemiBlngGrups.vue +209 -99
- package/src/js/cvKpToObj.mjs +42 -31
- package/src/js/draftSync.mjs +70 -0
- package/src/js/eachOwn.mjs +1 -1
- package/src/js/editorRefRows.mjs +284 -0
- package/src/js/externalRows.mjs +266 -0
- package/src/js/externalUi.mjs +158 -0
- package/src/js/findRefRows.mjs +35 -0
- package/src/js/findTouchedDup.mjs +78 -0
- package/src/js/fromAudit.mjs +417 -0
- package/src/js/getAddImpact.mjs +197 -0
- package/src/js/getAuthWarn.mjs +391 -0
- package/src/js/getCascadePreview.mjs +186 -0
- package/src/js/getDelRefCount.mjs +138 -0
- package/src/js/getEffMode.mjs +20 -0
- package/src/js/getKpLossy.mjs +44 -49
- package/src/js/getNameErrs.mjs +194 -0
- package/src/js/getOwn.mjs +1 -1
- package/src/js/getPropKey.mjs +37 -0
- package/src/js/isAuthMode.mjs +2 -2
- package/src/js/pairRowsToSnapshot.mjs +84 -0
- package/src/js/refAudit.mjs +287 -0
- package/src/js/refCascade.mjs +500 -0
- package/src/js/relDialogSend.mjs +127 -0
- package/src/js/rowDigest.mjs +193 -0
- package/src/plugins/dataReloadGuard.mjs +37 -0
- package/src/plugins/mShare.mjs +144 -140
- package/src/plugins/mUI.mjs +3 -8
- package/src/schema/tables/users.mjs +152 -152
- package/dist/css/app.6918ec45.css +0 -1
- package/dist/js/app-legacy.7cb7e394.js +0 -1
- package/dist/js/app.7cb7e394.js +0 -1
- package/docs/src_js_getKeyOrderLost.mjs.html +0 -121
- package/docs/src_js_getKpWriteLost.mjs.html +0 -100
- package/docs/src_js_keepKeyOrder.mjs.html +0 -139
- package/src/js/getKeyOrderLost.mjs +0 -49
- package/src/js/getKpWriteLost.mjs +0 -28
- package/src/js/keepKeyOrder.mjs +0 -67
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import { fileURLToPath } from 'url'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 判斷呼叫端模組是否為 node 直接執行之入口(CLI 區塊的進入條件)
|
|
7
|
+
*
|
|
8
|
+
* 套件經 symlink/junction 安裝(npm link、pnpm、workspaces)時, node 以真實路徑載入入口模組(import.meta.url 為真實路徑),
|
|
9
|
+
* 而 process.argv[1] 維持命令列所給之路徑, 兩者直接比較不相等, CLI 區塊靜默不執行(無報告卻正常結束; run08 外審 sol #5).
|
|
10
|
+
* 故兩邊皆取真實路徑再比對(Windows 路徑不分大小寫).
|
|
11
|
+
*
|
|
12
|
+
* @param {String} importMetaUrl 輸入呼叫端的 import.meta.url
|
|
13
|
+
* @param {String} [argv1=process.argv[1]] 輸入命令列給 node 的入口路徑, 預設 process.argv[1]
|
|
14
|
+
* @returns {Boolean} 回傳呼叫端模組即為入口為 true; 無法判定(路徑不存在等)為 false
|
|
15
|
+
*/
|
|
16
|
+
function isMain(importMetaUrl, argv1 = process.argv[1]) {
|
|
17
|
+
if (typeof argv1 !== 'string' || argv1 === '') {
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
let a = fs.realpathSync.native(fileURLToPath(importMetaUrl))
|
|
22
|
+
let b = fs.realpathSync.native(argv1)
|
|
23
|
+
return process.platform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
export default isMain
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
//名稱重複與使用者對應鍵之唯讀稽核工具(F-02/F-14; 規則帳本 R-NAME-UNIQ-03; ADR-034). 不屬測試, 不寫任何資料, 不提供自動刪除/改名——清理由管理者於後台逐一處理並確認授權.
|
|
2
|
+
//
|
|
3
|
+
//用法(自專案根; 套件安裝後此檔位於 node_modules/w-web-perm/server/tools/, 以該路徑執行即可, 匯出檔路徑相對於目前工作目錄):
|
|
4
|
+
// node server/tools/nameDupReport.mjs <匯出檔.json> [--mapping-by id|email|name] [--json]
|
|
5
|
+
// node server/tools/nameDupReport.mjs --audit <匯出檔.json> [--mapping-by id|email|name] [--json] (同上; 與 authMergeReport.mjs --audit 寫法一致)
|
|
6
|
+
//
|
|
7
|
+
// 匯出檔內容 { users, grups, pemis, targets }(各為列陣列, 如以資料通道 select 之結果或資料庫匯出)。
|
|
8
|
+
// --mapping-by 省略時 email 與 name 皆報告(部署之 mappingBy 設定不在資料庫內); 給 id 則只查 id 缺漏(id 為主鍵, 不會重複).
|
|
9
|
+
// --json 輸出結構化結果(供其他工具), 省略則輸出文字報告.
|
|
10
|
+
//
|
|
11
|
+
//報告內容:
|
|
12
|
+
// 1. grups/pemis 同名組: 各筆之 id/from/isActive/order, 以及被哪些 users.cgrups(查 grups 名)/grups.cpemis(查 pemis 名)以該名稱引用(只計名稱字串出現之引用; 解碼用 src/js/cvKpToArr.mjs,
|
|
13
|
+
// JSON5 解析失敗之列容錯: 視為無引用並列入「壞 JSON」清單). 比對為精確 ===(同伺服端 arrFinder).
|
|
14
|
+
// 已知邊界(增量外審 mmd-N8, 未處理): 陣列形式之引用元素缺 name 時, 伺服端 arrFinder 以 get(vv,'name','') 取得 ''(可對應名稱為 '' 之列), 本工具解碼後名稱為索引數字, 不計入該引用;
|
|
15
|
+
// 須同時存在「名稱為空字串之群組/權限」與「陣列形式且缺 name 之引用」, 空名稱者已另由 authMergeReport --audit 列出, 請一併人工檢視.
|
|
16
|
+
// 2. users 對應鍵: isActive==='y' 之列, 對應鍵(email/name/id)為空/空白/非字串者, 以及值重複者(精確 ===; 與 getTokenUser 之 select 一致), 附各列 id/name/from.
|
|
17
|
+
//
|
|
18
|
+
//【清理須知】引用以「名稱」比對, 改名級聯(F-07)尚未實作: 同名組中改名或刪除其一, 引用該名稱者會全部指向剩下的那一筆或失效.
|
|
19
|
+
// 請先以本報告列出引用者, 清理後逐一確認授權(getPermUserInfor/getPerm 之結果), 再處理下一組.
|
|
20
|
+
// 報告含 email(對應鍵)原文, 請妥善保管、勿外傳.
|
|
21
|
+
|
|
22
|
+
import fs from 'fs'
|
|
23
|
+
import isMain from './isMain.mjs'
|
|
24
|
+
import JSON5 from 'json5'
|
|
25
|
+
import cvKpToArr from '../../src/js/cvKpToArr.mjs'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
let isStr = (v) => typeof v === 'string'
|
|
29
|
+
let arr = (v) => (Array.isArray(v) ? v : [])
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
//decodeNames: 解碼授權字串(cgrups/cpemis)為 [{ name, mode, isActive }], 失敗回 null(壞 JSON); 空字串視為無內容([])
|
|
33
|
+
function decodeRefs(s) {
|
|
34
|
+
if (!isStr(s) || s.trim() === '') {
|
|
35
|
+
return []
|
|
36
|
+
}
|
|
37
|
+
let kp = null
|
|
38
|
+
try {
|
|
39
|
+
kp = JSON5.parse(s)
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
//名稱保留原型別: 伺服端 arrFinder 以 === 比對, 非字串名稱(如 1、['1'])不會對應任何群組/權限, 不得轉字串後報成引用者(外審 sol R8)
|
|
46
|
+
return cvKpToArr(kp).map((r) => ({ name: r.name, mode: r.mode, isActive: r.isActive }))
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
//dupGroups: 表內 name 為字串且出現 ≥ 2 次者之組(精確 ===)
|
|
55
|
+
function dupGroups(rows) {
|
|
56
|
+
let kp = new Map()
|
|
57
|
+
for (let r of arr(rows)) {
|
|
58
|
+
if (r && isStr(r.name)) {
|
|
59
|
+
if (!kp.has(r.name)) {
|
|
60
|
+
kp.set(r.name, [])
|
|
61
|
+
}
|
|
62
|
+
kp.get(r.name).push(r)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return [...kp].filter(([, rs]) => rs.length >= 2).map(([name, rs]) => ({ name, rows: rs }))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
//refsTo: 以 refTable 各列之授權欄位(refCol)引用 name 者
|
|
70
|
+
function refsTo(name, refRows, refCol, badJson) {
|
|
71
|
+
let out = []
|
|
72
|
+
for (let r of arr(refRows)) {
|
|
73
|
+
let ds = decodeRefs(r && r[refCol])
|
|
74
|
+
if (ds === null) {
|
|
75
|
+
badJson.push({ table: refCol === 'cgrups' ? 'users' : 'grups', id: r.id })
|
|
76
|
+
continue
|
|
77
|
+
}
|
|
78
|
+
for (let d of ds) {
|
|
79
|
+
if (d.name === name) {
|
|
80
|
+
out.push({ id: r.id, name: r.name, mode: d.mode, isActive: d.isActive })
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return out
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
//mapKeyIssues: isActive==='y' 之 users, 對應鍵 key 為空/空白/非字串(empty), 以及值重複(dup)
|
|
89
|
+
function mapKeyIssues(users, key) {
|
|
90
|
+
let act = arr(users).filter((u) => u && u.isActive === 'y')
|
|
91
|
+
let brief = (u) => ({ id: u.id, name: u.name, from: u.from })
|
|
92
|
+
let empty = act.filter((u) => !(isStr(u[key]) && u[key].trim().length > 0)).map(brief)
|
|
93
|
+
let kp = new Map()
|
|
94
|
+
for (let u of act) {
|
|
95
|
+
if (isStr(u[key]) && u[key].trim().length > 0) {
|
|
96
|
+
if (!kp.has(u[key])) {
|
|
97
|
+
kp.set(u[key], [])
|
|
98
|
+
}
|
|
99
|
+
kp.get(u[key]).push(brief(u))
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
let dup = [...kp].filter(([, rs]) => rs.length >= 2).map(([value, rows]) => ({ value, rows }))
|
|
103
|
+
return { empty, dup }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 建立稽核結果(純函數, 不讀寫檔案)
|
|
109
|
+
*
|
|
110
|
+
* @param {Object} data { users, grups, pemis, targets }
|
|
111
|
+
* @param {Object} [opt={}] 設定
|
|
112
|
+
* @param {String} [opt.mappingBy] 'id'|'email'|'name', 省略時 email 與 name 皆報告
|
|
113
|
+
* @returns {Object} 稽核結果
|
|
114
|
+
*/
|
|
115
|
+
function buildReport(data, opt = {}) {
|
|
116
|
+
let d = data || {}
|
|
117
|
+
let badJson = []
|
|
118
|
+
let brief = (r) => ({ id: r.id, from: r.from, isActive: r.isActive, order: r.order })
|
|
119
|
+
let grups = dupGroups(d.grups).map((g) => ({
|
|
120
|
+
name: g.name,
|
|
121
|
+
rows: g.rows.map(brief),
|
|
122
|
+
refs: refsTo(g.name, d.users, 'cgrups', badJson), //users.cgrups 以群組名引用
|
|
123
|
+
}))
|
|
124
|
+
let pemis = dupGroups(d.pemis).map((g) => ({
|
|
125
|
+
name: g.name,
|
|
126
|
+
rows: g.rows.map(brief),
|
|
127
|
+
refs: refsTo(g.name, d.grups, 'cpemis', badJson), //grups.cpemis 以權限名引用
|
|
128
|
+
}))
|
|
129
|
+
//壞 JSON 全表掃描(外審 sol R9): 不論有無同名組, users.cgrups/grups.cpemis 解析失敗者皆列出(refsTo 只掃同名組之引用者, 會漏列)
|
|
130
|
+
for (let [rows, col, table] of [[d.users, 'cgrups', 'users'], [d.grups, 'cpemis', 'grups']]) {
|
|
131
|
+
for (let r of arr(rows)) {
|
|
132
|
+
if (r && decodeRefs(r[col]) === null) {
|
|
133
|
+
badJson.push({ table, id: r.id })
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
let keys = (opt.mappingBy === 'id' || opt.mappingBy === 'email' || opt.mappingBy === 'name') ? [opt.mappingBy] : ['email', 'name']
|
|
138
|
+
let mapKey = {}
|
|
139
|
+
for (let key of keys) {
|
|
140
|
+
mapKey[key] = (key === 'id')
|
|
141
|
+
? { empty: arr(d.users).filter((u) => u && u.isActive === 'y' && !(isStr(u.id) && u.id !== '')).map((u) => ({ id: u.id, name: u.name, from: u.from })), dup: [] }
|
|
142
|
+
: mapKeyIssues(d.users, key)
|
|
143
|
+
}
|
|
144
|
+
//壞 JSON 去重(同一列被多個重複名稱之解碼重複記錄)
|
|
145
|
+
let seen = new Set()
|
|
146
|
+
badJson = badJson.filter((b) => {
|
|
147
|
+
let k = `${b.table}:${b.id}`
|
|
148
|
+
return seen.has(k) ? false : (seen.add(k), true)
|
|
149
|
+
})
|
|
150
|
+
return {
|
|
151
|
+
counts: { users: arr(d.users).length, grups: arr(d.grups).length, pemis: arr(d.pemis).length, targets: arr(d.targets).length },
|
|
152
|
+
grups,
|
|
153
|
+
pemis,
|
|
154
|
+
mapKey,
|
|
155
|
+
badJson,
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
//formatReport: 結果轉文字
|
|
161
|
+
function formatReport(rp) {
|
|
162
|
+
let L = []
|
|
163
|
+
let rowTxt = (r) => `id=${r.id} from=${JSON.stringify(r.from)} isActive=${JSON.stringify(r.isActive)} order=${r.order}`
|
|
164
|
+
L.push(`資料筆數: users ${rp.counts.users}、grups ${rp.counts.grups}、pemis ${rp.counts.pemis}、targets ${rp.counts.targets}`)
|
|
165
|
+
for (let [tab, refFrom, label] of [['grups', 'users.cgrups', '群組'], ['pemis', 'grups.cpemis', '權限']]) {
|
|
166
|
+
L.push('')
|
|
167
|
+
L.push(`== ${label}(${tab})同名組: ${rp[tab].length} 組 ==`)
|
|
168
|
+
for (let g of rp[tab]) {
|
|
169
|
+
L.push(`- 「${g.name}」${g.rows.length} 筆`)
|
|
170
|
+
g.rows.forEach((r) => L.push(` ${rowTxt(r)}`))
|
|
171
|
+
L.push(` 被 ${refFrom} 引用: ${g.refs.length} 處`)
|
|
172
|
+
g.refs.forEach((r) => L.push(` ${tab === 'grups' ? 'user' : 'grup'} id=${r.id} name=${JSON.stringify(r.name)} mode=${JSON.stringify(r.mode)} isActive=${JSON.stringify(r.isActive)}`))
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
for (let [key, v] of Object.entries(rp.mapKey)) {
|
|
176
|
+
L.push('')
|
|
177
|
+
L.push(`== users 對應鍵 ${key}(僅 isActive='y'): 空/空白/非字串 ${v.empty.length} 筆, 值重複 ${v.dup.length} 組 ==`)
|
|
178
|
+
v.empty.forEach((u) => L.push(`- 空: id=${u.id} name=${JSON.stringify(u.name)} from=${JSON.stringify(u.from)}`))
|
|
179
|
+
for (let g of v.dup) {
|
|
180
|
+
L.push(`- 重複「${g.value}」${g.rows.length} 筆`)
|
|
181
|
+
g.rows.forEach((u) => L.push(` id=${u.id} name=${JSON.stringify(u.name)} from=${JSON.stringify(u.from)}`))
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
L.push('')
|
|
185
|
+
L.push(`== 授權字串壞 JSON(解析失敗, 視為無引用): ${rp.badJson.length} 列 ==`)
|
|
186
|
+
rp.badJson.forEach((b) => L.push(`- ${b.table} id=${b.id}`))
|
|
187
|
+
return L.join('\n')
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
//CLI
|
|
192
|
+
if (isMain(import.meta.url)) {
|
|
193
|
+
let args = process.argv.slice(2)
|
|
194
|
+
let iMap = args.indexOf('--mapping-by')
|
|
195
|
+
let mappingBy = (iMap >= 0) ? args[iMap + 1] : undefined
|
|
196
|
+
let iAudit = args.indexOf('--audit')
|
|
197
|
+
let fp = (iAudit >= 0) ? args[iAudit + 1] : args.find((a, k) => !a.startsWith('--') && !(iMap >= 0 && k === iMap + 1))
|
|
198
|
+
if (!fp || (iMap >= 0 && !['id', 'email', 'name'].includes(mappingBy))) {
|
|
199
|
+
console.log('用法: node server/tools/nameDupReport.mjs <匯出檔.json> [--mapping-by id|email|name] [--json]')
|
|
200
|
+
process.exit(2)
|
|
201
|
+
}
|
|
202
|
+
let data = JSON.parse(fs.readFileSync(fp, 'utf8'))
|
|
203
|
+
let rp = buildReport(data, { mappingBy })
|
|
204
|
+
console.log(args.includes('--json') ? JSON.stringify(rp, null, 1) : formatReport(rp))
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
export { buildReport, formatReport, decodeRefs }
|
package/server/wrapInjected.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import isfun from 'wsemi/src/isfun.mjs'
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
//keysUpstream: 上游失敗可照記之 key 白名單 = w-web-sso 對外 helper 之 K 契約 14 個 key(w-web-sso ADR-068)
|
|
14
|
-
//與 sso 之同步點: sso 之 K 契約增減 key 時須同步本表(見
|
|
14
|
+
//與 sso 之同步點: sso 之 K 契約增減 key 時須同步本表(見 sgud/rule/rulebook.md「權杖不外洩」)
|
|
15
15
|
let keysUpstream = [
|
|
16
16
|
'invalidUrl',
|
|
17
17
|
'invalidTokenSelf',
|