feat: 增加 NapCat WebUI Gateway 部署配置
This commit is contained in:
parent
d0fbc13763
commit
eb162ddba6
8
API.md
8
API.md
@ -379,6 +379,14 @@ QQBot 运行态包括 NapCat 容器登录、OneBot v11 反向 WebSocket、MQTT
|
||||
|
||||
NapCat Chinese Desktop Runtime v8 使用 KT `NapCatQQ` fork 源码构建出的 `NapCat.Shell` artifact,并在 QQ `KickedOffLine` 后重置 native login service 再请求二维码;同一次踢下线事件只消费一次 reset,且只有明确二维码过期或扫码确认窗口失效的 QR session failure 才自动换码,避免扫码确认期间被 `ErrType=1/ErrCode=1` 抢刷新打断。构建前必须运行 `scripts/napcat-desktop-cn-stage-build.mjs` 生成 Docker build context;生产 `QQBOT_NAPCAT_IMAGE` 应指向验证过的 `kt-napcat-desktop-cn:desktop-cn-v8` digest。
|
||||
|
||||
### NapCat WebUI Gateway
|
||||
|
||||
NapCat WebUI Gateway 是独立部署的内部代理服务,生产镜像由 `dockerfile.gateway` 打包 `dist/apps/napcat-webui-gateway/main.js`,K8s 服务名为 `kt-napcat-webui-gateway`,端口 `48086`。API 侧只通过内部路由 `NAPCAT_WEBUI_GATEWAY_INTERNAL_BASE_URL` 创建、续期、撤销会话和交换一次性 ticket;浏览器只访问公开前缀 `NAPCAT_WEBUI_GATEWAY_PUBLIC_BASE_URL` 下的代理页面、静态资源和 WebSocket 转发,不能直连 NapCat 容器 WebUI。
|
||||
|
||||
必需环境变量:`NAPCAT_WEBUI_GATEWAY_INTERNAL_BASE_URL`、`NAPCAT_WEBUI_GATEWAY_PUBLIC_BASE_URL`、`NAPCAT_WEBUI_GATEWAY_INTERNAL_SECRET`、`NAPCAT_WEBUI_GATEWAY_REDIS_HOST`、`NAPCAT_WEBUI_GATEWAY_REDIS_PORT`、`NAPCAT_WEBUI_GATEWAY_SESSION_TTL_MS`、`NAPCAT_WEBUI_GATEWAY_TICKET_TTL_MS`、`NAPCAT_WEBUI_GATEWAY_UPSTREAM_TIMEOUT_MS`。生产 `NAPCAT_WEBUI_GATEWAY_INTERNAL_SECRET` 只来自 Jenkins 私有 `.env.production` 生成的 `kt-template-online-api-env` Secret,不写入 Git 或 manifest 字面量。
|
||||
|
||||
部署验收使用:`pnpm exec jest --runTestsByPath test/modules/qqbot/napcat-webui-gateway/gateway-deployment.spec.ts --runInBand`、`pnpm run typecheck`、`pnpm run build`、`Test-Path .\dist\apps\napcat-webui-gateway\main.js`、`git diff --check`。安全验收要求浏览器永远不接收 WebUI token、Credential、上游 URL/端口、Docker 拓扑、Redis 地址或内部 secret。
|
||||
|
||||
`napcat_login_event` 实体和表仅作为历史 schema 兼容保留;watchdog 不再写入 quick/password 恢复事件,也不再依赖该表判断是否恢复登录。
|
||||
|
||||
外发消息不直接抢发:后端会按 `QQBOT_SEND_GLOBAL_INTERVAL_MS`、`QQBOT_SEND_TARGET_INTERVAL_MS` 和 `QQBOT_SEND_JITTER_MS` 预约发送窗口,默认全局 2500ms、同会话 8000ms、抖动 0-800ms;如果等待超过 `QQBOT_SEND_MAX_QUEUE_WAIT_MS`,本次发送会在下发前被拒绝。在线命令和自动回复规则会叠加运行时保底冷却,默认命令 5000ms、规则 30000ms;复读机默认连续 4 次相同普通文本才触发,同一会话默认 10 分钟内只复读一次,并限制普通文本长度,减少自动行为被风控识别的概率。
|
||||
|
||||
30
Jenkinsfile
vendored
30
Jenkinsfile
vendored
@ -38,6 +38,7 @@ def requiredRuntimeEnvKeys() {
|
||||
'FFLOGS_CLIENT_SECRET',
|
||||
'QQBOT_PLUGIN_QUEUE_REDIS_HOST',
|
||||
'QQBOT_PLUGIN_QUEUE_REDIS_PORT',
|
||||
'NAPCAT_WEBUI_GATEWAY_INTERNAL_SECRET',
|
||||
]
|
||||
}
|
||||
|
||||
@ -85,6 +86,7 @@ pipeline {
|
||||
string(name: 'PUBLISH_BRANCH_PATTERN', defaultValue: '^(main|master|release/.+)$', description: '允许推送镜像的分支正则')
|
||||
string(name: 'DOCKER_REGISTRY', defaultValue: 'k3d-kt-registry.localhost:5000', description: '镜像仓库地址;K8s 发布默认使用 fnOS NAS 上的 k3d 本地 registry')
|
||||
string(name: 'IMAGE_NAME', defaultValue: 'kt-template-online-api', description: 'Docker 镜像名称')
|
||||
string(name: 'GATEWAY_IMAGE_NAME', defaultValue: 'kt-napcat-webui-gateway', description: 'NapCat WebUI Gateway Docker 镜像名称')
|
||||
string(name: 'IMAGE_TAG', defaultValue: '', description: '镜像标签,为空时使用 分支名-BUILD_NUMBER;PR 使用源分支名')
|
||||
string(name: 'CONTAINER_NAME', defaultValue: 'kt-template-online-api', description: '业务容器名称')
|
||||
string(name: 'CONTAINER_PORT', defaultValue: '48085', description: '宿主机映射端口,容器内固定使用 48085')
|
||||
@ -133,6 +135,8 @@ pipeline {
|
||||
env.DOCKER_REGISTRY_EFFECTIVE = registry ?: ''
|
||||
env.DOCKER_IMAGE = registry ? "${registry}/${params.IMAGE_NAME}:${env.IMAGE_TAG_FINAL}" : "${params.IMAGE_NAME}:${env.IMAGE_TAG_FINAL}"
|
||||
env.DOCKER_IMAGE_LATEST = registry ? "${registry}/${params.IMAGE_NAME}:latest" : "${params.IMAGE_NAME}:latest"
|
||||
env.GATEWAY_DOCKER_IMAGE = registry ? "${registry}/${params.GATEWAY_IMAGE_NAME}:${env.IMAGE_TAG_FINAL}" : "${params.GATEWAY_IMAGE_NAME}:${env.IMAGE_TAG_FINAL}"
|
||||
env.GATEWAY_DOCKER_IMAGE_LATEST = registry ? "${registry}/${params.GATEWAY_IMAGE_NAME}:latest" : "${params.GATEWAY_IMAGE_NAME}:latest"
|
||||
|
||||
// Agent 由 NAS 侧预先创建;这里仅确认 CI 所需的 Node/pnpm 环境可用。
|
||||
if (isUnix()) {
|
||||
@ -183,6 +187,8 @@ pipeline {
|
||||
Docker registry: ${env.DOCKER_REGISTRY_EFFECTIVE ?: '-'}
|
||||
Docker image: ${env.DOCKER_IMAGE}
|
||||
Docker latest: ${env.DOCKER_IMAGE_LATEST}
|
||||
Gateway image: ${env.GATEWAY_DOCKER_IMAGE}
|
||||
Gateway latest: ${env.GATEWAY_DOCKER_IMAGE_LATEST}
|
||||
Deploy target: ${params.DEPLOY_TARGET}
|
||||
Publish branch: ${env.IS_PUBLISH_BRANCH}
|
||||
Run container: ${params.RUN_DOCKER_CONTAINER}
|
||||
@ -237,16 +243,24 @@ pipeline {
|
||||
if (isUnix()) {
|
||||
runCmd("""
|
||||
test -f dist/main.js
|
||||
test -f dist/apps/napcat-webui-gateway/main.js
|
||||
docker build -f dockerfile -t ${env.DOCKER_IMAGE} .
|
||||
docker build -f dockerfile.gateway -t ${env.GATEWAY_DOCKER_IMAGE} .
|
||||
if [ '${env.DOCKER_IMAGE}' != '${env.DOCKER_IMAGE_LATEST}' ]; then
|
||||
docker tag ${env.DOCKER_IMAGE} ${env.DOCKER_IMAGE_LATEST}
|
||||
fi
|
||||
if [ '${env.GATEWAY_DOCKER_IMAGE}' != '${env.GATEWAY_DOCKER_IMAGE_LATEST}' ]; then
|
||||
docker tag ${env.GATEWAY_DOCKER_IMAGE} ${env.GATEWAY_DOCKER_IMAGE_LATEST}
|
||||
fi
|
||||
""".stripIndent())
|
||||
} else {
|
||||
runCmd('', """
|
||||
if not exist dist\\main.js exit /b 1
|
||||
if not exist dist\\apps\\napcat-webui-gateway\\main.js exit /b 1
|
||||
docker build -f dockerfile -t ${env.DOCKER_IMAGE} .
|
||||
docker build -f dockerfile.gateway -t ${env.GATEWAY_DOCKER_IMAGE} .
|
||||
if not "${env.DOCKER_IMAGE}"=="${env.DOCKER_IMAGE_LATEST}" docker tag ${env.DOCKER_IMAGE} ${env.DOCKER_IMAGE_LATEST}
|
||||
if not "${env.GATEWAY_DOCKER_IMAGE}"=="${env.GATEWAY_DOCKER_IMAGE_LATEST}" docker tag ${env.GATEWAY_DOCKER_IMAGE} ${env.GATEWAY_DOCKER_IMAGE_LATEST}
|
||||
""".stripIndent())
|
||||
}
|
||||
}
|
||||
@ -267,9 +281,14 @@ pipeline {
|
||||
runCmd("""
|
||||
docker push ${env.DOCKER_IMAGE}
|
||||
docker push ${env.DOCKER_IMAGE_LATEST}
|
||||
docker push ${env.GATEWAY_DOCKER_IMAGE}
|
||||
docker push ${env.GATEWAY_DOCKER_IMAGE_LATEST}
|
||||
""".stripIndent())
|
||||
} else {
|
||||
runCmd("docker push ${env.DOCKER_IMAGE}")
|
||||
runCmd("""
|
||||
docker push ${env.DOCKER_IMAGE}
|
||||
docker push ${env.GATEWAY_DOCKER_IMAGE}
|
||||
""".stripIndent())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -295,6 +314,7 @@ pipeline {
|
||||
def namespace = params.K8S_NAMESPACE?.trim() ?: 'kt-prod'
|
||||
def deploymentName = params.K8S_DEPLOYMENT?.trim() ?: 'kt-template-online-api'
|
||||
def containerName = params.K8S_CONTAINER?.trim() ?: 'api'
|
||||
def gatewayDeploymentName = 'kt-napcat-webui-gateway'
|
||||
def envSecret = params.K8S_ENV_SECRET?.trim() ?: 'kt-template-online-api-env'
|
||||
def rolloutTimeout = params.K8S_ROLLOUT_TIMEOUT?.trim() ?: '180s'
|
||||
def containerEnvFile = params.CONTAINER_ENV_FILE?.trim()
|
||||
@ -338,10 +358,14 @@ pipeline {
|
||||
|
||||
kubectl ${kubeConfigArg} apply -f ${shellQuote(manifestFile)}
|
||||
kubectl ${kubeConfigArg} ${namespaceArg} set image ${shellQuote("deployment/${deploymentName}")} ${shellQuote("${containerName}=${env.DOCKER_IMAGE}")}
|
||||
kubectl ${kubeConfigArg} ${namespaceArg} set image ${shellQuote('deployment/kt-napcat-webui-gateway')} ${shellQuote("gateway=${env.GATEWAY_DOCKER_IMAGE}")}
|
||||
kubectl ${kubeConfigArg} ${namespaceArg} annotate ${shellQuote("deployment/${deploymentName}")} \\
|
||||
${shellQuote("kubernetes.io/change-cause=${changeCause}")} --overwrite
|
||||
kubectl ${kubeConfigArg} ${namespaceArg} annotate ${shellQuote('deployment/kt-napcat-webui-gateway')} \\
|
||||
${shellQuote("kubernetes.io/change-cause=${changeCause}")} --overwrite
|
||||
kubectl ${kubeConfigArg} ${namespaceArg} rollout status ${shellQuote("deployment/${deploymentName}")} --timeout=${shellQuote(rolloutTimeout)}
|
||||
kubectl ${kubeConfigArg} ${namespaceArg} get pod,svc -l app=${shellQuote(deploymentName)}
|
||||
kubectl ${kubeConfigArg} ${namespaceArg} rollout status ${shellQuote('deployment/kt-napcat-webui-gateway')} --timeout=${shellQuote(rolloutTimeout)}
|
||||
kubectl ${kubeConfigArg} ${namespaceArg} get pod,svc -l ${shellQuote("app in (${deploymentName},${gatewayDeploymentName})")}
|
||||
""".stripIndent())
|
||||
}
|
||||
}
|
||||
@ -407,7 +431,7 @@ pipeline {
|
||||
|
||||
post {
|
||||
success {
|
||||
archiveArtifacts artifacts: 'dist/**,package.json,pnpm-lock.yaml,dockerfile,k8s/**,ci/fnos-k8s/**', fingerprint: true, allowEmptyArchive: true
|
||||
archiveArtifacts artifacts: 'dist/**,package.json,pnpm-lock.yaml,dockerfile,dockerfile.gateway,k8s/**,ci/fnos-k8s/**', fingerprint: true, allowEmptyArchive: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,6 +79,8 @@ Admin 环境总览面板使用 `ENV_DASHBOARD_*` 只读配置聚合 local-dev、
|
||||
|
||||
NapCat Runtime/Protocol Profile 已完成本地 API/Admin 实施,线上发布和账号闭环按 `docs/superpowers/plans/2026-06-18-qqbot-napcat-runtime-protocol-profile-implementation-plan.md` 的 Task 10 执行。当前实现覆盖运行态/协议/会话行为/历史登录事件兼容表/风险模式表,真实物理设备风格 hostname/MAC,NapCat/OneBot 配置 hash,KT `zh_CN.UTF-8` 中国桌面派生镜像资产,只读 `/qqbot/napcat/runtime/detail` 证据接口,watchdog 离线巡检告警,以及 Admin 账号页“运行态”抽屉;不绕过 QQ/Tencent 验证码、不修改 QQ/NTQQ 签名协议、不启用 privileged/host network,也不做账号级每小时/每日累计发送预算。NapCat Chinese Desktop Runtime v8 使用 KT `NapCatQQ` fork 源码构建出的 `NapCat.Shell` artifact,并在 QQ `KickedOffLine` 后重置 native login service 再请求二维码;同一次踢下线事件只消费一次 reset,且只有明确二维码过期或扫码确认窗口失效的 QR session failure 才自动换码,避免扫码确认期间被 `ErrType=1/ErrCode=1` 抢刷新打断。镜像必须先用 `scripts/napcat-desktop-cn-stage-build.mjs` staged build context,生产 `QQBOT_NAPCAT_IMAGE` 应指向验证过的 `kt-napcat-desktop-cn:desktop-cn-v8` digest。
|
||||
|
||||
NapCat WebUI Gateway 是独立运行的 NestJS 入口,生产镜像使用 `dockerfile.gateway` 打包 `dist/apps/napcat-webui-gateway/main.js` 并监听 `48086`。API 通过内部地址 `NAPCAT_WEBUI_GATEWAY_INTERNAL_BASE_URL=http://kt-napcat-webui-gateway:48086` 创建/续期/撤销 WebUI 会话,Admin 浏览器只访问公开前缀 `NAPCAT_WEBUI_GATEWAY_PUBLIC_BASE_URL=/napcat-webui`。Gateway 运行时需要 `NAPCAT_WEBUI_GATEWAY_INTERNAL_SECRET`、`NAPCAT_WEBUI_GATEWAY_REDIS_HOST`、`NAPCAT_WEBUI_GATEWAY_REDIS_PORT`、`NAPCAT_WEBUI_GATEWAY_SESSION_TTL_MS`、`NAPCAT_WEBUI_GATEWAY_TICKET_TTL_MS`、`NAPCAT_WEBUI_GATEWAY_UPSTREAM_TIMEOUT_MS`;生产 secret 由 Jenkins 从私有 `.env.production` 重建到 `kt-template-online-api-env`,不得写入 Git。验收命令:`pnpm exec jest --runTestsByPath test/modules/qqbot/napcat-webui-gateway/gateway-deployment.spec.ts --runInBand`、`pnpm run typecheck`、`pnpm run build`、`Test-Path .\dist\apps\napcat-webui-gateway\main.js`、`git diff --check`。安全边界:浏览器不得收到 WebUI token、Credential、上游 URL/端口、Docker 拓扑、Redis 地址或内部 secret。
|
||||
|
||||
## 启动
|
||||
|
||||
```bash
|
||||
|
||||
46
dockerfile.gateway
Normal file
46
dockerfile.gateway
Normal file
@ -0,0 +1,46 @@
|
||||
FROM node:22-bookworm-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV TZ=Asia/Shanghai
|
||||
ENV APP_PORT=48086
|
||||
ENV LOG_LEVEL=info
|
||||
ENV LOG_APP_NAME=kt-napcat-webui-gateway
|
||||
ENV LOG_PRETTY=false
|
||||
ENV LOKI_ENV=production
|
||||
ENV LOKI_PUSH_ENDPOINT=/loki/api/v1/push
|
||||
ENV LOKI_QUERY_ENDPOINT=/loki/api/v1/query_range
|
||||
ENV LOKI_PUSH_TIMEOUT_MS=30000
|
||||
ENV LOKI_QUERY_TIMEOUT_MS=10000
|
||||
ENV LOKI_BATCH_INTERVAL_SECONDS=5
|
||||
ENV LOKI_BATCH_MAX_BUFFER_SIZE=10000
|
||||
ENV LOKI_QUERY_MAX_LIMIT=1000
|
||||
ENV LOKI_SILENCE_ERRORS=true
|
||||
ENV FFLOGS_BASE_URL=https://cn.fflogs.com
|
||||
ENV FFLOGS_WEB_BASE_URL=https://cn.fflogs.com
|
||||
ENV FFLOGS_GRAPHQL_URL=https://cn.fflogs.com/api/v2/client
|
||||
ENV FFLOGS_TOKEN_URL=https://cn.fflogs.com/oauth/token
|
||||
ENV FFLOGS_DEFAULT_SERVER_REGION=CN
|
||||
ENV FFLOGS_REQUEST_TIMEOUT_MS=10000
|
||||
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends fontconfig fonts-noto-cjk openssh-client \
|
||||
&& fc-cache -f \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 生产镜像只安装运行依赖,dist 由 Jenkins Build stage 提前产出。
|
||||
# 跳过安装阶段脚本,避免 NODE_ENV=production 时 devDependency 中的 husky 不存在导致 prepare 失败。
|
||||
RUN npm install -g pnpm@9.15.9 --registry=https://registry.npmmirror.com \
|
||||
&& pnpm config set registry https://registry.npmmirror.com \
|
||||
&& pnpm install --prod --frozen-lockfile --ignore-scripts \
|
||||
&& pnpm rebuild skia-canvas
|
||||
|
||||
# dist 由 Jenkins 的 Build stage 生成,这里只打包运行产物。
|
||||
COPY dist ./dist
|
||||
|
||||
EXPOSE 48086
|
||||
|
||||
CMD ["node", "dist/apps/napcat-webui-gateway/main"]
|
||||
@ -42,6 +42,10 @@ spec:
|
||||
value: kt:qqbot:plugin-task
|
||||
- name: BANGDREAM_TSUGU_CACHE_ROOT
|
||||
value: /data/qqbot/plugins/bangdream/cache
|
||||
- name: NAPCAT_WEBUI_GATEWAY_INTERNAL_BASE_URL
|
||||
value: http://kt-napcat-webui-gateway:48086
|
||||
- name: NAPCAT_WEBUI_GATEWAY_PUBLIC_BASE_URL
|
||||
value: /napcat-webui
|
||||
# Jenkins 每次发布会从 Agent 私有 .env.production 重建这个 Secret。
|
||||
envFrom:
|
||||
- secretRef:
|
||||
@ -162,6 +166,87 @@ spec:
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kt-napcat-webui-gateway
|
||||
namespace: kt-prod
|
||||
labels:
|
||||
app: kt-napcat-webui-gateway
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: kt-napcat-webui-gateway
|
||||
ports:
|
||||
- name: http
|
||||
port: 48086
|
||||
targetPort: 48086
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kt-napcat-webui-gateway
|
||||
namespace: kt-prod
|
||||
labels:
|
||||
app: kt-napcat-webui-gateway
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 3
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: kt-napcat-webui-gateway
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: kt-napcat-webui-gateway
|
||||
spec:
|
||||
containers:
|
||||
- name: gateway
|
||||
image: k3d-kt-registry.localhost:5000/kt-napcat-webui-gateway:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 48086
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: production
|
||||
- name: TZ
|
||||
value: Asia/Shanghai
|
||||
- name: NAPCAT_WEBUI_GATEWAY_PORT
|
||||
value: "48086"
|
||||
- name: NAPCAT_WEBUI_GATEWAY_REDIS_HOST
|
||||
value: kt-qqbot-plugin-redis
|
||||
- name: NAPCAT_WEBUI_GATEWAY_REDIS_PORT
|
||||
value: "6379"
|
||||
- name: NAPCAT_WEBUI_GATEWAY_INTERNAL_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: kt-template-online-api-env
|
||||
key: NAPCAT_WEBUI_GATEWAY_INTERNAL_SECRET
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 48086
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 6
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 48086
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kt-qqbot-plugin-redis
|
||||
namespace: kt-prod
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
|
||||
const REPO_ROOT = resolve(__dirname, '../../../..');
|
||||
|
||||
/**
|
||||
* 读取仓库根目录下的部署配置文件。
|
||||
* @param relativePath - 仓库根目录相对路径。
|
||||
* @returns 文件文本内容。
|
||||
*/
|
||||
function readRepoFile(relativePath: string): string {
|
||||
return readFileSync(resolve(REPO_ROOT, relativePath), 'utf8');
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认仓库根目录下的部署配置文件存在。
|
||||
* @param relativePath - 仓库根目录相对路径。
|
||||
* @returns 绝对文件路径。
|
||||
*/
|
||||
function expectRepoFile(relativePath: string): string {
|
||||
const filePath = resolve(REPO_ROOT, relativePath);
|
||||
expect(existsSync(filePath)).toBe(true);
|
||||
return filePath;
|
||||
}
|
||||
|
||||
describe('NapCat WebUI Gateway deployment configuration', () => {
|
||||
it('packages the standalone gateway app in a dedicated runtime image', () => {
|
||||
expectRepoFile('dockerfile.gateway');
|
||||
|
||||
const dockerfile = readRepoFile('dockerfile.gateway');
|
||||
|
||||
expect(dockerfile).toContain('dist/apps/napcat-webui-gateway/main');
|
||||
expect(dockerfile).toContain('EXPOSE 48086');
|
||||
expect(dockerfile).toContain('LOG_APP_NAME=kt-napcat-webui-gateway');
|
||||
});
|
||||
|
||||
it('declares gateway API linkage and cluster runtime without literal secrets', () => {
|
||||
const manifest = readRepoFile('k8s/prod/api.yaml');
|
||||
|
||||
expect(manifest).toContain('kt-napcat-webui-gateway');
|
||||
expect(manifest).toContain('containerPort: 48086');
|
||||
expect(manifest).toContain('NAPCAT_WEBUI_GATEWAY_REDIS_HOST');
|
||||
expect(manifest).toContain('NAPCAT_WEBUI_GATEWAY_INTERNAL_SECRET');
|
||||
expect(manifest).toContain('NAPCAT_WEBUI_GATEWAY_INTERNAL_BASE_URL');
|
||||
expect(manifest).toContain('NAPCAT_WEBUI_GATEWAY_PUBLIC_BASE_URL');
|
||||
expect(manifest).toMatch(
|
||||
/name:\s*NAPCAT_WEBUI_GATEWAY_INTERNAL_SECRET[\s\S]*?valueFrom:[\s\S]*?secretKeyRef:/,
|
||||
);
|
||||
expect(manifest).not.toMatch(
|
||||
/name:\s*NAPCAT_WEBUI_GATEWAY_INTERNAL_SECRET[\s\S]*?value:\s*["']?[^"'\s]+/,
|
||||
);
|
||||
});
|
||||
|
||||
it('builds, pushes and rolls out the gateway image through Jenkins', () => {
|
||||
const jenkinsfile = readRepoFile('Jenkinsfile');
|
||||
|
||||
expect(jenkinsfile).toContain('GATEWAY_IMAGE_NAME');
|
||||
expect(jenkinsfile).toContain('dockerfile.gateway');
|
||||
expect(jenkinsfile).toContain('kt-napcat-webui-gateway');
|
||||
expect(jenkinsfile).toContain('GATEWAY_DOCKER_IMAGE');
|
||||
expect(jenkinsfile).toMatch(/docker push \$\{env\.GATEWAY_DOCKER_IMAGE\}/);
|
||||
expect(jenkinsfile).toMatch(
|
||||
/rollout status [\s\S]*deployment\/kt-napcat-webui-gateway/,
|
||||
);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user