Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
a396ae3
Implement account transfer registration flow
pood1e Jun 13, 2026
765ce39
Align verification code resend attempts
pood1e Jun 13, 2026
e558c28
Clarify wa transfer registration UI
pood1e Jun 14, 2026
a60ab0e
Implement old-device OTP inbox banner
pood1e Jun 14, 2026
313d42e
fix wa blocked channel display
pood1e Jun 15, 2026
0e8c133
fix wa blocked channel status
pood1e Jun 15, 2026
5410743
fix wa account transfer request params
pood1e Jun 15, 2026
381dd68
fix wa account transfer token param
pood1e Jun 15, 2026
05072ce
fix wa registration method list
pood1e Jun 15, 2026
0a0776e
fix wa account transfer wording
pood1e Jun 15, 2026
deb3914
Fix WA registration ephemeral material
pood1e Jun 15, 2026
0940685
Fix WA registration runtime session shape
pood1e Jun 15, 2026
96f3c7c
Fix WA WAMSYS runtime material lifecycle
pood1e Jun 15, 2026
5961647
Fix WA code request versioned session fields
pood1e Jun 15, 2026
dfaa821
Fix WA access session request shape
pood1e Jun 15, 2026
e5ed04c
Fix WA registration request runtime tokens
pood1e Jun 15, 2026
0b49829
Fix WA GPIA error-path device material
pood1e Jun 15, 2026
a404f17
Fix WA WAMSYS runtime map material
pood1e Jun 15, 2026
39684f8
Refresh WA registration AB props
pood1e Jun 15, 2026
2a74fe4
Apply WA pre-chatd AB props
pood1e Jun 15, 2026
8af3fe5
Honor WA registration request id AB flag
pood1e Jun 15, 2026
7cae39e
Revert "Honor WA registration request id AB flag"
pood1e Jun 15, 2026
109e66e
Align WA code request context field
pood1e Jun 15, 2026
d058ae0
Revert "Align WA code request context field"
pood1e Jun 15, 2026
1c00478
fix wamsys runtime path material
pood1e Jun 15, 2026
36c8a90
fix wamsys path age material
pood1e Jun 15, 2026
d038c70
align native registration device model
pood1e Jun 15, 2026
5e794ff
align registration token material
pood1e Jun 15, 2026
63c9ca8
Revert "align native registration device model"
pood1e Jun 15, 2026
d8ecdc5
align gpia native json material
pood1e Jun 15, 2026
1be566a
use runtime boot id for wamsys ga
pood1e Jun 15, 2026
16ed31d
align wamsys install scoped material
pood1e Jun 15, 2026
daa3323
honor registration recovery token flag
pood1e Jun 15, 2026
14b0af6
log registration field value hashes
pood1e Jun 15, 2026
d927f09
scope wamsys install material to native profile
pood1e Jun 15, 2026
e9eda88
align registration error-only material
pood1e Jun 15, 2026
6a482b3
match request code bridge fields
pood1e Jun 15, 2026
3dd8f2a
align registration client metrics material
pood1e Jun 15, 2026
0bfee01
align registration runtime profile
pood1e Jun 15, 2026
fad9af7
align registration wamsys permission digest
pood1e Jun 15, 2026
81d6d66
align registration code client metrics
pood1e Jun 15, 2026
0c08fd8
align registration wamsys boolean map order
pood1e Jun 15, 2026
d2b5b96
log registration wamsys ga shape
pood1e Jun 15, 2026
14ef6fa
align registration wamsys ga order
pood1e Jun 15, 2026
4ea2ab2
align registration wamsys path ages
pood1e Jun 15, 2026
bc92092
align registration route runtime fields
pood1e Jun 15, 2026
6039042
align registration code runtime aging
pood1e Jun 15, 2026
2abbcdc
align wamsys runtime serialization
pood1e Jun 15, 2026
bb68df5
Align GPIA source path with capture fixture
pood1e Jun 15, 2026
d2a2200
Revert "Align GPIA source path with capture fixture"
pood1e Jun 15, 2026
2bb1163
Align code request client metrics
pood1e Jun 15, 2026
fa381de
Align WAMSYS registration map order
pood1e Jun 15, 2026
8d35e96
Tie WAMSYS path ages to profile lifecycle
pood1e Jun 15, 2026
c714905
Align SMS code request field set
pood1e Jun 15, 2026
d97451d
Restore explicit SMS code method
pood1e Jun 15, 2026
14e47d1
Align code runtime material lifecycle
pood1e Jun 15, 2026
e8842db
Restore SMS code bridge metadata
pood1e Jun 15, 2026
cda456d
Restore code advertising bridge field
pood1e Jun 15, 2026
986fde8
Align code map with live request path
pood1e Jun 15, 2026
206e7ae
Revert "Align code map with live request path"
pood1e Jun 16, 2026
e784ace
Align code request user type
pood1e Jun 16, 2026
15ffb48
Align code request runtime status
pood1e Jun 16, 2026
5e8bc57
Align code request native runtime fields
pood1e Jun 16, 2026
21f0a30
Match code request native field set
pood1e Jun 16, 2026
210c58b
Match code request native advertising fields
pood1e Jun 16, 2026
5eb0a76
Refresh code request GPIA runtime digests
pood1e Jun 16, 2026
fc8a7e8
Use native shaped registration pid
pood1e Jun 16, 2026
7b8166c
Align registration WAMSYS parameter order
pood1e Jun 16, 2026
2ec963f
Stabilize native WAMSYS runtime identifiers
pood1e Jun 16, 2026
1509864
Align WA code request WAMSYS shape
pood1e Jun 16, 2026
19cf690
Omit SMS code SIM signal fields
pood1e Jun 16, 2026
861c6ac
Reuse registration attempt device state
pood1e Jun 16, 2026
0f8e4d8
Log native WAMSYS plaintext shapes
pood1e Jun 16, 2026
4e0d403
Align WAMSYS path age generation
pood1e Jun 16, 2026
bd62039
Accept SMS OTP side effect on no routes
pood1e Jun 16, 2026
c94a539
Append runtime feo2 status to code request
pood1e Jun 16, 2026
a19a8ce
Accept SMS no routes with fallback status
pood1e Jun 16, 2026
20d400a
Use clean APK feo2 default
pood1e Jun 16, 2026
a20515a
Send advertising id in code request
pood1e Jun 16, 2026
3a8d184
Append AB gated SIM signal code params
pood1e Jun 16, 2026
8c2a16b
Align code request optional params
pood1e Jun 16, 2026
9fe3b13
Treat delivered no routes as accepted code request
pood1e Jun 16, 2026
614ae35
Align GPIA error-only code
pood1e Jun 17, 2026
5747495
Add WA code parameter probe script
pood1e Jun 17, 2026
e52b04f
Support Colombia code parameter probes
pood1e Jun 17, 2026
5cb12f0
Add WA code parameter experiment runner
pood1e Jun 17, 2026
7cda968
Document WA code no_routes investigation
pood1e Jun 17, 2026
098c234
Document WA integrity profile probes
pood1e Jun 17, 2026
31eaa59
Sign WA code probe envelope
pood1e Jun 17, 2026
2a3c4ee
Document SMS-only UA factor probes
pood1e Jun 17, 2026
ba13e3b
Document SMS market UA probes
pood1e Jun 17, 2026
98ffdaa
Probe random WA device profiles
pood1e Jun 17, 2026
fe2a85d
Document WA device factor probes
pood1e Jun 17, 2026
2c52117
Probe WA code risk factors
pood1e Jun 17, 2026
9c00bd3
Document WA CO locale combo probes
pood1e Jun 17, 2026
5b82dc1
Probe WA routing factors with request caps
pood1e Jun 17, 2026
34c9fe3
Document WA rotating exit probe
pood1e Jun 17, 2026
46466d3
Document WA rotating exit retest
pood1e Jun 17, 2026
6c0f9aa
Probe WA default A11 candidates
pood1e Jun 17, 2026
4b06402
Improve WA default registration profile
pood1e Jun 17, 2026
a9cc538
Randomize WA Android 11 device profiles
pood1e Jun 17, 2026
8565c54
Reject WA no routes responses
pood1e Jun 17, 2026
cdd2182
Add per-request proxy lease probes
pood1e Jun 17, 2026
46f8d6f
Document per-request WA lease probes
pood1e Jun 17, 2026
6af4bcd
Document WA random A11 lease deep probe
pood1e Jun 17, 2026
0310dc4
Add WA random A11 boost factor arms
pood1e Jun 17, 2026
da29e6a
Record WA boost effective decision params
pood1e Jun 17, 2026
fec6324
Default new WA profiles to random generic A11
pood1e Jun 17, 2026
0c82a9c
Record WA default rollout and proxy lease findings
pood1e Jun 17, 2026
8860b28
Add WA registration proxy lease retention
pood1e Jun 17, 2026
dbc2e71
Show WA registration channels consistently
pood1e Jun 17, 2026
af3e522
Improve WA registration channel probing
pood1e Jun 17, 2026
ba05879
Record WA app registration probe comparison
pood1e Jun 17, 2026
7ebe3cf
Align WA GPIA error code with reverse material
pood1e Jun 17, 2026
43e42a7
Align WA WAMSYS path age generation
pood1e Jun 17, 2026
d3bfcdd
Remove guessed WA registration fields
pood1e Jun 17, 2026
9d529ae
Clarify WA WAMSYS no Android dependency
pood1e Jun 17, 2026
fdd383b
Restore confirmed WA WAMSYS fields
pood1e Jun 17, 2026
e0d9b90
Retry transient register submit failures
pood1e Jun 18, 2026
676001b
Retry register submit twice immediately
pood1e Jun 18, 2026
b53db53
Make registration proxy leases optional
pood1e Jun 18, 2026
e164539
Treat enabled proxy leases as optional
pood1e Jun 18, 2026
2de7333
Refine registration proxy lease mode handling
pood1e Jun 18, 2026
04ef82a
Polish WA registration proxy docs and pending channel state
pood1e Jun 18, 2026
d0af37b
Wire registration proxy lease env into compose
pood1e Jun 18, 2026
94ee88f
Fix registration channel lint
pood1e Jun 18, 2026
f892fcd
Move no routes investigation out of app repo
pood1e Jun 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ WA_APP_REDIS_URL=

