w-web-perm 1.0.48 → 1.0.50

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.
Files changed (111) hide show
  1. package/.github/workflows/ci-test.yml +2 -2
  2. package/README.md +5 -5
  3. package/dist/css/app.8781355a.css +1 -0
  4. package/dist/css/{chunk-vendors.f149e124.css → chunk-vendors.58917286.css} +1 -1
  5. package/dist/index.tmp +2 -2
  6. package/dist/js/app-legacy.b2754bb1.js +1 -0
  7. package/dist/js/app.b2754bb1.js +1 -0
  8. package/dist/js/chunk-vendors-legacy.c14a1ed1.js +21 -0
  9. package/dist/js/chunk-vendors.c14a1ed1.js +21 -0
  10. package/dist/w-web-perm.umd.js +2 -2
  11. package/dist/w-web-perm.umd.js.map +1 -1
  12. package/docs/WWebPerm.html +3 -3
  13. package/docs/WWebPerm.mjs.html +162 -210
  14. package/docs/index.html +1 -1
  15. package/logs/2026-06-19T16.log +191 -0
  16. package/logs/2026-06-19T23.log +0 -0
  17. package/logs/2026-06-20T00.log +207 -0
  18. package/logs/2026-06-20T11.log +221 -0
  19. package/logs/2026-06-20T12.log +146 -0
  20. package/logs/2026-06-20T13.log +134 -0
  21. package/logs/2026-06-20T14.log +296 -0
  22. package/logs/2026-06-20T15.log +225 -0
  23. package/logs/2026-06-20T16.log +174 -0
  24. package/logs/2026-06-21T16.log +110 -0
  25. package/logs/2026-06-21T17.log +301 -0
  26. package/logs/2026-06-21T18.log +238 -0
  27. package/logs/2026-06-21T19.log +141 -0
  28. package/logs/2026-06-21T20.log +99 -0
  29. package/logs/2026-06-21T21.log +143 -0
  30. package/logs/2026-06-21T23.log +26 -0
  31. package/logs/2026-06-22T00.log +33 -0
  32. package/logs/2026-06-22T08.log +235 -0
  33. package/logs/2026-06-22T09.log +246 -0
  34. package/logs/2026-06-22T10.log +301 -0
  35. package/logs/2026-06-22T11.log +140 -0
  36. package/logs/2026-06-22T12.log +22 -0
  37. package/logs/2026-06-22T14.log +44 -0
  38. package/logs/2026-06-22T23.log +951 -0
  39. package/logs/2026-06-23T00.log +263 -0
  40. package/logs/2026-06-23T08.log +611 -0
  41. package/logs/2026-06-30T21.log +3609 -0
  42. package/logs/2026-06-30T22.log +3682 -0
  43. package/logs/2026-06-30T23.log +592 -0
  44. package/logs/2026-07-01T12.log +0 -0
  45. package/logs/2026-07-09T22.log +2220 -0
  46. package/logs/2026-07-09T23.log +1140 -0
  47. package/logs/2026-07-10T12.log +6 -0
  48. package/logs/2026-07-10T13.log +351 -0
  49. package/logs/2026-07-10T16.log +1272 -0
  50. package/logs/2026-07-10T19.log +6 -0
  51. package/logs/2026-07-10T20.log +1883 -0
  52. package/logs/2026-07-10T21.log +3505 -0
  53. package/logs/2026-07-10T22.log +3395 -0
  54. package/logs/2026-07-11T00.log +1338 -0
  55. package/logs/2026-07-11T01.log +2492 -0
  56. package/logs/2026-07-11T17.log +19 -0
  57. package/package.json +23 -16
  58. package/public/index.html +2 -2
  59. package/server/WWebPerm.mjs +161 -209
  60. package/server/procCore.mjs +187 -0
  61. package/server/procLang.mjs +276 -60
  62. package/server/procStaInfor.mjs +128 -0
  63. package/server/provideTabs.mjs +1 -1
  64. package/server/srLog.mjs +30 -0
  65. package/server/staLogs/filterVpfsByWindow.mjs +27 -0
  66. package/server/staLogs/staEvent.callWorker.mjs +46 -0
  67. package/server/staLogs/staEvent.mjs +118 -0
  68. package/server/staLogs/staEvent.shellWorker.mjs +19 -0
  69. package/server/staLogs/staEventTable.mjs +102 -0
  70. package/settings.json +43 -0
  71. package/src/components/Common/CheckYes.vue +18 -1
  72. package/src/components/Layout.vue +1 -1
  73. package/src/components/LayoutContent.vue +13 -3
  74. package/src/components/LayoutContentGrups.vue +32 -25
  75. package/src/components/LayoutContentPemis.vue +32 -25
  76. package/src/components/LayoutContentStaInfor.vue +534 -0
  77. package/src/components/LayoutContentTargets.vue +35 -28
  78. package/src/components/LayoutContentUsers.vue +32 -25
  79. package/src/components/LayoutState.vue +5 -0
  80. package/src/components/VeCgrups.vue +19 -9
  81. package/src/components/VeCpemis.vue +19 -9
  82. package/src/components/VeCrules.vue +19 -9
  83. package/src/components/VeGrupBlngUsers.vue +35 -29
  84. package/src/components/VePemiBlngGrups.vue +35 -29
  85. package/src/getPerm.mjs +7 -17
  86. package/src/getPermUserInfor.mjs +8 -18
  87. package/src/main.js +4 -9
  88. package/src/perm.mjs +3 -4
  89. package/src/plugins/mShare.mjs +8 -13
  90. package/src/plugins/mUI.mjs +7 -44
  91. package/src/store/mutations.mjs +0 -10
  92. package/src/store/types.mjs +0 -2
  93. package/srv.mjs +76 -101
  94. package/tableTags.json +1 -1
  95. package/z/344/277/256/346/255/243/350/246/217/345/212/203/346/233/270.md +450 -0
  96. package/db/wperm/grups/data.mdb +0 -0
  97. package/db/wperm/grups/lock.mdb +0 -0
  98. package/db/wperm/pemis/data.mdb +0 -0
  99. package/db/wperm/pemis/lock.mdb +0 -0
  100. package/db/wperm/targets/data.mdb +0 -0
  101. package/db/wperm/targets/lock.mdb +0 -0
  102. package/db/wperm/users/data.mdb +0 -0
  103. package/db/wperm/users/lock.mdb +0 -0
  104. package/dist/css/app.30e81bfe.css +0 -1
  105. package/dist/js/app-legacy.3324392e.js +0 -1
  106. package/dist/js/app.3324392e.js +0 -1
  107. package/dist/js/chunk-vendors-legacy.e8703728.js +0 -21
  108. package/dist/js/chunk-vendors.e8703728.js +0 -21
  109. package/src/plugins/mDataSelectorSchema.mjs +0 -30
  110. package/tableTags-web-perm.json +0 -1
  111. package/test/getUserRules.test.mjs +0 -899
