feat: integrate creator hub douyin workflows

This commit is contained in:
2026-09-14 19:22:07 +08:00
parent 025fe62c37
commit 44a28954cf
42 changed files with 2850 additions and 880 deletions
+5 -27
View File
@@ -32,10 +32,7 @@ const (
worksEndpoint = "https://www.douyin.com/aweme/v1/web/aweme/post/"
)
var (
keyPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,127}$`)
credentialKeyPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._-]{0,63}/[A-Za-z0-9][A-Za-z0-9._/-]{0,126}$`)
)
var keyPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,127}$`)
var ErrInvalid = errors.New("invalid douyin connector input")
@@ -65,9 +62,9 @@ type Response struct {
}
// Browser is the deliberately narrow contract the restricted browser control
// plane must implement. It does not permit arbitrary CDP commands.
// plane must implement. Login happens in the managed browser session; the
// connector never injects stored credentials or cookies.
type Browser interface {
SetCookies(context.Context, []Cookie) error
Get(context.Context, string) (Response, error)
}
@@ -127,9 +124,7 @@ type Request struct {
func (connector Connector) Sync(ctx context.Context, request Request) (Result, error) {
if connector.Browser == nil || connector.Store == nil || !keyPattern.MatchString(request.AccountID) ||
!keyPattern.MatchString(request.PlatformAccountKey) ||
(request.Credential.Provider != "os_keyring" && request.Credential.Provider != "secret_manager") ||
!credentialKeyPattern.MatchString(request.Credential.Key) {
!keyPattern.MatchString(request.PlatformAccountKey) {
return Result{}, ErrInvalid
}
if err := ctx.Err(); err != nil {
@@ -140,24 +135,7 @@ func (connector Connector) Sync(ctx context.Context, request Request) (Result, e
if ctx.Err() != nil {
return connector.stop(ctx, request.AccountID, StatePolicyHold, ReasonAuthInvalid, Evidence{Phase: "login"})
}
if connector.Secrets == nil {
return connector.stop(ctx, request.AccountID, StatePolicyHold, ReasonAuthInvalid, Evidence{Phase: "login"})
}
credential, credentialErr := connector.Secrets.Resolve(ctx, request.Credential)
if credentialErr != nil {
return connector.stop(ctx, request.AccountID, StatePolicyHold, ReasonAuthInvalid, Evidence{Phase: "login"})
}
cookies, credentialErr := ParseCredential(credential)
if credentialErr != nil {
return connector.stop(ctx, request.AccountID, StatePolicyHold, ReasonAuthInvalid, Evidence{Phase: "login"})
}
if credentialErr = connector.Browser.SetCookies(ctx, cookies); credentialErr != nil {
return connector.stop(ctx, request.AccountID, StateNeedsConfirmation, ReasonUnknown, Evidence{Phase: "login"})
}
identityResponse, err = connector.Browser.Get(ctx, identityEndpoint)
if err != nil {
return connector.stop(ctx, request.AccountID, StateNeedsConfirmation, ReasonUnknown, Evidence{Phase: "identity"})
}
return connector.stop(ctx, request.AccountID, StateNeedsConfirmation, ReasonUnknown, Evidence{Phase: "login"})
}
if state, reason := classify(identityResponse); state != "" {
return connector.stop(ctx, request.AccountID, state, reason, Evidence{Phase: "identity", HTTPStatus: identityResponse.Status})
+12 -8
View File
@@ -83,7 +83,7 @@ func TestSyncLogsInVerifiesIdentityAndReadsOwnWorks(t *testing.T) {
store := &fakeStore{}
resolveCalls := 0
result, err := (Connector{Browser: browser, Secrets: fakeSecrets{value: []byte(credential), resolveCalls: &resolveCalls}, Store: store}).Sync(context.Background(), Request{
AccountID: "account-a", PlatformAccountKey: "sec-a", Credential: secretReference,
AccountID: "account-a", PlatformAccountKey: "sec-a",
})
if err != nil || result.State != StateSucceeded || !result.Evidence.IdentityVerified || result.Evidence.WorksSeen != 1 || !result.Evidence.HasMore {
t.Fatalf("unexpected result: %#v err=%v", result, err)
@@ -193,7 +193,7 @@ func TestSyncRejectsInvalidCredentialWithoutLeakingIt(t *testing.T) {
result, err := (Connector{Browser: browser, Secrets: fakeSecrets{value: []byte(`{"cookies":[{"name":"sessionid","value":"secret","domain":"evil.example"}]}`)}, Store: store}).Sync(context.Background(), Request{
AccountID: "account-a", PlatformAccountKey: "sec-a", Credential: secretReference,
})
if err != nil || result.State != StatePolicyHold || result.ReasonCode != ReasonAuthInvalid || len(browser.urls) != 1 || len(browser.cookies) != 0 || len(store.holds) != 1 {
if err != nil || result.State != StateNeedsConfirmation || result.ReasonCode != ReasonUnknown || len(browser.urls) != 1 || len(browser.cookies) != 0 || len(store.holds) != 1 {
t.Fatalf("unexpected invalid credential result: %#v browser=%#v holds=%#v err=%v", result, browser, store.holds, err)
}
encoded, _ := json.Marshal(result)
@@ -208,8 +208,8 @@ func TestSyncStopsWhenSecretReferenceCannotResolve(t *testing.T) {
result, err := (Connector{Browser: browser, Secrets: fakeSecrets{err: errors.New("secret unavailable")}, Store: store}).Sync(context.Background(), Request{
AccountID: "account-a", PlatformAccountKey: "sec-a", Credential: secretReference,
})
if err != nil || result.State != StatePolicyHold || result.ReasonCode != ReasonAuthInvalid || len(browser.urls) != 1 || len(store.holds) != 1 {
t.Fatalf("unavailable secret did not fail closed: result=%#v browser=%#v holds=%#v err=%v", result, browser, store.holds, err)
if err != nil || result.State != StateNeedsConfirmation || result.ReasonCode != ReasonUnknown || len(browser.urls) != 1 || len(store.holds) != 1 {
t.Fatalf("browser session did not fail closed without credential injection: result=%#v browser=%#v holds=%#v err=%v", result, browser, store.holds, err)
}
}
@@ -269,11 +269,15 @@ func TestSyncHoldsWithCancelledRequestContext(t *testing.T) {
}
}
func TestSyncRejectsNonSecretCredentialReference(t *testing.T) {
_, err := (Connector{Browser: &fakeBrowser{}, Secrets: fakeSecrets{}, Store: &fakeStore{}}).Sync(context.Background(), Request{
func TestSyncIgnoresLegacyCredentialReference(t *testing.T) {
browser := &fakeBrowser{responses: []Response{
{Status: 200, Body: identityBody("uid-a", "sec-a", "handle-a")},
{Status: 200, Body: []byte(`{"status_code":0,"has_more":false,"aweme_list":[]}`)},
}}
result, err := (Connector{Browser: browser, Store: &fakeStore{}}).Sync(context.Background(), Request{
AccountID: "account-a", PlatformAccountKey: "sec-a", Credential: SecretReference{Provider: "plain_text", Key: "raw-secret"},
})
if !errors.Is(err, ErrInvalid) {
t.Fatalf("accepted non-secret credential reference: %v", err)
if errors.Is(err, ErrInvalid) || result.State != StateSucceeded {
t.Fatalf("legacy credential reference affected browser-session sync: result=%#v err=%v", result, err)
}
}