update
This commit is contained in:
41
scripts/migrate_refresh/main.go
Normal file
41
scripts/migrate_refresh/main.go
Normal file
@ -0,0 +1,41 @@
|
||||
// Command migrate_refresh 一次性建表脚本(与 scripts/sql/*.sql 同源,手写 DDL 以保持与项目"SQL 托管表结构"约定一致)。
|
||||
//
|
||||
// 运行:go run ./scripts/migrate_refresh
|
||||
// 可用环境变量 DB_DSN 覆盖连接串(默认 root:root@127.0.0.1:3306/db_dev)。
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dsn := os.Getenv("DB_DSN")
|
||||
if dsn == "" {
|
||||
dsn = "root:root@tcp(127.0.0.1:3306)/db_dev?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
}
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatalf("open db: %v", err)
|
||||
}
|
||||
stmt := `CREATE TABLE IF NOT EXISTS refresh_tokens (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
user_id INT UNSIGNED NOT NULL,
|
||||
token_hash VARCHAR(64) NOT NULL,
|
||||
expires_at INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
revoked TINYINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
user_agent VARCHAR(255) NOT NULL DEFAULT '',
|
||||
ip VARCHAR(64) NOT NULL DEFAULT '',
|
||||
created_at INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_token_hash (token_hash),
|
||||
KEY idx_user_id (user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`
|
||||
if err := db.Exec(stmt).Error; err != nil {
|
||||
log.Fatalf("create refresh_tokens: %v", err)
|
||||
}
|
||||
log.Println("✓ refresh_tokens ready")
|
||||
}
|
||||
95
scripts/seed_users/main.go
Normal file
95
scripts/seed_users/main.go
Normal file
@ -0,0 +1,95 @@
|
||||
// Command seed_users 预置内部账号(运营/编辑用,不开放公开注册)。
|
||||
//
|
||||
// 项目已移除 AutoMigrate,表结构由 scripts/sql/005_create_users.sql 托管;
|
||||
// 本脚本用 CREATE TABLE IF NOT EXISTS 兜底建表,再 upsert 一个内部账号。
|
||||
//
|
||||
// 用法:
|
||||
// go run ./scripts/seed_users
|
||||
// SEED_ADMIN_PASSWORD='你的强密码' go run ./scripts/seed_users
|
||||
//
|
||||
// 默认账号 admin / admin@studio.local,密码取环境变量 SEED_ADMIN_PASSWORD,
|
||||
// 缺省回落强密码常量。生产部署请务必通过环境变量指定并尽快修改。
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"fashionapi/internal/config"
|
||||
"fashionapi/internal/database"
|
||||
"fashionapi/internal/model"
|
||||
)
|
||||
|
||||
func main() {
|
||||
configPath := flag.String("config", "", "配置文件路径,默认查找 configs/config.yml")
|
||||
flag.Parse()
|
||||
|
||||
cfg, err := config.Load(*configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("✗ 加载配置失败: %v", err)
|
||||
}
|
||||
db, err := database.New(cfg.Database)
|
||||
if err != nil {
|
||||
log.Fatalf("✗ 连接数据库失败: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
if e := database.Close(db); e != nil {
|
||||
log.Printf("! 关闭数据库连接失败: %v", e)
|
||||
}
|
||||
}()
|
||||
|
||||
// 1. 兜底建表(与 scripts/sql/005_create_users.sql 完全一致)
|
||||
createSQL := `CREATE TABLE IF NOT EXISTS users (
|
||||
id int unsigned NOT NULL AUTO_INCREMENT,
|
||||
created_at int unsigned NOT NULL DEFAULT 0,
|
||||
updated_at int unsigned NOT NULL DEFAULT 0,
|
||||
username varchar(64) NOT NULL,
|
||||
email varchar(191) NOT NULL,
|
||||
password_hash varchar(255) NOT NULL DEFAULT '',
|
||||
is_deleted tinyint unsigned NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_username (username),
|
||||
UNIQUE KEY uk_email (email)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`
|
||||
if e := db.Exec(createSQL).Error; e != nil {
|
||||
log.Fatalf("✗ 建表失败: %v", e)
|
||||
}
|
||||
log.Println("✓ users 表就绪")
|
||||
|
||||
// 2. 预置内部账号
|
||||
const username = "admin"
|
||||
const email = "admin@studio.local"
|
||||
password := os.Getenv("SEED_ADMIN_PASSWORD")
|
||||
if password == "" {
|
||||
password = "Studio#2026!Admin"
|
||||
}
|
||||
hash, e := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if e != nil {
|
||||
log.Fatalf("✗ 生成密码哈希失败: %v", e)
|
||||
}
|
||||
|
||||
var existing model.User
|
||||
qerr := db.Where("username = ?", username).First(&existing).Error
|
||||
switch {
|
||||
case errors.Is(qerr, gorm.ErrRecordNotFound):
|
||||
user := model.User{Username: username, Email: email, PasswordHash: string(hash)}
|
||||
if cerr := db.Create(&user).Error; cerr != nil {
|
||||
log.Fatalf("✗ 创建内部账号失败: %v", cerr)
|
||||
}
|
||||
log.Printf("✓ 已创建内部账号: %s / %s", username, email)
|
||||
case qerr != nil:
|
||||
log.Fatalf("✗ 查询账号失败: %v", qerr)
|
||||
default:
|
||||
existing.PasswordHash = string(hash)
|
||||
if uerr := db.Save(&existing).Error; uerr != nil {
|
||||
log.Fatalf("✗ 更新账号密码失败: %v", uerr)
|
||||
}
|
||||
log.Printf("✓ 内部账号已存在,已刷新密码哈希: %s", username)
|
||||
}
|
||||
log.Printf("ℹ 登录账号: %s 密码: %s(请尽快修改默认密码)", username, password)
|
||||
}
|
||||
14
scripts/sql/005_create_users.sql
Normal file
14
scripts/sql/005_create_users.sql
Normal file
@ -0,0 +1,14 @@
|
||||
-- 账号体系用户表(内部运营/编辑账号,不开放公开注册)。
|
||||
-- 与 scripts/seed_users/main.go 中内置的建表语句保持一致。
|
||||
CREATE TABLE IF NOT EXISTS `users` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`created_at` int unsigned NOT NULL DEFAULT 0,
|
||||
`updated_at` int unsigned NOT NULL DEFAULT 0,
|
||||
`username` varchar(64) NOT NULL,
|
||||
`email` varchar(191) NOT NULL,
|
||||
`password_hash` varchar(255) NOT NULL DEFAULT '',
|
||||
`is_deleted` tinyint unsigned NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_username` (`username`),
|
||||
UNIQUE KEY `uk_email` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
15
scripts/sql/006_create_refresh_tokens.sql
Normal file
15
scripts/sql/006_create_refresh_tokens.sql
Normal file
@ -0,0 +1,15 @@
|
||||
-- 006: refresh_tokens —— 刷新令牌表(双令牌续期 + 踢下线的服务端状态)
|
||||
-- 与项目约定一致:表结构由 SQL 托管,不依赖 GORM AutoMigrate。
|
||||
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
user_id INT UNSIGNED NOT NULL,
|
||||
token_hash VARCHAR(64) NOT NULL,
|
||||
expires_at INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
revoked TINYINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
user_agent VARCHAR(255) NOT NULL DEFAULT '',
|
||||
ip VARCHAR(64) NOT NULL DEFAULT '',
|
||||
created_at INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_token_hash (token_hash),
|
||||
KEY idx_user_id (user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
Reference in New Issue
Block a user