重构:移除环境变量管理员初始化

This commit is contained in:
yml2213
2026-08-13 14:21:22 +08:00
parent 538be52636
commit a637101ca8
7 changed files with 0 additions and 87 deletions
-2
View File
@@ -7,8 +7,6 @@ FRONTEND_PORT=15173
JWT_SECRET=your_very_secure_jwt_secret_here_change_me_in_production JWT_SECRET=your_very_secure_jwt_secret_here_change_me_in_production
DATA_ENCRYPTION_KEY=your_32_plus_char_data_encryption_key_change_me DATA_ENCRYPTION_KEY=your_32_plus_char_data_encryption_key_change_me
# 仅用于空数据库的首次启动;已有管理员账号时不会被覆盖。 # 仅用于空数据库的首次启动;已有管理员账号时不会被覆盖。
INITIAL_ADMIN_USERNAME=skin_nh_admin
INITIAL_ADMIN_PASSWORD=change_me_to_a_secure_password
POSTGRES_USER=affiliate POSTGRES_USER=affiliate
POSTGRES_PASSWORD=affiliate_dev_password POSTGRES_PASSWORD=affiliate_dev_password
POSTGRES_DB=affiliate_dash POSTGRES_DB=affiliate_dash
-4
View File
@@ -38,7 +38,6 @@ make dev
启动后: 启动后:
- 前端:`http://localhost:15173` - 前端:`http://localhost:15173`
- 后端:`http://localhost:18080` - 后端:`http://localhost:18080`
- 空数据库首次启动时由 `INITIAL_ADMIN_USERNAME` / `INITIAL_ADMIN_PASSWORD` 创建平台管理员
`Ctrl+C` 停止所有服务。 `Ctrl+C` 停止所有服务。
@@ -61,7 +60,6 @@ make frontend # 前端 :15173
cp .env.example .env cp .env.example .env
# 编辑 .env,确保以下变量已配置: # 编辑 .env,确保以下变量已配置:
# JWT_SECRET — 生产环境务必修改 # JWT_SECRET — 生产环境务必修改
# INITIAL_ADMIN_USERNAME / INITIAL_ADMIN_PASSWORD — 空数据库首次创建的平台管理员,不会覆盖已有账号
# OPEN_API_KEY — 皮肤源头对接 API Key # OPEN_API_KEY — 皮肤源头对接 API Key
# OPEN_API_SECRET — 签名密钥 # OPEN_API_SECRET — 签名密钥
# POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB — PostgreSQL 初始化配置 # POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB — PostgreSQL 初始化配置
@@ -104,8 +102,6 @@ make docker-down # 停止并移除
| `FRONTEND_PORT` | 前端开发端口 | `15173` | | `FRONTEND_PORT` | 前端开发端口 | `15173` |
| `HTTP_PORT` / `HTTPS_PORT` | Caddy 对外 HTTP/HTTPS 端口 | `80` / `443` | | `HTTP_PORT` / `HTTPS_PORT` | Caddy 对外 HTTP/HTTPS 端口 | `80` / `443` |
| `JWT_SECRET` | JWT 签名密钥 | 开发默认值 | | `JWT_SECRET` | JWT 签名密钥 | 开发默认值 |
| `INITIAL_ADMIN_USERNAME` | 空数据库首次创建的平台管理员用户名 | 必填(仅首次) |
| `INITIAL_ADMIN_PASSWORD` | 空数据库首次创建的平台管理员密码 | 必填,至少 8 位(仅首次) |
| `DATABASE_URL` | PostgreSQL 连接串 | 本地开发默认值 | | `DATABASE_URL` | PostgreSQL 连接串 | 本地开发默认值 |
| `DOCKER_DATABASE_URL` | Docker 后端容器内 PostgreSQL 连接串,通常无需设置 | Compose 内置默认值 | | `DOCKER_DATABASE_URL` | Docker 后端容器内 PostgreSQL 连接串,通常无需设置 | Compose 内置默认值 |
| `POSTGRES_PORT` | PostgreSQL 映射到宿主机的端口 | `15432` | | `POSTGRES_PORT` | PostgreSQL 映射到宿主机的端口 | `15432` |
-5
View File
@@ -84,10 +84,6 @@ func main() {
cfg.DeliveryLinkTTLMinutes, cfg.DeliveryLinkTTLMinutes,
) )
if err := authSvc.EnsureAdmin(cfg.InitialAdminUsername, cfg.InitialAdminPassword); err != nil {
log.Fatalf("ensure admin: %v", err)
}
openlog.Init(cfg.OpenAPIDebug) openlog.Init(cfg.OpenAPIDebug)
h := &router.Handlers{ h := &router.Handlers{
@@ -122,7 +118,6 @@ func main() {
addr := ":" + cfg.Port addr := ":" + cfg.Port
log.Printf("游戏皮肤供货平台 API 启动: http://localhost%s", addr) log.Printf("游戏皮肤供货平台 API 启动: http://localhost%s", addr)
log.Printf("数据库: PostgreSQL") log.Printf("数据库: PostgreSQL")
log.Printf("管理员账号已初始化;请通过账号菜单及时修改初始密码")
log.Printf("开放接口鉴权: X-App-Key + X-Timestamp + X-Nonce + X-Sign (HMAC-SHA256)") log.Printf("开放接口鉴权: X-App-Key + X-Timestamp + X-Nonce + X-Sign (HMAC-SHA256)")
if cfg.OpenAPIDebug { if cfg.OpenAPIDebug {
log.Printf("开放接口调试日志: 开启 (OPEN_API_DEBUG=0 可关闭)") log.Printf("开放接口调试日志: 开启 (OPEN_API_DEBUG=0 可关闭)")
-5
View File
@@ -14,9 +14,6 @@ type Config struct {
JWTSecret string JWTSecret string
DatabaseURL string DatabaseURL string
Mode string // debug / release Mode string // debug / release
// InitialAdminUsername 和 InitialAdminPassword 仅用于空数据库首次创建平台管理员。
InitialAdminUsername string
InitialAdminPassword string
// DataEncryptionKey 用于加密保存 API/回调密钥;为空时由 JWT_SECRET 派生。 // DataEncryptionKey 用于加密保存 API/回调密钥;为空时由 JWT_SECRET 派生。
DataEncryptionKey string DataEncryptionKey string
// OpenAPIKey 皮肤源头开放接口标识(Header: X-Api-Key,可公开给对接方) // OpenAPIKey 皮肤源头开放接口标识(Header: X-Api-Key,可公开给对接方)
@@ -70,8 +67,6 @@ func Load() *Config {
JWTSecret: getEnv("JWT_SECRET", "affiliate-dash-dev-secret-change-me"), JWTSecret: getEnv("JWT_SECRET", "affiliate-dash-dev-secret-change-me"),
DatabaseURL: getEnv("DATABASE_URL", "postgres://affiliate:affiliate_dev_password@127.0.0.1:15432/affiliate_dash?sslmode=disable&TimeZone=Asia/Shanghai"), DatabaseURL: getEnv("DATABASE_URL", "postgres://affiliate:affiliate_dev_password@127.0.0.1:15432/affiliate_dash?sslmode=disable&TimeZone=Asia/Shanghai"),
Mode: mode, Mode: mode,
InitialAdminUsername: getEnv("INITIAL_ADMIN_USERNAME", ""),
InitialAdminPassword: getEnv("INITIAL_ADMIN_PASSWORD", ""),
DataEncryptionKey: getEnv("DATA_ENCRYPTION_KEY", getEnv("JWT_SECRET", "affiliate-dash-dev-secret-change-me")), DataEncryptionKey: getEnv("DATA_ENCRYPTION_KEY", getEnv("JWT_SECRET", "affiliate-dash-dev-secret-change-me")),
OpenAPIKey: getEnv("OPEN_API_KEY", "sk_source_dev_key_change_me"), OpenAPIKey: getEnv("OPEN_API_KEY", "sk_source_dev_key_change_me"),
OpenAPISecret: getEnv("OPEN_API_SECRET", "sk_source_dev_secret_change_me"), OpenAPISecret: getEnv("OPEN_API_SECRET", "sk_source_dev_secret_change_me"),
-46
View File
@@ -153,49 +153,3 @@ func (s *AuthService) UpdateCurrentAccount(userID uint, in UpdateCurrentAccountI
} }
return updated, nil return updated, nil
} }
// EnsureAdmin creates the first platform administrator for an empty database.
// Existing administrator accounts are never changed by environment variables.
func (s *AuthService) EnsureAdmin(username, password string) error {
var count int64
s.db.Model(&model.User{}).Where("role = ?", model.RoleAdmin).Count(&count)
if count > 0 {
if s.tenant != nil {
var admins []model.User
if err := s.db.Where("role = ?", model.RoleAdmin).Find(&admins).Error; err != nil {
return err
}
for _, admin := range admins {
if err := s.tenant.EnsureSelfMember(admin.ID, model.MemberRoleOwner, admin.Status); err != nil {
return err
}
}
}
return nil
}
username = strings.TrimSpace(username)
if len(username) < 3 {
return errors.New("空数据库首次启动必须设置 INITIAL_ADMIN_USERNAME(至少 3 位)")
}
if len(password) < 8 {
return errors.New("空数据库首次启动必须设置 INITIAL_ADMIN_PASSWORD(至少 8 位)")
}
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return err
}
admin := &model.User{
Username: username,
PasswordHash: string(hash),
Nickname: username,
Role: model.RoleAdmin,
Status: 1,
}
if err := s.db.Create(admin).Error; err != nil {
return err
}
if s.tenant != nil {
return s.tenant.EnsureSelfMember(admin.ID, model.MemberRoleOwner, admin.Status)
}
return nil
}
-23
View File
@@ -336,26 +336,3 @@ func TestListMerchantAccountGroupsExcludesPlatformAdmins(t *testing.T) {
t.Fatalf("unexpected merchant account group: %+v", groups[0]) t.Fatalf("unexpected merchant account group: %+v", groups[0])
} }
} }
func TestEnsureAdminUsesConfiguredCredentialsOnlyForEmptyDatabase(t *testing.T) {
db := newServiceTestDB(t)
tenant := NewTenantService(db)
auth := NewAuthService(db, nil, tenant)
if err := auth.EnsureAdmin("skin_nh_admin", "secure-password"); err != nil {
t.Fatalf("create configured admin: %v", err)
}
var created model.User
if err := db.Where("username = ?", "skin_nh_admin").First(&created).Error; err != nil {
t.Fatalf("configured admin should exist: %v", err)
}
if err := bcrypt.CompareHashAndPassword([]byte(created.PasswordHash), []byte("secure-password")); err != nil {
t.Fatalf("configured password should match: %v", err)
}
if err := auth.EnsureAdmin("replacement-admin", "another-password"); err != nil {
t.Fatalf("existing admin should not be replaced: %v", err)
}
var count int64
if err := db.Model(&model.User{}).Where("role = ?", model.RoleAdmin).Count(&count).Error; err != nil || count != 1 {
t.Fatalf("existing admin should remain unchanged, count=%d err=%v", count, err)
}
}
-2
View File
@@ -40,8 +40,6 @@ services:
- PORT=8080 - PORT=8080
- TZ=${TZ:-Asia/Shanghai} - TZ=${TZ:-Asia/Shanghai}
- JWT_SECRET=${JWT_SECRET} - JWT_SECRET=${JWT_SECRET}
- INITIAL_ADMIN_USERNAME=${INITIAL_ADMIN_USERNAME:-}
- INITIAL_ADMIN_PASSWORD=${INITIAL_ADMIN_PASSWORD:-}
- "DATABASE_URL=${DOCKER_DATABASE_URL:-postgres://${POSTGRES_USER:-affiliate}:${POSTGRES_PASSWORD:-affiliate_dev_password}@postgres:5432/${POSTGRES_DB:-affiliate_dash}?sslmode=disable&TimeZone=${TZ:-Asia/Shanghai}}" - "DATABASE_URL=${DOCKER_DATABASE_URL:-postgres://${POSTGRES_USER:-affiliate}:${POSTGRES_PASSWORD:-affiliate_dev_password}@postgres:5432/${POSTGRES_DB:-affiliate_dash}?sslmode=disable&TimeZone=${TZ:-Asia/Shanghai}}"
- GIN_MODE=${GIN_MODE:-release} - GIN_MODE=${GIN_MODE:-release}
- CORS_ALLOWED_ORIGINS=${CORS_ALLOWED_ORIGINS:-} - CORS_ALLOWED_ORIGINS=${CORS_ALLOWED_ORIGINS:-}