c26b06f1cc
Go 控制面(SQLite + REST API)嵌入 Mihomo 数据面,Vue 3 多语言 Web 管理台。 含订阅/节点/规则/出站/监控/日志、OpenAPI、API Key、Gitea Actions CI 与开源文档; CI 使用 ubuntu-latest runner,Go/Docker 步骤走国内镜像与 shell,避免 GitHub 下载超时。 Co-authored-by: Cursor <cursoragent@cursor.com>
111 lines
2.8 KiB
Go
111 lines
2.8 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"time"
|
|
)
|
|
|
|
func (s *Store) ListAPIKeys(ctx context.Context) ([]APIKey, error) {
|
|
rows, err := s.db.QueryContext(ctx, `
|
|
SELECT id, name, key_prefix, enabled, last_used_at, created_at
|
|
FROM api_keys
|
|
ORDER BY id DESC`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
return scanAPIKeys(rows)
|
|
}
|
|
|
|
func (s *Store) CreateAPIKey(ctx context.Context, name, keyPrefix, keyHash string) (*APIKey, error) {
|
|
res, err := s.db.ExecContext(ctx, `
|
|
INSERT INTO api_keys(name, key_prefix, key_hash, enabled)
|
|
VALUES(?,?,?,1)`, name, keyPrefix, keyHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id, _ := res.LastInsertId()
|
|
return s.GetAPIKey(ctx, id)
|
|
}
|
|
|
|
func (s *Store) GetAPIKey(ctx context.Context, id int64) (*APIKey, error) {
|
|
row := s.db.QueryRowContext(ctx, `
|
|
SELECT id, name, key_prefix, enabled, last_used_at, created_at
|
|
FROM api_keys WHERE id=?`, id)
|
|
return scanAPIKey(row)
|
|
}
|
|
|
|
func (s *Store) DeleteAPIKey(ctx context.Context, id int64) error {
|
|
_, err := s.db.ExecContext(ctx, `DELETE FROM api_keys WHERE id=?`, id)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) VerifyAPIKey(ctx context.Context, keyHash string) (bool, error) {
|
|
var id int64
|
|
err := s.db.QueryRowContext(ctx, `
|
|
SELECT id FROM api_keys WHERE key_hash=? AND enabled=1`, keyHash).Scan(&id)
|
|
if err == sql.ErrNoRows {
|
|
return false, nil
|
|
}
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
_, _ = s.db.ExecContext(ctx, `UPDATE api_keys SET last_used_at=datetime('now') WHERE id=?`, id)
|
|
return true, nil
|
|
}
|
|
|
|
func scanAPIKeys(rows *sql.Rows) ([]APIKey, error) {
|
|
var out []APIKey
|
|
for rows.Next() {
|
|
item, err := scanAPIKeyFromRows(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, *item)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func scanAPIKey(row *sql.Row) (*APIKey, error) {
|
|
var item APIKey
|
|
var enabled int
|
|
var lastUsed, created sql.NullString
|
|
if err := row.Scan(&item.ID, &item.Name, &item.KeyPrefix, &enabled, &lastUsed, &created); err != nil {
|
|
return nil, err
|
|
}
|
|
item.Enabled = enabled == 1
|
|
if lastUsed.Valid {
|
|
if t, err := time.Parse("2006-01-02 15:04:05", lastUsed.String); err == nil {
|
|
item.LastUsedAt = &t
|
|
}
|
|
}
|
|
if created.Valid {
|
|
if t, err := time.Parse("2006-01-02 15:04:05", created.String); err == nil {
|
|
item.CreatedAt = t
|
|
}
|
|
}
|
|
return &item, nil
|
|
}
|
|
|
|
func scanAPIKeyFromRows(rows *sql.Rows) (*APIKey, error) {
|
|
var item APIKey
|
|
var enabled int
|
|
var lastUsed, created sql.NullString
|
|
if err := rows.Scan(&item.ID, &item.Name, &item.KeyPrefix, &enabled, &lastUsed, &created); err != nil {
|
|
return nil, err
|
|
}
|
|
item.Enabled = enabled == 1
|
|
if lastUsed.Valid {
|
|
if t, err := time.Parse("2006-01-02 15:04:05", lastUsed.String); err == nil {
|
|
item.LastUsedAt = &t
|
|
}
|
|
}
|
|
if created.Valid {
|
|
if t, err := time.Parse("2006-01-02 15:04:05", created.String); err == nil {
|
|
item.CreatedAt = t
|
|
}
|
|
}
|
|
return &item, nil
|
|
}
|