feat(api): add admin API for the SysAdmin-set auto-update maintenance window
Two admin-tier routes read and set a single platform-wide maintenance
window for the auto-update subsystem (decision core internal/updates):
GET /api/v1/updates/window
PUT /api/v1/updates/window
The window is stored as JSON {"start","end"} (RFC3339, or null when
unset) under the platform_settings key "update_window", reusing the
existing GetSetting/SetSetting KV seam -- no new Repo method, no
migration. Pointer times keep "unset" (null) distinct from a real
instant on both decode and encode; a never-set and an explicitly
cleared window both read back as {null,null}.
Validation mirrors the core's fail-closed Window: a window is either
fully set (both ends, end strictly after start) or fully cleared (both
null). A half-set, inverted, or empty-interval body is 400 and is never
persisted. Reads treat only a missing key as unset (ErrNotFound -> 200
nulls); any other store error 500s rather than fail open.
This is API + PERSISTENCE ONLY. Nothing consumes the stored window yet
-- the runner, the ReleaseSource/Notifier/Applier executors, and the
scheduler CronJob remain INTEGRATION-ONLY. Setting a window changes no
behavior until those land; it is the durable input they will read.
Nothing here force-updates ("不要强制自动更新").
This commit is contained in:
4 files changed
+392
No files matched your search
@@ -167,6 +167,26 @@ components:
|
||||
type: string
|
||||
description: Correlates the response with server logs (withRequestID middleware).
|
||||
|
||||
UpdateWindow:
|
||||
type: object
|
||||
description: >
|
||||
The SysAdmin-set auto-update maintenance window (internal/api/handlers_updates.go
|
||||
updateWindow). An absolute [start,end) interval during which Felis may apply a
|
||||
Scheduled component's update to itself; both ends null means unset (no apply is
|
||||
ever opened). Keys are always present; their values are null when unset.
|
||||
required: [start, end]
|
||||
properties:
|
||||
start:
|
||||
type: string
|
||||
format: date-time
|
||||
nullable: true
|
||||
description: Window start (RFC3339, inclusive), or null when unset.
|
||||
end:
|
||||
type: string
|
||||
format: date-time
|
||||
nullable: true
|
||||
description: Window end (RFC3339, exclusive), or null when unset.
|
||||
|
||||
PasskeyCredential:
|
||||
type: object
|
||||
description: >
|
||||
@@ -1464,6 +1484,67 @@ paths:
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
/api/v1/updates/window:
|
||||
get:
|
||||
tags: [admin-updates]
|
||||
operationId: getUpdateWindow
|
||||
summary: Read the SysAdmin-set auto-update maintenance window (admin).
|
||||
description: >-
|
||||
The single platform-wide maintenance window during which Felis may apply a
|
||||
Scheduled component's update to itself (decision core internal/updates). An
|
||||
unset window — never set, or explicitly cleared — reads back as
|
||||
{start:null,end:null}. API+persistence only: nothing consumes the window
|
||||
until the INTEGRATION runner and executors are wired, so setting it changes
|
||||
no behavior yet.
|
||||
x-felis-face: [external]
|
||||
x-felis-tier: admin
|
||||
security: [{ accessJWT: [] }]
|
||||
responses:
|
||||
'200':
|
||||
description: The current maintenance window (both ends null when unset).
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateWindow'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
put:
|
||||
tags: [admin-updates]
|
||||
operationId: setUpdateWindow
|
||||
summary: Set or clear the SysAdmin auto-update maintenance window (admin).
|
||||
description: >-
|
||||
Persist the maintenance window as an absolute [start,end) interval. Both
|
||||
ends must be set with end strictly after start, or both null to clear the
|
||||
window to unset. A half-set (exactly one end) or inverted/empty (end not
|
||||
after start) body is rejected 400, mirroring the decision core's fail-closed
|
||||
Window so a malformed schedule can never be stored. No forced auto-update:
|
||||
setting a window only permits an apply inside it; outside, a Scheduled
|
||||
component degrades to notify.
|
||||
x-felis-face: [external]
|
||||
x-felis-tier: admin
|
||||
security: [{ accessJWT: [] }]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateWindow'
|
||||
responses:
|
||||
'200':
|
||||
description: The stored maintenance window (echoed back).
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateWindow'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
|
||||
/api/v1/fleet:
|
||||
get:
|
||||
tags: [admin-servers]
|
||||
|
||||
Reference in new issue
Block a user