单二进制三角色 + Dock 适配器 + Swarm stack 达到可部署态;mgr1 实测订阅经 central-proxy bootstrap,探活 alive=41/52。 Co-authored-by: Cursor <cursoragent@cursor.com>
52 lines
1.4 KiB
Go
52 lines
1.4 KiB
Go
// storeglue_scan.go:api_keys 行扫描(store.scanKey 未导出,镜像之;仅显式列名)。
|
||
package gateway
|
||
|
||
import (
|
||
"database/sql"
|
||
"strings"
|
||
"time"
|
||
|
||
"onesvm.com/onesvm/browser-server/internal/config"
|
||
"onesvm.com/onesvm/browser-server/internal/store"
|
||
)
|
||
|
||
// _ 保留 sql 包引用(Raw 返回值使用)。
|
||
var _ = sql.ErrNoRows
|
||
|
||
// DBGlue store.DB 门面(gateway 内对 store 的扩展查询统一入口)。
|
||
type DBGlue struct {
|
||
db *store.DB
|
||
}
|
||
|
||
// NewDBGlue 构造。
|
||
func NewDBGlue(db *store.DB) *DBGlue { return &DBGlue{db: db} }
|
||
|
||
// Raw 暴露底层连接(KeysAll/ConsumerByName 共用)。
|
||
func (d *DBGlue) Raw() *sql.DB { return d.db.Raw() }
|
||
|
||
// scanKeyPublic 镜像 store.scanKey(列序与 keys.go keyCols 一致)。
|
||
func scanKeyPublic(row interface{ Scan(...any) error }) (*store.ApiKey, error) {
|
||
var k store.ApiKey
|
||
var scopes, createdAt string
|
||
var expires sql.NullString
|
||
if err := row.Scan(&k.ID, &k.ConsumerID, &k.Prefix, &k.Name, &k.Hash, &k.Salt,
|
||
&scopes, &k.RPM, &k.DailyQuota, &k.MonthlyQuota, &k.ConcurrentSessions,
|
||
&k.Status, &expires, &createdAt); err != nil {
|
||
return nil, err
|
||
}
|
||
k.Scopes = strings.Split(scopes, ",")
|
||
if expires.Valid && expires.String != "" {
|
||
t, err := time.Parse(time.RFC3339, expires.String)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
tz := t.In(config.TZ)
|
||
k.ExpiresAt = &tz
|
||
}
|
||
ct, err := time.Parse(time.RFC3339, createdAt)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
k.CreatedAt = ct
|
||
return &k, nil
|
||
}
|