10号功能修订
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
node_modules
|
||||
src
|
||||
public
|
||||
config
|
||||
dist/base
|
||||
dist/ld
|
||||
dist/_verify
|
||||
.git
|
||||
.workbuddy
|
||||
*.log
|
||||
vite.config.ts.timestamp-*
|
||||
@@ -1,91 +0,0 @@
|
||||
name: build
|
||||
|
||||
# 前端应用 CI:构建 → 打包 zip → 推 Nexus raw → 通知 projman 建档
|
||||
# 触发:push 到 main / 手动 Run workflow
|
||||
# 链路:clone 源码 -> node 容器 npm ci + npm run build -> 打包 dist 为 zip -> 推 Nexus raw -> 通知 projman 建档
|
||||
# 依赖仓库 Secrets:DEPLOY_TOKEN(webhook 鉴权)、NEXUS_USER / NEXUS_PASSWORD(上传 zip 到 Nexus raw)
|
||||
# 注意:CI 构建后自动建档(status=pending),前端发布仍由用户在「前端发布」Tab 点部署触发
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: docker:24
|
||||
options: --network infra-infra-net
|
||||
steps:
|
||||
- name: 安装 git / curl / zip(alpine)
|
||||
run: apk add --no-cache git curl zip
|
||||
|
||||
# 内网 Gitea 无法访问 GitHub 拉 actions/checkout,手动 clone 本仓库
|
||||
- name: 拉取源码(内网无 GitHub,手动 clone 本仓库)
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
git clone --depth 1 --branch main "http://x-access-token:${GITHUB_TOKEN}@gitea.yunhaixingtong.com/${{ github.repository }}.git" /tmp/src
|
||||
cp -a /tmp/src/. .
|
||||
rm -rf /tmp/src
|
||||
# 取提交备注首行,脱去换行/双引号/反斜杠(避免破坏 webhook JSON),截断到 200 字符
|
||||
COMMIT_MSG=$(git log -1 --pretty=%s | tr '\n' ' ' | sed 's/["\\]/ /g' | cut -c1-200)
|
||||
echo "COMMIT_MSG=${COMMIT_MSG}" >> "$GITHUB_ENV"
|
||||
|
||||
# 前端构建:起独立 node 容器(通过 docker.sock 调宿主 dockerd)
|
||||
# node:20 镜像预装 npm,npm ci 按 lockfile 安装依赖
|
||||
- name: 构建前端(node 容器)
|
||||
run: |
|
||||
set -e
|
||||
docker rm -f frontbuild 2>/dev/null || true
|
||||
docker run -d --name frontbuild -w /build node:20 sleep 3600
|
||||
docker cp "${PWD}/." frontbuild:/build/
|
||||
docker exec frontbuild sh -c '
|
||||
set -e
|
||||
cd /build
|
||||
if [ -f .npmrc ]; then sed -i "/SASS_BINARY_PATH/d" .npmrc; fi
|
||||
npm ci
|
||||
npm run build
|
||||
'
|
||||
echo "=== dist 产物 ==="
|
||||
docker exec frontbuild sh -c 'ls -la /build/dist/ | head -20'
|
||||
# 从容器 cp dist 到宿主工作目录
|
||||
docker cp frontbuild:/build/dist ./dist
|
||||
docker rm -f frontbuild
|
||||
echo "BUILD CHECK OK"
|
||||
|
||||
# 打包 dist → zip(进入 dist 目录打包,zip 根目录直接是 html/css/js 文件)
|
||||
- name: 打包产物
|
||||
run: |
|
||||
TAG=$(echo "$GITHUB_SHA" | cut -c1-8)
|
||||
ZIP_FILE="userhome-front-${TAG}.zip"
|
||||
cd dist && zip -r "../${ZIP_FILE}" . && cd ..
|
||||
echo "ZIP_FILE=${ZIP_FILE}" >> "$GITHUB_ENV"
|
||||
echo "TAG=${TAG}" >> "$GITHUB_ENV"
|
||||
ls -la "${ZIP_FILE}"
|
||||
|
||||
# 上传到 Nexus raw 仓库
|
||||
- name: 上传产物到 Nexus
|
||||
env:
|
||||
NEXUS_USER: ${{ secrets.NEXUS_USER }}
|
||||
NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}
|
||||
run: |
|
||||
set -e
|
||||
NEXUS_URL="http://nexus.yunhaixingtong.com:8081/repository/front-hosted/${ZIP_FILE}"
|
||||
# -f:HTTP >=400 时 curl 以非零退出,仓库不存在/认证失败必须让 job 变红,否则建档的是不存在的 URL
|
||||
curl -sSf -X PUT \
|
||||
-u "${NEXUS_USER}:${NEXUS_PASSWORD}" \
|
||||
--upload-file "${ZIP_FILE}" \
|
||||
"${NEXUS_URL}"
|
||||
echo "NEXUS_URL=${NEXUS_URL}" >> "$GITHUB_ENV"
|
||||
echo "uploaded to ${NEXUS_URL}"
|
||||
|
||||
# 通知 projman:webhook 收到后建档(type=front, status=pending),部署由用户在「前端发布」Tab 手动触发
|
||||
- name: 通知 projman 建档
|
||||
run: |
|
||||
set -e
|
||||
curl -sSf -X POST "http://192.168.10.160:8090/projman/api/deploy/webhook" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Deploy-Token: ${{ secrets.DEPLOY_TOKEN }}" \
|
||||
-d "{\"app\":\"userhome\",\"front\":\"front\",\"tag\":\"${TAG}\",\"sha\":\"${GITHUB_SHA}\",\"message\":\"${COMMIT_MSG}\",\"nexusUrl\":\"${NEXUS_URL}\"}"
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# WorkBuddy local state
|
||||
.workbuddy/
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
@@ -27,4 +24,3 @@ yarn.lock
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
*.timestamp-*.mjs
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// 纠正当前 api:gen 产物中命名空间的大小写错误
|
||||
// 映射依据:后端 service 下真实的 *Controller 类名 -> PascalCase 命名空间
|
||||
const fs = require('fs');
|
||||
const map = {
|
||||
Approle: 'AppRole',
|
||||
Appsettings: 'AppSettings',
|
||||
Groupauthz: 'GroupAuthz',
|
||||
Pushsubscription: 'PushSubscription',
|
||||
Signkeyinternal: 'SignKeyInternal',
|
||||
Sysgroup: 'SysGroup',
|
||||
Sysuser: 'SysUser',
|
||||
};
|
||||
const targets = ['src/api/apiDefinitions.ts', 'src/api/globals.d.ts'];
|
||||
for (const f of targets) {
|
||||
let s = fs.readFileSync(f, 'utf8');
|
||||
for (const [wrong, correct] of Object.entries(map)) {
|
||||
// 带引号 key:'Sysgroup. -> 'SysGroup.
|
||||
s = s.split(`'${wrong}.`).join(`'${correct}.`);
|
||||
// 裸引用(globals.d.ts 里的 interface/属性声明):Sysgroup: -> SysGroup: / Sysgroup. -> SysGroup.
|
||||
s = s.split(`${wrong}:`).join(`${correct}:`);
|
||||
s = s.split(`${wrong}.`).join(`${correct}.`);
|
||||
}
|
||||
fs.writeFileSync(f, s);
|
||||
console.log('fixed', f);
|
||||
}
|
||||
console.log('done');
|
||||
@@ -1,65 +0,0 @@
|
||||
import type { GeneratorConfig } from "@alova/wormhole";
|
||||
|
||||
export default {
|
||||
generator: [
|
||||
{
|
||||
input: "http://localhost:8060/v3/api-docs",
|
||||
// 文档平台类型
|
||||
platform: "swagger",
|
||||
|
||||
// 生成代码的输出目录
|
||||
output: "src/api",
|
||||
// 响应数据的媒体类型
|
||||
responseMediaType: "application/json",
|
||||
// 请求体数据的媒体类型
|
||||
bodyMediaType: "application/json",
|
||||
// 生成的 API 版本
|
||||
version: 1,
|
||||
// 生成代码类型
|
||||
type: "typescript",
|
||||
// 全局 API 名称
|
||||
global: "Apis",
|
||||
splitImplementation: true,
|
||||
implementation: "inline",
|
||||
// API 处理函数
|
||||
handleApi: apiDescriptor => {
|
||||
// 过滤掉已废弃的 API
|
||||
if (apiDescriptor.deprecated) {
|
||||
return undefined;
|
||||
}
|
||||
// 优先使用后端显式声明的 operationId(稳定、可读、无重名后缀),
|
||||
// 仅在 operationId 缺失时回退到 summary。切勿用 summary 覆盖 operationId,
|
||||
// 否则中文/长文本 summary 会变成方法名导致运行时查不到。
|
||||
const newOpId = apiDescriptor.operationId ?? apiDescriptor.summary?.trim();
|
||||
const url = apiDescriptor.url.replace(/^\/{platform}\//, "/api/");
|
||||
// springdoc 默认把 Controller 类名转为 kebab tag(如 base-controller),
|
||||
// 而业务代码统一用 Pascal 无 Controller 的命名空间(如 Base)调用。
|
||||
// 这里剥掉 -controller 后缀并转为 PascalCase,使 wormhole 生成的
|
||||
// API 路径(Base.getUserInfo 等)与 Apis.Base.xxx 调用风格一致。
|
||||
// 由于转换结果是合法 JS 标识符,wormhole 会原样保留、不做 camelCase 改写。
|
||||
// springdoc v3 的 tag 有两种形态,都要能正确转为 PascalCase 且无 Controller 后缀:
|
||||
// - Pascal+Controller:SysGroupController -> SysGroup
|
||||
// - kebab+controller:sys-group-controller -> SysGroup
|
||||
// 关键:剥掉尾部 Controller 后,要在 camelCase 边界补空格再逐段首字母大写,
|
||||
// 否则 SysGroup -> Sysgroup(后面全被 toLowerCase 压成小写)会让运行时查不到。
|
||||
const toNamespace = (tag: string): string => {
|
||||
const stripped = tag.replace(/controller$/i, "");
|
||||
const withSpaces = stripped.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
||||
const parts = withSpaces.split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
||||
return parts
|
||||
.map((p: string) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase())
|
||||
.join("");
|
||||
};
|
||||
if (Array.isArray(apiDescriptor.tags)) {
|
||||
apiDescriptor.tags = apiDescriptor.tags.map(toNamespace);
|
||||
}
|
||||
|
||||
return {
|
||||
...apiDescriptor,
|
||||
operationId: newOpId, // 关键:改掉它
|
||||
url,
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
} as GeneratorConfig;
|
||||
@@ -1,10 +0,0 @@
|
||||
# UserBase 前端静态镜像(基于已构建好的 dist/prod)
|
||||
# 构建上下文 = front 目录: docker build -f docker/Dockerfile -t <img> .
|
||||
# 说明:caddy 用 handle_path /userbase/* 剥掉前缀后转发进来,故 nginx 直接以 / 提供服务
|
||||
FROM nginx:1.27-alpine
|
||||
|
||||
RUN rm -rf /usr/share/nginx/html/*
|
||||
COPY dist/prod/ /usr/share/nginx/html/
|
||||
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
@@ -1,26 +0,0 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
gzip on;
|
||||
gzip_min_length 1k;
|
||||
gzip_comp_level 5;
|
||||
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
|
||||
|
||||
# hash 路由,所有非静态资源请求回落到 index.html
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location ~* \.(js|css|png|jpe?g|gif|svg|woff2?|ttf|eot)$ {
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, max-age=604800";
|
||||
}
|
||||
|
||||
location = /index.html {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
}
|
||||
Generated
-7751
File diff suppressed because it is too large
Load Diff
@@ -37,6 +37,8 @@
|
||||
"@types/node": "^20.4.4",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"node-sass": "^6.0.1",
|
||||
"sass-loader": "^10.0.1",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.1.4",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user