fix(kol): Phase 1 review — ACL lineage, expiry filters, tighter tenant scope

Addresses findings from consolidated Phase 1 code review:

- Add composite unique indexes + composite FKs so subscription_prompts and
  packages cannot reference mismatched (tenant_id, package_id, prompt_id)
  tuples, enforcing ACL lineage at the schema level.
- Add status CHECK constraints on all KOL tables as defense-in-depth.
- ListActiveSubscribersForPackage: drop meaningless tenant_id IS NOT NULL,
  add end_at expiry filter so fan-out skips expired subscribers.
- ApproveKolSubscription: add status='pending' guard to prevent double-approval.
- ListSubscriptionPromptsByTenant: join subscription/prompt/package/profile
  lifecycle so hidden/archived content no longer leaks; surface
  kol_display_name for UI.
- ListPublishedKolPackages: exclude expired subscribers from marketplace count.
- KolDashboardTrend: rewrite as CTE with day-series, returning both daily
  usage and new subscription counts.
- Remove cosmetic tenant_id IS NOT NULL from dashboard overview queries
  (the column is NOT NULL by schema).
- check_tenant_scope.sh: require tenant_id = sqlc.(n?)arg filter, reject
  tenant_id IS NOT NULL as a fake scope, maintain explicit exemption list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-17 08:34:12 +08:00
parent 3d4842e983
commit 70725193eb
12 changed files with 234 additions and 61 deletions
@@ -35,6 +35,8 @@ type KolSubscriptionPrompt struct {
PlatformHint *string
PackageName string
PublishedRevisionNo *int
// KolDisplayName is populated by ListSubscriptionPromptsByTenant (joins kol_profiles).
KolDisplayName string
// Fields below only populated by GetSubscriptionPromptByID.
SubscriptionStatus string
SubscriptionEndAt *time.Time
@@ -201,6 +203,7 @@ func (r *kolSubscriptionRepository) ListSubscriptionPromptsByTenant(ctx context.
PlatformHint: nullableText(row.PlatformHint),
PackageName: row.PackageName,
PublishedRevisionNo: nullableIntFromInt4(row.PublishedRevisionNo),
KolDisplayName: row.KolDisplayName,
}
}
return result, nil