feat(account): seed the time zone from the client's detected offset at creation
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m18s
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m18s
A new account's time_zone defaulted to 'UTC' until the player saved a profile, so the robot's sleep window and the turn-timeout away-window sweeper — both anchored to the account zone via account.ResolveZone — ran on UTC for every fresh player, skewing robot-game timing until a manual Settings save. Seed the zone at creation instead, from the client's detected "±HH:MM" offset. - Carry browser_tz on the three account-creating auth requests (TelegramLoginRequest, GuestLoginRequest, EmailRequestRequest — the email account is provisioned at the code-request step, not at login) through the fbs envelope (+ Go/TS codegen), the gateway transcode + backend client, and the backend auth handlers into ProvisionTelegram / ProvisionGuest / ProvisionEmail. - create() now writes time_zone explicitly: the validated detected offset, or 'UTC' (equal to the column default) when absent or malformed — deterministic, never guessed. The column is already NOT NULL DEFAULT 'UTC', so no migration is needed and existing accounts keep 'UTC'. An existing account is never overwritten on re-login. - A detected zero offset is stored as "+00:00" (the zone is known and equals UTC), distinct from the "UTC" default that means "unknown" — which the feedback console's three-zone Filed display already reflects. - Guard the guest handler against an empty payload (the bootstrap historically carried none) so it degrades to no-seed rather than panicking in GetRootAs*. - Tests: zone seeding across Telegram/guest/email plus the "+00:00"/malformed/empty cases and the not-overwrite rule; codec round-trip for the three auth encoders. ARCHITECTURE + FUNCTIONAL(+ru) updated.
This commit is contained in:
@@ -18,12 +18,14 @@ import (
|
||||
|
||||
// telegramAuthRequest carries the identity the connector extracted from a
|
||||
// validated initData payload. Username, FirstName and LanguageCode seed a
|
||||
// brand-new account's display name and language (first contact only).
|
||||
// brand-new account's display name and language; BrowserTZ (the client's detected
|
||||
// "±HH:MM" UTC offset) seeds its time zone (first contact only).
|
||||
type telegramAuthRequest struct {
|
||||
ExternalID string `json:"external_id"`
|
||||
Username string `json:"username"`
|
||||
FirstName string `json:"first_name"`
|
||||
LanguageCode string `json:"language_code"`
|
||||
BrowserTZ string `json:"browser_tz"`
|
||||
}
|
||||
|
||||
// handleTelegramAuth provisions (or finds) the account bound to a Telegram
|
||||
@@ -35,7 +37,7 @@ func (s *Server) handleTelegramAuth(c *gin.Context) {
|
||||
abortBadRequest(c, "external_id is required")
|
||||
return
|
||||
}
|
||||
acc, created, err := s.accounts.ProvisionTelegram(c.Request.Context(), req.ExternalID, req.LanguageCode, req.Username, req.FirstName)
|
||||
acc, created, err := s.accounts.ProvisionTelegram(c.Request.Context(), req.ExternalID, req.LanguageCode, req.Username, req.FirstName, req.BrowserTZ)
|
||||
if err != nil {
|
||||
s.abortErr(c, err)
|
||||
return
|
||||
@@ -97,9 +99,21 @@ func (s *Server) handlePushTarget(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// handleGuestAuth provisions a fresh ephemeral guest account and mints a session.
|
||||
// guestAuthRequest carries the guest bootstrap's optional time-zone seed: BrowserTZ
|
||||
// (the client's detected "±HH:MM" UTC offset) is written to the new guest account's
|
||||
// time zone, so robot timing is anchored to the player's zone from the first game.
|
||||
type guestAuthRequest struct {
|
||||
BrowserTZ string `json:"browser_tz"`
|
||||
}
|
||||
|
||||
// handleGuestAuth provisions a fresh ephemeral guest account and mints a session,
|
||||
// seeding its time zone from the optional detected browser offset.
|
||||
func (s *Server) handleGuestAuth(c *gin.Context) {
|
||||
acc, err := s.accounts.ProvisionGuest(c.Request.Context())
|
||||
// The body is optional: an absent or malformed one simply yields no time-zone seed
|
||||
// (the account keeps the UTC default), so a bind error must not fail the bootstrap.
|
||||
var req guestAuthRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
acc, err := s.accounts.ProvisionGuest(c.Request.Context(), req.BrowserTZ)
|
||||
if err != nil {
|
||||
s.abortErr(c, err)
|
||||
return
|
||||
@@ -107,9 +121,12 @@ func (s *Server) handleGuestAuth(c *gin.Context) {
|
||||
s.mintSession(c, acc)
|
||||
}
|
||||
|
||||
// emailRequest is an email-login code request.
|
||||
// emailRequest is an email-login code request. BrowserTZ (the client's detected
|
||||
// "±HH:MM" UTC offset) seeds the time zone of an account provisioned here on first
|
||||
// contact (the email account is created at the request step, not at login).
|
||||
type emailRequest struct {
|
||||
Email string `json:"email"`
|
||||
Email string `json:"email"`
|
||||
BrowserTZ string `json:"browser_tz"`
|
||||
}
|
||||
|
||||
// handleEmailRequest issues a login confirm-code to the email. It always reports
|
||||
@@ -121,7 +138,7 @@ func (s *Server) handleEmailRequest(c *gin.Context) {
|
||||
abortBadRequest(c, "email is required")
|
||||
return
|
||||
}
|
||||
if _, err := s.emails.RequestLoginCode(c.Request.Context(), req.Email); err != nil {
|
||||
if _, err := s.emails.RequestLoginCode(c.Request.Context(), req.Email, req.BrowserTZ); err != nil {
|
||||
s.abortErr(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user