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
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
@@ -27,4 +24,3 @@ yarn.lock
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.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",
|
"@types/node": "^20.4.4",
|
||||||
"@vitejs/plugin-vue": "^5.0.4",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
|
"node-sass": "^6.0.1",
|
||||||
|
"sass-loader": "^10.0.1",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^5.1.4",
|
"vite": "^5.1.4",
|
||||||
"vite-plugin-html": "^3.2.2",
|
"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