# Optional upstream proxies (leave empty for direct outbound)
WA_COMMON_PROXY=

# Optional registration proxy-runtime lease. Modes: optional, disabled, required.
WA_REGISTRATION_PROXY_LEASE_MODE=optional
PROXY_RUNTIME_API_BASE_URL=
PROXY_RUNTIME_SERVICE_AUTH_TOKEN=
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ docker compose up -d
- `WA_APP_PG_DSN`:可选 PostgreSQL DSN;为空时使用内置 SQLite 持久化。
- `WA_APP_REDIS_URL`:可选 Redis URL;为空时使用内置 SQLite 运行态存储。
- `WA_COMMON_PROXY`:系统默认 WA 出站代理;账号未配置代理策略且阶段代理为空时使用,仍为空则直连。
- `WA_REGISTRATION_PROXY_LEASE_MODE`:注册链路 proxy-runtime 租约模式;`optional` 会失败回退,`disabled` 关闭租约,`required` 强制租约成功。
- `PROXY_RUNTIME_API_BASE_URL`:可选 proxy-runtime API 地址;留空时不请求动态租约。
- `PROXY_RUNTIME_SERVICE_AUTH_TOKEN`:可选 proxy-runtime 服务令牌;示例文件只保留空占位。

PostgreSQL 和 Redis 都是可选组件。需要启用时,在 `docker-compose.yml` 中取消对应服务注释,并在 `.env` 中填写 `WA_APP_PG_DSN` / `WA_APP_REDIS_URL`。

