- 后端 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>
49 lines
1.2 KiB
Go
49 lines
1.2 KiB
Go
// Package apikey 生成与管理 API Key:sk- + 48 位 base62 随机串。
|
||
// 库中仅存 SHA-256 哈希与展示前缀(PLANNING §4.3.3)。
|
||
package apikey
|
||
|
||
import (
|
||
"crypto/rand"
|
||
"crypto/sha256"
|
||
"encoding/hex"
|
||
"strings"
|
||
)
|
||
|
||
const (
|
||
alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||
keyLen = 48
|
||
prefix = "sk-"
|
||
)
|
||
|
||
// Generate 生成明文 key(仅创建时展示一次)与哈希、前缀。
|
||
func Generate() (plain, hash, keyPrefix string, err error) {
|
||
buf := make([]byte, keyLen)
|
||
if _, err = rand.Read(buf); err != nil {
|
||
return "", "", "", err
|
||
}
|
||
for i := range buf {
|
||
buf[i] = alphabet[int(buf[i])%len(alphabet)]
|
||
}
|
||
plain = prefix + string(buf)
|
||
return plain, Hash(plain), Prefix(plain), nil
|
||
}
|
||
|
||
// Hash 返回 key 的 SHA-256 十六进制。
|
||
func Hash(key string) string {
|
||
sum := sha256.Sum256([]byte(key))
|
||
return hex.EncodeToString(sum[:])
|
||
}
|
||
|
||
// Prefix 展示前缀:sk-aB3cD5…(前 12 字符)
|
||
func Prefix(key string) string {
|
||
if len(key) <= 12 {
|
||
return key
|
||
}
|
||
return key[:12]
|
||
}
|
||
|
||
// Valid 校验明文格式。
|
||
func Valid(key string) bool {
|
||
return strings.HasPrefix(key, prefix) && len(key) == len(prefix)+keyLen
|
||
}
|