// SPDX-FileCopyrightText: 2026 Progmasoft // SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 namespace Progmasoft.Progmaweb.Api.Accounts; /// Body of POST /api/v1/accounts/register. /// /// Every field is optional on the wire so that a missing value is reported as a validation error of that field /// instead of a malformed request. /// /// Requested account name; see . /// Email address of the account; see . /// Password in plain text; see . It is hashed before it is stored. /// /// Whether the person accepted the Privacy Policy and the Security Policy; anything but is /// refused. See . /// public sealed record RegisterAccountRequest( string? AccountName, string? Email, string? Password, bool? AcceptPolicies = null); /// Body of POST /api/v1/accounts/login. /// Email address the account was registered with. /// Password in plain text. public sealed record LoginAccountRequest(string? Email, string? Password); /// Public view of an account, returned by the register, login and /me endpoints. /// Account name in its canonical spelling. /// Email address as the owner entered it. /// Moment the account was created, in UTC. public sealed record AccountResponse(string AccountName, string Email, DateTimeOffset CreatedAt); /// An account together with the session that was just opened for it. /// The signed-in account. /// /// Session token for the cookie. It exists only here and in the browser; the store keeps its digest. /// /// Moment the session stops being valid, in UTC. internal sealed record AuthenticatedAccount(AccountRecord Account, string SessionToken, DateTimeOffset ExpiresAt); /// Outcome of asking the store to create an account. internal enum CreateAccountStatus { /// The account was stored. Created, /// Another account already has this name, compared without case. AccountNameUnavailable, /// Another account already has this email address, compared without case. EmailUnavailable, /// Another account is already linked to this Google identity. GoogleSubjectUnavailable } /// Result of . /// Whether the account was stored, and if not, which uniqueness rule refused it. /// The stored account; unless is created. internal sealed record CreateAccountResult(CreateAccountStatus Status, AccountRecord? Account) { /// Creates the result of a stored account. /// The account that was stored. /// A result with the created status and the account. public static CreateAccountResult Created(AccountRecord account) => new(CreateAccountStatus.Created, account); /// Creates the result of a refused account name. /// A result without an account. public static CreateAccountResult AccountNameUnavailable() => new(CreateAccountStatus.AccountNameUnavailable, null); /// Creates the result of a refused email address. /// A result without an account. public static CreateAccountResult EmailUnavailable() => new(CreateAccountStatus.EmailUnavailable, null); /// Creates the result of a refused Google identity. /// A result without an account. public static CreateAccountResult GoogleSubjectUnavailable() => new(CreateAccountStatus.GoogleSubjectUnavailable, null); }