package model import "time" // 账户类别取值。 const ( AccountTypePaid = "paid" AccountTypeTrial = "trial" AccountTypeFree = "free" AccountTypeUnknown = "unknown" ) // 测活状态取值。 const ( AliveStatusAlive = "alive" AliveStatusDead = "dead" AliveStatusUnknown = "unknown" ) // Setting 是系统级键值配置;敏感值(如 telegram_bot_token)以 AES-GCM 密文存储。 type Setting struct { Key string `gorm:"primaryKey;size:64" json:"key"` Value string `json:"value"` UpdatedAt time.Time `json:"updatedAt"` } // Proxy 是出站代理配置(socks5 / http / https),供租户 SDK 调用按关联走代理; // 密码 AES-GCM 加密存储,任何响应不回明文。 type Proxy struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"uniqueIndex;size:64" json:"name"` Type string `gorm:"size:16" json:"type"` Host string `gorm:"size:256" json:"host"` Port int `json:"port"` Username string `gorm:"size:128" json:"username"` PasswordEnc string `json:"-"` // Country / City 为经代理出口实测的地理位置;GeoAt 为最近一次探测时间,nil 表示尚未探测 Country string `gorm:"size:64" json:"country"` City string `gorm:"size:64" json:"city"` GeoAt *time.Time `json:"geoAt"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } // SystemLog 是面板自身的操作审计(区别于 OCI 云端审计事件); // 只存方法、路径等元数据,绝不存请求体,避免私钥 / 口令入库。 type SystemLog struct { ID uint `gorm:"primaryKey" json:"id"` Username string `gorm:"size:64;index" json:"username"` Method string `gorm:"size:8" json:"method"` Path string `gorm:"size:256" json:"path"` Status int `json:"status"` DurationMs int64 `json:"durationMs"` ClientIP string `gorm:"size:64" json:"clientIp"` UserAgent string `gorm:"size:256" json:"userAgent"` // ErrMsg 仅失败请求(>=400)存响应的 error 文案,便于免翻服务端日志定位原因 ErrMsg string `gorm:"size:256" json:"errMsg"` CreatedAt time.Time `gorm:"index" json:"createdAt"` } // User 是可登录面板的账号,密码只存 bcrypt 哈希。 type User struct { ID uint `gorm:"primaryKey" json:"id"` Username string `gorm:"uniqueIndex;size:64" json:"username"` PasswordHash string `json:"-"` // TOTP 共享密钥 AES-GCM 密文;空串表示未启用两步验证 TotpSecretEnc string `json:"-"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } // UserIdentity 是管理员绑定的外部登录身份(OIDC / GitHub); // 绑定动作只发生在已登录会话内,未绑定的外部身份一律拒绝登录。 type UserIdentity struct { ID uint `gorm:"primaryKey" json:"id"` UserID uint `gorm:"index" json:"-"` Provider string `gorm:"size:16;uniqueIndex:idx_identity_provider_subject" json:"provider"` // OIDC sub / GitHub 数字 id;不回给前端 Subject string `gorm:"size:255;uniqueIndex:idx_identity_provider_subject" json:"-"` // 展示名:邮箱 / GitHub login Display string `gorm:"size:255" json:"display"` CreatedAt time.Time `json:"createdAt"` } // 任务类型取值。 const ( TaskTypeHealthCheck = "health_check" // 定时测活 TaskTypeSnatch = "snatch" // 抢机:反复尝试创建实例直到成功 TaskTypeCost = "cost" // 每日成本同步:拉 Usage API 写本地快照 TaskTypeAiProbe = "ai_probe" // AI 渠道周期探测:随渠道数量自动创建/激活/暂停 ) // 任务状态取值。 const ( TaskStatusActive = "active" // 按 cron 调度中 TaskStatusPaused = "paused" // 手动暂停 TaskStatusSucceeded = "succeeded" // 抢机成功后自动停止 TaskStatusFailed = "failed" // 熔断停止(如抢机连续鉴权失败) ) // Task 是一个按 cron 表达式周期执行的后台任务。 // Payload 按任务类型存 JSON:测活为 {"ociConfigIds":[...]}(空为全部), // 抢机为 {"ociConfigId":1,"instance":{...},"count":1}。 type Task struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"size:128" json:"name"` Type string `gorm:"size:32;index" json:"type"` CronExpr string `gorm:"size:64" json:"cronExpr"` Payload string `json:"payload"` Status string `gorm:"size:16;index" json:"status"` LastRunAt *time.Time `json:"lastRunAt"` LastError string `json:"lastError"` RunCount int `json:"runCount"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } // TaskLog 是任务的一次执行记录。 type TaskLog struct { ID uint `gorm:"primaryKey" json:"id"` TaskID uint `gorm:"index" json:"taskId"` Success bool `json:"success"` Message string `json:"message"` DurationMs int64 `json:"durationMs"` CreatedAt time.Time `json:"createdAt"` } // CheckSnapshot 是测活任务为每个配置留下的最新资源快照(每配置一行,覆盖更新)。 // InstanceCount 只统计配置默认区域,用于总览 KPI 与覆盖度标注。 type CheckSnapshot struct { ID uint `gorm:"primaryKey" json:"id"` OciConfigID uint `gorm:"uniqueIndex" json:"ociConfigId"` AliveStatus string `gorm:"size:16" json:"aliveStatus"` InstanceCount int `json:"instanceCount"` CheckedAt time.Time `json:"checkedAt"` } // CostSnapshot 是成本任务写入的每配置每日成本(UTC 日界,重复同步覆盖更新)。 type CostSnapshot struct { ID uint `gorm:"primaryKey" json:"id"` OciConfigID uint `gorm:"uniqueIndex:idx_cost_cfg_day" json:"ociConfigId"` Day string `gorm:"size:10;uniqueIndex:idx_cost_cfg_day" json:"day"` Amount float64 `json:"amount"` Currency string `gorm:"size:8" json:"currency"` SyncedAt time.Time `json:"syncedAt"` } // OciConfig 保存一份 OCI API Key 配置及其最近一次云端快照。 // 私钥和口令只以 AES-GCM 密文落库,且不出现在 JSON 响应中。 type OciConfig struct { ID uint `gorm:"primaryKey" json:"id"` Alias string `gorm:"uniqueIndex;size:64" json:"alias"` // Group 是面板内的自定义分组标签,空串表示未分组; // 列名避开 SQL 保留字 GROUP Group string `gorm:"column:group_name;size:64;index" json:"group"` // ProxyID 关联出站代理,nil 表示直连;该租户全部 SDK 调用经此代理 ProxyID *uint `gorm:"index" json:"proxyId"` UserOCID string `json:"userOcid"` TenancyOCID string `json:"tenancyOcid"` Fingerprint string `json:"fingerprint"` Region string `json:"region"` PrivateKeyEnc string `json:"-"` PassphraseEnc string `json:"-"` TenancyName string `json:"tenancyName"` HomeRegionKey string `json:"homeRegionKey"` AccountType string `json:"accountType"` SubscriptionID string `json:"subscriptionId"` PaymentModel string `json:"paymentModel"` SubscriptionTier string `json:"subscriptionTier"` PromotionStatus string `json:"promotionStatus"` PromotionAmount float32 `json:"promotionAmount"` PromotionExpires *time.Time `json:"promotionExpires"` AliveStatus string `json:"aliveStatus"` LastError string `json:"lastError"` LastVerifiedAt *time.Time `json:"lastVerifiedAt"` // 多区域 / 多区间支持:开启后订阅区域与 compartment 列表入库缓存, // 供筛选器免实时调 OCI;未开启的租户筛选器锁定主区域 / 根区间。 MultiRegion bool `json:"multiRegion"` MultiCompartment bool `json:"multiCompartment"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } // LogEvent 是 OCI 日志回传事件(方案A:Connector Hub → Notifications → HTTPS 订阅); // Payload 存 ONS 消息原文(链路单条 ≤128KB,面板另设 256KB 防御上限,超限截断并标记)。 type LogEvent struct { ID uint `gorm:"primaryKey" json:"id"` OciConfigID uint `gorm:"index" json:"ociConfigId"` // 经 secret 反查归属租户 MessageID string `gorm:"uniqueIndex;size:128" json:"messageId"` // 幂等键(X-OCI-NS-MessageId) EventType string `gorm:"size:128;index" json:"eventType"` // 异步解析回填,如 com.oraclecloud.ComputeApi.TerminateInstance Source string `gorm:"size:64" json:"source"` SourceIP string `gorm:"size:64" json:"sourceIp"` // 异步解析回填,Audit 事件的发起方 IP EventTime *time.Time `gorm:"index" json:"eventTime"` Payload string `json:"payload"` Truncated bool `json:"truncated"` Processed bool `gorm:"index" json:"processed"` ReceivedAt time.Time `json:"receivedAt"` } // RegionCache 是开启多区域支持的配置缓存的订阅区域(每配置多行,整组覆盖)。 // Status 非 READY(新订阅进行中)时读取接口会实时刷新,直到全部 READY。 type RegionCache struct { ID uint `gorm:"primaryKey" json:"-"` OciConfigID uint `gorm:"index;uniqueIndex:idx_region_cache" json:"-"` Key string `gorm:"size:8;uniqueIndex:idx_region_cache" json:"key"` Name string `gorm:"size:64" json:"name"` Status string `gorm:"size:16" json:"status"` IsHomeRegion bool `json:"isHomeRegion"` UpdatedAt time.Time `json:"updatedAt"` } // CompartmentCache 是开启多区间支持的配置缓存的 compartment(每配置多行,整组覆盖)。 type CompartmentCache struct { ID uint `gorm:"primaryKey" json:"-"` OciConfigID uint `gorm:"index;uniqueIndex:idx_comp_cache" json:"-"` OCID string `gorm:"size:128;uniqueIndex:idx_comp_cache" json:"id"` Name string `gorm:"size:128" json:"name"` State string `gorm:"size:16" json:"lifecycleState"` UpdatedAt time.Time `json:"updatedAt"` } // AiKey 是 AI 网关的访问密钥;明文不落库,仅存 SHA-256 与尾 4 位。 // Group 非空时该密钥只在同分组渠道内负载均衡(列名避开 SQL 保留字 group)。 type AiKey struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"uniqueIndex;size:64" json:"name"` KeyHash string `gorm:"uniqueIndex;size:64" json:"-"` Tail string `gorm:"size:4" json:"tail"` Group string `gorm:"column:key_group;size:32" json:"group"` Enabled bool `json:"enabled"` LastUsedAt *time.Time `json:"lastUsedAt"` // ContentLogUntil 是内容日志开启截止时间;nil = 永关(默认,红线), // 开启必须限时,到期自动停写 ContentLogUntil *time.Time `json:"contentLogUntil"` CreatedAt time.Time `json:"createdAt"` } // AiChannel 是号池渠道 = 租户配置 × 区域;Priority 数字小优先级高,组内按 Weight 加权随机。 // Group 是分组名(列名避开 SQL 保留字 group),空串归属默认分组。 type AiChannel struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"size:96" json:"name"` OciConfigID uint `gorm:"uniqueIndex:idx_ai_channel" json:"ociConfigId"` Region string `gorm:"size:32;uniqueIndex:idx_ai_channel" json:"region"` Group string `gorm:"column:channel_group;size:32;index" json:"group"` Enabled bool `json:"enabled"` Priority int `json:"priority"` Weight int `json:"weight"` // FailCount 连续失败计数;达阈值后 DisabledUntil 指数退避熔断,成功/探测通过复位 FailCount int `json:"failCount"` DisabledUntil *time.Time `json:"disabledUntil"` LastProbeAt *time.Time `json:"lastProbeAt"` ProbeStatus string `gorm:"size:16" json:"probeStatus"` // ok / no_service / no_quota / error ProbeError string `gorm:"size:512" json:"probeError"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } // AiModelCache 是渠道区域的可用模型缓存(整渠道覆盖式同步)。 // Capability 为 CHAT / EMBEDDING;存量空串视为 CHAT(加列前只同步对话模型)。 type AiModelCache struct { ID uint `gorm:"primaryKey" json:"-"` ChannelID uint `gorm:"index" json:"channelId"` ModelOcid string `gorm:"size:160" json:"modelOcid"` Name string `gorm:"size:96;index" json:"name"` Vendor string `gorm:"size:32" json:"vendor"` Capability string `gorm:"size:16" json:"capability"` SyncedAt time.Time `json:"syncedAt"` // DeprecatedAt 是 OCI 宣布的模型弃用时间;nil 表示未宣布。 // 弃用后仍可调用,直到 RetiredAt(按需推理退役,同步层已剔除过期项)。 DeprecatedAt *time.Time `json:"deprecatedAt"` RetiredAt *time.Time `json:"retiredAt"` } // AiCallLog 是网关调用日志:仅元数据与 token 用量,绝不记录 prompt / 响应正文。 type AiCallLog struct { ID uint `gorm:"primaryKey" json:"id"` KeyID uint `json:"keyId"` KeyName string `gorm:"size:64" json:"keyName"` Endpoint string `gorm:"size:16" json:"endpoint"` // openai / anthropic Model string `gorm:"size:96" json:"model"` ChannelID uint `json:"channelId"` ChannelName string `gorm:"size:96" json:"channelName"` Stream bool `json:"stream"` Status int `json:"status"` // ClientIP 是网关调用方来源 IP(尊重「真实 IP 头」安全设置,与系统日志同源) ClientIP string `gorm:"size:64" json:"clientIp"` PromptTokens int `json:"promptTokens"` CompletionTokens int `json:"completionTokens"` TotalTokens int `json:"totalTokens"` // CachedTokens 是输入中缓存命中(读取)的部分;OCI 隐式缓存无写入计数 CachedTokens int `json:"cachedTokens"` LatencyMs int64 `json:"latencyMs"` Retries int `json:"retries"` ErrMsg string `gorm:"size:512" json:"errMsg"` CreatedAt time.Time `gorm:"index" json:"createdAt"` } // AiContentLog 是网关调用正文抽样——「不落正文」红线的唯一显式例外: // 仅当调用密钥显式开启内容日志且未过期时写入,到期自动停写; // 请求正文与非流式响应正文各截断至 64KB,流式响应不记录;保留 7 天 / 1 万行。 type AiContentLog struct { ID uint `gorm:"primaryKey" json:"id"` // CallLogID 关联同次调用的 AiCallLog,调用日志详情据此反查正文 CallLogID uint `gorm:"index" json:"callLogId"` KeyID uint `gorm:"index" json:"keyId"` KeyName string `gorm:"size:64" json:"keyName"` Endpoint string `gorm:"size:16" json:"endpoint"` Model string `gorm:"size:96" json:"model"` Stream bool `json:"stream"` // RequestBody / ResponseBody 是截断后的正文 JSON RequestBody string `json:"requestBody"` ResponseBody string `json:"responseBody"` CreatedAt time.Time `gorm:"index" json:"createdAt"` }