"git@web.lueluesay.top:chenxi/sub2api.git" did not exist on "192efb84a0e82365fb25fa755c0da3e45677da1e"
Unverified Commit 6bccb8a8 authored by Wesley Liddick's avatar Wesley Liddick Committed by GitHub
Browse files

Merge branch 'main' into feature/antigravity-user-agent-configurable

parents 1fc6ef3d 3de1e0e4
...@@ -44,4 +44,4 @@ jobs: ...@@ -44,4 +44,4 @@ jobs:
with: with:
version: v2.7 version: v2.7
args: --timeout=5m args: --timeout=5m
working-directory: backend working-directory: backend
\ No newline at end of file
...@@ -121,7 +121,6 @@ AGENTS.md ...@@ -121,7 +121,6 @@ AGENTS.md
scripts scripts
.code-review-state .code-review-state
openspec/ openspec/
docs/
code-reviews/ code-reviews/
AGENTS.md AGENTS.md
backend/cmd/server/server backend/cmd/server/server
...@@ -129,4 +128,8 @@ deploy/docker-compose.override.yml ...@@ -129,4 +128,8 @@ deploy/docker-compose.override.yml
.gocache/ .gocache/
vite.config.js vite.config.js
docs/* docs/*
.serena/ .serena/
\ No newline at end of file .codex/
frontend/coverage/
aicodex
...@@ -36,7 +36,7 @@ RUN pnpm run build ...@@ -36,7 +36,7 @@ RUN pnpm run build
FROM ${GOLANG_IMAGE} AS backend-builder FROM ${GOLANG_IMAGE} AS backend-builder
# Build arguments for version info (set by CI) # Build arguments for version info (set by CI)
ARG VERSION=docker ARG VERSION=
ARG COMMIT=docker ARG COMMIT=docker
ARG DATE ARG DATE
ARG GOPROXY ARG GOPROXY
...@@ -61,9 +61,13 @@ COPY backend/ ./ ...@@ -61,9 +61,13 @@ COPY backend/ ./
COPY --from=frontend-builder /app/backend/internal/web/dist ./internal/web/dist COPY --from=frontend-builder /app/backend/internal/web/dist ./internal/web/dist
# Build the binary (BuildType=release for CI builds, embed frontend) # Build the binary (BuildType=release for CI builds, embed frontend)
RUN CGO_ENABLED=0 GOOS=linux go build \ # Version precedence: build arg VERSION > cmd/server/VERSION
RUN VERSION_VALUE="${VERSION}" && \
if [ -z "${VERSION_VALUE}" ]; then VERSION_VALUE="$(tr -d '\r\n' < ./cmd/server/VERSION)"; fi && \
DATE_VALUE="${DATE:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}" && \
CGO_ENABLED=0 GOOS=linux go build \
-tags embed \ -tags embed \
-ldflags="-s -w -X main.Commit=${COMMIT} -X main.Date=${DATE:-$(date -u +%Y-%m-%dT%H:%M:%SZ)} -X main.BuildType=release" \ -ldflags="-s -w -X main.Version=${VERSION_VALUE} -X main.Commit=${COMMIT} -X main.Date=${DATE_VALUE} -X main.BuildType=release" \
-o /app/sub2api \ -o /app/sub2api \
./cmd/server ./cmd/server
......
.PHONY: build build-backend build-frontend test test-backend test-frontend .PHONY: build build-backend build-frontend test test-backend test-frontend secret-scan
# 一键编译前后端 # 一键编译前后端
build: build-backend build-frontend build: build-backend build-frontend
...@@ -20,3 +20,6 @@ test-backend: ...@@ -20,3 +20,6 @@ test-backend:
test-frontend: test-frontend:
@pnpm --dir frontend run lint:check @pnpm --dir frontend run lint:check
@pnpm --dir frontend run typecheck @pnpm --dir frontend run typecheck
secret-scan:
@python3 tools/secret_scan.py
...@@ -363,6 +363,12 @@ default: ...@@ -363,6 +363,12 @@ default:
rate_multiplier: 1.0 rate_multiplier: 1.0
``` ```
### Sora Status (Temporarily Unavailable)
> ⚠️ Sora-related features are temporarily unavailable due to technical issues in upstream integration and media delivery.
> Please do not rely on Sora in production at this time.
> Existing `gateway.sora_*` configuration keys are reserved and may not take effect until these issues are resolved.
Additional security-related options are available in `config.yaml`: Additional security-related options are available in `config.yaml`:
- `cors.allowed_origins` for CORS allowlist - `cors.allowed_origins` for CORS allowlist
......
...@@ -139,6 +139,8 @@ curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install ...@@ -139,6 +139,8 @@ curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install
使用 Docker Compose 部署,包含 PostgreSQL 和 Redis 容器。 使用 Docker Compose 部署,包含 PostgreSQL 和 Redis 容器。
如果你的服务器是 **Ubuntu 24.04**,建议直接参考:`deploy/ubuntu24-docker-compose-aicodex.md`,其中包含「安装最新版 Docker + docker-compose-aicodex.yml 部署」的完整步骤。
#### 前置条件 #### 前置条件
- Docker 20.10+ - Docker 20.10+
...@@ -370,6 +372,33 @@ default: ...@@ -370,6 +372,33 @@ default:
rate_multiplier: 1.0 rate_multiplier: 1.0
``` ```
### Sora 功能状态(暂不可用)
> ⚠️ 当前 Sora 相关功能因上游接入与媒体链路存在技术问题,暂时不可用。
> 现阶段请勿在生产环境依赖 Sora 能力。
> 文档中的 `gateway.sora_*` 配置仅作预留,待技术问题修复后再恢复可用。
### Sora 媒体签名 URL(功能恢复后可选)
当配置 `gateway.sora_media_signing_key``gateway.sora_media_signed_url_ttl_seconds > 0` 时,网关会将 Sora 输出的媒体地址改写为临时签名 URL(`/sora/media-signed/...`)。这样无需 API Key 即可在浏览器中直接访问,且具备过期控制与防篡改能力(签名包含 path + query)。
```yaml
gateway:
# /sora/media 是否强制要求 API Key(默认 false)
sora_media_require_api_key: false
# 媒体临时签名密钥(为空则禁用签名)
sora_media_signing_key: "your-signing-key"
# 临时签名 URL 有效期(秒)
sora_media_signed_url_ttl_seconds: 900
```
> 若未配置签名密钥,`/sora/media-signed` 将返回 503。
> 如需更严格的访问控制,可将 `sora_media_require_api_key` 设为 true,仅允许携带 API Key 的 `/sora/media` 访问。
访问策略说明:
- `/sora/media`:内部调用或客户端携带 API Key 才能下载
- `/sora/media-signed`:外部可访问,但有签名 + 过期控制
`config.yaml` 还支持以下安全相关配置: `config.yaml` 还支持以下安全相关配置:
- `cors.allowed_origins` 配置 CORS 白名单 - `cors.allowed_origins` 配置 CORS 白名单
...@@ -383,6 +412,14 @@ default: ...@@ -383,6 +412,14 @@ default:
- `server.trusted_proxies` 启用可信代理解析 X-Forwarded-For - `server.trusted_proxies` 启用可信代理解析 X-Forwarded-For
- `turnstile.required` 在 release 模式强制启用 Turnstile - `turnstile.required` 在 release 模式强制启用 Turnstile
**网关防御纵深建议(重点)**
- `gateway.upstream_response_read_max_bytes`:限制非流式上游响应读取大小(默认 `8MB`),用于防止异常响应导致内存放大。
- `gateway.proxy_probe_response_read_max_bytes`:限制代理探测响应读取大小(默认 `1MB`)。
- `gateway.gemini_debug_response_headers`:默认 `false`,仅在排障时短时开启,避免高频请求日志开销。
- `/auth/register``/auth/login``/auth/login/2fa``/auth/send-verify-code` 已提供服务端兜底限流(Redis 故障时 fail-close)。
- 推荐将 WAF/CDN 作为第一层防护,服务端限流与响应读取上限作为第二层兜底;两层同时保留,避免旁路流量与误配置风险。
**⚠️ 安全警告:HTTP URL 配置** **⚠️ 安全警告:HTTP URL 配置**
`security.url_allowlist.enabled=false` 时,系统默认执行最小 URL 校验,**拒绝 HTTP URL**,仅允许 HTTPS。要允许 HTTP URL(例如用于开发或内网测试),必须显式设置: `security.url_allowlist.enabled=false` 时,系统默认执行最小 URL 校验,**拒绝 HTTP URL**,仅允许 HTTPS。要允许 HTTP URL(例如用于开发或内网测试),必须显式设置:
...@@ -428,6 +465,29 @@ Invalid base URL: invalid url scheme: http ...@@ -428,6 +465,29 @@ Invalid base URL: invalid url scheme: http
./sub2api ./sub2api
``` ```
#### HTTP/2 (h2c) 与 HTTP/1.1 回退
后端明文端口默认支持 h2c,并保留 HTTP/1.1 回退用于 WebSocket 与旧客户端。浏览器通常不支持 h2c,性能收益主要在反向代理或内网链路。
**反向代理示例(Caddy):**
```caddyfile
transport http {
versions h2c h1
}
```
**验证:**
```bash
# h2c prior knowledge
curl --http2-prior-knowledge -I http://localhost:8080/health
# HTTP/1.1 回退
curl --http1.1 -I http://localhost:8080/health
# WebSocket 回退验证(需管理员 token)
websocat -H="Sec-WebSocket-Protocol: sub2api-admin, jwt.<ADMIN_TOKEN>" ws://localhost:8080/api/v1/admin/ops/ws/qps
```
#### 开发模式 #### 开发模式
```bash ```bash
......
...@@ -14,4 +14,7 @@ test-integration: ...@@ -14,4 +14,7 @@ test-integration:
go test -tags=integration ./... go test -tags=integration ./...
test-e2e: test-e2e:
go test -tags=e2e ./... ./scripts/e2e-test.sh
test-e2e-local:
go test -tags=e2e -v -timeout=300s ./internal/integration/...
...@@ -17,7 +17,7 @@ func main() { ...@@ -17,7 +17,7 @@ func main() {
email := flag.String("email", "", "Admin email to issue a JWT for (defaults to first active admin)") email := flag.String("email", "", "Admin email to issue a JWT for (defaults to first active admin)")
flag.Parse() flag.Parse()
cfg, err := config.Load() cfg, err := config.LoadForBootstrap()
if err != nil { if err != nil {
log.Fatalf("failed to load config: %v", err) log.Fatalf("failed to load config: %v", err)
} }
......
0.1.83 0.1.83.4
\ No newline at end of file
...@@ -8,7 +8,6 @@ import ( ...@@ -8,7 +8,6 @@ import (
"errors" "errors"
"flag" "flag"
"log" "log"
"log/slog"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
...@@ -19,11 +18,14 @@ import ( ...@@ -19,11 +18,14 @@ import (
_ "github.com/Wei-Shaw/sub2api/ent/runtime" _ "github.com/Wei-Shaw/sub2api/ent/runtime"
"github.com/Wei-Shaw/sub2api/internal/config" "github.com/Wei-Shaw/sub2api/internal/config"
"github.com/Wei-Shaw/sub2api/internal/handler" "github.com/Wei-Shaw/sub2api/internal/handler"
"github.com/Wei-Shaw/sub2api/internal/pkg/logger"
"github.com/Wei-Shaw/sub2api/internal/server/middleware" "github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/Wei-Shaw/sub2api/internal/setup" "github.com/Wei-Shaw/sub2api/internal/setup"
"github.com/Wei-Shaw/sub2api/internal/web" "github.com/Wei-Shaw/sub2api/internal/web"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
) )
//go:embed VERSION //go:embed VERSION
...@@ -38,7 +40,12 @@ var ( ...@@ -38,7 +40,12 @@ var (
) )
func init() { func init() {
// Read version from embedded VERSION file // 如果 Version 已通过 ldflags 注入(例如 -X main.Version=...),则不要覆盖。
if strings.TrimSpace(Version) != "" {
return
}
// 默认从 embedded VERSION 文件读取版本号(编译期打包进二进制)。
Version = strings.TrimSpace(embeddedVersion) Version = strings.TrimSpace(embeddedVersion)
if Version == "" { if Version == "" {
Version = "0.0.0-dev" Version = "0.0.0-dev"
...@@ -47,22 +54,9 @@ func init() { ...@@ -47,22 +54,9 @@ func init() {
// initLogger configures the default slog handler based on gin.Mode(). // initLogger configures the default slog handler based on gin.Mode().
// In non-release mode, Debug level logs are enabled. // In non-release mode, Debug level logs are enabled.
func initLogger() {
var level slog.Level
if gin.Mode() == gin.ReleaseMode {
level = slog.LevelInfo
} else {
level = slog.LevelDebug
}
handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
Level: level,
})
slog.SetDefault(slog.New(handler))
}
func main() { func main() {
// Initialize slog logger based on gin mode logger.InitBootstrap()
initLogger() defer logger.Sync()
// Parse command line flags // Parse command line flags
setupMode := flag.Bool("setup", false, "Run setup wizard in CLI mode") setupMode := flag.Bool("setup", false, "Run setup wizard in CLI mode")
...@@ -122,16 +116,26 @@ func runSetupServer() { ...@@ -122,16 +116,26 @@ func runSetupServer() {
log.Printf("Setup wizard available at http://%s", addr) log.Printf("Setup wizard available at http://%s", addr)
log.Println("Complete the setup wizard to configure Sub2API") log.Println("Complete the setup wizard to configure Sub2API")
if err := r.Run(addr); err != nil { server := &http.Server{
Addr: addr,
Handler: h2c.NewHandler(r, &http2.Server{}),
ReadHeaderTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
}
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("Failed to start setup server: %v", err) log.Fatalf("Failed to start setup server: %v", err)
} }
} }
func runMainServer() { func runMainServer() {
cfg, err := config.Load() cfg, err := config.LoadForBootstrap()
if err != nil { if err != nil {
log.Fatalf("Failed to load config: %v", err) log.Fatalf("Failed to load config: %v", err)
} }
if err := logger.Init(logger.OptionsFromConfig(cfg.Log)); err != nil {
log.Fatalf("Failed to initialize logger: %v", err)
}
if cfg.RunMode == config.RunModeSimple { if cfg.RunMode == config.RunModeSimple {
log.Println("⚠️ WARNING: Running in SIMPLE mode - billing and quota checks are DISABLED") log.Println("⚠️ WARNING: Running in SIMPLE mode - billing and quota checks are DISABLED")
} }
......
...@@ -67,14 +67,19 @@ func provideCleanup( ...@@ -67,14 +67,19 @@ func provideCleanup(
opsAlertEvaluator *service.OpsAlertEvaluatorService, opsAlertEvaluator *service.OpsAlertEvaluatorService,
opsCleanup *service.OpsCleanupService, opsCleanup *service.OpsCleanupService,
opsScheduledReport *service.OpsScheduledReportService, opsScheduledReport *service.OpsScheduledReportService,
opsSystemLogSink *service.OpsSystemLogSink,
soraMediaCleanup *service.SoraMediaCleanupService,
schedulerSnapshot *service.SchedulerSnapshotService, schedulerSnapshot *service.SchedulerSnapshotService,
tokenRefresh *service.TokenRefreshService, tokenRefresh *service.TokenRefreshService,
accountExpiry *service.AccountExpiryService, accountExpiry *service.AccountExpiryService,
subscriptionExpiry *service.SubscriptionExpiryService, subscriptionExpiry *service.SubscriptionExpiryService,
usageCleanup *service.UsageCleanupService, usageCleanup *service.UsageCleanupService,
idempotencyCleanup *service.IdempotencyCleanupService,
pricing *service.PricingService, pricing *service.PricingService,
emailQueue *service.EmailQueueService, emailQueue *service.EmailQueueService,
billingCache *service.BillingCacheService, billingCache *service.BillingCacheService,
usageRecordWorkerPool *service.UsageRecordWorkerPool,
subscriptionService *service.SubscriptionService,
oauth *service.OAuthService, oauth *service.OAuthService,
openaiOAuth *service.OpenAIOAuthService, openaiOAuth *service.OpenAIOAuthService,
geminiOAuth *service.GeminiOAuthService, geminiOAuth *service.GeminiOAuthService,
...@@ -101,6 +106,18 @@ func provideCleanup( ...@@ -101,6 +106,18 @@ func provideCleanup(
} }
return nil return nil
}}, }},
{"OpsSystemLogSink", func() error {
if opsSystemLogSink != nil {
opsSystemLogSink.Stop()
}
return nil
}},
{"SoraMediaCleanupService", func() error {
if soraMediaCleanup != nil {
soraMediaCleanup.Stop()
}
return nil
}},
{"OpsAlertEvaluatorService", func() error { {"OpsAlertEvaluatorService", func() error {
if opsAlertEvaluator != nil { if opsAlertEvaluator != nil {
opsAlertEvaluator.Stop() opsAlertEvaluator.Stop()
...@@ -131,6 +148,12 @@ func provideCleanup( ...@@ -131,6 +148,12 @@ func provideCleanup(
} }
return nil return nil
}}, }},
{"IdempotencyCleanupService", func() error {
if idempotencyCleanup != nil {
idempotencyCleanup.Stop()
}
return nil
}},
{"TokenRefreshService", func() error { {"TokenRefreshService", func() error {
tokenRefresh.Stop() tokenRefresh.Stop()
return nil return nil
...@@ -143,6 +166,12 @@ func provideCleanup( ...@@ -143,6 +166,12 @@ func provideCleanup(
subscriptionExpiry.Stop() subscriptionExpiry.Stop()
return nil return nil
}}, }},
{"SubscriptionService", func() error {
if subscriptionService != nil {
subscriptionService.Stop()
}
return nil
}},
{"PricingService", func() error { {"PricingService", func() error {
pricing.Stop() pricing.Stop()
return nil return nil
...@@ -155,6 +184,12 @@ func provideCleanup( ...@@ -155,6 +184,12 @@ func provideCleanup(
billingCache.Stop() billingCache.Stop()
return nil return nil
}}, }},
{"UsageRecordWorkerPool", func() error {
if usageRecordWorkerPool != nil {
usageRecordWorkerPool.Stop()
}
return nil
}},
{"OAuthService", func() error { {"OAuthService", func() error {
oauth.Stop() oauth.Stop()
return nil return nil
......
...@@ -65,8 +65,8 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { ...@@ -65,8 +65,8 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
apiKeyAuthCacheInvalidator := service.ProvideAPIKeyAuthCacheInvalidator(apiKeyService) apiKeyAuthCacheInvalidator := service.ProvideAPIKeyAuthCacheInvalidator(apiKeyService)
promoService := service.NewPromoService(promoCodeRepository, userRepository, billingCacheService, client, apiKeyAuthCacheInvalidator) promoService := service.NewPromoService(promoCodeRepository, userRepository, billingCacheService, client, apiKeyAuthCacheInvalidator)
authService := service.NewAuthService(userRepository, redeemCodeRepository, refreshTokenCache, configConfig, settingService, emailService, turnstileService, emailQueueService, promoService) authService := service.NewAuthService(userRepository, redeemCodeRepository, refreshTokenCache, configConfig, settingService, emailService, turnstileService, emailQueueService, promoService)
userService := service.NewUserService(userRepository, apiKeyAuthCacheInvalidator) userService := service.NewUserService(userRepository, apiKeyAuthCacheInvalidator, billingCache)
subscriptionService := service.NewSubscriptionService(groupRepository, userSubscriptionRepository, billingCacheService) subscriptionService := service.NewSubscriptionService(groupRepository, userSubscriptionRepository, billingCacheService, client, configConfig)
redeemCache := repository.NewRedeemCache(redisClient) redeemCache := repository.NewRedeemCache(redisClient)
redeemService := service.NewRedeemService(redeemCodeRepository, userRepository, subscriptionService, redeemCache, billingCacheService, client, apiKeyAuthCacheInvalidator) redeemService := service.NewRedeemService(redeemCodeRepository, userRepository, subscriptionService, redeemCache, billingCacheService, client, apiKeyAuthCacheInvalidator)
secretEncryptor, err := repository.NewAESEncryptor(configConfig) secretEncryptor, err := repository.NewAESEncryptor(configConfig)
...@@ -98,10 +98,11 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { ...@@ -98,10 +98,11 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
dashboardHandler := admin.NewDashboardHandler(dashboardService, dashboardAggregationService) dashboardHandler := admin.NewDashboardHandler(dashboardService, dashboardAggregationService)
schedulerCache := repository.NewSchedulerCache(redisClient) schedulerCache := repository.NewSchedulerCache(redisClient)
accountRepository := repository.NewAccountRepository(client, db, schedulerCache) accountRepository := repository.NewAccountRepository(client, db, schedulerCache)
soraAccountRepository := repository.NewSoraAccountRepository(db)
proxyRepository := repository.NewProxyRepository(client, db) proxyRepository := repository.NewProxyRepository(client, db)
proxyExitInfoProber := repository.NewProxyExitInfoProber(configConfig) proxyExitInfoProber := repository.NewProxyExitInfoProber(configConfig)
proxyLatencyCache := repository.NewProxyLatencyCache(redisClient) proxyLatencyCache := repository.NewProxyLatencyCache(redisClient)
adminService := service.NewAdminService(userRepository, groupRepository, accountRepository, proxyRepository, apiKeyRepository, redeemCodeRepository, userGroupRateRepository, billingCacheService, proxyExitInfoProber, proxyLatencyCache, apiKeyAuthCacheInvalidator) adminService := service.NewAdminService(userRepository, groupRepository, accountRepository, soraAccountRepository, proxyRepository, apiKeyRepository, redeemCodeRepository, userGroupRateRepository, billingCacheService, proxyExitInfoProber, proxyLatencyCache, apiKeyAuthCacheInvalidator)
concurrencyCache := repository.ProvideConcurrencyCache(redisClient, configConfig) concurrencyCache := repository.ProvideConcurrencyCache(redisClient, configConfig)
concurrencyService := service.ProvideConcurrencyService(concurrencyCache, accountRepository, configConfig) concurrencyService := service.ProvideConcurrencyService(concurrencyCache, accountRepository, configConfig)
adminUserHandler := admin.NewUserHandler(adminService, concurrencyService) adminUserHandler := admin.NewUserHandler(adminService, concurrencyService)
...@@ -159,14 +160,17 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { ...@@ -159,14 +160,17 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
openAITokenProvider := service.NewOpenAITokenProvider(accountRepository, geminiTokenCache, openAIOAuthService) openAITokenProvider := service.NewOpenAITokenProvider(accountRepository, geminiTokenCache, openAIOAuthService)
openAIGatewayService := service.NewOpenAIGatewayService(accountRepository, usageLogRepository, userRepository, userSubscriptionRepository, gatewayCache, configConfig, schedulerSnapshotService, concurrencyService, billingService, rateLimitService, billingCacheService, httpUpstream, deferredService, openAITokenProvider) openAIGatewayService := service.NewOpenAIGatewayService(accountRepository, usageLogRepository, userRepository, userSubscriptionRepository, gatewayCache, configConfig, schedulerSnapshotService, concurrencyService, billingService, rateLimitService, billingCacheService, httpUpstream, deferredService, openAITokenProvider)
geminiMessagesCompatService := service.NewGeminiMessagesCompatService(accountRepository, groupRepository, gatewayCache, schedulerSnapshotService, geminiTokenProvider, rateLimitService, httpUpstream, antigravityGatewayService, configConfig) geminiMessagesCompatService := service.NewGeminiMessagesCompatService(accountRepository, groupRepository, gatewayCache, schedulerSnapshotService, geminiTokenProvider, rateLimitService, httpUpstream, antigravityGatewayService, configConfig)
opsService := service.NewOpsService(opsRepository, settingRepository, configConfig, accountRepository, userRepository, concurrencyService, gatewayService, openAIGatewayService, geminiMessagesCompatService, antigravityGatewayService) opsSystemLogSink := service.ProvideOpsSystemLogSink(opsRepository)
opsService := service.NewOpsService(opsRepository, settingRepository, configConfig, accountRepository, userRepository, concurrencyService, gatewayService, openAIGatewayService, geminiMessagesCompatService, antigravityGatewayService, opsSystemLogSink)
settingHandler := admin.NewSettingHandler(settingService, emailService, turnstileService, opsService) settingHandler := admin.NewSettingHandler(settingService, emailService, turnstileService, opsService)
opsHandler := admin.NewOpsHandler(opsService) opsHandler := admin.NewOpsHandler(opsService)
updateCache := repository.NewUpdateCache(redisClient) updateCache := repository.NewUpdateCache(redisClient)
gitHubReleaseClient := repository.ProvideGitHubReleaseClient(configConfig) gitHubReleaseClient := repository.ProvideGitHubReleaseClient(configConfig)
serviceBuildInfo := provideServiceBuildInfo(buildInfo) serviceBuildInfo := provideServiceBuildInfo(buildInfo)
updateService := service.ProvideUpdateService(updateCache, gitHubReleaseClient, serviceBuildInfo) updateService := service.ProvideUpdateService(updateCache, gitHubReleaseClient, serviceBuildInfo)
systemHandler := handler.ProvideSystemHandler(updateService) idempotencyRepository := repository.NewIdempotencyRepository(client, db)
systemOperationLockService := service.ProvideSystemOperationLockService(idempotencyRepository, configConfig)
systemHandler := handler.ProvideSystemHandler(updateService, systemOperationLockService)
adminSubscriptionHandler := admin.NewSubscriptionHandler(subscriptionService) adminSubscriptionHandler := admin.NewSubscriptionHandler(subscriptionService)
usageCleanupRepository := repository.NewUsageCleanupRepository(client, db) usageCleanupRepository := repository.NewUsageCleanupRepository(client, db)
usageCleanupService := service.ProvideUsageCleanupService(usageCleanupRepository, timingWheelService, dashboardAggregationService, configConfig) usageCleanupService := service.ProvideUsageCleanupService(usageCleanupRepository, timingWheelService, dashboardAggregationService, configConfig)
...@@ -180,11 +184,18 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { ...@@ -180,11 +184,18 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
errorPassthroughService := service.NewErrorPassthroughService(errorPassthroughRepository, errorPassthroughCache) errorPassthroughService := service.NewErrorPassthroughService(errorPassthroughRepository, errorPassthroughCache)
errorPassthroughHandler := admin.NewErrorPassthroughHandler(errorPassthroughService) errorPassthroughHandler := admin.NewErrorPassthroughHandler(errorPassthroughService)
adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, adminAnnouncementHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, promoHandler, settingHandler, opsHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler, userAttributeHandler, errorPassthroughHandler) adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, adminAnnouncementHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, promoHandler, settingHandler, opsHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler, userAttributeHandler, errorPassthroughHandler)
gatewayHandler := handler.NewGatewayHandler(gatewayService, geminiMessagesCompatService, antigravityGatewayService, userService, concurrencyService, billingCacheService, usageService, apiKeyService, errorPassthroughService, configConfig) usageRecordWorkerPool := service.NewUsageRecordWorkerPool(configConfig)
openAIGatewayHandler := handler.NewOpenAIGatewayHandler(openAIGatewayService, concurrencyService, billingCacheService, apiKeyService, errorPassthroughService, configConfig) gatewayHandler := handler.NewGatewayHandler(gatewayService, geminiMessagesCompatService, antigravityGatewayService, userService, concurrencyService, billingCacheService, usageService, apiKeyService, usageRecordWorkerPool, errorPassthroughService, configConfig)
openAIGatewayHandler := handler.NewOpenAIGatewayHandler(openAIGatewayService, concurrencyService, billingCacheService, apiKeyService, usageRecordWorkerPool, errorPassthroughService, configConfig)
soraDirectClient := service.ProvideSoraDirectClient(configConfig, httpUpstream, openAITokenProvider, accountRepository, soraAccountRepository)
soraMediaStorage := service.ProvideSoraMediaStorage(configConfig)
soraGatewayService := service.NewSoraGatewayService(soraDirectClient, soraMediaStorage, rateLimitService, configConfig)
soraGatewayHandler := handler.NewSoraGatewayHandler(gatewayService, soraGatewayService, concurrencyService, billingCacheService, usageRecordWorkerPool, configConfig)
handlerSettingHandler := handler.ProvideSettingHandler(settingService, buildInfo) handlerSettingHandler := handler.ProvideSettingHandler(settingService, buildInfo)
totpHandler := handler.NewTotpHandler(totpService) totpHandler := handler.NewTotpHandler(totpService)
handlers := handler.ProvideHandlers(authHandler, userHandler, apiKeyHandler, usageHandler, redeemHandler, subscriptionHandler, announcementHandler, adminHandlers, gatewayHandler, openAIGatewayHandler, handlerSettingHandler, totpHandler) idempotencyCoordinator := service.ProvideIdempotencyCoordinator(idempotencyRepository, configConfig)
idempotencyCleanupService := service.ProvideIdempotencyCleanupService(idempotencyRepository, configConfig)
handlers := handler.ProvideHandlers(authHandler, userHandler, apiKeyHandler, usageHandler, redeemHandler, subscriptionHandler, announcementHandler, adminHandlers, gatewayHandler, openAIGatewayHandler, soraGatewayHandler, handlerSettingHandler, totpHandler, idempotencyCoordinator, idempotencyCleanupService)
jwtAuthMiddleware := middleware.NewJWTAuthMiddleware(authService, userService) jwtAuthMiddleware := middleware.NewJWTAuthMiddleware(authService, userService)
adminAuthMiddleware := middleware.NewAdminAuthMiddleware(authService, userService, settingService) adminAuthMiddleware := middleware.NewAdminAuthMiddleware(authService, userService, settingService)
apiKeyAuthMiddleware := middleware.NewAPIKeyAuthMiddleware(apiKeyService, subscriptionService, configConfig) apiKeyAuthMiddleware := middleware.NewAPIKeyAuthMiddleware(apiKeyService, subscriptionService, configConfig)
...@@ -195,10 +206,11 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { ...@@ -195,10 +206,11 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
opsAlertEvaluatorService := service.ProvideOpsAlertEvaluatorService(opsService, opsRepository, emailService, redisClient, configConfig) opsAlertEvaluatorService := service.ProvideOpsAlertEvaluatorService(opsService, opsRepository, emailService, redisClient, configConfig)
opsCleanupService := service.ProvideOpsCleanupService(opsRepository, db, redisClient, configConfig) opsCleanupService := service.ProvideOpsCleanupService(opsRepository, db, redisClient, configConfig)
opsScheduledReportService := service.ProvideOpsScheduledReportService(opsService, userService, emailService, redisClient, configConfig) opsScheduledReportService := service.ProvideOpsScheduledReportService(opsService, userService, emailService, redisClient, configConfig)
tokenRefreshService := service.ProvideTokenRefreshService(accountRepository, oAuthService, openAIOAuthService, geminiOAuthService, antigravityOAuthService, compositeTokenCacheInvalidator, schedulerCache, configConfig) soraMediaCleanupService := service.ProvideSoraMediaCleanupService(soraMediaStorage, configConfig)
tokenRefreshService := service.ProvideTokenRefreshService(accountRepository, soraAccountRepository, oAuthService, openAIOAuthService, geminiOAuthService, antigravityOAuthService, compositeTokenCacheInvalidator, schedulerCache, configConfig)
accountExpiryService := service.ProvideAccountExpiryService(accountRepository) accountExpiryService := service.ProvideAccountExpiryService(accountRepository)
subscriptionExpiryService := service.ProvideSubscriptionExpiryService(userSubscriptionRepository) subscriptionExpiryService := service.ProvideSubscriptionExpiryService(userSubscriptionRepository)
v := provideCleanup(client, redisClient, opsMetricsCollector, opsAggregationService, opsAlertEvaluatorService, opsCleanupService, opsScheduledReportService, schedulerSnapshotService, tokenRefreshService, accountExpiryService, subscriptionExpiryService, usageCleanupService, pricingService, emailQueueService, billingCacheService, oAuthService, openAIOAuthService, geminiOAuthService, antigravityOAuthService) v := provideCleanup(client, redisClient, opsMetricsCollector, opsAggregationService, opsAlertEvaluatorService, opsCleanupService, opsScheduledReportService, opsSystemLogSink, soraMediaCleanupService, schedulerSnapshotService, tokenRefreshService, accountExpiryService, subscriptionExpiryService, usageCleanupService, idempotencyCleanupService, pricingService, emailQueueService, billingCacheService, usageRecordWorkerPool, subscriptionService, oAuthService, openAIOAuthService, geminiOAuthService, antigravityOAuthService)
application := &Application{ application := &Application{
Server: httpServer, Server: httpServer,
Cleanup: v, Cleanup: v,
...@@ -228,14 +240,19 @@ func provideCleanup( ...@@ -228,14 +240,19 @@ func provideCleanup(
opsAlertEvaluator *service.OpsAlertEvaluatorService, opsAlertEvaluator *service.OpsAlertEvaluatorService,
opsCleanup *service.OpsCleanupService, opsCleanup *service.OpsCleanupService,
opsScheduledReport *service.OpsScheduledReportService, opsScheduledReport *service.OpsScheduledReportService,
opsSystemLogSink *service.OpsSystemLogSink,
soraMediaCleanup *service.SoraMediaCleanupService,
schedulerSnapshot *service.SchedulerSnapshotService, schedulerSnapshot *service.SchedulerSnapshotService,
tokenRefresh *service.TokenRefreshService, tokenRefresh *service.TokenRefreshService,
accountExpiry *service.AccountExpiryService, accountExpiry *service.AccountExpiryService,
subscriptionExpiry *service.SubscriptionExpiryService, subscriptionExpiry *service.SubscriptionExpiryService,
usageCleanup *service.UsageCleanupService, usageCleanup *service.UsageCleanupService,
idempotencyCleanup *service.IdempotencyCleanupService,
pricing *service.PricingService, pricing *service.PricingService,
emailQueue *service.EmailQueueService, emailQueue *service.EmailQueueService,
billingCache *service.BillingCacheService, billingCache *service.BillingCacheService,
usageRecordWorkerPool *service.UsageRecordWorkerPool,
subscriptionService *service.SubscriptionService,
oauth *service.OAuthService, oauth *service.OAuthService,
openaiOAuth *service.OpenAIOAuthService, openaiOAuth *service.OpenAIOAuthService,
geminiOAuth *service.GeminiOAuthService, geminiOAuth *service.GeminiOAuthService,
...@@ -261,6 +278,18 @@ func provideCleanup( ...@@ -261,6 +278,18 @@ func provideCleanup(
} }
return nil return nil
}}, }},
{"OpsSystemLogSink", func() error {
if opsSystemLogSink != nil {
opsSystemLogSink.Stop()
}
return nil
}},
{"SoraMediaCleanupService", func() error {
if soraMediaCleanup != nil {
soraMediaCleanup.Stop()
}
return nil
}},
{"OpsAlertEvaluatorService", func() error { {"OpsAlertEvaluatorService", func() error {
if opsAlertEvaluator != nil { if opsAlertEvaluator != nil {
opsAlertEvaluator.Stop() opsAlertEvaluator.Stop()
...@@ -291,6 +320,12 @@ func provideCleanup( ...@@ -291,6 +320,12 @@ func provideCleanup(
} }
return nil return nil
}}, }},
{"IdempotencyCleanupService", func() error {
if idempotencyCleanup != nil {
idempotencyCleanup.Stop()
}
return nil
}},
{"TokenRefreshService", func() error { {"TokenRefreshService", func() error {
tokenRefresh.Stop() tokenRefresh.Stop()
return nil return nil
...@@ -303,6 +338,12 @@ func provideCleanup( ...@@ -303,6 +338,12 @@ func provideCleanup(
subscriptionExpiry.Stop() subscriptionExpiry.Stop()
return nil return nil
}}, }},
{"SubscriptionService", func() error {
if subscriptionService != nil {
subscriptionService.Stop()
}
return nil
}},
{"PricingService", func() error { {"PricingService", func() error {
pricing.Stop() pricing.Stop()
return nil return nil
...@@ -315,6 +356,12 @@ func provideCleanup( ...@@ -315,6 +356,12 @@ func provideCleanup(
billingCache.Stop() billingCache.Stop()
return nil return nil
}}, }},
{"UsageRecordWorkerPool", func() error {
if usageRecordWorkerPool != nil {
usageRecordWorkerPool.Stop()
}
return nil
}},
{"OAuthService", func() error { {"OAuthService", func() error {
oauth.Stop() oauth.Stop()
return nil return nil
......
...@@ -36,6 +36,8 @@ type APIKey struct { ...@@ -36,6 +36,8 @@ type APIKey struct {
GroupID *int64 `json:"group_id,omitempty"` GroupID *int64 `json:"group_id,omitempty"`
// Status holds the value of the "status" field. // Status holds the value of the "status" field.
Status string `json:"status,omitempty"` Status string `json:"status,omitempty"`
// Last usage time of this API key
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
// Allowed IPs/CIDRs, e.g. ["192.168.1.100", "10.0.0.0/8"] // Allowed IPs/CIDRs, e.g. ["192.168.1.100", "10.0.0.0/8"]
IPWhitelist []string `json:"ip_whitelist,omitempty"` IPWhitelist []string `json:"ip_whitelist,omitempty"`
// Blocked IPs/CIDRs // Blocked IPs/CIDRs
...@@ -109,7 +111,7 @@ func (*APIKey) scanValues(columns []string) ([]any, error) { ...@@ -109,7 +111,7 @@ func (*APIKey) scanValues(columns []string) ([]any, error) {
values[i] = new(sql.NullInt64) values[i] = new(sql.NullInt64)
case apikey.FieldKey, apikey.FieldName, apikey.FieldStatus: case apikey.FieldKey, apikey.FieldName, apikey.FieldStatus:
values[i] = new(sql.NullString) values[i] = new(sql.NullString)
case apikey.FieldCreatedAt, apikey.FieldUpdatedAt, apikey.FieldDeletedAt, apikey.FieldExpiresAt: case apikey.FieldCreatedAt, apikey.FieldUpdatedAt, apikey.FieldDeletedAt, apikey.FieldLastUsedAt, apikey.FieldExpiresAt:
values[i] = new(sql.NullTime) values[i] = new(sql.NullTime)
default: default:
values[i] = new(sql.UnknownType) values[i] = new(sql.UnknownType)
...@@ -182,6 +184,13 @@ func (_m *APIKey) assignValues(columns []string, values []any) error { ...@@ -182,6 +184,13 @@ func (_m *APIKey) assignValues(columns []string, values []any) error {
} else if value.Valid { } else if value.Valid {
_m.Status = value.String _m.Status = value.String
} }
case apikey.FieldLastUsedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field last_used_at", values[i])
} else if value.Valid {
_m.LastUsedAt = new(time.Time)
*_m.LastUsedAt = value.Time
}
case apikey.FieldIPWhitelist: case apikey.FieldIPWhitelist:
if value, ok := values[i].(*[]byte); !ok { if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field ip_whitelist", values[i]) return fmt.Errorf("unexpected type %T for field ip_whitelist", values[i])
...@@ -296,6 +305,11 @@ func (_m *APIKey) String() string { ...@@ -296,6 +305,11 @@ func (_m *APIKey) String() string {
builder.WriteString("status=") builder.WriteString("status=")
builder.WriteString(_m.Status) builder.WriteString(_m.Status)
builder.WriteString(", ") builder.WriteString(", ")
if v := _m.LastUsedAt; v != nil {
builder.WriteString("last_used_at=")
builder.WriteString(v.Format(time.ANSIC))
}
builder.WriteString(", ")
builder.WriteString("ip_whitelist=") builder.WriteString("ip_whitelist=")
builder.WriteString(fmt.Sprintf("%v", _m.IPWhitelist)) builder.WriteString(fmt.Sprintf("%v", _m.IPWhitelist))
builder.WriteString(", ") builder.WriteString(", ")
......
...@@ -31,6 +31,8 @@ const ( ...@@ -31,6 +31,8 @@ const (
FieldGroupID = "group_id" FieldGroupID = "group_id"
// FieldStatus holds the string denoting the status field in the database. // FieldStatus holds the string denoting the status field in the database.
FieldStatus = "status" FieldStatus = "status"
// FieldLastUsedAt holds the string denoting the last_used_at field in the database.
FieldLastUsedAt = "last_used_at"
// FieldIPWhitelist holds the string denoting the ip_whitelist field in the database. // FieldIPWhitelist holds the string denoting the ip_whitelist field in the database.
FieldIPWhitelist = "ip_whitelist" FieldIPWhitelist = "ip_whitelist"
// FieldIPBlacklist holds the string denoting the ip_blacklist field in the database. // FieldIPBlacklist holds the string denoting the ip_blacklist field in the database.
...@@ -83,6 +85,7 @@ var Columns = []string{ ...@@ -83,6 +85,7 @@ var Columns = []string{
FieldName, FieldName,
FieldGroupID, FieldGroupID,
FieldStatus, FieldStatus,
FieldLastUsedAt,
FieldIPWhitelist, FieldIPWhitelist,
FieldIPBlacklist, FieldIPBlacklist,
FieldQuota, FieldQuota,
...@@ -176,6 +179,11 @@ func ByStatus(opts ...sql.OrderTermOption) OrderOption { ...@@ -176,6 +179,11 @@ func ByStatus(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldStatus, opts...).ToFunc() return sql.OrderByField(FieldStatus, opts...).ToFunc()
} }
// ByLastUsedAt orders the results by the last_used_at field.
func ByLastUsedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldLastUsedAt, opts...).ToFunc()
}
// ByQuota orders the results by the quota field. // ByQuota orders the results by the quota field.
func ByQuota(opts ...sql.OrderTermOption) OrderOption { func ByQuota(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldQuota, opts...).ToFunc() return sql.OrderByField(FieldQuota, opts...).ToFunc()
......
...@@ -95,6 +95,11 @@ func Status(v string) predicate.APIKey { ...@@ -95,6 +95,11 @@ func Status(v string) predicate.APIKey {
return predicate.APIKey(sql.FieldEQ(FieldStatus, v)) return predicate.APIKey(sql.FieldEQ(FieldStatus, v))
} }
// LastUsedAt applies equality check predicate on the "last_used_at" field. It's identical to LastUsedAtEQ.
func LastUsedAt(v time.Time) predicate.APIKey {
return predicate.APIKey(sql.FieldEQ(FieldLastUsedAt, v))
}
// Quota applies equality check predicate on the "quota" field. It's identical to QuotaEQ. // Quota applies equality check predicate on the "quota" field. It's identical to QuotaEQ.
func Quota(v float64) predicate.APIKey { func Quota(v float64) predicate.APIKey {
return predicate.APIKey(sql.FieldEQ(FieldQuota, v)) return predicate.APIKey(sql.FieldEQ(FieldQuota, v))
...@@ -485,6 +490,56 @@ func StatusContainsFold(v string) predicate.APIKey { ...@@ -485,6 +490,56 @@ func StatusContainsFold(v string) predicate.APIKey {
return predicate.APIKey(sql.FieldContainsFold(FieldStatus, v)) return predicate.APIKey(sql.FieldContainsFold(FieldStatus, v))
} }
// LastUsedAtEQ applies the EQ predicate on the "last_used_at" field.
func LastUsedAtEQ(v time.Time) predicate.APIKey {
return predicate.APIKey(sql.FieldEQ(FieldLastUsedAt, v))
}
// LastUsedAtNEQ applies the NEQ predicate on the "last_used_at" field.
func LastUsedAtNEQ(v time.Time) predicate.APIKey {
return predicate.APIKey(sql.FieldNEQ(FieldLastUsedAt, v))
}
// LastUsedAtIn applies the In predicate on the "last_used_at" field.
func LastUsedAtIn(vs ...time.Time) predicate.APIKey {
return predicate.APIKey(sql.FieldIn(FieldLastUsedAt, vs...))
}
// LastUsedAtNotIn applies the NotIn predicate on the "last_used_at" field.
func LastUsedAtNotIn(vs ...time.Time) predicate.APIKey {
return predicate.APIKey(sql.FieldNotIn(FieldLastUsedAt, vs...))
}
// LastUsedAtGT applies the GT predicate on the "last_used_at" field.
func LastUsedAtGT(v time.Time) predicate.APIKey {
return predicate.APIKey(sql.FieldGT(FieldLastUsedAt, v))
}
// LastUsedAtGTE applies the GTE predicate on the "last_used_at" field.
func LastUsedAtGTE(v time.Time) predicate.APIKey {
return predicate.APIKey(sql.FieldGTE(FieldLastUsedAt, v))
}
// LastUsedAtLT applies the LT predicate on the "last_used_at" field.
func LastUsedAtLT(v time.Time) predicate.APIKey {
return predicate.APIKey(sql.FieldLT(FieldLastUsedAt, v))
}
// LastUsedAtLTE applies the LTE predicate on the "last_used_at" field.
func LastUsedAtLTE(v time.Time) predicate.APIKey {
return predicate.APIKey(sql.FieldLTE(FieldLastUsedAt, v))
}
// LastUsedAtIsNil applies the IsNil predicate on the "last_used_at" field.
func LastUsedAtIsNil() predicate.APIKey {
return predicate.APIKey(sql.FieldIsNull(FieldLastUsedAt))
}
// LastUsedAtNotNil applies the NotNil predicate on the "last_used_at" field.
func LastUsedAtNotNil() predicate.APIKey {
return predicate.APIKey(sql.FieldNotNull(FieldLastUsedAt))
}
// IPWhitelistIsNil applies the IsNil predicate on the "ip_whitelist" field. // IPWhitelistIsNil applies the IsNil predicate on the "ip_whitelist" field.
func IPWhitelistIsNil() predicate.APIKey { func IPWhitelistIsNil() predicate.APIKey {
return predicate.APIKey(sql.FieldIsNull(FieldIPWhitelist)) return predicate.APIKey(sql.FieldIsNull(FieldIPWhitelist))
......
...@@ -113,6 +113,20 @@ func (_c *APIKeyCreate) SetNillableStatus(v *string) *APIKeyCreate { ...@@ -113,6 +113,20 @@ func (_c *APIKeyCreate) SetNillableStatus(v *string) *APIKeyCreate {
return _c return _c
} }
// SetLastUsedAt sets the "last_used_at" field.
func (_c *APIKeyCreate) SetLastUsedAt(v time.Time) *APIKeyCreate {
_c.mutation.SetLastUsedAt(v)
return _c
}
// SetNillableLastUsedAt sets the "last_used_at" field if the given value is not nil.
func (_c *APIKeyCreate) SetNillableLastUsedAt(v *time.Time) *APIKeyCreate {
if v != nil {
_c.SetLastUsedAt(*v)
}
return _c
}
// SetIPWhitelist sets the "ip_whitelist" field. // SetIPWhitelist sets the "ip_whitelist" field.
func (_c *APIKeyCreate) SetIPWhitelist(v []string) *APIKeyCreate { func (_c *APIKeyCreate) SetIPWhitelist(v []string) *APIKeyCreate {
_c.mutation.SetIPWhitelist(v) _c.mutation.SetIPWhitelist(v)
...@@ -353,6 +367,10 @@ func (_c *APIKeyCreate) createSpec() (*APIKey, *sqlgraph.CreateSpec) { ...@@ -353,6 +367,10 @@ func (_c *APIKeyCreate) createSpec() (*APIKey, *sqlgraph.CreateSpec) {
_spec.SetField(apikey.FieldStatus, field.TypeString, value) _spec.SetField(apikey.FieldStatus, field.TypeString, value)
_node.Status = value _node.Status = value
} }
if value, ok := _c.mutation.LastUsedAt(); ok {
_spec.SetField(apikey.FieldLastUsedAt, field.TypeTime, value)
_node.LastUsedAt = &value
}
if value, ok := _c.mutation.IPWhitelist(); ok { if value, ok := _c.mutation.IPWhitelist(); ok {
_spec.SetField(apikey.FieldIPWhitelist, field.TypeJSON, value) _spec.SetField(apikey.FieldIPWhitelist, field.TypeJSON, value)
_node.IPWhitelist = value _node.IPWhitelist = value
...@@ -571,6 +589,24 @@ func (u *APIKeyUpsert) UpdateStatus() *APIKeyUpsert { ...@@ -571,6 +589,24 @@ func (u *APIKeyUpsert) UpdateStatus() *APIKeyUpsert {
return u return u
} }
// SetLastUsedAt sets the "last_used_at" field.
func (u *APIKeyUpsert) SetLastUsedAt(v time.Time) *APIKeyUpsert {
u.Set(apikey.FieldLastUsedAt, v)
return u
}
// UpdateLastUsedAt sets the "last_used_at" field to the value that was provided on create.
func (u *APIKeyUpsert) UpdateLastUsedAt() *APIKeyUpsert {
u.SetExcluded(apikey.FieldLastUsedAt)
return u
}
// ClearLastUsedAt clears the value of the "last_used_at" field.
func (u *APIKeyUpsert) ClearLastUsedAt() *APIKeyUpsert {
u.SetNull(apikey.FieldLastUsedAt)
return u
}
// SetIPWhitelist sets the "ip_whitelist" field. // SetIPWhitelist sets the "ip_whitelist" field.
func (u *APIKeyUpsert) SetIPWhitelist(v []string) *APIKeyUpsert { func (u *APIKeyUpsert) SetIPWhitelist(v []string) *APIKeyUpsert {
u.Set(apikey.FieldIPWhitelist, v) u.Set(apikey.FieldIPWhitelist, v)
...@@ -818,6 +854,27 @@ func (u *APIKeyUpsertOne) UpdateStatus() *APIKeyUpsertOne { ...@@ -818,6 +854,27 @@ func (u *APIKeyUpsertOne) UpdateStatus() *APIKeyUpsertOne {
}) })
} }
// SetLastUsedAt sets the "last_used_at" field.
func (u *APIKeyUpsertOne) SetLastUsedAt(v time.Time) *APIKeyUpsertOne {
return u.Update(func(s *APIKeyUpsert) {
s.SetLastUsedAt(v)
})
}
// UpdateLastUsedAt sets the "last_used_at" field to the value that was provided on create.
func (u *APIKeyUpsertOne) UpdateLastUsedAt() *APIKeyUpsertOne {
return u.Update(func(s *APIKeyUpsert) {
s.UpdateLastUsedAt()
})
}
// ClearLastUsedAt clears the value of the "last_used_at" field.
func (u *APIKeyUpsertOne) ClearLastUsedAt() *APIKeyUpsertOne {
return u.Update(func(s *APIKeyUpsert) {
s.ClearLastUsedAt()
})
}
// SetIPWhitelist sets the "ip_whitelist" field. // SetIPWhitelist sets the "ip_whitelist" field.
func (u *APIKeyUpsertOne) SetIPWhitelist(v []string) *APIKeyUpsertOne { func (u *APIKeyUpsertOne) SetIPWhitelist(v []string) *APIKeyUpsertOne {
return u.Update(func(s *APIKeyUpsert) { return u.Update(func(s *APIKeyUpsert) {
...@@ -1246,6 +1303,27 @@ func (u *APIKeyUpsertBulk) UpdateStatus() *APIKeyUpsertBulk { ...@@ -1246,6 +1303,27 @@ func (u *APIKeyUpsertBulk) UpdateStatus() *APIKeyUpsertBulk {
}) })
} }
// SetLastUsedAt sets the "last_used_at" field.
func (u *APIKeyUpsertBulk) SetLastUsedAt(v time.Time) *APIKeyUpsertBulk {
return u.Update(func(s *APIKeyUpsert) {
s.SetLastUsedAt(v)
})
}
// UpdateLastUsedAt sets the "last_used_at" field to the value that was provided on create.
func (u *APIKeyUpsertBulk) UpdateLastUsedAt() *APIKeyUpsertBulk {
return u.Update(func(s *APIKeyUpsert) {
s.UpdateLastUsedAt()
})
}
// ClearLastUsedAt clears the value of the "last_used_at" field.
func (u *APIKeyUpsertBulk) ClearLastUsedAt() *APIKeyUpsertBulk {
return u.Update(func(s *APIKeyUpsert) {
s.ClearLastUsedAt()
})
}
// SetIPWhitelist sets the "ip_whitelist" field. // SetIPWhitelist sets the "ip_whitelist" field.
func (u *APIKeyUpsertBulk) SetIPWhitelist(v []string) *APIKeyUpsertBulk { func (u *APIKeyUpsertBulk) SetIPWhitelist(v []string) *APIKeyUpsertBulk {
return u.Update(func(s *APIKeyUpsert) { return u.Update(func(s *APIKeyUpsert) {
......
...@@ -134,6 +134,26 @@ func (_u *APIKeyUpdate) SetNillableStatus(v *string) *APIKeyUpdate { ...@@ -134,6 +134,26 @@ func (_u *APIKeyUpdate) SetNillableStatus(v *string) *APIKeyUpdate {
return _u return _u
} }
// SetLastUsedAt sets the "last_used_at" field.
func (_u *APIKeyUpdate) SetLastUsedAt(v time.Time) *APIKeyUpdate {
_u.mutation.SetLastUsedAt(v)
return _u
}
// SetNillableLastUsedAt sets the "last_used_at" field if the given value is not nil.
func (_u *APIKeyUpdate) SetNillableLastUsedAt(v *time.Time) *APIKeyUpdate {
if v != nil {
_u.SetLastUsedAt(*v)
}
return _u
}
// ClearLastUsedAt clears the value of the "last_used_at" field.
func (_u *APIKeyUpdate) ClearLastUsedAt() *APIKeyUpdate {
_u.mutation.ClearLastUsedAt()
return _u
}
// SetIPWhitelist sets the "ip_whitelist" field. // SetIPWhitelist sets the "ip_whitelist" field.
func (_u *APIKeyUpdate) SetIPWhitelist(v []string) *APIKeyUpdate { func (_u *APIKeyUpdate) SetIPWhitelist(v []string) *APIKeyUpdate {
_u.mutation.SetIPWhitelist(v) _u.mutation.SetIPWhitelist(v)
...@@ -390,6 +410,12 @@ func (_u *APIKeyUpdate) sqlSave(ctx context.Context) (_node int, err error) { ...@@ -390,6 +410,12 @@ func (_u *APIKeyUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if value, ok := _u.mutation.Status(); ok { if value, ok := _u.mutation.Status(); ok {
_spec.SetField(apikey.FieldStatus, field.TypeString, value) _spec.SetField(apikey.FieldStatus, field.TypeString, value)
} }
if value, ok := _u.mutation.LastUsedAt(); ok {
_spec.SetField(apikey.FieldLastUsedAt, field.TypeTime, value)
}
if _u.mutation.LastUsedAtCleared() {
_spec.ClearField(apikey.FieldLastUsedAt, field.TypeTime)
}
if value, ok := _u.mutation.IPWhitelist(); ok { if value, ok := _u.mutation.IPWhitelist(); ok {
_spec.SetField(apikey.FieldIPWhitelist, field.TypeJSON, value) _spec.SetField(apikey.FieldIPWhitelist, field.TypeJSON, value)
} }
...@@ -655,6 +681,26 @@ func (_u *APIKeyUpdateOne) SetNillableStatus(v *string) *APIKeyUpdateOne { ...@@ -655,6 +681,26 @@ func (_u *APIKeyUpdateOne) SetNillableStatus(v *string) *APIKeyUpdateOne {
return _u return _u
} }
// SetLastUsedAt sets the "last_used_at" field.
func (_u *APIKeyUpdateOne) SetLastUsedAt(v time.Time) *APIKeyUpdateOne {
_u.mutation.SetLastUsedAt(v)
return _u
}
// SetNillableLastUsedAt sets the "last_used_at" field if the given value is not nil.
func (_u *APIKeyUpdateOne) SetNillableLastUsedAt(v *time.Time) *APIKeyUpdateOne {
if v != nil {
_u.SetLastUsedAt(*v)
}
return _u
}
// ClearLastUsedAt clears the value of the "last_used_at" field.
func (_u *APIKeyUpdateOne) ClearLastUsedAt() *APIKeyUpdateOne {
_u.mutation.ClearLastUsedAt()
return _u
}
// SetIPWhitelist sets the "ip_whitelist" field. // SetIPWhitelist sets the "ip_whitelist" field.
func (_u *APIKeyUpdateOne) SetIPWhitelist(v []string) *APIKeyUpdateOne { func (_u *APIKeyUpdateOne) SetIPWhitelist(v []string) *APIKeyUpdateOne {
_u.mutation.SetIPWhitelist(v) _u.mutation.SetIPWhitelist(v)
...@@ -941,6 +987,12 @@ func (_u *APIKeyUpdateOne) sqlSave(ctx context.Context) (_node *APIKey, err erro ...@@ -941,6 +987,12 @@ func (_u *APIKeyUpdateOne) sqlSave(ctx context.Context) (_node *APIKey, err erro
if value, ok := _u.mutation.Status(); ok { if value, ok := _u.mutation.Status(); ok {
_spec.SetField(apikey.FieldStatus, field.TypeString, value) _spec.SetField(apikey.FieldStatus, field.TypeString, value)
} }
if value, ok := _u.mutation.LastUsedAt(); ok {
_spec.SetField(apikey.FieldLastUsedAt, field.TypeTime, value)
}
if _u.mutation.LastUsedAtCleared() {
_spec.ClearField(apikey.FieldLastUsedAt, field.TypeTime)
}
if value, ok := _u.mutation.IPWhitelist(); ok { if value, ok := _u.mutation.IPWhitelist(); ok {
_spec.SetField(apikey.FieldIPWhitelist, field.TypeJSON, value) _spec.SetField(apikey.FieldIPWhitelist, field.TypeJSON, value)
} }
......
...@@ -26,6 +26,7 @@ import ( ...@@ -26,6 +26,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/promocodeusage" "github.com/Wei-Shaw/sub2api/ent/promocodeusage"
"github.com/Wei-Shaw/sub2api/ent/proxy" "github.com/Wei-Shaw/sub2api/ent/proxy"
"github.com/Wei-Shaw/sub2api/ent/redeemcode" "github.com/Wei-Shaw/sub2api/ent/redeemcode"
"github.com/Wei-Shaw/sub2api/ent/securitysecret"
"github.com/Wei-Shaw/sub2api/ent/setting" "github.com/Wei-Shaw/sub2api/ent/setting"
"github.com/Wei-Shaw/sub2api/ent/usagecleanuptask" "github.com/Wei-Shaw/sub2api/ent/usagecleanuptask"
"github.com/Wei-Shaw/sub2api/ent/usagelog" "github.com/Wei-Shaw/sub2api/ent/usagelog"
...@@ -65,6 +66,8 @@ type Client struct { ...@@ -65,6 +66,8 @@ type Client struct {
Proxy *ProxyClient Proxy *ProxyClient
// RedeemCode is the client for interacting with the RedeemCode builders. // RedeemCode is the client for interacting with the RedeemCode builders.
RedeemCode *RedeemCodeClient RedeemCode *RedeemCodeClient
// SecuritySecret is the client for interacting with the SecuritySecret builders.
SecuritySecret *SecuritySecretClient
// Setting is the client for interacting with the Setting builders. // Setting is the client for interacting with the Setting builders.
Setting *SettingClient Setting *SettingClient
// UsageCleanupTask is the client for interacting with the UsageCleanupTask builders. // UsageCleanupTask is the client for interacting with the UsageCleanupTask builders.
...@@ -103,6 +106,7 @@ func (c *Client) init() { ...@@ -103,6 +106,7 @@ func (c *Client) init() {
c.PromoCodeUsage = NewPromoCodeUsageClient(c.config) c.PromoCodeUsage = NewPromoCodeUsageClient(c.config)
c.Proxy = NewProxyClient(c.config) c.Proxy = NewProxyClient(c.config)
c.RedeemCode = NewRedeemCodeClient(c.config) c.RedeemCode = NewRedeemCodeClient(c.config)
c.SecuritySecret = NewSecuritySecretClient(c.config)
c.Setting = NewSettingClient(c.config) c.Setting = NewSettingClient(c.config)
c.UsageCleanupTask = NewUsageCleanupTaskClient(c.config) c.UsageCleanupTask = NewUsageCleanupTaskClient(c.config)
c.UsageLog = NewUsageLogClient(c.config) c.UsageLog = NewUsageLogClient(c.config)
...@@ -214,6 +218,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { ...@@ -214,6 +218,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
PromoCodeUsage: NewPromoCodeUsageClient(cfg), PromoCodeUsage: NewPromoCodeUsageClient(cfg),
Proxy: NewProxyClient(cfg), Proxy: NewProxyClient(cfg),
RedeemCode: NewRedeemCodeClient(cfg), RedeemCode: NewRedeemCodeClient(cfg),
SecuritySecret: NewSecuritySecretClient(cfg),
Setting: NewSettingClient(cfg), Setting: NewSettingClient(cfg),
UsageCleanupTask: NewUsageCleanupTaskClient(cfg), UsageCleanupTask: NewUsageCleanupTaskClient(cfg),
UsageLog: NewUsageLogClient(cfg), UsageLog: NewUsageLogClient(cfg),
...@@ -252,6 +257,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) ...@@ -252,6 +257,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
PromoCodeUsage: NewPromoCodeUsageClient(cfg), PromoCodeUsage: NewPromoCodeUsageClient(cfg),
Proxy: NewProxyClient(cfg), Proxy: NewProxyClient(cfg),
RedeemCode: NewRedeemCodeClient(cfg), RedeemCode: NewRedeemCodeClient(cfg),
SecuritySecret: NewSecuritySecretClient(cfg),
Setting: NewSettingClient(cfg), Setting: NewSettingClient(cfg),
UsageCleanupTask: NewUsageCleanupTaskClient(cfg), UsageCleanupTask: NewUsageCleanupTaskClient(cfg),
UsageLog: NewUsageLogClient(cfg), UsageLog: NewUsageLogClient(cfg),
...@@ -291,8 +297,8 @@ func (c *Client) Use(hooks ...Hook) { ...@@ -291,8 +297,8 @@ func (c *Client) Use(hooks ...Hook) {
for _, n := range []interface{ Use(...Hook) }{ for _, n := range []interface{ Use(...Hook) }{
c.APIKey, c.Account, c.AccountGroup, c.Announcement, c.AnnouncementRead, c.APIKey, c.Account, c.AccountGroup, c.Announcement, c.AnnouncementRead,
c.ErrorPassthroughRule, c.Group, c.PromoCode, c.PromoCodeUsage, c.Proxy, c.ErrorPassthroughRule, c.Group, c.PromoCode, c.PromoCodeUsage, c.Proxy,
c.RedeemCode, c.Setting, c.UsageCleanupTask, c.UsageLog, c.User, c.RedeemCode, c.SecuritySecret, c.Setting, c.UsageCleanupTask, c.UsageLog,
c.UserAllowedGroup, c.UserAttributeDefinition, c.UserAttributeValue, c.User, c.UserAllowedGroup, c.UserAttributeDefinition, c.UserAttributeValue,
c.UserSubscription, c.UserSubscription,
} { } {
n.Use(hooks...) n.Use(hooks...)
...@@ -305,8 +311,8 @@ func (c *Client) Intercept(interceptors ...Interceptor) { ...@@ -305,8 +311,8 @@ func (c *Client) Intercept(interceptors ...Interceptor) {
for _, n := range []interface{ Intercept(...Interceptor) }{ for _, n := range []interface{ Intercept(...Interceptor) }{
c.APIKey, c.Account, c.AccountGroup, c.Announcement, c.AnnouncementRead, c.APIKey, c.Account, c.AccountGroup, c.Announcement, c.AnnouncementRead,
c.ErrorPassthroughRule, c.Group, c.PromoCode, c.PromoCodeUsage, c.Proxy, c.ErrorPassthroughRule, c.Group, c.PromoCode, c.PromoCodeUsage, c.Proxy,
c.RedeemCode, c.Setting, c.UsageCleanupTask, c.UsageLog, c.User, c.RedeemCode, c.SecuritySecret, c.Setting, c.UsageCleanupTask, c.UsageLog,
c.UserAllowedGroup, c.UserAttributeDefinition, c.UserAttributeValue, c.User, c.UserAllowedGroup, c.UserAttributeDefinition, c.UserAttributeValue,
c.UserSubscription, c.UserSubscription,
} { } {
n.Intercept(interceptors...) n.Intercept(interceptors...)
...@@ -338,6 +344,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { ...@@ -338,6 +344,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
return c.Proxy.mutate(ctx, m) return c.Proxy.mutate(ctx, m)
case *RedeemCodeMutation: case *RedeemCodeMutation:
return c.RedeemCode.mutate(ctx, m) return c.RedeemCode.mutate(ctx, m)
case *SecuritySecretMutation:
return c.SecuritySecret.mutate(ctx, m)
case *SettingMutation: case *SettingMutation:
return c.Setting.mutate(ctx, m) return c.Setting.mutate(ctx, m)
case *UsageCleanupTaskMutation: case *UsageCleanupTaskMutation:
...@@ -2197,6 +2205,139 @@ func (c *RedeemCodeClient) mutate(ctx context.Context, m *RedeemCodeMutation) (V ...@@ -2197,6 +2205,139 @@ func (c *RedeemCodeClient) mutate(ctx context.Context, m *RedeemCodeMutation) (V
} }
} }
// SecuritySecretClient is a client for the SecuritySecret schema.
type SecuritySecretClient struct {
config
}
// NewSecuritySecretClient returns a client for the SecuritySecret from the given config.
func NewSecuritySecretClient(c config) *SecuritySecretClient {
return &SecuritySecretClient{config: c}
}
// Use adds a list of mutation hooks to the hooks stack.
// A call to `Use(f, g, h)` equals to `securitysecret.Hooks(f(g(h())))`.
func (c *SecuritySecretClient) Use(hooks ...Hook) {
c.hooks.SecuritySecret = append(c.hooks.SecuritySecret, hooks...)
}
// Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `securitysecret.Intercept(f(g(h())))`.
func (c *SecuritySecretClient) Intercept(interceptors ...Interceptor) {
c.inters.SecuritySecret = append(c.inters.SecuritySecret, interceptors...)
}
// Create returns a builder for creating a SecuritySecret entity.
func (c *SecuritySecretClient) Create() *SecuritySecretCreate {
mutation := newSecuritySecretMutation(c.config, OpCreate)
return &SecuritySecretCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// CreateBulk returns a builder for creating a bulk of SecuritySecret entities.
func (c *SecuritySecretClient) CreateBulk(builders ...*SecuritySecretCreate) *SecuritySecretCreateBulk {
return &SecuritySecretCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *SecuritySecretClient) MapCreateBulk(slice any, setFunc func(*SecuritySecretCreate, int)) *SecuritySecretCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &SecuritySecretCreateBulk{err: fmt.Errorf("calling to SecuritySecretClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*SecuritySecretCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &SecuritySecretCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for SecuritySecret.
func (c *SecuritySecretClient) Update() *SecuritySecretUpdate {
mutation := newSecuritySecretMutation(c.config, OpUpdate)
return &SecuritySecretUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOne returns an update builder for the given entity.
func (c *SecuritySecretClient) UpdateOne(_m *SecuritySecret) *SecuritySecretUpdateOne {
mutation := newSecuritySecretMutation(c.config, OpUpdateOne, withSecuritySecret(_m))
return &SecuritySecretUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOneID returns an update builder for the given id.
func (c *SecuritySecretClient) UpdateOneID(id int64) *SecuritySecretUpdateOne {
mutation := newSecuritySecretMutation(c.config, OpUpdateOne, withSecuritySecretID(id))
return &SecuritySecretUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// Delete returns a delete builder for SecuritySecret.
func (c *SecuritySecretClient) Delete() *SecuritySecretDelete {
mutation := newSecuritySecretMutation(c.config, OpDelete)
return &SecuritySecretDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// DeleteOne returns a builder for deleting the given entity.
func (c *SecuritySecretClient) DeleteOne(_m *SecuritySecret) *SecuritySecretDeleteOne {
return c.DeleteOneID(_m.ID)
}
// DeleteOneID returns a builder for deleting the given entity by its id.
func (c *SecuritySecretClient) DeleteOneID(id int64) *SecuritySecretDeleteOne {
builder := c.Delete().Where(securitysecret.ID(id))
builder.mutation.id = &id
builder.mutation.op = OpDeleteOne
return &SecuritySecretDeleteOne{builder}
}
// Query returns a query builder for SecuritySecret.
func (c *SecuritySecretClient) Query() *SecuritySecretQuery {
return &SecuritySecretQuery{
config: c.config,
ctx: &QueryContext{Type: TypeSecuritySecret},
inters: c.Interceptors(),
}
}
// Get returns a SecuritySecret entity by its id.
func (c *SecuritySecretClient) Get(ctx context.Context, id int64) (*SecuritySecret, error) {
return c.Query().Where(securitysecret.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
func (c *SecuritySecretClient) GetX(ctx context.Context, id int64) *SecuritySecret {
obj, err := c.Get(ctx, id)
if err != nil {
panic(err)
}
return obj
}
// Hooks returns the client hooks.
func (c *SecuritySecretClient) Hooks() []Hook {
return c.hooks.SecuritySecret
}
// Interceptors returns the client interceptors.
func (c *SecuritySecretClient) Interceptors() []Interceptor {
return c.inters.SecuritySecret
}
func (c *SecuritySecretClient) mutate(ctx context.Context, m *SecuritySecretMutation) (Value, error) {
switch m.Op() {
case OpCreate:
return (&SecuritySecretCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdate:
return (&SecuritySecretUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdateOne:
return (&SecuritySecretUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpDelete, OpDeleteOne:
return (&SecuritySecretDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
default:
return nil, fmt.Errorf("ent: unknown SecuritySecret mutation op: %q", m.Op())
}
}
// SettingClient is a client for the Setting schema. // SettingClient is a client for the Setting schema.
type SettingClient struct { type SettingClient struct {
config config
...@@ -3607,13 +3748,13 @@ type ( ...@@ -3607,13 +3748,13 @@ type (
hooks struct { hooks struct {
APIKey, Account, AccountGroup, Announcement, AnnouncementRead, APIKey, Account, AccountGroup, Announcement, AnnouncementRead,
ErrorPassthroughRule, Group, PromoCode, PromoCodeUsage, Proxy, RedeemCode, ErrorPassthroughRule, Group, PromoCode, PromoCodeUsage, Proxy, RedeemCode,
Setting, UsageCleanupTask, UsageLog, User, UserAllowedGroup, SecuritySecret, Setting, UsageCleanupTask, UsageLog, User, UserAllowedGroup,
UserAttributeDefinition, UserAttributeValue, UserSubscription []ent.Hook UserAttributeDefinition, UserAttributeValue, UserSubscription []ent.Hook
} }
inters struct { inters struct {
APIKey, Account, AccountGroup, Announcement, AnnouncementRead, APIKey, Account, AccountGroup, Announcement, AnnouncementRead,
ErrorPassthroughRule, Group, PromoCode, PromoCodeUsage, Proxy, RedeemCode, ErrorPassthroughRule, Group, PromoCode, PromoCodeUsage, Proxy, RedeemCode,
Setting, UsageCleanupTask, UsageLog, User, UserAllowedGroup, SecuritySecret, Setting, UsageCleanupTask, UsageLog, User, UserAllowedGroup,
UserAttributeDefinition, UserAttributeValue, UserSubscription []ent.Interceptor UserAttributeDefinition, UserAttributeValue, UserSubscription []ent.Interceptor
} }
) )
......
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/promocodeusage" "github.com/Wei-Shaw/sub2api/ent/promocodeusage"
"github.com/Wei-Shaw/sub2api/ent/proxy" "github.com/Wei-Shaw/sub2api/ent/proxy"
"github.com/Wei-Shaw/sub2api/ent/redeemcode" "github.com/Wei-Shaw/sub2api/ent/redeemcode"
"github.com/Wei-Shaw/sub2api/ent/securitysecret"
"github.com/Wei-Shaw/sub2api/ent/setting" "github.com/Wei-Shaw/sub2api/ent/setting"
"github.com/Wei-Shaw/sub2api/ent/usagecleanuptask" "github.com/Wei-Shaw/sub2api/ent/usagecleanuptask"
"github.com/Wei-Shaw/sub2api/ent/usagelog" "github.com/Wei-Shaw/sub2api/ent/usagelog"
...@@ -102,6 +103,7 @@ func checkColumn(t, c string) error { ...@@ -102,6 +103,7 @@ func checkColumn(t, c string) error {
promocodeusage.Table: promocodeusage.ValidColumn, promocodeusage.Table: promocodeusage.ValidColumn,
proxy.Table: proxy.ValidColumn, proxy.Table: proxy.ValidColumn,
redeemcode.Table: redeemcode.ValidColumn, redeemcode.Table: redeemcode.ValidColumn,
securitysecret.Table: securitysecret.ValidColumn,
setting.Table: setting.ValidColumn, setting.Table: setting.ValidColumn,
usagecleanuptask.Table: usagecleanuptask.ValidColumn, usagecleanuptask.Table: usagecleanuptask.ValidColumn,
usagelog.Table: usagelog.ValidColumn, usagelog.Table: usagelog.ValidColumn,
......
...@@ -52,6 +52,14 @@ type Group struct { ...@@ -52,6 +52,14 @@ type Group struct {
ImagePrice2k *float64 `json:"image_price_2k,omitempty"` ImagePrice2k *float64 `json:"image_price_2k,omitempty"`
// ImagePrice4k holds the value of the "image_price_4k" field. // ImagePrice4k holds the value of the "image_price_4k" field.
ImagePrice4k *float64 `json:"image_price_4k,omitempty"` ImagePrice4k *float64 `json:"image_price_4k,omitempty"`
// SoraImagePrice360 holds the value of the "sora_image_price_360" field.
SoraImagePrice360 *float64 `json:"sora_image_price_360,omitempty"`
// SoraImagePrice540 holds the value of the "sora_image_price_540" field.
SoraImagePrice540 *float64 `json:"sora_image_price_540,omitempty"`
// SoraVideoPricePerRequest holds the value of the "sora_video_price_per_request" field.
SoraVideoPricePerRequest *float64 `json:"sora_video_price_per_request,omitempty"`
// SoraVideoPricePerRequestHd holds the value of the "sora_video_price_per_request_hd" field.
SoraVideoPricePerRequestHd *float64 `json:"sora_video_price_per_request_hd,omitempty"`
// 是否仅允许 Claude Code 客户端 // 是否仅允许 Claude Code 客户端
ClaudeCodeOnly bool `json:"claude_code_only,omitempty"` ClaudeCodeOnly bool `json:"claude_code_only,omitempty"`
// 非 Claude Code 请求降级使用的分组 ID // 非 Claude Code 请求降级使用的分组 ID
...@@ -178,7 +186,7 @@ func (*Group) scanValues(columns []string) ([]any, error) { ...@@ -178,7 +186,7 @@ func (*Group) scanValues(columns []string) ([]any, error) {
values[i] = new([]byte) values[i] = new([]byte)
case group.FieldIsExclusive, group.FieldClaudeCodeOnly, group.FieldModelRoutingEnabled, group.FieldMcpXMLInject: case group.FieldIsExclusive, group.FieldClaudeCodeOnly, group.FieldModelRoutingEnabled, group.FieldMcpXMLInject:
values[i] = new(sql.NullBool) values[i] = new(sql.NullBool)
case group.FieldRateMultiplier, group.FieldDailyLimitUsd, group.FieldWeeklyLimitUsd, group.FieldMonthlyLimitUsd, group.FieldImagePrice1k, group.FieldImagePrice2k, group.FieldImagePrice4k: case group.FieldRateMultiplier, group.FieldDailyLimitUsd, group.FieldWeeklyLimitUsd, group.FieldMonthlyLimitUsd, group.FieldImagePrice1k, group.FieldImagePrice2k, group.FieldImagePrice4k, group.FieldSoraImagePrice360, group.FieldSoraImagePrice540, group.FieldSoraVideoPricePerRequest, group.FieldSoraVideoPricePerRequestHd:
values[i] = new(sql.NullFloat64) values[i] = new(sql.NullFloat64)
case group.FieldID, group.FieldDefaultValidityDays, group.FieldFallbackGroupID, group.FieldFallbackGroupIDOnInvalidRequest, group.FieldSortOrder: case group.FieldID, group.FieldDefaultValidityDays, group.FieldFallbackGroupID, group.FieldFallbackGroupIDOnInvalidRequest, group.FieldSortOrder:
values[i] = new(sql.NullInt64) values[i] = new(sql.NullInt64)
...@@ -317,6 +325,34 @@ func (_m *Group) assignValues(columns []string, values []any) error { ...@@ -317,6 +325,34 @@ func (_m *Group) assignValues(columns []string, values []any) error {
_m.ImagePrice4k = new(float64) _m.ImagePrice4k = new(float64)
*_m.ImagePrice4k = value.Float64 *_m.ImagePrice4k = value.Float64
} }
case group.FieldSoraImagePrice360:
if value, ok := values[i].(*sql.NullFloat64); !ok {
return fmt.Errorf("unexpected type %T for field sora_image_price_360", values[i])
} else if value.Valid {
_m.SoraImagePrice360 = new(float64)
*_m.SoraImagePrice360 = value.Float64
}
case group.FieldSoraImagePrice540:
if value, ok := values[i].(*sql.NullFloat64); !ok {
return fmt.Errorf("unexpected type %T for field sora_image_price_540", values[i])
} else if value.Valid {
_m.SoraImagePrice540 = new(float64)
*_m.SoraImagePrice540 = value.Float64
}
case group.FieldSoraVideoPricePerRequest:
if value, ok := values[i].(*sql.NullFloat64); !ok {
return fmt.Errorf("unexpected type %T for field sora_video_price_per_request", values[i])
} else if value.Valid {
_m.SoraVideoPricePerRequest = new(float64)
*_m.SoraVideoPricePerRequest = value.Float64
}
case group.FieldSoraVideoPricePerRequestHd:
if value, ok := values[i].(*sql.NullFloat64); !ok {
return fmt.Errorf("unexpected type %T for field sora_video_price_per_request_hd", values[i])
} else if value.Valid {
_m.SoraVideoPricePerRequestHd = new(float64)
*_m.SoraVideoPricePerRequestHd = value.Float64
}
case group.FieldClaudeCodeOnly: case group.FieldClaudeCodeOnly:
if value, ok := values[i].(*sql.NullBool); !ok { if value, ok := values[i].(*sql.NullBool); !ok {
return fmt.Errorf("unexpected type %T for field claude_code_only", values[i]) return fmt.Errorf("unexpected type %T for field claude_code_only", values[i])
...@@ -514,6 +550,26 @@ func (_m *Group) String() string { ...@@ -514,6 +550,26 @@ func (_m *Group) String() string {
builder.WriteString(fmt.Sprintf("%v", *v)) builder.WriteString(fmt.Sprintf("%v", *v))
} }
builder.WriteString(", ") builder.WriteString(", ")
if v := _m.SoraImagePrice360; v != nil {
builder.WriteString("sora_image_price_360=")
builder.WriteString(fmt.Sprintf("%v", *v))
}
builder.WriteString(", ")
if v := _m.SoraImagePrice540; v != nil {
builder.WriteString("sora_image_price_540=")
builder.WriteString(fmt.Sprintf("%v", *v))
}
builder.WriteString(", ")
if v := _m.SoraVideoPricePerRequest; v != nil {
builder.WriteString("sora_video_price_per_request=")
builder.WriteString(fmt.Sprintf("%v", *v))
}
builder.WriteString(", ")
if v := _m.SoraVideoPricePerRequestHd; v != nil {
builder.WriteString("sora_video_price_per_request_hd=")
builder.WriteString(fmt.Sprintf("%v", *v))
}
builder.WriteString(", ")
builder.WriteString("claude_code_only=") builder.WriteString("claude_code_only=")
builder.WriteString(fmt.Sprintf("%v", _m.ClaudeCodeOnly)) builder.WriteString(fmt.Sprintf("%v", _m.ClaudeCodeOnly))
builder.WriteString(", ") builder.WriteString(", ")
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment