feat(update): 主机每日记录组件版本比对,面板更新页展示可用更新与应用命令
This commit is contained in:
28 files changed
+994
-10
No files matched your search
@@ -28,7 +28,10 @@ A grep across `*.md` and `*.go` returns both sets; only the Go ones are seams.
|
||||
|
||||
- `internal/updates/seams.go:32` — `Notifier`. `internal/mail` sends OTP over SMTP,
|
||||
but nothing adapts it to this interface and no in-game channel exists. `felis
|
||||
update` passes nil deliberately: a human typing the command is the notification.
|
||||
update` passes nil deliberately. The notification is the panel instead:
|
||||
`felis-update-check.timer` runs `felis update --record` daily on the host, which
|
||||
stores the report under `platform_settings.update_report`, and **Admin → Updates →
|
||||
Component versions** shows it with the command that applies each update.
|
||||
- `internal/updates/seams.go:43` — `Applier`. Nothing applies an update anywhere. A
|
||||
nil applier is not silent — `Run` records `errNoApplier` against every planned
|
||||
apply, so a mis-scheduled apply is loud rather than lost.
|
||||
|
||||
@@ -383,6 +383,51 @@ components:
|
||||
format: int64
|
||||
description: The freshness limit (26h), shared with `felis db check` and FelisDBBackupStale.
|
||||
|
||||
UpdateReport:
|
||||
type: object
|
||||
description: >
|
||||
The newest version check the host recorded (internal/api/handlers_updates.go
|
||||
updateReportView; the record is internal/updates StatusReport, written by
|
||||
`felis update --record`, which felis-update-check.timer runs daily).
|
||||
required: [report, stale, max_age_seconds]
|
||||
properties:
|
||||
report:
|
||||
type: object
|
||||
nullable: true
|
||||
description: >
|
||||
Null until the first check has been recorded (internal/updates
|
||||
StatusReport).
|
||||
required: [checked_at, felis, components]
|
||||
properties:
|
||||
checked_at: { type: string, format: date-time }
|
||||
felis: { type: string, description: Version of the felis binary that ran the check. }
|
||||
components:
|
||||
type: array
|
||||
items: { $ref: '#/components/schemas/UpdateComponent' }
|
||||
stale:
|
||||
type: boolean
|
||||
description: True when there is no record or it is older than max_age_seconds.
|
||||
max_age_seconds:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The freshness limit (26h).
|
||||
|
||||
UpdateComponent:
|
||||
type: object
|
||||
description: >
|
||||
One component's line. available has a newer stable release (latest);
|
||||
unknown means the release feed could not be read and unreadable that the
|
||||
installed version could not, both with error; pinned never changes by policy.
|
||||
required: [name, state]
|
||||
properties:
|
||||
name: { type: string }
|
||||
current: { type: string, description: Installed version; omitted when unreadable. }
|
||||
latest: { type: string, description: The newer stable release; present only when state is available. }
|
||||
state: { type: string, enum: [current, available, unknown, unreadable, pinned] }
|
||||
selector: { type: string, description: 'The `felis update --<selector>` flag that prints how to apply it; omitted when none.' }
|
||||
note: { type: string, description: What the release lookup learned beyond the version; omitted when none. }
|
||||
error: { type: string, description: Why a version is missing; omitted otherwise. }
|
||||
|
||||
PasskeyCredential:
|
||||
type: object
|
||||
description: >
|
||||
@@ -3308,6 +3353,32 @@ paths:
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
|
||||
/api/v1/updates/report:
|
||||
get:
|
||||
tags: [admin-updates]
|
||||
operationId: getUpdateReport
|
||||
summary: The newest recorded version check of every tracked component (admin).
|
||||
description: >-
|
||||
What `felis update --record` last stored in platform_settings; the
|
||||
installer's felis-update-check.timer runs it daily on the host, where the
|
||||
installed versions are readable. report is null before the first check;
|
||||
stale is true then, and whenever the check is older than max_age_seconds.
|
||||
Read-only: Felis applies no update on its own.
|
||||
x-felis-face: [external]
|
||||
x-felis-tier: admin
|
||||
security: [{ sessionCookie: [] }]
|
||||
responses:
|
||||
'200':
|
||||
description: The newest recorded check and whether it is stale.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateReport'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
|
||||
/api/v1/fleet:
|
||||
get:
|
||||
tags: [admin-servers]
|
||||
|
||||
@@ -247,6 +247,20 @@ against their newest releases; `--k3s`, `--cloudflared`, `--jre` and `--postgres
|
||||
it to one. PostgreSQL is compared within its major, since a minor release is a package
|
||||
update, and a major past its end of life gets a note naming the current one.
|
||||
|
||||
The installer also sets up `felis-update-check.timer`, which runs `felis update --record`
|
||||
once a day around 05:30 (and at boot after a missed run). `--record` stores the result
|
||||
in `platform_settings`, and the panel's **Admin → Updates → Component versions** card
|
||||
shows it: each component's installed and newest version, and for the ones with a newer
|
||||
release the `sudo felis update --<component>` line that prints how to apply it. Felis
|
||||
applies nothing on its own; the installer re-run above is the apply path. The card turns
|
||||
red when the newest record is older than 26 hours, meaning the timer stopped:
|
||||
|
||||
```sh
|
||||
systemctl list-timers felis-update-check.timer
|
||||
journalctl -u felis-update-check -n 50 --no-pager
|
||||
sudo felis update --record # record a fresh check now
|
||||
```
|
||||
|
||||
### PostgreSQL major versions [CODE-ONLY]
|
||||
|
||||
The installer takes the major the distribution ships (13 on EL9) and never moves it. To
|
||||
|
||||
Reference in new issue
Block a user