@@ -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'
@@ -23,15 +21,16 @@ import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
23
21
  import fsIsFile from 'wsemi/src/fsIsFile.mjs'
24
22
  import replace from 'wsemi/src/replace.mjs'
25
23
  import strdelleft from 'wsemi/src/strdelleft.mjs'
26
- import haskey from 'wsemi/src/haskey.mjs'
27
- import arrHas from 'wsemi/src/arrHas.mjs'
28
- import ltdtDiffByKey from 'wsemi/src/ltdtDiffByKey.mjs'
29
24
  import ltdtmapping from 'wsemi/src/ltdtmapping.mjs'
25
+ import pmKeyMutex from 'wsemi/src/pmKeyMutex.mjs'
30
26
  import WServHapiServer from 'w-serv-hapi/src/WServHapiServer.mjs'
31
27
  import WServOrm from 'w-serv-orm/src/WServOrm.mjs'
32
28
  import ds from '../src/schema/index.mjs'
33
29
  import { getUserRules } from '../src/plugins/mShare.mjs'
30
+ import procCore from './procCore.mjs'
34
31
  import procLang from './procLang.mjs'
32
+ import srLogInit from './srLog.mjs'
33
+ import procStaInfor from './procStaInfor.mjs'
35
34
 
36
35
 
37
36
  /**
@@ -41,7 +40,7 @@ import procLang from './procLang.mjs'
41
40
  * @param {Function} WOrm 輸入資料庫ORM函數
42
41
  * @param {String} url 輸入資料庫連線字串,例如w-orm-lmdb為'./db',或w-orm-mongodb為'mongodb://username:password@$127.0.0.1:27017'
43
42
  * @param {String} db 輸入資料庫名稱字串
44
- * @param {Function} getUserByToken 輸入處理函數,函數會傳入使用者token,通過此函數處理後並回傳使用者資訊物件,並至少須提供'id'、'email'、'name'、'isAdmin'欄位,且'isAdmin'限輸入'y'或'n',且輸入'y'時會複寫權限系統該使用者之'isAdmin'欄位值
43
+ * @param {Function} getUserByToken 輸入處理函數,函數會傳入使用者token,通過此函數處理後並回傳使用者資訊物件,並至少須提供'id'、'email'、'name'、'isAdmin'欄位,且'isAdmin'限輸入'y'或'n'。注意: token 回傳之'isAdmin'僅供識別,權限系統一律以自身users表該使用者之'isAdmin'欄位值為權威,不會被 token 宣稱之值複寫
45
44
  * @param {Function} verifyClientUser 輸入驗證瀏覽使用者身份之處理函數,函數會傳入使用者資訊物件,通過此函數識別後回傳布林值,允許使用者回傳true,反之回傳false
46
45
  * @param {Function} verifyAppUser 輸入驗證應用程序使用者身份之處理函數,函數會傳入使用者資訊物件,通過此函數識別後回傳布林值,允許使用者回傳true,反之回傳false
47
46
  * @param {Object} [opt={}] 輸入設定物件,預設{}
@@ -268,6 +267,10 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
268
267
  let kpLang = procLang({ kpLangExt, webName, webDescription })
269
268
 
270
269
 
270
+ //srLog
271
+ let srLog = srLogInit(opt)
272
+
273
+
271
274
  //WServOrm
272
275
  let optWServOrm = {
273
276
  useCheckUser,
@@ -280,12 +283,35 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
280
283
  }
281
284
  catch (err) {
282
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」的半死伺服器
283
288
  }
284
289
  let { woItems, procOrm } = wp
285
290
 
286
291
 
292
+ //pmKeyMutex: per-key in-memory mutex, 同 key 序列化、不同 key 並行.
293
+ //perm 為「整表批次 diff」寫入, key 粒度為 {op}:{keyTable}, 防同表並行 race (lost update).
294
+ let kmx = pmKeyMutex()
295
+
296
+
297
+ //procCore: 整表批次 diff 寫入邏輯 (updateTabItems + updateTargets/Pemis/Grups/Users)
298
+ let pc = procCore(woItems, procOrm, { srLog, kmx })
299
+
300
+
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) })
307
+
308
+
287
309
  //getWebInfor
288
310
  let getWebInfor = (userId) => {
311
+
312
+ //info
313
+ srLog.info({ event: 'getWebInfor-success', userId })
314
+
289
315
  return {
290
316
 
291
317
  // webName, //已併入kpLang
@@ -348,162 +374,32 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
348
374
  // }
349
375
 
350
376
 
351
- //updateTabItems
352
- let updateTabItems = async (keyTable, userId, rows, keyDetect) => {
353
- // console.log('updateTabItems', keyTable, userId, rows.length, keyDetect)
354
-
355
- //ltdtmapping
356
- rows = ltdtmapping(rows, ds[keyTable].keys)
357
- // console.log('ltdtmapping rows', rows)
358
-
359
- //重給order
360
- rows = map(rows, (r, k) => {
361
- r.order = k + 1
362
- return r
363
- })
364
-
365
- //ckKey
366
- let ckKey = (rows, key) => {
367
- let err = null
368
-
369
- //check
370
- let kp = {}
371
- each(rows, (v, k) => {
372
-
373
- //value
374
- let value = get(v, key, '')
375
-
376
- //check
377
- if (!isestr(value)) {
378
- err = `rows[${k}].${key} is not an effective string`
379
- return false //跳出
380
- }
381
-
382
- //check
383
- if (haskey(kp, value)) {
384
- err = `rows[${k}].${key}[${value}] is duplicate`
385
- return false //跳出
386
- }
387
-
388
- //kp
389
- kp[value] = true
390
-
391
- })
392
-
393
- return err
394
- }
395
-
396
- //偵測未給予或重複
397
- let err = null
398
- if (true) {
399
- if (arrHas(keyTable, ['targets'])) {
400
- err = ckKey(rows, 'id')
401
- if (err !== null) {
402
- return Promise.reject(err)
403
- }
404
- }
405
- if (arrHas(keyTable, ['pemis', 'grups'])) { //users可重複name故不列入
406
- err = ckKey(rows, 'name')
407
- if (err !== null) {
408
- return Promise.reject(err)
409
- }
410
- }
411
- if (arrHas(keyTable, ['users'])) {
412
- err = ckKey(rows, 'email')
413
- if (err !== null) {
414
- return Promise.reject(err)
415
- }
416
- }
417
- }
418
-
419
- //ltdtDiffByKey
420
- let ltdtOld = await woItems[keyTable].select()
421
- let ltdtNew = rows
422
- let r = ltdtDiffByKey(ltdtOld, ltdtNew, keyDetect)
423
- // console.log('ltdtDiffByKey r', r)
424
-
425
- //del
426
- if (size(r.del) > 0) {
427
- await procOrm(userId, keyTable, 'del', r.del) //須使用procOrm才有辦法自動給予相關欄位
428
- // .catch((err) => {
429
- // console.log('woItems[keyTable].del err', err)
430
- // })
431
- }
432
-
433
- //add
434
- if (size(r.add) > 0) {
435
- await procOrm(userId, keyTable, 'insert', r.add) //須使用procOrm才有辦法自動給予相關欄位
436
- // .catch((err) => {
437
- // console.log('woItems[keyTable].insert err', err)
438
- // })
439
- }
440
-
441
- //diff
442
- if (size(r.diff) > 0) {
443
- await procOrm(userId, keyTable, 'save', r.diff) //須使用procOrm才有辦法自動給予相關欄位
444
- // .catch((err) => {
445
- // console.log('woItems[keyTable].save err', err)
446
- // })
447
- }
448
-
449
- return ltdtNew
450
- }
451
-
452
-
453
- //updateTargets
454
- let updateTargets = async (userId, rows) => {
455
- rows = await updateTabItems('targets', userId, rows, 'id')
456
- return rows
457
- }
458
-
459
-
460
- //updatePemis
461
- let updatePemis = async (userId, rows) => {
462
- rows = await updateTabItems('pemis', userId, rows, 'name')
463
- return rows
464
- }
465
-
466
-
467
- //updateGrups
468
- let updateGrups = async (userId, rows) => {
469
- rows = await updateTabItems('grups', userId, rows, 'name')
470
- return rows
471
- }
472
-
473
-
474
- //updateUsers
475
- let updateUsers = async (userId, rows) => {
476
- rows = await updateTabItems('users', userId, rows, 'id')
477
- return rows
478
- }
479
-
480
-
481
377
  //checkUser
482
378
  let checkUser = async(user) => {
483
379
  let id = get(user, 'id', '')
484
380
  if (!isestr(id)) {
485
381
  console.log('user', user)
486
- console.log('can not get the userId')
487
- return Promise.reject(`can not get the userId`)
382
+ srLog.error({ event: 'checkUser-error', msg: 'cannotGetUserId' })
383
+ return Promise.reject(`cannotGetUserId`)
488
384
  }
489
385
  let email = get(user, 'email', '')
490
386
  if (!isestr(email)) {
491
387
  console.log('user', user)
492
- console.log('can not get the email of user')
493
- return Promise.reject(`can not get the email of user`)
388
+ srLog.error({ event: 'checkUser-error', msg: 'cannotGetUserEmail' })
389
+ return Promise.reject(`cannotGetUserEmail`)
494
390
  }
495
391
  let name = get(user, 'name', '')
496
392
  if (!isestr(name)) {
497
393
  console.log('user', user)
498
- console.log('can not get userName')
499
- return Promise.reject(`can not get userName`)
394
+ srLog.error({ event: 'checkUser-error', msg: 'cannotGetUserName' })
395
+ return Promise.reject(`cannotGetUserName`)
500
396
  }
501
397
  let isAdmin = get(user, 'isAdmin', '')
502
398
  if (isAdmin !== 'y' && isAdmin !== 'n') {
503
399
  console.log('user', user)
504
400
  console.log('user.isAdmin is not y or n', user.isAdmin)
505
- console.log('can not get the role of user')
506
- return Promise.reject(`can not get the role of user`)
401
+ srLog.error({ event: 'checkUser-error', msg: 'cannotGetUserRole' })
402
+ return Promise.reject(`cannotGetUserRole`)
507
403
  }
508
404
  return true
509
405
  }
@@ -526,8 +422,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
526
422
  //check
527
423
  if (!iseobj(userSelf)) {
528
424
  console.log(`token`, token)
529
- console.log(`can not find the user from token`)
530
- return Promise.reject(`can not find the user from token`)
425
+ srLog.error({ event: 'getTokenUser-error', msg: 'cannotFindUserFromToken' })
426
+ return Promise.reject(`cannotFindUserFromToken`)
531
427
  }
532
428
 
533
429
  //check userSelf
@@ -541,8 +437,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
541
437
  if (!isestr(vSelf)) {
542
438
  console.log('userSelf', userSelf)
543
439
  console.log('mappingBy', mappingBy)
544
- console.log('can not get the prop of user by mappingBy')
545
- return Promise.reject(`can not get the prop of user by mappingBy`)
440
+ srLog.error({ event: 'getTokenUser-error', msg: 'cannotGetUserProp', mappingBy })
441
+ return Promise.reject(`cannotGetUserProp`)
546
442
  }
547
443
 
548
444
  //userFind
@@ -554,17 +450,14 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
554
450
  if (!iseobj(userFind)) {
555
451
  console.log('userSelf', userSelf)
556
452
  console.log('mappingBy', mappingBy)
557
- console.log('can not get the user from perm')
558
- return Promise.reject(`can not get the user from perm`)
453
+ srLog.error({ event: 'getTokenUser-error', msg: 'cannotGetUserFromPerm', mappingBy })
454
+ return Promise.reject(`cannotGetUserFromPerm`)
559
455
  }
560
456
 
561
- //複寫isAdmin
562
- let isAdminSrc = get(userSelf, 'isAdmin', '')
563
- let isAdminSelf = get(userFind, 'isAdmin', '')
564
- if (isAdminSrc !== isAdminSelf) {
565
- userFind.isAdmin = isAdminSelf
566
- }
567
- // 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 提權路徑。)
568
461
 
569
462
  return userFind
570
463
  }
@@ -602,8 +495,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
602
495
  //check
603
496
  if (!b) {
604
497
  console.log('user', user)
605
- console.log(`user does not have permission`)
606
- return Promise.reject(`user does not have permission`)
498
+ srLog.error({ event: 'getAndVerifyClientUser-error', msg: 'userNoPermission', from })
499
+ return Promise.reject(`userNoPermission`)
607
500
  }
608
501
 
609
502
  return user
@@ -627,8 +520,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
627
520
  //check
628
521
  if (!iseobj(user)) {
629
522
  console.log(`token`, token)
630
- console.log(`can not find the user from token`)
631
- return Promise.reject(`can not find the user from token`)
523
+ srLog.error({ event: 'getAndVerifyAppUser-error', msg: 'cannotFindUserFromToken' })
524
+ return Promise.reject(`cannotFindUserFromToken`)
632
525
  }
633
526
 
634
527
  //verifyAppUser
@@ -640,8 +533,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
640
533
  //check
641
534
  if (!b) {
642
535
  console.log('user', user)
643
- console.log(`user does not have permission`)
644
- return Promise.reject(`user does not have permission`)
536
+ srLog.error({ event: 'getAndVerifyAppUser-error', msg: 'userNoPermission', from })
537
+ return Promise.reject(`userNoPermission`)
645
538
  }
646
539
 
647
540
  return user
@@ -662,8 +555,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
662
555
  //check
663
556
  if (!iseobj(payload)) {
664
557
  console.log('payload', payload)
665
- console.log('invalid payload in req')
666
- return Promise.reject(`invalid payload in req`)
558
+ srLog.error({ event: 'parsePayload-error', msg: 'invalidPayload' })
559
+ return Promise.reject(`invalidPayload`)
667
560
  }
668
561
 
669
562
  //from
@@ -672,8 +565,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
672
565
  //check
673
566
  if (!isestr(from)) {
674
567
  console.log('payload', payload)
675
- console.log('invalid from in payload')
676
- return Promise.reject(`invalid from in payload`)
568
+ srLog.error({ event: 'parsePayload-error', msg: 'invalidFromInPayload' })
569
+ return Promise.reject(`invalidFromInPayload`)
677
570
  }
678
571
 
679
572
  //rows
@@ -682,8 +575,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
682
575
  //check
683
576
  if (!isearr(rows)) {
684
577
  console.log('payload', payload)
685
- console.log(`invalid rows in payload`)
686
- return Promise.reject(`invalid rows in payload`)
578
+ srLog.error({ event: 'parsePayload-error', msg: 'invalidRowsInPayload' })
579
+ return Promise.reject(`invalidRowsInPayload`)
687
580
  }
688
581
 
689
582
  //inp
@@ -699,6 +592,9 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
699
592
  //syncAndReplaceTabs
700
593
  let syncAndReplaceTabs = async (userId, inp, keyTable) => {
701
594
 
595
+ //序列化同表之並行整表 delAll+insert: 防 lost update (同 keyTable 序列化, 不同表並行)
596
+ return await kmx('syncAndReplaceTabs:' + keyTable, async () => {
597
+
702
598
  //from
703
599
  let from = get(inp, 'from', '')
704
600
  // console.log('from', from)
@@ -706,8 +602,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
706
602
  //check
707
603
  if (!isestr(from)) {
708
604
  console.log('inp', inp)
709
- console.log(`invalid from`)
710
- return Promise.reject(`invalid from`)
605
+ srLog.error({ event: 'syncAndReplaceTabs-error', msg: 'invalidFrom', keyTable })
606
+ return Promise.reject(`invalidFrom`)
711
607
  }
712
608
 
713
609
  //rows
@@ -718,7 +614,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
718
614
  if (!isearr(rows)) {
719
615
  console.log('inp', inp)
720
616
  console.log('keyTable', keyTable)
721
- return Promise.reject(`invalid rows`)
617
+ srLog.error({ event: 'syncAndReplaceTabs-error', msg: 'invalidRows', keyTable })
618
+ return Promise.reject(`invalidRows`)
722
619
  }
723
620
 
724
621
  //save from
@@ -741,6 +638,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
741
638
  // console.log('r', r)
742
639
 
743
640
  return r
641
+
642
+ })
744
643
  }
745
644
 
746
645
 
@@ -801,8 +700,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
801
700
  //check
802
701
  if (!iseobj(kur)) {
803
702
  console.log('userId', userId)
804
- console.log(`can not get rules of user`)
805
- return Promise.reject(`can not get rules of user`)
703
+ srLog.error({ event: 'getGenUserAndRulesByUserId-error', msg: 'cannotGetUserRules', userId })
704
+ return Promise.reject(`cannotGetUserRules`)
806
705
  }
807
706
 
808
707
  //grupsNames
@@ -876,8 +775,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
876
775
  console.log('req.query', get(req, 'query'))
877
776
  console.log('token', token)
878
777
  console.log('[API]getUserByToken/check token: invalid token')
879
- console.log(`token does not have permission`)
880
- return Promise.reject(`token does not have permission`)
778
+ srLog.error({ event: 'api/getUserByToken-error', msg: 'tokenNoPermission' })
779
+ return Promise.reject(`tokenNoPermission`)
881
780
  }
882
781
 
883
782
  //getAndVerifyClientUser
@@ -888,10 +787,13 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
888
787
  if (!iseobj(user)) {
889
788
  console.log('token', token)
890
789
  console.log('[API]getUserByToken/check user: invalid user')
891
- console.log(`token does not have permission`)
892
- return Promise.reject(`token does not have permission`)
790
+ srLog.error({ event: 'api/getUserByToken-error', msg: 'tokenNoPermission' })
791
+ return Promise.reject(`tokenNoPermission`)
893
792
  }
894
793
 
794
+ //info
795
+ srLog.info({ event: 'api/getUserByToken-success', userId: get(user, 'id', '') })
796
+
895
797
  return user
896
798
  }
897
799
 
@@ -923,8 +825,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
923
825
  console.log('req.query', get(req, 'query'))
924
826
  console.log('token', token)
925
827
  console.log('[API]getPerm/check token: invalid token')
926
- console.log(`token does not have permission`)
927
- return Promise.reject(`token does not have permission`)
828
+ srLog.error({ event: 'api/getPerm-error', msg: 'tokenNoPermission' })
829
+ return Promise.reject(`tokenNoPermission`)
928
830
  }
929
831
 
930
832
  //getAndVerifyClientUser, 發起token的所屬使用者, 驗證僅對client使用者, 不得使用app使用者
@@ -935,8 +837,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
935
837
  if (!iseobj(userSelf)) {
936
838
  console.log('token', token)
937
839
  console.log('[API]getPerm/check userSelf: invalid userSelf')
938
- console.log(`token does not have permission`)
939
- return Promise.reject(`token does not have permission`)
840
+ srLog.error({ event: 'api/getPerm-error', msg: 'tokenNoPermission' })
841
+ return Promise.reject(`tokenNoPermission`)
940
842
  }
941
843
 
942
844
  //userIdSelf, client使用者的userId
@@ -948,8 +850,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
948
850
  console.log('token', token)
949
851
  console.log('userSelf', userSelf)
950
852
  console.log('[API]getPerm/check userIdSelf: invalid userIdSelf')
951
- console.log(`token does not have permission`)
952
- return Promise.reject(`token does not have permission`)
853
+ srLog.error({ event: 'api/getPerm-error', msg: 'tokenNoPermission' })
854
+ return Promise.reject(`tokenNoPermission`)
953
855
  }
954
856
 
955
857
  //getGenUserAndRulesByUserId, 查找到的client使用者與權限
@@ -962,10 +864,13 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
962
864
  console.log('userSelf', userSelf)
963
865
  console.log('userIdSelf', userIdSelf)
964
866
  console.log('[API]getPerm/check userWithRulesSelf: invalid userWithRulesSelf')
965
- console.log(`token does not have permission`)
966
- return Promise.reject(`token does not have permission`)
867
+ srLog.error({ event: 'api/getPerm-error', msg: 'tokenNoPermission' })
868
+ return Promise.reject(`tokenNoPermission`)
967
869
  }
968
870
 
871
+ //info
872
+ srLog.info({ event: 'api/getPerm-success', userId: userIdSelf })
873
+
969
874
  return userWithRulesSelf
970
875
  }
971
876
 
@@ -996,8 +901,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
996
901
  if (!isestr(tokenSelf)) {
997
902
  console.log('req.query', get(req, 'query'))
998
903
  console.log('[API]getPermUserInfor/check tokenSelf: invalid tokenSelf')
999
- console.log(`token does not have permission`)
1000
- return Promise.reject(`token does not have permission`)
904
+ srLog.error({ event: 'api/getPermUserInfor-error', msg: 'tokenNoPermission' })
905
+ return Promise.reject(`tokenNoPermission`)
1001
906
  }
1002
907
 
1003
908
  //getAndVerifyAppUser, 發起token的所屬使用者, 驗證須為app使用者
@@ -1009,8 +914,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1009
914
  console.log('req.query', get(req, 'query'))
1010
915
  console.log('tokenSelf', tokenSelf)
1011
916
  console.log('[API]getPermUserInfor/check userSelf: invalid userSelf')
1012
- console.log(`token does not have permission`)
1013
- return Promise.reject(`token does not have permission`)
917
+ srLog.error({ event: 'api/getPermUserInfor-error', msg: 'tokenNoPermission' })
918
+ return Promise.reject(`tokenNoPermission`)
1014
919
  }
1015
920
 
1016
921
  //userIdFind, 欲查找使用者的userId
@@ -1022,8 +927,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1022
927
  console.log('req.query', get(req, 'query'))
1023
928
  console.log('tokenSelf', tokenSelf)
1024
929
  console.log('[API]getPermUserInfor/check userIdFind: invalid userIdFind')
1025
- console.log(`token does not have permission`)
1026
- return Promise.reject(`token does not have permission`)
930
+ srLog.error({ event: 'api/getPermUserInfor-error', msg: 'tokenNoPermission' })
931
+ return Promise.reject(`tokenNoPermission`)
1027
932
  }
1028
933
 
1029
934
  //getGenUserAndRulesByUserId, 查找到的使用者與權限
@@ -1034,10 +939,13 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1034
939
  if (!iseobj(userWithRulesFind)) {
1035
940
  console.log('userIdFind', userIdFind)
1036
941
  console.log('[API]getPermUserInfor/check userWithRulesFind: invalid userWithRulesFind')
1037
- console.log(`userId does not have permrules`)
1038
- return Promise.reject(`userId does not have permrules`)
942
+ srLog.error({ event: 'api/getPermUserInfor-error', msg: 'userIdNoPermrules' })
943
+ return Promise.reject(`userIdNoPermrules`)
1039
944
  }
1040
945
 
946
+ //info
947
+ srLog.info({ event: 'api/getPermUserInfor-success', userId: userIdFind })
948
+
1041
949
  return userWithRulesFind
1042
950
  }
1043
951
 
@@ -1069,8 +977,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1069
977
  console.log('req.query', get(req, 'query'))
1070
978
  console.log('token', token)
1071
979
  console.log('[API]syncAndReplaceTabs/check token: invalid token')
1072
- console.log(`token does not have permission`)
1073
- return Promise.reject(`token does not have permission`)
980
+ srLog.error({ event: 'api/syncAndReplaceTabs-error', msg: 'tokenNoPermission' })
981
+ return Promise.reject(`tokenNoPermission`)
1074
982
  }
1075
983
 
1076
984
  //keyTable
@@ -1081,15 +989,16 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1081
989
  console.log('req.query', get(req, 'query'))
1082
990
  console.log('keyTable', keyTable)
1083
991
  console.log('[API]syncAndReplaceTabs/check keyTable: invalid keyTable')
1084
- console.log(`token does not have permission`)
1085
- return Promise.reject(`token does not have permission`)
992
+ srLog.error({ event: 'api/syncAndReplaceTabs-error', msg: 'tokenNoPermission' })
993
+ return Promise.reject(`tokenNoPermission`)
1086
994
  }
1087
995
 
1088
996
  //check keyTable
1089
997
  let allowedTables = ['targets', 'pemis', 'grups', 'users']
1090
998
  if (!allowedTables.includes(keyTable)) {
1091
999
  console.log('[API]syncAndReplaceTabs/check keyTable: unexpected keyTable', keyTable)
1092
- return Promise.reject(`invalid keyTable: ${keyTable}`)
1000
+ srLog.error({ event: 'api/syncAndReplaceTabs-error', msg: 'invalidKeyTable', keyTable })
1001
+ return Promise.reject(`invalidKeyTable`)
1093
1002
  }
1094
1003
 
1095
1004
  //getAndVerifyAppUser
@@ -1099,8 +1008,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1099
1008
  if (!iseobj(user)) {
1100
1009
  console.log('token', token)
1101
1010
  console.log('[API]syncAndReplaceTabs/check user: invalid user')
1102
- console.log(`token does not have permission`)
1103
- return Promise.reject(`token does not have permission`)
1011
+ srLog.error({ event: 'api/syncAndReplaceTabs-error', msg: 'tokenNoPermission' })
1012
+ return Promise.reject(`tokenNoPermission`)
1104
1013
  }
1105
1014
 
1106
1015
  //userId
@@ -1111,8 +1020,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1111
1020
  console.log('token', token)
1112
1021
  console.log('user', user)
1113
1022
  console.log('[API]syncAndReplaceTabs/check userId: invalid userId')
1114
- console.log(`token does not have permission`)
1115
- return Promise.reject(`token does not have permission`)
1023
+ srLog.error({ event: 'api/syncAndReplaceTabs-error', msg: 'tokenNoPermission' })
1024
+ return Promise.reject(`tokenNoPermission`)
1116
1025
  }
1117
1026
 
1118
1027
  //parsePayload
@@ -1121,6 +1030,9 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1121
1030
  //syncAndReplaceTabs
1122
1031
  let r = await syncAndReplaceTabs(userId, inp, keyTable)
1123
1032
 
1033
+ //info
1034
+ srLog.info({ event: 'api/syncAndReplaceTabs-success', userId, keyTable })
1035
+
1124
1036
  return r
1125
1037
  }
1126
1038
 
@@ -1147,7 +1059,7 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1147
1059
 
1148
1060
  //WServHapiServer
1149
1061
  instWServHapiServer = new WServHapiServer({
1150
- port: opt.serverPort,
1062
+ port: serverPort,
1151
1063
  pathStaticFiles,
1152
1064
  apiName: 'api',
1153
1065
  apis,
@@ -1171,6 +1083,21 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1171
1083
  let b = await p.checkToken(token)
1172
1084
  // console.log('b', b)
1173
1085
 
1086
+ //ip, 對齊sso慣例x-forwarded-for取第1個, fallback到req remoteAddress, 取不到則略過
1087
+ let ip = get(headers, 'x-forwarded-for', '').split(',')[0].trim()
1088
+ if (!isestr(ip)) {
1089
+ ip = get(req, 'info.remoteAddress', '') || get(req, 'socket.remoteAddress', '') || get(req, 'connection.remoteAddress', '')
1090
+ }
1091
+
1092
+ //origin
1093
+ let origin = get(headers, 'origin', '')
1094
+
1095
+ //referer
1096
+ let referer = get(headers, 'referer', '')
1097
+
1098
+ //info
1099
+ srLog.info({ event: 'verifyConn', ok: b, apiType, ip, origin, referer })
1100
+
1174
1101
  return b
1175
1102
  },
1176
1103
  getUserIdByToken: async (token) => { //可使用async或sync函數
@@ -1180,7 +1107,8 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1180
1107
  if (!isestr(userId)) {
1181
1108
  console.log('token', token)
1182
1109
  console.log('userId', userId)
1183
- return Promise.reject(`can not find user.id`)
1110
+ srLog.error({ event: 'getUserIdByToken-error', msg: 'cannotFindUserId' })
1111
+ return Promise.reject(`cannotFindUserId`)
1184
1112
  }
1185
1113
  return userId
1186
1114
  },
@@ -1200,10 +1128,34 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1200
1128
  // getGrupsList,
1201
1129
  // getUsersList,
1202
1130
 
1203
- updateTargets,
1204
- updatePemis,
1205
- updateGrups,
1206
- updateUsers,
1131
+ updateTargets: pc.updateTargets,
1132
+ updatePemis: pc.updatePemis,
1133
+ updateGrups: pc.updateGrups,
1134
+ updateUsers: pc.updateUsers,
1135
+
1136
+ getStaEvent: async (userId, timeLength, timeInterval) => {
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
+ }
1146
+ },
1147
+
1148
+ getStaEventTable: async (userId) => {
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
+ }
1158
+ },
1207
1159
 
1208
1160
  },
1209
1161
  fnTableTags: 'tableTags-web-perm.json',