imean-service-engine 1.5.0 → 1.7.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/mod.cjs CHANGED
@@ -7,6 +7,7 @@ var nodeServer = require('@hono/node-server');
7
7
  var etcd3 = require('etcd3');
8
8
  var fs = require('fs-extra');
9
9
  var hono = require('hono');
10
+ var timing = require('hono/timing');
10
11
  var api = require('@opentelemetry/api');
11
12
  var winston = require('winston');
12
13
  var prettier = require('prettier');
@@ -17,6 +18,8 @@ var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
17
18
  var types_js = require('@modelcontextprotocol/sdk/types.js');
18
19
  var streaming = require('hono/streaming');
19
20
  var ulid = require('ulid');
21
+ var html = require('hono/html');
22
+ var jsxRuntime = require('hono/jsx/jsx-runtime');
20
23
  var dayjs = require('dayjs');
21
24
 
22
25
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -595,6 +598,62 @@ var ActionHandler = class {
595
598
  function isAsyncIterable(obj) {
596
599
  return obj != null && typeof obj[Symbol.asyncIterator] === "function";
597
600
  }
601
+
602
+ // decorators/page.ts
603
+ var PAGE_METADATA = Symbol("page:metadata");
604
+ function Page(options) {
605
+ return function(_target, context) {
606
+ const methodName = context.name;
607
+ context.addInitializer(function() {
608
+ const prototype = this.constructor.prototype;
609
+ const existingMetadata = prototype[PAGE_METADATA] || {};
610
+ existingMetadata[methodName] = {
611
+ name: methodName,
612
+ description: options.description || "",
613
+ method: options.method,
614
+ path: options.path
615
+ };
616
+ prototype[PAGE_METADATA] = existingMetadata;
617
+ });
618
+ };
619
+ }
620
+ function getPageMetadata(target) {
621
+ return target.constructor.prototype[PAGE_METADATA] ?? {};
622
+ }
623
+ var tracer3 = api.trace.getTracer("page-handler");
624
+ var PageHandler = class {
625
+ constructor(moduleInstance, options, moduleName) {
626
+ this.moduleInstance = moduleInstance;
627
+ this.options = options;
628
+ this.moduleName = moduleName;
629
+ }
630
+ async handle(ctx) {
631
+ return await tracer3.startActiveSpan(
632
+ `handle ${this.moduleName}.${this.options.name}`,
633
+ async (span) => {
634
+ span.setAttribute("module", this.moduleName);
635
+ span.setAttribute("page", this.options.name);
636
+ span.setAttribute("path", this.options.path);
637
+ try {
638
+ const result = await this.moduleInstance[this.options.name].apply(
639
+ this.moduleInstance,
640
+ [ctx]
641
+ );
642
+ return ctx.html(result);
643
+ } catch (error) {
644
+ span.recordException(error);
645
+ span.setStatus({
646
+ code: api.SpanStatusCode.ERROR,
647
+ message: error.message
648
+ });
649
+ throw error;
650
+ } finally {
651
+ span.end();
652
+ }
653
+ }
654
+ );
655
+ }
656
+ };
598
657
  var WebSocketHandler = class {
599
658
  constructor(microservice, options) {
600
659
  this.microservice = microservice;
@@ -779,10 +838,12 @@ var Microservice = class {
779
838
  lease;
780
839
  scheduler;
781
840
  abortController;
782
- isShuttingDown = false;
783
841
  statisticsTimer;
784
842
  wsHandler;
785
843
  actionHandlers = /* @__PURE__ */ new Map();
844
+ pageHandlers = /* @__PURE__ */ new Map();
845
+ activeRequests = /* @__PURE__ */ new Map();
846
+ status = "running";
786
847
  modules = /* @__PURE__ */ new Map();
787
848
  fetch;
788
849
  options;
@@ -790,6 +851,7 @@ var Microservice = class {
790
851
  serviceId;
791
852
  constructor(options) {
792
853
  this.app = new hono.Hono();
854
+ this.app.use(timing.timing());
793
855
  this.nodeWebSocket = nodeWs.createNodeWebSocket({ app: this.app });
794
856
  this.serviceId = crypto.randomUUID();
795
857
  this.options = {
@@ -805,6 +867,10 @@ var Microservice = class {
805
867
  websocket: { enabled: false },
806
868
  cacheAdapter: new MemoryCacheAdapter(),
807
869
  plugins: [],
870
+ gracefulShutdown: {
871
+ timeout: 3e4,
872
+ cleanupHooks: []
873
+ },
808
874
  ...options
809
875
  };
810
876
  this.cache = this.options.cacheAdapter;
@@ -824,6 +890,11 @@ var Microservice = class {
824
890
  }
825
891
  await this.registerService(true);
826
892
  await this.initPlugins();
893
+ this.app.get(this.options.prefix, (ctx) => {
894
+ const name = this.options.name ?? "Microservice";
895
+ const version = this.options.version ?? "1.0.0";
896
+ return ctx.text(`${name} is ${this.status}. version: ${version}`);
897
+ });
827
898
  }
828
899
  async initModules() {
829
900
  for (const ModuleClass of this.options.modules) {
@@ -838,6 +909,7 @@ var Microservice = class {
838
909
  logger_default.info(`[ \u6CE8\u518C\u6A21\u5757 ] ${moduleName} ${metadata.options.description}`);
839
910
  this.modules.set(moduleName, moduleInstance);
840
911
  const actions = getActionMetadata(ModuleClass.prototype);
912
+ const pages = getPageMetadata(ModuleClass.prototype);
841
913
  for (const [actionName, actionMetadata] of Object.entries(actions)) {
842
914
  const handler = new ActionHandler(
843
915
  moduleInstance,
@@ -851,6 +923,18 @@ var Microservice = class {
851
923
  `[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description} ${actionMetadata.mcp ? "MCP:" + actionMetadata.mcp?.type : ""}`
852
924
  );
853
925
  }
926
+ for (const [_, page] of Object.entries(pages)) {
927
+ const handler = new PageHandler(
928
+ moduleInstance,
929
+ page,
930
+ moduleName
931
+ );
932
+ this.pageHandlers.set(`${moduleName}.${page.name}`, handler);
933
+ this.app[page.method](`${this.options.prefix}${page.path}`, (ctx) => handler.handle(ctx));
934
+ logger_default.info(
935
+ `[ \u6CE8\u518C\u9875\u9762 ] ${moduleName}.${page.name} ${page.method.toUpperCase()} ${page.path} ${page.description}`
936
+ );
937
+ }
854
938
  const schedules = getScheduleMetadata(ModuleClass.prototype);
855
939
  if (schedules && Object.keys(schedules).length > 0) {
856
940
  if (!this.scheduler && this.etcdClient) {
@@ -880,11 +964,6 @@ var Microservice = class {
880
964
  initRoutes() {
881
965
  const startTime = Date.now();
882
966
  const prefix = this.options.prefix || "/api";
883
- this.app.get(prefix, (ctx) => {
884
- const name = this.options.name ?? "Microservice";
885
- const version = this.options.version ?? "1.0.0";
886
- return ctx.text(`${name} is running. version: ${version}`);
887
- });
888
967
  this.app.get(`${prefix}/health`, (ctx) => {
889
968
  return ctx.json({
890
969
  status: "ok",
@@ -899,7 +978,9 @@ var Microservice = class {
899
978
  version: this.options.version,
900
979
  env: this.options.env,
901
980
  modules: this.getModules(),
902
- stats: Object.fromEntries(this.statsMap)
981
+ stats: Object.fromEntries(this.statsMap),
982
+ activeRequests: this.getActiveRequestCount(),
983
+ status: this.status
903
984
  });
904
985
  });
905
986
  this.app.get(`${prefix}/client.ts`, async (ctx) => {
@@ -1096,7 +1177,12 @@ var Microservice = class {
1096
1177
  process.on("SIGINT", () => {
1097
1178
  logger_default.info(`
1098
1179
  Received SIGINT signal`);
1099
- this.gracefulShutdown();
1180
+ this.shutdown();
1181
+ });
1182
+ process.on("SIGTERM", () => {
1183
+ logger_default.info(`
1184
+ Received SIGTERM signal`);
1185
+ this.shutdown();
1100
1186
  });
1101
1187
  process.on("unhandledrejection", (event) => {
1102
1188
  logger_default.error("Unhandled rejection:", event.reason);
@@ -1108,14 +1194,16 @@ Received SIGINT signal`);
1108
1194
  /**
1109
1195
  * 优雅停机
1110
1196
  */
1111
- async gracefulShutdown() {
1112
- if (this.isShuttingDown) return;
1113
- this.isShuttingDown = true;
1114
- logger_default.info("\nGraceful shutdown initiated...");
1197
+ async shutdown() {
1198
+ if (this.status === "shutting_down") return;
1199
+ this.status = "shutting_down";
1200
+ logger_default.info("\nshutdown initiated...");
1115
1201
  if (this.statisticsTimer) clearInterval(this.statisticsTimer);
1116
1202
  try {
1203
+ await this.waitForActiveRequests();
1204
+ await this.executeCleanupHooks();
1117
1205
  await this.stop();
1118
- logger_default.info("Graceful shutdown completed");
1206
+ logger_default.info("shutdown completed");
1119
1207
  } catch (error) {
1120
1208
  logger_default.error("Error during shutdown:", error);
1121
1209
  process.exit(1);
@@ -1123,6 +1211,57 @@ Received SIGINT signal`);
1123
1211
  process.exit(0);
1124
1212
  }
1125
1213
  }
1214
+ /**
1215
+ * 等待所有活跃请求完成
1216
+ */
1217
+ async waitForActiveRequests() {
1218
+ const timeout = this.options.gracefulShutdown?.timeout || 3e4;
1219
+ const startTime = Date.now();
1220
+ logger_default.info(`Waiting for ${this.activeRequests.size} active requests to complete...`);
1221
+ return new Promise((resolve) => {
1222
+ const checkInterval = setInterval(() => {
1223
+ const elapsed = Date.now() - startTime;
1224
+ if (this.activeRequests.size === 0) {
1225
+ clearInterval(checkInterval);
1226
+ logger_default.info("All active requests completed");
1227
+ resolve();
1228
+ } else if (elapsed >= timeout) {
1229
+ clearInterval(checkInterval);
1230
+ logger_default.warn(`Timeout waiting for requests to complete. ${this.activeRequests.size} requests still active`);
1231
+ resolve();
1232
+ } else {
1233
+ logger_default.info(`Still waiting for ${this.activeRequests.size} requests... (${elapsed}ms elapsed)`);
1234
+ }
1235
+ }, 1e3);
1236
+ });
1237
+ }
1238
+ /**
1239
+ * 执行清理hook
1240
+ */
1241
+ async executeCleanupHooks() {
1242
+ const hooks = this.options.gracefulShutdown?.cleanupHooks || [];
1243
+ if (hooks.length === 0) {
1244
+ logger_default.info("No cleanup hooks configured");
1245
+ return;
1246
+ }
1247
+ logger_default.info(`Executing ${hooks.length} cleanup hooks...`);
1248
+ for (const hook of hooks) {
1249
+ try {
1250
+ const timeout = hook.timeout || 5e3;
1251
+ logger_default.info(`Executing cleanup hook: ${hook.name}`);
1252
+ await Promise.race([
1253
+ Promise.resolve(hook.cleanup()),
1254
+ new Promise(
1255
+ (_, reject) => setTimeout(() => reject(new Error(`Cleanup hook ${hook.name} timeout`)), timeout)
1256
+ )
1257
+ ]);
1258
+ logger_default.info(`Cleanup hook ${hook.name} completed successfully`);
1259
+ } catch (error) {
1260
+ logger_default.error(`Cleanup hook ${hook.name} failed:`, error);
1261
+ }
1262
+ }
1263
+ logger_default.info("All cleanup hooks completed");
1264
+ }
1126
1265
  initStatsEventManager() {
1127
1266
  this.statisticsTimer = setInterval(async () => {
1128
1267
  await this.updateStats();
@@ -1164,14 +1303,54 @@ Received SIGINT signal`);
1164
1303
  }
1165
1304
  return handler;
1166
1305
  }
1306
+ /**
1307
+ * 添加活跃请求跟踪
1308
+ */
1309
+ addActiveRequest(requestId, requestInfo) {
1310
+ this.activeRequests.set(requestId, requestInfo);
1311
+ }
1312
+ /**
1313
+ * 移除活跃请求跟踪
1314
+ */
1315
+ removeActiveRequest(requestId) {
1316
+ this.activeRequests.delete(requestId);
1317
+ }
1318
+ /**
1319
+ * 获取当前活跃请求数量
1320
+ */
1321
+ getActiveRequestCount() {
1322
+ return this.activeRequests.size;
1323
+ }
1324
+ /**
1325
+ * 获取当前活跃请求信息
1326
+ */
1327
+ getActiveRequests() {
1328
+ return Array.from(this.activeRequests.values());
1329
+ }
1167
1330
  handleRequest = async (ctx) => {
1168
1331
  const { moduleName, actionName } = ctx.req.param();
1169
1332
  const handler = this.getActionHandler(moduleName, actionName);
1333
+ const requestId = crypto.randomUUID();
1334
+ const startTime = Date.now();
1335
+ if (this.status === "shutting_down") {
1336
+ return ctx.json({
1337
+ success: false,
1338
+ error: "Service is shutting down"
1339
+ }, 503);
1340
+ }
1170
1341
  try {
1171
1342
  const paramsText = await ctx.req.text();
1343
+ this.addActiveRequest(requestId, {
1344
+ id: requestId,
1345
+ moduleName,
1346
+ actionName,
1347
+ startTime,
1348
+ params: paramsText
1349
+ });
1172
1350
  const result = await handler.handle(paramsText);
1173
1351
  if (handler.metadata.stream) {
1174
1352
  const encoder = new TextEncoder();
1353
+ const microservice = this;
1175
1354
  const stream = new ReadableStream({
1176
1355
  async start(controller) {
1177
1356
  try {
@@ -1195,6 +1374,8 @@ Received SIGINT signal`);
1195
1374
  encoder.encode(ejson4__default.default.stringify(response) + "\n")
1196
1375
  );
1197
1376
  controller.close();
1377
+ } finally {
1378
+ microservice.removeActiveRequest(requestId);
1198
1379
  }
1199
1380
  }
1200
1381
  });
@@ -1206,8 +1387,10 @@ Received SIGINT signal`);
1206
1387
  }
1207
1388
  });
1208
1389
  }
1390
+ this.removeActiveRequest(requestId);
1209
1391
  return ctx.text(ejson4__default.default.stringify({ success: true, data: result }));
1210
1392
  } catch (error) {
1393
+ this.removeActiveRequest(requestId);
1211
1394
  return ctx.json({ success: false, error: error.message });
1212
1395
  }
1213
1396
  };
@@ -1223,28 +1406,6 @@ Received SIGINT signal`);
1223
1406
  await this.waitingInitialization;
1224
1407
  }
1225
1408
  };
1226
-
1227
- // utils/checker.ts
1228
- async function startCheck(checkers, pass) {
1229
- logger_default.info("[ \u9884\u68C0\u5F00\u59CB ]");
1230
- for (const [index, checker] of checkers.entries()) {
1231
- const seq = index + 1;
1232
- logger_default.info(`${seq}. ${checker.name}`);
1233
- try {
1234
- if (checker.skip) {
1235
- logger_default.warn(`${seq}. ${checker.name} [\u8DF3\u8FC7]`);
1236
- continue;
1237
- }
1238
- await checker.check();
1239
- logger_default.info(`${seq}. ${checker.name} [\u6210\u529F]`);
1240
- } catch (error) {
1241
- logger_default.error(`${seq}. ${checker.name} [\u5931\u8D25]`);
1242
- throw error;
1243
- }
1244
- }
1245
- logger_default.info("[ \u9884\u68C0\u5B8C\u6210 ]");
1246
- if (pass) await pass();
1247
- }
1248
1409
  var HonoTransport = class {
1249
1410
  constructor(url, stream, closeStream) {
1250
1411
  this.url = url;
@@ -1360,22 +1521,221 @@ var ModelContextProtocolPlugin = class extends Plugin {
1360
1521
  );
1361
1522
  };
1362
1523
  };
1524
+ var DEFAULT_FAVICON = /* @__PURE__ */ jsxRuntime.jsx(
1525
+ "link",
1526
+ {
1527
+ rel: "icon",
1528
+ href: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' width='100' height='100'><defs><linearGradient id='nodeGradient' x1='0%' y1='0%' x2='100%' y2='100%'><stop offset='0%' stop-color='%233498db'/><stop offset='100%' stop-color='%232980b9'/></linearGradient><linearGradient id='centerNodeGradient' x1='0%' y1='0%' x2='100%' y2='100%'><stop offset='0%' stop-color='%232ecc71'/><stop offset='100%' stop-color='%2327ae60'/></linearGradient></defs><circle cx='50' cy='50' r='45' fill='%23f5f7fa'/><path d='M30,30 L50,50' stroke='%23bdc3c7' stroke-width='2' stroke-linecap='round'/><path d='M70,30 L50,50' stroke='%23bdc3c7' stroke-width='2' stroke-linecap='round'/><path d='M30,70 L50,50' stroke='%23bdc3c7' stroke-width='2' stroke-linecap='round'/><path d='M70,70 L50,50' stroke='%23bdc3c7' stroke-width='2' stroke-linecap='round'/><polygon points='30,15 45,25 45,45 30,55 15,45 15,25' fill='url(%23nodeGradient)' stroke='%232980b9' stroke-width='1.5'/><polygon points='70,15 85,25 85,45 70,55 55,45 55,25' fill='url(%23nodeGradient)' stroke='%232980b9' stroke-width='1.5'/><polygon points='30,45 45,55 45,75 30,85 15,75 15,55' fill='url(%23nodeGradient)' stroke='%232980b9' stroke-width='1.5'/><polygon points='70,45 85,55 85,75 70,85 55,75 55,55' fill='url(%23nodeGradient)' stroke='%232980b9' stroke-width='1.5'/><polygon points='50,30 65,40 65,60 50,70 35,60 35,40' fill='url(%23centerNodeGradient)' stroke='%2327ae60' stroke-width='2'/><circle cx='30' cy='30' r='3' fill='%23ffffff'/><circle cx='70' cy='30' r='3' fill='%23ffffff'/><circle cx='30' cy='70' r='3' fill='%23ffffff'/><circle cx='70' cy='70' r='3' fill='%23ffffff'/><circle cx='50' cy='50' r='4' fill='%23ffffff'/></svg>",
1529
+ type: "image/svg+xml"
1530
+ }
1531
+ );
1532
+ var BaseLayout = (props = {
1533
+ title: "Microservice Template"
1534
+ }) => html.html`<!DOCTYPE html>
1535
+ <html>
1536
+ <head>
1537
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1538
+ <title>${props.title}</title>
1539
+ ${props.heads}
1540
+ </head>
1541
+ <body>
1542
+ ${props.children}
1543
+ </body>
1544
+ </html>`;
1545
+ var HtmxLayout = (props = {
1546
+ title: "Microservice Template"
1547
+ }) => BaseLayout({
1548
+ title: props.title,
1549
+ heads: html.html`
1550
+ <script src="https://unpkg.com/htmx.org@latest"></script>
1551
+ <script src="https://unpkg.com/hyperscript.org@latest"></script>
1552
+ <script src="https://cdn.tailwindcss.com"></script>
1553
+ ${props.favicon || DEFAULT_FAVICON}
1554
+ `,
1555
+ children: props.children
1556
+ });
1557
+ var InfoCard = ({
1558
+ icon,
1559
+ iconColor,
1560
+ bgColor,
1561
+ label,
1562
+ value
1563
+ }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${bgColor} p-4 rounded-lg`, children: [
1564
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center mb-2", children: [
1565
+ /* @__PURE__ */ jsxRuntime.jsx(
1566
+ "svg",
1567
+ {
1568
+ className: `w-5 h-5 ${iconColor} mr-2`,
1569
+ fill: "none",
1570
+ stroke: "currentColor",
1571
+ viewBox: "0 0 24 24",
1572
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1573
+ "path",
1574
+ {
1575
+ strokeLinecap: "round",
1576
+ strokeLinejoin: "round",
1577
+ strokeWidth: 2,
1578
+ d: icon
1579
+ }
1580
+ )
1581
+ }
1582
+ ),
1583
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-600", children: label })
1584
+ ] }),
1585
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-xl font-semibold text-gray-900`, children: value })
1586
+ ] });
1587
+ var getEnvironmentBadgeClass = (env) => {
1588
+ switch (env) {
1589
+ case "prod":
1590
+ return "bg-red-100 text-red-800";
1591
+ case "stg":
1592
+ return "bg-yellow-100 text-yellow-800";
1593
+ case "dev":
1594
+ default:
1595
+ return "bg-blue-100 text-blue-800";
1596
+ }
1597
+ };
1598
+ var ServiceInfoCards = ({
1599
+ serviceInfo
1600
+ }) => {
1601
+ const infoCards = [
1602
+ {
1603
+ icon: "M7 4V2a1 1 0 011-1h8a1 1 0 011 1v2m-9 0h10m-10 0a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 002-2V6a2 2 0 00-2-2",
1604
+ iconColor: "text-blue-600",
1605
+ bgColor: "bg-blue-50",
1606
+ label: "\u670D\u52A1\u540D\u79F0",
1607
+ value: serviceInfo.name
1608
+ },
1609
+ {
1610
+ icon: "M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1",
1611
+ iconColor: "text-orange-600",
1612
+ bgColor: "bg-orange-50",
1613
+ label: "\u670D\u52A1\u8DEF\u5F84",
1614
+ value: serviceInfo.prefix || "/",
1615
+ isMonospace: true
1616
+ },
1617
+ {
1618
+ icon: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z",
1619
+ iconColor: "text-green-600",
1620
+ bgColor: "bg-green-50",
1621
+ label: "\u8FD0\u884C\u73AF\u5883",
1622
+ value: /* @__PURE__ */ jsxRuntime.jsx(
1623
+ "span",
1624
+ {
1625
+ className: `px-2 py-1 rounded-full text-sm ${getEnvironmentBadgeClass(serviceInfo.env ?? "dev")}`,
1626
+ children: serviceInfo.env ?? "dev"
1627
+ }
1628
+ )
1629
+ },
1630
+ {
1631
+ icon: "M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z",
1632
+ iconColor: "text-purple-600",
1633
+ bgColor: "bg-purple-50",
1634
+ label: "\u7248\u672C\u53F7",
1635
+ value: serviceInfo.version || "unknown"
1636
+ }
1637
+ ];
1638
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-md p-6 mb-8", children: [
1639
+ /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-2xl font-semibold text-gray-800 mb-6 flex items-center", children: [
1640
+ /* @__PURE__ */ jsxRuntime.jsx(
1641
+ "svg",
1642
+ {
1643
+ className: "w-6 h-6 mr-2 text-blue-600",
1644
+ fill: "none",
1645
+ stroke: "currentColor",
1646
+ viewBox: "0 0 24 24",
1647
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1648
+ "path",
1649
+ {
1650
+ strokeLinecap: "round",
1651
+ strokeLinejoin: "round",
1652
+ strokeWidth: 2,
1653
+ d: "M13 10V3L4 14h7v7l9-11h-7z"
1654
+ }
1655
+ )
1656
+ }
1657
+ ),
1658
+ "\u670D\u52A1\u57FA\u672C\u4FE1\u606F"
1659
+ ] }),
1660
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-6", children: infoCards.map((card, index) => /* @__PURE__ */ jsxRuntime.jsx(InfoCard, { ...card }, index)) })
1661
+ ] });
1662
+ };
1663
+ var ServiceStatusPage = ({
1664
+ serviceInfo
1665
+ }) => {
1666
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen bg-gray-50 py-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-6xl mx-auto px-4", children: [
1667
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-8", children: /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl font-bold text-gray-900 mb-2", children: "Service Status" }) }),
1668
+ /* @__PURE__ */ jsxRuntime.jsx(ServiceInfoCards, { serviceInfo })
1669
+ ] }) });
1670
+ };
1671
+ var ServiceStatusPage_default = ServiceStatusPage;
1672
+
1673
+ // core/plugins/page/mod.ts
1674
+ var PageRenderPlugin = class extends Plugin {
1675
+ initialize = async (engine) => {
1676
+ const app = engine.getApp();
1677
+ app.get(`${engine.options.prefix}`, async (ctx) => {
1678
+ return ctx.html(HtmxLayout({
1679
+ title: engine.options.name,
1680
+ children: ServiceStatusPage_default({
1681
+ serviceInfo: {
1682
+ name: engine.options.name,
1683
+ prefix: engine.options.prefix,
1684
+ version: engine.options.version,
1685
+ env: engine.options.env,
1686
+ id: engine.serviceId,
1687
+ modules: engine.getModules(false)
1688
+ }
1689
+ })
1690
+ }));
1691
+ });
1692
+ logger_default.info(`PageRenderPlugin enabled`);
1693
+ };
1694
+ };
1695
+
1696
+ // utils/checker.ts
1697
+ async function startCheck(checkers, pass) {
1698
+ logger_default.info("[ \u9884\u68C0\u5F00\u59CB ]");
1699
+ for (const [index, checker] of checkers.entries()) {
1700
+ const seq = index + 1;
1701
+ logger_default.info(`${seq}. ${checker.name}`);
1702
+ try {
1703
+ if (checker.skip) {
1704
+ logger_default.warn(`${seq}. ${checker.name} [\u8DF3\u8FC7]`);
1705
+ continue;
1706
+ }
1707
+ await checker.check();
1708
+ logger_default.info(`${seq}. ${checker.name} [\u6210\u529F]`);
1709
+ } catch (error) {
1710
+ logger_default.error(`${seq}. ${checker.name} [\u5931\u8D25]`);
1711
+ throw error;
1712
+ }
1713
+ }
1714
+ logger_default.info("[ \u9884\u68C0\u5B8C\u6210 ]");
1715
+ if (pass) await pass();
1716
+ }
1363
1717
 
