M0-M4: 推倒重来基线(基建+用户/密钥/核心代理+前端+管理后台+三协议互转)
- 后端 Go+Gin+GORM: 配置(OT_ env)/SQLite/Postgres 双驱动、用户体系(argon2id+JWT access/refresh)、 API Key(sk- 48位, 仅存 SHA-256 哈希) - 代理网关: /v1/chat/completions、/v1/responses、/v1/messages、/v1/models;错误按客户端协议返回 - 三协议互转(convert 包): Chat↔Messages↔Responses 请求/响应 + 流式 SSE 逐事件转换(直通优先) - 用量计费: 异步批量记账、余额扣减、balance_logs、usage_daily 日聚合 - 管理 API: 用户/渠道 CRUD+测试+模型导入/模型定价+绑定/统计/系统配置 - 前端 Vue3+TS+Tailwind(taste-skill 设计 tokens): Landing/登录注册/控制台/管理后台, 自建组件+Phosphor 图标+自建 SVG 趋势图, 已过 web-design-guidelines 复查 - mock 上游: OpenAI+Anthropic 双协议模拟(含流式) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,9 @@ package store
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
@@ -16,6 +19,10 @@ func Open(driver, dsn string) (*gorm.DB, error) {
|
||||
case "postgres":
|
||||
dialector = postgresDialector(dsn)
|
||||
default:
|
||||
// 确保 SQLite 文件所在目录存在
|
||||
if dir := sqliteDir(dsn); dir != "" {
|
||||
_ = os.MkdirAll(dir, 0o755)
|
||||
}
|
||||
dialector = sqlite.Open(dsn)
|
||||
}
|
||||
|
||||
@@ -32,3 +39,18 @@ func Open(driver, dsn string) (*gorm.DB, error) {
|
||||
log.Printf("store: connected driver=%s (migrated)", driver)
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// sqliteDir 提取 SQLite DSN 中的目录部分(忽略 file: 前缀与查询参数)。
|
||||
func sqliteDir(dsn string) string {
|
||||
d := dsn
|
||||
if i := strings.IndexByte(d, '?'); i >= 0 {
|
||||
d = d[:i]
|
||||
}
|
||||
if strings.HasPrefix(d, "file:") {
|
||||
d = d[len("file:"):]
|
||||
}
|
||||
if d == "" || d == ":memory:" || strings.Contains(d, "::") {
|
||||
return ""
|
||||
}
|
||||
return filepath.Dir(d)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user