feat(api): 开发平台、门店相关代码
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import { spawn } from 'node:child_process';
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const scriptDirectory = dirname(fileURLToPath(import.meta.url));
|
||||
const repositoryRoot = resolve(scriptDirectory, '../../..');
|
||||
const environmentFile = resolve(repositoryRoot, '.env');
|
||||
const localEnvironment = existsSync(environmentFile)
|
||||
? parseEnvironment(readFileSync(environmentFile, 'utf8'))
|
||||
: {};
|
||||
const apiPort = process.env.API_PORT ?? localEnvironment.API_PORT ?? '8200';
|
||||
const swaggerEnabled =
|
||||
(process.env.SWAGGER_ENABLED ??
|
||||
localEnvironment.SWAGGER_ENABLED ??
|
||||
'true') === 'true';
|
||||
const nodeEnvironment =
|
||||
process.env.NODE_ENV ?? localEnvironment.NODE_ENV ?? 'development';
|
||||
const swaggerUrl = `http://localhost:${apiPort}/api-docs`;
|
||||
const tsxCommand = process.platform === 'win32' ? 'tsx.cmd' : 'tsx';
|
||||
const apiProcess = spawn(tsxCommand, ['watch', 'src/main.ts'], {
|
||||
shell: process.platform === 'win32',
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
if (
|
||||
process.platform === 'darwin' &&
|
||||
nodeEnvironment === 'development' &&
|
||||
swaggerEnabled
|
||||
) {
|
||||
void openSwaggerAfterApiIsReady();
|
||||
}
|
||||
|
||||
for (const signal of ['SIGINT', 'SIGTERM']) {
|
||||
process.on(signal, () => apiProcess.kill(signal));
|
||||
}
|
||||
|
||||
apiProcess.on('exit', (code, signal) => {
|
||||
if (signal) process.exitCode = 1;
|
||||
else process.exitCode = code ?? 0;
|
||||
});
|
||||
|
||||
function parseEnvironment(content) {
|
||||
return Object.fromEntries(
|
||||
content
|
||||
.split(/\r?\n/)
|
||||
.map((line) => line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/))
|
||||
.filter(Boolean)
|
||||
.map((match) => [match[1], match[2]]),
|
||||
);
|
||||
}
|
||||
|
||||
async function openSwaggerAfterApiIsReady() {
|
||||
const deadline = Date.now() + 30_000;
|
||||
while (Date.now() < deadline) {
|
||||
try {
|
||||
const response = await fetch(`http://localhost:${apiPort}/health`);
|
||||
if (response.ok) {
|
||||
// 开发启动器进程不会随 tsx watch 的热重启重建,因此只会打开一次。
|
||||
spawn('open', [swaggerUrl], {
|
||||
detached: true,
|
||||
stdio: 'ignore',
|
||||
}).unref();
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// API 仍在启动或热重启,继续等待到超时为止。
|
||||
}
|
||||
await new Promise((resolveDelay) => setTimeout(resolveDelay, 250));
|
||||
}
|
||||
console.warn(`Swagger 未在 30 秒内就绪:${swaggerUrl}`);
|
||||
}
|
||||
Reference in New Issue
Block a user