@lark-apaas/devtool-kits 1.2.10-alpha.0 → 1.2.11-alpha.0
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 +534 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +549 -31
- 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
|
|
|
@@ -2163,20 +2163,20 @@ async function readServerLogs(logDir, options = {}) {
|
|
|
2163
2163
|
}
|
|
2164
2164
|
__name(readServerLogs, "readServerLogs");
|
|
2165
2165
|
async function readLogsBySource(logDir, source) {
|
|
2166
|
-
const { join:
|
|
2166
|
+
const { join: join7 } = await import("path");
|
|
2167
2167
|
let filePath;
|
|
2168
2168
|
let parser;
|
|
2169
2169
|
if (source === "server") {
|
|
2170
|
-
filePath =
|
|
2170
|
+
filePath = join7(logDir, "server.log");
|
|
2171
2171
|
parser = /* @__PURE__ */ __name((line) => parsePinoLog(line, "server"), "parser");
|
|
2172
2172
|
} else if (source === "trace") {
|
|
2173
|
-
filePath =
|
|
2173
|
+
filePath = join7(logDir, "trace.log");
|
|
2174
2174
|
parser = /* @__PURE__ */ __name((line) => parsePinoLog(line, "trace"), "parser");
|
|
2175
2175
|
} else if (source === "server-std") {
|
|
2176
|
-
filePath =
|
|
2176
|
+
filePath = join7(logDir, "server.std.log");
|
|
2177
2177
|
parser = /* @__PURE__ */ __name((line) => parseStdLog(line, "server-std"), "parser");
|
|
2178
2178
|
} else if (source === "client-std") {
|
|
2179
|
-
filePath =
|
|
2179
|
+
filePath = join7(logDir, "client.std.log");
|
|
2180
2180
|
parser = /* @__PURE__ */ __name((line) => parseStdLog(line, "client-std"), "parser");
|
|
2181
2181
|
} else {
|
|
2182
2182
|
console.warn(`[readLogsBySource] Unknown source: ${source}`);
|
|
@@ -2344,7 +2344,7 @@ function generateUUID() {
|
|
|
2344
2344
|
});
|
|
2345
2345
|
}
|
|
2346
2346
|
__name(generateUUID, "generateUUID");
|
|
2347
|
-
async function readTriggerList(filePath, trigger,
|
|
2347
|
+
async function readTriggerList(filePath, trigger, path8, limit, triggerID) {
|
|
2348
2348
|
if (!await fileExists(filePath)) {
|
|
2349
2349
|
return void 0;
|
|
2350
2350
|
}
|
|
@@ -2370,7 +2370,7 @@ async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
|
|
|
2370
2370
|
if (alreadyAdded) {
|
|
2371
2371
|
return false;
|
|
2372
2372
|
}
|
|
2373
|
-
const isAutomationTrigger = builder.path?.endsWith(
|
|
2373
|
+
const isAutomationTrigger = builder.path?.endsWith(path8);
|
|
2374
2374
|
if (!isAutomationTrigger) {
|
|
2375
2375
|
return false;
|
|
2376
2376
|
}
|
|
@@ -2453,7 +2453,7 @@ async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
|
|
|
2453
2453
|
};
|
|
2454
2454
|
}
|
|
2455
2455
|
__name(readTriggerList, "readTriggerList");
|
|
2456
|
-
async function readTriggerDetail(filePath,
|
|
2456
|
+
async function readTriggerDetail(filePath, path8, instanceID) {
|
|
2457
2457
|
const exists = await fileExists(filePath);
|
|
2458
2458
|
if (!exists) {
|
|
2459
2459
|
return void 0;
|
|
@@ -2469,7 +2469,7 @@ async function readTriggerDetail(filePath, path7, instanceID) {
|
|
|
2469
2469
|
for await (const line of rl) {
|
|
2470
2470
|
const entry = parseLogLine2(line);
|
|
2471
2471
|
if (!entry) continue;
|
|
2472
|
-
const isAutomationTrigger = entry.path?.endsWith(
|
|
2472
|
+
const isAutomationTrigger = entry.path?.endsWith(path8);
|
|
2473
2473
|
const hasInstanceID = entry.instance_id === instanceID && entry.trigger;
|
|
2474
2474
|
if (!isAutomationTrigger || !hasInstanceID) continue;
|
|
2475
2475
|
matches.push(entry);
|
|
@@ -2611,16 +2611,16 @@ function createGetTriggerListHandler(logDir) {
|
|
|
2611
2611
|
});
|
|
2612
2612
|
}
|
|
2613
2613
|
const triggerID = typeof req.query.triggerID === "string" ? req.query.triggerID.trim() : void 0;
|
|
2614
|
-
const
|
|
2614
|
+
const path8 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
|
|
2615
2615
|
const limit = parseLimit(req.query.limit, 10, 200);
|
|
2616
2616
|
try {
|
|
2617
|
-
const result = await readTriggerList(traceLogPath, trigger,
|
|
2617
|
+
const result = await readTriggerList(traceLogPath, trigger, path8, limit, triggerID);
|
|
2618
2618
|
if (!result) {
|
|
2619
2619
|
return handleNotFound(res, traceLogPath);
|
|
2620
2620
|
}
|
|
2621
2621
|
res.json({
|
|
2622
2622
|
file: getRelativePath(traceLogPath),
|
|
2623
|
-
path:
|
|
2623
|
+
path: path8,
|
|
2624
2624
|
...result
|
|
2625
2625
|
});
|
|
2626
2626
|
} catch (error) {
|
|
@@ -2638,9 +2638,9 @@ function createGetTriggerDetailHandler(logDir) {
|
|
|
2638
2638
|
message: "instanceID is required"
|
|
2639
2639
|
});
|
|
2640
2640
|
}
|
|
2641
|
-
const
|
|
2641
|
+
const path8 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
|
|
2642
2642
|
try {
|
|
2643
|
-
const result = await readTriggerDetail(traceLogPath,
|
|
2643
|
+
const result = await readTriggerDetail(traceLogPath, path8, instanceID);
|
|
2644
2644
|
if (!result) {
|
|
2645
2645
|
return handleNotFound(res, traceLogPath);
|
|
2646
2646
|
}
|
|
@@ -2722,6 +2722,521 @@ function createHealthCheckHandler(options = {}) {
|
|
|
2722
2722
|
}
|
|
2723
2723
|
__name(createHealthCheckHandler, "createHealthCheckHandler");
|
|
2724
2724
|
|
|
2725
|
+
// src/middlewares/dev-logs/sse/log-watcher.ts
|
|
2726
|
+
import * as fs9 from "fs";
|
|
2727
|
+
import * as path6 from "path";
|
|
2728
|
+
function mapPinoLevelToServerLogLevel2(pinoLevel) {
|
|
2729
|
+
if (typeof pinoLevel === "string") {
|
|
2730
|
+
const lower = pinoLevel.toLowerCase();
|
|
2731
|
+
if (lower === "fatal") return "fatal";
|
|
2732
|
+
if (lower === "error") return "error";
|
|
2733
|
+
if (lower === "warn" || lower === "warning") return "warn";
|
|
2734
|
+
if (lower === "info" || lower === "log") return "log";
|
|
2735
|
+
if (lower === "debug") return "debug";
|
|
2736
|
+
if (lower === "trace" || lower === "verbose") return "verbose";
|
|
2737
|
+
return "log";
|
|
2738
|
+
}
|
|
2739
|
+
if (pinoLevel >= 60) return "fatal";
|
|
2740
|
+
if (pinoLevel >= 50) return "error";
|
|
2741
|
+
if (pinoLevel >= 40) return "warn";
|
|
2742
|
+
if (pinoLevel >= 30) return "log";
|
|
2743
|
+
if (pinoLevel >= 20) return "debug";
|
|
2744
|
+
return "verbose";
|
|
2745
|
+
}
|
|
2746
|
+
__name(mapPinoLevelToServerLogLevel2, "mapPinoLevelToServerLogLevel");
|
|
2747
|
+
function extractLogLevel2(text) {
|
|
2748
|
+
const lower = text.toLowerCase();
|
|
2749
|
+
if (lower.includes("fatal") || lower.includes("critical")) return "fatal";
|
|
2750
|
+
if (lower.includes("error") || lower.includes("<e>") || lower.includes("\u2716")) return "error";
|
|
2751
|
+
if (lower.includes("warn") || lower.includes("warning") || lower.includes("<w>") || lower.includes("\u26A0")) return "warn";
|
|
2752
|
+
if (lower.includes("debug") || lower.includes("<d>")) return "debug";
|
|
2753
|
+
if (lower.includes("verbose") || lower.includes("trace")) return "verbose";
|
|
2754
|
+
return "log";
|
|
2755
|
+
}
|
|
2756
|
+
__name(extractLogLevel2, "extractLogLevel");
|
|
2757
|
+
function generateUUID2() {
|
|
2758
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
2759
|
+
const r = Math.random() * 16 | 0;
|
|
2760
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
2761
|
+
return v.toString(16);
|
|
2762
|
+
});
|
|
2763
|
+
}
|
|
2764
|
+
__name(generateUUID2, "generateUUID");
|
|
2765
|
+
function parsePinoLog2(line, source) {
|
|
2766
|
+
try {
|
|
2767
|
+
const pinoLog = JSON.parse(line);
|
|
2768
|
+
const id = generateUUID2();
|
|
2769
|
+
return {
|
|
2770
|
+
id,
|
|
2771
|
+
level: mapPinoLevelToServerLogLevel2(pinoLog.level),
|
|
2772
|
+
timestamp: new Date(pinoLog.time).getTime(),
|
|
2773
|
+
message: pinoLog.message || pinoLog.msg || "",
|
|
2774
|
+
context: pinoLog.context || null,
|
|
2775
|
+
traceId: pinoLog.trace_id || null,
|
|
2776
|
+
userId: pinoLog.user_id || null,
|
|
2777
|
+
appId: pinoLog.app_id || null,
|
|
2778
|
+
tenantId: pinoLog.tenant_id || null,
|
|
2779
|
+
stack: pinoLog.stack || null,
|
|
2780
|
+
meta: {
|
|
2781
|
+
pid: pinoLog.pid,
|
|
2782
|
+
hostname: pinoLog.hostname,
|
|
2783
|
+
path: pinoLog.path,
|
|
2784
|
+
method: pinoLog.method,
|
|
2785
|
+
statusCode: pinoLog.status_code,
|
|
2786
|
+
durationMs: pinoLog.duration_ms,
|
|
2787
|
+
ip: pinoLog.ip,
|
|
2788
|
+
requestBody: pinoLog.request_body,
|
|
2789
|
+
responseBody: pinoLog.response_body
|
|
2790
|
+
},
|
|
2791
|
+
tags: [
|
|
2792
|
+
source
|
|
2793
|
+
]
|
|
2794
|
+
};
|
|
2795
|
+
} catch {
|
|
2796
|
+
return null;
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
__name(parsePinoLog2, "parsePinoLog");
|
|
2800
|
+
function parseStdLog2(line, source) {
|
|
2801
|
+
const id = generateUUID2();
|
|
2802
|
+
const match = line.match(/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(server|client)\] (.*)$/);
|
|
2803
|
+
if (!match) {
|
|
2804
|
+
return {
|
|
2805
|
+
id,
|
|
2806
|
+
level: extractLogLevel2(line),
|
|
2807
|
+
timestamp: Date.now(),
|
|
2808
|
+
message: line,
|
|
2809
|
+
context: null,
|
|
2810
|
+
traceId: null,
|
|
2811
|
+
userId: null,
|
|
2812
|
+
appId: null,
|
|
2813
|
+
tenantId: null,
|
|
2814
|
+
stack: null,
|
|
2815
|
+
meta: null,
|
|
2816
|
+
tags: [
|
|
2817
|
+
source
|
|
2818
|
+
]
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
const [, timeStr, , content] = match;
|
|
2822
|
+
let timestamp;
|
|
2823
|
+
try {
|
|
2824
|
+
const isoStr = timeStr.replace(" ", "T");
|
|
2825
|
+
timestamp = new Date(isoStr).getTime();
|
|
2826
|
+
if (isNaN(timestamp)) {
|
|
2827
|
+
timestamp = Date.now();
|
|
2828
|
+
}
|
|
2829
|
+
} catch {
|
|
2830
|
+
timestamp = Date.now();
|
|
2831
|
+
}
|
|
2832
|
+
return {
|
|
2833
|
+
id,
|
|
2834
|
+
level: extractLogLevel2(content),
|
|
2835
|
+
timestamp,
|
|
2836
|
+
message: content,
|
|
2837
|
+
context: null,
|
|
2838
|
+
traceId: null,
|
|
2839
|
+
userId: null,
|
|
2840
|
+
appId: null,
|
|
2841
|
+
tenantId: null,
|
|
2842
|
+
stack: null,
|
|
2843
|
+
meta: null,
|
|
2844
|
+
tags: [
|
|
2845
|
+
source
|
|
2846
|
+
]
|
|
2847
|
+
};
|
|
2848
|
+
}
|
|
2849
|
+
__name(parseStdLog2, "parseStdLog");
|
|
2850
|
+
var LogWatcher = class {
|
|
2851
|
+
static {
|
|
2852
|
+
__name(this, "LogWatcher");
|
|
2853
|
+
}
|
|
2854
|
+
logDir = "";
|
|
2855
|
+
watchers = /* @__PURE__ */ new Map();
|
|
2856
|
+
filePositions = /* @__PURE__ */ new Map();
|
|
2857
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
2858
|
+
isRunning = false;
|
|
2859
|
+
debug = false;
|
|
2860
|
+
logFiles = [
|
|
2861
|
+
{
|
|
2862
|
+
fileName: "server.log",
|
|
2863
|
+
source: "server",
|
|
2864
|
+
parser: parsePinoLog2
|
|
2865
|
+
},
|
|
2866
|
+
{
|
|
2867
|
+
fileName: "trace.log",
|
|
2868
|
+
source: "trace",
|
|
2869
|
+
parser: parsePinoLog2
|
|
2870
|
+
},
|
|
2871
|
+
{
|
|
2872
|
+
fileName: "server.std.log",
|
|
2873
|
+
source: "server-std",
|
|
2874
|
+
parser: parseStdLog2
|
|
2875
|
+
},
|
|
2876
|
+
{
|
|
2877
|
+
fileName: "client.std.log",
|
|
2878
|
+
source: "client-std",
|
|
2879
|
+
parser: parseStdLog2
|
|
2880
|
+
}
|
|
2881
|
+
];
|
|
2882
|
+
constructor(options = {}) {
|
|
2883
|
+
this.debug = options.debug ?? false;
|
|
2884
|
+
}
|
|
2885
|
+
log(...args) {
|
|
2886
|
+
if (this.debug) {
|
|
2887
|
+
console.log("[LogWatcher]", ...args);
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
/**
|
|
2891
|
+
* Start watching log files
|
|
2892
|
+
*/
|
|
2893
|
+
start(logDir) {
|
|
2894
|
+
if (this.isRunning) {
|
|
2895
|
+
this.log("Already running, ignoring start call");
|
|
2896
|
+
return;
|
|
2897
|
+
}
|
|
2898
|
+
this.logDir = logDir;
|
|
2899
|
+
this.isRunning = true;
|
|
2900
|
+
this.log(`Starting to watch log files in: ${logDir}`);
|
|
2901
|
+
for (const config of this.logFiles) {
|
|
2902
|
+
this.watchFile(config);
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
/**
|
|
2906
|
+
* Stop watching all files
|
|
2907
|
+
*/
|
|
2908
|
+
stop() {
|
|
2909
|
+
if (!this.isRunning) {
|
|
2910
|
+
return;
|
|
2911
|
+
}
|
|
2912
|
+
this.log("Stopping file watchers");
|
|
2913
|
+
this.isRunning = false;
|
|
2914
|
+
Array.from(this.watchers.entries()).forEach(([fileName, watcher]) => {
|
|
2915
|
+
watcher.close();
|
|
2916
|
+
this.log(`Closed watcher for: ${fileName}`);
|
|
2917
|
+
});
|
|
2918
|
+
this.watchers.clear();
|
|
2919
|
+
this.filePositions.clear();
|
|
2920
|
+
}
|
|
2921
|
+
/**
|
|
2922
|
+
* Subscribe to new log events
|
|
2923
|
+
*/
|
|
2924
|
+
onLog(callback) {
|
|
2925
|
+
this.subscribers.add(callback);
|
|
2926
|
+
this.log(`Subscriber added, total: ${this.subscribers.size}`);
|
|
2927
|
+
return () => {
|
|
2928
|
+
this.subscribers.delete(callback);
|
|
2929
|
+
this.log(`Subscriber removed, total: ${this.subscribers.size}`);
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
/**
|
|
2933
|
+
* Get subscriber count
|
|
2934
|
+
*/
|
|
2935
|
+
getSubscriberCount() {
|
|
2936
|
+
return this.subscribers.size;
|
|
2937
|
+
}
|
|
2938
|
+
/**
|
|
2939
|
+
* Watch a single log file
|
|
2940
|
+
*/
|
|
2941
|
+
watchFile(config) {
|
|
2942
|
+
const filePath = path6.join(this.logDir, config.fileName);
|
|
2943
|
+
if (!fs9.existsSync(filePath)) {
|
|
2944
|
+
this.log(`File not found, skipping: ${config.fileName}`);
|
|
2945
|
+
return;
|
|
2946
|
+
}
|
|
2947
|
+
try {
|
|
2948
|
+
const stats = fs9.statSync(filePath);
|
|
2949
|
+
this.filePositions.set(config.fileName, stats.size);
|
|
2950
|
+
this.log(`Initialized position for ${config.fileName}: ${stats.size} bytes`);
|
|
2951
|
+
} catch (error) {
|
|
2952
|
+
this.log(`Failed to get initial position for ${config.fileName}:`, error);
|
|
2953
|
+
this.filePositions.set(config.fileName, 0);
|
|
2954
|
+
}
|
|
2955
|
+
try {
|
|
2956
|
+
const watcher = fs9.watch(filePath, (eventType) => {
|
|
2957
|
+
if (eventType === "change") {
|
|
2958
|
+
this.handleFileChange(config);
|
|
2959
|
+
}
|
|
2960
|
+
});
|
|
2961
|
+
watcher.on("error", (error) => {
|
|
2962
|
+
this.log(`Watcher error for ${config.fileName}:`, error);
|
|
2963
|
+
this.restartWatcher(config);
|
|
2964
|
+
});
|
|
2965
|
+
this.watchers.set(config.fileName, watcher);
|
|
2966
|
+
this.log(`Started watching: ${config.fileName}`);
|
|
2967
|
+
} catch (error) {
|
|
2968
|
+
this.log(`Failed to start watcher for ${config.fileName}:`, error);
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
/**
|
|
2972
|
+
* Restart a file watcher after error
|
|
2973
|
+
*/
|
|
2974
|
+
restartWatcher(config) {
|
|
2975
|
+
const existingWatcher = this.watchers.get(config.fileName);
|
|
2976
|
+
if (existingWatcher) {
|
|
2977
|
+
existingWatcher.close();
|
|
2978
|
+
this.watchers.delete(config.fileName);
|
|
2979
|
+
}
|
|
2980
|
+
setTimeout(() => {
|
|
2981
|
+
if (this.isRunning) {
|
|
2982
|
+
this.log(`Restarting watcher for: ${config.fileName}`);
|
|
2983
|
+
this.watchFile(config);
|
|
2984
|
+
}
|
|
2985
|
+
}, 1e3);
|
|
2986
|
+
}
|
|
2987
|
+
/**
|
|
2988
|
+
* Handle file change event - read new content
|
|
2989
|
+
*/
|
|
2990
|
+
handleFileChange(config) {
|
|
2991
|
+
const filePath = path6.join(this.logDir, config.fileName);
|
|
2992
|
+
const lastPosition = this.filePositions.get(config.fileName) || 0;
|
|
2993
|
+
try {
|
|
2994
|
+
const stats = fs9.statSync(filePath);
|
|
2995
|
+
const currentSize = stats.size;
|
|
2996
|
+
if (currentSize < lastPosition) {
|
|
2997
|
+
this.log(`File ${config.fileName} was truncated, resetting position`);
|
|
2998
|
+
this.filePositions.set(config.fileName, 0);
|
|
2999
|
+
this.handleFileChange(config);
|
|
3000
|
+
return;
|
|
3001
|
+
}
|
|
3002
|
+
if (currentSize === lastPosition) {
|
|
3003
|
+
return;
|
|
3004
|
+
}
|
|
3005
|
+
const readSize = currentSize - lastPosition;
|
|
3006
|
+
const buffer = Buffer.alloc(readSize);
|
|
3007
|
+
const fd = fs9.openSync(filePath, "r");
|
|
3008
|
+
try {
|
|
3009
|
+
fs9.readSync(fd, buffer, 0, readSize, lastPosition);
|
|
3010
|
+
} finally {
|
|
3011
|
+
fs9.closeSync(fd);
|
|
3012
|
+
}
|
|
3013
|
+
this.filePositions.set(config.fileName, currentSize);
|
|
3014
|
+
const content = buffer.toString("utf8");
|
|
3015
|
+
const lines = content.split("\n");
|
|
3016
|
+
for (const line of lines) {
|
|
3017
|
+
if (!line.trim()) continue;
|
|
3018
|
+
try {
|
|
3019
|
+
const log = config.parser(line, config.source);
|
|
3020
|
+
if (log) {
|
|
3021
|
+
this.notifySubscribers(log);
|
|
3022
|
+
}
|
|
3023
|
+
} catch {
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
} catch (error) {
|
|
3027
|
+
this.log(`Error reading file ${config.fileName}:`, error);
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
/**
|
|
3031
|
+
* Notify all subscribers of new log
|
|
3032
|
+
*/
|
|
3033
|
+
notifySubscribers(log) {
|
|
3034
|
+
Array.from(this.subscribers).forEach((subscriber) => {
|
|
3035
|
+
try {
|
|
3036
|
+
subscriber(log);
|
|
3037
|
+
} catch (error) {
|
|
3038
|
+
this.log("Subscriber error:", error);
|
|
3039
|
+
}
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
3042
|
+
};
|
|
3043
|
+
|
|
3044
|
+
// src/middlewares/dev-logs/sse/client-manager.ts
|
|
3045
|
+
var ClientManager = class {
|
|
3046
|
+
static {
|
|
3047
|
+
__name(this, "ClientManager");
|
|
3048
|
+
}
|
|
3049
|
+
clients = /* @__PURE__ */ new Map();
|
|
3050
|
+
debug = false;
|
|
3051
|
+
constructor(options = {}) {
|
|
3052
|
+
this.debug = options.debug ?? false;
|
|
3053
|
+
}
|
|
3054
|
+
log(...args) {
|
|
3055
|
+
if (this.debug) {
|
|
3056
|
+
console.log("[ClientManager]", ...args);
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
/**
|
|
3060
|
+
* Generate a unique client ID
|
|
3061
|
+
*/
|
|
3062
|
+
generateClientId() {
|
|
3063
|
+
return `client_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
3064
|
+
}
|
|
3065
|
+
/**
|
|
3066
|
+
* Add a new client connection
|
|
3067
|
+
*/
|
|
3068
|
+
addClient(id, res) {
|
|
3069
|
+
this.clients.set(id, {
|
|
3070
|
+
id,
|
|
3071
|
+
res,
|
|
3072
|
+
connectedAt: Date.now()
|
|
3073
|
+
});
|
|
3074
|
+
this.log(`Client connected: ${id}, total clients: ${this.clients.size}`);
|
|
3075
|
+
}
|
|
3076
|
+
/**
|
|
3077
|
+
* Remove a client connection
|
|
3078
|
+
*/
|
|
3079
|
+
removeClient(id) {
|
|
3080
|
+
if (this.clients.has(id)) {
|
|
3081
|
+
this.clients.delete(id);
|
|
3082
|
+
this.log(`Client disconnected: ${id}, total clients: ${this.clients.size}`);
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
/**
|
|
3086
|
+
* Get current client count
|
|
3087
|
+
*/
|
|
3088
|
+
getClientCount() {
|
|
3089
|
+
return this.clients.size;
|
|
3090
|
+
}
|
|
3091
|
+
/**
|
|
3092
|
+
* Check if a client exists
|
|
3093
|
+
*/
|
|
3094
|
+
hasClient(id) {
|
|
3095
|
+
return this.clients.has(id);
|
|
3096
|
+
}
|
|
3097
|
+
/**
|
|
3098
|
+
* Send SSE event to a specific client
|
|
3099
|
+
*/
|
|
3100
|
+
sendToClient(clientId, event, data) {
|
|
3101
|
+
const client = this.clients.get(clientId);
|
|
3102
|
+
if (!client) {
|
|
3103
|
+
return false;
|
|
3104
|
+
}
|
|
3105
|
+
try {
|
|
3106
|
+
const message = this.formatSSEMessage(event, data);
|
|
3107
|
+
client.res.write(message);
|
|
3108
|
+
return true;
|
|
3109
|
+
} catch (error) {
|
|
3110
|
+
this.log(`Failed to send to client ${clientId}:`, error);
|
|
3111
|
+
this.removeClient(clientId);
|
|
3112
|
+
return false;
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
/**
|
|
3116
|
+
* Broadcast SSE event to all clients
|
|
3117
|
+
*/
|
|
3118
|
+
broadcast(event, data) {
|
|
3119
|
+
const message = this.formatSSEMessage(event, data);
|
|
3120
|
+
const failedClients = [];
|
|
3121
|
+
Array.from(this.clients.entries()).forEach(([id, client]) => {
|
|
3122
|
+
try {
|
|
3123
|
+
client.res.write(message);
|
|
3124
|
+
} catch (error) {
|
|
3125
|
+
this.log(`Broadcast failed for client ${id}:`, error);
|
|
3126
|
+
failedClients.push(id);
|
|
3127
|
+
}
|
|
3128
|
+
});
|
|
3129
|
+
failedClients.forEach((id) => {
|
|
3130
|
+
this.removeClient(id);
|
|
3131
|
+
});
|
|
3132
|
+
}
|
|
3133
|
+
/**
|
|
3134
|
+
* Format SSE message
|
|
3135
|
+
* SSE format: event: <event>\ndata: <json>\n\n
|
|
3136
|
+
*/
|
|
3137
|
+
formatSSEMessage(event, data) {
|
|
3138
|
+
const jsonData = JSON.stringify(data);
|
|
3139
|
+
return `event: ${event}
|
|
3140
|
+
data: ${jsonData}
|
|
3141
|
+
|
|
3142
|
+
`;
|
|
3143
|
+
}
|
|
3144
|
+
/**
|
|
3145
|
+
* Get all client IDs
|
|
3146
|
+
*/
|
|
3147
|
+
getClientIds() {
|
|
3148
|
+
return Array.from(this.clients.keys());
|
|
3149
|
+
}
|
|
3150
|
+
/**
|
|
3151
|
+
* Get client info
|
|
3152
|
+
*/
|
|
3153
|
+
getClientInfo(id) {
|
|
3154
|
+
const client = this.clients.get(id);
|
|
3155
|
+
if (!client) {
|
|
3156
|
+
return null;
|
|
3157
|
+
}
|
|
3158
|
+
return {
|
|
3159
|
+
id: client.id,
|
|
3160
|
+
connectedAt: client.connectedAt
|
|
3161
|
+
};
|
|
3162
|
+
}
|
|
3163
|
+
/**
|
|
3164
|
+
* Close all client connections
|
|
3165
|
+
*/
|
|
3166
|
+
closeAll() {
|
|
3167
|
+
this.log(`Closing all ${this.clients.size} clients`);
|
|
3168
|
+
Array.from(this.clients.entries()).forEach(([id, client]) => {
|
|
3169
|
+
try {
|
|
3170
|
+
client.res.end();
|
|
3171
|
+
} catch (error) {
|
|
3172
|
+
this.log(`Error closing client ${id}:`, error);
|
|
3173
|
+
}
|
|
3174
|
+
});
|
|
3175
|
+
this.clients.clear();
|
|
3176
|
+
}
|
|
3177
|
+
};
|
|
3178
|
+
|
|
3179
|
+
// src/middlewares/dev-logs/sse/sse.controller.ts
|
|
3180
|
+
function createSSEHandler(logDir, options = {}) {
|
|
3181
|
+
const { debug = false, heartbeatInterval = 3e4 } = options;
|
|
3182
|
+
const logWatcher = new LogWatcher({
|
|
3183
|
+
debug
|
|
3184
|
+
});
|
|
3185
|
+
const clientManager = new ClientManager({
|
|
3186
|
+
debug
|
|
3187
|
+
});
|
|
3188
|
+
const log = /* @__PURE__ */ __name((...args) => {
|
|
3189
|
+
if (debug) {
|
|
3190
|
+
console.log("[SSEHandler]", ...args);
|
|
3191
|
+
}
|
|
3192
|
+
}, "log");
|
|
3193
|
+
return (req, res) => {
|
|
3194
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
3195
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
3196
|
+
res.setHeader("Connection", "keep-alive");
|
|
3197
|
+
res.setHeader("X-Accel-Buffering", "no");
|
|
3198
|
+
res.setTimeout(0);
|
|
3199
|
+
const clientId = clientManager.generateClientId();
|
|
3200
|
+
clientManager.addClient(clientId, res);
|
|
3201
|
+
log(`New SSE connection: ${clientId}`);
|
|
3202
|
+
if (clientManager.getClientCount() === 1) {
|
|
3203
|
+
log("First client connected, starting log watcher");
|
|
3204
|
+
logWatcher.start(logDir);
|
|
3205
|
+
}
|
|
3206
|
+
const unsubscribe = logWatcher.onLog((logEntry) => {
|
|
3207
|
+
clientManager.sendToClient(clientId, "log", logEntry);
|
|
3208
|
+
});
|
|
3209
|
+
clientManager.sendToClient(clientId, "connected", {
|
|
3210
|
+
clientId,
|
|
3211
|
+
timestamp: Date.now()
|
|
3212
|
+
});
|
|
3213
|
+
const heartbeat = setInterval(() => {
|
|
3214
|
+
const success = clientManager.sendToClient(clientId, "heartbeat", {
|
|
3215
|
+
timestamp: Date.now()
|
|
3216
|
+
});
|
|
3217
|
+
if (!success) {
|
|
3218
|
+
clearInterval(heartbeat);
|
|
3219
|
+
}
|
|
3220
|
+
}, heartbeatInterval);
|
|
3221
|
+
const cleanup = /* @__PURE__ */ __name(() => {
|
|
3222
|
+
log(`Client disconnected: ${clientId}`);
|
|
3223
|
+
clearInterval(heartbeat);
|
|
3224
|
+
unsubscribe();
|
|
3225
|
+
clientManager.removeClient(clientId);
|
|
3226
|
+
if (clientManager.getClientCount() === 0) {
|
|
3227
|
+
log("No more clients, stopping log watcher");
|
|
3228
|
+
logWatcher.stop();
|
|
3229
|
+
}
|
|
3230
|
+
}, "cleanup");
|
|
3231
|
+
req.on("close", cleanup);
|
|
3232
|
+
req.on("error", (error) => {
|
|
3233
|
+
log(`Client error ${clientId}:`, error);
|
|
3234
|
+
cleanup();
|
|
3235
|
+
});
|
|
3236
|
+
};
|
|
3237
|
+
}
|
|
3238
|
+
__name(createSSEHandler, "createSSEHandler");
|
|
3239
|
+
|
|
2725
3240
|
// src/middlewares/dev-logs/router.ts
|
|
2726
3241
|
function createDevLogRouter(options = {}) {
|
|
2727
3242
|
const logDir = resolveLogDir(options.logDir);
|
|
@@ -2730,6 +3245,9 @@ function createDevLogRouter(options = {}) {
|
|
|
2730
3245
|
router.get("/trace/recent", createGetRecentTracesHandler(logDir));
|
|
2731
3246
|
router.get("/files/:fileName", createGetLogFileHandler(logDir));
|
|
2732
3247
|
router.get("/server-logs", createGetServerLogsHandler(logDir));
|
|
3248
|
+
router.get("/server-logs/stream", createSSEHandler(logDir, {
|
|
3249
|
+
debug: true
|
|
3250
|
+
}));
|
|
2733
3251
|
router.get("/trace/trigger/list", createGetTriggerListHandler(logDir));
|
|
2734
3252
|
router.get("/trace/trigger/:instanceID", createGetTriggerDetailHandler(logDir));
|
|
2735
3253
|
router.get("/health", createHealthCheckHandler());
|
|
@@ -2790,22 +3308,22 @@ __name(createDevLogsMiddleware, "createDevLogsMiddleware");
|
|
|
2790
3308
|
import express3 from "express";
|
|
2791
3309
|
|
|
2792
3310
|
// src/middlewares/collect-logs/controller.ts
|
|
2793
|
-
import { join as
|
|
2794
|
-
import
|
|
3311
|
+
import { join as join6 } from "path";
|
|
3312
|
+
import fs11 from "fs";
|
|
2795
3313
|
|
|
2796
3314
|
// src/middlewares/collect-logs/utils.ts
|
|
2797
|
-
import { isAbsolute as isAbsolute2, join as
|
|
2798
|
-
import
|
|
3315
|
+
import { isAbsolute as isAbsolute2, join as join5 } from "path";
|
|
3316
|
+
import fs10 from "fs";
|
|
2799
3317
|
function resolveLogDir2(provided) {
|
|
2800
3318
|
if (!provided) {
|
|
2801
|
-
return
|
|
3319
|
+
return join5(process.cwd(), "logs");
|
|
2802
3320
|
}
|
|
2803
|
-
return isAbsolute2(provided) ? provided :
|
|
3321
|
+
return isAbsolute2(provided) ? provided : join5(process.cwd(), provided);
|
|
2804
3322
|
}
|
|
2805
3323
|
__name(resolveLogDir2, "resolveLogDir");
|
|
2806
3324
|
function ensureDir(dir) {
|
|
2807
|
-
if (!
|
|
2808
|
-
|
|
3325
|
+
if (!fs10.existsSync(dir)) {
|
|
3326
|
+
fs10.mkdirSync(dir, {
|
|
2809
3327
|
recursive: true
|
|
2810
3328
|
});
|
|
2811
3329
|
}
|
|
@@ -2823,7 +3341,7 @@ __name(serializeError2, "serializeError");
|
|
|
2823
3341
|
|
|
2824
3342
|
// src/middlewares/collect-logs/controller.ts
|
|
2825
3343
|
function collectLogsHandler(logDir, fileName) {
|
|
2826
|
-
const filePath =
|
|
3344
|
+
const filePath = join6(logDir, fileName);
|
|
2827
3345
|
ensureDir(logDir);
|
|
2828
3346
|
return async (req, res) => {
|
|
2829
3347
|
try {
|
|
@@ -2837,7 +3355,7 @@ function collectLogsHandler(logDir, fileName) {
|
|
|
2837
3355
|
...logContent,
|
|
2838
3356
|
server_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
2839
3357
|
}) + "\n";
|
|
2840
|
-
await
|
|
3358
|
+
await fs11.promises.appendFile(filePath, logLine);
|
|
2841
3359
|
res.json({
|
|
2842
3360
|
success: true
|
|
2843
3361
|
});
|
|
@@ -2848,7 +3366,7 @@ function collectLogsHandler(logDir, fileName) {
|
|
|
2848
3366
|
}
|
|
2849
3367
|
__name(collectLogsHandler, "collectLogsHandler");
|
|
2850
3368
|
function collectLogsBatchHandler(logDir, fileName) {
|
|
2851
|
-
const filePath =
|
|
3369
|
+
const filePath = join6(logDir, fileName);
|
|
2852
3370
|
ensureDir(logDir);
|
|
2853
3371
|
return async (req, res) => {
|
|
2854
3372
|
try {
|
|
@@ -2865,7 +3383,7 @@ function collectLogsBatchHandler(logDir, fileName) {
|
|
|
2865
3383
|
server_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
2866
3384
|
}) + "\n");
|
|
2867
3385
|
}
|
|
2868
|
-
await
|
|
3386
|
+
await fs11.promises.appendFile(filePath, logLines.join(""));
|
|
2869
3387
|
res.json({
|
|
2870
3388
|
success: true
|
|
2871
3389
|
});
|
|
@@ -2928,7 +3446,7 @@ function isGlobalMiddleware(middleware) {
|
|
|
2928
3446
|
}
|
|
2929
3447
|
__name(isGlobalMiddleware, "isGlobalMiddleware");
|
|
2930
3448
|
function computeMountPath(basePath, mountPath) {
|
|
2931
|
-
const routePath =
|
|
3449
|
+
const routePath = path7.posix.join(basePath, mountPath);
|
|
2932
3450
|
return routePath.startsWith("/") ? routePath : `/${routePath}`;
|
|
2933
3451
|
}
|
|
2934
3452
|
__name(computeMountPath, "computeMountPath");
|
|
@@ -2936,7 +3454,7 @@ function logMiddlewareRegistration(middleware, fullMountPath) {
|
|
|
2936
3454
|
if (middleware.routes && middleware.routes.length > 0) {
|
|
2937
3455
|
console.log(`[Middleware] Registered: ${middleware.name} at ${fullMountPath}`);
|
|
2938
3456
|
middleware.routes.forEach((route) => {
|
|
2939
|
-
const routePath = route.path === "/" ? fullMountPath :
|
|
3457
|
+
const routePath = route.path === "/" ? fullMountPath : path7.posix.join(fullMountPath, route.path);
|
|
2940
3458
|
console.log(` ${route.method} ${routePath} - ${route.description}`);
|
|
2941
3459
|
});
|
|
2942
3460
|
} else {
|