diff --git a/internal/account/store.go b/internal/account/store.go
index e9215ec..947864a 100644
--- a/internal/account/store.go
+++ b/internal/account/store.go
@@ -45,7 +45,6 @@ type Account struct {
Cookies string `json:"-"`
CredentialReference CredentialReference `json:"-"`
CredentialKey string `json:"-"`
- AuthorizationStatus string `json:"authorization_status"`
RuntimeStatus string `json:"runtime_status"`
Version int64 `json:"version"`
}
@@ -154,8 +153,8 @@ func (s *Store) CreateAccount(ctx context.Context, account Account, credentials
defer tx.Rollback()
if _, err := tx.ExecContext(ctx, `
INSERT INTO social_account
- (account_id, credential_provider, credential_key, name, platform, platform_account_key, tags, authorization_kind, authorization_status, status)
- VALUES ($1, $2, $3, $4, $5, $6, $7, 'owned', 'authorized', 'paused')`, account.ID,
+ (account_id, credential_provider, credential_key, name, platform, platform_account_key, tags, status)
+ VALUES ($1, $2, $3, $4, $5, $6, $7, 'paused')`, account.ID,
account.CredentialReference.Provider, account.CredentialKey,
account.Name, account.Platform, account.PlatformAccountKey, account.Tags); err != nil {
return publicDatabaseError(err)
@@ -189,7 +188,7 @@ func commitKnownRolledBack(err error) bool {
func (s *Store) ListAccounts(ctx context.Context) ([]Account, error) {
rows, err := s.db.QueryContext(ctx, `
SELECT account.account_id, account.name, account.platform, account.platform_account_key, account.tags,
- account.authorization_status, account.status, account.version
+ account.status, account.version
FROM social_account account
ORDER BY account.created_at, account.account_id`)
if err != nil {
@@ -213,7 +212,7 @@ func (s *Store) GetAccount(ctx context.Context, id string) (Account, error) {
}
return scanAccount(s.db.QueryRowContext(ctx, `
SELECT account.account_id, account.name, account.platform, account.platform_account_key, account.tags,
- account.authorization_status, account.status, account.version
+ account.status, account.version
FROM social_account account
WHERE account.account_id = $1`, id))
}
@@ -246,7 +245,7 @@ func scanAccount(row accountScanner) (Account, error) {
var account Account
var tags pgtype.FlatArray[string]
if err := row.Scan(&account.ID, &account.Name, &account.Platform, &account.PlatformAccountKey, pgtype.NewMap().SQLScanner(&tags),
- &account.AuthorizationStatus, &account.RuntimeStatus, &account.Version); err != nil {
+ &account.RuntimeStatus, &account.Version); err != nil {
return Account{}, rowError(err)
}
account.Tags = []string(tags)
@@ -282,14 +281,6 @@ func validAccount(account Account) bool {
}
func (s *Store) PauseAccount(ctx context.Context, accountID string) error {
- return s.disableAccount(ctx, accountID, false)
-}
-
-func (s *Store) RevokeAccount(ctx context.Context, accountID string) error {
- return s.disableAccount(ctx, accountID, true)
-}
-
-func (s *Store) disableAccount(ctx context.Context, accountID string, revoke bool) error {
if !idPattern.MatchString(accountID) {
return ErrInvalid
}
@@ -299,32 +290,21 @@ func (s *Store) disableAccount(ctx context.Context, accountID string, revoke boo
}
defer tx.Rollback()
var version int64
- var authorizationStatus, runtimeStatus string
- if err := tx.QueryRowContext(ctx, `SELECT version, authorization_status, status FROM social_account WHERE account_id = $1 FOR UPDATE`, accountID).
- Scan(&version, &authorizationStatus, &runtimeStatus); err != nil {
+ var runtimeStatus string
+ if err := tx.QueryRowContext(ctx, `SELECT version, status FROM social_account WHERE account_id = $1 FOR UPDATE`, accountID).
+ Scan(&version, &runtimeStatus); err != nil {
return rowError(err)
}
- unchanged := (revoke && authorizationStatus == "revoked") || (!revoke && runtimeStatus == "paused")
+ unchanged := runtimeStatus == "paused"
if !unchanged {
if err := tx.QueryRowContext(ctx, `
UPDATE social_account
- SET authorization_status = CASE WHEN $2 THEN 'revoked' ELSE authorization_status END,
- status = 'paused', paused_at = now(), revoked_at = CASE WHEN $2 THEN now() ELSE revoked_at END,
+ SET status = 'paused', paused_at = now(),
version = version + 1, updated_at = now()
- WHERE account_id = $1 RETURNING version`, accountID, revoke).Scan(&version); err != nil {
+ WHERE account_id = $1 RETURNING version`, accountID).Scan(&version); err != nil {
return errors.New("change account state")
}
- }
- reason := "account_paused"
- if revoke {
- reason = "account_revoked"
- }
- if !unchanged {
- eventType := "account_paused"
- if revoke {
- eventType = "account_revoked"
- }
- if err := appendAudit(ctx, tx, eventType, reason, accountID, map[string]any{
+ if err := appendAudit(ctx, tx, "account_paused", "account_paused", accountID, map[string]any{
"account_version": version,
}); err != nil {
return err
@@ -342,14 +322,11 @@ func (s *Store) ResumeAccount(ctx context.Context, accountID string) error {
return errors.New("begin resume transaction")
}
defer tx.Rollback()
- var authorizationStatus, runtimeStatus string
- if err := tx.QueryRowContext(ctx, `SELECT authorization_status, status FROM social_account WHERE account_id = $1 FOR UPDATE`, accountID).
- Scan(&authorizationStatus, &runtimeStatus); err != nil {
+ var runtimeStatus string
+ if err := tx.QueryRowContext(ctx, `SELECT status FROM social_account WHERE account_id = $1 FOR UPDATE`, accountID).
+ Scan(&runtimeStatus); err != nil {
return rowError(err)
}
- if authorizationStatus == "revoked" {
- return ErrConflict
- }
var ready bool
if err := tx.QueryRowContext(ctx, `
SELECT EXISTS (
diff --git a/internal/account/store_test.go b/internal/account/store_test.go
index aa9a8fe..a3ca02c 100644
--- a/internal/account/store_test.go
+++ b/internal/account/store_test.go
@@ -263,7 +263,7 @@ func TestAccountStoreLifecycleAgainstPostgres(t *testing.T) {
t.Fatalf("list accounts: %d err=%v", len(accounts), err)
}
account, err := store.GetAccount(ctx, "account-lifecycle-a")
- if err != nil || account.RuntimeStatus != "paused" || account.AuthorizationStatus != "authorized" || len(account.Tags) != 1 {
+ if err != nil || account.RuntimeStatus != "paused" || len(account.Tags) != 1 {
t.Fatalf("get account: %#v err=%v", account, err)
}
if _, err := store.GetAccount(ctx, "account-invalid"); !errors.Is(err, ErrInvalid) && !errors.Is(err, ErrNotFound) {
@@ -312,15 +312,6 @@ func TestAccountStoreLifecycleAgainstPostgres(t *testing.T) {
if account, err := store.GetAccount(ctx, "account-lifecycle-a"); err != nil || account.RuntimeStatus != "paused" {
t.Fatalf("paused account: %#v err=%v", account, err)
}
- if err := store.RevokeAccount(ctx, "account-lifecycle-b"); err != nil {
- t.Fatalf("revoke: %v", err)
- }
- if err := store.ResumeAccount(ctx, "account-lifecycle-b"); !errors.Is(err, ErrConflict) {
- t.Fatalf("resume revoked account must conflict: %v", err)
- }
- if err := store.RevokeAccount(ctx, "account-lifecycle-b"); err != nil {
- t.Fatalf("revoke must stay idempotent: %v", err)
- }
// 审计:分页 + 过滤 + 非法过滤参数
page, err := store.ListAudit(ctx, AuditFilter{AccountID: "account-lifecycle-a", Page: 1, PageSize: 10})
diff --git a/internal/controlplane/api/account_environment_unit_test.go b/internal/controlplane/api/account_environment_unit_test.go
index c7b5483..5a88021 100644
--- a/internal/controlplane/api/account_environment_unit_test.go
+++ b/internal/controlplane/api/account_environment_unit_test.go
@@ -110,18 +110,6 @@ func TestAccountEnvironmentAutoBindingAndStart(t *testing.T) {
t.Fatalf("start audit pair missing: rows=%d err=%v", startAudits, err)
}
- // start 冲突分支:吊销账号后 start → 409 readiness blocked
- if response := do(app, http.MethodPost, "/api/phase-a/accounts/"+accountID+"/revoke", ""); response.Code != http.StatusNoContent {
- t.Fatalf("revoke failed: %d: %s", response.Code, response.Body.String())
- }
- response = do(app, http.MethodPost, "/api/phase-a/accounts/"+accountID+"/start", "")
- if response.Code != http.StatusConflict {
- t.Fatalf("expected 409 start on revoked account, got %d: %s", response.Code, response.Body.String())
- }
- var conflict map[string]string
- if err := json.Unmarshal(response.Body.Bytes(), &conflict); err != nil || conflict["reason_code"] != "account_revoked" || conflict["readiness"] != "blocked" {
- t.Fatalf("start conflict payload: %s err=%v", response.Body.String(), err)
- }
}
func TestAccountEnvironmentDeletionLifecycle(t *testing.T) {
@@ -234,8 +222,8 @@ func TestAccountAuditEndpointFilters(t *testing.T) {
t.Cleanup(func() { _ = auditDB.Close() })
if _, err := auditDB.ExecContext(ctx, `
INSERT INTO gateway (name, endpoint, token) VALUES ('gw-1', 'http://gw-1:8081', 'unit-test-gateway-token');
- INSERT INTO social_account (account_id, credential_provider, credential_key, name, platform, platform_account_key, authorization_kind, authorization_status, status)
- VALUES ('account-audit-0000000000000000', 'os_keyring', 'creatorhub/account-audit-0000000000000000', '审计账号', 'douyin', 'key-audit', 'owned', 'authorized', 'paused');
+ INSERT INTO social_account (account_id, credential_provider, credential_key, name, platform, platform_account_key, status)
+ VALUES ('account-audit-0000000000000000', 'os_keyring', 'creatorhub/account-audit-0000000000000000', '审计账号', 'douyin', 'key-audit', 'paused');
INSERT INTO audit_event (event_type, account_id, actor, reason_code, details)
SELECT 'account_created', account.id, 'local-user', 'account_created', '{"platform":"douyin"}'
FROM social_account account WHERE account.account_id = 'account-audit-0000000000000000'`); err != nil {
diff --git a/internal/controlplane/api/accounts_operations.go b/internal/controlplane/api/accounts_operations.go
index 1775b2b..28c3129 100644
--- a/internal/controlplane/api/accounts_operations.go
+++ b/internal/controlplane/api/accounts_operations.go
@@ -49,7 +49,7 @@ func RegisterAccountRoutes(app *fiber.App, store *accountdomain.Store, runtimeSt
PlatformAccountKey: strings.TrimSpace(input.PlatformAccountKey), Tags: tags, Cookies: strings.TrimSpace(input.Cookies),
CredentialReference: accountdomain.CredentialReference{ID: accountID + "-cookies", Provider: "os_keyring"},
CredentialKey: "creatorhub/" + accountID + "/cookies",
- AuthorizationStatus: "authorized", RuntimeStatus: "paused", Version: 1,
+ RuntimeStatus: "paused", Version: 1,
}
if err := store.CreateAccount(c.Context(), account, credentials); err != nil {
if errors.Is(err, accountdomain.ErrAccountCreationUnknown) {
@@ -165,23 +165,6 @@ func RegisterAccountRoutes(app *fiber.App, store *accountdomain.Store, runtimeSt
return c.SendStatus(fiber.StatusNoContent)
})
- app.Post("/api/phase-a/accounts/:id/revoke", func(c fiber.Ctx) error {
- unlock, err := lockAccountResources(c.Context(), runtimeStore, c.Params("id"))
- if err != nil {
- return hubError(c, err)
- }
- defer unlock()
- if err := store.RevokeAccount(c.Context(), c.Params("id")); err != nil {
- return phaseAError(c, err)
- }
- if runtimeStore != nil {
- if err := stopAccountRuntime(c.Context(), runtimeStore, c.Params("id")); err != nil {
- return hubError(c, err)
- }
- }
- return c.SendStatus(fiber.StatusNoContent)
- })
-
app.Get("/api/phase-a/audit", func(c fiber.Ctx) error {
filter, err := auditFilter(c)
if err != nil {
@@ -234,8 +217,6 @@ func auditFilter(c fiber.Ctx) (accountdomain.AuditFilter, error) {
func resumeBlockReason(account accountdomain.Account, environment hub.EnvironmentContext, bindingFound bool) string {
switch {
- case account.AuthorizationStatus != "authorized":
- return "account_revoked"
case !bindingFound:
return "binding_missing"
case environment.Exit.ID != "" && environment.Exit.HealthStatus != "healthy":
@@ -328,8 +309,8 @@ func accountDetailPayload(store *accountdomain.Store, runtimeStore HubStore) fib
payload := map[string]any{
"id": account.ID, "name": account.Name, "platform": account.Platform,
"platform_account_key": account.PlatformAccountKey, "tags": account.Tags,
- "authorization_status": account.AuthorizationStatus, "runtime_status": account.RuntimeStatus,
- "version": account.Version,
+ "runtime_status": account.RuntimeStatus,
+ "version": account.Version,
}
if runtimeStore != nil {
if environment, envErr := runtimeStore.GetEnvironmentContextForAccount(c.Context(), account.ID); envErr == nil {
diff --git a/internal/controlplane/api/creator.go b/internal/controlplane/api/creator.go
index 6a52203..0d74231 100644
--- a/internal/controlplane/api/creator.go
+++ b/internal/controlplane/api/creator.go
@@ -735,8 +735,7 @@ func creatorLoginQRCode(ctx context.Context, store *creator.Store, phaseAStore *
return nil, err
}
if account.Platform != creator.PlatformDouyin || profile.Platform != creator.PlatformDouyin ||
- account.AuthorizationStatus != "authorized" || profile.PlatformAccountKey == "" ||
- account.PlatformAccountKey != profile.PlatformAccountKey {
+ profile.PlatformAccountKey == "" || account.PlatformAccountKey != profile.PlatformAccountKey {
return nil, creator.ErrConflict
}
unlock, lockErr := lockAccountResources(ctx, hubStore, accountID)
@@ -784,7 +783,7 @@ func verifyCreatorAccount(ctx context.Context, store *creator.Store, phaseAStore
if err != nil {
return creator.LoginResult{}, err
}
- if account.Platform != profile.Platform || account.Platform != creator.PlatformDouyin || account.AuthorizationStatus != "authorized" || profile.PlatformAccountKey == "" || account.PlatformAccountKey != profile.PlatformAccountKey {
+ if account.Platform != profile.Platform || account.Platform != creator.PlatformDouyin || profile.PlatformAccountKey == "" || account.PlatformAccountKey != profile.PlatformAccountKey {
return creator.LoginResult{}, creator.ErrConflict
}
environment, err := hubStore.GetEnvironmentContextForAccount(ctx, accountID)
@@ -1221,7 +1220,7 @@ func newDouyinAccountBrowser(ctx context.Context, store *creator.Store, phaseASt
if err != nil {
return creatorGatewayBrowser{}, err
}
- if account.Platform != creator.PlatformDouyin || account.AuthorizationStatus != "authorized" {
+ if account.Platform != creator.PlatformDouyin {
return creatorGatewayBrowser{}, creator.ErrConflict
}
profile, err := store.GetAccountProfile(ctx, accountID)
@@ -1536,7 +1535,7 @@ func refreshCreatorMetricWork(ctx context.Context, store *creator.Store, phaseAS
if err != nil {
return err
}
- if account.Platform != creator.PlatformDouyin || account.AuthorizationStatus != "authorized" || (profile.BusinessStatus != "normal" && profile.BusinessStatus != "muted") || profile.LoginStatus != "logged_in" {
+ if account.Platform != creator.PlatformDouyin || (profile.BusinessStatus != "normal" && profile.BusinessStatus != "muted") || profile.LoginStatus != "logged_in" {
return creator.ErrConflict
}
environment, err := hubStore.GetEnvironmentContextForAccount(ctx, accountID)
@@ -1600,7 +1599,7 @@ func syncCreatorOwned(ctx context.Context, store *creator.Store, phaseAStore *ac
if err != nil {
return err
}
- if account.Platform != creator.PlatformDouyin || account.AuthorizationStatus != "authorized" {
+ if account.Platform != creator.PlatformDouyin {
return creator.ErrConflict
}
profile, err := store.GetAccountProfile(ctx, accountID)
@@ -1676,7 +1675,7 @@ func creatorCollectionAccount(ctx context.Context, store *creator.Store, phaseAS
return "", err
}
for _, account := range accounts {
- if account.Platform != platform || account.AuthorizationStatus != "authorized" {
+ if account.Platform != platform {
continue
}
profile, err := store.GetAccountProfile(ctx, account.ID)
diff --git a/internal/controlplane/api/douyin_test.go b/internal/controlplane/api/douyin_test.go
index 003d121..805aee3 100644
--- a/internal/controlplane/api/douyin_test.go
+++ b/internal/controlplane/api/douyin_test.go
@@ -108,7 +108,7 @@ func TestDouyinGatewayBrowserDoesNotEchoCredentialOnFailure(t *testing.T) {
func readyDouyinEnvironment() hub.EnvironmentContext {
return hub.EnvironmentContext{Env: hub.Env{Alias: "account-a", Gateway: "gateway-a"}, AccountID: "account-a",
- AccountStatus: "active", AuthorizationStatus: "authorized", BindingVersion: 2,
+ AccountStatus: "active", BindingVersion: 2,
Exit: hub.NetworkExit{ID: "exit-a", HealthStatus: "healthy"},
RuntimeID: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", RuntimeNetworkID: "native-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
}
diff --git a/internal/controlplane/api/environments.go b/internal/controlplane/api/environments.go
index 1ba5fe6..eb6b7cb 100644
--- a/internal/controlplane/api/environments.go
+++ b/internal/controlplane/api/environments.go
@@ -333,7 +333,6 @@ type envView struct {
Endpoint string `json:"endpoint"`
AccountID string `json:"account_id"`
AccountStatus string `json:"account_status"`
- AuthorizationStatus string `json:"authorization_status"`
NetworkExitID string `json:"network_exit_id"`
NetworkExitHealth string `json:"network_exit_health"`
BindingVersion int64 `json:"binding_version"`
@@ -350,8 +349,6 @@ type envView struct {
func environmentScheduleReadiness(environment hub.EnvironmentContext) (string, string) {
switch {
- case environment.AuthorizationStatus != "authorized":
- return "blocked", "account_revoked"
case environment.AccountStatus != "active":
return "blocked", "account_paused"
case environment.Exit.ID != "" && environment.Exit.HealthStatus != "healthy":
@@ -392,7 +389,7 @@ func validCreatedRuntime(created runtimeStatus, environment hub.EnvironmentConte
}
func accountRunnable(environment hub.EnvironmentContext) bool {
- return environment.AccountStatus == "active" && environment.AuthorizationStatus == "authorized"
+ return environment.AccountStatus == "active"
}
func releaseRuntime(ctx context.Context, store RuntimeStopStore, environment hub.EnvironmentContext) error {
@@ -525,7 +522,7 @@ func getBrowser(store HubStore) fiber.Handler {
view := envView{
Env: environment.Env, State: "recorded", Status: "已记录运行实例",
AccountID: environment.AccountID, AccountStatus: environment.AccountStatus,
- AuthorizationStatus: environment.AuthorizationStatus, NetworkExitID: environment.Exit.ID,
+ NetworkExitID: environment.Exit.ID,
NetworkExitHealth: environment.Exit.HealthStatus, BindingVersion: environment.BindingVersion,
RuntimeID: environment.RuntimeID, RuntimeNodeID: environment.RuntimeNodeID,
ScheduleStatus: "blocked", ScheduleBlockReason: "binding_missing",
@@ -885,7 +882,7 @@ func listBrowsers(store HubStore) fiber.Handler {
RecoveryRequired: true, GatewayReachable: false,
}
if environment, contextErr := store.GetEnvironmentContext(c.Context(), env.Alias); contextErr == nil {
- view.AccountID, view.AccountStatus, view.AuthorizationStatus = environment.AccountID, environment.AccountStatus, environment.AuthorizationStatus
+ view.AccountID, view.AccountStatus = environment.AccountID, environment.AccountStatus
view.NetworkExitID, view.NetworkExitHealth, view.BindingVersion = environment.Exit.ID, environment.Exit.HealthStatus, environment.BindingVersion
view.RuntimeID = environment.RuntimeID
view.RuntimeID = environment.RuntimeID
diff --git a/internal/controlplane/api/gateway_browser_unit_test.go b/internal/controlplane/api/gateway_browser_unit_test.go
index 204e850..1550cbf 100644
--- a/internal/controlplane/api/gateway_browser_unit_test.go
+++ b/internal/controlplane/api/gateway_browser_unit_test.go
@@ -14,14 +14,13 @@ import (
func testRunnableEnvironment() hub.EnvironmentContext {
return hub.EnvironmentContext{
- Env: hub.Env{Alias: "account-1"},
- AccountID: "account-1",
- AccountStatus: "active",
- AuthorizationStatus: "authorized",
- RuntimeID: "runtime-1",
- RuntimeNetworkID: "network-1",
- Exit: hub.NetworkExit{ID: "exit-1", HealthStatus: "healthy"},
- BindingVersion: 1,
+ Env: hub.Env{Alias: "account-1"},
+ AccountID: "account-1",
+ AccountStatus: "active",
+ RuntimeID: "runtime-1",
+ RuntimeNetworkID: "network-1",
+ Exit: hub.NetworkExit{ID: "exit-1", HealthStatus: "healthy"},
+ BindingVersion: 1,
}
}
diff --git a/internal/controlplane/api/hub_test.go b/internal/controlplane/api/hub_test.go
index 6c0950a..cec27e6 100644
--- a/internal/controlplane/api/hub_test.go
+++ b/internal/controlplane/api/hub_test.go
@@ -67,7 +67,7 @@ func newMemoryStore() *memoryStore {
}
func TestResumeBlockReasonIsStable(t *testing.T) {
- account := accountdomain.Account{AuthorizationStatus: "authorized"}
+ account := accountdomain.Account{}
healthy := hub.EnvironmentContext{Exit: hub.NetworkExit{ID: "exit-a", HealthStatus: "healthy"}}
for name, test := range map[string]struct {
account accountdomain.Account
@@ -75,7 +75,6 @@ func TestResumeBlockReasonIsStable(t *testing.T) {
found bool
want string
}{
- "revoked": {accountdomain.Account{AuthorizationStatus: "revoked"}, healthy, true, "account_revoked"},
"missing binding": {account, hub.EnvironmentContext{}, false, "binding_missing"},
"direct exit": {account, hub.EnvironmentContext{}, true, "account_conflict"},
"unhealthy exit": {account, hub.EnvironmentContext{Exit: hub.NetworkExit{ID: "exit-a", HealthStatus: "unhealthy"}}, true, "network_exit_unhealthy"},
@@ -281,7 +280,7 @@ func (s *memoryStore) CreateBoundEnv(ctx context.Context, env hub.Env, accountID
s.mu.Lock()
if existing, ok := s.bindings[env.Alias]; ok {
if existing.AccountStatus == "" {
- existing.AccountStatus, existing.AuthorizationStatus = "active", "authorized"
+ existing.AccountStatus = "active"
}
s.mu.Unlock()
return existing, false, nil
@@ -292,7 +291,7 @@ func (s *memoryStore) CreateBoundEnv(ctx context.Context, env hub.Env, accountID
}
s.mu.Lock()
defer s.mu.Unlock()
- bound := hub.EnvironmentContext{Env: env, AccountID: accountID, AccountStatus: "active", AuthorizationStatus: "authorized",
+ bound := hub.EnvironmentContext{Env: env, AccountID: accountID, AccountStatus: "active",
BindingVersion: 1, Exit: s.exits[exitID]}
s.bindings[env.Alias] = bound
return bound, true, nil
@@ -302,7 +301,7 @@ func (s *memoryStore) GetEnvironmentContext(_ context.Context, alias string) (hu
defer s.mu.Unlock()
if bound, ok := s.bindings[alias]; ok {
if bound.AccountStatus == "" {
- bound.AccountStatus, bound.AuthorizationStatus = "active", "authorized"
+ bound.AccountStatus = "active"
}
return bound, nil
}
@@ -310,7 +309,7 @@ func (s *memoryStore) GetEnvironmentContext(_ context.Context, alias string) (hu
if !ok {
return hub.EnvironmentContext{}, hub.ErrNotFound
}
- return hub.EnvironmentContext{Env: env, AccountID: alias, AccountStatus: "active", AuthorizationStatus: "authorized",
+ return hub.EnvironmentContext{Env: env, AccountID: alias, AccountStatus: "active",
BindingVersion: 1, Exit: s.exits["exit-1"]}, nil
}
func (s *memoryStore) GetEnvironmentContextForAccount(ctx context.Context, accountID string) (hub.EnvironmentContext, error) {
@@ -368,9 +367,9 @@ func (s *memoryStore) ActivateRuntime(_ context.Context, alias, runtimeID string
bound = hub.EnvironmentContext{Env: s.envs[alias], AccountID: alias, BindingVersion: 1, Exit: s.exits["exit-1"]}
}
if bound.AccountStatus == "" {
- bound.AccountStatus, bound.AuthorizationStatus = "active", "authorized"
+ bound.AccountStatus = "active"
}
- if bound.AccountStatus != "active" || bound.AuthorizationStatus != "authorized" ||
+ if bound.AccountStatus != "active" ||
bound.BindingVersion != bindingVersion || bound.Exit.ID != exitID || bound.RuntimeCleanupPending {
return hub.EnvironmentContext{}, hub.ErrConflict
}
@@ -1031,8 +1030,7 @@ func TestCreateBrowserDeterministicRejectionDoesNotWedgeBinding(t *testing.T) {
func TestReconcileGatewayCreateDoesNotReuseOldNetworkGeneration(t *testing.T) {
store := newMemoryStore()
store.envs["account-a"] = hub.Env{Alias: "account-a", Gateway: "gw-1"}
- environment := hub.EnvironmentContext{Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "active",
- AuthorizationStatus: "authorized", BindingVersion: 1, Exit: store.exits["exit-1"],
+ environment := hub.EnvironmentContext{Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "active", BindingVersion: 1, Exit: store.exits["exit-1"],
RuntimeID: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", RuntimeNetworkID: "native-99999999999999999999999999999999"}
store.bindings[environment.Alias] = environment
gateway := &fakeGateway{token: "unit-test-gateway-token", runtimes: []runtimeStatus{{
@@ -1106,14 +1104,13 @@ func TestActivationConflictKeepsTheGenerationWonByTheHeartbeat(t *testing.T) {
candidateID := "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
candidateNetwork := "native-88888888888888888888888888888888"
current := hub.EnvironmentContext{
- Env: store.envs["account-a"],
- AccountID: "account-a",
- AccountStatus: "active",
- AuthorizationStatus: "authorized",
- BindingVersion: 1,
- RuntimeID: candidateID,
- RuntimeNetworkID: candidateNetwork,
- Exit: store.exits["exit-1"],
+ Env: store.envs["account-a"],
+ AccountID: "account-a",
+ AccountStatus: "active",
+ BindingVersion: 1,
+ RuntimeID: candidateID,
+ RuntimeNetworkID: candidateNetwork,
+ Exit: store.exits["exit-1"],
}
store.bindings["account-a"] = current
conflict := &activationConflictStore{memoryStore: store, current: current}
@@ -1130,8 +1127,7 @@ func TestActivationConflictKeepsTheGenerationWonByTheHeartbeat(t *testing.T) {
func TestLegacyActiveRuntimeWithoutNetworkGenerationDoesNotTouchSuccessor(t *testing.T) {
store := newMemoryStore()
store.envs["account-a"] = hub.Env{Alias: "account-a", Gateway: "gw-1"}
- environment := hub.EnvironmentContext{Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "active",
- AuthorizationStatus: "authorized", BindingVersion: 1, Exit: store.exits["exit-1"],
+ environment := hub.EnvironmentContext{Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "active", BindingVersion: 1, Exit: store.exits["exit-1"],
RuntimeID: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}
store.bindings[environment.Alias] = environment
gateway := &fakeGateway{token: "unit-test-gateway-token", runtimes: []runtimeStatus{{
@@ -1567,8 +1563,8 @@ func TestPauseAndRevokeStopContainerWithoutRuntimeLease(t *testing.T) {
fixture.gateway.mu.Lock()
fixture.gateway.runtimes[0].State = "running"
fixture.gateway.mu.Unlock()
- if response := do(app, http.MethodPost, "/api/phase-a/accounts/account-a/revoke", ""); response.Code != http.StatusNoContent {
- t.Fatalf("revoke running orphan without lease: %d %s", response.Code, response.Body.String())
+ if response := do(app, http.MethodPost, "/api/phase-a/accounts/account-a/pause", ""); response.Code != http.StatusNoContent {
+ t.Fatalf("pause running orphan without lease: %d %s", response.Code, response.Body.String())
}
after, err := fixture.store.GetEnvironmentContext(ctx, fixture.bound.Alias)
@@ -1576,8 +1572,8 @@ func TestPauseAndRevokeStopContainerWithoutRuntimeLease(t *testing.T) {
t.Fatalf("lease-free stop did not converge: %#v err=%v", after, err)
}
account, err := accountStore.GetAccount(ctx, fixture.bound.AccountID)
- if err != nil || account.AuthorizationStatus != "revoked" || account.RuntimeStatus != "paused" {
- t.Fatalf("revoke gate did not remain closed: %#v err=%v", account, err)
+ if err != nil || account.RuntimeStatus != "paused" {
+ t.Fatalf("pause gate did not apply: %#v err=%v", account, err)
}
fixture.gateway.mu.Lock()
container := fixture.gateway.runtimes[0]
@@ -1596,7 +1592,7 @@ func TestPauseAndRevokeStopContainerWithoutRuntimeLease(t *testing.T) {
}
}
if stopCalls != 2 {
- t.Fatalf("pause/revoke did not reconcile both lease-free runtimes: %#v", requests)
+ t.Fatalf("pause did not reconcile both lease-free runtimes: %#v", requests)
}
}
@@ -1874,8 +1870,7 @@ func TestRemoveGatewayRuntimePreservesKnownNetworkGeneration(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
store := newMemoryStore()
store.envs["account-a"] = hub.Env{Alias: "account-a", Gateway: "gw-1"}
- environment := hub.EnvironmentContext{Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "active",
- AuthorizationStatus: "authorized", BindingVersion: 1, Exit: store.exits["exit-1"],
+ environment := hub.EnvironmentContext{Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "active", BindingVersion: 1, Exit: store.exits["exit-1"],
RuntimeID: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", RuntimeNetworkID: "native-99999999999999999999999999999999"}
store.bindings["account-a"] = environment
gateway := &fakeGateway{token: "unit-test-gateway-token", runtimes: []runtimeStatus{{ID: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", Alias: "account-a",
@@ -2670,70 +2665,68 @@ func TestPostgresNonRunnableReconcileAuditsRuntimeRelease(t *testing.T) {
t.Skip("set CREATORHUB_POSTGRES_TEST_URL to run PostgreSQL integration coverage")
}
ctx := context.Background()
- for _, authorization := range []string{"authorized", "revoked"} {
- for _, observation := range []string{"stopped", "missing"} {
- for _, releaseFailure := range []bool{false, true} {
- name := authorization + "/" + observation + "/success"
- if releaseFailure {
- name = authorization + "/" + observation + "/release failure"
+ for _, observation := range []string{"stopped", "missing"} {
+ for _, releaseFailure := range []bool{false, true} {
+ name := observation + "/success"
+ if releaseFailure {
+ name = observation + "/release failure"
+ }
+ t.Run(name, func(t *testing.T) {
+ fixture := newPostgresRebindFixture(t, databaseURL)
+ setFixtureAccountStatus(t, fixture.databaseURL, "active")
+ var err error
+ fixture.bound, err = fixture.store.ActivateRuntime(ctx, "account-a", "stopped-container",
+ fixture.bound.BindingVersion, fixture.bound.Exit.ID, "stopped-network")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := fixture.db.ExecContext(ctx, `
+ UPDATE social_account SET status = 'paused' WHERE account_id = 'account-a'`); err != nil {
+ t.Fatal(err)
+ }
+ if observation == "stopped" {
+ fixture.gateway.runtimes = []runtimeStatus{{
+ ID: "stopped-container", Alias: "account-a", State: "exited",
+ BindingVersion: fixture.bound.BindingVersion, NetworkExitID: fixture.bound.Exit.ID,
+ }}
}
- t.Run(name, func(t *testing.T) {
- fixture := newPostgresRebindFixture(t, databaseURL)
- setFixtureAccountStatus(t, fixture.databaseURL, "active")
- var err error
- fixture.bound, err = fixture.store.ActivateRuntime(ctx, "account-a", "stopped-container",
- fixture.bound.BindingVersion, fixture.bound.Exit.ID, "stopped-network")
- if err != nil {
- t.Fatal(err)
- }
- if _, err := fixture.db.ExecContext(ctx, `
- UPDATE social_account SET status = 'paused', authorization_status = $1 WHERE account_id = 'account-a'`, authorization); err != nil {
- t.Fatal(err)
- }
- if observation == "stopped" {
- fixture.gateway.runtimes = []runtimeStatus{{
- ID: "stopped-container", Alias: "account-a", State: "exited",
- BindingVersion: fixture.bound.BindingVersion, NetworkExitID: fixture.bound.Exit.ID,
- }}
- }
- var store HubStore = fixture.store
- if releaseFailure {
- store = failingRuntimeReleaseStore{Store: fixture.store, err: errors.New("release unavailable")}
- }
- err = reconcileRuntimeLeases(ctx, store, fakeExitProbe{}, func(hub.NetworkExitAccess) (string, error) { return "", nil })
- wantOutcome, wantReason := "succeeded", "runtime_released"
- if releaseFailure {
- wantOutcome, wantReason = "failed", "runtime_release_failed"
- }
- if (err != nil) != releaseFailure {
- t.Fatalf("reconcile error=%v, releaseFailure=%v", err, releaseFailure)
- }
- after, err := fixture.store.GetEnvironmentContext(ctx, "account-a")
- if err != nil || (after.RuntimeID != "") == !releaseFailure {
- t.Fatalf("runtime lease after reconcile: %#v err=%v", after, err)
- }
+ var store HubStore = fixture.store
+ if releaseFailure {
+ store = failingRuntimeReleaseStore{Store: fixture.store, err: errors.New("release unavailable")}
+ }
+ err = reconcileRuntimeLeases(ctx, store, fakeExitProbe{}, func(hub.NetworkExitAccess) (string, error) { return "", nil })
+ wantOutcome, wantReason := "succeeded", "runtime_released"
+ if releaseFailure {
+ wantOutcome, wantReason = "failed", "runtime_release_failed"
+ }
+ if (err != nil) != releaseFailure {
+ t.Fatalf("reconcile error=%v, releaseFailure=%v", err, releaseFailure)
+ }
+ after, err := fixture.store.GetEnvironmentContext(ctx, "account-a")
+ if err != nil || (after.RuntimeID != "") == !releaseFailure {
+ t.Fatalf("runtime lease after reconcile: %#v err=%v", after, err)
+ }
- var events, operations, missingOperations int
- var eventTypes, outcome, reason string
- if err := fixture.db.QueryRowContext(ctx, `
+ var events, operations, missingOperations int
+ var eventTypes, outcome, reason string
+ if err := fixture.db.QueryRowContext(ctx, `
SELECT count(*), count(DISTINCT operation_id),
count(*) FILTER (WHERE coalesce(operation_id, '') = ''),
string_agg(event_type, ',' ORDER BY id),
coalesce(max(outcome) FILTER (WHERE event_type = 'environment_action_finished'), ''),
coalesce(max(reason_code) FILTER (WHERE event_type = 'environment_action_finished'), '')
FROM audit_event WHERE action = 'reconcile'`).Scan(
- &events, &operations, &missingOperations, &eventTypes, &outcome, &reason); err != nil {
- t.Fatal(err)
- }
- if events != 2 || operations != 1 || missingOperations != 0 ||
- eventTypes != "environment_action_requested,environment_action_finished" ||
- outcome != wantOutcome || reason != wantReason {
- t.Fatalf("reconcile audit mismatch: events=%d operations=%d missing=%d types=%q outcome=%q reason=%q",
- events, operations, missingOperations, eventTypes, outcome, reason)
- }
- })
- }
+ &events, &operations, &missingOperations, &eventTypes, &outcome, &reason); err != nil {
+ t.Fatal(err)
+ }
+ if events != 2 || operations != 1 || missingOperations != 0 ||
+ eventTypes != "environment_action_requested,environment_action_finished" ||
+ outcome != wantOutcome || reason != wantReason {
+ t.Fatalf("reconcile audit mismatch: events=%d operations=%d missing=%d types=%q outcome=%q reason=%q",
+ events, operations, missingOperations, eventTypes, outcome, reason)
+ }
+ })
}
}
}
@@ -2761,17 +2754,16 @@ func TestBrowserActionRoutesStartStopAndRejectsUnknown(t *testing.T) {
func TestStartRejectsPausedOrRevokedAccountBeforeGatewayCall(t *testing.T) {
for _, test := range []struct {
- name, status, authorization string
+ name, status string
}{
- {name: "paused", status: "paused", authorization: "authorized"},
- {name: "revoked", status: "paused", authorization: "revoked"},
+ {name: "paused", status: "paused"},
} {
t.Run(test.name, func(t *testing.T) {
store := newMemoryStore()
store.envs["account-a"] = hub.Env{Alias: "account-a", Name: "甲", Gateway: "gw-1", Fingerprint: hub.Fingerprint{Seed: 1}}
store.bindings["account-a"] = hub.EnvironmentContext{
Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: test.status,
- AuthorizationStatus: test.authorization, BindingVersion: 1, Exit: store.exits["exit-1"],
+ BindingVersion: 1, Exit: store.exits["exit-1"],
}
gateway := &fakeGateway{token: "unit-test-gateway-token"}
app := newTestApp(t, store, gateway)
@@ -2788,12 +2780,12 @@ func TestStartRejectsPausedOrRevokedAccountBeforeGatewayCall(t *testing.T) {
}
func TestReconcileStopsRuntimeForPausedOrRevokedAccount(t *testing.T) {
- for _, authorization := range []string{"authorized", "revoked"} {
- t.Run(authorization, func(t *testing.T) {
+ {
+ t.Run("paused", func(t *testing.T) {
store := newMemoryStore()
store.envs["account-a"] = hub.Env{Alias: "account-a", Name: "甲", Gateway: "gw-1", Fingerprint: hub.Fingerprint{Seed: 1}}
store.bindings["account-a"] = hub.EnvironmentContext{
- Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "paused", AuthorizationStatus: authorization,
+ Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "paused",
BindingVersion: 1, Exit: store.exits["exit-1"],
RuntimeID: "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
}
@@ -2830,7 +2822,7 @@ func TestActivationConflictCleanupIsGenerationSafe(t *testing.T) {
store := newMemoryStore()
store.envs["account-a"] = hub.Env{Alias: "account-a", Name: "甲", Gateway: "gw-1", Fingerprint: hub.Fingerprint{Seed: 1}}
environment := hub.EnvironmentContext{
- Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "paused", AuthorizationStatus: "authorized",
+ Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "paused",
BindingVersion: 2, Exit: store.exits["exit-1"],
}
store.bindings[environment.Alias] = environment
@@ -2865,7 +2857,7 @@ func TestExplicitStopRejectsStaleBindingBeforeGatewaySideEffect(t *testing.T) {
store := newMemoryStore()
store.envs["account-a"] = hub.Env{Alias: "account-a", Name: "甲", Gateway: "gw-1", Fingerprint: hub.Fingerprint{Seed: 1}}
store.bindings["account-a"] = hub.EnvironmentContext{
- Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "active", AuthorizationStatus: "authorized",
+ Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "active",
BindingVersion: 2, Exit: store.exits["exit-1"], RuntimeID: "new-container",
}
gateway := &fakeGateway{token: "unit-test-gateway-token", runtimes: []runtimeStatus{{
@@ -2890,7 +2882,7 @@ func TestReconcileFinishedAuditUsesRebuiltRuntime(t *testing.T) {
store.exits["exit-2"] = hub.NetworkExit{ID: "exit-2", Protocol: "http", Host: "proxy-2.example", Port: 8080, HealthStatus: "healthy", Version: 1}
store.envs["account-a"] = hub.Env{Alias: "account-a", Name: "甲", Gateway: "gw-1", Fingerprint: hub.Fingerprint{Seed: 1}}
store.bindings["account-a"] = hub.EnvironmentContext{
- Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "active", AuthorizationStatus: "authorized",
+ Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "active",
BindingVersion: 2, Exit: store.exits["exit-2"], RuntimeID: "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", RuntimeNetworkID: "native-66666666666666666666666666666666",
}
gateway := &fakeGateway{token: "unit-test-gateway-token", runtimes: []runtimeStatus{{
@@ -2913,7 +2905,7 @@ func TestReconcileContextRefreshFailureClearsAllAuditCorrelation(t *testing.T) {
store := newMemoryStore()
store.envs["account-a"] = hub.Env{Alias: "account-a", Name: "甲", Gateway: "gw-1", Fingerprint: hub.Fingerprint{Seed: 1}}
store.bindings["account-a"] = hub.EnvironmentContext{
- Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "active", AuthorizationStatus: "authorized",
+ Env: store.envs["account-a"], AccountID: "account-a", AccountStatus: "active",
BindingVersion: 2, Exit: store.exits["exit-1"], RuntimeID: "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", RuntimeNetworkID: "native-66666666666666666666666666666666",
}
gateway := &fakeGateway{token: "unit-test-gateway-token", runtimes: []runtimeStatus{{
diff --git a/internal/controlplane/app/app_test.go b/internal/controlplane/app/app_test.go
index bc74711..80a5c7a 100644
--- a/internal/controlplane/app/app_test.go
+++ b/internal/controlplane/app/app_test.go
@@ -366,7 +366,6 @@ func controlPlaneRouteMatrix() []controlPlaneRouteCase {
{http.MethodPost, "/api/phase-a/accounts/:id/environment", "/api/phase-a/accounts/missing/environment", "", http.StatusNotFound},
{http.MethodPost, "/api/phase-a/accounts/:id/start", "/api/phase-a/accounts/missing/start", "", http.StatusNotFound},
{http.MethodPost, "/api/phase-a/accounts/:id/resume", "/api/phase-a/accounts/missing/resume", "", http.StatusNotFound},
- {http.MethodPost, "/api/phase-a/accounts/:id/revoke", "/api/phase-a/accounts/missing/revoke", "", http.StatusNotFound},
{http.MethodGet, "/api/phase-a/audit", "/api/phase-a/audit", "", http.StatusOK},
}
diff --git a/internal/creator/accounts.go b/internal/creator/accounts.go
index 360d365..bd7c5d3 100644
--- a/internal/creator/accounts.go
+++ b/internal/creator/accounts.go
@@ -24,7 +24,7 @@ func (s *Store) EnsureAccountProfile(ctx context.Context, accountID string) erro
func accountProfileQuery() string {
return `
SELECT a.account_id, a.name, a.platform, a.platform_account_key,
- a.authorization_kind, a.authorization_status, a.status,
+ a.status,
p.login_username, p.password_configured, p.real_name_status,
p.real_name, p.identity_number, p.note, p.business_status,
p.big_account, p.reply_requirements, p.login_status, p.login_reason,
@@ -39,7 +39,7 @@ func scanAccountProfile(scanner interface{ Scan(...any) error }) (AccountProfile
var checkedAt sql.NullTime
if err := scanner.Scan(
&result.ID, &result.Name, &result.Platform, &result.PlatformAccountKey,
- &result.AuthorizationKind, &result.AuthorizationStatus, &result.RuntimeStatus,
+ &result.RuntimeStatus,
&result.LoginUsername, &result.PasswordConfigured, &result.RealNameStatus,
&result.RealName, &result.IdentityNumber, &result.Note, &result.BusinessStatus,
&result.BigAccount, &result.ReplyRequirements, &result.LoginStatus, &result.LoginReason,
diff --git a/internal/creator/collection.go b/internal/creator/collection.go
index 3607277..0062e37 100644
--- a/internal/creator/collection.go
+++ b/internal/creator/collection.go
@@ -340,7 +340,7 @@ func (s *Store) ListDueOwnedAccounts(ctx context.Context, now time.Time, interva
ON works_checkpoint.source_type='owned' AND works_checkpoint.source_id=account.account_id AND works_checkpoint.collection_kind='works'
LEFT JOIN creator_collection_checkpoint comments_checkpoint
ON comments_checkpoint.source_type='owned' AND comments_checkpoint.source_id=account.account_id AND comments_checkpoint.collection_kind='comments'
- WHERE account.platform = 'douyin' AND account.authorization_status='authorized'
+ WHERE account.platform = 'douyin'
AND profile.login_status='logged_in'
AND COALESCE(works_checkpoint.status, '') <> 'blocked'
AND COALESCE(comments_checkpoint.status, '') <> 'blocked'
diff --git a/internal/creator/logic.go b/internal/creator/logic.go
index 12f9bdc..d553028 100644
--- a/internal/creator/logic.go
+++ b/internal/creator/logic.go
@@ -222,7 +222,7 @@ func CanWrite(profile AccountProfile, automatic bool, action string) error {
return ErrConflict
}
}
- if profile.LoginStatus != "logged_in" || profile.AuthorizationStatus != "authorized" {
+ if profile.LoginStatus != "logged_in" {
return ErrConflict
}
return nil
diff --git a/internal/creator/logic_test.go b/internal/creator/logic_test.go
index edcac73..98468a5 100644
--- a/internal/creator/logic_test.go
+++ b/internal/creator/logic_test.go
@@ -69,7 +69,7 @@ func TestActionTargetAndBusinessStatusGates(t *testing.T) {
if ActionTargetValid(ActionReplyComment, "uid-1", "", "", "comment") {
t.Fatal("reply without a comment target must be blocked")
}
- profile := AccountProfile{AuthorizationStatus: "authorized", LoginStatus: "logged_in", BusinessStatus: "muted"}
+ profile := AccountProfile{LoginStatus: "logged_in", BusinessStatus: "muted"}
if err := CanWrite(profile, false, ActionDM); err == nil {
t.Fatal("muted accounts cannot manually send DMs")
}
diff --git a/internal/creator/models.go b/internal/creator/models.go
index 3828d2b..5c5bc1a 100644
--- a/internal/creator/models.go
+++ b/internal/creator/models.go
@@ -52,27 +52,25 @@ type SettingsUpdate struct {
}
type AccountProfile struct {
- ID string `json:"id"`
- Name string `json:"name"`
- Platform string `json:"platform"`
- PlatformAccountKey string `json:"platform_account_key"`
- AuthorizationKind string `json:"authorization_kind"`
- AuthorizationStatus string `json:"authorization_status"`
- RuntimeStatus string `json:"runtime_status"`
- LoginUsername string `json:"login_username"`
- PasswordConfigured bool `json:"password_configured"`
- RealNameStatus string `json:"real_name_status"`
- RealName string `json:"real_name"`
- IdentityNumber string `json:"identity_number"`
- Note string `json:"note"`
- BusinessStatus string `json:"business_status"`
- BigAccount bool `json:"big_account"`
- ReplyRequirements string `json:"reply_requirements"`
- LoginStatus string `json:"login_status"`
- LoginReason string `json:"login_reason"`
- LoginCheckedAt *time.Time `json:"login_checked_at,omitempty"`
- CooldownSeconds int64 `json:"cooldown_seconds"`
- UpdatedAt time.Time `json:"updated_at"`
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Platform string `json:"platform"`
+ PlatformAccountKey string `json:"platform_account_key"`
+ RuntimeStatus string `json:"runtime_status"`
+ LoginUsername string `json:"login_username"`
+ PasswordConfigured bool `json:"password_configured"`
+ RealNameStatus string `json:"real_name_status"`
+ RealName string `json:"real_name"`
+ IdentityNumber string `json:"identity_number"`
+ Note string `json:"note"`
+ BusinessStatus string `json:"business_status"`
+ BigAccount bool `json:"big_account"`
+ ReplyRequirements string `json:"reply_requirements"`
+ LoginStatus string `json:"login_status"`
+ LoginReason string `json:"login_reason"`
+ LoginCheckedAt *time.Time `json:"login_checked_at,omitempty"`
+ CooldownSeconds int64 `json:"cooldown_seconds"`
+ UpdatedAt time.Time `json:"updated_at"`
}
type AccountProfileUpdate struct {
diff --git a/internal/environment/environment.go b/internal/environment/environment.go
index 4780608..a905548 100644
--- a/internal/environment/environment.go
+++ b/internal/environment/environment.go
@@ -49,7 +49,6 @@ type EnvironmentContext struct {
Env
AccountID string `json:"account_id"`
AccountStatus string `json:"account_status"`
- AuthorizationStatus string `json:"authorization_status"`
BindingVersion int64 `json:"binding_version"`
RuntimeCleanupPending bool `json:"runtime_cleanup_pending,omitempty"`
RuntimeCleanupBindingVersion int64 `json:"runtime_cleanup_binding_version,omitempty"`
@@ -432,7 +431,6 @@ func (s *Store) CreateBoundEnv(ctx context.Context, env Env, accountID, exitID s
JOIN gateway ON gateway.name = $3
LEFT JOIN network_exit network ON network.exit_id = NULLIF($6, '')
WHERE account.account_id = $5 AND account.status = 'paused'
- AND account.authorization_status = 'authorized'
AND ($6 = '' OR network.health_status = 'healthy')
RETURNING alias`, env.Alias, env.Name, env.Gateway, encoded, accountID, exitID).Scan(&created); err != nil {
return EnvironmentContext{}, false, rowError(err)
@@ -464,7 +462,7 @@ func (s *Store) GetEnvironmentContext(ctx context.Context, alias string) (Enviro
var cleanupBindingVersion sql.NullInt64
err = tx.QueryRowContext(ctx, `
SELECT environment.alias, environment.name, gateway.name,
- environment.fingerprint, environment.created_at, account.account_id, account.status, account.authorization_status,
+ environment.fingerprint, environment.created_at, account.account_id, account.status,
environment.version,
environment.runtime_cleanup_pending, environment.runtime_cleanup_binding_version,
environment.runtime_cleanup_runtime_id, environment.runtime_cleanup_network_id,
@@ -481,7 +479,7 @@ func (s *Store) GetEnvironmentContext(ctx context.Context, alias string) (Enviro
LEFT JOIN network_exit network ON network.id = environment.exit_id
WHERE environment.alias = $1`, alias).
Scan(&result.Alias, &result.Name, &result.Gateway, &encoded, &result.CreatedAt,
- &result.AccountID, &result.AccountStatus, &result.AuthorizationStatus,
+ &result.AccountID, &result.AccountStatus,
&result.BindingVersion, &result.RuntimeCleanupPending, &cleanupBindingVersion,
&cleanupRuntimeID, &cleanupNetworkID,
&result.Exit.ID, &result.Exit.Protocol, &result.Exit.Host, &result.Exit.Port,
@@ -584,7 +582,6 @@ func validateEnvironmentRebind(ctx context.Context, tx *sql.Tx, alias, exitID st
FROM browser_env environment
JOIN social_account account ON account.id = environment.account_id
WHERE environment.alias = $1 AND account.status = 'paused'
- AND account.authorization_status = 'authorized'
AND NOT environment.runtime_cleanup_pending
FOR UPDATE OF environment, account`, alias).Scan(&accountID, &bindingVersion)
if errors.Is(err, sql.ErrNoRows) {
@@ -688,21 +685,21 @@ func (s *Store) ActivateRuntime(ctx context.Context, alias, runtimeID string, bi
return EnvironmentContext{}, errors.New("begin runtime activation")
}
defer tx.Rollback()
- var accountID, currentExitID, accountStatus, authorizationStatus string
+ var accountID, currentExitID, accountStatus string
var currentBindingVersion int64
var cleanupPending bool
err = tx.QueryRowContext(ctx, `
SELECT account.account_id, environment.version, COALESCE(network.exit_id, ''), environment.runtime_cleanup_pending,
- account.status, account.authorization_status
+ account.status
FROM browser_env environment
JOIN social_account account ON account.id = environment.account_id
LEFT JOIN network_exit network ON network.id = environment.exit_id
WHERE environment.alias = $1 FOR UPDATE OF environment, account`, alias).
- Scan(&accountID, ¤tBindingVersion, ¤tExitID, &cleanupPending, &accountStatus, &authorizationStatus)
+ Scan(&accountID, ¤tBindingVersion, ¤tExitID, &cleanupPending, &accountStatus)
if err != nil {
return EnvironmentContext{}, rowError(err)
}
- if cleanupPending || accountStatus != "active" || authorizationStatus != "authorized" ||
+ if cleanupPending || accountStatus != "active" ||
currentBindingVersion != bindingVersion || currentExitID != exitID {
return EnvironmentContext{}, ErrConflict
}
diff --git a/internal/environment/migration043_probe_test.go b/internal/environment/migration043_probe_test.go
index c2e09fd..77aebc6 100644
--- a/internal/environment/migration043_probe_test.go
+++ b/internal/environment/migration043_probe_test.go
@@ -70,6 +70,6 @@ func TestMigration043ConsolidatedSchemaShape(t *testing.T) {
// audit_event 任务列已删
assertDatabaseCount(t, db, `SELECT count(*) FROM information_schema.columns WHERE table_schema = current_schema()
AND table_name = 'audit_event' AND column_name IN ('confirmation_id','confirmation_version','attempt_id','task_id','runtime_instance_id')`, 0)
- // 统一登记表:1-38(除 36)、1017-1044、43 全部登记
- assertDatabaseCount(t, db, `SELECT count(*) FROM schema_migration`, 51)
+ // 统一登记表:1-38(除 36)、1017-1045、43 全部登记
+ assertDatabaseCount(t, db, `SELECT count(*) FROM schema_migration`, 52)
}
diff --git a/internal/environment/migration_test.go b/internal/environment/migration_test.go
index b6e8f9d..a44e30a 100644
--- a/internal/environment/migration_test.go
+++ b/internal/environment/migration_test.go
@@ -30,7 +30,7 @@ func TestUnifiedAccountMigration(t *testing.T) {
t.Fatal(err)
}
defer db.Close()
- assertDatabaseCount(t, db, `SELECT count(*) FROM schema_migration`, 51)
+ assertDatabaseCount(t, db, `SELECT count(*) FROM schema_migration`, 52)
assertDatabaseCount(t, db, `SELECT count(*) FROM information_schema.tables WHERE table_schema = current_schema()
AND table_name IN ('social_account', 'browser_env', 'network_exit', 'environment_binding')`, 3)
assertDatabaseCount(t, db, `SELECT count(*) FROM information_schema.tables WHERE table_schema = current_schema() AND table_name = 'browser_image'`, 0)
@@ -42,7 +42,7 @@ func TestUnifiedAccountMigration(t *testing.T) {
store = openFullyMigratedHub(t, ctx, testURL)
store.Close()
- assertDatabaseCount(t, db, `SELECT count(*) FROM schema_migration`, 51)
+ assertDatabaseCount(t, db, `SELECT count(*) FROM schema_migration`, 52)
})
t.Run("legacy migration 013 without account secrets is repaired forward", func(t *testing.T) {
@@ -129,13 +129,13 @@ func TestUnifiedAccountMigration(t *testing.T) {
assertDatabaseCount(t, db, `SELECT count(*) FROM audit_event`, 0)
assertDatabaseCount(t, db, `SELECT count(*) FROM schema_migration WHERE version IN (3, 4, 5, 6, 7, 8, 9)`, 7)
if _, err := db.Exec(`
- INSERT INTO social_account (account_id, credential_provider, credential_key, platform, platform_account_key, authorization_kind, authorization_status)
- VALUES ('mapped', 'os_keyring', 'creatorhub/mapped', 'mock', 'mapped', 'owned', 'authorized')`); err != nil {
+ INSERT INTO social_account (account_id, credential_provider, credential_key, platform, platform_account_key)
+ VALUES ('mapped', 'os_keyring', 'creatorhub/mapped', 'mock', 'mapped')`); err != nil {
t.Fatal(err)
}
if _, err := db.Exec(`
- INSERT INTO social_account (account_id, credential_provider, credential_key, platform, platform_account_key, authorization_kind, authorization_status)
- VALUES ('duplicate', 'os_keyring', 'creatorhub/duplicate', 'mock', 'mapped', 'owned', 'authorized')`); err == nil {
+ INSERT INTO social_account (account_id, credential_provider, credential_key, platform, platform_account_key)
+ VALUES ('duplicate', 'os_keyring', 'creatorhub/duplicate', 'mock', 'mapped')`); err == nil {
t.Fatal("duplicate platform account must fail")
}
})
diff --git a/internal/environment/migrations/1045_drop_authorization_status.sql b/internal/environment/migrations/1045_drop_authorization_status.sql
new file mode 100644
index 0000000..8460213
--- /dev/null
+++ b/internal/environment/migrations/1045_drop_authorization_status.sql
@@ -0,0 +1,6 @@
+-- 移除「授权状态/授权种类」概念:产品已收敛为自有账号管理,前端无撤销入口,
+-- authorization_status 恒为 'authorized'、authorization_kind 恒为 'owned',列与判定全链路删除;
+-- 账号有效性由 status(active/paused)单一状态机表达。
+ALTER TABLE social_account DROP COLUMN IF EXISTS authorization_status;
+ALTER TABLE social_account DROP COLUMN IF EXISTS revoked_at;
+ALTER TABLE social_account DROP COLUMN IF EXISTS authorization_kind;
diff --git a/internal/environment/store.go b/internal/environment/store.go
index bcfe6d4..a727e69 100644
--- a/internal/environment/store.go
+++ b/internal/environment/store.go
@@ -173,6 +173,9 @@ var migration1043 string
//go:embed migrations/1044_creator_account_metric_friend_count.sql
var migration1044 string
+//go:embed migrations/1045_drop_authorization_status.sql
+var migration1045 string
+
var (
ErrConflict = errors.New("resource conflicts with existing state")
ErrInvalid = errors.New("invalid environment input")
@@ -312,7 +315,7 @@ func (s *Store) migrate(ctx context.Context) error {
{1023, migration1023}, {1024, migration1024}, {1025, migration1025}, {1026, migration1026}, {1027, migration1027}, {1028, migration1028},
{1029, migration1029}, {1030, migration1030}, {1031, migration1031}, {1032, migration1032}, {1033, migration1033}, {1034, migration1034},
{1035, migration1035}, {1036, migration1036}, {1037, migration1037}, {1038, migration1038}, {1039, migration1039}, {1040, migration1040},
- {1041, migration1041}, {1042, migration1042}, {1043, migration1043}, {1044, migration1044},
+ {1041, migration1041}, {1042, migration1042}, {1043, migration1043}, {1044, migration1044}, {1045, migration1045},
{43, migration043}, {44, migration044}} {
var applied bool
if err := tx.QueryRowContext(ctx, `SELECT EXISTS (SELECT 1 FROM schema_migration WHERE version = $1)`, migration.version).Scan(&applied); err != nil {
diff --git a/internal/environment/store_test.go b/internal/environment/store_test.go
index 39d73ae..3b6fb78 100644
--- a/internal/environment/store_test.go
+++ b/internal/environment/store_test.go
@@ -293,9 +293,9 @@ func TestFingerprintSeedIsGloballyUnique(t *testing.T) {
t.Fatal(err)
}
if _, err := store.db.ExecContext(ctx, `
- INSERT INTO social_account (account_id, credential_provider, credential_key, platform, platform_account_key, authorization_kind, authorization_status)
- VALUES ('seed-account-a', 'os_keyring', 'creatorhub/seed-a', 'mock', 'seed-account-a', 'owned', 'authorized'),
- ('seed-account-b', 'os_keyring', 'creatorhub/seed-b', 'mock', 'seed-account-b', 'owned', 'authorized')`); err != nil {
+ INSERT INTO social_account (account_id, credential_provider, credential_key, platform, platform_account_key)
+ VALUES ('seed-account-a', 'os_keyring', 'creatorhub/seed-a', 'mock', 'seed-account-a'),
+ ('seed-account-b', 'os_keyring', 'creatorhub/seed-b', 'mock', 'seed-account-b')`); err != nil {
t.Fatal(err)
}
env := Env{Alias: "seed-environment-a", Name: "Seed A", Gateway: "gw-seed"}
@@ -355,8 +355,8 @@ func TestHubWorkflow(t *testing.T) {
}
if _, err := store.db.ExecContext(ctx, `
- INSERT INTO social_account (account_id, credential_provider, credential_key, platform, platform_account_key, authorization_kind, authorization_status)
- VALUES ('shop-owner', 'os_keyring', 'creatorhub/shop-owner', 'mock', 'shop-owner', 'owned', 'authorized')`); err != nil {
+ INSERT INTO social_account (account_id, credential_provider, credential_key, platform, platform_account_key)
+ VALUES ('shop-owner', 'os_keyring', 'creatorhub/shop-owner', 'mock', 'shop-owner')`); err != nil {
t.Fatal(err)
}
env := Env{
@@ -370,8 +370,8 @@ func TestHubWorkflow(t *testing.T) {
t.Fatalf("expected idempotent environment reuse, created=%v err=%v", created, err)
}
if _, err := store.db.ExecContext(ctx, `
- INSERT INTO social_account (account_id, credential_provider, credential_key, platform, platform_account_key, authorization_kind, authorization_status)
- VALUES ('shop-owner-2', 'os_keyring', 'creatorhub/shop-owner-2', 'mock', 'shop-owner-2', 'owned', 'authorized')`); err != nil {
+ INSERT INTO social_account (account_id, credential_provider, credential_key, platform, platform_account_key)
+ VALUES ('shop-owner-2', 'os_keyring', 'creatorhub/shop-owner-2', 'mock', 'shop-owner-2')`); err != nil {
t.Fatal(err)
}
if _, _, err := store.CreateBoundEnv(ctx, Env{Alias: "shop-02", Name: "店铺二号", Gateway: "missing", Fingerprint: Fingerprint{Seed: 2}}, "shop-owner-2", ""); !errors.Is(err, ErrNotFound) {
@@ -437,8 +437,8 @@ func TestNetworkExitBindingRuntimeAndAuditWorkflow(t *testing.T) {
}
if _, err := store.db.ExecContext(ctx, `
INSERT INTO social_account
- (account_id, credential_provider, credential_key, platform, platform_account_key, authorization_kind, authorization_status)
- VALUES ('account-a', 'os_keyring', 'creatorhub/account-a', 'mock', 'account-a', 'owned', 'authorized')`); err != nil {
+ (account_id, credential_provider, credential_key, platform, platform_account_key)
+ VALUES ('account-a', 'os_keyring', 'creatorhub/account-a', 'mock', 'account-a')`); err != nil {
t.Fatal(err)
}
if _, err := store.CreateGateway(ctx, "gw-main", "http://127.0.0.1:8081", "unit-test-gateway-token"); err != nil {
@@ -502,8 +502,8 @@ func TestNetworkExitBindingRuntimeAndAuditWorkflow(t *testing.T) {
}
if _, err := store.db.ExecContext(ctx, `
INSERT INTO social_account
- (account_id, credential_provider, credential_key, platform, platform_account_key, authorization_kind, authorization_status)
- VALUES ('account-b', 'os_keyring', 'creatorhub/account-b', 'mock', 'account-b', 'owned', 'authorized');
+ (account_id, credential_provider, credential_key, platform, platform_account_key)
+ VALUES ('account-b', 'os_keyring', 'creatorhub/account-b', 'mock', 'account-b');
INSERT INTO browser_env (alias, name, gateway_id, fingerprint, account_id)
VALUES ('environment-b', '环境 B', (SELECT g.id FROM gateway g WHERE g.name = 'gw-main'), '{"seed":2}',
(SELECT a.id FROM social_account a WHERE a.account_id = 'account-b'))`); err != nil {
@@ -692,8 +692,8 @@ func TestRuntimeCleanupFenceAndBoundCreateReadiness(t *testing.T) {
store := openFullyMigratedHub(t, ctx, isolatedDatabaseURL(t, databaseURL))
t.Cleanup(func() { _ = store.Close() })
if _, err := store.db.ExecContext(ctx, `
- INSERT INTO social_account (account_id, credential_provider, credential_key, platform, platform_account_key, authorization_kind, authorization_status)
- VALUES ('fence-a', 'os_keyring', 'creatorhub/fence-a', 'mock', 'fence-a', 'owned', 'authorized')`); err != nil {
+ INSERT INTO social_account (account_id, credential_provider, credential_key, platform, platform_account_key)
+ VALUES ('fence-a', 'os_keyring', 'creatorhub/fence-a', 'mock', 'fence-a')`); err != nil {
t.Fatal(err)
}
if _, err := store.CreateGateway(ctx, "gw-fence", "http://127.0.0.1:8081", "unit-test-gateway-token"); err != nil {
diff --git a/web/src/components/accounts/AccountManagementList.tsx b/web/src/components/accounts/AccountManagementList.tsx
index dae89a8..1306eed 100644
--- a/web/src/components/accounts/AccountManagementList.tsx
+++ b/web/src/components/accounts/AccountManagementList.tsx
@@ -19,7 +19,6 @@ interface Row {
platform_account_key: string;
tags?: string[];
runtime_status?: string;
- authorization_status?: string;
work_count?: number;
latest_published_at?: string | null;
follower_count?: number | null;
@@ -127,7 +126,7 @@ export default function AccountManagementList() {
}
}
- // 扫码后核验:成功视为已登录,刷新列表拿到最新 authorization_status。
+ // 扫码后核验:成功视为已登录,刷新列表同步最新账号状态。
async function verifyLogin() {
if (!loginTarget) return;
setLoginBusy(true);
@@ -212,12 +211,9 @@ export default function AccountManagementList() {
title: '状态',
dataIndex: 'status',
render: (_, account) => (
-
-
- {account.runtime_status === 'active' ? '启用' : '暂停'}
-
- {account.authorization_status === 'authorized' ? '已授权' : '已撤销'}
-
+
+ {account.runtime_status === 'active' ? '启用' : '暂停'}
+
) },
{
title: '操作',
diff --git a/web/src/pages/accounts/$id/index.tsx b/web/src/pages/accounts/$id/index.tsx
index 8751155..4e77f57 100644
--- a/web/src/pages/accounts/$id/index.tsx
+++ b/web/src/pages/accounts/$id/index.tsx
@@ -209,7 +209,6 @@ export default function Page() {
- {account.authorization_status === 'authorized' ? '已授权' : '已撤销'}
{`${account.runtime_status === 'active' ? '启用' : '暂停'} · 版本 ${account.version}`}
{account.tags?.join('、') || '无'}
diff --git a/web/src/utils/helpers.ts b/web/src/utils/helpers.ts
index c0d930d..11838ee 100644
--- a/web/src/utils/helpers.ts
+++ b/web/src/utils/helpers.ts
@@ -58,9 +58,8 @@ export function extractShareURL(value?: string): string {
return candidates?.find((candidate) => platformForShareURL(candidate)) || candidates?.[0] || '';
}
-// 账号可恢复性判定(对齐归档版 accountReadiness):授权、绑定、出口健康、账号暂停四层。
+// 账号可恢复性判定(对齐归档版 accountReadiness):绑定、出口健康、账号暂停三层。
export const readinessReasonText: Record = {
- account_revoked: '授权已撤销',
account_paused: '账号已暂停',
binding_missing: '未绑定运行环境',
network_exit_missing: '未绑定固定出口',
@@ -80,8 +79,6 @@ export interface AccountReadiness {
}
export function accountReadiness(account: any, binding: any, bindingError: any = null): AccountReadiness {
- if (account.authorization_status !== 'authorized')
- return { label: '授权已撤销', reason: 'account_revoked', canResume: false, ready: false };
if (bindingError) return { label: '环境状态未知', reason: 'environment_unavailable', canResume: false, ready: false };
if (!binding) return { label: '未绑定运行环境', reason: 'binding_missing', canResume: false, ready: false };
let blocked = '';
@@ -105,9 +102,5 @@ export function accountReadiness(account: any, binding: any, bindingError: any =
}
export function isCollectionAccount(account: any): boolean {
- return (
- account.authorization_status === 'authorized' &&
- ['normal', 'muted'].includes(account.business_status) &&
- account.login_status === 'logged_in'
- );
+ return ['normal', 'muted'].includes(account.business_status) && account.login_status === 'logged_in';
}