w-web-sso 1.0.38 → 1.0.39

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 (98) hide show
  1. package/.github/workflows/ci-test.yml +2 -2
  2. package/_gh-pages-deploy/.nojekyll +0 -0
  3. package/_gh-pages-deploy/WWebSso.html +400 -0
  4. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  5. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  6. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  7. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  8. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  9. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  10. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  11. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  12. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  13. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
  14. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  15. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  16. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  17. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  18. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
  19. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  20. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  21. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  22. package/_gh-pages-deploy/index.html +84 -0
  23. package/_gh-pages-deploy/scripts/collapse.js +39 -0
  24. package/_gh-pages-deploy/scripts/commonNav.js +28 -0
  25. package/_gh-pages-deploy/scripts/linenumber.js +25 -0
  26. package/_gh-pages-deploy/scripts/nav.js +12 -0
  27. package/_gh-pages-deploy/scripts/polyfill.js +4 -0
  28. package/_gh-pages-deploy/scripts/prettify/Apache-License-2.0.txt +202 -0
  29. package/_gh-pages-deploy/scripts/prettify/lang-css.js +2 -0
  30. package/_gh-pages-deploy/scripts/prettify/prettify.js +28 -0
  31. package/_gh-pages-deploy/scripts/search.js +99 -0
  32. package/{docs/WWebSso.mjs.html → _gh-pages-deploy/server_WWebSso.mjs.html} +1333 -1149
  33. package/_gh-pages-deploy/src_components_Common_CheckYesNo.vue.html +195 -0
  34. package/_gh-pages-deploy/styles/jsdoc.css +776 -0
  35. package/_gh-pages-deploy/styles/prettify.css +80 -0
  36. package/dist/css/app.5f246189.css +1 -0
  37. package/dist/css/{chunk-vendors.7b9a50ef.css → chunk-vendors.f831c81d.css} +1 -1
  38. package/dist/index.tmp +2 -6
  39. package/dist/js/app-legacy.73f3eae0.js +1 -0
  40. package/dist/js/app.73f3eae0.js +1 -0
  41. package/dist/js/{chunk-vendors-legacy.d71138ee.js → chunk-vendors-legacy.7c56bceb.js} +6 -6
  42. package/dist/js/{chunk-vendors.d71138ee.js → chunk-vendors.7c56bceb.js} +6 -6
  43. package/dist/w-web-sso.umd.js +5 -3
  44. package/dist/w-web-sso.umd.js.map +1 -1
  45. package/docs/WWebSso.html +2 -2
  46. package/docs/index.html +1 -1
  47. package/docs/server_WWebSso.mjs.html +1402 -0
  48. package/docs/src_components_Common_CheckYesNo.vue.html +195 -0
  49. package/g.getSettings.mjs +49 -3
  50. package/g.initialData.mjs +103 -78
  51. package/package.json +12 -9
  52. package/public/index.html +3 -16
  53. package/server/WWebSso.mjs +1330 -1146
  54. package/server/genRandomPassword.mjs +120 -0
  55. package/server/procCore.mjs +700 -176
  56. package/server/procLang.mjs +1231 -1047
  57. package/server/procProtect.mjs +40 -12
  58. package/server/procStaInfor.mjs +46 -26
  59. package/server/template/changePasswordEmail-cht.html +1 -0
  60. package/server/template/changePasswordEmail-eng.html +1 -0
  61. package/server/template/regVerifyEmail-cht.html +1 -0
  62. package/server/template/regVerifyEmail-eng.html +1 -0
  63. package/server/template/resetPasswordEmail-cht.html +1 -0
  64. package/server/template/resetPasswordEmail-eng.html +1 -0
  65. package/server/template/verifyEmailResult.html +26 -0
  66. package/settings.json +48 -11
  67. package/src/App.vue +24 -3
  68. package/src/components/Common/CheckYes.vue +18 -1
  69. package/src/components/Common/CheckYesNo.vue +32 -0
  70. package/src/components/Layout.vue +12 -7
  71. package/src/components/LayoutContent.vue +23 -0
  72. package/src/components/LayoutContentIps.vue +96 -77
  73. package/src/components/LayoutContentStaInfor.vue +146 -43
  74. package/src/components/LayoutContentTokens.vue +139 -103
  75. package/src/components/LayoutContentUserInfor.vue +518 -1017
  76. package/src/components/LayoutContentUsers.vue +276 -189
  77. package/src/components/PageLogin.vue +460 -20
  78. package/src/components/PageUser.vue +109 -37
  79. package/src/main.js +1 -5
  80. package/src/plugins/mShare.mjs +0 -215
  81. package/src/plugins/mUI.mjs +119 -45
  82. package/src/schema/tables/users.mjs +12 -1
  83. package/srv.mjs +2 -2
  84. package/db/wsso/ips/data.mdb +0 -0
  85. package/db/wsso/ips/lock.mdb +0 -0
  86. package/db/wsso/tokens/data.mdb +0 -0
  87. package/db/wsso/tokens/lock.mdb +0 -0
  88. package/db/wsso/users/data.mdb +0 -0
  89. package/db/wsso/users/lock.mdb +0 -0
  90. package/dist/css/app.d7bc51ed.css +0 -1
  91. package/dist/js/app-legacy.ca0d1dfd.js +0 -1
  92. package/dist/js/app.ca0d1dfd.js +0 -1
  93. package/g.provideUsers.mjs +0 -45
  94. package/server/provideTabs.mjs +0 -71
  95. package/src/components/Common/LabelItem.vue +0 -106
  96. package/src/components/ruleGroup.vue.txt +0 -644
  97. package/src/plugins/mDataSupport.mjs +0 -23
  98. package/test/all.test.mjs +0 -10
