imean-service-engine 1.6.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/README.md +298 -0
- package/dist/mod.cjs +278 -27
- package/dist/mod.d.cts +46 -8
- package/dist/mod.d.ts +46 -8
- package/dist/mod.js +273 -28
- package/package.json +1 -1
package/README.md
CHANGED
@@ -18,6 +18,7 @@
|
|
18
18
|
- 支持双向通信
|
19
19
|
- 使用 Brotli 压缩,减少数据传输量
|
20
20
|
- 自动重连和心跳检测
|
21
|
+
- 🌐 内置 PageRenderPlugin 支持服务端渲染页面,集成 HTMX 和 Hyperscript
|
21
22
|
|
22
23
|
## TODOs
|
23
24
|
|
@@ -135,6 +136,273 @@ const found = await client.users.getUser(user.id);
|
|
135
136
|
|
136
137
|
## 高级特性
|
137
138
|
|
139
|
+
### PageRenderPlugin - 服务端渲染页面
|
140
|
+
|
141
|
+
PageRenderPlugin 为微服务框架提供了服务端渲染页面的能力,集成了 HTMX 和 Hyperscript,让你可以轻松构建现代化的 Web 应用。
|
142
|
+
|
143
|
+
#### 启用 PageRenderPlugin
|
144
|
+
|
145
|
+
```typescript
|
146
|
+
import { Microservice, PageRenderPlugin } from "imean-service-engine";
|
147
|
+
|
148
|
+
const service = new Microservice({
|
149
|
+
modules: [UserService],
|
150
|
+
plugins: [new PageRenderPlugin()],
|
151
|
+
});
|
152
|
+
```
|
153
|
+
|
154
|
+
#### 使用 @Page 装饰器
|
155
|
+
|
156
|
+
使用 `@Page` 装饰器可以将模块方法暴露为 Web 页面:
|
157
|
+
|
158
|
+
```typescript
|
159
|
+
import { Page, HtmxLayout } from "imean-service-engine";
|
160
|
+
|
161
|
+
@Module("web")
|
162
|
+
class WebService {
|
163
|
+
@Page({
|
164
|
+
path: "/greeting",
|
165
|
+
method: "get",
|
166
|
+
description: "问候页面",
|
167
|
+
})
|
168
|
+
greetingPage(ctx: Context) {
|
169
|
+
return (
|
170
|
+
<HtmxLayout title="问候页面">
|
171
|
+
<div class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 p-8">
|
172
|
+
<div class="max-w-4xl mx-auto">
|
173
|
+
<h1 class="text-4xl font-bold text-center text-gray-800 mb-8">
|
174
|
+
HTMX 交互示例
|
175
|
+
</h1>
|
176
|
+
<div class="bg-white rounded-lg shadow-lg p-6">
|
177
|
+
<h2 class="text-2xl font-semibold mb-4 text-gray-700">问候语</h2>
|
178
|
+
<div id="greeting" class="text-xl p-4 bg-blue-50 rounded-lg">
|
179
|
+
欢迎使用微服务框架!
|
180
|
+
</div>
|
181
|
+
<button
|
182
|
+
class="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
183
|
+
hx-post="/api/greeting"
|
184
|
+
hx-target="#greeting"
|
185
|
+
hx-swap="innerHTML"
|
186
|
+
>
|
187
|
+
更新问候语
|
188
|
+
</button>
|
189
|
+
</div>
|
190
|
+
</div>
|
191
|
+
</div>
|
192
|
+
</HtmxLayout>
|
193
|
+
);
|
194
|
+
}
|
195
|
+
|
196
|
+
@Page({
|
197
|
+
path: "/greeting",
|
198
|
+
method: "post",
|
199
|
+
description: "更新问候语",
|
200
|
+
})
|
201
|
+
updateGreeting(ctx: Context) {
|
202
|
+
return "你好,世界!当前时间:" + new Date().toLocaleString();
|
203
|
+
}
|
204
|
+
}
|
205
|
+
```
|
206
|
+
|
207
|
+
#### JSX 配置
|
208
|
+
|
209
|
+
要使用 JSX 语法,需要在 `tsconfig.json` 中配置:
|
210
|
+
|
211
|
+
```json
|
212
|
+
{
|
213
|
+
"compilerOptions": {
|
214
|
+
"jsx": "react-jsx",
|
215
|
+
"jsxImportSource": "hono/jsx"
|
216
|
+
}
|
217
|
+
}
|
218
|
+
```
|
219
|
+
|
220
|
+
#### HtmxLayout 组件
|
221
|
+
|
222
|
+
`HtmxLayout` 提供了预配置的页面布局,包含:
|
223
|
+
|
224
|
+
- HTMX 库(最新版本)
|
225
|
+
- Hyperscript 库(最新版本)
|
226
|
+
- Tailwind CSS(CDN 版本)
|
227
|
+
- 响应式设计支持
|
228
|
+
- 默认图标
|
229
|
+
|
230
|
+
```typescript
|
231
|
+
import { HtmxLayout } from "imean-service-engine";
|
232
|
+
|
233
|
+
// 基本用法
|
234
|
+
const page = (
|
235
|
+
<HtmxLayout title="我的页面">
|
236
|
+
<div>页面内容</div>
|
237
|
+
</HtmxLayout>
|
238
|
+
);
|
239
|
+
|
240
|
+
// 自定义图标
|
241
|
+
const pageWithCustomIcon = (
|
242
|
+
<HtmxLayout title="我的页面" favicon={<link rel="icon" href="/custom-icon.ico" />}>
|
243
|
+
<div>页面内容</div>
|
244
|
+
</HtmxLayout>
|
245
|
+
);
|
246
|
+
```
|
247
|
+
|
248
|
+
#### BaseLayout 组件
|
249
|
+
|
250
|
+
如果你不想使用 HTMX 和 Hyperscript,而是想使用其他前端框架(如 React、Vue 等),可以使用 `BaseLayout` 组件:
|
251
|
+
|
252
|
+
```typescript
|
253
|
+
import { BaseLayout } from "imean-service-engine";
|
254
|
+
|
255
|
+
// 使用 BaseLayout 自定义页面
|
256
|
+
const customPage = (
|
257
|
+
<BaseLayout title="自定义页面">
|
258
|
+
<div>页面内容</div>
|
259
|
+
</BaseLayout>
|
260
|
+
);
|
261
|
+
|
262
|
+
// 自定义头部内容
|
263
|
+
const pageWithCustomHead = (
|
264
|
+
<BaseLayout
|
265
|
+
title="自定义页面"
|
266
|
+
heads={
|
267
|
+
<>
|
268
|
+
<link rel="stylesheet" href="/custom.css" />
|
269
|
+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
270
|
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
271
|
+
</>
|
272
|
+
}
|
273
|
+
>
|
274
|
+
<div id="root">React 应用将在这里渲染</div>
|
275
|
+
</BaseLayout>
|
276
|
+
);
|
277
|
+
```
|
278
|
+
|
279
|
+
`BaseLayout` 提供:
|
280
|
+
- 基本的 HTML 结构
|
281
|
+
- 可自定义的 `<head>` 内容
|
282
|
+
- 可自定义的页面标题
|
283
|
+
- 可自定义的图标
|
284
|
+
|
285
|
+
#### HTMX 交互示例
|
286
|
+
|
287
|
+
结合 HTMX 可以实现丰富的交互效果:
|
288
|
+
|
289
|
+
```typescript
|
290
|
+
@Page({
|
291
|
+
path: "/users",
|
292
|
+
method: "get",
|
293
|
+
description: "用户列表页面",
|
294
|
+
})
|
295
|
+
usersPage(ctx: Context) {
|
296
|
+
return (
|
297
|
+
<HtmxLayout title="用户管理">
|
298
|
+
<div class="container mx-auto p-8">
|
299
|
+
<h1 class="text-3xl font-bold mb-6">用户管理</h1>
|
300
|
+
|
301
|
+
{/* 用户列表 */}
|
302
|
+
<div
|
303
|
+
id="user-list"
|
304
|
+
hx-get="/api/users/list"
|
305
|
+
hx-trigger="load"
|
306
|
+
>
|
307
|
+
加载中...
|
308
|
+
</div>
|
309
|
+
|
310
|
+
{/* 添加用户表单 */}
|
311
|
+
<div class="mt-8 bg-white rounded-lg shadow p-6">
|
312
|
+
<h2 class="text-xl font-semibold mb-4">添加新用户</h2>
|
313
|
+
<form
|
314
|
+
hx-post="/api/users/add"
|
315
|
+
hx-target="#user-list"
|
316
|
+
hx-swap="outerHTML"
|
317
|
+
>
|
318
|
+
<div class="grid grid-cols-2 gap-4">
|
319
|
+
<input
|
320
|
+
type="text"
|
321
|
+
name="name"
|
322
|
+
placeholder="姓名"
|
323
|
+
class="px-3 py-2 border rounded-md"
|
324
|
+
required
|
325
|
+
/>
|
326
|
+
<input
|
327
|
+
type="number"
|
328
|
+
name="age"
|
329
|
+
placeholder="年龄"
|
330
|
+
class="px-3 py-2 border rounded-md"
|
331
|
+
required
|
332
|
+
/>
|
333
|
+
</div>
|
334
|
+
<button
|
335
|
+
type="submit"
|
336
|
+
class="mt-4 px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
|
337
|
+
>
|
338
|
+
添加用户
|
339
|
+
</button>
|
340
|
+
</form>
|
341
|
+
</div>
|
342
|
+
</div>
|
343
|
+
</HtmxLayout>
|
344
|
+
);
|
345
|
+
}
|
346
|
+
```
|
347
|
+
|
348
|
+
#### Hyperscript 增强交互
|
349
|
+
|
350
|
+
使用 Hyperscript 可以实现更复杂的客户端逻辑:
|
351
|
+
|
352
|
+
```typescript
|
353
|
+
// 带加载状态的按钮
|
354
|
+
<button
|
355
|
+
hx-post="/api/users/refresh"
|
356
|
+
hx-target="#user-list"
|
357
|
+
hx-swap="innerHTML"
|
358
|
+
_="on htmx:beforeRequest hide #button-text then show #loading-spinner end
|
359
|
+
on htmx:afterRequest hide #loading-spinner then show #button-text end"
|
360
|
+
>
|
361
|
+
<span id="loading-spinner" class="htmx-indicator">
|
362
|
+
<svg class="animate-spin h-4 w-4" viewBox="0 0 24 24">
|
363
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
364
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
365
|
+
</svg>
|
366
|
+
加载中...
|
367
|
+
</span>
|
368
|
+
<span id="button-text">刷新用户列表</span>
|
369
|
+
</button>
|
370
|
+
```
|
371
|
+
|
372
|
+
#### 服务状态页面
|
373
|
+
|
374
|
+
PageRenderPlugin 自动在服务根路径(`/api`)提供服务的状态页面,显示:
|
375
|
+
|
376
|
+
- 服务基本信息(名称、版本、环境)
|
377
|
+
- 模块列表和 API 端点
|
378
|
+
- 服务健康状态
|
379
|
+
|
380
|
+
访问 `http://localhost:3000/api` 即可查看服务状态页面。
|
381
|
+
|
382
|
+
#### 最佳实践
|
383
|
+
|
384
|
+
1. **页面组织**:将页面逻辑与 API 逻辑分离
|
385
|
+
2. **组件复用**:使用 HtmxLayout 确保一致的页面结构
|
386
|
+
3. **渐进增强**:优先使用 HTMX 实现交互,必要时使用 Hyperscript
|
387
|
+
4. **响应式设计**:利用 Tailwind CSS 构建响应式界面
|
388
|
+
5. **布局选择**:
|
389
|
+
- 使用 `HtmxLayout` 进行快速原型开发和简单交互
|
390
|
+
- 使用 `BaseLayout` 集成复杂的前端框架(React、Vue 等)
|
391
|
+
- 根据项目需求选择合适的布局组件
|
392
|
+
|
393
|
+
```typescript
|
394
|
+
// 推荐的目录结构
|
395
|
+
src/
|
396
|
+
├── pages/ # 页面组件
|
397
|
+
│ ├── users.tsx
|
398
|
+
│ └── dashboard.tsx
|
399
|
+
├── services/ # 服务模块
|
400
|
+
│ ├── user.ts
|
401
|
+
│ └── web.ts
|
402
|
+
└── layouts/ # 自定义布局
|
403
|
+
└── admin.tsx
|
404
|
+
```
|
405
|
+
|
138
406
|
### 幂等性和重试机制
|
139
407
|
|
140
408
|
框架提供了智能的重试机制,但仅对标记为幂等的操作生效:
|
@@ -180,6 +448,35 @@ interface ActionOptions {
|
|
180
448
|
}
|
181
449
|
```
|
182
450
|
|
451
|
+
#### @Page(options: PageOptions)
|
452
|
+
|
453
|
+
定义一个页面路由(需要启用 PageRenderPlugin)。
|
454
|
+
|
455
|
+
```typescript
|
456
|
+
interface PageOptions {
|
457
|
+
method: "get" | "post" | "put" | "delete" | "patch" | "options";
|
458
|
+
path: string;
|
459
|
+
description?: string;
|
460
|
+
}
|
461
|
+
```
|
462
|
+
|
463
|
+
示例:
|
464
|
+
|
465
|
+
```typescript
|
466
|
+
@Page({
|
467
|
+
path: "/dashboard",
|
468
|
+
method: "get",
|
469
|
+
description: "仪表板页面",
|
470
|
+
})
|
471
|
+
dashboardPage(ctx: Context) {
|
472
|
+
return (
|
473
|
+
<HtmxLayout title="仪表板">
|
474
|
+
<div>仪表板内容</div>
|
475
|
+
</HtmxLayout>
|
476
|
+
);
|
477
|
+
}
|
478
|
+
```
|
479
|
+
|
183
480
|
### Microservice
|
184
481
|
|
185
482
|
#### constructor(options: MicroserviceOptions)
|
@@ -190,6 +487,7 @@ interface ActionOptions {
|
|
190
487
|
interface MicroserviceOptions {
|
191
488
|
modules: (new () => any)[]; // 模块类数组
|
192
489
|
prefix?: string; // API 前缀,默认为 "/api"
|
490
|
+
plugins?: Plugin[]; // 插件数组,如 PageRenderPlugin
|
193
491
|
}
|
194
492
|
```
|
195
493
|
|
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;
|
@@ -782,6 +841,7 @@ var Microservice = class {
|
|
782
841
|
statisticsTimer;
|
783
842
|
wsHandler;
|
784
843
|
actionHandlers = /* @__PURE__ */ new Map();
|
844
|
+
pageHandlers = /* @__PURE__ */ new Map();
|
785
845
|
activeRequests = /* @__PURE__ */ new Map();
|
786
846
|
status = "running";
|
787
847
|
modules = /* @__PURE__ */ new Map();
|
@@ -791,6 +851,7 @@ var Microservice = class {
|
|
791
851
|
serviceId;
|
792
852
|
constructor(options) {
|
793
853
|
this.app = new hono.Hono();
|
854
|
+
this.app.use(timing.timing());
|
794
855
|
this.nodeWebSocket = nodeWs.createNodeWebSocket({ app: this.app });
|
795
856
|
this.serviceId = crypto.randomUUID();
|
796
857
|
this.options = {
|
@@ -829,6 +890,11 @@ var Microservice = class {
|
|
829
890
|
}
|
830
891
|
await this.registerService(true);
|
831
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
|
+
});
|
832
898
|
}
|
833
899
|
async initModules() {
|
834
900
|
for (const ModuleClass of this.options.modules) {
|
@@ -843,6 +909,7 @@ var Microservice = class {
|
|
843
909
|
logger_default.info(`[ \u6CE8\u518C\u6A21\u5757 ] ${moduleName} ${metadata.options.description}`);
|
844
910
|
this.modules.set(moduleName, moduleInstance);
|
845
911
|
const actions = getActionMetadata(ModuleClass.prototype);
|
912
|
+
const pages = getPageMetadata(ModuleClass.prototype);
|
846
913
|
for (const [actionName, actionMetadata] of Object.entries(actions)) {
|
847
914
|
const handler = new ActionHandler(
|
848
915
|
moduleInstance,
|
@@ -856,6 +923,18 @@ var Microservice = class {
|
|
856
923
|
`[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description} ${actionMetadata.mcp ? "MCP:" + actionMetadata.mcp?.type : ""}`
|
857
924
|
);
|
858
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
|
+
}
|
859
938
|
const schedules = getScheduleMetadata(ModuleClass.prototype);
|
860
939
|
if (schedules && Object.keys(schedules).length > 0) {
|
861
940
|
if (!this.scheduler && this.etcdClient) {
|
@@ -885,11 +964,6 @@ var Microservice = class {
|
|
885
964
|
initRoutes() {
|
886
965
|
const startTime = Date.now();
|
887
966
|
const prefix = this.options.prefix || "/api";
|
888
|
-
this.app.get(prefix, (ctx) => {
|
889
|
-
const name = this.options.name ?? "Microservice";
|
890
|
-
const version = this.options.version ?? "1.0.0";
|
891
|
-
return ctx.text(`${name} is ${this.status}. version: ${version}`);
|
892
|
-
});
|
893
967
|
this.app.get(`${prefix}/health`, (ctx) => {
|
894
968
|
return ctx.json({
|
895
969
|
status: "ok",
|
@@ -1332,28 +1406,6 @@ Received SIGTERM signal`);
|
|
1332
1406
|
await this.waitingInitialization;
|
1333
1407
|
}
|
1334
1408
|
};
|
1335
|
-
|
1336
|
-
// utils/checker.ts
|
1337
|
-
async function startCheck(checkers, pass) {
|
1338
|
-
logger_default.info("[ \u9884\u68C0\u5F00\u59CB ]");
|
1339
|
-
for (const [index, checker] of checkers.entries()) {
|
1340
|
-
const seq = index + 1;
|
1341
|
-
logger_default.info(`${seq}. ${checker.name}`);
|
1342
|
-
try {
|
1343
|
-
if (checker.skip) {
|
1344
|
-
logger_default.warn(`${seq}. ${checker.name} [\u8DF3\u8FC7]`);
|
1345
|
-
continue;
|
1346
|
-
}
|
1347
|
-
await checker.check();
|
1348
|
-
logger_default.info(`${seq}. ${checker.name} [\u6210\u529F]`);
|
1349
|
-
} catch (error) {
|
1350
|
-
logger_default.error(`${seq}. ${checker.name} [\u5931\u8D25]`);
|
1351
|
-
throw error;
|
1352
|
-
}
|
1353
|
-
}
|
1354
|
-
logger_default.info("[ \u9884\u68C0\u5B8C\u6210 ]");
|
1355
|
-
if (pass) await pass();
|
1356
|
-
}
|
1357
1409
|
var HonoTransport = class {
|
1358
1410
|
constructor(url, stream, closeStream) {
|
1359
1411
|
this.url = url;
|
@@ -1469,22 +1521,221 @@ var ModelContextProtocolPlugin = class extends Plugin {
|
|
1469
1521
|
);
|
1470
1522
|
};
|
1471
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
|
+
}
|
1472
1717
|
|
1473
1718
|
Object.defineProperty(exports, "dayjs", {
|
1474
1719
|
enumerable: true,
|
1475
1720
|
get: function () { return dayjs__default.default; }
|
1476
1721
|
});
|
1477
1722
|
exports.Action = Action;
|
1723
|
+
exports.BaseLayout = BaseLayout;
|
1478
1724
|
exports.CacheAdapter = CacheAdapter;
|
1725
|
+
exports.HtmxLayout = HtmxLayout;
|
1479
1726
|
exports.MemoryCacheAdapter = MemoryCacheAdapter;
|
1480
1727
|
exports.Microservice = Microservice;
|
1481
1728
|
exports.ModelContextProtocolPlugin = ModelContextProtocolPlugin;
|
1482
1729
|
exports.Module = Module;
|
1730
|
+
exports.Page = Page;
|
1731
|
+
exports.PageRenderPlugin = PageRenderPlugin;
|
1483
1732
|
exports.Plugin = Plugin;
|
1484
1733
|
exports.RedisCacheAdapter = RedisCacheAdapter;
|
1485
1734
|
exports.Schedule = Schedule;
|
1486
1735
|
exports.ScheduleMode = ScheduleMode;
|
1487
1736
|
exports.ServiceContext = ServiceContext;
|
1737
|
+
exports.ServiceInfoCards = ServiceInfoCards;
|
1738
|
+
exports.ServiceStatusPage = ServiceStatusPage;
|
1488
1739
|
exports.logger = logger_default;
|
1489
1740
|
exports.startCheck = startCheck;
|
1490
1741
|
Object.keys(zod).forEach(function (k) {
|
package/dist/mod.d.cts
CHANGED
@@ -6,6 +6,8 @@ import { Lease, Etcd3 } from 'etcd3';
|
|
6
6
|
import { Hono } from 'hono';
|
7
7
|
export { default as dayjs } from 'dayjs';
|
8
8
|
import winston from 'winston';
|
9
|
+
import * as hono_utils_html from 'hono/utils/html';
|
10
|
+
import * as hono_jsx_jsx_dev_runtime from 'hono/jsx/jsx-dev-runtime';
|
9
11
|
|
10
12
|
declare abstract class CacheAdapter {
|
11
13
|
abstract get(key: string): Promise<any>;
|
@@ -39,6 +41,14 @@ interface ActionOptions {
|
|
39
41
|
stream?: boolean;
|
40
42
|
mcp?: McpOptions;
|
41
43
|
}
|
44
|
+
interface PageOptions {
|
45
|
+
method: "get" | "post" | "put" | "delete" | "patch" | "options";
|
46
|
+
path: string;
|
47
|
+
description?: string;
|
48
|
+
}
|
49
|
+
interface PageMetadata extends PageOptions {
|
50
|
+
name: string;
|
51
|
+
}
|
42
52
|
interface ActionMetadata extends ActionOptions {
|
43
53
|
name: string;
|
44
54
|
idempotence: boolean;
|
@@ -226,6 +236,7 @@ declare class Microservice {
|
|
226
236
|
private statisticsTimer?;
|
227
237
|
private wsHandler?;
|
228
238
|
private actionHandlers;
|
239
|
+
private pageHandlers;
|
229
240
|
private activeRequests;
|
230
241
|
private status;
|
231
242
|
modules: Map<string, ModuleInfo>;
|
@@ -304,13 +315,6 @@ declare class Microservice {
|
|
304
315
|
init(): Promise<void>;
|
305
316
|
}
|
306
317
|
|
307
|
-
interface PreStartChecker {
|
308
|
-
name: string;
|
309
|
-
check: () => Promise<void> | void;
|
310
|
-
skip?: boolean;
|
311
|
-
}
|
312
|
-
declare function startCheck(checkers: PreStartChecker[], pass?: () => void | Promise<void>): Promise<void>;
|
313
|
-
|
314
318
|
declare class ModelContextProtocolPlugin extends Plugin {
|
315
319
|
private mcpServer;
|
316
320
|
private transports;
|
@@ -318,6 +322,35 @@ declare class ModelContextProtocolPlugin extends Plugin {
|
|
318
322
|
initialize: (engine: Microservice) => Promise<void>;
|
319
323
|
}
|
320
324
|
|
325
|
+
declare const BaseLayout: (props?: {
|
326
|
+
children?: any;
|
327
|
+
title?: string;
|
328
|
+
heads?: any;
|
329
|
+
}) => hono_utils_html.HtmlEscapedString | Promise<hono_utils_html.HtmlEscapedString>;
|
330
|
+
declare const HtmxLayout: (props?: {
|
331
|
+
children?: any;
|
332
|
+
title: string;
|
333
|
+
favicon?: any;
|
334
|
+
}) => hono_utils_html.HtmlEscapedString | Promise<hono_utils_html.HtmlEscapedString>;
|
335
|
+
|
336
|
+
declare const ServiceInfoCards: ({ serviceInfo, }: {
|
337
|
+
serviceInfo: ServiceInfo;
|
338
|
+
}) => hono_jsx_jsx_dev_runtime.JSX.Element;
|
339
|
+
declare const ServiceStatusPage: ({ serviceInfo, }: {
|
340
|
+
serviceInfo: ServiceInfo;
|
341
|
+
}) => hono_jsx_jsx_dev_runtime.JSX.Element;
|
342
|
+
|
343
|
+
declare class PageRenderPlugin extends Plugin {
|
344
|
+
initialize: (engine: Microservice) => Promise<void>;
|
345
|
+
}
|
346
|
+
|
347
|
+
interface PreStartChecker {
|
348
|
+
name: string;
|
349
|
+
check: () => Promise<void> | void;
|
350
|
+
skip?: boolean;
|
351
|
+
}
|
352
|
+
declare function startCheck(checkers: PreStartChecker[], pass?: () => void | Promise<void>): Promise<void>;
|
353
|
+
|
321
354
|
/**
|
322
355
|
* 用于给微服务模块方法添加的注解
|
323
356
|
*/
|
@@ -330,6 +363,11 @@ declare function Action(options: ActionOptions): Function;
|
|
330
363
|
*/
|
331
364
|
declare function Module(name: string, options?: ModuleOptions): Function;
|
332
365
|
|
366
|
+
/**
|
367
|
+
* 用于给微服务模块方法添加的页面注解
|
368
|
+
*/
|
369
|
+
declare function Page(options: PageOptions): Function;
|
370
|
+
|
333
371
|
/**
|
334
372
|
* 用于给微服务模块方法添加的调度注解
|
335
373
|
*/
|
@@ -337,4 +375,4 @@ declare function Schedule(options: ScheduleOptions): Function;
|
|
337
375
|
|
338
376
|
declare const logger: winston.Logger;
|
339
377
|
|
340
|
-
export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type CleanupHook, type EtcdConfig, type EventServiceInfo, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, ModelContextProtocolPlugin, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Plugin, type PreStartChecker, RedisCacheAdapter, type RequestInfo, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
|
378
|
+
export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, BaseLayout, CacheAdapter, type CacheFn, type CleanupHook, type EtcdConfig, type EventServiceInfo, HtmxLayout, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, ModelContextProtocolPlugin, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Page, type PageMetadata, type PageOptions, PageRenderPlugin, Plugin, type PreStartChecker, RedisCacheAdapter, type RequestInfo, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, ServiceInfoCards, type ServiceStats, ServiceStatusPage, type StatisticsEvent, type StreamResponse, logger, startCheck };
|
package/dist/mod.d.ts
CHANGED
@@ -6,6 +6,8 @@ import { Lease, Etcd3 } from 'etcd3';
|
|
6
6
|
import { Hono } from 'hono';
|
7
7
|
export { default as dayjs } from 'dayjs';
|
8
8
|
import winston from 'winston';
|
9
|
+
import * as hono_utils_html from 'hono/utils/html';
|
10
|
+
import * as hono_jsx_jsx_dev_runtime from 'hono/jsx/jsx-dev-runtime';
|
9
11
|
|
10
12
|
declare abstract class CacheAdapter {
|
11
13
|
abstract get(key: string): Promise<any>;
|
@@ -39,6 +41,14 @@ interface ActionOptions {
|
|
39
41
|
stream?: boolean;
|
40
42
|
mcp?: McpOptions;
|
41
43
|
}
|
44
|
+
interface PageOptions {
|
45
|
+
method: "get" | "post" | "put" | "delete" | "patch" | "options";
|
46
|
+
path: string;
|
47
|
+
description?: string;
|
48
|
+
}
|
49
|
+
interface PageMetadata extends PageOptions {
|
50
|
+
name: string;
|
51
|
+
}
|
42
52
|
interface ActionMetadata extends ActionOptions {
|
43
53
|
name: string;
|
44
54
|
idempotence: boolean;
|
@@ -226,6 +236,7 @@ declare class Microservice {
|
|
226
236
|
private statisticsTimer?;
|
227
237
|
private wsHandler?;
|
228
238
|
private actionHandlers;
|
239
|
+
private pageHandlers;
|
229
240
|
private activeRequests;
|
230
241
|
private status;
|
231
242
|
modules: Map<string, ModuleInfo>;
|
@@ -304,13 +315,6 @@ declare class Microservice {
|
|
304
315
|
init(): Promise<void>;
|
305
316
|
}
|
306
317
|
|
307
|
-
interface PreStartChecker {
|
308
|
-
name: string;
|
309
|
-
check: () => Promise<void> | void;
|
310
|
-
skip?: boolean;
|
311
|
-
}
|
312
|
-
declare function startCheck(checkers: PreStartChecker[], pass?: () => void | Promise<void>): Promise<void>;
|
313
|
-
|
314
318
|
declare class ModelContextProtocolPlugin extends Plugin {
|
315
319
|
private mcpServer;
|
316
320
|
private transports;
|
@@ -318,6 +322,35 @@ declare class ModelContextProtocolPlugin extends Plugin {
|
|
318
322
|
initialize: (engine: Microservice) => Promise<void>;
|
319
323
|
}
|
320
324
|
|
325
|
+
declare const BaseLayout: (props?: {
|
326
|
+
children?: any;
|
327
|
+
title?: string;
|
328
|
+
heads?: any;
|
329
|
+
}) => hono_utils_html.HtmlEscapedString | Promise<hono_utils_html.HtmlEscapedString>;
|
330
|
+
declare const HtmxLayout: (props?: {
|
331
|
+
children?: any;
|
332
|
+
title: string;
|
333
|
+
favicon?: any;
|
334
|
+
}) => hono_utils_html.HtmlEscapedString | Promise<hono_utils_html.HtmlEscapedString>;
|
335
|
+
|
336
|
+
declare const ServiceInfoCards: ({ serviceInfo, }: {
|
337
|
+
serviceInfo: ServiceInfo;
|
338
|
+
}) => hono_jsx_jsx_dev_runtime.JSX.Element;
|
339
|
+
declare const ServiceStatusPage: ({ serviceInfo, }: {
|
340
|
+
serviceInfo: ServiceInfo;
|
341
|
+
}) => hono_jsx_jsx_dev_runtime.JSX.Element;
|
342
|
+
|
343
|
+
declare class PageRenderPlugin extends Plugin {
|
344
|
+
initialize: (engine: Microservice) => Promise<void>;
|
345
|
+
}
|
346
|
+
|
347
|
+
interface PreStartChecker {
|
348
|
+
name: string;
|
349
|
+
check: () => Promise<void> | void;
|
350
|
+
skip?: boolean;
|
351
|
+
}
|
352
|
+
declare function startCheck(checkers: PreStartChecker[], pass?: () => void | Promise<void>): Promise<void>;
|
353
|
+
|
321
354
|
/**
|
322
355
|
* 用于给微服务模块方法添加的注解
|
323
356
|
*/
|
@@ -330,6 +363,11 @@ declare function Action(options: ActionOptions): Function;
|
|
330
363
|
*/
|
331
364
|
declare function Module(name: string, options?: ModuleOptions): Function;
|
332
365
|
|
366
|
+
/**
|
367
|
+
* 用于给微服务模块方法添加的页面注解
|
368
|
+
*/
|
369
|
+
declare function Page(options: PageOptions): Function;
|
370
|
+
|
333
371
|
/**
|
334
372
|
* 用于给微服务模块方法添加的调度注解
|
335
373
|
*/
|
@@ -337,4 +375,4 @@ declare function Schedule(options: ScheduleOptions): Function;
|
|
337
375
|
|
338
376
|
declare const logger: winston.Logger;
|
339
377
|
|
340
|
-
export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type CleanupHook, type EtcdConfig, type EventServiceInfo, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, ModelContextProtocolPlugin, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Plugin, type PreStartChecker, RedisCacheAdapter, type RequestInfo, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
|
378
|
+
export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, BaseLayout, CacheAdapter, type CacheFn, type CleanupHook, type EtcdConfig, type EventServiceInfo, HtmxLayout, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, ModelContextProtocolPlugin, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Page, type PageMetadata, type PageOptions, PageRenderPlugin, Plugin, type PreStartChecker, RedisCacheAdapter, type RequestInfo, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, ServiceInfoCards, type ServiceStats, ServiceStatusPage, type StatisticsEvent, type StreamResponse, logger, startCheck };
|
package/dist/mod.js
CHANGED
@@ -6,6 +6,7 @@ import { serve } from '@hono/node-server';
|
|
6
6
|
import { Etcd3 } from 'etcd3';
|
7
7
|
import fs from 'fs-extra';
|
8
8
|
import { Hono } from 'hono';
|
9
|
+
import { timing } from 'hono/timing';
|
9
10
|
import { trace, SpanStatusCode } from '@opentelemetry/api';
|
10
11
|
import winston, { format } from 'winston';
|
11
12
|
import prettier from 'prettier';
|
@@ -17,6 +18,8 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
17
18
|
import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
|
18
19
|
import { streamSSE } from 'hono/streaming';
|
19
20
|
import { ulid } from 'ulid';
|
21
|
+
import { html } from 'hono/html';
|
22
|
+
import { jsx, jsxs } from 'hono/jsx/jsx-runtime';
|
20
23
|
export { default as dayjs } from 'dayjs';
|
21
24
|
|
22
25
|
// mod.ts
|
@@ -586,6 +589,62 @@ var ActionHandler = class {
|
|
586
589
|
function isAsyncIterable(obj) {
|
587
590
|
return obj != null && typeof obj[Symbol.asyncIterator] === "function";
|
588
591
|
}
|
592
|
+
|
593
|
+
// decorators/page.ts
|
594
|
+
var PAGE_METADATA = Symbol("page:metadata");
|
595
|
+
function Page(options) {
|
596
|
+
return function(_target, context) {
|
597
|
+
const methodName = context.name;
|
598
|
+
context.addInitializer(function() {
|
599
|
+
const prototype = this.constructor.prototype;
|
600
|
+
const existingMetadata = prototype[PAGE_METADATA] || {};
|
601
|
+
existingMetadata[methodName] = {
|
602
|
+
name: methodName,
|
603
|
+
description: options.description || "",
|
604
|
+
method: options.method,
|
605
|
+
path: options.path
|
606
|
+
};
|
607
|
+
prototype[PAGE_METADATA] = existingMetadata;
|
608
|
+
});
|
609
|
+
};
|
610
|
+
}
|
611
|
+
function getPageMetadata(target) {
|
612
|
+
return target.constructor.prototype[PAGE_METADATA] ?? {};
|
613
|
+
}
|
614
|
+
var tracer3 = trace.getTracer("page-handler");
|
615
|
+
var PageHandler = class {
|
616
|
+
constructor(moduleInstance, options, moduleName) {
|
617
|
+
this.moduleInstance = moduleInstance;
|
618
|
+
this.options = options;
|
619
|
+
this.moduleName = moduleName;
|
620
|
+
}
|
621
|
+
async handle(ctx) {
|
622
|
+
return await tracer3.startActiveSpan(
|
623
|
+
`handle ${this.moduleName}.${this.options.name}`,
|
624
|
+
async (span) => {
|
625
|
+
span.setAttribute("module", this.moduleName);
|
626
|
+
span.setAttribute("page", this.options.name);
|
627
|
+
span.setAttribute("path", this.options.path);
|
628
|
+
try {
|
629
|
+
const result = await this.moduleInstance[this.options.name].apply(
|
630
|
+
this.moduleInstance,
|
631
|
+
[ctx]
|
632
|
+
);
|
633
|
+
return ctx.html(result);
|
634
|
+
} catch (error) {
|
635
|
+
span.recordException(error);
|
636
|
+
span.setStatus({
|
637
|
+
code: SpanStatusCode.ERROR,
|
638
|
+
message: error.message
|
639
|
+
});
|
640
|
+
throw error;
|
641
|
+
} finally {
|
642
|
+
span.end();
|
643
|
+
}
|
644
|
+
}
|
645
|
+
);
|
646
|
+
}
|
647
|
+
};
|
589
648
|
var WebSocketHandler = class {
|
590
649
|
constructor(microservice, options) {
|
591
650
|
this.microservice = microservice;
|
@@ -773,6 +832,7 @@ var Microservice = class {
|
|
773
832
|
statisticsTimer;
|
774
833
|
wsHandler;
|
775
834
|
actionHandlers = /* @__PURE__ */ new Map();
|
835
|
+
pageHandlers = /* @__PURE__ */ new Map();
|
776
836
|
activeRequests = /* @__PURE__ */ new Map();
|
777
837
|
status = "running";
|
778
838
|
modules = /* @__PURE__ */ new Map();
|
@@ -782,6 +842,7 @@ var Microservice = class {
|
|
782
842
|
serviceId;
|
783
843
|
constructor(options) {
|
784
844
|
this.app = new Hono();
|
845
|
+
this.app.use(timing());
|
785
846
|
this.nodeWebSocket = createNodeWebSocket({ app: this.app });
|
786
847
|
this.serviceId = crypto.randomUUID();
|
787
848
|
this.options = {
|
@@ -820,6 +881,11 @@ var Microservice = class {
|
|
820
881
|
}
|
821
882
|
await this.registerService(true);
|
822
883
|
await this.initPlugins();
|
884
|
+
this.app.get(this.options.prefix, (ctx) => {
|
885
|
+
const name = this.options.name ?? "Microservice";
|
886
|
+
const version = this.options.version ?? "1.0.0";
|
887
|
+
return ctx.text(`${name} is ${this.status}. version: ${version}`);
|
888
|
+
});
|
823
889
|
}
|
824
890
|
async initModules() {
|
825
891
|
for (const ModuleClass of this.options.modules) {
|
@@ -834,6 +900,7 @@ var Microservice = class {
|
|
834
900
|
logger_default.info(`[ \u6CE8\u518C\u6A21\u5757 ] ${moduleName} ${metadata.options.description}`);
|
835
901
|
this.modules.set(moduleName, moduleInstance);
|
836
902
|
const actions = getActionMetadata(ModuleClass.prototype);
|
903
|
+
const pages = getPageMetadata(ModuleClass.prototype);
|
837
904
|
for (const [actionName, actionMetadata] of Object.entries(actions)) {
|
838
905
|
const handler = new ActionHandler(
|
839
906
|
moduleInstance,
|
@@ -847,6 +914,18 @@ var Microservice = class {
|
|
847
914
|
`[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description} ${actionMetadata.mcp ? "MCP:" + actionMetadata.mcp?.type : ""}`
|
848
915
|
);
|
849
916
|
}
|
917
|
+
for (const [_, page] of Object.entries(pages)) {
|
918
|
+
const handler = new PageHandler(
|
919
|
+
moduleInstance,
|
920
|
+
page,
|
921
|
+
moduleName
|
922
|
+
);
|
923
|
+
this.pageHandlers.set(`${moduleName}.${page.name}`, handler);
|
924
|
+
this.app[page.method](`${this.options.prefix}${page.path}`, (ctx) => handler.handle(ctx));
|
925
|
+
logger_default.info(
|
926
|
+
`[ \u6CE8\u518C\u9875\u9762 ] ${moduleName}.${page.name} ${page.method.toUpperCase()} ${page.path} ${page.description}`
|
927
|
+
);
|
928
|
+
}
|
850
929
|
const schedules = getScheduleMetadata(ModuleClass.prototype);
|
851
930
|
if (schedules && Object.keys(schedules).length > 0) {
|
852
931
|
if (!this.scheduler && this.etcdClient) {
|
@@ -876,11 +955,6 @@ var Microservice = class {
|
|
876
955
|
initRoutes() {
|
877
956
|
const startTime = Date.now();
|
878
957
|
const prefix = this.options.prefix || "/api";
|
879
|
-
this.app.get(prefix, (ctx) => {
|
880
|
-
const name = this.options.name ?? "Microservice";
|
881
|
-
const version = this.options.version ?? "1.0.0";
|
882
|
-
return ctx.text(`${name} is ${this.status}. version: ${version}`);
|
883
|
-
});
|
884
958
|
this.app.get(`${prefix}/health`, (ctx) => {
|
885
959
|
return ctx.json({
|
886
960
|
status: "ok",
|
@@ -1323,28 +1397,6 @@ Received SIGTERM signal`);
|
|
1323
1397
|
await this.waitingInitialization;
|
1324
1398
|
}
|
1325
1399
|
};
|
1326
|
-
|
1327
|
-
// utils/checker.ts
|
1328
|
-
async function startCheck(checkers, pass) {
|
1329
|
-
logger_default.info("[ \u9884\u68C0\u5F00\u59CB ]");
|
1330
|
-
for (const [index, checker] of checkers.entries()) {
|
1331
|
-
const seq = index + 1;
|
1332
|
-
logger_default.info(`${seq}. ${checker.name}`);
|
1333
|
-
try {
|
1334
|
-
if (checker.skip) {
|
1335
|
-
logger_default.warn(`${seq}. ${checker.name} [\u8DF3\u8FC7]`);
|
1336
|
-
continue;
|
1337
|
-
}
|
1338
|
-
await checker.check();
|
1339
|
-
logger_default.info(`${seq}. ${checker.name} [\u6210\u529F]`);
|
1340
|
-
} catch (error) {
|
1341
|
-
logger_default.error(`${seq}. ${checker.name} [\u5931\u8D25]`);
|
1342
|
-
throw error;
|
1343
|
-
}
|
1344
|
-
}
|
1345
|
-
logger_default.info("[ \u9884\u68C0\u5B8C\u6210 ]");
|
1346
|
-
if (pass) await pass();
|
1347
|
-
}
|
1348
1400
|
var HonoTransport = class {
|
1349
1401
|
constructor(url, stream, closeStream) {
|
1350
1402
|
this.url = url;
|
@@ -1460,5 +1512,198 @@ var ModelContextProtocolPlugin = class extends Plugin {
|
|
1460
1512
|
);
|
1461
1513
|
};
|
1462
1514
|
};
|
1515
|
+
var DEFAULT_FAVICON = /* @__PURE__ */ jsx(
|
1516
|
+
"link",
|
1517
|
+
{
|
1518
|
+
rel: "icon",
|
1519
|
+
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>",
|
1520
|
+
type: "image/svg+xml"
|
1521
|
+
}
|
1522
|
+
);
|
1523
|
+
var BaseLayout = (props = {
|
1524
|
+
title: "Microservice Template"
|
1525
|
+
}) => html`<!DOCTYPE html>
|
1526
|
+
<html>
|
1527
|
+
<head>
|
1528
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
1529
|
+
<title>${props.title}</title>
|
1530
|
+
${props.heads}
|
1531
|
+
</head>
|
1532
|
+
<body>
|
1533
|
+
${props.children}
|
1534
|
+
</body>
|
1535
|
+
</html>`;
|
1536
|
+
var HtmxLayout = (props = {
|
1537
|
+
title: "Microservice Template"
|
1538
|
+
}) => BaseLayout({
|
1539
|
+
title: props.title,
|
1540
|
+
heads: html`
|
1541
|
+
<script src="https://unpkg.com/htmx.org@latest"></script>
|
1542
|
+
<script src="https://unpkg.com/hyperscript.org@latest"></script>
|
1543
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
1544
|
+
${props.favicon || DEFAULT_FAVICON}
|
1545
|
+
`,
|
1546
|
+
children: props.children
|
1547
|
+
});
|
1548
|
+
var InfoCard = ({
|
1549
|
+
icon,
|
1550
|
+
iconColor,
|
1551
|
+
bgColor,
|
1552
|
+
label,
|
1553
|
+
value
|
1554
|
+
}) => /* @__PURE__ */ jsxs("div", { className: `${bgColor} p-4 rounded-lg`, children: [
|
1555
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center mb-2", children: [
|
1556
|
+
/* @__PURE__ */ jsx(
|
1557
|
+
"svg",
|
1558
|
+
{
|
1559
|
+
className: `w-5 h-5 ${iconColor} mr-2`,
|
1560
|
+
fill: "none",
|
1561
|
+
stroke: "currentColor",
|
1562
|
+
viewBox: "0 0 24 24",
|
1563
|
+
children: /* @__PURE__ */ jsx(
|
1564
|
+
"path",
|
1565
|
+
{
|
1566
|
+
strokeLinecap: "round",
|
1567
|
+
strokeLinejoin: "round",
|
1568
|
+
strokeWidth: 2,
|
1569
|
+
d: icon
|
1570
|
+
}
|
1571
|
+
)
|
1572
|
+
}
|
1573
|
+
),
|
1574
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-600", children: label })
|
1575
|
+
] }),
|
1576
|
+
/* @__PURE__ */ jsx("p", { className: `text-xl font-semibold text-gray-900`, children: value })
|
1577
|
+
] });
|
1578
|
+
var getEnvironmentBadgeClass = (env) => {
|
1579
|
+
switch (env) {
|
1580
|
+
case "prod":
|
1581
|
+
return "bg-red-100 text-red-800";
|
1582
|
+
case "stg":
|
1583
|
+
return "bg-yellow-100 text-yellow-800";
|
1584
|
+
case "dev":
|
1585
|
+
default:
|
1586
|
+
return "bg-blue-100 text-blue-800";
|
1587
|
+
}
|
1588
|
+
};
|
1589
|
+
var ServiceInfoCards = ({
|
1590
|
+
serviceInfo
|
1591
|
+
}) => {
|
1592
|
+
const infoCards = [
|
1593
|
+
{
|
1594
|
+
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",
|
1595
|
+
iconColor: "text-blue-600",
|
1596
|
+
bgColor: "bg-blue-50",
|
1597
|
+
label: "\u670D\u52A1\u540D\u79F0",
|
1598
|
+
value: serviceInfo.name
|
1599
|
+
},
|
1600
|
+
{
|
1601
|
+
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",
|
1602
|
+
iconColor: "text-orange-600",
|
1603
|
+
bgColor: "bg-orange-50",
|
1604
|
+
label: "\u670D\u52A1\u8DEF\u5F84",
|
1605
|
+
value: serviceInfo.prefix || "/",
|
1606
|
+
isMonospace: true
|
1607
|
+
},
|
1608
|
+
{
|
1609
|
+
icon: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z",
|
1610
|
+
iconColor: "text-green-600",
|
1611
|
+
bgColor: "bg-green-50",
|
1612
|
+
label: "\u8FD0\u884C\u73AF\u5883",
|
1613
|
+
value: /* @__PURE__ */ jsx(
|
1614
|
+
"span",
|
1615
|
+
{
|
1616
|
+
className: `px-2 py-1 rounded-full text-sm ${getEnvironmentBadgeClass(serviceInfo.env ?? "dev")}`,
|
1617
|
+
children: serviceInfo.env ?? "dev"
|
1618
|
+
}
|
1619
|
+
)
|
1620
|
+
},
|
1621
|
+
{
|
1622
|
+
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",
|
1623
|
+
iconColor: "text-purple-600",
|
1624
|
+
bgColor: "bg-purple-50",
|
1625
|
+
label: "\u7248\u672C\u53F7",
|
1626
|
+
value: serviceInfo.version || "unknown"
|
1627
|
+
}
|
1628
|
+
];
|
1629
|
+
return /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-md p-6 mb-8", children: [
|
1630
|
+
/* @__PURE__ */ jsxs("h2", { className: "text-2xl font-semibold text-gray-800 mb-6 flex items-center", children: [
|
1631
|
+
/* @__PURE__ */ jsx(
|
1632
|
+
"svg",
|
1633
|
+
{
|
1634
|
+
className: "w-6 h-6 mr-2 text-blue-600",
|
1635
|
+
fill: "none",
|
1636
|
+
stroke: "currentColor",
|
1637
|
+
viewBox: "0 0 24 24",
|
1638
|
+
children: /* @__PURE__ */ jsx(
|
1639
|
+
"path",
|
1640
|
+
{
|
1641
|
+
strokeLinecap: "round",
|
1642
|
+
strokeLinejoin: "round",
|
1643
|
+
strokeWidth: 2,
|
1644
|
+
d: "M13 10V3L4 14h7v7l9-11h-7z"
|
1645
|
+
}
|
1646
|
+
)
|
1647
|
+
}
|
1648
|
+
),
|
1649
|
+
"\u670D\u52A1\u57FA\u672C\u4FE1\u606F"
|
1650
|
+
] }),
|
1651
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-6", children: infoCards.map((card, index) => /* @__PURE__ */ jsx(InfoCard, { ...card }, index)) })
|
1652
|
+
] });
|
1653
|
+
};
|
1654
|
+
var ServiceStatusPage = ({
|
1655
|
+
serviceInfo
|
1656
|
+
}) => {
|
1657
|
+
return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-gray-50 py-8", children: /* @__PURE__ */ jsxs("div", { className: "max-w-6xl mx-auto px-4", children: [
|
1658
|
+
/* @__PURE__ */ jsx("div", { className: "mb-8", children: /* @__PURE__ */ jsx("h1", { className: "text-4xl font-bold text-gray-900 mb-2", children: "Service Status" }) }),
|
1659
|
+
/* @__PURE__ */ jsx(ServiceInfoCards, { serviceInfo })
|
1660
|
+
] }) });
|
1661
|
+
};
|
1662
|
+
var ServiceStatusPage_default = ServiceStatusPage;
|
1663
|
+
|
1664
|
+
// core/plugins/page/mod.ts
|
1665
|
+
var PageRenderPlugin = class extends Plugin {
|
1666
|
+
initialize = async (engine) => {
|
1667
|
+
const app = engine.getApp();
|
1668
|
+
app.get(`${engine.options.prefix}`, async (ctx) => {
|
1669
|
+
return ctx.html(HtmxLayout({
|
1670
|
+
title: engine.options.name,
|
1671
|
+
children: ServiceStatusPage_default({
|
1672
|
+
serviceInfo: {
|
1673
|
+
name: engine.options.name,
|
1674
|
+
prefix: engine.options.prefix,
|
1675
|
+
version: engine.options.version,
|
1676
|
+
env: engine.options.env,
|
1677
|
+
id: engine.serviceId,
|
1678
|
+
modules: engine.getModules(false)
|
1679
|
+
}
|
1680
|
+
})
|
1681
|
+
}));
|
1682
|
+
});
|
1683
|
+
logger_default.info(`PageRenderPlugin enabled`);
|
1684
|
+
};
|
1685
|
+
};
|
1686
|
+
|
1687
|
+
// utils/checker.ts
|
1688
|
+
async function startCheck(checkers, pass) {
|
1689
|
+
logger_default.info("[ \u9884\u68C0\u5F00\u59CB ]");
|
1690
|
+
for (const [index, checker] of checkers.entries()) {
|
1691
|
+
const seq = index + 1;
|
1692
|
+
logger_default.info(`${seq}. ${checker.name}`);
|
1693
|
+
try {
|
1694
|
+
if (checker.skip) {
|
1695
|
+
logger_default.warn(`${seq}. ${checker.name} [\u8DF3\u8FC7]`);
|
1696
|
+
continue;
|
1697
|
+
}
|
1698
|
+
await checker.check();
|
1699
|
+
logger_default.info(`${seq}. ${checker.name} [\u6210\u529F]`);
|
1700
|
+
} catch (error) {
|
1701
|
+
logger_default.error(`${seq}. ${checker.name} [\u5931\u8D25]`);
|
1702
|
+
throw error;
|
1703
|
+
}
|
1704
|
+
}
|
1705
|
+
logger_default.info("[ \u9884\u68C0\u5B8C\u6210 ]");
|
1706
|
+
if (pass) await pass();
|
1707
|
+
}
|
1463
1708
|
|
1464
|
-
export { Action, CacheAdapter, MemoryCacheAdapter, Microservice, ModelContextProtocolPlugin, Module, Plugin, RedisCacheAdapter, Schedule, ScheduleMode, ServiceContext, logger_default as logger, startCheck };
|
1709
|
+
export { Action, BaseLayout, CacheAdapter, HtmxLayout, MemoryCacheAdapter, Microservice, ModelContextProtocolPlugin, Module, Page, PageRenderPlugin, Plugin, RedisCacheAdapter, Schedule, ScheduleMode, ServiceContext, ServiceInfoCards, ServiceStatusPage, logger_default as logger, startCheck };
|