feat(public): type the brand block so mobile clients get typed theming
All checks were successful
PR Checks / client-build (pull_request) Successful in 9m29s
PR Checks / server-tests (pull_request) Successful in 10m30s
PR Checks / bot-install (pull_request) Successful in 9m21s

Branding is already returned by GET /public/settings (the `brand` block:
name/colors/logo/hero/favicon, per-shard from BRAND_*). §8.6 of the Android
plan asks to confirm it — this makes it a first-class part of the contract so
the app's OpenAPI codegen produces typed branding instead of an untyped map.

- Swagger: add Brand + PublicSettings schemas; /public/settings now references
  PublicSettings (was additionalProperties:true). Brand documents that asset
  fields may be site-relative paths (resolve against the base URL).
- test/publicBrand.test.js locks the brand theming contract the app depends on
  (all fields present; BRAND_* defaults; admin site_title/contact_email
  overrides; accentInt never leaked).

No behavior change to the response — it already carried `brand`; this types and
guards it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NgyHnrNa8WwG3doxvxjuCr
This commit is contained in:
2026-07-19 11:58:01 -05:00
parent 90c8eae20f
commit c35509e8b3
4 changed files with 367 additions and 8 deletions

View File

@@ -1636,16 +1636,15 @@
"tags": [
"Public"
],
"summary": "Public site settings",
"description": "Whitelisted, non-sensitive settings the client needs to render the site.",
"summary": "Public site settings + branding",
"description": "Whitelisted, non-sensitive settings plus the per-shard brand block (name/colors/logo/hero/favicon) a client themes itself from, and derived registration / game-account-signup availability flags.",
"responses": {
"200": {
"description": "Key/value settings",
"description": "Public settings + branding",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true
"$ref": "#/components/schemas/PublicSettings"
}
}
}
@@ -14005,6 +14004,275 @@
}
}
},
"Brand": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "object"
},
"description": {
"type": "string",
"example": "Per-shard branding (BRAND_* env, with admin overrides for name/contactEmail). A client themes itself from this — one instance runs as any shard. Asset fields (logo/hero/favicon) may be site-relative paths; resolve them against the site base URL."
},
"properties": {
"type": "object",
"properties": {
"name": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
},
"example": {
"type": "string",
"example": "Runic Gateway"
}
}
},
"shortName": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
},
"example": {
"type": "string",
"example": "Runic Gateway"
}
}
},
"tagline": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
},
"example": {
"type": "string",
"example": "an independent private Ultima Online shard"
}
}
},
"description": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
}
}
},
"contactEmail": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
},
"example": {
"type": "string",
"example": ""
}
}
},
"url": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
},
"example": {
"type": "string",
"example": ""
}
}
},
"accent": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
},
"example": {
"type": "string",
"example": "#7f99bd"
},
"description": {
"type": "string",
"example": "Seed/accent color (hex) for theming."
}
}
},
"logo": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
},
"example": {
"type": "string",
"example": ""
},
"description": {
"type": "string",
"example": "Logo URL or site-relative path; empty = no logo."
}
}
},
"hero": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
},
"example": {
"type": "string",
"example": "/assets/img/runic-emblem.png"
},
"description": {
"type": "string",
"example": "Hero image URL or site-relative path."
}
}
},
"favicon": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
},
"example": {
"type": "string",
"example": "/assets/img/favicon.ico"
},
"description": {
"type": "string",
"example": "Favicon URL or site-relative path."
}
}
}
}
}
}
},
"PublicSettings": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "object"
},
"description": {
"type": "string",
"example": "Public site settings + branding (GET /public/settings). Whitelisted string settings, plus derived availability flags and the brand block a client themes from. Additional whitelisted keys may appear."
},
"properties": {
"type": "object",
"properties": {
"site_title": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
},
"example": {
"type": "string",
"example": "Runic Gateway"
}
}
},
"status_message": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
},
"example": {
"type": "string",
"example": ""
}
}
},
"maintenance_message": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "string"
},
"example": {
"type": "string",
"example": ""
}
}
},
"registration": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "object"
},
"properties": {
"type": "object",
"properties": {
"password": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "boolean"
}
}
},
"sso": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "boolean"
}
}
}
}
}
}
},
"gameAccountSignup": {
"type": "object",
"properties": {
"type": {
"type": "string",
"example": "boolean"
},
"example": {
"type": "boolean",
"example": false
}
}
},
"brand": {
"$ref": "#/components/schemas/Brand"
}
}
},
"additionalProperties": {
"type": "boolean",
"example": true
}
}
},
"DeletedId": {
"type": "object",
"properties": {