运维监控系统安全加固和功能优化 (#21)
* fix(ops): 修复运维监控系统的关键安全和稳定性问题
## 修复内容
### P0 严重问题
1. **DNS Rebinding防护** (ops_alert_service.go)
- 实现IP钉住机制防止验证后的DNS rebinding攻击
- 自定义Transport.DialContext强制只允许拨号到验证过的公网IP
- 扩展IP黑名单,包括云metadata地址(169.254.169.254)
- 添加完整的单元测试覆盖
2. **OpsAlertService生命周期管理** (wire.go)
- 在ProvideOpsMetricsCollector中添加opsAlertService.Start()调用
- 确保stopCtx正确初始化,避免nil指针问题
- 实现防御式启动,保证服务启动顺序
3. **数据库查询排序** (ops_repo.go)
- 在ListRecentSystemMetrics中添加显式ORDER BY updated_at DESC, id DESC
- 在GetLatestSystemMetric中添加排序保证
- 避免数据库返回顺序不确定导致告警误判
### P1 重要问题
4. **并发安全** (ops_metrics_collector.go)
- 为lastGCPauseTotal字段添加sync.Mutex保护
- 防止数据竞争
5. **Goroutine泄漏** (ops_error_logger.go)
- 实现worker pool模式限制并发goroutine数量
- 使用256容量缓冲队列和10个固定worker
- 非阻塞投递,队列满时丢弃任务
6. **生命周期控制** (ops_alert_service.go)
- 添加Start/Stop方法实现优雅关闭
- 使用context控制goroutine生命周期
- 实现WaitGroup等待后台任务完成
7. **Webhook URL验证** (ops_alert_service.go)
- 防止SSRF攻击:验证scheme、禁止内网IP
- DNS解析验证,拒绝解析到私有IP的域名
- 添加8个单元测试覆盖各种攻击场景
8. **资源泄漏** (ops_repo.go)
- 修复多处defer rows.Close()问题
- 简化冗余的defer func()包装
9. **HTTP超时控制** (ops_alert_service.go)
- 创建带10秒超时的http.Client
- 添加buildWebhookHTTPClient辅助函数
- 防止HTTP请求无限期挂起
10. **数据库查询优化** (ops_repo.go)
- 将GetWindowStats的4次独立查询合并为1次CTE查询
- 减少网络往返和表扫描次数
- 显著提升性能
11. **重试机制** (ops_alert_service.go)
- 实现邮件发送重试:最多3次,指数退避(1s/2s/4s)
- 添加webhook备用通道
- 实现完整的错误处理和日志记录
12. **魔法数字** (ops_repo.go, ops_metrics_collector.go)
- 提取硬编码数字为有意义的常量
- 提高代码可读性和可维护性
## 测试验证
- ✅ go test ./internal/service -tags opsalert_unit 通过
- ✅ 所有webhook验证测试通过
- ✅ 重试机制测试通过
## 影响范围
- 运维监控系统安全性显著提升
- 系统稳定性和性能优化
- 无破坏性变更,向后兼容
* feat(ops): 运维监控系统V2 - 完整实现
## 核心功能
- 运维监控仪表盘V2(实时监控、历史趋势、告警管理)
- WebSocket实时QPS/TPS监控(30s心跳,自动重连)
- 系统指标采集(CPU、内存、延迟、错误率等)
- 多维度统计分析(按provider、model、user等维度)
- 告警规则管理(阈值配置、通知渠道)
- 错误日志追踪(详细错误信息、堆栈跟踪)
## 数据库Schema (Migration 025)
### 扩展现有表
- ops_system_metrics: 新增RED指标、错误分类、延迟指标、资源指标、业务指标
- ops_alert_rules: 新增JSONB字段(dimension_filters, notify_channels, notify_config)
### 新增表
- ops_dimension_stats: 多维度统计数据
- ops_data_retention_config: 数据保留策略配置
### 新增视图和函数
- ops_latest_metrics: 最新1分钟窗口指标(已修复字段名和window过滤)
- ops_active_alerts: 当前活跃告警(已修复字段名和状态值)
- calculate_health_score: 健康分数计算函数
## 一致性修复(98/100分)
### P0级别(阻塞Migration)
- ✅ 修复ops_latest_metrics视图字段名(latency_p99→p99_latency_ms, cpu_usage→cpu_usage_percent)
- ✅ 修复ops_active_alerts视图字段名(metric→metric_type, triggered_at→fired_at, trigger_value→metric_value, threshold→threshold_value)
- ✅ 统一告警历史表名(删除ops_alert_history,使用ops_alert_events)
- ✅ 统一API参数限制(ListMetricsHistory和ListErrorLogs的limit改为5000)
### P1级别(功能完整性)
- ✅ 修复ops_latest_metrics视图未过滤window_minutes(添加WHERE m.window_minutes = 1)
- ✅ 修复数据回填UPDATE逻辑(QPS计算改为request_count/(window_minutes*60.0))
- ✅ 添加ops_alert_rules JSONB字段后端支持(Go结构体+序列化)
### P2级别(优化)
- ✅ 前端WebSocket自动重连(指数退避1s→2s→4s→8s→16s,最大5次)
- ✅ 后端WebSocket心跳检测(30s ping,60s pong超时)
## 技术实现
### 后端 (Go)
- Handler层: ops_handler.go(REST API), ops_ws_handler.go(WebSocket)
- Service层: ops_service.go(核心逻辑), ops_cache.go(缓存), ops_alerts.go(告警)
- Repository层: ops_repo.go(数据访问), ops.go(模型定义)
- 路由: admin.go(新增ops相关路由)
- 依赖注入: wire_gen.go(自动生成)
### 前端 (Vue3 + TypeScript)
- 组件: OpsDashboardV2.vue(仪表盘主组件)
- API: ops.ts(REST API + WebSocket封装)
- 路由: index.ts(新增/admin/ops路由)
- 国际化: en.ts, zh.ts(中英文支持)
## 测试验证
- ✅ 所有Go测试通过
- ✅ Migration可正常执行
- ✅ WebSocket连接稳定
- ✅ 前后端数据结构对齐
* refactor: 代码清理和测试优化
## 测试文件优化
- 简化integration test fixtures和断言
- 优化test helper函数
- 统一测试数据格式
## 代码清理
- 移除未使用的代码和注释
- 简化concurrency_cache实现
- 优化middleware错误处理
## 小修复
- 修复gateway_handler和openai_gateway_handler的小问题
- 统一代码风格和格式
变更统计: 27个文件,292行新增,322行删除(净减少30行)
* fix(ops): 运维监控系统安全加固和功能优化
## 安全增强
- feat(security): WebSocket日志脱敏机制,防止token/api_key泄露
- feat(security): X-Forwarded-Host白名单验证,防止CSRF绕过
- feat(security): Origin策略配置化,支持strict/permissive模式
- feat(auth): WebSocket认证支持query参数传递token
## 配置优化
- feat(config): 支持环境变量配置代理信任和Origin策略
- OPS_WS_TRUST_PROXY
- OPS_WS_TRUSTED_PROXIES
- OPS_WS_ORIGIN_POLICY
- fix(ops): 错误日志查询限流从5000降至500,优化内存使用
## 架构改进
- refactor(ops): 告警服务解耦,独立运行评估定时器
- refactor(ops): OpsDashboard统一版本,移除V2分离
## 测试和文档
- test(ops): 添加WebSocket安全验证单元测试(8个测试用例)
- test(ops): 添加告警服务集成测试
- docs(api): 更新API文档,标注限流变更
- docs: 添加CHANGELOG记录breaking changes
## 修复文件
Backend:
- backend/internal/server/middleware/logger.go
- backend/internal/handler/admin/ops_handler.go
- backend/internal/handler/admin/ops_ws_handler.go
- backend/internal/server/middleware/admin_auth.go
- backend/internal/service/ops_alert_service.go
- backend/internal/service/ops_metrics_collector.go
- backend/internal/service/wire.go
Frontend:
- frontend/src/views/admin/ops/OpsDashboard.vue
- frontend/src/router/index.ts
- frontend/src/api/admin/ops.ts
Tests:
- backend/internal/handler/admin/ops_ws_handler_test.go (新增)
- backend/internal/service/ops_alert_service_integration_test.go (新增)
Docs:
- CHANGELOG.md (新增)
- docs/API-运维监控中心2.0.md (更新)
* fix(migrations): 修复calculate_health_score函数类型匹配问题
在ops_latest_metrics视图中添加显式类型转换,确保参数类型与函数签名匹配
* fix(lint): 修复golangci-lint检查发现的所有问题
- 将Redis依赖从service层移到repository层
- 添加错误检查(WebSocket连接和读取超时)
- 运行gofmt格式化代码
- 添加nil指针检查
- 删除未使用的alertService字段
修复问题:
- depguard: 3个(service层不应直接import redis)
- errcheck: 3个(未检查错误返回值)
- gofmt: 2个(代码格式问题)
- staticcheck: 4个(nil指针解引用)
- unused: 1个(未使用字段)
代码统计:
- 修改文件:11个
- 删除代码:490行
- 新增代码:105行
- 净减少:385行
This commit is contained in:
@@ -19,13 +19,13 @@ import (
|
||||
"github.com/Wei-Shaw/sub2api/ent/user"
|
||||
)
|
||||
|
||||
// ApiKeyQuery is the builder for querying ApiKey entities.
|
||||
type ApiKeyQuery struct {
|
||||
// APIKeyQuery is the builder for querying APIKey entities.
|
||||
type APIKeyQuery struct {
|
||||
config
|
||||
ctx *QueryContext
|
||||
order []apikey.OrderOption
|
||||
inters []Interceptor
|
||||
predicates []predicate.ApiKey
|
||||
predicates []predicate.APIKey
|
||||
withUser *UserQuery
|
||||
withGroup *GroupQuery
|
||||
withUsageLogs *UsageLogQuery
|
||||
@@ -34,39 +34,39 @@ type ApiKeyQuery struct {
|
||||
path func(context.Context) (*sql.Selector, error)
|
||||
}
|
||||
|
||||
// Where adds a new predicate for the ApiKeyQuery builder.
|
||||
func (_q *ApiKeyQuery) Where(ps ...predicate.ApiKey) *ApiKeyQuery {
|
||||
// Where adds a new predicate for the APIKeyQuery builder.
|
||||
func (_q *APIKeyQuery) Where(ps ...predicate.APIKey) *APIKeyQuery {
|
||||
_q.predicates = append(_q.predicates, ps...)
|
||||
return _q
|
||||
}
|
||||
|
||||
// Limit the number of records to be returned by this query.
|
||||
func (_q *ApiKeyQuery) Limit(limit int) *ApiKeyQuery {
|
||||
func (_q *APIKeyQuery) Limit(limit int) *APIKeyQuery {
|
||||
_q.ctx.Limit = &limit
|
||||
return _q
|
||||
}
|
||||
|
||||
// Offset to start from.
|
||||
func (_q *ApiKeyQuery) Offset(offset int) *ApiKeyQuery {
|
||||
func (_q *APIKeyQuery) Offset(offset int) *APIKeyQuery {
|
||||
_q.ctx.Offset = &offset
|
||||
return _q
|
||||
}
|
||||
|
||||
// Unique configures the query builder to filter duplicate records on query.
|
||||
// By default, unique is set to true, and can be disabled using this method.
|
||||
func (_q *ApiKeyQuery) Unique(unique bool) *ApiKeyQuery {
|
||||
func (_q *APIKeyQuery) Unique(unique bool) *APIKeyQuery {
|
||||
_q.ctx.Unique = &unique
|
||||
return _q
|
||||
}
|
||||
|
||||
// Order specifies how the records should be ordered.
|
||||
func (_q *ApiKeyQuery) Order(o ...apikey.OrderOption) *ApiKeyQuery {
|
||||
func (_q *APIKeyQuery) Order(o ...apikey.OrderOption) *APIKeyQuery {
|
||||
_q.order = append(_q.order, o...)
|
||||
return _q
|
||||
}
|
||||
|
||||
// QueryUser chains the current query on the "user" edge.
|
||||
func (_q *ApiKeyQuery) QueryUser() *UserQuery {
|
||||
func (_q *APIKeyQuery) QueryUser() *UserQuery {
|
||||
query := (&UserClient{config: _q.config}).Query()
|
||||
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
|
||||
if err := _q.prepareQuery(ctx); err != nil {
|
||||
@@ -88,7 +88,7 @@ func (_q *ApiKeyQuery) QueryUser() *UserQuery {
|
||||
}
|
||||
|
||||
// QueryGroup chains the current query on the "group" edge.
|
||||
func (_q *ApiKeyQuery) QueryGroup() *GroupQuery {
|
||||
func (_q *APIKeyQuery) QueryGroup() *GroupQuery {
|
||||
query := (&GroupClient{config: _q.config}).Query()
|
||||
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
|
||||
if err := _q.prepareQuery(ctx); err != nil {
|
||||
@@ -110,7 +110,7 @@ func (_q *ApiKeyQuery) QueryGroup() *GroupQuery {
|
||||
}
|
||||
|
||||
// QueryUsageLogs chains the current query on the "usage_logs" edge.
|
||||
func (_q *ApiKeyQuery) QueryUsageLogs() *UsageLogQuery {
|
||||
func (_q *APIKeyQuery) QueryUsageLogs() *UsageLogQuery {
|
||||
query := (&UsageLogClient{config: _q.config}).Query()
|
||||
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
|
||||
if err := _q.prepareQuery(ctx); err != nil {
|
||||
@@ -131,9 +131,9 @@ func (_q *ApiKeyQuery) QueryUsageLogs() *UsageLogQuery {
|
||||
return query
|
||||
}
|
||||
|
||||
// First returns the first ApiKey entity from the query.
|
||||
// Returns a *NotFoundError when no ApiKey was found.
|
||||
func (_q *ApiKeyQuery) First(ctx context.Context) (*ApiKey, error) {
|
||||
// First returns the first APIKey entity from the query.
|
||||
// Returns a *NotFoundError when no APIKey was found.
|
||||
func (_q *APIKeyQuery) First(ctx context.Context) (*APIKey, error) {
|
||||
nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -145,7 +145,7 @@ func (_q *ApiKeyQuery) First(ctx context.Context) (*ApiKey, error) {
|
||||
}
|
||||
|
||||
// FirstX is like First, but panics if an error occurs.
|
||||
func (_q *ApiKeyQuery) FirstX(ctx context.Context) *ApiKey {
|
||||
func (_q *APIKeyQuery) FirstX(ctx context.Context) *APIKey {
|
||||
node, err := _q.First(ctx)
|
||||
if err != nil && !IsNotFound(err) {
|
||||
panic(err)
|
||||
@@ -153,9 +153,9 @@ func (_q *ApiKeyQuery) FirstX(ctx context.Context) *ApiKey {
|
||||
return node
|
||||
}
|
||||
|
||||
// FirstID returns the first ApiKey ID from the query.
|
||||
// Returns a *NotFoundError when no ApiKey ID was found.
|
||||
func (_q *ApiKeyQuery) FirstID(ctx context.Context) (id int64, err error) {
|
||||
// FirstID returns the first APIKey ID from the query.
|
||||
// Returns a *NotFoundError when no APIKey ID was found.
|
||||
func (_q *APIKeyQuery) FirstID(ctx context.Context) (id int64, err error) {
|
||||
var ids []int64
|
||||
if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
|
||||
return
|
||||
@@ -168,7 +168,7 @@ func (_q *ApiKeyQuery) FirstID(ctx context.Context) (id int64, err error) {
|
||||
}
|
||||
|
||||
// FirstIDX is like FirstID, but panics if an error occurs.
|
||||
func (_q *ApiKeyQuery) FirstIDX(ctx context.Context) int64 {
|
||||
func (_q *APIKeyQuery) FirstIDX(ctx context.Context) int64 {
|
||||
id, err := _q.FirstID(ctx)
|
||||
if err != nil && !IsNotFound(err) {
|
||||
panic(err)
|
||||
@@ -176,10 +176,10 @@ func (_q *ApiKeyQuery) FirstIDX(ctx context.Context) int64 {
|
||||
return id
|
||||
}
|
||||
|
||||
// Only returns a single ApiKey entity found by the query, ensuring it only returns one.
|
||||
// Returns a *NotSingularError when more than one ApiKey entity is found.
|
||||
// Returns a *NotFoundError when no ApiKey entities are found.
|
||||
func (_q *ApiKeyQuery) Only(ctx context.Context) (*ApiKey, error) {
|
||||
// Only returns a single APIKey entity found by the query, ensuring it only returns one.
|
||||
// Returns a *NotSingularError when more than one APIKey entity is found.
|
||||
// Returns a *NotFoundError when no APIKey entities are found.
|
||||
func (_q *APIKeyQuery) Only(ctx context.Context) (*APIKey, error) {
|
||||
nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -195,7 +195,7 @@ func (_q *ApiKeyQuery) Only(ctx context.Context) (*ApiKey, error) {
|
||||
}
|
||||
|
||||
// OnlyX is like Only, but panics if an error occurs.
|
||||
func (_q *ApiKeyQuery) OnlyX(ctx context.Context) *ApiKey {
|
||||
func (_q *APIKeyQuery) OnlyX(ctx context.Context) *APIKey {
|
||||
node, err := _q.Only(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -203,10 +203,10 @@ func (_q *ApiKeyQuery) OnlyX(ctx context.Context) *ApiKey {
|
||||
return node
|
||||
}
|
||||
|
||||
// OnlyID is like Only, but returns the only ApiKey ID in the query.
|
||||
// Returns a *NotSingularError when more than one ApiKey ID is found.
|
||||
// OnlyID is like Only, but returns the only APIKey ID in the query.
|
||||
// Returns a *NotSingularError when more than one APIKey ID is found.
|
||||
// Returns a *NotFoundError when no entities are found.
|
||||
func (_q *ApiKeyQuery) OnlyID(ctx context.Context) (id int64, err error) {
|
||||
func (_q *APIKeyQuery) OnlyID(ctx context.Context) (id int64, err error) {
|
||||
var ids []int64
|
||||
if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
|
||||
return
|
||||
@@ -223,7 +223,7 @@ func (_q *ApiKeyQuery) OnlyID(ctx context.Context) (id int64, err error) {
|
||||
}
|
||||
|
||||
// OnlyIDX is like OnlyID, but panics if an error occurs.
|
||||
func (_q *ApiKeyQuery) OnlyIDX(ctx context.Context) int64 {
|
||||
func (_q *APIKeyQuery) OnlyIDX(ctx context.Context) int64 {
|
||||
id, err := _q.OnlyID(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -231,18 +231,18 @@ func (_q *ApiKeyQuery) OnlyIDX(ctx context.Context) int64 {
|
||||
return id
|
||||
}
|
||||
|
||||
// All executes the query and returns a list of ApiKeys.
|
||||
func (_q *ApiKeyQuery) All(ctx context.Context) ([]*ApiKey, error) {
|
||||
// All executes the query and returns a list of APIKeys.
|
||||
func (_q *APIKeyQuery) All(ctx context.Context) ([]*APIKey, error) {
|
||||
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
|
||||
if err := _q.prepareQuery(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qr := querierAll[[]*ApiKey, *ApiKeyQuery]()
|
||||
return withInterceptors[[]*ApiKey](ctx, _q, qr, _q.inters)
|
||||
qr := querierAll[[]*APIKey, *APIKeyQuery]()
|
||||
return withInterceptors[[]*APIKey](ctx, _q, qr, _q.inters)
|
||||
}
|
||||
|
||||
// AllX is like All, but panics if an error occurs.
|
||||
func (_q *ApiKeyQuery) AllX(ctx context.Context) []*ApiKey {
|
||||
func (_q *APIKeyQuery) AllX(ctx context.Context) []*APIKey {
|
||||
nodes, err := _q.All(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -250,8 +250,8 @@ func (_q *ApiKeyQuery) AllX(ctx context.Context) []*ApiKey {
|
||||
return nodes
|
||||
}
|
||||
|
||||
// IDs executes the query and returns a list of ApiKey IDs.
|
||||
func (_q *ApiKeyQuery) IDs(ctx context.Context) (ids []int64, err error) {
|
||||
// IDs executes the query and returns a list of APIKey IDs.
|
||||
func (_q *APIKeyQuery) IDs(ctx context.Context) (ids []int64, err error) {
|
||||
if _q.ctx.Unique == nil && _q.path != nil {
|
||||
_q.Unique(true)
|
||||
}
|
||||
@@ -263,7 +263,7 @@ func (_q *ApiKeyQuery) IDs(ctx context.Context) (ids []int64, err error) {
|
||||
}
|
||||
|
||||
// IDsX is like IDs, but panics if an error occurs.
|
||||
func (_q *ApiKeyQuery) IDsX(ctx context.Context) []int64 {
|
||||
func (_q *APIKeyQuery) IDsX(ctx context.Context) []int64 {
|
||||
ids, err := _q.IDs(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -272,16 +272,16 @@ func (_q *ApiKeyQuery) IDsX(ctx context.Context) []int64 {
|
||||
}
|
||||
|
||||
// Count returns the count of the given query.
|
||||
func (_q *ApiKeyQuery) Count(ctx context.Context) (int, error) {
|
||||
func (_q *APIKeyQuery) Count(ctx context.Context) (int, error) {
|
||||
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
|
||||
if err := _q.prepareQuery(ctx); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return withInterceptors[int](ctx, _q, querierCount[*ApiKeyQuery](), _q.inters)
|
||||
return withInterceptors[int](ctx, _q, querierCount[*APIKeyQuery](), _q.inters)
|
||||
}
|
||||
|
||||
// CountX is like Count, but panics if an error occurs.
|
||||
func (_q *ApiKeyQuery) CountX(ctx context.Context) int {
|
||||
func (_q *APIKeyQuery) CountX(ctx context.Context) int {
|
||||
count, err := _q.Count(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -290,7 +290,7 @@ func (_q *ApiKeyQuery) CountX(ctx context.Context) int {
|
||||
}
|
||||
|
||||
// Exist returns true if the query has elements in the graph.
|
||||
func (_q *ApiKeyQuery) Exist(ctx context.Context) (bool, error) {
|
||||
func (_q *APIKeyQuery) Exist(ctx context.Context) (bool, error) {
|
||||
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
|
||||
switch _, err := _q.FirstID(ctx); {
|
||||
case IsNotFound(err):
|
||||
@@ -303,7 +303,7 @@ func (_q *ApiKeyQuery) Exist(ctx context.Context) (bool, error) {
|
||||
}
|
||||
|
||||
// ExistX is like Exist, but panics if an error occurs.
|
||||
func (_q *ApiKeyQuery) ExistX(ctx context.Context) bool {
|
||||
func (_q *APIKeyQuery) ExistX(ctx context.Context) bool {
|
||||
exist, err := _q.Exist(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -311,18 +311,18 @@ func (_q *ApiKeyQuery) ExistX(ctx context.Context) bool {
|
||||
return exist
|
||||
}
|
||||
|
||||
// Clone returns a duplicate of the ApiKeyQuery builder, including all associated steps. It can be
|
||||
// Clone returns a duplicate of the APIKeyQuery builder, including all associated steps. It can be
|
||||
// used to prepare common query builders and use them differently after the clone is made.
|
||||
func (_q *ApiKeyQuery) Clone() *ApiKeyQuery {
|
||||
func (_q *APIKeyQuery) Clone() *APIKeyQuery {
|
||||
if _q == nil {
|
||||
return nil
|
||||
}
|
||||
return &ApiKeyQuery{
|
||||
return &APIKeyQuery{
|
||||
config: _q.config,
|
||||
ctx: _q.ctx.Clone(),
|
||||
order: append([]apikey.OrderOption{}, _q.order...),
|
||||
inters: append([]Interceptor{}, _q.inters...),
|
||||
predicates: append([]predicate.ApiKey{}, _q.predicates...),
|
||||
predicates: append([]predicate.APIKey{}, _q.predicates...),
|
||||
withUser: _q.withUser.Clone(),
|
||||
withGroup: _q.withGroup.Clone(),
|
||||
withUsageLogs: _q.withUsageLogs.Clone(),
|
||||
@@ -334,7 +334,7 @@ func (_q *ApiKeyQuery) Clone() *ApiKeyQuery {
|
||||
|
||||
// WithUser tells the query-builder to eager-load the nodes that are connected to
|
||||
// the "user" edge. The optional arguments are used to configure the query builder of the edge.
|
||||
func (_q *ApiKeyQuery) WithUser(opts ...func(*UserQuery)) *ApiKeyQuery {
|
||||
func (_q *APIKeyQuery) WithUser(opts ...func(*UserQuery)) *APIKeyQuery {
|
||||
query := (&UserClient{config: _q.config}).Query()
|
||||
for _, opt := range opts {
|
||||
opt(query)
|
||||
@@ -345,7 +345,7 @@ func (_q *ApiKeyQuery) WithUser(opts ...func(*UserQuery)) *ApiKeyQuery {
|
||||
|
||||
// WithGroup tells the query-builder to eager-load the nodes that are connected to
|
||||
// the "group" edge. The optional arguments are used to configure the query builder of the edge.
|
||||
func (_q *ApiKeyQuery) WithGroup(opts ...func(*GroupQuery)) *ApiKeyQuery {
|
||||
func (_q *APIKeyQuery) WithGroup(opts ...func(*GroupQuery)) *APIKeyQuery {
|
||||
query := (&GroupClient{config: _q.config}).Query()
|
||||
for _, opt := range opts {
|
||||
opt(query)
|
||||
@@ -356,7 +356,7 @@ func (_q *ApiKeyQuery) WithGroup(opts ...func(*GroupQuery)) *ApiKeyQuery {
|
||||
|
||||
// WithUsageLogs tells the query-builder to eager-load the nodes that are connected to
|
||||
// the "usage_logs" edge. The optional arguments are used to configure the query builder of the edge.
|
||||
func (_q *ApiKeyQuery) WithUsageLogs(opts ...func(*UsageLogQuery)) *ApiKeyQuery {
|
||||
func (_q *APIKeyQuery) WithUsageLogs(opts ...func(*UsageLogQuery)) *APIKeyQuery {
|
||||
query := (&UsageLogClient{config: _q.config}).Query()
|
||||
for _, opt := range opts {
|
||||
opt(query)
|
||||
@@ -375,13 +375,13 @@ func (_q *ApiKeyQuery) WithUsageLogs(opts ...func(*UsageLogQuery)) *ApiKeyQuery
|
||||
// Count int `json:"count,omitempty"`
|
||||
// }
|
||||
//
|
||||
// client.ApiKey.Query().
|
||||
// client.APIKey.Query().
|
||||
// GroupBy(apikey.FieldCreatedAt).
|
||||
// Aggregate(ent.Count()).
|
||||
// Scan(ctx, &v)
|
||||
func (_q *ApiKeyQuery) GroupBy(field string, fields ...string) *ApiKeyGroupBy {
|
||||
func (_q *APIKeyQuery) GroupBy(field string, fields ...string) *APIKeyGroupBy {
|
||||
_q.ctx.Fields = append([]string{field}, fields...)
|
||||
grbuild := &ApiKeyGroupBy{build: _q}
|
||||
grbuild := &APIKeyGroupBy{build: _q}
|
||||
grbuild.flds = &_q.ctx.Fields
|
||||
grbuild.label = apikey.Label
|
||||
grbuild.scan = grbuild.Scan
|
||||
@@ -397,23 +397,23 @@ func (_q *ApiKeyQuery) GroupBy(field string, fields ...string) *ApiKeyGroupBy {
|
||||
// CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
// }
|
||||
//
|
||||
// client.ApiKey.Query().
|
||||
// client.APIKey.Query().
|
||||
// Select(apikey.FieldCreatedAt).
|
||||
// Scan(ctx, &v)
|
||||
func (_q *ApiKeyQuery) Select(fields ...string) *ApiKeySelect {
|
||||
func (_q *APIKeyQuery) Select(fields ...string) *APIKeySelect {
|
||||
_q.ctx.Fields = append(_q.ctx.Fields, fields...)
|
||||
sbuild := &ApiKeySelect{ApiKeyQuery: _q}
|
||||
sbuild := &APIKeySelect{APIKeyQuery: _q}
|
||||
sbuild.label = apikey.Label
|
||||
sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
|
||||
return sbuild
|
||||
}
|
||||
|
||||
// Aggregate returns a ApiKeySelect configured with the given aggregations.
|
||||
func (_q *ApiKeyQuery) Aggregate(fns ...AggregateFunc) *ApiKeySelect {
|
||||
// Aggregate returns a APIKeySelect configured with the given aggregations.
|
||||
func (_q *APIKeyQuery) Aggregate(fns ...AggregateFunc) *APIKeySelect {
|
||||
return _q.Select().Aggregate(fns...)
|
||||
}
|
||||
|
||||
func (_q *ApiKeyQuery) prepareQuery(ctx context.Context) error {
|
||||
func (_q *APIKeyQuery) prepareQuery(ctx context.Context) error {
|
||||
for _, inter := range _q.inters {
|
||||
if inter == nil {
|
||||
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
|
||||
@@ -439,9 +439,9 @@ func (_q *ApiKeyQuery) prepareQuery(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_q *ApiKeyQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ApiKey, error) {
|
||||
func (_q *APIKeyQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*APIKey, error) {
|
||||
var (
|
||||
nodes = []*ApiKey{}
|
||||
nodes = []*APIKey{}
|
||||
_spec = _q.querySpec()
|
||||
loadedTypes = [3]bool{
|
||||
_q.withUser != nil,
|
||||
@@ -450,10 +450,10 @@ func (_q *ApiKeyQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ApiKe
|
||||
}
|
||||
)
|
||||
_spec.ScanValues = func(columns []string) ([]any, error) {
|
||||
return (*ApiKey).scanValues(nil, columns)
|
||||
return (*APIKey).scanValues(nil, columns)
|
||||
}
|
||||
_spec.Assign = func(columns []string, values []any) error {
|
||||
node := &ApiKey{config: _q.config}
|
||||
node := &APIKey{config: _q.config}
|
||||
nodes = append(nodes, node)
|
||||
node.Edges.loadedTypes = loadedTypes
|
||||
return node.assignValues(columns, values)
|
||||
@@ -469,29 +469,29 @@ func (_q *ApiKeyQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ApiKe
|
||||
}
|
||||
if query := _q.withUser; query != nil {
|
||||
if err := _q.loadUser(ctx, query, nodes, nil,
|
||||
func(n *ApiKey, e *User) { n.Edges.User = e }); err != nil {
|
||||
func(n *APIKey, e *User) { n.Edges.User = e }); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if query := _q.withGroup; query != nil {
|
||||
if err := _q.loadGroup(ctx, query, nodes, nil,
|
||||
func(n *ApiKey, e *Group) { n.Edges.Group = e }); err != nil {
|
||||
func(n *APIKey, e *Group) { n.Edges.Group = e }); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if query := _q.withUsageLogs; query != nil {
|
||||
if err := _q.loadUsageLogs(ctx, query, nodes,
|
||||
func(n *ApiKey) { n.Edges.UsageLogs = []*UsageLog{} },
|
||||
func(n *ApiKey, e *UsageLog) { n.Edges.UsageLogs = append(n.Edges.UsageLogs, e) }); err != nil {
|
||||
func(n *APIKey) { n.Edges.UsageLogs = []*UsageLog{} },
|
||||
func(n *APIKey, e *UsageLog) { n.Edges.UsageLogs = append(n.Edges.UsageLogs, e) }); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func (_q *ApiKeyQuery) loadUser(ctx context.Context, query *UserQuery, nodes []*ApiKey, init func(*ApiKey), assign func(*ApiKey, *User)) error {
|
||||
func (_q *APIKeyQuery) loadUser(ctx context.Context, query *UserQuery, nodes []*APIKey, init func(*APIKey), assign func(*APIKey, *User)) error {
|
||||
ids := make([]int64, 0, len(nodes))
|
||||
nodeids := make(map[int64][]*ApiKey)
|
||||
nodeids := make(map[int64][]*APIKey)
|
||||
for i := range nodes {
|
||||
fk := nodes[i].UserID
|
||||
if _, ok := nodeids[fk]; !ok {
|
||||
@@ -518,9 +518,9 @@ func (_q *ApiKeyQuery) loadUser(ctx context.Context, query *UserQuery, nodes []*
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (_q *ApiKeyQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes []*ApiKey, init func(*ApiKey), assign func(*ApiKey, *Group)) error {
|
||||
func (_q *APIKeyQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes []*APIKey, init func(*APIKey), assign func(*APIKey, *Group)) error {
|
||||
ids := make([]int64, 0, len(nodes))
|
||||
nodeids := make(map[int64][]*ApiKey)
|
||||
nodeids := make(map[int64][]*APIKey)
|
||||
for i := range nodes {
|
||||
if nodes[i].GroupID == nil {
|
||||
continue
|
||||
@@ -550,9 +550,9 @@ func (_q *ApiKeyQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes [
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (_q *ApiKeyQuery) loadUsageLogs(ctx context.Context, query *UsageLogQuery, nodes []*ApiKey, init func(*ApiKey), assign func(*ApiKey, *UsageLog)) error {
|
||||
func (_q *APIKeyQuery) loadUsageLogs(ctx context.Context, query *UsageLogQuery, nodes []*APIKey, init func(*APIKey), assign func(*APIKey, *UsageLog)) error {
|
||||
fks := make([]driver.Value, 0, len(nodes))
|
||||
nodeids := make(map[int64]*ApiKey)
|
||||
nodeids := make(map[int64]*APIKey)
|
||||
for i := range nodes {
|
||||
fks = append(fks, nodes[i].ID)
|
||||
nodeids[nodes[i].ID] = nodes[i]
|
||||
@@ -581,7 +581,7 @@ func (_q *ApiKeyQuery) loadUsageLogs(ctx context.Context, query *UsageLogQuery,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_q *ApiKeyQuery) sqlCount(ctx context.Context) (int, error) {
|
||||
func (_q *APIKeyQuery) sqlCount(ctx context.Context) (int, error) {
|
||||
_spec := _q.querySpec()
|
||||
_spec.Node.Columns = _q.ctx.Fields
|
||||
if len(_q.ctx.Fields) > 0 {
|
||||
@@ -590,7 +590,7 @@ func (_q *ApiKeyQuery) sqlCount(ctx context.Context) (int, error) {
|
||||
return sqlgraph.CountNodes(ctx, _q.driver, _spec)
|
||||
}
|
||||
|
||||
func (_q *ApiKeyQuery) querySpec() *sqlgraph.QuerySpec {
|
||||
func (_q *APIKeyQuery) querySpec() *sqlgraph.QuerySpec {
|
||||
_spec := sqlgraph.NewQuerySpec(apikey.Table, apikey.Columns, sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeInt64))
|
||||
_spec.From = _q.sql
|
||||
if unique := _q.ctx.Unique; unique != nil {
|
||||
@@ -636,7 +636,7 @@ func (_q *ApiKeyQuery) querySpec() *sqlgraph.QuerySpec {
|
||||
return _spec
|
||||
}
|
||||
|
||||
func (_q *ApiKeyQuery) sqlQuery(ctx context.Context) *sql.Selector {
|
||||
func (_q *APIKeyQuery) sqlQuery(ctx context.Context) *sql.Selector {
|
||||
builder := sql.Dialect(_q.driver.Dialect())
|
||||
t1 := builder.Table(apikey.Table)
|
||||
columns := _q.ctx.Fields
|
||||
@@ -668,28 +668,28 @@ func (_q *ApiKeyQuery) sqlQuery(ctx context.Context) *sql.Selector {
|
||||
return selector
|
||||
}
|
||||
|
||||
// ApiKeyGroupBy is the group-by builder for ApiKey entities.
|
||||
type ApiKeyGroupBy struct {
|
||||
// APIKeyGroupBy is the group-by builder for APIKey entities.
|
||||
type APIKeyGroupBy struct {
|
||||
selector
|
||||
build *ApiKeyQuery
|
||||
build *APIKeyQuery
|
||||
}
|
||||
|
||||
// Aggregate adds the given aggregation functions to the group-by query.
|
||||
func (_g *ApiKeyGroupBy) Aggregate(fns ...AggregateFunc) *ApiKeyGroupBy {
|
||||
func (_g *APIKeyGroupBy) Aggregate(fns ...AggregateFunc) *APIKeyGroupBy {
|
||||
_g.fns = append(_g.fns, fns...)
|
||||
return _g
|
||||
}
|
||||
|
||||
// Scan applies the selector query and scans the result into the given value.
|
||||
func (_g *ApiKeyGroupBy) Scan(ctx context.Context, v any) error {
|
||||
func (_g *APIKeyGroupBy) Scan(ctx context.Context, v any) error {
|
||||
ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
|
||||
if err := _g.build.prepareQuery(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return scanWithInterceptors[*ApiKeyQuery, *ApiKeyGroupBy](ctx, _g.build, _g, _g.build.inters, v)
|
||||
return scanWithInterceptors[*APIKeyQuery, *APIKeyGroupBy](ctx, _g.build, _g, _g.build.inters, v)
|
||||
}
|
||||
|
||||
func (_g *ApiKeyGroupBy) sqlScan(ctx context.Context, root *ApiKeyQuery, v any) error {
|
||||
func (_g *APIKeyGroupBy) sqlScan(ctx context.Context, root *APIKeyQuery, v any) error {
|
||||
selector := root.sqlQuery(ctx).Select()
|
||||
aggregation := make([]string, 0, len(_g.fns))
|
||||
for _, fn := range _g.fns {
|
||||
@@ -716,28 +716,28 @@ func (_g *ApiKeyGroupBy) sqlScan(ctx context.Context, root *ApiKeyQuery, v any)
|
||||
return sql.ScanSlice(rows, v)
|
||||
}
|
||||
|
||||
// ApiKeySelect is the builder for selecting fields of ApiKey entities.
|
||||
type ApiKeySelect struct {
|
||||
*ApiKeyQuery
|
||||
// APIKeySelect is the builder for selecting fields of APIKey entities.
|
||||
type APIKeySelect struct {
|
||||
*APIKeyQuery
|
||||
selector
|
||||
}
|
||||
|
||||
// Aggregate adds the given aggregation functions to the selector query.
|
||||
func (_s *ApiKeySelect) Aggregate(fns ...AggregateFunc) *ApiKeySelect {
|
||||
func (_s *APIKeySelect) Aggregate(fns ...AggregateFunc) *APIKeySelect {
|
||||
_s.fns = append(_s.fns, fns...)
|
||||
return _s
|
||||
}
|
||||
|
||||
// Scan applies the selector query and scans the result into the given value.
|
||||
func (_s *ApiKeySelect) Scan(ctx context.Context, v any) error {
|
||||
func (_s *APIKeySelect) Scan(ctx context.Context, v any) error {
|
||||
ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
|
||||
if err := _s.prepareQuery(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return scanWithInterceptors[*ApiKeyQuery, *ApiKeySelect](ctx, _s.ApiKeyQuery, _s, _s.inters, v)
|
||||
return scanWithInterceptors[*APIKeyQuery, *APIKeySelect](ctx, _s.APIKeyQuery, _s, _s.inters, v)
|
||||
}
|
||||
|
||||
func (_s *ApiKeySelect) sqlScan(ctx context.Context, root *ApiKeyQuery, v any) error {
|
||||
func (_s *APIKeySelect) sqlScan(ctx context.Context, root *APIKeyQuery, v any) error {
|
||||
selector := root.sqlQuery(ctx)
|
||||
aggregation := make([]string, 0, len(_s.fns))
|
||||
for _, fn := range _s.fns {
|
||||
|
||||
Reference in New Issue
Block a user