chore: 重构数据库迁移与 nginx 配置目录
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
|
||||
## Security & Configuration Tips
|
||||
- Backend configuration is stored under `sproutgate-backend/data/config/` (admin, auth, email, check-in, registration invites).
|
||||
- Backend MySQL connection comes only from environment variables (`DB_DSN`, or `DB_HOST`/`DB_PORT`/`DB_USER`/`DB_PASSWORD`/`DB_NAME`); no credentials are hardcoded. Set them in `sproutgate-backend/.env` (gitignored) for local dev — it's auto-loaded on `go run .`.
|
||||
- Frontend API base URL can be set via `sproutgate-frontend/.env` using `VITE_API_BASE`.
|
||||
- Avoid committing real credentials or production tokens; use local overrides for secrets.
|
||||
- API reference lives in `sproutgate-backend/API_DOCS.md` and is served at `GET /api/docs`. Brief JSON overview: `GET /` and `GET /api`.
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
@echo off
|
||||
setlocal
|
||||
cd /d "%~dp0"
|
||||
call "%~dp0build-linux-amd64.bat"
|
||||
exit /b %errorlevel%
|
||||
@@ -22,15 +22,16 @@ echo [INFO] Working directory: %CD%
|
||||
echo [INFO] Building %APP_NAME% for linux amd64...
|
||||
echo [INFO] Output: %OUTPUT_FILE%
|
||||
|
||||
set "CGO_ENABLED=0"
|
||||
set "GOOS=linux"
|
||||
set "GOARCH=amd64"
|
||||
set "GOCACHE=%GOCACHE_DIR%"
|
||||
|
||||
echo [INFO] Regenerating Swagger docs (swag)...
|
||||
go run github.com/swaggo/swag/cmd/swag@latest init -g main.go -o docs --parseInternal
|
||||
if errorlevel 1 goto fail_build
|
||||
|
||||
set "CGO_ENABLED=0"
|
||||
set "GOOS=linux"
|
||||
set "GOARCH=amd64"
|
||||
|
||||
go build -trimpath -ldflags "-s -w" -o "%OUTPUT_FILE%" .
|
||||
if errorlevel 1 goto fail_build
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
//
|
||||
// 环境变量(与主程序相同):
|
||||
//
|
||||
// APP_ENV=production 使用生产数据库(默认使用开发测试库)
|
||||
// DB_DSN 自定义 DSN(优先级最高)
|
||||
// DB_HOST/DB_PORT/DB_USER/DB_PASSWORD/DB_NAME 连接参数(DB_NAME 默认 sproutgate)
|
||||
// DB_DSN 自定义 DSN(优先级最高)
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -8,6 +8,11 @@ services:
|
||||
environment:
|
||||
PORT: "8080"
|
||||
DATA_DIR: /data
|
||||
DB_HOST: ${DB_HOST}
|
||||
DB_PORT: ${DB_PORT:-3306}
|
||||
DB_USER: ${DB_USER}
|
||||
DB_PASSWORD: ${DB_PASSWORD}
|
||||
DB_NAME: ${DB_NAME:-sproutgate}
|
||||
volumes:
|
||||
- ./data:/data
|
||||
ports:
|
||||
|
||||
@@ -4,12 +4,40 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
// loadDotEnv 在当前工作目录查找 .env 文件并加载到进程环境变量中(已存在的变量不覆盖)。
|
||||
// 仅用于本地开发场景:生产部署通过 docker-compose / 容器环境变量直接注入,没有 .env 文件时静默跳过。
|
||||
func loadDotEnv() {
|
||||
data, err := os.ReadFile(".env")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
key, value, ok := strings.Cut(line, "=")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
key = strings.TrimSpace(key)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := os.LookupEnv(key); exists {
|
||||
continue
|
||||
}
|
||||
os.Setenv(key, strings.TrimSpace(value))
|
||||
}
|
||||
}
|
||||
|
||||
// DBConfig holds MySQL connection parameters.
|
||||
type DBConfig struct {
|
||||
Host string
|
||||
@@ -24,39 +52,42 @@ func (c DBConfig) DSN() string {
|
||||
c.User, c.Password, c.Host, c.Port, c.DBName)
|
||||
}
|
||||
|
||||
func devConfig() DBConfig {
|
||||
return DBConfig{
|
||||
Host: "10.1.1.100",
|
||||
Port: "3306",
|
||||
User: "sproutgate-test",
|
||||
Password: "sproutgate-test",
|
||||
DBName: "sproutgate-test",
|
||||
// envConfig 从环境变量读取连接参数。Host/User/Password 没有内置默认值,必须由
|
||||
// 部署环境(本地 shell、.env、docker-compose 等)显式提供;DBName 默认统一为
|
||||
// "sproutgate",开发和生产环境共用同一个库名。
|
||||
func envConfig() (DBConfig, error) {
|
||||
host := os.Getenv("DB_HOST")
|
||||
user := os.Getenv("DB_USER")
|
||||
password := os.Getenv("DB_PASSWORD")
|
||||
if host == "" || user == "" || password == "" {
|
||||
return DBConfig{}, fmt.Errorf("缺少数据库环境变量,请设置 DB_HOST/DB_USER/DB_PASSWORD(或直接设置 DB_DSN)")
|
||||
}
|
||||
}
|
||||
|
||||
func prodConfig() DBConfig {
|
||||
return DBConfig{
|
||||
Host: "192.168.1.100",
|
||||
Port: "3306",
|
||||
User: "sproutgate",
|
||||
Password: "sproutgate",
|
||||
DBName: "sproutgate",
|
||||
port := os.Getenv("DB_PORT")
|
||||
if port == "" {
|
||||
port = "3306"
|
||||
}
|
||||
dbName := os.Getenv("DB_NAME")
|
||||
if dbName == "" {
|
||||
dbName = "sproutgate"
|
||||
}
|
||||
|
||||
return DBConfig{Host: host, Port: port, User: user, Password: password, DBName: dbName}, nil
|
||||
}
|
||||
|
||||
// Open 根据环境变量打开数据库连接。
|
||||
// 优先级:DB_DSN > APP_ENV(production/prod -> 生产库) > 默认开发库
|
||||
// 优先级:DB_DSN(完整自定义连接串)> DB_HOST/DB_PORT/DB_USER/DB_PASSWORD/DB_NAME
|
||||
func Open() (*gorm.DB, error) {
|
||||
loadDotEnv()
|
||||
|
||||
dsn := os.Getenv("DB_DSN")
|
||||
if dsn == "" {
|
||||
env := os.Getenv("APP_ENV")
|
||||
if env == "production" || env == "prod" {
|
||||
dsn = prodConfig().DSN()
|
||||
log.Println("[database] 使用生产数据库 192.168.1.100:3306/sproutgate")
|
||||
} else {
|
||||
dsn = devConfig().DSN()
|
||||
log.Println("[database] 使用开发数据库 10.1.1.100:3306/sproutgate-test")
|
||||
cfg, err := envConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dsn = cfg.DSN()
|
||||
log.Printf("[database] 使用数据库 %s:%s/%s", cfg.Host, cfg.Port, cfg.DBName)
|
||||
} else {
|
||||
log.Println("[database] 使用自定义 DB_DSN")
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `main.go` | 入口:连接数据库、初始化 `Store`、注册路由 |
|
||||
| `internal/database/` | MySQL 连接与环境选择(`DB_DSN` / `APP_ENV`) |
|
||||
| `internal/database/` | MySQL 连接(`DB_DSN` 或 `DB_HOST`/`DB_USER`/`DB_PASSWORD`/`DB_NAME`/`DB_PORT`,支持 `.env` 本地加载) |
|
||||
| `internal/handlers/` | HTTP 处理:认证、资料、签到、公开页、管理端等 |
|
||||
| `internal/models/` | 领域模型(用户、待激活、重置密码、辅助邮箱等) |
|
||||
| `internal/storage/` | 持久化:`Store` + GORM 模型与业务方法 |
|
||||
@@ -25,24 +25,35 @@
|
||||
|
||||
## 环境与数据库
|
||||
|
||||
连接字符串优先级:**`DB_DSN` > 内置规则**。
|
||||
代码中**不再内置任何数据库地址或账号密码**,连接参数完全来自环境变量,连接字符串优先级:**`DB_DSN` > `DB_HOST`/`DB_PORT`/`DB_USER`/`DB_PASSWORD`/`DB_NAME`**。
|
||||
|
||||
1. **若设置 `DB_DSN`**:直接使用该完整 DSN(适合 CI、容器或临时切换)。
|
||||
2. **否则**:根据 `APP_ENV` 选择内置库:
|
||||
- `APP_ENV=production` 或 `prod` → 生产库:`192.168.1.100:3306` / 库名 `sproutgate` / 用户 `sproutgate`
|
||||
- 未设置或其它值 → 开发/测试库:`10.1.1.100:3306` / 库名 `sproutgate-test` / 用户 `sproutgate-test`
|
||||
2. **否则**:用 `DB_HOST`/`DB_USER`/`DB_PASSWORD` 拼接 DSN,`DB_HOST`/`DB_USER`/`DB_PASSWORD` 三者缺一即报错退出;`DB_PORT` 默认 `3306`,`DB_NAME` 默认 `sproutgate`(开发、生产统一用这个库名,仅 host/账号不同)。
|
||||
|
||||
本地开发时,在 `sproutgate-backend/.env`(已在 `.gitignore` 中忽略,不会提交)里写:
|
||||
|
||||
```
|
||||
DB_HOST=10.1.1.100
|
||||
DB_PORT=3306
|
||||
DB_USER=sproutgate-test
|
||||
DB_PASSWORD=sproutgate-test
|
||||
DB_NAME=sproutgate
|
||||
```
|
||||
|
||||
`go run .` / `go run ./cmd/migrate` 启动时会自动从当前目录读取并加载 `.env`(已存在的环境变量不会被覆盖);生产部署通过容器/`docker-compose` 环境变量注入,不依赖 `.env` 文件。
|
||||
|
||||
其它常用环境变量:
|
||||
|
||||
| 变量 | 说明 | 默认 |
|
||||
|------|------|------|
|
||||
| `PORT` | HTTP 监听端口 | `8080` |
|
||||
| `APP_ENV` | 影响默认 MySQL 选择与 `/api/health` 返回的 `env` | 空则 health 中为 `development` |
|
||||
| `APP_ENV` | 仅影响 `/api/health` 返回的 `env` 字段,不再决定数据库选择 | 空则 health 中为 `development` |
|
||||
| `GIN_MODE` | `release` 时 GORM 日志级别更安静(警告级) | debug 模式 |
|
||||
| `DB_DSN` | 完整 MySQL DSN,覆盖上述内置地址 | 未设置 |
|
||||
| `DB_DSN` | 完整 MySQL DSN,覆盖 `DB_HOST` 等单项配置 | 未设置 |
|
||||
| `DB_HOST` / `DB_PORT` / `DB_USER` / `DB_PASSWORD` / `DB_NAME` | MySQL 连接参数;`DB_HOST`/`DB_USER`/`DB_PASSWORD` 必填,无内置默认值 | `DB_PORT=3306`,`DB_NAME=sproutgate` |
|
||||
| `GEO_LOOKUP_URL` | `GET /api/auth/me` 未带 `X-Visit-Location` 时,服务端按 IP 请求该基址反查展示地理位置(实现见 `internal/clientgeo`,会拼接 `?ip=`) | 默认 `https://cf-ip-geo.smyhub.com/api` |
|
||||
|
||||
**建议**:生产部署时设置 `APP_ENV=production`,并确保 MySQL 防火墙与账号权限正确;敏感连接信息也可只通过 `DB_DSN` 注入,无需改代码。
|
||||
**建议**:生产部署通过 `docker-compose.yml` 旁的 `.env`(同样被 `.gitignore` 忽略)注入 `DB_HOST`/`DB_USER`/`DB_PASSWORD`,不要把真实账号密码写进任何会提交到仓库的文件。
|
||||
|
||||
## 数据库表(AutoMigrate)
|
||||
|
||||
@@ -66,16 +77,18 @@
|
||||
在 **`sproutgate-backend`** 目录下:
|
||||
|
||||
```bash
|
||||
# 导入到当前环境对应的库(默认开发库,除非设置 APP_ENV 或 DB_DSN)
|
||||
# 导入到 .env(或当前环境变量)指向的库
|
||||
go run ./cmd/migrate --data-dir ./data
|
||||
```
|
||||
|
||||
Windows PowerShell 示例(写入生产库):
|
||||
Windows PowerShell 示例(临时指定生产库连接参数):
|
||||
|
||||
```powershell
|
||||
$env:APP_ENV = "production"
|
||||
$env:DB_HOST = "192.168.1.100"
|
||||
$env:DB_USER = "sproutgate"
|
||||
$env:DB_PASSWORD = "<生产密码>"
|
||||
go run ./cmd/migrate --data-dir ./data
|
||||
Remove-Item Env:APP_ENV # 可选:清除变量,避免影响本机其它命令
|
||||
Remove-Item Env:DB_HOST, Env:DB_USER, Env:DB_PASSWORD # 可选:清除变量,避免影响本机其它命令
|
||||
```
|
||||
|
||||
迁移内容:`data/config/*.json`(写入 `app_configs` 与 `invite_codes`)、`data/users/*.json`(写入 `users`)。对已存在主键执行 **UPSERT**:同账号、同配置键会更新为迁移文件中的值。
|
||||
|
||||
Reference in New Issue
Block a user