Expand Down
13 changes: 12 additions & 1 deletion cmd/wa-app-service/dashboard_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ func (s *dashboardHTTP) handleAccountOTPMessages(w http.ResponseWriter, r *http.
WaAccountId: q.Get("wa_account_id"),
Limit: int32(positiveInt(q.Get("limit"), 20)),
Cursor: q.Get("cursor"),
IncludeSensitiveValues: true,
IncludeSensitiveValues: queryBool(q.Get("include_sensitive_values"), true),
})
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "load WA OTP history failed"})
Expand Down Expand Up @@ -847,6 +847,17 @@ func positiveInt(value string, fallback int) int {
return parsed
}

func queryBool(value string, fallback bool) bool {
switch strings.ToLower(strings.TrimSpace(value)) {
case "true", "1", "yes", "on":
return true
case "false", "0", "no", "off":
return false
default:
return fallback
}
}

func methodNotAllowed(w http.ResponseWriter, allowed string) {
w.Header().Set("Allow", allowed)
writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"})
Expand Down
2 changes: 2 additions & 0 deletions cmd/wa-app-service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ func main() {
}
service := app.NewServer(store, runtime, engine, clock, ids)
service.SetCommonProxyURL(cfg.CommonProxy)
service.SetRegistrationProxyLeaseMode(cfg.RegistrationProxyLeaseMode)
service.SetProxyRuntimeLeaseClient(cfg.ProxyRuntimeAPI, cfg.ProxyRuntimeToken)
authConfig := newDashboardAuthConfig(cfg.DashboardAuthPass)
grpcListenAddr := configValue(cfg.GRPCListenAddr, defaultGRPCListenAddr)
dashboardHTTPAddr := configValue(cfg.DashboardHTTPAddr, defaultDashboardHTTPAddr)
Expand Down
3 changes: 3 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ services:
WA_APP_PG_DSN: ${WA_APP_PG_DSN:-}
WA_APP_REDIS_URL: ${WA_APP_REDIS_URL:-}
WA_COMMON_PROXY: ${WA_COMMON_PROXY:-}
WA_REGISTRATION_PROXY_LEASE_MODE: ${WA_REGISTRATION_PROXY_LEASE_MODE:-optional}
PROXY_RUNTIME_API_BASE_URL: ${PROXY_RUNTIME_API_BASE_URL:-}
PROXY_RUNTIME_SERVICE_AUTH_TOKEN: ${PROXY_RUNTIME_SERVICE_AUTH_TOKEN:-}
WA_APP_DATA_DIR: ${WA_APP_DATA_DIR:-/var/lib/wa-app}
ports:
- "127.0.0.1:50091:50091"
Expand Down
189 changes: 165 additions & 24 deletions internal/app/action_gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ import (
)

const transientStateTTL = 30 * time.Minute
const registrationAttemptStateTTL = 26 * time.Hour
const registrationOTPWaitDefaultTTL = 20 * time.Minute

type registrationOTPWait struct {
WAAccountID string `json:"wa_account_id"`
VerificationRequestID string `json:"verification_request_id"`
ResumeURL string `json:"resume_url"`
CreatedAtUnix int64 `json:"created_at_unix"`
WAAccountID string `json:"wa_account_id"`
VerificationRequestID string `json:"verification_request_id"`
ResumeURL string `json:"resume_url"`
CreatedAtUnix int64 `json:"created_at_unix"`
ProxyLease registrationProxyLease `json:"proxy_lease,omitempty"`
}

type actionGateway struct{ server *Server }
Expand Down Expand Up @@ -61,6 +63,10 @@ func (g *actionGateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
result, err = g.resumeOTP(r.Context(), payload)
case "registration/submit-otp":
result, err = g.submitOTP(r.Context(), payload)
case "registration/account-transfer/refresh":
result, err = g.refreshAccountTransferChallenge(r.Context(), payload)
case "registration/account-transfer/poll":
result, err = g.pollAccountTransferRegistration(r.Context(), payload)
case "registration/cleanup-failed-account":
result, err = g.cleanupFailedRegistration(r.Context(), payload)
case "registration/persist-login-state":
Expand Down Expand Up @@ -168,7 +174,7 @@ func (g *actionGateway) requestSMSOTP(ctx context.Context, payload map[string]an
if reason := directRegistrationMethodUnsupportedReason(method); reason != "" {
return registrationMethodUnsupportedMap(method, reason), nil
}
runner, route, managedRoute, err := g.registrationRequestRunner(ctx, payload)
runner, route, managedRoute, proxyLease, err := g.registrationRequestRunner(ctx, payload)
if err != nil {
return nil, err
}
Expand All @@ -182,20 +188,42 @@ func (g *actionGateway) requestSMSOTP(ctx context.Context, payload map[string]an
}, runner)
runner.CloseIdleConnections()
if err != nil {
g.releaseRegistrationProxyLease(context.Background(), proxyLease)
return nil, err
}
if resp.GetError() != nil {
g.releaseRegistrationProxyLease(context.Background(), proxyLease)
return map[string]any{"success": false, "error": protoMap(resp.GetError()), "error_message": resp.GetError().GetMessage()}, nil
}
record := resp.GetVerificationRequest()
success := record.GetStatus() == waappv1.VerificationRequestStatus_VERIFICATION_REQUEST_STATUS_SENT || record.GetStatus() == waappv1.VerificationRequestStatus_VERIFICATION_REQUEST_STATUS_WAITING
if !success {
g.releaseRegistrationProxyLease(context.Background(), proxyLease)
}
if success && validRegistrationProxyLease(proxyLease) {
wait := registrationOTPWait{
WAAccountID: record.GetWaAccountId(),
VerificationRequestID: record.GetVerificationRequestId(),
CreatedAtUnix: time.Now().UTC().Unix(),
ProxyLease: proxyLease,
}
if err := g.saveRegistrationOTPWait(ctx, wait, registrationOTPWaitDefaultTTL); err != nil {
g.releaseRegistrationProxyLease(context.Background(), proxyLease)
return nil, err
}
}
response := map[string]any{
"success": record.GetStatus() == waappv1.VerificationRequestStatus_VERIFICATION_REQUEST_STATUS_SENT || record.GetStatus() == waappv1.VerificationRequestStatus_VERIFICATION_REQUEST_STATUS_WAITING,
"success": success,
"status": record.GetStatus().String(),
"verification_request_id": record.GetVerificationRequestId(),
"verification_request": protoMap(record),
"method_statuses": protoMethodStatusMaps(record.GetMethodStatuses()),
"proxy": registrationProxyRouteMap(route, managedRoute),
}
if challenge := resp.GetAccountTransferChallenge(); challenge != nil {
response["account_transfer_challenge"] = protoMap(challenge)
response["registration_phase"] = "ACCOUNT_TRANSFER_WAITING"
}
if seconds := durationSeconds(record.GetRetryAfter()); seconds > 0 {
response["retry_after_seconds"] = seconds
}
Expand All @@ -207,6 +235,11 @@ func (g *actionGateway) awaitOTP(ctx context.Context, payload map[string]any) (m
if err != nil {
return nil, err
}
if wait.ProxyLease.LeaseID == "" && g.server.registrationProxyLeaseEnabled() {
if existing, err := g.loadRegistrationOTPWait(ctx, wait.WAAccountID, wait.VerificationRequestID); err == nil {
wait.ProxyLease = existing.ProxyLease
}
}
if err := g.saveRegistrationOTPWait(ctx, wait, ttl); err != nil {
return nil, err
}
Expand All @@ -231,6 +264,7 @@ func (g *actionGateway) resumeOTP(ctx context.Context, payload map[string]any) (
if err := postRegistrationOTPResume(ctx, wait, code); err != nil {
return nil, err
}
g.releaseRegistrationProxyLease(context.Background(), wait.ProxyLease)
_ = g.deleteRegistrationOTPWait(ctx, wait)
return map[string]any{"success": true, "wa_account_id": wait.WAAccountID, "verification_request_id": wait.VerificationRequestID}, nil
}
Expand Down Expand Up @@ -357,7 +391,7 @@ func registrationOTPWaitAccountKey(waAccountIDValue string) string {
}

func (g *actionGateway) submitOTP(ctx context.Context, payload map[string]any) (map[string]any, error) {
runner, route, managedRoute, err := g.registrationSubmitRunner(ctx, payload)
runner, route, managedRoute, proxyLease, err := g.registrationSubmitRunner(ctx, payload)
if err != nil {
return nil, err
}
Expand All @@ -374,6 +408,13 @@ func (g *actionGateway) submitOTP(ctx context.Context, payload map[string]any) (
return map[string]any{"success": false, "error": protoMap(resp.GetError()), "error_message": resp.GetError().GetMessage(), "registration": protoMap(resp.GetRegistration())}, nil
}
success := resp.GetRegistration().GetStatus() == waappv1.RegistrationStatus_REGISTRATION_STATUS_REGISTERED && resp.GetLoginState().GetStatus() == waappv1.LoginStateStatus_LOGIN_STATE_STATUS_ACTIVE
if success {
g.releaseRegistrationProxyLease(context.Background(), proxyLease)
_ = g.deleteRegistrationOTPWait(ctx, registrationOTPWait{
WAAccountID: resp.GetRegistration().GetWaAccountId(),
VerificationRequestID: resp.GetRegistration().GetVerificationRequestId(),
})
}
return map[string]any{
"success": success,
"status": resp.GetRegistration().GetStatus().String(),
Expand All @@ -383,15 +424,99 @@ func (g *actionGateway) submitOTP(ctx context.Context, payload map[string]any) (
}, nil
}

func (g *actionGateway) refreshAccountTransferChallenge(ctx context.Context, payload map[string]any) (map[string]any, error) {
resp, err := g.server.RefreshAccountTransferChallenge(ctx, &waappv1.RefreshAccountTransferChallengeRequest{
Context: actionContext(payload),
VerificationRequestId: textField(payload, "verification_request_id"),
})
if err != nil {
return nil, err
}
if resp.GetError() != nil {
return map[string]any{"success": false, "error": protoMap(resp.GetError()), "error_message": resp.GetError().GetMessage()}, nil
}
return map[string]any{
"success": true,
"registration_phase": "ACCOUNT_TRANSFER_WAITING",
"account_transfer_challenge": protoMap(resp.GetAccountTransferChallenge()),
}, nil
}

func (g *actionGateway) pollAccountTransferRegistration(ctx context.Context, payload map[string]any) (map[string]any, error) {
attempts := int(numberField(payload, "max_attempts"))
if attempts <= 0 {
attempts = 1
}
if attempts > 100 {
attempts = 100
}
interval := time.Duration(numberField(payload, "interval_seconds")) * time.Second
var result map[string]any
for attempt := 0; attempt < attempts; attempt++ {
if attempt > 0 && interval > 0 {
timer := time.NewTimer(interval)
select {
case <-ctx.Done():
timer.Stop()
return nil, ctx.Err()
case <-timer.C:
}
}
submitPayload := cloneActionPayload(payload)
submitPayload["code"] = ""
resultValue, err := g.submitOTP(ctx, submitPayload)
if err != nil {
return nil, err
}
result = resultValue
if boolField(result, "success") {
_ = g.deleteRegistrationOTPWait(ctx, registrationOTPWait{WAAccountID: textField(payload, "wa_account_id"), VerificationRequestID: textField(payload, "verification_request_id")})
result["attempts"] = attempt + 1
return result, nil
}
if !accountTransferPollRetryable(result) {
result["attempts"] = attempt + 1
return result, nil
}
}
if result == nil {
result = map[string]any{"success": false}
}
result["registration_phase"] = "ACCOUNT_TRANSFER_WAITING"
result["attempts"] = attempts
return result, nil
}

func accountTransferPollRetryable(result map[string]any) bool {
if result == nil {
return true
}
if textField(result, "status") == waappv1.RegistrationStatus_REGISTRATION_STATUS_SUBMITTED.String() {
return true
}
errorMap := objectField(result, "error")
if boolField(errorMap, "retryable") {
return true
}
message := strings.ToLower(firstNonEmpty(textField(result, "error_message"), textField(errorMap, "message")))
return strings.Contains(message, "pending") || strings.Contains(message, "temporarily") || strings.Contains(message, "too_recent")
}

func (g *actionGateway) cleanupFailedRegistration(ctx context.Context, payload map[string]any) (map[string]any, error) {
reqCtx := actionContext(payload)
accountID := cleanupWAAccountID(payload)
verificationRequestID := cleanupVerificationRequestID(payload)
if verificationRequestID != "" || accountID != "" {
_ = g.deleteRegistrationOTPWait(ctx, registrationOTPWait{
WAAccountID: accountID,
VerificationRequestID: verificationRequestID,
})
wait, err := g.loadRegistrationOTPWait(ctx, accountID, verificationRequestID)
if err == nil {
g.releaseRegistrationProxyLease(context.Background(), wait.ProxyLease)
_ = g.deleteRegistrationOTPWait(ctx, wait)
} else {
_ = g.deleteRegistrationOTPWait(ctx, registrationOTPWait{
WAAccountID: accountID,
VerificationRequestID: verificationRequestID,
})
}
}
if accountID == "" {
return map[string]any{"success": true, "deleted": false, "reason": "missing_wa_account_id"}, nil
Expand Down Expand Up @@ -554,7 +679,7 @@ func (s *Server) ensureDefaultProtocolProfile(ctx context.Context) (*waappv1.Pro
waappv1.ProtocolCapability_PROTOCOL_CAPABILITY_MESSAGE_SESSION,
waappv1.ProtocolCapability_PROTOCOL_CAPABILITY_ACCOUNT_SETTINGS,
},
RegistrationFlows: []waappv1.RegistrationFlowKind{waappv1.RegistrationFlowKind_REGISTRATION_FLOW_KIND_NEW_ACCOUNT},
RegistrationFlows: []waappv1.RegistrationFlowKind{waappv1.RegistrationFlowKind_REGISTRATION_FLOW_KIND_NEW_ACCOUNT, waappv1.RegistrationFlowKind_REGISTRATION_FLOW_KIND_EXISTING_ACCOUNT},
MessageTransports: []waappv1.MessageTransportKind{waappv1.MessageTransportKind_MESSAGE_TRANSPORT_KIND_LONG_CONNECTION},
DiscoveredAt: timestamppb.New(now),
Audit: &waappv1.AuditStamp{CreatedAt: timestamppb.New(now), UpdatedAt: timestamppb.New(now)},
Expand All @@ -577,10 +702,10 @@ func (g *actionGateway) nativeEngineForPayload(payload map[string]any) (*NativeE
return engine.WithProxyURL(proxyURL)
}

func (g *actionGateway) registrationRequestRunner(ctx context.Context, payload map[string]any) (*NativeEngine, WAProxyRoute, bool, error) {
func (g *actionGateway) registrationRequestRunner(ctx context.Context, payload map[string]any) (*NativeEngine, WAProxyRoute, bool, registrationProxyLease, error) {
engine, err := g.nativeEngine()
if err != nil {
return nil, WAProxyRoute{}, false, err
return nil, WAProxyRoute{}, false, registrationProxyLease{}, err
}
route, useProxy, err := g.server.resolveWAProxyRoute(ctx, waProxyResolveRequest{
Stage: waProxyStageRegistration,
Expand All @@ -589,35 +714,51 @@ func (g *actionGateway) registrationRequestRunner(ctx context.Context, payload m
CountryCode: proxyCountryCodeFromPayload(payload),
})
if err != nil {
return nil, WAProxyRoute{}, false, err
return nil, WAProxyRoute{}, false, registrationProxyLease{}, err
}
if !useProxy {
return engine, route, false, nil
return engine, route, false, registrationProxyLease{}, nil
}
lease, leasedRoute, err := g.acquireRegistrationProxyLease(ctx, payload, route, registrationOTPWaitDefaultTTL)
if err != nil {
return nil, WAProxyRoute{}, false, registrationProxyLease{}, err
}
if validRegistrationProxyLease(lease) {
route = leasedRoute
}
proxied, err := engine.WithProxyURL(route.ProxyURL)
if err != nil {
return nil, WAProxyRoute{}, false, err
g.releaseRegistrationProxyLease(context.Background(), lease)
return nil, WAProxyRoute{}, false, registrationProxyLease{}, err
}
return proxied, route, true, nil
return proxied, route, true, lease, nil
}

func (g *actionGateway) registrationSubmitRunner(ctx context.Context, payload map[string]any) (*NativeEngine, WAProxyRoute, bool, error) {
func (g *actionGateway) registrationSubmitRunner(ctx context.Context, payload map[string]any) (*NativeEngine, WAProxyRoute, bool, registrationProxyLease, error) {
engine, err := g.nativeEngine()
if err != nil {
return nil, WAProxyRoute{}, false, err
return nil, WAProxyRoute{}, false, registrationProxyLease{}, err
}
if wait, err := g.loadRegistrationOTPWait(ctx, textField(payload, "wa_account_id"), textField(payload, "verification_request_id")); err == nil && g.server.registrationProxyLeaseEnabled() && validRegistrationProxyLease(wait.ProxyLease) {
route := proxyRuntimeLeaseRoute(wait.ProxyLease, WAProxyRoute{Source: waProxySourceSystemCommon, PolicyMode: waProxyModeCommon})
proxied, err := engine.WithProxyURL(route.ProxyURL)
if err != nil {
return nil, WAProxyRoute{}, false, registrationProxyLease{}, err
}
return proxied, route, true, wait.ProxyLease, nil
}
route, useProxy, err := g.registrationSubmitProxyRoute(ctx, payload)
if err != nil {
return nil, WAProxyRoute{}, false, err
return nil, WAProxyRoute{}, false, registrationProxyLease{}, err
}
if !useProxy {
return engine, route, false, nil
return engine, route, false, registrationProxyLease{}, nil
}
proxied, err := engine.WithProxyURL(route.ProxyURL)
if err != nil {
return nil, WAProxyRoute{}, false, err
return nil, WAProxyRoute{}, false, registrationProxyLease{}, err
}
return proxied, route, true, nil
return proxied, route, true, registrationProxyLease{}, nil
}

func (g *actionGateway) registrationSubmitProxyRoute(ctx context.Context, payload map[string]any) (WAProxyRoute, bool, error) {
Expand Down
Loading
Loading