| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- package repository
- import (
- "context"
- "encoding/json"
- "strings"
- "time"
- dbent "github.com/Wei-Shaw/sub2api/ent"
- "github.com/Wei-Shaw/sub2api/internal/service"
- "github.com/lib/pq"
- )
- const accountProtectionEnabledSQL = "COALESCE(extra -> 'anti_degradation' = 'true'::jsonb, extra #> '{anti_degrade,enabled}' = 'true'::jsonb, false)"
- // An atomic expression preserves the committed state even when partial updates
- // come from imports or asynchronous credential refreshes with stale snapshots.
- func preserveProtectionExtraSQL(ctx context.Context, expression string) string {
- if service.ProtectionManagedWrite(ctx) {
- return expression
- }
- base := "ARRAY['anti_degradation','protection_scope','anti_degrade']::text[]"
- mode1 := "ARRAY['anti_degradation','protection_scope','anti_degrade','codex_fingerprint_mode','enable_tls_fingerprint','tls_fingerprint_builtin','tls_fingerprint_profile_id','proxy_mode']::text[]"
- modes := "'" + strings.Join(service.RegisteredProtectionModes(), "','") + "'"
- keys := "(CASE WHEN (COALESCE(extra #>> '{anti_degrade,enabled}', 'true') <> 'false' AND (extra #> '{anti_degrade,policy_version}' IS NOT NULL OR extra #>> '{anti_degrade,mode}' IN (" + modes + "))) THEN " + mode1 + " ELSE " + base + " END)"
- return "((" + expression + ") - " + keys + ") || COALESCE((SELECT jsonb_object_agg(key,value) FROM jsonb_each(COALESCE(extra,'{}'::jsonb)) WHERE key = ANY(" + keys + ")), '{}'::jsonb)"
- }
- func protectedConcurrencySQL(expression string) string {
- return "CASE WHEN " + accountProtectionEnabledSQL + " AND " + expression + " <= 0 THEN 16 ELSE " + expression + " END"
- }
- func preserveLockedAccountProtection(ctx context.Context, client *dbent.Client, a *service.Account) error {
- // lockAndMergeAccountProbeExtra already holds FOR NO KEY UPDATE in this
- // transaction. Re-read just extra under that lock to prevent stale writes.
- expected, compareVersion := service.GetProtectionWriteExpectation(ctx)
- query := "SELECT extra FROM accounts WHERE id = $1 AND deleted_at IS NULL"
- if compareVersion {
- query = "SELECT extra, updated_at FROM accounts WHERE id = $1 AND deleted_at IS NULL"
- }
- rows, err := client.QueryContext(ctx, query, a.ID)
- if err != nil {
- return err
- }
- defer rows.Close()
- if !rows.Next() {
- if err := rows.Err(); err != nil {
- return err
- }
- return service.ErrAccountNotFound
- }
- var raw []byte
- var revision time.Time
- var scanErr error
- if compareVersion {
- scanErr = rows.Scan(&raw, &revision)
- } else {
- scanErr = rows.Scan(&raw)
- }
- if scanErr != nil {
- return scanErr
- }
- if compareVersion && (expected.AccountID != a.ID || !expected.UpdatedAt.Equal(revision)) {
- return service.ErrProtectionConflict
- }
- var extra map[string]any
- if len(raw) > 0 {
- if err := json.Unmarshal(raw, &extra); err != nil {
- return err
- }
- }
- current := &service.Account{Platform: a.Platform, Type: a.Type, Extra: extra}
- if !service.ProtectionManagedWrite(ctx) && service.ProtectedProxyModeConflict(current, a.Extra) {
- return service.ErrProtectedProxyModeChange
- }
- a.Extra = service.PreserveAccountProtection(ctx, current, a.Extra)
- service.BoundAccountProtectionConcurrency(a)
- if err := service.ValidateAccountProtectionConfiguration(a); err != nil {
- return err
- }
- return rows.Err()
- }
- func validateLockedBulkProxyMode(ctx context.Context, exec sqlExecutor, ids []int64, incoming map[string]any) error {
- rows, err := exec.QueryContext(ctx, `SELECT platform,type,extra FROM accounts WHERE id=ANY($1) AND deleted_at IS NULL ORDER BY id FOR NO KEY UPDATE`, pq.Array(ids))
- if err != nil {
- return err
- }
- defer rows.Close()
- for rows.Next() {
- var a service.Account
- var raw []byte
- if err := rows.Scan(&a.Platform, &a.Type, &raw); err != nil {
- return err
- }
- if err := json.Unmarshal(raw, &a.Extra); err != nil {
- return err
- }
- if service.ProtectedProxyModeConflict(&a, incoming) {
- return service.ErrProtectedProxyModeChange
- }
- }
- return rows.Err()
- }
|