feat(M0): 工程骨架 — Go 后端 + Vue3 前端脚手架

- Go 工程:cmd/usernode CLI(serve/migrate/admin create/reset-password/user otp)
  + internal/{config,model,service,system,auth,cron,api,router,server,pkg,webui}
- 配置:TOML + 环境变量覆盖(USERNODE_<SEC>_<FIELD>),config.example.toml
- 数据:GORM 双驱动(SQLite/MySQL)8 表模型,migrate 子命令可跑通
- 系统层:system.Manager 接口(useradd/userdel/passwd 白名单,dev dry-run)
- HTTP:Gin 路由骨架(/api/v1 + 501 占位),healthz,slog 结构化日志,优雅启停
- 前端:Vite + Vue3 + TS + Element Plus 最小可运行(web/),go:embed 打通
- 构建:deploy/Containerfile 多阶段单二进制镜像,web/Containerfile.dev 前端 dev
  镜像,Makefile(build/test/dev/web-build/image);go.mod 锁定 go 1.22
- 验证:go build/vet/test 通过;podman 镜像构建运行 healthz+embed 通过
This commit is contained in:
2026-08-29 22:43:27 +08:00
parent f70b344c85
commit ae45aba607
46 changed files with 2878 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
# ws_usernode 单二进制镜像(podman 多阶段构建)
#
# 构建(走代理时):
# podman build -t ws-usernode:latest \
# --build-arg HTTP_PROXY=http://10.62.25.123:7897 \
# --build-arg HTTPS_PROXY=http://10.62.25.123:7897 \
# --build-arg ALL_PROXY=http://10.62.25.123:7897 \
# -f deploy/Containerfile .
# 无代理环境可省略 --build-argContainerfile 内缺省为空,不影响)。
# 建议加 --network=host,规避容器网络插件缺失导致的拉包失败。
# ---------- 阶段 1:前端构建(node:20-alpine ----------
FROM node:20-alpine AS web-build
# proxychains 无法注入容器,代理环境变量须显式传入
ARG HTTP_PROXY=
ARG HTTPS_PROXY=
ARG ALL_PROXY=
ARG NO_PROXY=
ENV HTTP_PROXY=$HTTP_PROXY \
HTTPS_PROXY=$HTTPS_PROXY \
ALL_PROXY=$ALL_PROXY \
NO_PROXY=$NO_PROXY
WORKDIR /web
COPY web/package.json web/package-lock.json* ./
RUN npm install
COPY web/ .
RUN npm run build
# 产物:/web/dist
# ---------- 阶段 2Go 构建(golang:1.22-alpine ----------
FROM golang:1.22-alpine AS go-build
ARG HTTP_PROXY=
ARG HTTPS_PROXY=
ARG ALL_PROXY=
ARG NO_PROXY=
ENV HTTP_PROXY=$HTTP_PROXY \
HTTPS_PROXY=$HTTPS_PROXY \
ALL_PROXY=$ALL_PROXY \
NO_PROXY=$NO_PROXY
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# 用前端构建产物覆盖 internal/webui/distgo:embed 入口)
COPY --from=web-build /web/dist ./internal/webui/dist
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/usernode ./cmd/usernode
# ---------- 阶段 3:运行镜像 ----------
FROM alpine:3.20
RUN apk add --no-cache ca-certificates tzdata \
&& addgroup -S usernode && adduser -S -G usernode usernode
COPY --from=go-build /out/usernode /usr/local/bin/usernode
# 数据目录(SQLite 默认 data/usernode.db),运行时以专有用户运行
RUN mkdir -p /data && chown usernode:usernode /data
VOLUME ["/data"]
USER usernode
WORKDIR /data
EXPOSE 8080
ENTRYPOINT ["/usr/local/bin/usernode"]
CMD ["serve", "--config", "/etc/usernode/config.toml"]