更新项目

This commit is contained in:
ryan
2026-06-08 16:57:32 +08:00
parent c6cc8c3305
commit c1fbf73f7e
19 changed files with 224 additions and 113 deletions
+29
View File
@@ -0,0 +1,29 @@
.git
.idea
.vscode
.DS_Store
Thumbs.db
config.yaml
.env
.env.*
bin/
build/
dist/
data/
logs/
uploads/
s3_cache/
frontend/node_modules/
frontend/.next/
frontend/out/
frontend/build/
frontend/disk/
frontend/.env
frontend/next-env.d.ts
frontend/*.tsbuildinfo
frontend/package-lock.json
internal/router/dist/
+5 -2
View File
@@ -39,14 +39,17 @@
以下是项目的顶层目录结构及其职责, 如果有新增目录或文件,请务必在此处同步更新:
```
Refreshing/ # 项目根目录(模块名: github.com/linux-do/credit)
wavelet/ # 项目根目录(模块名: github.com/linux-do/credit)
├── main.go # 程序入口,调用 internal/cmd
├── go.mod / go.sum # Go 模块依赖
├── config.yaml # 运行时配置(不提交到 Git)
├── config.example.yaml # 配置模板(需提交)
├── DEPLOYMENT_zh.md # 部署说明文档(中文版)
├── Makefile # 常用命令(swagger/tidy/license)
├── Dockerfile # 后端容器镜像构建
├── docker/ # Docker 镜像构建文件(集成/前端/后端)
│ ├── Dockerfile # 标准集成镜像(前端静态导出嵌入后端)
│ ├── Dockerfile.frontend # 仅前端镜像(Next.js)
│ └── Dockerfile.backend # 仅后端镜像(Go API/Worker/Scheduler)
├── docker-compose.yml # 本地依赖服务(PostgreSQL / Redis / ClickHouse)
├── .editorconfig # 编辑器格式规范
├── .gitignore
+5 -2
View File
@@ -39,14 +39,17 @@
以下是项目的顶层目录结构及其职责, 如果有新增目录或文件,请务必在此处同步更新:
```
Refreshing/ # 项目根目录(模块名: github.com/linux-do/credit)
wavelet/ # 项目根目录(模块名: github.com/linux-do/credit)
├── main.go # 程序入口,调用 internal/cmd
├── go.mod / go.sum # Go 模块依赖
├── config.yaml # 运行时配置(不提交到 Git)
├── config.example.yaml # 配置模板(需提交)
├── DEPLOYMENT_zh.md # 部署说明文档(中文版)
├── Makefile # 常用命令(swagger/tidy/license)
├── Dockerfile # 后端容器镜像构建
├── docker/ # Docker 镜像构建文件(集成/前端/后端)
│ ├── Dockerfile # 标准集成镜像(前端静态导出嵌入后端)
│ ├── Dockerfile.frontend # 仅前端镜像(Next.js)
│ └── Dockerfile.backend # 仅后端镜像(Go API/Worker/Scheduler)
├── docker-compose.yml # 本地依赖服务(PostgreSQL / Redis / ClickHouse)
├── .editorconfig # 编辑器格式规范
├── .gitignore
-23
View File
@@ -1,23 +0,0 @@
FROM alpine:3.23
# set the time zone to Beijing Time in the Eastern 8th Time Zone
ARG TZ=Asia/Shanghai
RUN apk add --no-cache tzdata && \
cp /usr/share/zoneinfo/${TZ} /etc/localtime && \
echo "${TZ}" > /etc/timezone
WORKDIR /app
# set build arg for platform
ARG TARGETPLATFORM
# copy the appropriate binary based on platform
COPY credit-server-${TARGETPLATFORM#linux/} ./credit-server
# copy docs
COPY docs ./docs
EXPOSE 8000
# set entrypoint
ENTRYPOINT ["./credit-server"]
+1 -1
View File
@@ -5,4 +5,4 @@ build-embedded:
cd frontend && pnpm build:embed
rm -rf internal/router/dist
cp -R frontend/out internal/router/dist
go build -tags embed_frontend -o bin/credit main.go
go build -tags embed_frontend -o bin/wavelet main.go
+4 -4
View File
@@ -1,4 +1,4 @@
# Wavelet
# wavelet
🚀 A modern, production-ready full-stack boilerplate for building scalable web applications
@@ -11,7 +11,7 @@
## 📖 Introduction
**Wavelet** is a generic, production-ready full-stack boilerplate built with **Go (Gin + GORM)** on the backend and **Next.js (App Router + Shadcn UI)** on the frontend. It ships with everything you need to bootstrap a modern SaaS, internal tool, or developer platform — without the boilerplate headaches.
**wavelet** is a generic, production-ready full-stack boilerplate built with **Go (Gin + GORM)** on the backend and **Next.js (App Router + Shadcn UI)** on the frontend. It ships with everything you need to bootstrap a modern SaaS, internal tool, or developer platform — without the boilerplate headaches.
The project was designed from the ground up to be **framework-first and business-agnostic**: plug in your own domain logic while reusing the battle-tested infrastructure that comes out of the box.
@@ -209,11 +209,11 @@ pnpm format
## 📁 Project Structure
```
Refreshing/
wavelet/
├── main.go # Entry point (delegates to internal/cmd)
├── config.example.yaml # Configuration template
├── Makefile # Common commands (swagger, tidy, license)
├── Dockerfile # Container image build
├── docker/ # Docker image build files (integrated/frontend/backend)
├── docs/ # Swagger auto-generated docs
├── frontend/ # Next.js frontend application
│ ├── app/ # App Router pages
+4 -4
View File
@@ -1,4 +1,4 @@
# Wavelet
# wavelet
🚀 现代化、生产就绪的全栈应用脚手架
@@ -11,7 +11,7 @@
## 📖 项目简介
**Wavelet** 是一个通用型、生产就绪的现代全栈脚手架,后端采用 **Go(Gin + GORM)**,前端采用 **Next.js(App Router + Shadcn UI)**。项目开箱即用,内置构建现代 SaaS、内部工具或开发者平台所需的核心基础设施。
**wavelet** 是一个通用型、生产就绪的现代全栈脚手架,后端采用 **Go(Gin + GORM)**,前端采用 **Next.js(App Router + Shadcn UI)**。项目开箱即用,内置构建现代 SaaS、内部工具或开发者平台所需的核心基础设施。
项目设计理念是 **框架优先、业务中立**:您可以在沿用经过实战检验的底层基础设施的同时,自由接入自己的业务逻辑。
@@ -209,11 +209,11 @@ pnpm format
## 📁 项目结构
```
Refreshing/
wavelet/
├── main.go # 程序入口(委托给 internal/cmd)
├── config.example.yaml # 配置模板
├── Makefile # 常用命令(swagger、tidy、license)
├── Dockerfile # 容器镜像构建
├── docker/ # Docker 镜像构建文件(集成/前端/后端)
├── docs/ # Swagger 自动生成文档
├── frontend/ # Next.js 前端应用
│ ├── app/ # App Router 页面
+3 -4
View File
@@ -1,15 +1,15 @@
# Wavelet — Full-Stack Boilerplate Config
# wavelet — Full-Stack Boilerplate Config
# Copy this file to config.yaml and fill in your values.
# Fields marked with <...> are required; others have sensible defaults.
# ─── Application ────────────────────────────────────────────────────────────────
app:
app_name: "Wavelet"
app_name: "wavelet"
env: "development" # development | testing | production
addr: ":8000"
node_id: 1 # Snowflake node ID (0-1023). Must be unique per instance.
graceful_shutdown_timeout: 30
session_cookie_name: "Wavelet_session_id" # Change to something unique before deploy
session_cookie_name: "wavelet_session_id" # Change to something unique before deploy
session_secret: "<uniq-random-string>" # Cannot be changed after first start
session_domain: "" # e.g. ".yourdomain.com"
session_age: 86400 # Session lifetime in seconds (default: 24h)
@@ -127,4 +127,3 @@ s3:
local_cache:
enabled: false
cache_dir: "./s3_cache"
+1 -1
View File
@@ -1,7 +1,7 @@
services:
postgres:
image: postgres:17-alpine
container_name: Wavelet-postgres
container_name: wavelet-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:-wavelet}
+59
View File
@@ -0,0 +1,59 @@
# syntax=docker/dockerfile:1.7
ARG GO_VERSION=1.25
ARG NODE_VERSION=22
ARG ALPINE_VERSION=3.23
FROM node:${NODE_VERSION}-alpine AS frontend-builder
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
ENV NODE_ENV=production
ENV NEXT_STANDALONE_EXPORT=true
RUN corepack enable && corepack prepare pnpm@10.10.0 --activate
WORKDIR /workspace/frontend
COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY frontend/ ./
RUN pnpm build:embed
FROM golang:${GO_VERSION}-alpine AS backend-builder
RUN apk add --no-cache ca-certificates git
WORKDIR /workspace
COPY go.mod go.sum ./
RUN go mod download
COPY . .
COPY --from=frontend-builder /workspace/frontend/out ./internal/router/dist
RUN CGO_ENABLED=0 GOOS=linux go build \
-tags embed_frontend \
-trimpath \
-ldflags="-s -w" \
-o /out/wavelet \
./main.go
FROM alpine:${ALPINE_VERSION}
ARG TZ=Asia/Shanghai
RUN apk add --no-cache ca-certificates tzdata && \
cp /usr/share/zoneinfo/${TZ} /etc/localtime && \
echo "${TZ}" > /etc/timezone
WORKDIR /app
COPY --from=backend-builder /out/wavelet ./wavelet
COPY docs ./docs
EXPOSE 8000
ENTRYPOINT ["./wavelet"]
CMD ["api"]
+39
View File
@@ -0,0 +1,39 @@
# syntax=docker/dockerfile:1.7
ARG GO_VERSION=1.25
ARG ALPINE_VERSION=3.23
FROM golang:${GO_VERSION}-alpine AS builder
RUN apk add --no-cache ca-certificates git
WORKDIR /workspace
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
-trimpath \
-ldflags="-s -w" \
-o /out/wavelet \
./main.go
FROM alpine:${ALPINE_VERSION}
ARG TZ=Asia/Shanghai
RUN apk add --no-cache ca-certificates tzdata && \
cp /usr/share/zoneinfo/${TZ} /etc/localtime && \
echo "${TZ}" > /etc/timezone
WORKDIR /app
COPY --from=builder /out/wavelet ./wavelet
COPY docs ./docs
EXPOSE 8000
ENTRYPOINT ["./wavelet"]
CMD ["api"]
+54
View File
@@ -0,0 +1,54 @@
# syntax=docker/dockerfile:1.7
ARG NODE_VERSION=22
FROM node:${NODE_VERSION}-alpine AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable && corepack prepare pnpm@10.10.0 --activate
FROM base AS builder
ENV NODE_ENV=production
ENV NEXT_PUBLIC_LINUX_DO_CREDIT_BACKEND_URL=https://build-placeholder.invalid
ENV LINUX_DO_CREDIT_SESSION_COOKIE_NAME=__LINUX_DO_CREDIT_SESSION_COOKIE_NAME__
ENV LINUX_DO_CREDIT_RATE_LIMIT_ENABLED=__LINUX_DO_CREDIT_RATE_LIMIT_ENABLED__
ENV LINUX_DO_CREDIT_BACKEND_URL=https://build-placeholder-2.invalid
ARG VERSION=""
ARG BUILD_DATE=""
WORKDIR /app
COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY frontend/ ./
RUN if [ -n "$VERSION" ] || [ -n "$BUILD_DATE" ]; then \
VERSION="$VERSION" BUILD_DATE="$BUILD_DATE" node -e "const fs = require('fs'); const pkg = require('./package.json'); const version = process.env.VERSION; const buildDate = process.env.BUILD_DATE; if (version) pkg.version = version; if (buildDate) pkg.buildDate = buildDate; fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\\n')"; \
fi
RUN pnpm build
RUN grep -rl \
-e "https://build-placeholder.invalid" \
-e "https://build-placeholder-2.invalid" \
-e "__LINUX_DO_CREDIT_SESSION_COOKIE_NAME__" \
-e "__LINUX_DO_CREDIT_RATE_LIMIT_ENABLED__" \
/app/.next > /app/.replace.files
FROM base AS runner
ENV NODE_ENV=production
WORKDIR /app
COPY --from=builder /app ./
EXPOSE 3010
ENTRYPOINT ["./entrypoint.sh"]
CMD ["pnpm", "start"]
+15 -15
View File
@@ -1,6 +1,6 @@
# Wavelet 部署指南
# wavelet 部署指南
本文档详细介绍了 **Wavelet** 脚手架系统在不同业务阶段的部署方案,涵盖从**最小化单机部署**到**最大化高可用分布式部署**的全生命周期架构。
本文档详细介绍了 **wavelet** 脚手架系统在不同业务阶段的部署方案,涵盖从**最小化单机部署**到**最大化高可用分布式部署**的全生命周期架构。
---
@@ -10,9 +10,9 @@
| 组件名称 | 运行命令/形式 | 职责说明 | 必选/可选 |
| :--- | :--- | :--- | :--- |
| **HTTP API 服务** | `bin/credit api` | 接收并处理前端及第三方的 RESTful API 请求 | **必选** |
| **异步任务工作进程** | `bin/credit worker` | 消费并处理异步队列任务(如邮件发送、清理上传文件等) | **必选** |
| **定时任务调度器** | `bin/credit scheduler` | 定时向 Redis 队列下发 Cron 任务(仅负责触发,不负责执行) | **必选** |
| **HTTP API 服务** | `bin/wavelet api` | 接收并处理前端及第三方的 RESTful API 请求 | **必选** |
| **异步任务工作进程** | `bin/wavelet worker` | 消费并处理异步队列任务(如邮件发送、清理上传文件等) | **必选** |
| **定时任务调度器** | `bin/wavelet scheduler` | 定时向 Redis 队列下发 Cron 任务(仅负责触发,不负责执行) | **必选** |
| **前端服务 (Node.js)** | `pnpm start` | 提供 React/Next.js 页面服务(在分离部署时使用) | 分离模式必选 |
| **PostgreSQL** | 关系型主数据库 | 存储用户、系统配置、认证源、任务执行记录等核心数据 | **必选** |
| **Redis** | 缓存与消息队列中间件 | 存储 Session 会话、临时缓存以及 Asynq 异步任务队列数据 | **必选** |
@@ -99,12 +99,12 @@ docker compose up -d
make build-embedded
```
该命令会自动完成前端的静态编译导出 (`frontend/out`)、复制到 Go 后端目录,最后使用 `-tags embed_frontend` 生成后端单文件:
- 产物路径:`bin/credit`
- 产物路径:`bin/wavelet`
#### 3. 进程管理 (使用 Systemd)
将 `bin/credit` 拷贝到生产服务器 `/usr/local/bin/credit`,并为 `api`、`worker` 和 `scheduler` 配置 Systemd 管理服务。
将 `bin/wavelet` 拷贝到生产服务器 `/usr/local/bin/wavelet`,并为 `api`、`worker` 和 `scheduler` 配置 Systemd 管理服务。
新建 API 进程服务文件 `/etc/systemd/system/refreshing-api.service`:
新建 API 进程服务文件 `/etc/systemd/system/wavelet-api.service`:
```ini
[Unit]
Description=Refreshing API Service
@@ -114,14 +114,14 @@ After=network.target
Type=simple
User=root
WorkingDirectory=/app
ExecStart=/usr/local/bin/credit api
ExecStart=/usr/local/bin/wavelet api
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
```
同理,新建 Worker 服务 `/etc/systemd/system/refreshing-worker.service`(将命令改为 `credit worker`),以及 Scheduler 服务 `/etc/systemd/system/refreshing-scheduler.service`(将命令改为 `credit scheduler`)。
同理,新建 Worker 服务 `/etc/systemd/system/wavelet-worker.service`(将命令改为 `wavelet worker`),以及 Scheduler 服务 `/etc/systemd/system/wavelet-scheduler.service`(将命令改为 `wavelet scheduler`)。
启动并启用所有服务:
```bash
@@ -171,9 +171,9 @@ server {
#### 1. 部署后端 Go 服务
1. 编译后端:
```bash
go build -o bin/credit main.go
go build -o bin/wavelet main.go
```
2. 在后端服务器上,同样使用 Systemd 或 Docker 守护启动 `credit api`、`credit worker` 和 `credit scheduler`。
2. 在后端服务器上,同样使用 Systemd 或 Docker 守护启动 `wavelet api`、`wavelet worker` 和 `wavelet scheduler`。
3. 配置后端 Nginx 将客户端 API 请求(如 `/api/...`)反向代理至后端绑定的端口(如 `:8000`)。
#### 2. 部署前端 Next.js 服务
@@ -301,9 +301,9 @@ s3:
```
#### 4. 后端进程横向拆分部署
- **API 集群**:启动数十个甚至上百个 `credit api` 无状态容器。它们可以通过负载均衡器直接挂载,支持随时弹性缩容扩容。
- **Worker 集群**:启动多个 `credit worker` 容器。因为 `Asynq` 基于 Redis 分布式处理,多个 Worker 进程可以安全地同时运行并竞抢同一队列的异步任务,自动保障任务的并发吞吐能力。
- **Scheduler 独占**:**【注意】** 为避免重复触发定时 Cron 任务,`credit scheduler` 定时调度器进程**同一时间应仅运行单个活跃实例**(主备高可用可以通过容器平台的单实例保障或 K8s Job 机制来限制实例数为 1)。
- **API 集群**:启动数十个甚至上百个 `wavelet api` 无状态容器。它们可以通过负载均衡器直接挂载,支持随时弹性缩容扩容。
- **Worker 集群**:启动多个 `wavelet worker` 容器。因为 `Asynq` 基于 Redis 分布式处理,多个 Worker 进程可以安全地同时运行并竞抢同一队列的异步任务,自动保障任务的并发吞吐能力。
- **Scheduler 独占**:**【注意】** 为避免重复触发定时 Cron 任务,`wavelet scheduler` 定时调度器进程**同一时间应仅运行单个活跃实例**(主备高可用可以通过容器平台的单实例保障或 K8s Job 机制来限制实例数为 1)。
#### 5. ClickHouse 高并发同步
在大数据量、高频支付结算场景下,开启 ClickHouse 以接收系统的历史数据同步(如订单流水和任务大宽表),通过定时器把 PostgreSQL 的压力转移到 ClickHouse 列式存储中。
-52
View File
@@ -1,52 +0,0 @@
# ---- base stage ----
FROM node:22-alpine AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable && corepack prepare pnpm@10.10.0 --activate
# ---- build stage ----
FROM base AS builder
ENV NODE_ENV=production
ENV NEXT_PUBLIC_LINUX_DO_CREDIT_BACKEND_URL=https://build-placeholder.invalid
ENV LINUX_DO_CREDIT_SESSION_COOKIE_NAME=__LINUX_DO_CREDIT_SESSION_COOKIE_NAME__
ENV LINUX_DO_CREDIT_RATE_LIMIT_ENABLED=__LINUX_DO_CREDIT_RATE_LIMIT_ENABLED__
ENV LINUX_DO_CREDIT_BACKEND_URL=https://build-placeholder-2.invalid
ARG VERSION=""
ARG BUILD_DATE=""
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN if [ -n "$VERSION" ] || [ -n "$BUILD_DATE" ]; then \
VERSION="$VERSION" BUILD_DATE="$BUILD_DATE" node -e "const fs = require('fs'); const pkg = require('./package.json'); const version = process.env.VERSION; const buildDate = process.env.BUILD_DATE; if (version) pkg.version = version; if (buildDate) pkg.buildDate = buildDate; fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\\n')"; \
fi
RUN pnpm build
RUN grep -rl \
-e "https://build-placeholder.invalid" \
-e "https://build-placeholder-2.invalid" \
-e "__LINUX_DO_CREDIT_SESSION_COOKIE_NAME__" \
-e "__LINUX_DO_CREDIT_RATE_LIMIT_ENABLED__" \
/app/.next > /app/.replace.files
# ---- runner stage ----
FROM base AS runner
WORKDIR /app
COPY --from=builder /app .
EXPOSE 3000
ENTRYPOINT ["./entrypoint.sh"]
CMD ["pnpm", "start"]
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "Wavelet",
"name": "wavelet",
"version": "1.3.9",
"buildDate": "2026-04-22 16:00:00",
"private": true,
+1 -1
View File
@@ -23,7 +23,7 @@ import (
var apiCmd = &cobra.Command{
Use: "api",
Short: "credit API",
Short: "wavelet API",
Run: func(cmd *cobra.Command, args []string) {
router.Serve()
},
+1 -1
View File
@@ -24,7 +24,7 @@ import (
)
var rootCmd = &cobra.Command{
Use: "linux-do-credit",
Use: "wavelet",
PreRun: func(cmd *cobra.Command, args []string) {
migrator.Migrate()
},
+1 -1
View File
@@ -26,7 +26,7 @@ import (
var schedulerCmd = &cobra.Command{
Use: "scheduler",
Short: "credit Scheduler",
Short: "wavelet Scheduler",
Run: func(cmd *cobra.Command, args []string) {
log.Println("[Scheduler] 启动定时任务调度服务")
if err := scheduler.StartScheduler(); err != nil {
+1 -1
View File
@@ -26,7 +26,7 @@ import (
var workerCmd = &cobra.Command{
Use: "worker",
Short: "credit Worker",
Short: "wavelet Worker",
Run: func(cmd *cobra.Command, args []string) {
log.Println("[Worker] 启动任务处理服务")
if err := worker.StartWorker(); err != nil {