feat: initialize panel access before linking Minecraft accounts

Configure connection and storage before creating or resuming the one-time Owner login. Remove Minecraft prerequisites from setup and preserve established login credentials.

Let staff preview and confirm roles from configured authentication sources using the existing account-link storage and game UUID mapping. Retain in-game code proof for players, add client-version and lobby guidance, and support NodePort passkey origins.
This commit is contained in:
Lemon-miaow committed 2026-10-04 03:05:05 +08:00
1 parent 75845d8f58
commit efbbe27629
59 files changed
+1702 -1564

No files matched your search

+101 -3
View File
@@ -3945,11 +3945,11 @@ paths:
get:
tags: [auth]
operationId: ownerStatus
summary: Report whether an Owner has been bound on this install.
summary: Report whether an Owner has been created on this install.
description: >-
Public, pre-session probe the sign-in page reads on load. Until `felis setup`
binds an Owner, local sign-in is off and every login door answers 403
local_auth_disabled; the page then explains that no Owner exists and how to bind
creates an Owner, local sign-in is off and every login door answers 403
local_auth_disabled; the page then explains that no Owner exists and how to create
one instead of offering the doors. It discloses only whether the install is
still unclaimed, and claiming it needs root on the host. It is not gated on
local_auth_enabled and does not draw on the login doors' per-address rate limit.
@@ -6458,6 +6458,104 @@ paths:
application/json:
schema: { $ref: '#/components/schemas/Error' }
/api/v1/account/link/sources:
get:
tags: [account]
operationId: linkSources
summary: List configured sources for staff game-role designation.
x-felis-face: [external]
x-felis-tier: admin
security: [{ sessionCookie: [] }]
responses:
'200':
description: Sources in game-authentication priority order; no upstream URLs are exposed.
content:
application/json:
schema:
type: object
required: [sources]
properties:
sources:
type: array
items:
type: object
required: [tag, lookup_available]
properties:
tag: { type: string }
lookup_available: { type: boolean }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
/api/v1/account/link/profile:
get:
tags: [account]
operationId: lookupProfile
summary: Look up a role by name or native UUID in a selected authentication source.
description: Staff-only preview; role lookup does not prove account ownership and creates no binding.
x-felis-face: [external]
x-felis-tier: admin
security: [{ sessionCookie: [] }]
parameters:
- { name: source, in: query, required: true, schema: { type: string } }
- { name: profile, in: query, required: true, schema: { type: string } }
responses:
'200':
description: Role found. mc_uuid uses the exact same per-source mapping as game authentication.
content:
application/json:
schema:
type: object
required: [source, name, profile_uuid, mc_uuid, auth_source]
properties:
source: { type: string }
name: { type: string }
profile_uuid: { type: string }
mc_uuid: { type: string, format: uuid }
auth_source: { type: string, enum: [mojang, thirdparty] }
'400': { $ref: '#/components/responses/BadRequest' }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { description: No matching role in the selected source. }
'502': { description: Source returned an invalid or mismatched profile. }
'503': { description: Source unavailable. }
post:
tags: [account]
operationId: linkProfile
summary: Designate a role as the authenticated staff account's game identity.
description: Requires a fresh login factor. Re-queries the native UUID, maps it on the server, and binds only to the caller. Other users' bindings cannot be overwritten. Panel initialization does not require this operation.
x-felis-face: [external]
x-felis-tier: admin
security: [{ sessionCookie: [] }]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [source, profile_uuid]
properties:
source: { type: string }
profile_uuid: { type: string, format: uuid }
responses:
'200':
description: Role linked, idempotently for the same user and UUID.
content:
application/json:
schema:
type: object
required: [linked, mc_uuid, auth_source]
properties:
linked: { type: boolean }
mc_uuid: { type: string, format: uuid }
auth_source: { type: string, enum: [mojang, thirdparty] }
'400': { $ref: '#/components/responses/BadRequest' }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { description: Role no longer exists. }
'409': { description: Role already linked to another user, or reauthentication required. }
'502': { description: Source returned an invalid or mismatched profile. }
'503': { description: Source unavailable. }
/api/v1/account/link/start:
post:
tags: [account]