@@ -43,7 +43,6 @@ import arrInsert from 'wsemi/src/arrInsert.mjs'
43
43
  import replace from 'wsemi/src/replace.mjs'
44
44
  import timemsTZ2past from 'wsemi/src/timemsTZ2past.mjs'
45
45
  import convertToTree from 'wsemi/src/convertToTree.mjs'
46
- // import WUiDwload from 'w-ui-dwload/src/WUiDwload.mjs'
47
46
 
48
47
 
49
48
  let vo = Vue.prototype
@@ -134,16 +133,19 @@ function validLang(lang) {
134
133
  function getLang() {
135
134
  let lang = ''
136
135
 
137
- //from window
136
+ //from URL ?lang= (轉址攜帶語系時最優先,例如登入後 redirect 帶當前語系)
138
137
  if (!isestr(lang)) {
139
- let _lang = get(window, '___pmwsso___.language', '')
140
- // console.log('_lang(from window)', _lang)
141
- if (isestr(_lang)) {
142
- lang = validLang(_lang) //有可能取到未取代前模板符號
138
+ try {
139
+ let _lang = new URLSearchParams(window.location.search).get('lang') || ''
140
+ // console.log('_lang(from url)', _lang)
141
+ if (isestr(_lang)) {
142
+ lang = validLang(_lang)
143
+ }
143
144
  }
145
+ catch (err) {}
144
146
  }
145
147
 
146
- //from store
148
+ //from store (使用者當下選的,或 App.vue beforeMount 已設定)
147
149
  if (!isestr(lang)) {
148
150
  let _lang = get(vo, '$store.state.lang', '')
149
151
  // console.log('_lang(from store)', _lang)
@@ -152,6 +154,15 @@ function getLang() {
152
154
  }
153
155
  }
154
156
 
157
+ //from window (server 預設語系,fallback)
158
+ if (!isestr(lang)) {
159
+ let _lang = get(window, '___pmwsso___.language', '')
160
+ // console.log('_lang(from window)', _lang)
161
+ if (isestr(_lang)) {
162
+ lang = validLang(_lang) //有可能取到未取代前模板符號
163
+ }
164
+ }
165
+
155
166
  //validLang
156
167
  lang = validLang(lang) //有可能給予非預期lang
157
168
 
@@ -217,6 +228,23 @@ function getKpText(key) {
217
228
  }
218
229
 
219
230
 
231
+ //getKpTextErr: 錯誤訊息專用翻譯 — 先 getKpText(key) 取得模板, 再插值密碼政策值 (minLength / maxLength /
232
+ //consecutiveCharsMinMatch 來自 webInfor.passwordPolicyInfo, 即時反映 settings). 供「錯誤 reject key」顯示用:
233
+ //多數錯誤 key 無模板變數 (replace 為 no-op); 僅密碼政策 key (userPassword_keyLim*) 含 {minLength} 等需插值.
234
+ //設計: 後端一律 reject key 字串 (msg-key 契約), 前端統一以 $tErr(key) 翻譯+插值顯示.
235
+ function getKpTextErr(key) {
236
+ let t = getKpText(key)
237
+ if (!isestr(t)) {
238
+ return t
239
+ }
240
+ let p = get(vo, '$store.state.webInfor.passwordPolicyInfo', {})
241
+ t = t.replace('{minLength}', get(p, 'minLength', ''))
242
+ t = t.replace('{maxLength}', get(p, 'maxLength', ''))
243
+ t = t.replace('{consecutiveCharsMinMatch}', get(p, 'consecutiveCharsMinMatch', ''))
244
+ return t
245
+ }
246
+
247
+
220
248
  function syncHeight() {
221
249
 
222
250
  //heightToolbar
@@ -302,21 +330,23 @@ function getUrlView() {
302
330
  }
303
331
 
304
332
 
305
- function goUrl(url, token) {
333
+ function goUrl(url, token, lang) {
306
334
 
307
- //replace
335
+ //replace {token}
308
336
  url = url.replaceAll('{token}', token)
309
337
 
338
+ //replace {lang} 佔位符(若 redir 模板顯式包含)
339
+ if (isestr(lang)) {
340
+ url = url.replaceAll('{lang}', lang)
341
+ }
342
+
343
+ //自動 append &lang= 給轉址後的系統作為依據(已含 lang 參數則不覆寫)
344
+ if (isestr(lang) && !/[?&]lang=/.test(url)) {
345
+ let sep = url.includes('?') ? '&' : '?'
346
+ url = `${url}${sep}lang=${encodeURIComponent(lang)}`
347
+ }
348
+
310
349
  //href
311
- // if (isDev()) {
312
- // console.log('10s後重導至:', url)
313
- // setTimeout(() => {
314
- // window.location.href = url
315
- // }, 10 * 1000)
316
- // }
317
- // else {
318
- // window.location.href = url
319
- // }
320
350
  window.location.href = url
321
351
 
322
352
  }
@@ -337,10 +367,10 @@ function login(account, password, opt = {}) {
337
367
  //pm
338
368
  let pm = genPm()
339
369
 
340
- async function core() {
370
+ //hideLoading, 結束預設隱藏loading, 只有當成功登入要轉址時不隱藏loading, 其他成功或失敗都要隱藏
371
+ let hideLoadingForEnd = true
341
372
 
342
- //show loading
343
- updateLoading(true)
373
+ let core = async() => {
344
374
 
345
375
  //delay
346
376
  await delay(1000)
@@ -357,11 +387,15 @@ function login(account, password, opt = {}) {
357
387
  //reject
358
388
  pm.reject('invalid $keyLS')
359
389
 
390
+ //結束後隱藏loading
391
+ hideLoadingForEnd = true
392
+
360
393
  return
361
394
  }
362
395
 
363
- //loginByAccountAndPassword
364
- let u = await vo.$fapi.loginByAccountAndPassword(account, password)
396
+ //loginByAccountAndPassword (帶 lang, 後端 reject { key, msg } 之 msg 依 lang 翻譯)
397
+ let langLogin = get(vo, '$store.state.lang', 'eng')
398
+ let u = await vo.$fapi.loginByAccountAndPassword(langLogin, account, password)
365
399
  // console.log('u', u)
366
400
 
367
401
  //key
@@ -378,6 +412,9 @@ function login(account, password, opt = {}) {
378
412
  //reject
379
413
  pm.reject('invalid user')
380
414
 
415
+ //結束後隱藏loading
416
+ hideLoadingForEnd = true
417
+
381
418
  return
382
419
  }
383
420
 
@@ -394,6 +431,9 @@ function login(account, password, opt = {}) {
394
431
  //reject
395
432
  pm.reject('invalid token')
396
433
 
434
+ //結束後隱藏loading
435
+ hideLoadingForEnd = true
436
+
397
437
  return
398
438
  }
399
439
 
@@ -404,6 +444,13 @@ function login(account, password, opt = {}) {
404
444
  updateUserToken(token)
405
445
  updateUserSelf(u)
406
446
 
447
+ //強制變更密碼模式: 不論原本是否要 useRedir, 一律不走 redir,
448
+ //並把 view 切到 'user' 確保 PageUser 進入強制變更模式 (見 PageUser.mounted)
449
+ if (get(u, 'isForceChangePw', '') === 'y') {
450
+ useRedir = false
451
+ updateViewState('user')
452
+ }
453
+
407
454
  //useRedir
408
455
  if (useRedir) {
409
456
 
@@ -420,43 +467,57 @@ function login(account, password, opt = {}) {
420
467
  //reject
421
468
  pm.reject('invalid redir')
422
469
 
470
+ //結束後隱藏loading
471
+ hideLoadingForEnd = true
472
+
423
473
  return
424
474
  }
425
475
 
426
- //goUrl
427
- goUrl(redir, token)
428
- // console.log('goUrl', redir, 'token', token)
476
+ //goUrl,帶上當前 lang 讓轉址後系統能讀 ?lang=
477
+ let lang = get(vo, '$store.state.lang', '')
478
+ goUrl(redir, token, lang)
479
+ // console.log('goUrl', redir, 'token', token, 'lang', lang)
429
480
 
430
481
  //resolve
431
482
  pm.resolve('redir')
432
483
 
433
- // //hide loading, 登入成功不能先隱藏loading, 開始轉址到轉跳會有一段時間, 持續顯示loading不隱藏, 避免使用者誤會登入失敗
434
- // updateLoading(false)
484
+ //結束後不隱藏loading, 因開始轉址到轉跳會有一段時間, 持續顯示loading不隱藏, 避免使用者誤會登入失敗
485
+ hideLoadingForEnd = false
435
486
 
436
487
  return
437
488
  }
438
489
 
439
- //hide loading, 登入成功後且非轉址時隱藏loading
440
- updateLoading(false)
490
+ //結束後隱藏loading, 登入成功後且非轉址時隱藏loading
491
+ hideLoadingForEnd = true
441
492
 
442
493
  //resolve
443
494
  pm.resolve('done')
444
495
 
445
496
  }
446
497
 
498
+ //show loading
499
+ updateLoading(true)
500
+
447
501
  //core
448
502
  core()
449
503
  .catch((err) => {
450
504
  // console.log('login', err)
451
505
 
452
- //alert
453
- vo.$alert(vo.$t('failedLoginForCatch'), { type: 'error' })
506
+ // //alert
507
+ // vo.$alert(vo.$t('failedLoginForCatch'), { type: 'error' })
454
508
 
455
509
  //reject
456
510
  pm.reject(err)
457
511
 
458
- //hide loading, 登入失敗後隱藏loading
459
- updateLoading(false)
512
+ })
513
+ .finally(() => {
514
+
515
+ if (hideLoadingForEnd) {
516
+
517
+ //hide loading, 登入失敗後隱藏loading
518
+ updateLoading(false)
519
+
520
+ }
460
521
 
461
522
  })
462
523
 
@@ -482,7 +543,7 @@ function autoLogin(opt = {}) {
482
543
  //pm
483
544
  let pm = genPm()
484
545
 
485
- async function core() {
546
+ let core = async () => {
486
547
  let errTemp = null
487
548
 
488
549
  //waitFun, 因須使用webKey故須等待syncState
@@ -519,29 +580,33 @@ function autoLogin(opt = {}) {
519
580
 
520
581
  //check
521
582
  if (!isestr(token)) {
522
- // console.log('invalid token')
583
+ // console.log('no token')
523
584
 
524
585
  //reject
525
- pm.reject('invalid token')
586
+ pm.reject('no token')
526
587
 
527
588
  return
528
589
  }
529
590
 
530
591
  //checkToken
531
- await vo.$fapi.checkToken(token) //斷線有重試機制, resolve僅回傳true, reject代表無效token或檢測token發生錯誤
592
+ let b = false
593
+ await vo.$fapi.checkToken(token) //斷線有重試機制; 後端 wrapper 在過期/無效時一律 reject, 僅在有效時 resolve(true), 不會 resolve(false)
594
+ .then((res) => {
595
+ b = res === true
596
+ })
532
597
  .catch((err) => {
533
598
  // console.log('checkToken catch', err)
534
599
  errTemp = err
535
600
  })
536
601
 
537
602
  //check
538
- if (errTemp) {
603
+ if (errTemp || !b) {
539
604
 
540
605
  //setItem, 清空token
541
606
  await localStorage.setItem(key, '')
542
607
 
543
608
  //reject
544
- pm.reject('token error')
609
+ pm.reject(errTemp ? 'token error' : 'token expired')
545
610
 
546
611
  return
547
612
  }
@@ -564,6 +629,13 @@ function autoLogin(opt = {}) {
564
629
  updateUserToken(token)
565
630
  updateUserSelf(u)
566
631
 
632
+ //強制變更密碼模式: 不論原本是否要 useRedir, 一律不走 redir,
633
+ //並把 view 切到 'user' 確保 PageUser 進入強制變更模式 (見 PageUser.mounted)
634
+ if (get(u, 'isForceChangePw', '') === 'y') {
635
+ useRedir = false
636
+ updateViewState('user')
637
+ }
638
+
567
639
  //useRedir
568
640
  if (useRedir) {
569
641
 
@@ -584,9 +656,10 @@ function autoLogin(opt = {}) {
584
656
  return
585
657
  }
586
658
 
587
- //goUrl
588
- goUrl(redir, token)
589
- // console.log('goUrl', redir, 'token', token)
659
+ //goUrl,帶上當前 lang 讓轉址後系統能讀 ?lang=
660
+ let lang = get(vo, '$store.state.lang', '')
661
+ goUrl(redir, token, lang)
662
+ // console.log('goUrl', redir, 'token', token, 'lang', lang)
590
663
 
591
664
  //resolve
592
665
  pm.resolve('redir')
@@ -604,8 +677,8 @@ function autoLogin(opt = {}) {
604
677
  .catch((err) => {
605
678
  // console.log('autoLogin', err)
606
679
 
607
- //alert
608
- vo.$alert(vo.$t('failedLoginForCatch'), { type: 'error' })
680
+ // //alert
681
+ // vo.$alert(vo.$t('failedLoginForCatch'), { type: 'error' })
609
682
 
610
683
  //reject
611
684
  pm.reject(err)
@@ -729,6 +802,7 @@ let mUI = {
729
802
 
730
803
  setLang,
731
804
  getKpText,
805
+ getKpTextErr,
732
806
 
733
807
  syncHeight,
734
808
 
@@ -54,6 +54,10 @@ let settings = {
54
54
  name: '是否為系統管理員',
55
55
  type: 'STRING',
56
56
  },
57
+ tokenVerify: {
58
+ name: '註冊後驗證用token',
59
+ type: 'STRING',
60
+ },
57
61
  timeVerified: {
58
62
  name: '驗證時間',
59
63
  type: 'STRING',
@@ -66,6 +70,10 @@ let settings = {
66
70
  name: '封鎖時間',
67
71
  type: 'STRING',
68
72
  },
73
+ isForceChangePw: { //僅'y'或'n'
74
+ name: '是否須強制變更密碼',
75
+ type: 'STRING',
76
+ },
69
77
  userId: {
70
78
  name: '創建使用者主鍵',
71
79
  type: 'STRING',
@@ -82,7 +90,7 @@ let settings = {
82
90
  name: '更新時間',
83
91
  type: 'STRING',
84
92
  },
85
- isActive: {
93
+ isActive: { //僅'y'或'n'
86
94
  name: '是否有效',
87
95
  type: 'STRING',
88
96
  },
@@ -96,6 +104,9 @@ let funNew = (ndata = {}) => {
96
104
  o.userIdUpdate = o.userId
97
105
  o.timeUpdate = o.timeCreate
98
106
  o.isActive = 'y'
107
+ if (o.isForceChangePw !== 'y' && o.isForceChangePw !== 'n') {
108
+ o.isForceChangePw = 'n'
109
+ }
99
110
  return o
100
111
  }
101
112
 
package/srv.mjs CHANGED
@@ -8,10 +8,10 @@ let st = getSettings()
8
8
 
9
9
  let url = st.dbUrl
10
10
  let db = st.dbName
11
- let pathSettings = './settings.json'
11
+ let pathSettings = process.argv[2] || './settings.json'
12
12
 
13
13
  //WWebSso
14
- let instWWebSso = WWebSso(WOrm, url, db, pathSettings)
14
+ let instWWebSso = WWebSso(WOrm, url, db, pathSettings, st)
15
15
 
16
16
  instWWebSso.on('error', (err) => {
17
17
  console.log(err)
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1 +0,0 @@
1
- @media (min-width:640px){.sm\:grid-cols-2[data-v-99dfb112]{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:grid-cols-1[data-v-99dfb112]{grid-template-columns:repeat(1,minmax(0,1fr))}.lg\:grid-cols-4[data-v-99dfb112]{grid-template-columns:repeat(4,minmax(0,1fr))}}.status-danger[data-v-0620fb52]{background-color:#fef2f2;color:#b91c1c;border:1px solid #fecaca}.status-warning[data-v-0620fb52]{background-color:#fffbeb;color:#b45309;border:1px solid #fde68a}.status-success[data-v-0620fb52]{background-color:#f0fdf4;color:#15803d;border:1px solid #bbf7d0}.status-info[data-v-0620fb52]{background-color:#f8fafc;color:#475569;border:1px solid #e2e8f0}body,html{font-family:微軟正黑體,Microsoft JhengHei,MicrosoftJhengHeiRegular,Avenir,Helvetica,Arial,sans-serif;overflow-y:hidden}a,button,div,input,p,pre,span,textarea{font-family:inherit}