w-web-api 1.0.77 → 1.0.79
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/css/{app.ae472c4d.css → app.9fbdb016.css} +1 -1
- package/dist/index.tmp +1 -1
- package/dist/js/app-legacy.075a8cf9.js +1 -0
- package/dist/js/app.075a8cf9.js +1 -0
- package/dist/js/{chunk-vendors.47cf6375.js → chunk-vendors-legacy.de0404f6.js} +1 -1
- package/dist/js/{chunk-vendors-legacy.47cf6375.js → chunk-vendors.de0404f6.js} +1 -1
- package/dist/w-web-api.umd.js +2 -2
- package/dist/w-web-api.umd.js.map +1 -1
- package/docs/WWebApi.html +122 -2
- package/docs/index.html +1 -1
- package/docs/server_WWebApi.mjs.html +5 -2
- package/docs/src_components_Common_CheckYesNo.vue.html +1 -1
- package/package.json +16 -14
- package/server/WWebApi.mjs +4 -1
- package/server/procApis.mjs +26 -14
- package/server/procLang.mjs +0 -17
- package/server/srLog.mjs +13 -4
- package/server/staLogs/staEvent.mjs +8 -120
- package/server/staLogs/staLogsCore.callWorker.mjs +78 -0
- package/server/staLogs/staLogsCore.mjs +420 -0
- package/server/staLogs/staLogsCore.shellWorker.mjs +20 -0
- package/server/staLogs/test_staEvent.mjs +23 -7
- package/settings.json +1 -0
- package/src/components/LayoutContentEdit.vue +14 -2
- package/src/components/LayoutContentStats.vue +445 -557
- package/tableTags.json +1 -1
- package/dist/js/app-legacy.2aa145e5.js +0 -1
- package/dist/js/app.2aa145e5.js +0 -1
- package/logs/2026-06-19T16.log +0 -6
- package/logs/2026-06-20T11.log +0 -4
- package/logs/2026-06-20T14.log +0 -2
- package/logs/2026-06-21T20.log +0 -1
- package/logs/2026-06-21T21.log +0 -4
- package/logs/2026-06-22T07.log +0 -12
- package/logs/2026-06-22T08.log +0 -808
- package/logs/2026-06-22T09.log +0 -104
- package/logs/2026-06-22T10.log +0 -26
- package/logs/2026-06-22T11.log +0 -860
- package/logs/2026-06-22T12.log +0 -430
- package/logs/2026-06-22T23.log +0 -1547
- package/logs/2026-06-23T00.log +0 -473
- package/logs/2026-06-23T08.log +0 -231
- package/logs/2026-06-23T09.log +0 -179
- package/logs/2026-07-01T11.log +0 -488
- package/logs/2026-07-09T22.log +0 -370
- package/logs/2026-07-09T23.log +0 -370
- package/logs/2026-07-10T12.log +0 -284
- package/logs/2026-07-10T13.log +0 -777
- package/logs/2026-07-11T17.log +0 -2
- package/logs/2026-07-23T16.log +0 -856
- package/logs/2026-08-19T22.log +0 -0
- package/logs/2026-08-25T20.log +0 -0
- package/logs/2026-08-25T21.log +0 -1891
- package/z/344/277/256/346/255/243/350/246/217/345/212/203/346/233/270.md +0 -246
package/docs/WWebApi.html
CHANGED
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
|
|
81
81
|
<dt class="tag-source">Source:</dt>
|
|
82
82
|
<dd class="tag-source"><ul class="dummy"><li>
|
|
83
|
-
<a href="server_WWebApi.mjs.html">server/WWebApi.mjs</a>, <a href="server_WWebApi.mjs.html#
|
|
83
|
+
<a href="server_WWebApi.mjs.html">server/WWebApi.mjs</a>, <a href="server_WWebApi.mjs.html#line139">line 139</a>
|
|
84
84
|
</li></ul></dd>
|
|
85
85
|
|
|
86
86
|
|
|
@@ -871,6 +871,126 @@ instWWebApi.on('error', (err) => {
|
|
|
871
871
|
</tr>
|
|
872
872
|
|
|
873
873
|
|
|
874
|
+
|
|
875
|
+
<tr>
|
|
876
|
+
|
|
877
|
+
<td class="name"><code>logFd</code></td>
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
<td class="type">
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
<span class="param-type">String</span>
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
</td>
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
<td class="attributes">
|
|
892
|
+
|
|
893
|
+
<optional><br>
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
</td>
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
<td class="default">
|
|
904
|
+
|
|
905
|
+
<code>'./logs'</code>
|
|
906
|
+
|
|
907
|
+
</td>
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
<td class="description last"><p>輸入log紀錄儲存位置字串(srLog 寫入與統計掃描共用),預設'./logs'</p></td>
|
|
911
|
+
</tr>
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
<tr>
|
|
916
|
+
|
|
917
|
+
<td class="name"><code>logInterval</code></td>
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
<td class="type">
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
<span class="param-type">String</span>
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
</td>
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
<td class="attributes">
|
|
932
|
+
|
|
933
|
+
<optional><br>
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
|
|
939
|
+
</td>
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
<td class="default">
|
|
944
|
+
|
|
945
|
+
<code>'hr'</code>
|
|
946
|
+
|
|
947
|
+
</td>
|
|
948
|
+
|
|
949
|
+
|
|
950
|
+
<td class="description last"><p>輸入log紀錄檔案拆檔時長字串,可選'hr'或'day',預設'hr'</p></td>
|
|
951
|
+
</tr>
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
<tr>
|
|
956
|
+
|
|
957
|
+
<td class="name"><code>logNumKeep</code></td>
|
|
958
|
+
|
|
959
|
+
|
|
960
|
+
<td class="type">
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
<span class="param-type">Integer</span>
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
</td>
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
<td class="attributes">
|
|
972
|
+
|
|
973
|
+
<optional><br>
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
</td>
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
<td class="default">
|
|
984
|
+
|
|
985
|
+
<code>null</code>
|
|
986
|
+
|
|
987
|
+
</td>
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
<td class="description last"><p>輸入log檔保留數上限正整數,超出者自最舊刪除,未給採w-syslog預設(hr模式365*24、day模式365),預設null</p></td>
|
|
991
|
+
</tr>
|
|
992
|
+
|
|
993
|
+
|
|
874
994
|
</tbody>
|
|
875
995
|
</table>
|
|
876
996
|
|
|
@@ -959,7 +1079,7 @@ instWWebApi.on('error', (err) => {
|
|
|
959
1079
|
<br class="clear">
|
|
960
1080
|
|
|
961
1081
|
<footer>
|
|
962
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
1082
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 02 2026 19:51:31 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
963
1083
|
</footer>
|
|
964
1084
|
|
|
965
1085
|
<script>prettyPrint();</script>
|
package/docs/index.html
CHANGED
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
<br class="clear">
|
|
72
72
|
|
|
73
73
|
<footer>
|
|
74
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
74
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 02 2026 19:51:31 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
75
75
|
</footer>
|
|
76
76
|
|
|
77
77
|
<script>prettyPrint();</script>
|
|
@@ -100,6 +100,9 @@ import ds from '../src/schema/index.mjs'
|
|
|
100
100
|
* @param {String} [opt.webLogo=''] 輸入站台logo字串,採base64格式,預設''
|
|
101
101
|
* @param {String} [opt.subfolder=''] 輸入站台所在子目錄字串,提供站台位於內網採反向代理進行服務時,故需支援位於子目錄情形,預設''
|
|
102
102
|
* @param {String} [opt.urlRedirect=''] 輸入錯誤時自動轉址字串,提供站台例如無法登入或驗證失敗時須自動轉址,預設''
|
|
103
|
+
* @param {String} [opt.logFd='./logs'] 輸入log紀錄儲存位置字串(srLog 寫入與統計掃描共用),預設'./logs'
|
|
104
|
+
* @param {String} [opt.logInterval='hr'] 輸入log紀錄檔案拆檔時長字串,可選'hr'或'day',預設'hr'
|
|
105
|
+
* @param {Integer} [opt.logNumKeep=null] 輸入log檔保留數上限正整數,超出者自最舊刪除,未給採w-syslog預設(hr模式365*24、day模式365),預設null
|
|
103
106
|
* @returns {Object} 回傳物件,其內server為hapi伺服器實體,wsrv為w-converhp的伺服器事件物件,wsds為w-serv-webdata的伺服器事件物件,可監聽error事件
|
|
104
107
|
* @example
|
|
105
108
|
*
|
|
@@ -955,7 +958,7 @@ function WWebApi(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser,
|
|
|
955
958
|
//統計資訊頁:回傳各 event 之時段發生頻率(讀 logFd 之 log)。連線之 token 認證由 verifyConn 把關;
|
|
956
959
|
//授權(限管理員)屬套件使用者責任(見 verifyConn 註解與設計要點與取捨「套件授權責任」)。timeLength 天數 / timeInterval 'hr'|'day'。
|
|
957
960
|
getStaEvent: wrapErrLog('getStaEvent', (userId, timeLength = 7, timeInterval = 'hr') => {
|
|
958
|
-
return staEvent(timeLength, timeInterval, { fdLog: logFd })
|
|
961
|
+
return staEvent(timeLength, timeInterval, { fdLog: logFd, srLog })
|
|
959
962
|
}),
|
|
960
963
|
|
|
961
964
|
// getAPIsList: (userId, query = {}) => { //待提供api管理系統界面使用
|
|
@@ -989,7 +992,7 @@ export default WWebApi
|
|
|
989
992
|
<br class="clear">
|
|
990
993
|
|
|
991
994
|
<footer>
|
|
992
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
995
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 02 2026 19:51:31 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
993
996
|
</footer>
|
|
994
997
|
|
|
995
998
|
<script>prettyPrint();</script>
|
|
@@ -174,7 +174,7 @@ export default {
|
|
|
174
174
|
<br class="clear">
|
|
175
175
|
|
|
176
176
|
<footer>
|
|
177
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
177
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 02 2026 19:51:31 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
178
178
|
</footer>
|
|
179
179
|
|
|
180
180
|
<script>prettyPrint();</script>
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "w-web-api",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.79",
|
|
4
4
|
"main": "dist/w-web-api.umd.js",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"axios": "^1.18.1",
|
|
7
7
|
"json5": "^2.2.3",
|
|
8
|
-
"w-component-vue": "^2.5.
|
|
9
|
-
"w-data-collector": "^1.0.
|
|
10
|
-
"w-echarts-vue": "^1.0.
|
|
11
|
-
"w-md2html": "^1.0.
|
|
12
|
-
"w-serv-hapi": "^1.0.
|
|
13
|
-
"w-serv-orm": "^1.0.
|
|
14
|
-
"w-syslog": "^1.0.
|
|
15
|
-
"w-ui-loginout": "^1.0.
|
|
16
|
-
"wsemi": "^1.8.
|
|
8
|
+
"w-component-vue": "^2.5.9",
|
|
9
|
+
"w-data-collector": "^1.0.35",
|
|
10
|
+
"w-echarts-vue": "^1.0.7",
|
|
11
|
+
"w-md2html": "^1.0.49",
|
|
12
|
+
"w-serv-hapi": "^1.0.85",
|
|
13
|
+
"w-serv-orm": "^1.0.52",
|
|
14
|
+
"w-syslog": "^1.0.24",
|
|
15
|
+
"w-ui-loginout": "^1.0.23",
|
|
16
|
+
"wsemi": "^1.8.84"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@vue/cli-plugin-babel": "^5.0.8",
|
|
@@ -28,16 +28,18 @@
|
|
|
28
28
|
"eslint-plugin-vue": "^9.10.0",
|
|
29
29
|
"vue": "^2.7.16",
|
|
30
30
|
"vuex": "^3.6.2",
|
|
31
|
-
"w-orm-lmdb": "^1.0.
|
|
31
|
+
"w-orm-lmdb": "^1.0.29",
|
|
32
32
|
"w-package-tools": "^1.1.15",
|
|
33
33
|
"w-package-tools-e2e": "^1.0.0",
|
|
34
|
-
"w-screenctl": "^1.0.
|
|
34
|
+
"w-screenctl": "^1.0.17"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
37
|
"serve": "vue-cli-service serve",
|
|
38
38
|
"build": "vue-cli-service build",
|
|
39
|
-
"test:unit": "node server/staLogs/test_staEvent.mjs",
|
|
40
|
-
"test": "node server/staLogs/test_staEvent.mjs && mocha --timeout 300000",
|
|
39
|
+
"test:unit": "node server/staLogs/test_staEvent.mjs && mocha test/unit-*.test.mjs --timeout 300000",
|
|
40
|
+
"test:apiunit": "node server/staLogs/test_staEvent.mjs && mocha test/unit-*.test.mjs test/api-*.test.mjs --timeout 300000",
|
|
41
|
+
"test:e2e": "node test/run-e2e-isolated.mjs",
|
|
42
|
+
"test": "npm run test:apiunit && npm run test:e2e",
|
|
41
43
|
"lint": "vue-cli-service lint",
|
|
42
44
|
"deploy": "gh-pages -d docs"
|
|
43
45
|
},
|
package/server/WWebApi.mjs
CHANGED
|
@@ -53,6 +53,9 @@ import ds from '../src/schema/index.mjs'
|
|
|
53
53
|
* @param {String} [opt.webLogo=''] 輸入站台logo字串,採base64格式,預設''
|
|
54
54
|
* @param {String} [opt.subfolder=''] 輸入站台所在子目錄字串,提供站台位於內網採反向代理進行服務時,故需支援位於子目錄情形,預設''
|
|
55
55
|
* @param {String} [opt.urlRedirect=''] 輸入錯誤時自動轉址字串,提供站台例如無法登入或驗證失敗時須自動轉址,預設''
|
|
56
|
+
* @param {String} [opt.logFd='./logs'] 輸入log紀錄儲存位置字串(srLog 寫入與統計掃描共用),預設'./logs'
|
|
57
|
+
* @param {String} [opt.logInterval='hr'] 輸入log紀錄檔案拆檔時長字串,可選'hr'或'day',預設'hr'
|
|
58
|
+
* @param {Integer} [opt.logNumKeep=null] 輸入log檔保留數上限正整數,超出者自最舊刪除,未給採w-syslog預設(hr模式365*24、day模式365),預設null
|
|
56
59
|
* @returns {Object} 回傳物件,其內server為hapi伺服器實體,wsrv為w-converhp的伺服器事件物件,wsds為w-serv-webdata的伺服器事件物件,可監聽error事件
|
|
57
60
|
* @example
|
|
58
61
|
*
|
|
@@ -908,7 +911,7 @@ function WWebApi(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser,
|
|
|
908
911
|
//統計資訊頁:回傳各 event 之時段發生頻率(讀 logFd 之 log)。連線之 token 認證由 verifyConn 把關;
|
|
909
912
|
//授權(限管理員)屬套件使用者責任(見 verifyConn 註解與設計要點與取捨「套件授權責任」)。timeLength 天數 / timeInterval 'hr'|'day'。
|
|
910
913
|
getStaEvent: wrapErrLog('getStaEvent', (userId, timeLength = 7, timeInterval = 'hr') => {
|
|
911
|
-
return staEvent(timeLength, timeInterval, { fdLog: logFd })
|
|
914
|
+
return staEvent(timeLength, timeInterval, { fdLog: logFd, srLog })
|
|
912
915
|
}),
|
|
913
916
|
|
|
914
917
|
// getAPIsList: (userId, query = {}) => { //待提供api管理系統界面使用
|
package/server/procApis.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import iseobj from 'wsemi/src/iseobj.mjs'
|
|
2
2
|
import isestr from 'wsemi/src/isestr.mjs'
|
|
3
|
+
import pmKeyMutex from 'wsemi/src/pmKeyMutex.mjs'
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
function procApis(deps = {}) {
|
|
@@ -7,6 +8,10 @@ function procApis(deps = {}) {
|
|
|
7
8
|
//deps
|
|
8
9
|
let { woItems, procOrm, ds } = deps
|
|
9
10
|
|
|
11
|
+
//kmx: 後端層雙擊 / 並發防護 (CLAUDE.md 三層防護之第 3 層): 同 key 之寫入序列化, 防 API 直打 / e2e race 繞過前端
|
|
12
|
+
// key: 既有筆 `saveApi:<id>` / `deleteApi:<id>`; 新增筆無 id 以 `saveApi:new:<name>` 占位 (同名並發新增第 2 次仍序列化執行, 由呼叫端 / 後續唯一性檢查決定是否拒絕)
|
|
13
|
+
let kmx = pmKeyMutex()
|
|
14
|
+
|
|
10
15
|
|
|
11
16
|
let getApisList = async (userId) => {
|
|
12
17
|
return await woItems.apis.select({})
|
|
@@ -20,21 +25,27 @@ function procApis(deps = {}) {
|
|
|
20
25
|
return Promise.reject('errApiRowInvalid')
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
let
|
|
28
|
+
//key
|
|
29
|
+
let key = isestr(row.id) ? `saveApi:${row.id}` : `saveApi:new:${String(row.name || '')}`
|
|
30
|
+
|
|
31
|
+
return kmx(key, async () => {
|
|
32
|
+
|
|
33
|
+
//正規化:funNew 配 id/時間/預設並過濾欄位
|
|
34
|
+
let o = ds.apis.funNew(row)
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
//保留原 id 與創建時間(既有筆)
|
|
37
|
+
if (isestr(row.id)) {
|
|
38
|
+
o.id = row.id
|
|
39
|
+
if (isestr(row.timeCreate)) {
|
|
40
|
+
o.timeCreate = row.timeCreate
|
|
41
|
+
}
|
|
31
42
|
}
|
|
32
|
-
}
|
|
33
43
|
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
//save
|
|
45
|
+
let r = await procOrm(userId, 'apis', 'save', [o])
|
|
36
46
|
|
|
37
|
-
|
|
47
|
+
return r
|
|
48
|
+
})
|
|
38
49
|
}
|
|
39
50
|
|
|
40
51
|
|
|
@@ -45,9 +56,10 @@ function procApis(deps = {}) {
|
|
|
45
56
|
return Promise.reject('errApiIdInvalid')
|
|
46
57
|
}
|
|
47
58
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
return kmx(`deleteApi:${id}`, async () => {
|
|
60
|
+
let r = await procOrm(userId, 'apis', 'del', { id })
|
|
61
|
+
return r
|
|
62
|
+
})
|
|
51
63
|
}
|
|
52
64
|
|
|
53
65
|
|
package/server/procLang.mjs
CHANGED
|
@@ -2,7 +2,6 @@ import get from 'lodash-es/get.js'
|
|
|
2
2
|
import each from 'lodash-es/each.js'
|
|
3
3
|
import iseobj from 'wsemi/src/iseobj.mjs'
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
let kpLang = {
|
|
7
6
|
|
|
8
7
|
csIng: {
|
|
@@ -26,7 +25,6 @@ let kpLang = {
|
|
|
26
25
|
cht: '拒絕登入',
|
|
27
26
|
},
|
|
28
27
|
|
|
29
|
-
|
|
30
28
|
systemMessage: {
|
|
31
29
|
eng: 'System message',
|
|
32
30
|
cht: '系統確認訊息',
|
|
@@ -178,7 +176,6 @@ let kpLang = {
|
|
|
178
176
|
cht: '回傳數據範例',
|
|
179
177
|
},
|
|
180
178
|
|
|
181
|
-
|
|
182
179
|
//--- API 管理(展示/編輯/測試)---
|
|
183
180
|
|
|
184
181
|
docInput: {
|
|
@@ -226,18 +223,6 @@ let kpLang = {
|
|
|
226
223
|
eng: 'Frequency of backend logged events (APIs, kpFunExt, connection verification).',
|
|
227
224
|
cht: '後端各事件(API、kpFunExt、連線驗證)之發生頻率統計。',
|
|
228
225
|
},
|
|
229
|
-
selectEvents: {
|
|
230
|
-
eng: 'Select events',
|
|
231
|
-
cht: '選擇事件',
|
|
232
|
-
},
|
|
233
|
-
selectAll: {
|
|
234
|
-
eng: 'All',
|
|
235
|
-
cht: '全選',
|
|
236
|
-
},
|
|
237
|
-
selectNone: {
|
|
238
|
-
eng: 'Clear',
|
|
239
|
-
cht: '清除',
|
|
240
|
-
},
|
|
241
226
|
eventStatsTable: {
|
|
242
227
|
eng: 'Event Statistics',
|
|
243
228
|
cht: '事件統計表',
|
|
@@ -555,7 +540,6 @@ let kpLang = {
|
|
|
555
540
|
|
|
556
541
|
}
|
|
557
542
|
|
|
558
|
-
|
|
559
543
|
let init = (opt = {}) => {
|
|
560
544
|
|
|
561
545
|
//kpLangExt
|
|
@@ -640,5 +624,4 @@ let init = (opt = {}) => {
|
|
|
640
624
|
return r
|
|
641
625
|
}
|
|
642
626
|
|
|
643
|
-
|
|
644
627
|
export default init
|
package/server/srLog.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import get from 'lodash-es/get.js'
|
|
2
2
|
import isestr from 'wsemi/src/isestr.mjs'
|
|
3
|
+
import ispint from 'wsemi/src/ispint.mjs'
|
|
4
|
+
import cint from 'wsemi/src/cint.mjs'
|
|
3
5
|
import WSyslog from 'w-syslog/src/WSyslog.mjs'
|
|
4
6
|
|
|
5
7
|
|
|
@@ -18,10 +20,17 @@ let init = (opt = {}) => {
|
|
|
18
20
|
interval = 'hr'
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
//numKeep, settings 之 logNumKeep (opt-in): 未給採 w-syslog 預設 (hr: 365*24, day: 365), 有給但非正整數視為設定錯誤
|
|
24
|
+
let numKeep = get(opt, 'logNumKeep', null)
|
|
25
|
+
let o = { fdLog, interval }
|
|
26
|
+
if (numKeep !== null && numKeep !== undefined && numKeep !== '') {
|
|
27
|
+
if (!ispint(numKeep)) {
|
|
28
|
+
throw new Error(`invalid logNumKeep[${numKeep}], must be positive integer`)
|
|
29
|
+
}
|
|
30
|
+
o.numKeep = cint(numKeep)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let srLog = WSyslog(o)
|
|
25
34
|
|
|
26
35
|
return srLog
|
|
27
36
|
}
|
|
@@ -1,131 +1,19 @@
|
|
|
1
|
-
import
|
|
2
|
-
import get from 'lodash-es/get.js'
|
|
3
|
-
import size from 'lodash-es/size.js'
|
|
4
|
-
import groupBy from 'lodash-es/groupBy.js'
|
|
5
|
-
import each from 'lodash-es/each.js'
|
|
6
|
-
import mapValues from 'lodash-es/mapValues.js'
|
|
7
|
-
import merge from 'lodash-es/merge.js'
|
|
8
|
-
import genPm from 'wsemi/src/genPm.mjs'
|
|
9
|
-
import isestr from 'wsemi/src/isestr.mjs'
|
|
10
|
-
import fsTreeFolder from 'wsemi/src/fsTreeFolder.mjs'
|
|
11
|
-
import fsBuildReadStreamText from 'wsemi/src/fsBuildReadStreamText.mjs'
|
|
12
|
-
import filterVpfsByWindow from './filterVpfsByWindow.mjs'
|
|
1
|
+
import { staLogs } from './staLogsCore.callWorker.mjs'
|
|
13
2
|
|
|
14
3
|
|
|
15
|
-
//staEvent
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
//回傳格式(時間遞增、無資料時段補 0):
|
|
4
|
+
//staEvent(通用事件頻率統計):掃 fdLog 下所有 NDJSON log 檔,依時間 bucket(hr/day)+ 各 event 名聚合次數。
|
|
5
|
+
//自 ADR-028 起由 staLogsCore 提供:單趟掃描 + 檔級彙總快取(封閉小時檔只掃一次)+ single-flight,掃描交單一 worker(主執行緒不阻塞)。
|
|
6
|
+
//簽章與輸出格式不變(時間遞增、無資料時段補 { count: 0 }):
|
|
20
7
|
// [
|
|
21
8
|
// { time: '2026-06-22T10', data: { count: 6, 'verifyConn': 3, 'kpfun-getApisList': 2, 'kpfun-saveApi': 1 } },
|
|
22
9
|
// { time: '2026-06-22T11', data: { count: 0 } },
|
|
23
10
|
// ...
|
|
24
11
|
// ]
|
|
25
|
-
|
|
26
|
-
//參數:
|
|
27
|
-
// timeLength 往回統計天數(預設 7)
|
|
28
|
-
// timeInterval 時間粒度 'hr'(每小時,預設)/ 'day'(每日)
|
|
29
|
-
// opt.fdLog log 資料夾(預設 './logs',須與 srLog 寫入之 logFd 一致;e2e 可指向合成 log 達 hermetic)
|
|
12
|
+
//參數:timeLength 往回統計天數(預設 7);timeInterval 'hr'(預設)/ 'day';opt.fdLog log 資料夾(預設 './logs');opt.srLog(單檔略過時記 warn)
|
|
30
13
|
async function staEvent(timeLength = 7, timeInterval = 'hr', opt = {}) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
let fdLog = get(opt, 'fdLog')
|
|
34
|
-
if (!isestr(fdLog)) {
|
|
35
|
-
fdLog = './logs'
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
//fmt
|
|
39
|
-
let fmt = timeInterval === 'day' ? 'YYYY-MM-DD' : 'YYYY-MM-DDTHH'
|
|
40
|
-
|
|
41
|
-
//unit
|
|
42
|
-
let unit = timeInterval === 'hr' ? 'hour' : 'day'
|
|
43
|
-
|
|
44
|
-
//kpTime,產生完整的時間區間(無資料時段亦補 { count: 0 },圖表 x 軸不留空洞)
|
|
45
|
-
let now = ot()
|
|
46
|
-
let tStart = now.subtract(timeLength, 'day')
|
|
47
|
-
let tCurr = tStart.startOf(unit)
|
|
48
|
-
let tEnd = now.startOf(unit)
|
|
49
|
-
let kpTime = {}
|
|
50
|
-
while (!tCurr.isAfter(tEnd)) {
|
|
51
|
-
kpTime[tCurr.format(fmt)] = { data: { count: 0 } }
|
|
52
|
-
tCurr = tCurr.add(1, unit)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
//vpfs
|
|
56
|
-
let vpfs = fsTreeFolder(fdLog)
|
|
57
|
-
vpfs = filterVpfsByWindow(vpfs, tStart, fmt) //開檔前剔除窗外檔, 見該模組註解
|
|
58
|
-
|
|
59
|
-
//logs(逐檔逐行 stream parse,挑出 tStart 之後、且帶 event 字串者)
|
|
60
|
-
let logs = []
|
|
61
|
-
for (let vpf of vpfs) {
|
|
62
|
-
|
|
63
|
-
//fp
|
|
64
|
-
let fp = vpf.path
|
|
65
|
-
|
|
66
|
-
//ev
|
|
67
|
-
let ev = fsBuildReadStreamText(fp)
|
|
68
|
-
|
|
69
|
-
//line
|
|
70
|
-
ev.on('line', (line) => {
|
|
71
|
-
let v = null
|
|
72
|
-
try {
|
|
73
|
-
v = JSON.parse(line)
|
|
74
|
-
let t = ot(v.time)
|
|
75
|
-
let timeFmt = t.format(fmt)
|
|
76
|
-
let b1 = t.isAfter(tStart)
|
|
77
|
-
let b2 = isestr(v.event) //通用:任何帶 event 字串之 log 皆納入(非鎖定單一 event)
|
|
78
|
-
let b = b1 && b2
|
|
79
|
-
if (b) {
|
|
80
|
-
logs.push({
|
|
81
|
-
timeFmt,
|
|
82
|
-
...v,
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
catch (err) {} //略過非 JSON / 壞行
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
//await close
|
|
90
|
-
let pm = genPm()
|
|
91
|
-
ev.on('close', () => {
|
|
92
|
-
pm.resolve()
|
|
93
|
-
})
|
|
94
|
-
await pm
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
//gs,依時間 bucket 分組
|
|
99
|
-
let gs = groupBy(logs, 'timeFmt')
|
|
100
|
-
|
|
101
|
-
//gsLog,各 bucket 內依 event 名計數 + 總數 count
|
|
102
|
-
let gsLog = mapValues(gs, vs => {
|
|
103
|
-
let count = size(vs)
|
|
104
|
-
let gev = groupBy(vs, 'event')
|
|
105
|
-
let kp = {}
|
|
106
|
-
each(gev, (vvs, event) => {
|
|
107
|
-
kp[event] = size(vvs)
|
|
108
|
-
})
|
|
109
|
-
return {
|
|
110
|
-
data: {
|
|
111
|
-
count,
|
|
112
|
-
...kp,
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
//merge(補零時段 + 實際資料)
|
|
118
|
-
let mr = merge({}, kpTime, gsLog)
|
|
119
|
-
|
|
120
|
-
//rs(時間遞增排序輸出)
|
|
121
|
-
let rs = Object.keys(mr)
|
|
122
|
-
.sort()
|
|
123
|
-
.map(time => ({
|
|
124
|
-
time,
|
|
125
|
-
...mr[time]
|
|
126
|
-
}))
|
|
127
|
-
|
|
128
|
-
return rs
|
|
14
|
+
let r = await staLogs(timeLength, timeInterval, opt)
|
|
15
|
+
return r.rs
|
|
129
16
|
}
|
|
130
17
|
|
|
18
|
+
|
|
131
19
|
export default staEvent
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { fileURLToPath } from 'url'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { Worker } from 'worker_threads'
|
|
4
|
+
import genPm from 'wsemi/src/genPm.mjs'
|
|
5
|
+
import { staLogs as staLogsCore } from './staLogsCore.mjs'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
let __filename = fileURLToPath(import.meta.url)
|
|
9
|
+
let __dirname = path.dirname(__filename)
|
|
10
|
+
|
|
11
|
+
let fpWkDefault = path.resolve(__dirname, 'staLogsCore.shellWorker.mjs')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
//runWorker: 以單一 worker 執行掃描, 回傳 worker 之 payload; fpWk 可注入 (測試用)
|
|
15
|
+
// 終止路徑一律走 settle(): message(done/error) / error / exit 三者任一先到者定案, 其餘為 no-op, 且皆 terminate 回收
|
|
16
|
+
// exit 為必要: worker 未送任何訊息即結束 (如 worker 內 process.exit) 時若無 exit 監聽, promise 永不落定,
|
|
17
|
+
// 又因 staLogsCore 之 single-flight 以 finally 釋放 in-flight key, 該 key 會永久卡死 (後續同參數呼叫皆取得死 promise, 需重啟後端)
|
|
18
|
+
function runWorker(param, fpWk = fpWkDefault) {
|
|
19
|
+
let pm = genPm()
|
|
20
|
+
|
|
21
|
+
//wk
|
|
22
|
+
let wk = new Worker(fpWk)
|
|
23
|
+
|
|
24
|
+
//settle
|
|
25
|
+
let settled = false
|
|
26
|
+
let settle = (fn, v) => {
|
|
27
|
+
if (!settled) {
|
|
28
|
+
settled = true
|
|
29
|
+
fn(v)
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
wk.terminate()
|
|
33
|
+
}
|
|
34
|
+
catch (err) {}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
wk.on('message', (msg) => {
|
|
38
|
+
if (msg.mode === 'done') {
|
|
39
|
+
settle(pm.resolve, msg.payload)
|
|
40
|
+
}
|
|
41
|
+
else if (msg.mode === 'error') {
|
|
42
|
+
settle(pm.reject, msg.payload)
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
wk.on('error', (err) => {
|
|
47
|
+
settle(pm.reject, err)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
wk.on('exit', (code) => {
|
|
51
|
+
settle(pm.reject, new Error(`staLogs worker exited without result, code=${code}`))
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
wk.postMessage(param)
|
|
55
|
+
|
|
56
|
+
return pm
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
//scanFilesByWorker: 把「需重掃之檔案清單」交給單一 worker 掃描 (主執行緒不阻塞), 回傳每檔彙總
|
|
61
|
+
async function scanFilesByWorker(files, tStartMs, fmt) {
|
|
62
|
+
|
|
63
|
+
//無需重掃時不 spawn
|
|
64
|
+
if (files.length === 0) {
|
|
65
|
+
return []
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return runWorker({ files, tStartMs, fmt })
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
function staLogs(timeLength = 7, timeInterval = 'hr', opt = {}) {
|
|
73
|
+
return staLogsCore(timeLength, timeInterval, { ...opt, scanFiles: scanFilesByWorker })
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
export { staLogs, scanFilesByWorker, runWorker }
|
|
78
|
+
export default staLogs
|