w-web-perm 1.0.78 → 1.0.80
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/index.tmp +1 -1
- package/dist/js/app-legacy.85fce10c.js +1 -0
- package/dist/js/app.85fce10c.js +1 -0
- package/dist/js/{chunk-vendors-legacy.884ef407.js → chunk-vendors-legacy.b57e1cce.js} +2 -2
- package/dist/js/{chunk-vendors.884ef407.js → chunk-vendors.b57e1cce.js} +2 -2
- package/dist/w-web-perm.umd.js +2 -2
- package/dist/w-web-perm.umd.js.map +1 -1
- package/docs/WWebPerm.html +4 -4
- package/docs/global.html +610 -0
- package/docs/index.html +2 -2
- package/docs/{WWebPerm.mjs.html → server_WWebPerm.mjs.html} +84 -94
- package/docs/server_procCore.mjs.html +311 -0
- package/docs/src_fetchJson.mjs.html +113 -0
- package/package.json +18 -19
- package/server/WWebPerm.mjs +80 -90
- package/server/procCore.mjs +56 -4
- package/server/procLang.mjs +3 -3
- package/server/provideTabs.mjs +8 -9
- package/src/components/LayoutContentGrups.vue +5 -1
- package/src/components/LayoutContentPemis.vue +5 -1
- package/src/components/LayoutContentTargets.vue +5 -1
- package/src/components/LayoutContentUsers.vue +21 -8
- package/src/components/VeCgrups.vue +3 -3
- package/src/components/VeCpemis.vue +2 -2
- package/src/components/VeGrupBlngUsers.vue +2 -2
- package/src/components/VePemiBlngGrups.vue +2 -2
- package/src/fetchJson.mjs +41 -0
- package/src/getPerm.mjs +7 -7
- package/src/getPermUserInfor.mjs +7 -7
- package/src/main.js +1 -1
- package/src/perm.mjs +5 -4
- package/src/plugins/mUI.mjs +17 -0
- package/srv.mjs +8 -0
- package/tableTags-web-perm.json +1 -0
- package/dist/js/app-legacy.71fa8c3b.js +0 -1
- package/dist/js/app.71fa8c3b.js +0 -1
- package/tableTags.json +0 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
|
|
5
5
|
<meta charset="utf-8">
|
|
6
|
-
<title>WWebPerm.mjs - Documentation</title>
|
|
6
|
+
<title>server/WWebPerm.mjs - Documentation</title>
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
<script src="scripts/prettify/prettify.js"></script>
|
|
@@ -29,13 +29,13 @@
|
|
|
29
29
|
<nav >
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WWebPerm.html">WWebPerm</a></li></ul>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WWebPerm.html">WWebPerm</a></li></ul><h3>Global</h3><ul><li><a href="global.html#fetchJson">fetchJson</a></li><li><a href="global.html#mergePickKeysOnly">mergePickKeysOnly</a></li></ul>
|
|
33
33
|
|
|
34
34
|
</nav>
|
|
35
35
|
|
|
36
36
|
<div id="main">
|
|
37
37
|
|
|
38
|
-
<h1 class="page-title">WWebPerm.mjs</h1>
|
|
38
|
+
<h1 class="page-title">server/WWebPerm.mjs</h1>
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
|
|
@@ -88,7 +88,7 @@ import procStaInfor from './procStaInfor.mjs'
|
|
|
88
88
|
* @param {String} url 輸入資料庫連線字串,例如w-orm-lmdb為'./db',或w-orm-mongodb為'mongodb://username:password@$127.0.0.1:27017'
|
|
89
89
|
* @param {String} db 輸入資料庫名稱字串
|
|
90
90
|
* @param {Function} getUserByToken 輸入處理函數,函數會傳入使用者token,通過此函數處理後並回傳使用者資訊物件,並至少須提供'id'、'email'、'name'、'isAdmin'欄位,且'isAdmin'限輸入'y'或'n'。注意: token 回傳之'isAdmin'僅供識別,權限系統一律以自身users表該使用者之'isAdmin'欄位值為權威,不會被 token 宣稱之值複寫
|
|
91
|
-
* @param {Function} verifyClientUser
|
|
91
|
+
* @param {Function} verifyClientUser 輸入驗證瀏覽使用者身份之處理函數,函數會傳入使用者資訊物件(perm users 表之列)與來源字串from,通過此函數識別後回傳布林值(可為Promise),允許使用者回傳true,反之回傳false。from 值域: 'getUserByToken'(登入)、'getPerm'(api/getPerm)、以及資料通道之 apiType 'main'(RPC 與 ORM 執行)、'upload-controller'、'upload-slice'、'download-get-filename'、'download-get-file'、'download';資料通道與登入採同一授權強度(反查 perm users 且 isActive='y' 後再經本函數)
|
|
92
92
|
* @param {Function} verifyAppUser 輸入驗證應用程序使用者身份之處理函數,函數會傳入使用者資訊物件,通過此函數識別後回傳布林值,允許使用者回傳true,反之回傳false
|
|
93
93
|
* @param {Object} [opt={}] 輸入設定物件,預設{}
|
|
94
94
|
* @param {Integer} [opt.serverPort=11006] 輸入伺服器通訊port,預設11006
|
|
@@ -396,34 +396,6 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
396
396
|
}
|
|
397
397
|
|
|
398
398
|
|
|
399
|
-
// //getTargetsList
|
|
400
|
-
// let getTargetsList = async (userId) => {
|
|
401
|
-
// let rs = await woItems.targets.select()
|
|
402
|
-
// return rs
|
|
403
|
-
// }
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
// //getPemisList
|
|
407
|
-
// let getPemisList = async (userId) => {
|
|
408
|
-
// let rs = await woItems.pemis.select()
|
|
409
|
-
// return rs
|
|
410
|
-
// }
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
// //getGrupsList
|
|
414
|
-
// let getGrupsList = async (userId) => {
|
|
415
|
-
// let rs = await woItems.grups.select()
|
|
416
|
-
// return rs
|
|
417
|
-
// }
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
// //getUsersList
|
|
421
|
-
// let getUsersList = async (userId) => {
|
|
422
|
-
// let rs = await woItems.users.select()
|
|
423
|
-
// return rs
|
|
424
|
-
// }
|
|
425
|
-
|
|
426
|
-
|
|
427
399
|
//checkUser
|
|
428
400
|
let checkUser = async(user) => {
|
|
429
401
|
let id = get(user, 'id', '')
|
|
@@ -513,22 +485,6 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
513
485
|
}
|
|
514
486
|
|
|
515
487
|
|
|
516
|
-
//p
|
|
517
|
-
let p = {
|
|
518
|
-
checkToken: async(token) => {
|
|
519
|
-
|
|
520
|
-
//getUserByToken
|
|
521
|
-
let u = await getUserByToken(token)
|
|
522
|
-
// console.log('u', u)
|
|
523
|
-
|
|
524
|
-
//checkUser
|
|
525
|
-
await checkUser(u)
|
|
526
|
-
|
|
527
|
-
return true
|
|
528
|
-
},
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
|
|
532
488
|
//getAndVerifyClientUser
|
|
533
489
|
let getAndVerifyClientUser = async (token, from = '') => {
|
|
534
490
|
//基於token查找client使用者, 會基於外部getUserByToken以及perm內部users進行查找並比對
|
|
@@ -591,6 +547,25 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
591
547
|
}
|
|
592
548
|
|
|
593
549
|
|
|
550
|
+
//p
|
|
551
|
+
let p = {
|
|
552
|
+
//checkToken, 資料通道(/api/main 之 RPC 與 ORM 執行、上傳、下載)之連線驗證, 與 api/getUserByToken(登入)採同一授權強度:
|
|
553
|
+
//外部 getUserByToken → 欄位齊備 → 反查 perm users(mappingBy)且 isActive='y' → verifyClientUser(user, from)
|
|
554
|
+
//why: 2026-09-07 前僅驗外部 token 欄位齊備(checkUser), 不查 perm users 與 isActive, 亦不經 verifyClientUser,
|
|
555
|
+
//致不在 perm users 表之外部使用者、被停用(isActive='n')之使用者仍可經資料通道讀寫全部權限資料(實測 A3/A4, 見 tmp/probe-report.json);
|
|
556
|
+
//改為與登入入口對稱, 使 UI 之「是否有效」與部署層 verifyClientUser 政策對資料通道亦生效.
|
|
557
|
+
//from 為 w-converhp 之 apiType('main' / 'upload-controller' / 'upload-slice' / 'download-get-filename' / 'download-get-file' / 'download'),
|
|
558
|
+
//供部署層 verifyClientUser(user, from) 區分入口.
|
|
559
|
+
checkToken: async(token, from = '') => {
|
|
560
|
+
|
|
561
|
+
//getAndVerifyClientUser, reject 時 key: cannotFindUserFromToken / cannotGetUserFromPerm / userNoPermission 等
|
|
562
|
+
await getAndVerifyClientUser(token, from)
|
|
563
|
+
|
|
564
|
+
return true
|
|
565
|
+
},
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
|
|
594
569
|
//parsePayload
|
|
595
570
|
let parsePayload = async (req) => {
|
|
596
571
|
|
|
@@ -645,49 +620,49 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
645
620
|
//序列化同表之並行整表 delAll+insert: 防 lost update (同 keyTable 序列化, 不同表並行)
|
|
646
621
|
return await kmx('syncAndReplaceTabs:' + keyTable, async () => {
|
|
647
622
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
623
|
+
//from
|
|
624
|
+
let from = get(inp, 'from', '')
|
|
625
|
+
// console.log('from', from)
|
|
651
626
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
627
|
+
//check
|
|
628
|
+
if (!isestr(from)) {
|
|
629
|
+
console.log('inp', inp)
|
|
630
|
+
srLog.error({ event: 'syncAndReplaceTabs-error', msg: 'invalidFrom', keyTable })
|
|
631
|
+
return Promise.reject(`invalidFrom`)
|
|
632
|
+
}
|
|
658
633
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
634
|
+
//rows
|
|
635
|
+
let rows = get(inp, 'rows', [])
|
|
636
|
+
// console.log(keyTable, rows)
|
|
662
637
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
638
|
+
//check
|
|
639
|
+
if (!isearr(rows)) {
|
|
640
|
+
console.log('inp', inp)
|
|
641
|
+
console.log('keyTable', keyTable)
|
|
642
|
+
srLog.error({ event: 'syncAndReplaceTabs-error', msg: 'invalidRows', keyTable })
|
|
643
|
+
return Promise.reject(`invalidRows`)
|
|
644
|
+
}
|
|
670
645
|
|
|
671
|
-
|
|
672
|
-
|
|
646
|
+
//save from
|
|
647
|
+
rows = map(rows, (v, k) => {
|
|
673
648
|
// v.order = k + 1 //order大部分由外部給予, 不能複寫
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
649
|
+
v.from = from
|
|
650
|
+
return v
|
|
651
|
+
})
|
|
677
652
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
653
|
+
//ltdtmapping
|
|
654
|
+
rows = ltdtmapping(rows, ds[keyTable].keys)
|
|
655
|
+
// console.log('ltdtmapping rows', rows)
|
|
681
656
|
|
|
682
|
-
|
|
683
|
-
|
|
657
|
+
//delAll from
|
|
658
|
+
await woItems[keyTable].delAll({ from })
|
|
684
659
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
660
|
+
//insert
|
|
661
|
+
// let r = await woItems[keyTable].insert(rows)
|
|
662
|
+
let r = await procOrm(userId, keyTable, 'insert', rows) //須使用procOrm才有辦法自動給予相關欄位
|
|
663
|
+
// console.log('r', r)
|
|
689
664
|
|
|
690
|
-
|
|
665
|
+
return r
|
|
691
666
|
|
|
692
667
|
})
|
|
693
668
|
}
|
|
@@ -1129,8 +1104,16 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
1129
1104
|
return false
|
|
1130
1105
|
}
|
|
1131
1106
|
|
|
1132
|
-
//checkToken
|
|
1133
|
-
let b =
|
|
1107
|
+
//checkToken, 失敗一律回 false(傳輸層回 permission denied); reject 之 err key 記於 verifyConn log 之 msg
|
|
1108
|
+
let b = false
|
|
1109
|
+
let msg = ''
|
|
1110
|
+
await p.checkToken(token, apiType)
|
|
1111
|
+
.then(() => {
|
|
1112
|
+
b = true
|
|
1113
|
+
})
|
|
1114
|
+
.catch((err) => {
|
|
1115
|
+
msg = (typeof err === 'string') ? err : ((err && err.message) || 'checkTokenFail')
|
|
1116
|
+
})
|
|
1134
1117
|
// console.log('b', b)
|
|
1135
1118
|
|
|
1136
1119
|
//ip, 對齊sso慣例x-forwarded-for取第1個, fallback到req remoteAddress, 取不到則略過
|
|
@@ -1145,8 +1128,13 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
1145
1128
|
//referer
|
|
1146
1129
|
let referer = get(headers, 'referer', '')
|
|
1147
1130
|
|
|
1148
|
-
//info
|
|
1149
|
-
|
|
1131
|
+
//info, 失敗時 msg 為 err key(cannotFindUserFromToken / cannotGetUserFromPerm / userNoPermission 等), 供資安稽核與統計
|
|
1132
|
+
if (b) {
|
|
1133
|
+
srLog.info({ event: 'verifyConn', ok: b, apiType, ip, origin, referer })
|
|
1134
|
+
}
|
|
1135
|
+
else {
|
|
1136
|
+
srLog.error({ event: 'verifyConn-error', ok: b, apiType, ip, origin, referer, msg })
|
|
1137
|
+
}
|
|
1150
1138
|
|
|
1151
1139
|
return b
|
|
1152
1140
|
},
|
|
@@ -1173,15 +1161,14 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
1173
1161
|
|
|
1174
1162
|
getWebInfor,
|
|
1175
1163
|
|
|
1176
|
-
// getTargetsList,
|
|
1177
|
-
// getPemisList,
|
|
1178
|
-
// getGrupsList,
|
|
1179
|
-
// getUsersList,
|
|
1180
|
-
|
|
1181
1164
|
updateTargets: pc.updateTargets,
|
|
1182
1165
|
updatePemis: pc.updatePemis,
|
|
1183
1166
|
updateGrups: pc.updateGrups,
|
|
1184
|
-
updateUsers:
|
|
1167
|
+
//updateUsers, modeEditUsers='for:grups' 時 API 與 UI 一致只採納既有使用者之 cgrups 變更, 身分欄/新增/刪除/順序以資料庫為準
|
|
1168
|
+
updateUsers: (userId, rows) => {
|
|
1169
|
+
let opt = (modeEditUsers === 'for:grups') ? { pickKeysOnly: ['cgrups'] } : {}
|
|
1170
|
+
return pc.updateUsers(userId, rows, opt)
|
|
1171
|
+
},
|
|
1185
1172
|
|
|
1186
1173
|
getStaEvent: async (userId, timeLength, timeInterval) => {
|
|
1187
1174
|
try {
|
|
@@ -1208,7 +1195,10 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
1208
1195
|
},
|
|
1209
1196
|
|
|
1210
1197
|
},
|
|
1211
|
-
|
|
1198
|
+
//fpTableTags: 各資料表時間戳檔案(供前端判定是否需重新同步)。
|
|
1199
|
+
//w-serv-hapi 1.0.87 起之正式名稱; 舊名 fnTableTags 於該版之前傳下去等同未給(w-serv-webdata 只讀 fpTableTags),
|
|
1200
|
+
//故 1.0.86 及之前實際寫的是預設之 ./tableTags.json, 升版後改用本檔名。
|
|
1201
|
+
fpTableTags: 'tableTags-web-perm.json',
|
|
1212
1202
|
})
|
|
1213
1203
|
|
|
1214
1204
|
|
|
@@ -1231,7 +1221,7 @@ export default WWebPerm
|
|
|
1231
1221
|
<br class="clear">
|
|
1232
1222
|
|
|
1233
1223
|
<footer>
|
|
1234
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Sep
|
|
1224
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Sep 13 2026 15:35:25 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
1235
1225
|
</footer>
|
|
1236
1226
|
|
|
1237
1227
|
<script>prettyPrint();</script>
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<title>server/procCore.mjs - Documentation</title>
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
<script src="scripts/prettify/prettify.js"></script>
|
|
10
|
+
<script src="scripts/prettify/lang-css.js"></script>
|
|
11
|
+
<!--[if lt IE 9]>
|
|
12
|
+
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
13
|
+
<![endif]-->
|
|
14
|
+
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
|
|
15
|
+
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
|
|
16
|
+
<script src="scripts/nav.js" defer></script>
|
|
17
|
+
|
|
18
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
|
|
22
|
+
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
|
|
23
|
+
<label for="nav-trigger" class="navicon-button x">
|
|
24
|
+
<div class="navicon"></div>
|
|
25
|
+
</label>
|
|
26
|
+
|
|
27
|
+
<label for="nav-trigger" class="overlay"></label>
|
|
28
|
+
|
|
29
|
+
<nav >
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WWebPerm.html">WWebPerm</a></li></ul><h3>Global</h3><ul><li><a href="global.html#fetchJson">fetchJson</a></li><li><a href="global.html#mergePickKeysOnly">mergePickKeysOnly</a></li></ul>
|
|
33
|
+
|
|
34
|
+
</nav>
|
|
35
|
+
|
|
36
|
+
<div id="main">
|
|
37
|
+
|
|
38
|
+
<h1 class="page-title">server/procCore.mjs</h1>
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
<section>
|
|
47
|
+
<article>
|
|
48
|
+
<pre class="prettyprint source linenums"><code>import get from 'lodash-es/get.js'
|
|
49
|
+
import each from 'lodash-es/each.js'
|
|
50
|
+
import map from 'lodash-es/map.js'
|
|
51
|
+
import size from 'lodash-es/size.js'
|
|
52
|
+
import sortBy from 'lodash-es/sortBy.js'
|
|
53
|
+
import isestr from 'wsemi/src/isestr.mjs'
|
|
54
|
+
import isearr from 'wsemi/src/isearr.mjs'
|
|
55
|
+
import haskey from 'wsemi/src/haskey.mjs'
|
|
56
|
+
import arrHas from 'wsemi/src/arrHas.mjs'
|
|
57
|
+
import ltdtDiffByKey from 'wsemi/src/ltdtDiffByKey.mjs'
|
|
58
|
+
import ltdtmapping from 'wsemi/src/ltdtmapping.mjs'
|
|
59
|
+
import ds from '../src/schema/index.mjs'
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 僅採納既有列之指定欄位變更, 其餘欄位、新增列、缺列(刪除)與順序一律以資料庫現值為準
|
|
64
|
+
*
|
|
65
|
+
* 用於 modeEditUsers='for:grups': 使用者身分由外部單一登入系統同步, 本系統只擁有群組指派(cgrups), 故 API 層須與 UI 一致只接受 cgrups 變更
|
|
66
|
+
*
|
|
67
|
+
* @param {Array} ltdtDb 資料庫現有列(select 結果, 依 order 排序後為基底)
|
|
68
|
+
* @param {Array} rowsIn 前端/外部送入之整表列
|
|
69
|
+
* @param {String} keyDetect 對應列之鍵欄位(如 'id')
|
|
70
|
+
* @param {Array} pickKeys 允許採納之欄位名陣列(如 ['cgrups'])
|
|
71
|
+
* @returns {Array} 以資料庫列為基底、僅覆蓋 pickKeys 之新列陣列(順序同資料庫)
|
|
72
|
+
*/
|
|
73
|
+
function mergePickKeysOnly(ltdtDb, rowsIn, keyDetect, pickKeys) {
|
|
74
|
+
let kpIn = {}
|
|
75
|
+
each(rowsIn, (r) => {
|
|
76
|
+
let v = get(r, keyDetect, '')
|
|
77
|
+
if (isestr(v)) {
|
|
78
|
+
kpIn[v] = r
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
let ltdt = sortBy(ltdtDb, 'order')
|
|
82
|
+
ltdt = map(ltdt, (u) => {
|
|
83
|
+
let r = { ...u }
|
|
84
|
+
let v = get(u, keyDetect, '')
|
|
85
|
+
let rin = get(kpIn, v, null)
|
|
86
|
+
if (rin !== null) {
|
|
87
|
+
each(pickKeys, (k) => {
|
|
88
|
+
if (haskey(rin, k)) {
|
|
89
|
+
r[k] = rin[k]
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
return r
|
|
94
|
+
})
|
|
95
|
+
return ltdt
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
function proc(woItems, procOrm, { srLog, kmx }) {
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
//updateTabItems
|
|
103
|
+
let updateTabItems = async (keyTable, userId, rows, keyDetect, opt = {}) => {
|
|
104
|
+
// console.log('updateTabItems', keyTable, userId, rows.length, keyDetect)
|
|
105
|
+
|
|
106
|
+
//pickKeysOnly, 僅採納既有列之指定欄位變更(見 mergePickKeysOnly), 未給則整表 diff 寫入
|
|
107
|
+
let pickKeysOnly = get(opt, 'pickKeysOnly', null)
|
|
108
|
+
|
|
109
|
+
//序列化同表之並行整表批次寫入: 防 lost update (同 keyTable 序列化, 不同表並行)
|
|
110
|
+
return await kmx('updateTabItems:' + keyTable, async () => {
|
|
111
|
+
|
|
112
|
+
//pickKeysOnly, 須於 mutex 內讀取資料庫為基底, 避免與並行寫入 race
|
|
113
|
+
if (isearr(pickKeysOnly)) {
|
|
114
|
+
let ltdtDb = await woItems[keyTable].select()
|
|
115
|
+
rows = mergePickKeysOnly(ltdtDb, rows, keyDetect, pickKeysOnly)
|
|
116
|
+
srLog.info({ event: 'updateTabItems-pickKeysOnly', keyTable, userId, pickKeysOnly })
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
//ltdtmapping
|
|
120
|
+
rows = ltdtmapping(rows, ds[keyTable].keys)
|
|
121
|
+
// console.log('ltdtmapping rows', rows)
|
|
122
|
+
|
|
123
|
+
//重給order
|
|
124
|
+
rows = map(rows, (r, k) => {
|
|
125
|
+
r.order = k + 1
|
|
126
|
+
return r
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
//ckKey, 回傳 err key (saveRowFieldInvalid / saveRowFieldDuplicate) 或 null; 動態細節 (row index/field/value) 改記 srLog, 不進使用者訊息
|
|
130
|
+
let ckKey = (rows, key) => {
|
|
131
|
+
let err = null
|
|
132
|
+
|
|
133
|
+
//check
|
|
134
|
+
let kp = {}
|
|
135
|
+
each(rows, (v, k) => {
|
|
136
|
+
|
|
137
|
+
//value
|
|
138
|
+
let value = get(v, key, '')
|
|
139
|
+
|
|
140
|
+
//check
|
|
141
|
+
if (!isestr(value)) {
|
|
142
|
+
srLog.error({ event: 'updateTabItems-ckKey-error', keyTable, key, msg: 'saveRowFieldInvalid', row: k })
|
|
143
|
+
err = `saveRowFieldInvalid`
|
|
144
|
+
return false //跳出
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
//check
|
|
148
|
+
if (haskey(kp, value)) {
|
|
149
|
+
srLog.error({ event: 'updateTabItems-ckKey-error', keyTable, key, msg: 'saveRowFieldDuplicate', row: k, value })
|
|
150
|
+
err = `saveRowFieldDuplicate`
|
|
151
|
+
return false //跳出
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
//kp
|
|
155
|
+
kp[value] = true
|
|
156
|
+
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
return err
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
//偵測未給予或重複
|
|
163
|
+
let err = null
|
|
164
|
+
if (true) {
|
|
165
|
+
if (arrHas(keyTable, ['targets'])) {
|
|
166
|
+
err = ckKey(rows, 'id')
|
|
167
|
+
if (err !== null) {
|
|
168
|
+
return Promise.reject(err)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (arrHas(keyTable, ['pemis', 'grups'])) { //users可重複name故不列入
|
|
172
|
+
err = ckKey(rows, 'name')
|
|
173
|
+
if (err !== null) {
|
|
174
|
+
return Promise.reject(err)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (arrHas(keyTable, ['users'])) {
|
|
178
|
+
err = ckKey(rows, 'email')
|
|
179
|
+
if (err !== null) {
|
|
180
|
+
return Promise.reject(err)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
//ltdtDiffByKey
|
|
186
|
+
let ltdtOld = await woItems[keyTable].select()
|
|
187
|
+
let ltdtNew = rows
|
|
188
|
+
let r = ltdtDiffByKey(ltdtOld, ltdtNew, keyDetect)
|
|
189
|
+
// console.log('ltdtDiffByKey r', r)
|
|
190
|
+
|
|
191
|
+
//del
|
|
192
|
+
if (size(r.del) > 0) {
|
|
193
|
+
await procOrm(userId, keyTable, 'del', r.del) //須使用procOrm才有辦法自動給予相關欄位
|
|
194
|
+
// .catch((err) => {
|
|
195
|
+
// console.log('woItems[keyTable].del err', err)
|
|
196
|
+
// })
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
//add
|
|
200
|
+
if (size(r.add) > 0) {
|
|
201
|
+
await procOrm(userId, keyTable, 'insert', r.add) //須使用procOrm才有辦法自動給予相關欄位
|
|
202
|
+
// .catch((err) => {
|
|
203
|
+
// console.log('woItems[keyTable].insert err', err)
|
|
204
|
+
// })
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
//diff
|
|
208
|
+
if (size(r.diff) > 0) {
|
|
209
|
+
await procOrm(userId, keyTable, 'save', r.diff) //須使用procOrm才有辦法自動給予相關欄位
|
|
210
|
+
// .catch((err) => {
|
|
211
|
+
// console.log('woItems[keyTable].save err', err)
|
|
212
|
+
// })
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return ltdtNew
|
|
216
|
+
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
//errLog: 失敗統一記 err key (updateTabItems reject 為 key 字串如 saveRowFieldInvalid; procOrm 失敗為 Error)
|
|
222
|
+
let errLog = (event, userId, err) => {
|
|
223
|
+
srLog.error({ event, userId, msg: (typeof err === 'string') ? err : ((err && err.message) || event) })
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
//updateTargets
|
|
228
|
+
let updateTargets = async (userId, rows) => {
|
|
229
|
+
try {
|
|
230
|
+
await updateTabItems('targets', userId, rows, 'id')
|
|
231
|
+
}
|
|
232
|
+
catch (err) { errLog('updateTargets-error', userId, err); throw err }
|
|
233
|
+
srLog.info({ event: 'updateTargets-success', userId })
|
|
234
|
+
return 'saveTabItemsSuccess' //成功 msg key
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
//updatePemis
|
|
239
|
+
let updatePemis = async (userId, rows) => {
|
|
240
|
+
try {
|
|
241
|
+
await updateTabItems('pemis', userId, rows, 'name')
|
|
242
|
+
}
|
|
243
|
+
catch (err) { errLog('updatePemis-error', userId, err); throw err }
|
|
244
|
+
srLog.info({ event: 'updatePemis-success', userId })
|
|
245
|
+
return 'saveTabItemsSuccess' //成功 msg key
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
//updateGrups
|
|
250
|
+
let updateGrups = async (userId, rows) => {
|
|
251
|
+
try {
|
|
252
|
+
await updateTabItems('grups', userId, rows, 'name')
|
|
253
|
+
}
|
|
254
|
+
catch (err) { errLog('updateGrups-error', userId, err); throw err }
|
|
255
|
+
srLog.info({ event: 'updateGrups-success', userId })
|
|
256
|
+
return 'saveTabItemsSuccess' //成功 msg key
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
//updateUsers, opt.pickKeysOnly 給 ['cgrups'] 時僅採納既有使用者之群組指派變更(modeEditUsers='for:grups')
|
|
261
|
+
let updateUsers = async (userId, rows, opt = {}) => {
|
|
262
|
+
try {
|
|
263
|
+
await updateTabItems('users', userId, rows, 'id', opt)
|
|
264
|
+
}
|
|
265
|
+
catch (err) { errLog('updateUsers-error', userId, err); throw err }
|
|
266
|
+
srLog.info({ event: 'updateUsers-success', userId })
|
|
267
|
+
return 'saveTabItemsSuccess' //成功 msg key
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
let p = {
|
|
272
|
+
updateTargets,
|
|
273
|
+
updatePemis,
|
|
274
|
+
updateGrups,
|
|
275
|
+
updateUsers,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
return p
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
export { mergePickKeysOnly }
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
export default proc
|
|
287
|
+
</code></pre>
|
|
288
|
+
</article>
|
|
289
|
+
</section>
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<br class="clear">
|
|
299
|
+
|
|
300
|
+
<footer>
|
|
301
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Sep 13 2026 15:35:25 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
302
|
+
</footer>
|
|
303
|
+
|
|
304
|
+
<script>prettyPrint();</script>
|
|
305
|
+
<script src="scripts/polyfill.js"></script>
|
|
306
|
+
<script src="scripts/linenumber.js"></script>
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
</body>
|
|
311
|
+
</html>
|