thread-master/apps/backend/internal/module/threads/provider/fake.go

93 lines
2.5 KiB
Go
Raw Permalink Normal View History

2026-07-13 01:15:30 +00:00
package provider
import (
"context"
"fmt"
"net/url"
"strings"
"apps/backend/internal/module/threads/domain"
)
// FakeProvider implements OAuth without Meta (dev + tests).
// AuthorizeURL points at callbackURL with code=fake&state=...
type FakeProvider struct {
// CallbackBase is full callback path e.g. http://127.0.0.1:8888/api/v1/threads-accounts/oauth/callback
CallbackBase string
// FailExchange forces TH-04
FailExchange bool
// FailRefresh forces TH-07
FailRefresh bool
// FixedUserID when set, Exchange always returns this Threads user id (reconnect tests)
FixedUserID string
// FixedUsername optional with FixedUserID
FixedUsername string
}
func (f *FakeProvider) Name() string { return "fake" }
func (f *FakeProvider) AuthorizeURL(state, redirectURI string) string {
base := f.CallbackBase
if base == "" {
base = redirectURI
}
u, err := url.Parse(base)
if err != nil {
return base + "?code=fake&state=" + url.QueryEscape(state)
}
q := u.Query()
q.Set("code", "fake")
q.Set("state", state)
u.RawQuery = q.Encode()
return u.String()
}
func (f *FakeProvider) ExchangeCode(ctx context.Context, code, redirectURI string) (*domain.TokenPair, error) {
_ = ctx
_ = redirectURI
if f.FailExchange || code == "fail" {
return nil, domain.ErrOAuthExchange
}
if code != "fake" && !strings.HasPrefix(code, "fake") {
// still accept any code in pure fake mode for flexibility
}
id := f.FixedUserID
if id == "" {
id = fmt.Sprintf("fake_%d", domain.NowNano()%1_000_000)
}
user := f.FixedUsername
if user == "" {
user = "threads_" + id
}
return &domain.TokenPair{
AccessToken: "fake-access-" + id + fmt.Sprintf("_%d", domain.NowNano()%10000),
RefreshToken: "fake-refresh-" + id,
ExpiresIn: 3600 * 24 * 60, // ~60d
UserID: id,
Username: user,
DisplayName: "Threads " + user,
AvatarURL: "",
}, nil
}
func (f *FakeProvider) Refresh(ctx context.Context, refreshToken string) (*domain.TokenPair, error) {
_ = ctx
if f.FailRefresh || refreshToken == "" || strings.Contains(refreshToken, "bad") {
return nil, domain.ErrRefreshFailed
}
id := "refreshed"
if strings.HasPrefix(refreshToken, "fake-refresh-") {
id = strings.TrimPrefix(refreshToken, "fake-refresh-")
}
return &domain.TokenPair{
AccessToken: "fake-access-" + id,
RefreshToken: refreshToken,
ExpiresIn: 3600 * 24 * 60,
UserID: id,
Username: "threads_" + id,
DisplayName: "Threads " + id,
}, nil
}
var _ domain.Provider = (*FakeProvider)(nil)