import { BadRequestException, Injectable, ServiceUnavailableException } from '@nestjs/common'; @Injectable() export class AdminDeployService { async triggerDeploy() { const url = (process.env.DEPLOY_WEBHOOK_URL || 'http://127.0.0.1:8095/deploy').trim(); const secret = (process.env.DEPLOY_WEBHOOK_SECRET || '').trim(); if (!secret) { throw new ServiceUnavailableException('未配置 DEPLOY_WEBHOOK_SECRET,无法触发发布'); } let res: Response; try { res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Deploy-Token': secret, }, body: JSON.stringify({ source: 'admin' }), }); } catch (err) { throw new ServiceUnavailableException( `无法连接部署 webhook:${err instanceof Error ? err.message : 'network error'}`, ); } const text = await res.text(); let data: { ok?: boolean; accepted?: boolean; started?: boolean; message?: string; skipped?: boolean } = {}; try { data = text ? (JSON.parse(text) as typeof data) : {}; } catch { throw new ServiceUnavailableException(`部署 webhook 返回非 JSON(HTTP ${res.status})`); } if (res.status === 403) { throw new BadRequestException(data.message || '部署 webhook 鉴权失败'); } if (!res.ok && res.status !== 202) { throw new ServiceUnavailableException(data.message || `部署 webhook 失败(HTTP ${res.status})`); } return { accepted: true, started: data.started !== false && !data.skipped, message: data.message || (data.started === false ? 'deploy debounced' : 'deploy started'), }; } }