@lark-apaas/devtool-kits 1.2.11-alpha.1 → 1.2.11-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +527 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +542 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1465,7 +1465,7 @@ function sendSimpleRedirect(req, res) {
|
|
|
1465
1465
|
__name(sendSimpleRedirect, "sendSimpleRedirect");
|
|
1466
1466
|
|
|
1467
1467
|
// src/middlewares/index.ts
|
|
1468
|
-
import
|
|
1468
|
+
import path7 from "path";
|
|
1469
1469
|
|
|
1470
1470
|
// src/middlewares/openapi/router.ts
|
|
1471
1471
|
import express from "express";
|
|
@@ -1809,8 +1809,8 @@ function hasSpecialPatterns(pattern) {
|
|
|
1809
1809
|
return /[{*]/.test(pattern);
|
|
1810
1810
|
}
|
|
1811
1811
|
__name(hasSpecialPatterns, "hasSpecialPatterns");
|
|
1812
|
-
function normalizePathForMatching(
|
|
1813
|
-
return
|
|
1812
|
+
function normalizePathForMatching(path8) {
|
|
1813
|
+
return path8.replace(/\/+/g, "/").replace(/\/+$/, "");
|
|
1814
1814
|
}
|
|
1815
1815
|
__name(normalizePathForMatching, "normalizePathForMatching");
|
|
1816
1816
|
|
|
@@ -2357,7 +2357,7 @@ __name(readLogsBySource, "readLogsBySource");
|
|
|
2357
2357
|
// src/middlewares/dev-logs/services/trigger.service.ts
|
|
2358
2358
|
import { createReadStream as createReadStream3 } from "fs";
|
|
2359
2359
|
import { createInterface as createInterface3 } from "readline";
|
|
2360
|
-
async function readTriggerList(filePath, trigger,
|
|
2360
|
+
async function readTriggerList(filePath, trigger, path8, limit, triggerID) {
|
|
2361
2361
|
if (!await fileExists(filePath)) {
|
|
2362
2362
|
return void 0;
|
|
2363
2363
|
}
|
|
@@ -2383,7 +2383,7 @@ async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
|
|
|
2383
2383
|
if (alreadyAdded) {
|
|
2384
2384
|
return false;
|
|
2385
2385
|
}
|
|
2386
|
-
const isAutomationTrigger = builder.path?.endsWith(
|
|
2386
|
+
const isAutomationTrigger = builder.path?.endsWith(path8);
|
|
2387
2387
|
if (!isAutomationTrigger) {
|
|
2388
2388
|
return false;
|
|
2389
2389
|
}
|
|
@@ -2466,7 +2466,7 @@ async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
|
|
|
2466
2466
|
};
|
|
2467
2467
|
}
|
|
2468
2468
|
__name(readTriggerList, "readTriggerList");
|
|
2469
|
-
async function readTriggerDetail(filePath,
|
|
2469
|
+
async function readTriggerDetail(filePath, path8, instanceID) {
|
|
2470
2470
|
const exists = await fileExists(filePath);
|
|
2471
2471
|
if (!exists) {
|
|
2472
2472
|
return void 0;
|
|
@@ -2482,7 +2482,7 @@ async function readTriggerDetail(filePath, path7, instanceID) {
|
|
|
2482
2482
|
for await (const line of rl) {
|
|
2483
2483
|
const entry = parseLogLine2(line);
|
|
2484
2484
|
if (!entry) continue;
|
|
2485
|
-
const isAutomationTrigger = entry.path?.endsWith(
|
|
2485
|
+
const isAutomationTrigger = entry.path?.endsWith(path8);
|
|
2486
2486
|
const hasInstanceID = entry.instance_id === instanceID && entry.trigger;
|
|
2487
2487
|
if (!isAutomationTrigger || !hasInstanceID) continue;
|
|
2488
2488
|
matches.push(entry);
|
|
@@ -2736,16 +2736,16 @@ function createGetTriggerListHandler(logDir) {
|
|
|
2736
2736
|
});
|
|
2737
2737
|
}
|
|
2738
2738
|
const triggerID = typeof req.query.triggerID === "string" ? req.query.triggerID.trim() : void 0;
|
|
2739
|
-
const
|
|
2739
|
+
const path8 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
|
|
2740
2740
|
const limit = parseLimit(req.query.limit, 10, 200);
|
|
2741
2741
|
try {
|
|
2742
|
-
const result = await readTriggerList(traceLogPath, trigger,
|
|
2742
|
+
const result = await readTriggerList(traceLogPath, trigger, path8, limit, triggerID);
|
|
2743
2743
|
if (!result) {
|
|
2744
2744
|
return handleNotFound(res, traceLogPath);
|
|
2745
2745
|
}
|
|
2746
2746
|
res.json({
|
|
2747
2747
|
file: getRelativePath(traceLogPath),
|
|
2748
|
-
path:
|
|
2748
|
+
path: path8,
|
|
2749
2749
|
...result
|
|
2750
2750
|
});
|
|
2751
2751
|
} catch (error) {
|
|
@@ -2763,9 +2763,9 @@ function createGetTriggerDetailHandler(logDir) {
|
|
|
2763
2763
|
message: "instanceID is required"
|
|
2764
2764
|
});
|
|
2765
2765
|
}
|
|
2766
|
-
const
|
|
2766
|
+
const path8 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
|
|
2767
2767
|
try {
|
|
2768
|
-
const result = await readTriggerDetail(traceLogPath,
|
|
2768
|
+
const result = await readTriggerDetail(traceLogPath, path8, instanceID);
|
|
2769
2769
|
if (!result) {
|
|
2770
2770
|
return handleNotFound(res, traceLogPath);
|
|
2771
2771
|
}
|
|
@@ -2872,6 +2872,521 @@ function createHealthCheckHandler(options = {}) {
|
|
|
2872
2872
|
}
|
|
2873
2873
|
__name(createHealthCheckHandler, "createHealthCheckHandler");
|
|
2874
2874
|
|
|
2875
|
+
// src/middlewares/dev-logs/sse/log-watcher.ts
|
|
2876
|
+
import * as fs9 from "fs";
|
|
2877
|
+
import * as path6 from "path";
|
|
2878
|
+
function mapPinoLevelToServerLogLevel2(pinoLevel) {
|
|
2879
|
+
if (typeof pinoLevel === "string") {
|
|
2880
|
+
const lower = pinoLevel.toLowerCase();
|
|
2881
|
+
if (lower === "fatal") return "fatal";
|
|
2882
|
+
if (lower === "error") return "error";
|
|
2883
|
+
if (lower === "warn" || lower === "warning") return "warn";
|
|
2884
|
+
if (lower === "info" || lower === "log") return "log";
|
|
2885
|
+
if (lower === "debug") return "debug";
|
|
2886
|
+
if (lower === "trace" || lower === "verbose") return "verbose";
|
|
2887
|
+
return "log";
|
|
2888
|
+
}
|
|
2889
|
+
if (pinoLevel >= 60) return "fatal";
|
|
2890
|
+
if (pinoLevel >= 50) return "error";
|
|
2891
|
+
if (pinoLevel >= 40) return "warn";
|
|
2892
|
+
if (pinoLevel >= 30) return "log";
|
|
2893
|
+
if (pinoLevel >= 20) return "debug";
|
|
2894
|
+
return "verbose";
|
|
2895
|
+
}
|
|
2896
|
+
__name(mapPinoLevelToServerLogLevel2, "mapPinoLevelToServerLogLevel");
|
|
2897
|
+
function extractLogLevel2(text) {
|
|
2898
|
+
const lower = text.toLowerCase();
|
|
2899
|
+
if (lower.includes("fatal") || lower.includes("critical")) return "fatal";
|
|
2900
|
+
if (lower.includes("error") || lower.includes("<e>") || lower.includes("\u2716")) return "error";
|
|
2901
|
+
if (lower.includes("warn") || lower.includes("warning") || lower.includes("<w>") || lower.includes("\u26A0")) return "warn";
|
|
2902
|
+
if (lower.includes("debug") || lower.includes("<d>")) return "debug";
|
|
2903
|
+
if (lower.includes("verbose") || lower.includes("trace")) return "verbose";
|
|
2904
|
+
return "log";
|
|
2905
|
+
}
|
|
2906
|
+
__name(extractLogLevel2, "extractLogLevel");
|
|
2907
|
+
function generateUUID2() {
|
|
2908
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
2909
|
+
const r = Math.random() * 16 | 0;
|
|
2910
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
2911
|
+
return v.toString(16);
|
|
2912
|
+
});
|
|
2913
|
+
}
|
|
2914
|
+
__name(generateUUID2, "generateUUID");
|
|
2915
|
+
function parsePinoLog2(line, source) {
|
|
2916
|
+
try {
|
|
2917
|
+
const pinoLog = JSON.parse(line);
|
|
2918
|
+
const id = generateUUID2();
|
|
2919
|
+
return {
|
|
2920
|
+
id,
|
|
2921
|
+
level: mapPinoLevelToServerLogLevel2(pinoLog.level),
|
|
2922
|
+
timestamp: new Date(pinoLog.time).getTime(),
|
|
2923
|
+
message: pinoLog.message || pinoLog.msg || "",
|
|
2924
|
+
context: pinoLog.context || null,
|
|
2925
|
+
traceId: pinoLog.trace_id || null,
|
|
2926
|
+
userId: pinoLog.user_id || null,
|
|
2927
|
+
appId: pinoLog.app_id || null,
|
|
2928
|
+
tenantId: pinoLog.tenant_id || null,
|
|
2929
|
+
stack: pinoLog.stack || null,
|
|
2930
|
+
meta: {
|
|
2931
|
+
pid: pinoLog.pid,
|
|
2932
|
+
hostname: pinoLog.hostname,
|
|
2933
|
+
path: pinoLog.path,
|
|
2934
|
+
method: pinoLog.method,
|
|
2935
|
+
statusCode: pinoLog.status_code,
|
|
2936
|
+
durationMs: pinoLog.duration_ms,
|
|
2937
|
+
ip: pinoLog.ip,
|
|
2938
|
+
requestBody: pinoLog.request_body,
|
|
2939
|
+
responseBody: pinoLog.response_body
|
|
2940
|
+
},
|
|
2941
|
+
tags: [
|
|
2942
|
+
source
|
|
2943
|
+
]
|
|
2944
|
+
};
|
|
2945
|
+
} catch {
|
|
2946
|
+
return null;
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
__name(parsePinoLog2, "parsePinoLog");
|
|
2950
|
+
function parseStdLog2(line, source) {
|
|
2951
|
+
const id = generateUUID2();
|
|
2952
|
+
const match = line.match(/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(server|client)\] (.*)$/);
|
|
2953
|
+
if (!match) {
|
|
2954
|
+
return {
|
|
2955
|
+
id,
|
|
2956
|
+
level: extractLogLevel2(line),
|
|
2957
|
+
timestamp: Date.now(),
|
|
2958
|
+
message: line,
|
|
2959
|
+
context: null,
|
|
2960
|
+
traceId: null,
|
|
2961
|
+
userId: null,
|
|
2962
|
+
appId: null,
|
|
2963
|
+
tenantId: null,
|
|
2964
|
+
stack: null,
|
|
2965
|
+
meta: null,
|
|
2966
|
+
tags: [
|
|
2967
|
+
source
|
|
2968
|
+
]
|
|
2969
|
+
};
|
|
2970
|
+
}
|
|
2971
|
+
const [, timeStr, , content] = match;
|
|
2972
|
+
let timestamp;
|
|
2973
|
+
try {
|
|
2974
|
+
const isoStr = timeStr.replace(" ", "T");
|
|
2975
|
+
timestamp = new Date(isoStr).getTime();
|
|
2976
|
+
if (isNaN(timestamp)) {
|
|
2977
|
+
timestamp = Date.now();
|
|
2978
|
+
}
|
|
2979
|
+
} catch {
|
|
2980
|
+
timestamp = Date.now();
|
|
2981
|
+
}
|
|
2982
|
+
return {
|
|
2983
|
+
id,
|
|
2984
|
+
level: extractLogLevel2(content),
|
|
2985
|
+
timestamp,
|
|
2986
|
+
message: content,
|
|
2987
|
+
context: null,
|
|
2988
|
+
traceId: null,
|
|
2989
|
+
userId: null,
|
|
2990
|
+
appId: null,
|
|
2991
|
+
tenantId: null,
|
|
2992
|
+
stack: null,
|
|
2993
|
+
meta: null,
|
|
2994
|
+
tags: [
|
|
2995
|
+
source
|
|
2996
|
+
]
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
__name(parseStdLog2, "parseStdLog");
|
|
3000
|
+
var LogWatcher = class {
|
|
3001
|
+
static {
|
|
3002
|
+
__name(this, "LogWatcher");
|
|
3003
|
+
}
|
|
3004
|
+
logDir = "";
|
|
3005
|
+
watchers = /* @__PURE__ */ new Map();
|
|
3006
|
+
filePositions = /* @__PURE__ */ new Map();
|
|
3007
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
3008
|
+
isRunning = false;
|
|
3009
|
+
debug = false;
|
|
3010
|
+
logFiles = [
|
|
3011
|
+
{
|
|
3012
|
+
fileName: "server.log",
|
|
3013
|
+
source: "server",
|
|
3014
|
+
parser: parsePinoLog2
|
|
3015
|
+
},
|
|
3016
|
+
{
|
|
3017
|
+
fileName: "trace.log",
|
|
3018
|
+
source: "trace",
|
|
3019
|
+
parser: parsePinoLog2
|
|
3020
|
+
},
|
|
3021
|
+
{
|
|
3022
|
+
fileName: "server.std.log",
|
|
3023
|
+
source: "server-std",
|
|
3024
|
+
parser: parseStdLog2
|
|
3025
|
+
},
|
|
3026
|
+
{
|
|
3027
|
+
fileName: "client.std.log",
|
|
3028
|
+
source: "client-std",
|
|
3029
|
+
parser: parseStdLog2
|
|
3030
|
+
}
|
|
3031
|
+
];
|
|
3032
|
+
constructor(options = {}) {
|
|
3033
|
+
this.debug = options.debug ?? false;
|
|
3034
|
+
}
|
|
3035
|
+
log(...args) {
|
|
3036
|
+
if (this.debug) {
|
|
3037
|
+
console.log("[LogWatcher]", ...args);
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
/**
|
|
3041
|
+
* Start watching log files
|
|
3042
|
+
*/
|
|
3043
|
+
start(logDir) {
|
|
3044
|
+
if (this.isRunning) {
|
|
3045
|
+
this.log("Already running, ignoring start call");
|
|
3046
|
+
return;
|
|
3047
|
+
}
|
|
3048
|
+
this.logDir = logDir;
|
|
3049
|
+
this.isRunning = true;
|
|
3050
|
+
this.log(`Starting to watch log files in: ${logDir}`);
|
|
3051
|
+
for (const config of this.logFiles) {
|
|
3052
|
+
this.watchFile(config);
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
/**
|
|
3056
|
+
* Stop watching all files
|
|
3057
|
+
*/
|
|
3058
|
+
stop() {
|
|
3059
|
+
if (!this.isRunning) {
|
|
3060
|
+
return;
|
|
3061
|
+
}
|
|
3062
|
+
this.log("Stopping file watchers");
|
|
3063
|
+
this.isRunning = false;
|
|
3064
|
+
Array.from(this.watchers.entries()).forEach(([fileName, watcher]) => {
|
|
3065
|
+
watcher.close();
|
|
3066
|
+
this.log(`Closed watcher for: ${fileName}`);
|
|
3067
|
+
});
|
|
3068
|
+
this.watchers.clear();
|
|
3069
|
+
this.filePositions.clear();
|
|
3070
|
+
}
|
|
3071
|
+
/**
|
|
3072
|
+
* Subscribe to new log events
|
|
3073
|
+
*/
|
|
3074
|
+
onLog(callback) {
|
|
3075
|
+
this.subscribers.add(callback);
|
|
3076
|
+
this.log(`Subscriber added, total: ${this.subscribers.size}`);
|
|
3077
|
+
return () => {
|
|
3078
|
+
this.subscribers.delete(callback);
|
|
3079
|
+
this.log(`Subscriber removed, total: ${this.subscribers.size}`);
|
|
3080
|
+
};
|
|
3081
|
+
}
|
|
3082
|
+
/**
|
|
3083
|
+
* Get subscriber count
|
|
3084
|
+
*/
|
|
3085
|
+
getSubscriberCount() {
|
|
3086
|
+
return this.subscribers.size;
|
|
3087
|
+
}
|
|
3088
|
+
/**
|
|
3089
|
+
* Watch a single log file
|
|
3090
|
+
*/
|
|
3091
|
+
watchFile(config) {
|
|
3092
|
+
const filePath = path6.join(this.logDir, config.fileName);
|
|
3093
|
+
if (!fs9.existsSync(filePath)) {
|
|
3094
|
+
this.log(`File not found, skipping: ${config.fileName}`);
|
|
3095
|
+
return;
|
|
3096
|
+
}
|
|
3097
|
+
try {
|
|
3098
|
+
const stats = fs9.statSync(filePath);
|
|
3099
|
+
this.filePositions.set(config.fileName, stats.size);
|
|
3100
|
+
this.log(`Initialized position for ${config.fileName}: ${stats.size} bytes`);
|
|
3101
|
+
} catch (error) {
|
|
3102
|
+
this.log(`Failed to get initial position for ${config.fileName}:`, error);
|
|
3103
|
+
this.filePositions.set(config.fileName, 0);
|
|
3104
|
+
}
|
|
3105
|
+
try {
|
|
3106
|
+
const watcher = fs9.watch(filePath, (eventType) => {
|
|
3107
|
+
if (eventType === "change") {
|
|
3108
|
+
this.handleFileChange(config);
|
|
3109
|
+
}
|
|
3110
|
+
});
|
|
3111
|
+
watcher.on("error", (error) => {
|
|
3112
|
+
this.log(`Watcher error for ${config.fileName}:`, error);
|
|
3113
|
+
this.restartWatcher(config);
|
|
3114
|
+
});
|
|
3115
|
+
this.watchers.set(config.fileName, watcher);
|
|
3116
|
+
this.log(`Started watching: ${config.fileName}`);
|
|
3117
|
+
} catch (error) {
|
|
3118
|
+
this.log(`Failed to start watcher for ${config.fileName}:`, error);
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
/**
|
|
3122
|
+
* Restart a file watcher after error
|
|
3123
|
+
*/
|
|
3124
|
+
restartWatcher(config) {
|
|
3125
|
+
const existingWatcher = this.watchers.get(config.fileName);
|
|
3126
|
+
if (existingWatcher) {
|
|
3127
|
+
existingWatcher.close();
|
|
3128
|
+
this.watchers.delete(config.fileName);
|
|
3129
|
+
}
|
|
3130
|
+
setTimeout(() => {
|
|
3131
|
+
if (this.isRunning) {
|
|
3132
|
+
this.log(`Restarting watcher for: ${config.fileName}`);
|
|
3133
|
+
this.watchFile(config);
|
|
3134
|
+
}
|
|
3135
|
+
}, 1e3);
|
|
3136
|
+
}
|
|
3137
|
+
/**
|
|
3138
|
+
* Handle file change event - read new content
|
|
3139
|
+
*/
|
|
3140
|
+
handleFileChange(config) {
|
|
3141
|
+
const filePath = path6.join(this.logDir, config.fileName);
|
|
3142
|
+
const lastPosition = this.filePositions.get(config.fileName) || 0;
|
|
3143
|
+
try {
|
|
3144
|
+
const stats = fs9.statSync(filePath);
|
|
3145
|
+
const currentSize = stats.size;
|
|
3146
|
+
if (currentSize < lastPosition) {
|
|
3147
|
+
this.log(`File ${config.fileName} was truncated, resetting position`);
|
|
3148
|
+
this.filePositions.set(config.fileName, 0);
|
|
3149
|
+
this.handleFileChange(config);
|
|
3150
|
+
return;
|
|
3151
|
+
}
|
|
3152
|
+
if (currentSize === lastPosition) {
|
|
3153
|
+
return;
|
|
3154
|
+
}
|
|
3155
|
+
const readSize = currentSize - lastPosition;
|
|
3156
|
+
const buffer = Buffer.alloc(readSize);
|
|
3157
|
+
const fd = fs9.openSync(filePath, "r");
|
|
3158
|
+
try {
|
|
3159
|
+
fs9.readSync(fd, buffer, 0, readSize, lastPosition);
|
|
3160
|
+
} finally {
|
|
3161
|
+
fs9.closeSync(fd);
|
|
3162
|
+
}
|
|
3163
|
+
this.filePositions.set(config.fileName, currentSize);
|
|
3164
|
+
const content = buffer.toString("utf8");
|
|
3165
|
+
const lines = content.split("\n");
|
|
3166
|
+
for (const line of lines) {
|
|
3167
|
+
if (!line.trim()) continue;
|
|
3168
|
+
try {
|
|
3169
|
+
const log = config.parser(line, config.source);
|
|
3170
|
+
if (log) {
|
|
3171
|
+
this.notifySubscribers(log);
|
|
3172
|
+
}
|
|
3173
|
+
} catch {
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
} catch (error) {
|
|
3177
|
+
this.log(`Error reading file ${config.fileName}:`, error);
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
/**
|
|
3181
|
+
* Notify all subscribers of new log
|
|
3182
|
+
*/
|
|
3183
|
+
notifySubscribers(log) {
|
|
3184
|
+
Array.from(this.subscribers).forEach((subscriber) => {
|
|
3185
|
+
try {
|
|
3186
|
+
subscriber(log);
|
|
3187
|
+
} catch (error) {
|
|
3188
|
+
this.log("Subscriber error:", error);
|
|
3189
|
+
}
|
|
3190
|
+
});
|
|
3191
|
+
}
|
|
3192
|
+
};
|
|
3193
|
+
|
|
3194
|
+
// src/middlewares/dev-logs/sse/client-manager.ts
|
|
3195
|
+
var ClientManager = class {
|
|
3196
|
+
static {
|
|
3197
|
+
__name(this, "ClientManager");
|
|
3198
|
+
}
|
|
3199
|
+
clients = /* @__PURE__ */ new Map();
|
|
3200
|
+
debug = false;
|
|
3201
|
+
constructor(options = {}) {
|
|
3202
|
+
this.debug = options.debug ?? false;
|
|
3203
|
+
}
|
|
3204
|
+
log(...args) {
|
|
3205
|
+
if (this.debug) {
|
|
3206
|
+
console.log("[ClientManager]", ...args);
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
/**
|
|
3210
|
+
* Generate a unique client ID
|
|
3211
|
+
*/
|
|
3212
|
+
generateClientId() {
|
|
3213
|
+
return `client_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
3214
|
+
}
|
|
3215
|
+
/**
|
|
3216
|
+
* Add a new client connection
|
|
3217
|
+
*/
|
|
3218
|
+
addClient(id, res) {
|
|
3219
|
+
this.clients.set(id, {
|
|
3220
|
+
id,
|
|
3221
|
+
res,
|
|
3222
|
+
connectedAt: Date.now()
|
|
3223
|
+
});
|
|
3224
|
+
this.log(`Client connected: ${id}, total clients: ${this.clients.size}`);
|
|
3225
|
+
}
|
|
3226
|
+
/**
|
|
3227
|
+
* Remove a client connection
|
|
3228
|
+
*/
|
|
3229
|
+
removeClient(id) {
|
|
3230
|
+
if (this.clients.has(id)) {
|
|
3231
|
+
this.clients.delete(id);
|
|
3232
|
+
this.log(`Client disconnected: ${id}, total clients: ${this.clients.size}`);
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
/**
|
|
3236
|
+
* Get current client count
|
|
3237
|
+
*/
|
|
3238
|
+
getClientCount() {
|
|
3239
|
+
return this.clients.size;
|
|
3240
|
+
}
|
|
3241
|
+
/**
|
|
3242
|
+
* Check if a client exists
|
|
3243
|
+
*/
|
|
3244
|
+
hasClient(id) {
|
|
3245
|
+
return this.clients.has(id);
|
|
3246
|
+
}
|
|
3247
|
+
/**
|
|
3248
|
+
* Send SSE event to a specific client
|
|
3249
|
+
*/
|
|
3250
|
+
sendToClient(clientId, event, data) {
|
|
3251
|
+
const client = this.clients.get(clientId);
|
|
3252
|
+
if (!client) {
|
|
3253
|
+
return false;
|
|
3254
|
+
}
|
|
3255
|
+
try {
|
|
3256
|
+
const message = this.formatSSEMessage(event, data);
|
|
3257
|
+
client.res.write(message);
|
|
3258
|
+
return true;
|
|
3259
|
+
} catch (error) {
|
|
3260
|
+
this.log(`Failed to send to client ${clientId}:`, error);
|
|
3261
|
+
this.removeClient(clientId);
|
|
3262
|
+
return false;
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
/**
|
|
3266
|
+
* Broadcast SSE event to all clients
|
|
3267
|
+
*/
|
|
3268
|
+
broadcast(event, data) {
|
|
3269
|
+
const message = this.formatSSEMessage(event, data);
|
|
3270
|
+
const failedClients = [];
|
|
3271
|
+
Array.from(this.clients.entries()).forEach(([id, client]) => {
|
|
3272
|
+
try {
|
|
3273
|
+
client.res.write(message);
|
|
3274
|
+
} catch (error) {
|
|
3275
|
+
this.log(`Broadcast failed for client ${id}:`, error);
|
|
3276
|
+
failedClients.push(id);
|
|
3277
|
+
}
|
|
3278
|
+
});
|
|
3279
|
+
failedClients.forEach((id) => {
|
|
3280
|
+
this.removeClient(id);
|
|
3281
|
+
});
|
|
3282
|
+
}
|
|
3283
|
+
/**
|
|
3284
|
+
* Format SSE message
|
|
3285
|
+
* SSE format: event: <event>\ndata: <json>\n\n
|
|
3286
|
+
*/
|
|
3287
|
+
formatSSEMessage(event, data) {
|
|
3288
|
+
const jsonData = JSON.stringify(data);
|
|
3289
|
+
return `event: ${event}
|
|
3290
|
+
data: ${jsonData}
|
|
3291
|
+
|
|
3292
|
+
`;
|
|
3293
|
+
}
|
|
3294
|
+
/**
|
|
3295
|
+
* Get all client IDs
|
|
3296
|
+
*/
|
|
3297
|
+
getClientIds() {
|
|
3298
|
+
return Array.from(this.clients.keys());
|
|
3299
|
+
}
|
|
3300
|
+
/**
|
|
3301
|
+
* Get client info
|
|
3302
|
+
*/
|
|
3303
|
+
getClientInfo(id) {
|
|
3304
|
+
const client = this.clients.get(id);
|
|
3305
|
+
if (!client) {
|
|
3306
|
+
return null;
|
|
3307
|
+
}
|
|
3308
|
+
return {
|
|
3309
|
+
id: client.id,
|
|
3310
|
+
connectedAt: client.connectedAt
|
|
3311
|
+
};
|
|
3312
|
+
}
|
|
3313
|
+
/**
|
|
3314
|
+
* Close all client connections
|
|
3315
|
+
*/
|
|
3316
|
+
closeAll() {
|
|
3317
|
+
this.log(`Closing all ${this.clients.size} clients`);
|
|
3318
|
+
Array.from(this.clients.entries()).forEach(([id, client]) => {
|
|
3319
|
+
try {
|
|
3320
|
+
client.res.end();
|
|
3321
|
+
} catch (error) {
|
|
3322
|
+
this.log(`Error closing client ${id}:`, error);
|
|
3323
|
+
}
|
|
3324
|
+
});
|
|
3325
|
+
this.clients.clear();
|
|
3326
|
+
}
|
|
3327
|
+
};
|
|
3328
|
+
|
|
3329
|
+
// src/middlewares/dev-logs/sse/sse.controller.ts
|
|
3330
|
+
function createSSEHandler(logDir, options = {}) {
|
|
3331
|
+
const { debug = false, heartbeatInterval = 3e4 } = options;
|
|
3332
|
+
const logWatcher = new LogWatcher({
|
|
3333
|
+
debug
|
|
3334
|
+
});
|
|
3335
|
+
const clientManager = new ClientManager({
|
|
3336
|
+
debug
|
|
3337
|
+
});
|
|
3338
|
+
const log = /* @__PURE__ */ __name((...args) => {
|
|
3339
|
+
if (debug) {
|
|
3340
|
+
console.log("[SSEHandler]", ...args);
|
|
3341
|
+
}
|
|
3342
|
+
}, "log");
|
|
3343
|
+
return (req, res) => {
|
|
3344
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
3345
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
3346
|
+
res.setHeader("Connection", "keep-alive");
|
|
3347
|
+
res.setHeader("X-Accel-Buffering", "no");
|
|
3348
|
+
res.setTimeout(0);
|
|
3349
|
+
const clientId = clientManager.generateClientId();
|
|
3350
|
+
clientManager.addClient(clientId, res);
|
|
3351
|
+
log(`New SSE connection: ${clientId}`);
|
|
3352
|
+
if (clientManager.getClientCount() === 1) {
|
|
3353
|
+
log("First client connected, starting log watcher");
|
|
3354
|
+
logWatcher.start(logDir);
|
|
3355
|
+
}
|
|
3356
|
+
const unsubscribe = logWatcher.onLog((logEntry) => {
|
|
3357
|
+
clientManager.sendToClient(clientId, "log", logEntry);
|
|
3358
|
+
});
|
|
3359
|
+
clientManager.sendToClient(clientId, "connected", {
|
|
3360
|
+
clientId,
|
|
3361
|
+
timestamp: Date.now()
|
|
3362
|
+
});
|
|
3363
|
+
const heartbeat = setInterval(() => {
|
|
3364
|
+
const success = clientManager.sendToClient(clientId, "heartbeat", {
|
|
3365
|
+
timestamp: Date.now()
|
|
3366
|
+
});
|
|
3367
|
+
if (!success) {
|
|
3368
|
+
clearInterval(heartbeat);
|
|
3369
|
+
}
|
|
3370
|
+
}, heartbeatInterval);
|
|
3371
|
+
const cleanup = /* @__PURE__ */ __name(() => {
|
|
3372
|
+
log(`Client disconnected: ${clientId}`);
|
|
3373
|
+
clearInterval(heartbeat);
|
|
3374
|
+
unsubscribe();
|
|
3375
|
+
clientManager.removeClient(clientId);
|
|
3376
|
+
if (clientManager.getClientCount() === 0) {
|
|
3377
|
+
log("No more clients, stopping log watcher");
|
|
3378
|
+
logWatcher.stop();
|
|
3379
|
+
}
|
|
3380
|
+
}, "cleanup");
|
|
3381
|
+
req.on("close", cleanup);
|
|
3382
|
+
req.on("error", (error) => {
|
|
3383
|
+
log(`Client error ${clientId}:`, error);
|
|
3384
|
+
cleanup();
|
|
3385
|
+
});
|
|
3386
|
+
};
|
|
3387
|
+
}
|
|
3388
|
+
__name(createSSEHandler, "createSSEHandler");
|
|
3389
|
+
|
|
2875
3390
|
// src/middlewares/dev-logs/router.ts
|
|
2876
3391
|
function createDevLogRouter(options = {}) {
|
|
2877
3392
|
const logDir = resolveLogDir(options.logDir);
|
|
@@ -2880,6 +3395,7 @@ function createDevLogRouter(options = {}) {
|
|
|
2880
3395
|
router.get("/trace/recent", createGetRecentTracesHandler(logDir));
|
|
2881
3396
|
router.get("/files/:fileName", createGetLogFileHandler(logDir));
|
|
2882
3397
|
router.get("/server-logs", createGetServerLogsHandler(logDir));
|
|
3398
|
+
router.get("/server-logs/stream", createSSEHandler(logDir));
|
|
2883
3399
|
router.get("/trace/trigger/list", createGetTriggerListHandler(logDir));
|
|
2884
3400
|
router.get("/trace/trigger/:instanceID", createGetTriggerDetailHandler(logDir));
|
|
2885
3401
|
router.get("/trace/capability/list", createGetCapabilityTraceListHandler(logDir));
|
|
@@ -2941,22 +3457,22 @@ __name(createDevLogsMiddleware, "createDevLogsMiddleware");
|
|
|
2941
3457
|
import express3 from "express";
|
|
2942
3458
|
|
|
2943
3459
|
// src/middlewares/collect-logs/controller.ts
|
|
2944
|
-
import { join as
|
|
2945
|
-
import
|
|
3460
|
+
import { join as join7 } from "path";
|
|
3461
|
+
import fs11 from "fs";
|
|
2946
3462
|
|
|
2947
3463
|
// src/middlewares/collect-logs/utils.ts
|
|
2948
|
-
import { isAbsolute as isAbsolute2, join as
|
|
2949
|
-
import
|
|
3464
|
+
import { isAbsolute as isAbsolute2, join as join6 } from "path";
|
|
3465
|
+
import fs10 from "fs";
|
|
2950
3466
|
function resolveLogDir2(provided) {
|
|
2951
3467
|
if (!provided) {
|
|
2952
|
-
return
|
|
3468
|
+
return join6(process.cwd(), "logs");
|
|
2953
3469
|
}
|
|
2954
|
-
return isAbsolute2(provided) ? provided :
|
|
3470
|
+
return isAbsolute2(provided) ? provided : join6(process.cwd(), provided);
|
|
2955
3471
|
}
|
|
2956
3472
|
__name(resolveLogDir2, "resolveLogDir");
|
|
2957
3473
|
function ensureDir(dir) {
|
|
2958
|
-
if (!
|
|
2959
|
-
|
|
3474
|
+
if (!fs10.existsSync(dir)) {
|
|
3475
|
+
fs10.mkdirSync(dir, {
|
|
2960
3476
|
recursive: true
|
|
2961
3477
|
});
|
|
2962
3478
|
}
|
|
@@ -2974,7 +3490,7 @@ __name(serializeError2, "serializeError");
|
|
|
2974
3490
|
|
|
2975
3491
|
// src/middlewares/collect-logs/controller.ts
|
|
2976
3492
|
function collectLogsHandler(logDir, fileName) {
|
|
2977
|
-
const filePath =
|
|
3493
|
+
const filePath = join7(logDir, fileName);
|
|
2978
3494
|
ensureDir(logDir);
|
|
2979
3495
|
return async (req, res) => {
|
|
2980
3496
|
try {
|
|
@@ -2988,7 +3504,7 @@ function collectLogsHandler(logDir, fileName) {
|
|
|
2988
3504
|
...logContent,
|
|
2989
3505
|
server_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
2990
3506
|
}) + "\n";
|
|
2991
|
-
await
|
|
3507
|
+
await fs11.promises.appendFile(filePath, logLine);
|
|
2992
3508
|
res.json({
|
|
2993
3509
|
success: true
|
|
2994
3510
|
});
|
|
@@ -2999,7 +3515,7 @@ function collectLogsHandler(logDir, fileName) {
|
|
|
2999
3515
|
}
|
|
3000
3516
|
__name(collectLogsHandler, "collectLogsHandler");
|
|
3001
3517
|
function collectLogsBatchHandler(logDir, fileName) {
|
|
3002
|
-
const filePath =
|
|
3518
|
+
const filePath = join7(logDir, fileName);
|
|
3003
3519
|
ensureDir(logDir);
|
|
3004
3520
|
return async (req, res) => {
|
|
3005
3521
|
try {
|
|
@@ -3016,7 +3532,7 @@ function collectLogsBatchHandler(logDir, fileName) {
|
|
|
3016
3532
|
server_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
3017
3533
|
}) + "\n");
|
|
3018
3534
|
}
|
|
3019
|
-
await
|
|
3535
|
+
await fs11.promises.appendFile(filePath, logLines.join(""));
|
|
3020
3536
|
res.json({
|
|
3021
3537
|
success: true
|
|
3022
3538
|
});
|
|
@@ -3079,7 +3595,7 @@ function isGlobalMiddleware(middleware) {
|
|
|
3079
3595
|
}
|
|
3080
3596
|
__name(isGlobalMiddleware, "isGlobalMiddleware");
|
|
3081
3597
|
function computeMountPath(basePath, mountPath) {
|
|
3082
|
-
const routePath =
|
|
3598
|
+
const routePath = path7.posix.join(basePath, mountPath);
|
|
3083
3599
|
return routePath.startsWith("/") ? routePath : `/${routePath}`;
|
|
3084
3600
|
}
|
|
3085
3601
|
__name(computeMountPath, "computeMountPath");
|
|
@@ -3087,7 +3603,7 @@ function logMiddlewareRegistration(middleware, fullMountPath) {
|
|
|
3087
3603
|
if (middleware.routes && middleware.routes.length > 0) {
|
|
3088
3604
|
console.log(`[Middleware] Registered: ${middleware.name} at ${fullMountPath}`);
|
|
3089
3605
|
middleware.routes.forEach((route) => {
|
|
3090
|
-
const routePath = route.path === "/" ? fullMountPath :
|
|
3606
|
+
const routePath = route.path === "/" ? fullMountPath : path7.posix.join(fullMountPath, route.path);
|
|
3091
3607
|
console.log(` ${route.method} ${routePath} - ${route.description}`);
|
|
3092
3608
|
});
|
|
3093
3609
|
} else {
|