w-web-perm 1.0.49 → 1.0.51
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 +5 -5
- package/dist/css/app.8781355a.css +1 -0
- package/dist/css/{chunk-vendors.2d3ef2ba.css → chunk-vendors.58917286.css} +1 -1
- package/dist/index.tmp +1 -1
- package/dist/js/app-legacy.b2754bb1.js +1 -0
- package/dist/js/app.b2754bb1.js +1 -0
- package/dist/js/{chunk-vendors-legacy.8fdd4435.js → chunk-vendors-legacy.c14a1ed1.js} +6 -6
- package/dist/js/{chunk-vendors.8fdd4435.js → chunk-vendors.c14a1ed1.js} +6 -6
- package/dist/w-web-perm.umd.js +2 -2
- package/dist/w-web-perm.umd.js.map +1 -1
- package/docs/WWebPerm.html +3 -3
- package/docs/WWebPerm.mjs.html +33 -20
- package/docs/index.html +1 -1
- package/logs/2026-06-19T16.log +191 -0
- package/logs/2026-06-20T00.log +207 -0
- package/logs/2026-06-20T11.log +221 -0
- package/logs/2026-06-20T12.log +146 -0
- package/logs/2026-06-20T13.log +134 -0
- package/logs/2026-06-20T14.log +296 -0
- package/logs/2026-06-20T15.log +225 -0
- package/logs/2026-06-20T16.log +174 -0
- package/logs/2026-06-21T16.log +110 -0
- package/logs/2026-06-21T17.log +301 -0
- package/logs/2026-06-21T18.log +238 -0
- package/logs/2026-06-21T19.log +141 -0
- package/logs/2026-06-21T20.log +99 -0
- package/logs/2026-06-21T21.log +143 -0
- package/logs/2026-06-21T23.log +26 -0
- package/logs/2026-06-22T00.log +33 -0
- package/logs/2026-06-22T08.log +235 -0
- package/logs/2026-06-22T09.log +246 -0
- package/logs/2026-06-22T10.log +301 -0
- package/logs/2026-06-22T11.log +140 -0
- package/logs/2026-06-22T12.log +22 -0
- package/logs/2026-06-22T14.log +44 -0
- package/logs/2026-06-22T23.log +951 -0
- package/logs/2026-06-23T00.log +263 -0
- package/logs/2026-06-23T08.log +611 -0
- package/logs/2026-06-30T21.log +3609 -0
- package/logs/2026-06-30T22.log +3682 -0
- package/logs/2026-06-30T23.log +592 -0
- package/logs/2026-07-01T12.log +0 -0
- package/logs/2026-07-09T22.log +2220 -0
- package/logs/2026-07-09T23.log +1140 -0
- package/logs/2026-07-10T12.log +6 -0
- package/logs/2026-07-10T13.log +351 -0
- package/logs/2026-07-10T16.log +1272 -0
- package/logs/2026-07-10T19.log +6 -0
- package/logs/2026-07-10T20.log +1883 -0
- package/logs/2026-07-10T21.log +3505 -0
- package/logs/2026-07-10T22.log +3395 -0
- package/logs/2026-07-11T00.log +1338 -0
- package/logs/2026-07-11T01.log +2492 -0
- package/logs/2026-07-11T17.log +19 -0
- package/package.json +18 -15
- package/server/WWebPerm.mjs +32 -19
- package/server/procCore.mjs +22 -4
- package/server/procLang.mjs +84 -65
- package/server/procStaInfor.mjs +10 -0
- package/server/staLogs/filterVpfsByWindow.mjs +27 -0
- package/server/staLogs/staEvent.mjs +2 -0
- package/server/staLogs/staEventTable.mjs +2 -0
- package/settings.json +3 -0
- package/src/components/LayoutContent.vue +0 -2
- package/src/components/LayoutContentGrups.vue +0 -2
- package/src/components/LayoutContentPemis.vue +0 -2
- package/src/components/LayoutContentStaInfor.vue +0 -2
- package/src/components/LayoutContentTargets.vue +3 -5
- package/src/components/LayoutContentUsers.vue +0 -2
- package/src/components/VeCgrups.vue +7 -6
- package/src/components/VeCpemis.vue +7 -6
- package/src/components/VeCrules.vue +7 -6
- package/src/components/VeGrupBlngUsers.vue +21 -27
- package/src/components/VePemiBlngGrups.vue +21 -27
- package/src/main.js +3 -9
- package/src/perm.mjs +3 -4
- package/src/plugins/mUI.mjs +0 -48
- package/src/store/mutations.mjs +0 -10
- package/src/store/types.mjs +0 -2
- package/tableTags.json +1 -1
- package/z/344/277/256/346/255/243/350/246/217/345/212/203/346/233/270.md +450 -0
- package/_gh-pages-deploy/WWebPerm.html +0 -938
- package/_gh-pages-deploy/WWebPerm.mjs.html +0 -1228
- package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Bold.eot +0 -0
- package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Bold.woff +0 -0
- package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
- package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Regular.eot +0 -0
- package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
- package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Regular.woff +0 -0
- package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
- package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
- package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -978
- package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
- package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
- package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
- package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
- package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -1049
- package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
- package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
- package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
- package/_gh-pages-deploy/index.html +0 -84
- package/_gh-pages-deploy/scripts/collapse.js +0 -39
- package/_gh-pages-deploy/scripts/commonNav.js +0 -28
- package/_gh-pages-deploy/scripts/linenumber.js +0 -25
- package/_gh-pages-deploy/scripts/nav.js +0 -12
- package/_gh-pages-deploy/scripts/polyfill.js +0 -4
- package/_gh-pages-deploy/scripts/prettify/Apache-License-2.0.txt +0 -202
- package/_gh-pages-deploy/scripts/prettify/lang-css.js +0 -2
- package/_gh-pages-deploy/scripts/prettify/prettify.js +0 -28
- package/_gh-pages-deploy/scripts/search.js +0 -99
- package/_gh-pages-deploy/styles/jsdoc.css +0 -776
- package/_gh-pages-deploy/styles/prettify.css +0 -80
- package/dist/css/app.d8b16216.css +0 -1
- package/dist/js/app-legacy.fb48310a.js +0 -1
- package/dist/js/app.fb48310a.js +0 -1
- package/server/staLogs/test_staEvent.mjs +0 -22
- package/src/plugins/mDataSelectorSchema.mjs +0 -30
- package/tableTags-web-perm.json +0 -1
- /package/{_gh-pages-deploy/.nojekyll → logs/2026-06-19T23.log} +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{"level":30,"time":1783761673030,"pid":21968,"hostname":"semi-win-dev3","event":"api/getPerm-success","userId":"id-for-admin"}
|
|
2
|
+
{"level":50,"time":1783761673423,"pid":21968,"hostname":"semi-win-dev3","event":"getTokenUser-error","msg":"cannotFindUserFromToken"}
|
|
3
|
+
{"level":30,"time":1783761673428,"pid":21968,"hostname":"semi-win-dev3","event":"api/getPerm-success","userId":"id-for-admin"}
|
|
4
|
+
{"level":30,"time":1783761673433,"pid":21968,"hostname":"semi-win-dev3","event":"api/getPerm-success","userId":"id-for-admin"}
|
|
5
|
+
{"level":30,"time":1783761673440,"pid":21968,"hostname":"semi-win-dev3","event":"api/getPerm-success","userId":"id-for-admin"}
|
|
6
|
+
{"level":30,"time":1783761673444,"pid":21968,"hostname":"semi-win-dev3","event":"api/getPerm-success","userId":"id-for-admin"}
|
|
7
|
+
{"level":30,"time":1783761673450,"pid":21968,"hostname":"semi-win-dev3","event":"api/getPermUserInfor-success","userId":"id-for-peter"}
|
|
8
|
+
{"level":50,"time":1783761673453,"pid":21968,"hostname":"semi-win-dev3","event":"api/getPermUserInfor-error","msg":"tokenNoPermission"}
|
|
9
|
+
{"level":50,"time":1783761673455,"pid":21968,"hostname":"semi-win-dev3","event":"getAndVerifyAppUser-error","msg":"cannotFindUserFromToken"}
|
|
10
|
+
{"level":30,"time":1783761673458,"pid":21968,"hostname":"semi-win-dev3","event":"api/getPermUserInfor-success","userId":"id-not-exist-xyz"}
|
|
11
|
+
{"level":30,"time":1783761673461,"pid":21968,"hostname":"semi-win-dev3","event":"api/getPermUserInfor-success","userId":"id-for-peter"}
|
|
12
|
+
{"level":30,"time":1783761673463,"pid":21968,"hostname":"semi-win-dev3","event":"api/getPermUserInfor-success","userId":"id-for-peter"}
|
|
13
|
+
{"level":30,"time":1783761673466,"pid":21968,"hostname":"semi-win-dev3","event":"api/getPermUserInfor-success","userId":"id-for-peter"}
|
|
14
|
+
{"level":30,"time":1783762697759,"pid":18212,"hostname":"semi-win-dev3","event":"api/getPerm-success","userId":"id-for-admin"}
|
|
15
|
+
{"level":50,"time":1783762698186,"pid":18212,"hostname":"semi-win-dev3","event":"getTokenUser-error","msg":"cannotFindUserFromToken"}
|
|
16
|
+
{"level":30,"time":1783762698190,"pid":18212,"hostname":"semi-win-dev3","event":"api/getPerm-success","userId":"id-for-admin"}
|
|
17
|
+
{"level":30,"time":1783762698193,"pid":18212,"hostname":"semi-win-dev3","event":"api/getPerm-success","userId":"id-for-admin"}
|
|
18
|
+
{"level":30,"time":1783762698196,"pid":18212,"hostname":"semi-win-dev3","event":"api/getPerm-success","userId":"id-for-admin"}
|
|
19
|
+
{"level":30,"time":1783762698200,"pid":18212,"hostname":"semi-win-dev3","event":"api/getPerm-success","userId":"id-for-admin"}
|
package/package.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "w-web-perm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.51",
|
|
4
4
|
"main": "dist/w-web-perm.umd.js",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@mdi/js": "^7.4.47",
|
|
7
|
-
"axios": "^1.18.
|
|
7
|
+
"axios": "^1.18.1",
|
|
8
8
|
"json5": "^2.2.3",
|
|
9
9
|
"lodash-es": "^4.18.1",
|
|
10
10
|
"showdown": "^2.1.0",
|
|
11
|
-
"w-aggrid-vue": "^2.0.
|
|
12
|
-
"w-component-vue": "^2.4.
|
|
13
|
-
"w-data-collector": "^1.0.
|
|
14
|
-
"w-serv-hapi": "^1.0.
|
|
15
|
-
"w-serv-orm": "^1.0.
|
|
16
|
-
"w-ui-loginout": "^1.0.
|
|
17
|
-
"w-syslog": "^1.0.
|
|
18
|
-
"wsemi": "^1.8.
|
|
11
|
+
"w-aggrid-vue": "^2.0.58",
|
|
12
|
+
"w-component-vue": "^2.4.74",
|
|
13
|
+
"w-data-collector": "^1.0.23",
|
|
14
|
+
"w-serv-hapi": "^1.0.74",
|
|
15
|
+
"w-serv-orm": "^1.0.40",
|
|
16
|
+
"w-ui-loginout": "^1.0.11",
|
|
17
|
+
"w-syslog": "^1.0.10",
|
|
18
|
+
"wsemi": "^1.8.66"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@vue/cli-plugin-babel": "^5.0.8",
|
|
@@ -29,18 +29,21 @@
|
|
|
29
29
|
"eslint-plugin-promise": "^6.1.1",
|
|
30
30
|
"eslint-plugin-vue": "^9.10.0",
|
|
31
31
|
"pixelmatch": "^7.2.0",
|
|
32
|
-
"playwright": "^1.61.
|
|
32
|
+
"playwright": "^1.61.1",
|
|
33
33
|
"pngjs": "^7.0.0",
|
|
34
34
|
"vue": "^2.6.14",
|
|
35
35
|
"vuex": "^3.6.2",
|
|
36
|
-
"w-orm-lmdb": "^1.0.
|
|
37
|
-
"w-screenctl": "^1.0.
|
|
38
|
-
"w-package-tools": "^1.1.
|
|
36
|
+
"w-orm-lmdb": "^1.0.8",
|
|
37
|
+
"w-screenctl": "^1.0.4",
|
|
38
|
+
"w-package-tools": "^1.1.11"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"serve": "vue-cli-service serve",
|
|
42
42
|
"build": "vue-cli-service build",
|
|
43
|
-
"test": "mocha --timeout 300000",
|
|
43
|
+
"test:unit": "mocha test/unit-staEvent.test.mjs --timeout 300000",
|
|
44
|
+
"test:apiunit": "mocha test/unit-*.test.mjs test/api-*.test.mjs test/getUserRules.test.mjs --timeout 300000",
|
|
45
|
+
"test:e2e": "node test/run-e2e-isolated.mjs",
|
|
46
|
+
"test": "npm run test:apiunit && npm run test:e2e",
|
|
44
47
|
"lint": "vue-cli-service lint",
|
|
45
48
|
"deploy": "gh-pages -d docs"
|
|
46
49
|
},
|
package/server/WWebPerm.mjs
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import fs from 'fs'
|
|
3
3
|
import get from 'lodash-es/get.js'
|
|
4
|
-
import each from 'lodash-es/each.js'
|
|
5
4
|
import map from 'lodash-es/map.js'
|
|
6
5
|
import keys from 'lodash-es/keys.js'
|
|
7
|
-
import size from 'lodash-es/size.js'
|
|
8
6
|
import join from 'lodash-es/join.js'
|
|
9
7
|
import iseobj from 'wsemi/src/iseobj.mjs'
|
|
10
8
|
import isestr from 'wsemi/src/isestr.mjs'
|
|
@@ -42,7 +40,7 @@ import procStaInfor from './procStaInfor.mjs'
|
|
|
42
40
|
* @param {Function} WOrm 輸入資料庫ORM函數
|
|
43
41
|
* @param {String} url 輸入資料庫連線字串,例如w-orm-lmdb為'./db',或w-orm-mongodb為'mongodb://username:password@$127.0.0.1:27017'
|
|
44
42
|
* @param {String} db 輸入資料庫名稱字串
|
|
45
|
-
* @param {Function} getUserByToken 輸入處理函數,函數會傳入使用者token,通過此函數處理後並回傳使用者資訊物件,並至少須提供'id'、'email'、'name'、'isAdmin'欄位,且'isAdmin'限輸入'y'或'n'
|
|
43
|
+
* @param {Function} getUserByToken 輸入處理函數,函數會傳入使用者token,通過此函數處理後並回傳使用者資訊物件,並至少須提供'id'、'email'、'name'、'isAdmin'欄位,且'isAdmin'限輸入'y'或'n'。注意: token 回傳之'isAdmin'僅供識別,權限系統一律以自身users表該使用者之'isAdmin'欄位值為權威,不會被 token 宣稱之值複寫
|
|
46
44
|
* @param {Function} verifyClientUser 輸入驗證瀏覽使用者身份之處理函數,函數會傳入使用者資訊物件,通過此函數識別後回傳布林值,允許使用者回傳true,反之回傳false
|
|
47
45
|
* @param {Function} verifyAppUser 輸入驗證應用程序使用者身份之處理函數,函數會傳入使用者資訊物件,通過此函數識別後回傳布林值,允許使用者回傳true,反之回傳false
|
|
48
46
|
* @param {Object} [opt={}] 輸入設定物件,預設{}
|
|
@@ -285,6 +283,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
285
283
|
}
|
|
286
284
|
catch (err) {
|
|
287
285
|
console.log(err)
|
|
286
|
+
srLog.error({ event: 'WServOrm-init-error', msg: (err && err.message) || 'WServOrmInitFailed' })
|
|
287
|
+
throw err //fail-fast: ORM 初始化失敗直接終止啟動, 不以 wp={} 繼續組裝出「port 起得來但 woItems/procOrm 為 undefined、CRUD 全 crash」的半死伺服器
|
|
288
288
|
}
|
|
289
289
|
let { woItems, procOrm } = wp
|
|
290
290
|
|
|
@@ -298,8 +298,12 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
298
298
|
let pc = procCore(woItems, procOrm, { srLog, kmx })
|
|
299
299
|
|
|
300
300
|
|
|
301
|
-
//procStaInfor: 統計資訊, fdLog 對齊 srLog
|
|
302
|
-
let
|
|
301
|
+
//procStaInfor: 統計資訊, fdLog 對齊 srLog 使用同一目錄(讀 settings.logFd, fallback 邏輯同 srLog.mjs); mock 由 settings.staEventMock 注入(e2e 統計圖穩定用)
|
|
302
|
+
let logFd = get(opt, 'logFd', '')
|
|
303
|
+
if (!isestr(logFd)) {
|
|
304
|
+
logFd = './_logs'
|
|
305
|
+
}
|
|
306
|
+
let psi = procStaInfor({ fdLog: logFd, mock: get(opt, 'staEventMock', false) })
|
|
303
307
|
|
|
304
308
|
|
|
305
309
|
//getWebInfor
|
|
@@ -450,13 +454,10 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
450
454
|
return Promise.reject(`cannotGetUserFromPerm`)
|
|
451
455
|
}
|
|
452
456
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
userFind.isAdmin = isAdminSelf
|
|
458
|
-
}
|
|
459
|
-
// console.log('userFind(isAdmin)', userFind)
|
|
457
|
+
//isAdmin 權威為 perm users 表之欄位值, 不以 token(getUserByToken)宣稱之 isAdmin 複寫。
|
|
458
|
+
//(2026-07-11 稽核 A-C1 裁決(b): 原此處有一段恆為 no-op 之「複寫isAdmin」死碼
|
|
459
|
+
// (userFind.isAdmin = isAdminSelf, 把自己指派給自己), 與 JSDoc 舊文「輸入'y'時會複寫」互斥;
|
|
460
|
+
// 依業主裁決採保守解: 刪除死碼、文件對齊實際行為, 不啟用 token→admin 提權路徑。)
|
|
460
461
|
|
|
461
462
|
return userFind
|
|
462
463
|
}
|
|
@@ -1058,7 +1059,7 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
1058
1059
|
|
|
1059
1060
|
//WServHapiServer
|
|
1060
1061
|
instWServHapiServer = new WServHapiServer({
|
|
1061
|
-
port:
|
|
1062
|
+
port: serverPort,
|
|
1062
1063
|
pathStaticFiles,
|
|
1063
1064
|
apiName: 'api',
|
|
1064
1065
|
apis,
|
|
@@ -1133,15 +1134,27 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
|
|
|
1133
1134
|
updateUsers: pc.updateUsers,
|
|
1134
1135
|
|
|
1135
1136
|
getStaEvent: async (userId, timeLength, timeInterval) => {
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1137
|
+
try {
|
|
1138
|
+
let rs = await psi.getStaEvent(userId, timeLength, timeInterval)
|
|
1139
|
+
srLog.info({ event: 'getStaEvent-success', userId })
|
|
1140
|
+
return rs
|
|
1141
|
+
}
|
|
1142
|
+
catch (err) {
|
|
1143
|
+
srLog.error({ event: 'getStaEvent-error', userId, msg: (typeof err === 'string') ? err : ((err && err.message) || 'cannotGetStaEvent') })
|
|
1144
|
+
throw err
|
|
1145
|
+
}
|
|
1139
1146
|
},
|
|
1140
1147
|
|
|
1141
1148
|
getStaEventTable: async (userId) => {
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1149
|
+
try {
|
|
1150
|
+
let rs = await psi.getStaEventTable(userId)
|
|
1151
|
+
srLog.info({ event: 'getStaEventTable-success', userId })
|
|
1152
|
+
return rs
|
|
1153
|
+
}
|
|
1154
|
+
catch (err) {
|
|
1155
|
+
srLog.error({ event: 'getStaEventTable-error', userId, msg: (typeof err === 'string') ? err : ((err && err.message) || 'cannotGetStaEventTable') })
|
|
1156
|
+
throw err
|
|
1157
|
+
}
|
|
1145
1158
|
},
|
|
1146
1159
|
|
|
1147
1160
|
},
|
package/server/procCore.mjs
CHANGED
|
@@ -122,9 +122,18 @@ function proc(woItems, procOrm, { srLog, kmx }) {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
|
|
125
|
+
//errLog: 失敗統一記 err key (updateTabItems reject 為 key 字串如 saveRowFieldInvalid; procOrm 失敗為 Error)
|
|
126
|
+
let errLog = (event, userId, err) => {
|
|
127
|
+
srLog.error({ event, userId, msg: (typeof err === 'string') ? err : ((err && err.message) || event) })
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
125
131
|
//updateTargets
|
|
126
132
|
let updateTargets = async (userId, rows) => {
|
|
127
|
-
|
|
133
|
+
try {
|
|
134
|
+
await updateTabItems('targets', userId, rows, 'id')
|
|
135
|
+
}
|
|
136
|
+
catch (err) { errLog('updateTargets-error', userId, err); throw err }
|
|
128
137
|
srLog.info({ event: 'updateTargets-success', userId })
|
|
129
138
|
return 'saveTabItemsSuccess' //成功 msg key
|
|
130
139
|
}
|
|
@@ -132,7 +141,10 @@ function proc(woItems, procOrm, { srLog, kmx }) {
|
|
|
132
141
|
|
|
133
142
|
//updatePemis
|
|
134
143
|
let updatePemis = async (userId, rows) => {
|
|
135
|
-
|
|
144
|
+
try {
|
|
145
|
+
await updateTabItems('pemis', userId, rows, 'name')
|
|
146
|
+
}
|
|
147
|
+
catch (err) { errLog('updatePemis-error', userId, err); throw err }
|
|
136
148
|
srLog.info({ event: 'updatePemis-success', userId })
|
|
137
149
|
return 'saveTabItemsSuccess' //成功 msg key
|
|
138
150
|
}
|
|
@@ -140,7 +152,10 @@ function proc(woItems, procOrm, { srLog, kmx }) {
|
|
|
140
152
|
|
|
141
153
|
//updateGrups
|
|
142
154
|
let updateGrups = async (userId, rows) => {
|
|
143
|
-
|
|
155
|
+
try {
|
|
156
|
+
await updateTabItems('grups', userId, rows, 'name')
|
|
157
|
+
}
|
|
158
|
+
catch (err) { errLog('updateGrups-error', userId, err); throw err }
|
|
144
159
|
srLog.info({ event: 'updateGrups-success', userId })
|
|
145
160
|
return 'saveTabItemsSuccess' //成功 msg key
|
|
146
161
|
}
|
|
@@ -148,7 +163,10 @@ function proc(woItems, procOrm, { srLog, kmx }) {
|
|
|
148
163
|
|
|
149
164
|
//updateUsers
|
|
150
165
|
let updateUsers = async (userId, rows) => {
|
|
151
|
-
|
|
166
|
+
try {
|
|
167
|
+
await updateTabItems('users', userId, rows, 'id')
|
|
168
|
+
}
|
|
169
|
+
catch (err) { errLog('updateUsers-error', userId, err); throw err }
|
|
152
170
|
srLog.info({ event: 'updateUsers-success', userId })
|
|
153
171
|
return 'saveTabItemsSuccess' //成功 msg key
|
|
154
172
|
}
|
package/server/procLang.mjs
CHANGED
|
@@ -57,10 +57,11 @@ let kpLang = {
|
|
|
57
57
|
cht: '等待數據中...',
|
|
58
58
|
},
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
//目前無引用,保留供日後使用(見 spec/設計要點與取捨.md ADR-014)
|
|
61
|
+
// isEditabled: {
|
|
62
|
+
// eng: 'Editable',
|
|
63
|
+
// cht: '是否編輯',
|
|
64
|
+
// },
|
|
64
65
|
saveChanges: {
|
|
65
66
|
eng: `Save changes`,
|
|
66
67
|
cht: `儲存變更`,
|
|
@@ -85,10 +86,11 @@ let kpLang = {
|
|
|
85
86
|
eng: `Delete`,
|
|
86
87
|
cht: `刪除`,
|
|
87
88
|
},
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
//目前無引用,保留供日後使用(見 spec/設計要點與取捨.md ADR-014)
|
|
90
|
+
// cancel: {
|
|
91
|
+
// eng: `Cancel`,
|
|
92
|
+
// cht: `取消`,
|
|
93
|
+
// },
|
|
92
94
|
screenFull: {
|
|
93
95
|
eng: `Max size`,
|
|
94
96
|
cht: `最大化尺寸`,
|
|
@@ -221,10 +223,11 @@ let kpLang = {
|
|
|
221
223
|
cht: `無法找到使用者 Id`,
|
|
222
224
|
},
|
|
223
225
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
//目前無引用,保留供日後使用(見 spec/設計要點與取捨.md ADR-014)
|
|
227
|
+
// unknow: {
|
|
228
|
+
// eng: `Unknow`,
|
|
229
|
+
// cht: `未知`,
|
|
230
|
+
// },
|
|
228
231
|
|
|
229
232
|
username: {
|
|
230
233
|
eng: `Username`,
|
|
@@ -263,10 +266,11 @@ let kpLang = {
|
|
|
263
266
|
eng: `Use groups`,
|
|
264
267
|
cht: `所屬權限群組`,
|
|
265
268
|
},
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
269
|
+
//目前無引用,保留供日後使用(見 spec/設計要點與取捨.md ADR-014)
|
|
270
|
+
// belongPemis: {
|
|
271
|
+
// eng: `Use permissions`,
|
|
272
|
+
// cht: `所屬權限`,
|
|
273
|
+
// },
|
|
270
274
|
belongGrupsNames: {
|
|
271
275
|
eng: `Use permission groups`,
|
|
272
276
|
cht: `所屬權限群組名稱`,
|
|
@@ -289,6 +293,10 @@ let kpLang = {
|
|
|
289
293
|
eng: `Name errors`,
|
|
290
294
|
cht: `名稱出現錯誤待修復`,
|
|
291
295
|
},
|
|
296
|
+
errInIds: {
|
|
297
|
+
eng: `ID errors`,
|
|
298
|
+
cht: `ID 出現錯誤待修復`,
|
|
299
|
+
},
|
|
292
300
|
errInEmails: {
|
|
293
301
|
eng: `Email errors`,
|
|
294
302
|
cht: `Email出現錯誤待修復`,
|
|
@@ -383,10 +391,11 @@ let kpLang = {
|
|
|
383
391
|
eng: `Enable`,
|
|
384
392
|
cht: `是否使用`,
|
|
385
393
|
},
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
394
|
+
//目前無引用,保留供日後使用(見 spec/設計要點與取捨.md ADR-014)
|
|
395
|
+
// targetClearTargetsFail: {
|
|
396
|
+
// eng: `Failed to clear existing targets`,
|
|
397
|
+
// cht: `清除既有對象數據失敗`,
|
|
398
|
+
// },
|
|
390
399
|
targetSaveTargetsFail: {
|
|
391
400
|
eng: `Failed to save targets`,
|
|
392
401
|
cht: `儲存對象數據失敗`,
|
|
@@ -476,10 +485,11 @@ let kpLang = {
|
|
|
476
485
|
eng: `Has {n} rule(s){nms}`,
|
|
477
486
|
cht: `使用共 {n} 項對象{nms}`,
|
|
478
487
|
},
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
488
|
+
//目前無引用,保留供日後使用(見 spec/設計要點與取捨.md ADR-014)
|
|
489
|
+
// pemiClearPemisFail: {
|
|
490
|
+
// eng: `Failed to clear existing permissions`,
|
|
491
|
+
// cht: `清除既有權限數據失敗`,
|
|
492
|
+
// },
|
|
483
493
|
pemiSavePemisFail: {
|
|
484
494
|
eng: `Failed to save permissions`,
|
|
485
495
|
cht: `儲存權限數據失敗`,
|
|
@@ -603,10 +613,11 @@ let kpLang = {
|
|
|
603
613
|
eng: `Has {n} permission(s){nms}`,
|
|
604
614
|
cht: `使用 {n} 項權限{nms}`,
|
|
605
615
|
},
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
616
|
+
//目前無引用,保留供日後使用(見 spec/設計要點與取捨.md ADR-014)
|
|
617
|
+
// grupClearGrupsFail: {
|
|
618
|
+
// eng: `Failed to clear existing permission groups`,
|
|
619
|
+
// cht: `清除既有權限群組數據失敗`,
|
|
620
|
+
// },
|
|
610
621
|
grupSaveGrupsEmpty: {
|
|
611
622
|
eng: `No user`,
|
|
612
623
|
cht: `未有變更權限群組資料`,
|
|
@@ -727,7 +738,7 @@ let kpLang = {
|
|
|
727
738
|
cht: `編輯使用權限群組`,
|
|
728
739
|
},
|
|
729
740
|
userEditCgrupsForDisplay: {
|
|
730
|
-
eng: `
|
|
741
|
+
eng: `Permission groups of user`, //展示版不帶 Edit 動詞(2026-07-10 訂正: 原誤與編輯版同值, 唯讀對話框標題顯示「Edit...」且 e2e 無法以標題區分模式)
|
|
731
742
|
cht: `展示使用權限群組`,
|
|
732
743
|
},
|
|
733
744
|
userEditCgrupsNoUserId: {
|
|
@@ -784,10 +795,11 @@ let kpLang = {
|
|
|
784
795
|
cht: `刪除勾選權限`,
|
|
785
796
|
},
|
|
786
797
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
798
|
+
//目前無引用,保留供日後使用(見 spec/設計要點與取捨.md ADR-014)
|
|
799
|
+
// allDefaults: {
|
|
800
|
+
// eng: `All defaults`,
|
|
801
|
+
// cht: `全部項目預設值`,
|
|
802
|
+
// },
|
|
791
803
|
show: {
|
|
792
804
|
eng: `Show`,
|
|
793
805
|
cht: `顯示`,
|
|
@@ -796,34 +808,36 @@ let kpLang = {
|
|
|
796
808
|
eng: `Active`,
|
|
797
809
|
cht: `啟用`,
|
|
798
810
|
},
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
811
|
+
//目前無引用,保留供日後使用(見 spec/設計要點與取捨.md ADR-014)
|
|
812
|
+
// showChildren: {
|
|
813
|
+
// eng: `Show children`,
|
|
814
|
+
// cht: `所屬顯示`,
|
|
815
|
+
// },
|
|
816
|
+
// hideChildren: {
|
|
817
|
+
// eng: `Hide children`,
|
|
818
|
+
// cht: `所屬不顯示`,
|
|
819
|
+
// },
|
|
820
|
+
// activateChildren: {
|
|
821
|
+
// eng: `Activate children`,
|
|
822
|
+
// cht: `所屬啟用`,
|
|
823
|
+
// },
|
|
824
|
+
// deactivateChildren: {
|
|
825
|
+
// eng: `Deactivate children`,
|
|
826
|
+
// cht: `所屬不啟用`,
|
|
827
|
+
// },
|
|
828
|
+
//目前無引用,保留供日後使用(見 spec/設計要點與取捨.md ADR-014)
|
|
829
|
+
// isUsernameEmpty: {
|
|
830
|
+
// eng: `Username is empty`,
|
|
831
|
+
// cht: `使用者名稱不得為空`,
|
|
832
|
+
// },
|
|
833
|
+
// failedToSaveUser: {
|
|
834
|
+
// eng: `Failed to save the user`,
|
|
835
|
+
// cht: `變更使用者失敗`,
|
|
836
|
+
// },
|
|
837
|
+
// successfulToSaveUser: {
|
|
838
|
+
// eng: `Save the user successfully`,
|
|
839
|
+
// cht: `變更使用者成功`,
|
|
840
|
+
// },
|
|
827
841
|
|
|
828
842
|
userId: {
|
|
829
843
|
eng: `ID of the created user`,
|
|
@@ -891,10 +905,11 @@ let kpLang = {
|
|
|
891
905
|
eng: `Event Frequency`,
|
|
892
906
|
cht: `事件發生頻率`,
|
|
893
907
|
},
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
908
|
+
//目前無引用,保留供日後使用(見 spec/設計要點與取捨.md ADR-014)
|
|
909
|
+
// staTimeGroup: {
|
|
910
|
+
// eng: `Time grouping`,
|
|
911
|
+
// cht: `時間分組`,
|
|
912
|
+
// },
|
|
898
913
|
staTimeHr: {
|
|
899
914
|
eng: `Hourly`,
|
|
900
915
|
cht: `每小時`,
|
|
@@ -923,6 +938,10 @@ let kpLang = {
|
|
|
923
938
|
eng: `No event data`,
|
|
924
939
|
cht: `尚無事件資料`,
|
|
925
940
|
},
|
|
941
|
+
getDataError: {
|
|
942
|
+
eng: `Failed to load data`,
|
|
943
|
+
cht: `資料載入失敗`,
|
|
944
|
+
},
|
|
926
945
|
staTableTitle: {
|
|
927
946
|
eng: `Event Statistics`,
|
|
928
947
|
cht: `事件統計表`,
|
package/server/procStaInfor.mjs
CHANGED
|
@@ -77,6 +77,11 @@ function proc(opt = {}) {
|
|
|
77
77
|
let cacheKey = `${timeLength}:${timeInterval}`
|
|
78
78
|
|
|
79
79
|
let r = await ocGetStaEvent.getProxy(cacheKey, { fun: _getStaEvent, inputs: [timeLength, timeInterval], timeExpired: 30 * 1000 }) //快取30秒
|
|
80
|
+
if (!Array.isArray(r)) {
|
|
81
|
+
//_getStaEvent(worker) 失敗時 cache 之 .catch 會回 undefined 並把 undefined 快取 30s; 清除該快取避免窗內重試仍拿 undefined, 並 reject 讓上層 (kpFunExt) 記 err key
|
|
82
|
+
ocGetStaEvent.clear(cacheKey)
|
|
83
|
+
return Promise.reject('cannotGetStaEvent')
|
|
84
|
+
}
|
|
80
85
|
return r
|
|
81
86
|
}
|
|
82
87
|
|
|
@@ -98,6 +103,11 @@ function proc(opt = {}) {
|
|
|
98
103
|
let getStaEventTable = async (userId) => {
|
|
99
104
|
|
|
100
105
|
let r = await ocGetStaEventTable.getProxy('staEventTable', { fun: _getStaEventTable, inputs: [], timeExpired: 30 * 1000 }) //快取30秒
|
|
106
|
+
if (!Array.isArray(r)) {
|
|
107
|
+
//同 getStaEvent: staEventTable 失敗時 cache 回 undefined 並快取 30s; 清除避免窗內重試仍拿 undefined, 並 reject 讓上層記 err key
|
|
108
|
+
ocGetStaEventTable.clear('staEventTable')
|
|
109
|
+
return Promise.reject('cannotGetStaEventTable')
|
|
110
|
+
}
|
|
101
111
|
return r
|
|
102
112
|
}
|
|
103
113
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import get from 'lodash-es/get.js'
|
|
2
|
+
import filter from 'lodash-es/filter.js'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
//filterVpfsByWindow: 開檔前依「檔名時間窗」過濾 log 檔清單, 剔除窗外檔.
|
|
6
|
+
//w-syslog 檔名為 ISO 前綴 (hr: `YYYY-MM-DDTHH.log` / day: `YYYY-MM-DD.log`), 字典序 ≡ 時間序,
|
|
7
|
+
//故可用字串比較篩選 (w-syslog 自身 cleanLogs 亦依賴此性質, 見 node_modules/w-syslog/src/WSyslog.mjs:127-128).
|
|
8
|
+
//邊界檔一律保留, 其內 per-line 之時間判斷仍為最終判準 → 輸出語意零改變.
|
|
9
|
+
//fail-open: 檔名不符 ISO 前綴者一律保留 (寧可多讀, 不可漏讀).
|
|
10
|
+
//vpfs 為 fsTreeFolder 回傳之物件陣列, 每筆含 { path, name, isFolder, level }.
|
|
11
|
+
//【粒度自適應】比較時以「檔名長度」截取 keyStart: srLog 之 logInterval (決定檔名粒度) 與 staLogs 之
|
|
12
|
+
//timeInterval (決定 fmt) 為兩個獨立設定, 兩者不一定相同. 若檔名為 day 粒度 (10 字元) 而 fmt 為 hr 粒度
|
|
13
|
+
//(13 字元), 直接比較會把「當天檔」誤判為窗外 (`'2026-07-08' < '2026-07-08T21'`) → 漏讀整天資料.
|
|
14
|
+
//截取後 day 檔以日期比日期、hr 檔以小時比小時, 兩種粒度皆正確且一律偏保留 (fail-safe).
|
|
15
|
+
function filterVpfsByWindow(vpfs, tStart, fmt) {
|
|
16
|
+
let keyStart = tStart.format(fmt)
|
|
17
|
+
return filter(vpfs, (vpf) => {
|
|
18
|
+
let bn = get(vpf, 'name', '').replace(/\.log$/, '')
|
|
19
|
+
if (!/^\d{4}-\d{2}-\d{2}/.test(bn)) {
|
|
20
|
+
return true //fail-open: 非預期檔名一律保留
|
|
21
|
+
}
|
|
22
|
+
return bn >= keyStart.slice(0, bn.length)
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
export default filterVpfsByWindow
|
|
@@ -9,6 +9,7 @@ import genPm from 'wsemi/src/genPm.mjs'
|
|
|
9
9
|
import isestr from 'wsemi/src/isestr.mjs'
|
|
10
10
|
import fsTreeFolder from 'wsemi/src/fsTreeFolder.mjs'
|
|
11
11
|
import fsBuildReadStreamText from 'wsemi/src/fsBuildReadStreamText.mjs'
|
|
12
|
+
import filterVpfsByWindow from './filterVpfsByWindow.mjs'
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
//統計各 event 事件於各時間桶之發生頻率(鏡像 SSO staLogs/staToken.mjs;差異:不限特定 event,改依 event 名分組)。
|
|
@@ -40,6 +41,7 @@ async function staEvent(timeLength = 7, timeInterval = 'hr', opt = {}) {
|
|
|
40
41
|
|
|
41
42
|
//vpfs
|
|
42
43
|
let vpfs = fsTreeFolder(fdLog)
|
|
44
|
+
vpfs = filterVpfsByWindow(vpfs, tStart, fmt) //開檔前剔除窗外檔, 見該模組註解
|
|
43
45
|
|
|
44
46
|
//logs
|
|
45
47
|
let logs = []
|
|
@@ -5,6 +5,7 @@ import genPm from 'wsemi/src/genPm.mjs'
|
|
|
5
5
|
import isestr from 'wsemi/src/isestr.mjs'
|
|
6
6
|
import fsTreeFolder from 'wsemi/src/fsTreeFolder.mjs'
|
|
7
7
|
import fsBuildReadStreamText from 'wsemi/src/fsBuildReadStreamText.mjs'
|
|
8
|
+
import filterVpfsByWindow from './filterVpfsByWindow.mjs'
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
//統計各 event 事件於近 1日/8hr/4hr/1hr 各窗內之發生數量(鏡像 staEvent.mjs 的 NDJSON 讀取方式)。
|
|
@@ -26,6 +27,7 @@ async function staEventTable(opt = {}) {
|
|
|
26
27
|
|
|
27
28
|
//vpfs
|
|
28
29
|
let vpfs = fsTreeFolder(fdLog)
|
|
30
|
+
vpfs = filterVpfsByWindow(vpfs, now.subtract(25, 'hour'), 'YYYY-MM-DDTHH') //開檔前剔除窗外檔; 用 25h(非 24h)是因 diff('hour') 取整, 最舊可能到 now-25h, 多留一小時確保不漏讀
|
|
29
31
|
|
|
30
32
|
//逐檔逐行讀取
|
|
31
33
|
for (let vpf of vpfs) {
|
package/settings.json
CHANGED
|
@@ -167,7 +167,6 @@ export default {
|
|
|
167
167
|
|
|
168
168
|
menuKey: 'mmTargets',
|
|
169
169
|
|
|
170
|
-
panelWidth: 0,
|
|
171
170
|
panelHeight: 0,
|
|
172
171
|
|
|
173
172
|
drawer: true,
|
|
@@ -232,7 +231,6 @@ export default {
|
|
|
232
231
|
let vo = this
|
|
233
232
|
|
|
234
233
|
//save
|
|
235
|
-
vo.panelWidth = msg.snew.clientWidth
|
|
236
234
|
vo.panelHeight = msg.snew.clientHeight
|
|
237
235
|
// console.log('vo.panelHeight', vo.panelHeight)
|
|
238
236
|
|
|
@@ -296,7 +296,6 @@ export default {
|
|
|
296
296
|
mdiPencilOutline,
|
|
297
297
|
mdiContentCopy,
|
|
298
298
|
|
|
299
|
-
panelWidth: 100,
|
|
300
299
|
panelHeight: 100,
|
|
301
300
|
headHeight: 100,
|
|
302
301
|
|
|
@@ -624,7 +623,6 @@ export default {
|
|
|
624
623
|
let vo = this
|
|
625
624
|
|
|
626
625
|
//panelWidth, panelHeight
|
|
627
|
-
vo.panelWidth = msg.snew.offsetWidth
|
|
628
626
|
vo.panelHeight = msg.snew.offsetHeight
|
|
629
627
|
|
|
630
628
|
},
|
|
@@ -296,7 +296,6 @@ export default {
|
|
|
296
296
|
mdiPencilOutline,
|
|
297
297
|
mdiContentCopy,
|
|
298
298
|
|
|
299
|
-
panelWidth: 100,
|
|
300
299
|
panelHeight: 100,
|
|
301
300
|
headHeight: 100,
|
|
302
301
|
|
|
@@ -624,7 +623,6 @@ export default {
|
|
|
624
623
|
let vo = this
|
|
625
624
|
|
|
626
625
|
//panelWidth, panelHeight
|
|
627
|
-
vo.panelWidth = msg.snew.offsetWidth
|
|
628
626
|
vo.panelHeight = msg.snew.offsetHeight
|
|
629
627
|
|
|
630
628
|
},
|
|
@@ -179,7 +179,6 @@ export default {
|
|
|
179
179
|
|
|
180
180
|
dbc: debounce(300),
|
|
181
181
|
|
|
182
|
-
panelWidth: 100,
|
|
183
182
|
panelHeight: 100,
|
|
184
183
|
|
|
185
184
|
firstLoading: true,
|
|
@@ -259,7 +258,6 @@ export default {
|
|
|
259
258
|
let vo = this
|
|
260
259
|
|
|
261
260
|
//panelWidth, panelHeight
|
|
262
|
-
vo.panelWidth = msg.snew.offsetWidth
|
|
263
261
|
vo.panelHeight = msg.snew.offsetHeight
|
|
264
262
|
|
|
265
263
|
},
|