chore: initialize SaaS order service

This commit is contained in:
2026-09-03 21:36:37 +08:00
commit 02a8975198
12 changed files with 5531 additions and 0 deletions

14
src/config/app.default.ts Normal file
View File

@@ -0,0 +1,14 @@
import type { MidwayConfig } from '@midwayjs/core';
import { defineSaasServiceHostConfig } from '@cool-midway/rpc/saas-host';
import { GeneratedSaasModule } from '../saas/generated';
const host = defineSaasServiceHostConfig({
generated: GeneratedSaasModule,
});
export default {
...host,
keys: 'saas-module-service-local-v1',
koa: { port: Number(process.env.PORT || 7101) },
asyncContextManager: { enable: true },
} as MidwayConfig;

12
src/configuration.ts Normal file
View File

@@ -0,0 +1,12 @@
import { Configuration } from '@midwayjs/core';
import * as koa from '@midwayjs/koa';
import * as validate from '@midwayjs/validate';
import * as rpc from '@cool-midway/rpc';
import * as saasHost from '@cool-midway/rpc/saas-host';
import * as DefaultConfig from './config/app.default';
@Configuration({
imports: [koa, validate, rpc, saasHost],
importConfigs: [{ default: DefaultConfig }],
})
export class SaasModuleServiceConfiguration {}

90
src/controller/health.ts Normal file
View File

@@ -0,0 +1,90 @@
import { Config, Controller, Get, Inject } from '@midwayjs/core';
import type { Context } from '@midwayjs/koa';
import type {
RpcHostReadiness,
RpcHostReadinessSnapshot,
} from '@cool-midway/rpc/saas-host';
@Controller('/health')
export class SaasModuleHealthController {
@Inject()
ctx: Context;
@Config('cool.rpc.moduleCode')
moduleCode: string;
@Config('cool.rpc.serviceVersion')
serviceVersion: string;
@Config('cool.rpc.minSchemaVersion')
minSchemaVersion: number;
@Config('cool.rpc.maxSchemaVersion')
maxSchemaVersion: number;
@Get('/')
async health() {
const readiness = await this.resolveReadiness();
if (!readiness.ready) this.ctx.status = 503;
return {
...readiness,
moduleCode: this.moduleCode,
serviceVersion: this.serviceVersion,
schemaRange: [this.minSchemaVersion, this.maxSchemaVersion],
};
}
private async resolveReadiness(): Promise<RpcHostReadinessSnapshot> {
try {
const container = this.ctx?.requestContext;
if (!container) {
return this.unavailable('RPC_HOST_READINESS_PROVIDER_MISSING');
}
const readiness = await container.getAsync<RpcHostReadiness>(
'rpcHostReadiness'
);
if (!readiness) {
return this.unavailable('RPC_HOST_READINESS_PROVIDER_MISSING');
}
const snapshot = readiness?.snapshot?.();
return this.isReadinessSnapshot(snapshot)
? snapshot
: this.unavailable('RPC_HOST_READINESS_SNAPSHOT_INVALID');
} catch {
return this.unavailable('RPC_HOST_READINESS_UNAVAILABLE');
}
}
private isReadinessSnapshot(
snapshot: RpcHostReadinessSnapshot | undefined
): snapshot is RpcHostReadinessSnapshot {
const checkStates = ['pending', 'ok', 'error'];
return Boolean(
snapshot &&
typeof snapshot.ready === 'boolean' &&
['starting', 'ready', 'degraded', 'draining', 'stopped'].includes(
snapshot.status
) &&
snapshot.ready === (snapshot.status === 'ready') &&
Number.isFinite(snapshot.checkedAt) &&
snapshot.checks &&
checkStates.includes(snapshot.checks.bootstrap) &&
checkStates.includes(snapshot.checks.lease) &&
checkStates.includes(snapshot.checks.registry)
);
}
private unavailable(reason: string): RpcHostReadinessSnapshot {
return {
ready: false,
status: 'degraded',
reason,
checkedAt: Date.now(),
checks: {
bootstrap: 'error',
lease: 'error',
registry: 'error',
},
};
}
}