feat: user service

This commit is contained in:
Ilia Denisov
2026-04-10 19:05:02 +02:00
committed by GitHub
parent 710bad712e
commit 23ffcb7535
140 changed files with 33418 additions and 952 deletions
+103 -32
View File
@@ -1,5 +1,9 @@
# User Service Implementation Plan
This plan has been already implemented and stays here for historical reasons.
It should NOT be threated as source of truth for service functionality.
## Planning Principles
This plan is aligned with the current repository architecture and is written
@@ -17,7 +21,9 @@ Execution priorities:
- keep the first version storage-agnostic at the domain boundary even if Redis
is the initial backend
## Stage 01 — Freeze Vocabulary, Contracts, and Cross-Service Ownership
## ~~Stage 01~~ — Freeze Vocabulary, Contracts, and Cross-Service Ownership
Status: implemented.
### Goal
@@ -38,8 +44,10 @@ Remove naming ambiguity and freeze the service boundary before implementation.
- workflow and history in `Geo Profile Service`
- Freeze the auth-facing internal REST endpoints already reserved by
`Auth / Session Service`.
- Freeze the need for create-only registration context on
`EnsureUserByEmail`.
- Freeze the exact create-only registration context shape on
`EnsureUserByEmail`:
- `preferred_language`
- `time_zone`
### Deliverables
@@ -58,7 +66,9 @@ Remove naming ambiguity and freeze the service boundary before implementation.
- none yet beyond documentation review
## Stage 02 — Define Domain Entities and Redis-Backed Logical State
## ~~Stage 02~~ — Define Domain Entities and Redis-Backed Logical State
Status: implemented.
### Goal
@@ -97,7 +107,9 @@ without revisiting core semantics.
- domain validation tests for required fields
- tests for effective-state evaluation of active versus expired records
## Stage 03 — Implement Auth-Facing Resolution, Ensure, Existence, and E-Mail Blocking
## ~~Stage 03~~ — Implement Auth-Facing Resolution, Ensure, Existence, and E-Mail Blocking
Status: implemented.
### Goal
@@ -122,6 +134,12 @@ Provide the minimum trusted API needed by `Auth / Session Service`.
- trusted internal REST handlers for auth-facing endpoints
- domain services for resolution and block behavior
- Redis-backed storage for user existence and blocked-email subjects
- runnable `cmd/userservice` process using `Gin` and `go-redis/v9`
- durable create path that already materializes:
- opaque `user_id`
- generated `player-<shortid>` race name
- stored `preferred_language` and `time_zone`
- initial free entitlement snapshot
### Exit Criteria
@@ -137,22 +155,26 @@ Provide the minimum trusted API needed by `Auth / Session Service`.
- block by user id on unknown user returns not found
- repeated block calls stay idempotent
## Stage 04 — Add New-User Creation Context from Auth
## ~~Stage 04~~Implement New-User Creation Context from Auth
Status: implemented.
### Goal
Support first-login user creation with initial settings captured at confirm
time.
Tighten the already-implemented first-login create path with stricter semantic
validation.
### Tasks
- Extend `EnsureUserByEmail` contract with create-only registration context:
- Preserve the already-frozen create-only `EnsureUserByEmail`
registration context with:
- `preferred_language`
- `time_zone`
- Validate `preferred_language` as BCP 47.
- Validate `time_zone` as IANA TZ name.
- Generate initial `race_name` in `player-<shortid>` form during creation.
- Initialize the newly created user with:
- Tighten `preferred_language` validation to BCP 47 semantics.
- Tighten `time_zone` validation to IANA TZ semantics.
- Preserve generated initial `race_name` in `player-<shortid>` form during
creation.
- Preserve the newly created user initialization with:
- free entitlement
- no active sanctions
- no custom limits
@@ -161,9 +183,9 @@ time.
### Deliverables
- extended ensure-by-email request model
- create-user domain service
- create-user domain service using the frozen ensure-by-email request model
- generated-race-name helper
- create-path validation for `preferred_language` and `time_zone`
### Exit Criteria
@@ -177,7 +199,9 @@ time.
- existing user ensure ignores create-only registration context
- invalid BCP 47 or IANA inputs are rejected on create path
## Stage 05 — Implement Self-Service Account Read and Split Profile/Settings Mutations
## ~~Stage 05~~ — Implement Self-Service Account Read and Split Profile/Settings Mutations
Status: implemented.
### Goal
@@ -220,7 +244,9 @@ Expose the minimal authenticated account surface routed by `Edge Gateway`.
- `UpdateMySettings` validates BCP 47 and IANA values
- active `profile_update_block` denies both update flows
## Stage 06 — Implement race_name Uniqueness Policy Behind a Dedicated Interface
## ~~Stage 06~~ — Implement race_name Uniqueness Policy Behind a Dedicated Interface
Status: implemented.
### Goal
@@ -256,7 +282,9 @@ Keep `race_name` uniqueness strict and replaceable.
- rename releases the old reservation only after the new one is secured
- failed reservation backend causes mutation to fail closed
## Stage 07 — Implement Entitlement History Plus Materialized Current Snapshot
## ~~Stage 07~~ — Implement Entitlement History Plus Materialized Current Snapshot
Status: implemented.
### Goal
@@ -298,7 +326,9 @@ Support both auditability and fast synchronous entitlement reads.
- free default is created for new users
- extending or revoking access preserves deterministic current-state behavior
## Stage 08 — Implement Sanctions and Limit Records with Active/Effective Evaluation
## ~~Stage 08~~ — Implement Sanctions and Limit Records with Active/Effective Evaluation
Status: implemented.
### Goal
@@ -317,11 +347,23 @@ consumers.
- `profile_update_block`
- Freeze v1 limit catalog:
- `max_owned_private_games`
- `max_active_private_games`
- `max_pending_public_applications`
- `max_pending_private_join_requests`
- `max_pending_private_invites_sent`
- `max_active_game_memberships`
- Freeze supported v1 limit semantics:
- paid effective defaults:
- `max_owned_private_games=3`
- `max_pending_public_applications=10`
- `max_active_game_memberships=10`
- free effective defaults:
- `max_owned_private_games` is omitted
- `max_pending_public_applications=3`
- `max_active_game_memberships=3`
- `max_active_game_memberships` applies only to public games
- `max_pending_public_applications` is the total public-games budget and is
interpreted by `Game Lobby` together with current active public
memberships
- Keep legacy retired limit codes backward-compatible on reads, but reject
them for new trusted limit commands.
- Implement active/effective evaluation with current time.
- Implement trusted explicit commands to apply/remove sanctions and set/remove
limits.
@@ -343,9 +385,14 @@ consumers.
- active sanctions appear in account reads
- expired sanctions and limits stop affecting effective state
- retired legacy limit records are ignored during reads and effective
evaluation
- retired legacy limit codes are rejected by trusted limit commands
- applying and removing sanctions/limits is idempotent where appropriate
## Stage 09 — Implement Lobby Eligibility Snapshot API
## ~~Stage 09~~ — Implement Lobby Eligibility Snapshot API
Status: implemented.
### Goal
@@ -361,6 +408,16 @@ user-level access decisions.
- active lobby-relevant sanctions
- effective lobby-relevant limits
- derived booleans for lobby decisions
- Freeze the lobby-facing effective limit catalog:
- paid users receive `max_owned_private_games=3`,
`max_pending_public_applications=10`, and
`max_active_game_memberships=10`
- free users omit `max_owned_private_games` and receive
`max_pending_public_applications=3` and
`max_active_game_memberships=3`
- `max_pending_public_applications` remains the total public-games budget
consumed together with current active public memberships inside
`Game Lobby`
- Keep the response read-optimized so lobby does not need multiple dependent
calls back into `User Service`.
- Define deterministic not-found behavior.
@@ -381,8 +438,12 @@ user-level access decisions.
- lobby eligibility snapshot reflects paid status, sanctions, and limits
- unknown user returns stable not-found behavior
- derived booleans remain consistent with raw effective state
- free and paid snapshots materialize the reduced three-code effective limit
catalog correctly
## Stage 10 — Implement Geo declared_country Sync Command
## ~~Stage 10~~ — Implement Geo declared_country Sync Command
Status: implemented.
### Goal
@@ -416,7 +477,9 @@ Support the current-country denormalization path owned by `Geo Profile Service`.
- invalid country codes are rejected
- country sync emits the correct auxiliary event after commit
## Stage 11 — Implement Admin Lookup, Filtered Listing, and Explicit Trusted Mutations
## ~~Stage 11~~ — Implement Admin Lookup, Filtered Listing, and Explicit Trusted Mutations
Status: implemented.
### Goal
@@ -462,7 +525,9 @@ operations.
- exact lookups by `user_id`, email, and `race_name` resolve the correct user
- every trusted mutation preserves actor and reason metadata
## Stage 12 — Add Per-Domain-Area Async Events and Observability
## ~~Stage 12~~ — Add Per-Domain-Area Async Events and Observability
Status: implemented.
### Goal
@@ -505,7 +570,9 @@ truth.
- event payloads include minimum required metadata
- observability hooks do not change business behavior
## Stage 13 — Add Contract Tests Against Auth, Lobby, and Geo Expectations
## ~~Stage 13~~ — Add Contract Tests Against Auth, Lobby, and Geo Expectations
Status: implemented.
### Goal
@@ -542,7 +609,9 @@ must satisfy for other services.
- lobby eligibility snapshot reflects paid status, sanctions, and limits
- geo country sync changes only current `declared_country`
## Stage 14 — Add Rollout Notes for Gateway/Auth/OpenAPI Updates and Shared geoip
## ~~Stage 14~~ — Add Rollout Notes for Gateway/Auth/OpenAPI Updates and Shared geoip
Status: implemented.
### Goal
@@ -551,11 +620,13 @@ its intended end-to-end form.
### Tasks
- Document the required `gateway` public `confirm-email-code` addition of
- Document the required `gateway` public `confirm-email-code` dependency on
`time_zone`.
- Document the required `authsession` public OpenAPI preservation of the same
`time_zone` requirement.
- Document that the frozen `authsession -> user` ensure contract requires
create-only `registration_context` with `preferred_language` and
`time_zone`.
- Document the required `authsession` public OpenAPI mirror change.
- Document the required `authsession -> user` ensure contract extension for
create-only registration context.
- Document the required shared `pkg/geoip` package for gateway and geo.
- Document README follow-up updates needed in `gateway` and `geoprofile`.
- Define rollout order so the cross-service contract changes do not land in an