1364
1718
  Object.defineProperty(exports, "dayjs", {
1365
1719
  enumerable: true,
1366
1720
  get: function () { return dayjs__default.default; }
1367
1721
  });
1368
1722
  exports.Action = Action;
1723
+ exports.BaseLayout = BaseLayout;
1369
1724
  exports.CacheAdapter = CacheAdapter;
1725
+ exports.HtmxLayout = HtmxLayout;
1370
1726
  exports.MemoryCacheAdapter = MemoryCacheAdapter;
1371
1727
  exports.Microservice = Microservice;
1372
1728
  exports.ModelContextProtocolPlugin = ModelContextProtocolPlugin;
1373
1729
  exports.Module = Module;
1730
+ exports.Page = Page;
1731
+ exports.PageRenderPlugin = PageRenderPlugin;
1374
1732
  exports.Plugin = Plugin;
1375
1733
  exports.RedisCacheAdapter = RedisCacheAdapter;
1376
1734
  exports.Schedule = Schedule;
1377
1735
  exports.ScheduleMode = ScheduleMode;
1378
1736
  exports.ServiceContext = ServiceContext;
1737
+ exports.ServiceInfoCards = ServiceInfoCards;
1738
+ exports.ServiceStatusPage = ServiceStatusPage;
1379
1739
  exports.logger = logger_default;
1380
1740
  exports.startCheck = startCheck;
1381
1741
  Object.keys(zod).forEach(function (k) {