feat(messages): extraction des messages importants APT (tâche 5 backlog)
- extractImportantMessages (TDD) : E:/dpkg error → error, W:/GPG → warning, déprecations/EOL → future_major_change ; nettoyage des secrets dans les URLs - recordImportantMessages : dédup par (machine, source, message) non acquitté → maj lastSeenAt, sinon insert (firstSeen/lastSeen) dans important_messages - branché dans refreshMachine (sortie APT) avec snapshotId - routes GET /machines/:id/messages + POST .../:msgId/ack - UI : carte « Messages importants » (badge sévérité + ack) dans le panneau détail tsc 0 · 118 tests · build OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
api,
|
||||
type DockerSettingsView,
|
||||
type DockerStackRow,
|
||||
type ImportantMessageView,
|
||||
type MachineHardwareView,
|
||||
type ProbeResultView,
|
||||
type ProfileManifestView,
|
||||
@@ -834,6 +835,27 @@ function fmtBytes(b: number | null): string {
|
||||
return `${b} o`;
|
||||
}
|
||||
|
||||
function MessagesCard({ machineId }: { machineId: string }) {
|
||||
const [msgs, setMsgs] = useState<ImportantMessageView[]>([]);
|
||||
const load = () => { void api.machineMessages(machineId).then(setMsgs).catch(() => setMsgs([])); };
|
||||
useEffect(load, [machineId]);
|
||||
if (msgs.length === 0) return null;
|
||||
return (
|
||||
<div className="machine-detail-card">
|
||||
<span className="label">Messages importants ({msgs.length})</span>
|
||||
{msgs.slice(0, 8).map((m) => (
|
||||
<div key={m.id} className="docker-service">
|
||||
<DockerBadge status={m.severity === "error" ? "error" : m.severity === "warning" ? "updates_available" : "candidate"} />
|
||||
<span className="docker-service-name mono" title={m.message}>{m.message}</span>
|
||||
<button className="interactive su-viewtoggle-btn" style={{ padding: "2px 7px", fontSize: 11 }}
|
||||
onClick={() => api.ackMessage(machineId, m.id).then(load)}>ack</button>
|
||||
</div>
|
||||
))}
|
||||
{msgs.length > 8 && <span className="machine-placeholder">+{msgs.length - 8} autres…</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function HardwareSection({ machineId }: { machineId: string }) {
|
||||
const [hw, setHw] = useState<MachineHardwareView | null>(null);
|
||||
const [metrics, setMetrics] = useState<MachineMetricsSimple | null>(null);
|
||||
@@ -1049,6 +1071,8 @@ export function MachineDetailPanel({
|
||||
<IconButton icon="cog" label="Profil & proxy (sonde)" active={false} danger={false} primary={false} onClick={() => setConfigOpen(true)} />
|
||||
</div>
|
||||
|
||||
<MessagesCard machineId={machine.id} />
|
||||
|
||||
<div className="machine-detail-cards">
|
||||
<div className="machine-detail-card">
|
||||
<span className="label">System info</span>
|
||||
|
||||
@@ -67,6 +67,8 @@ export const api = {
|
||||
req<MachineView>(`/machines/${id}`, { method: "PATCH", body: JSON.stringify(body) }),
|
||||
probe: (id: string) => req<ProbeResultView>(`/machines/${id}/probe`, { method: "POST" }),
|
||||
machineHardware: (id: string) => req<MachineHardwareView>(`/machines/${id}/hardware`),
|
||||
machineMessages: (id: string) => req<ImportantMessageView[]>(`/machines/${id}/messages`),
|
||||
ackMessage: (id: string, msgId: string) => req<{ ok: boolean }>(`/machines/${id}/messages/${msgId}/ack`, { method: "POST" }),
|
||||
latestMetrics: (id: string) => req<MachineMetricsSimple | null>(`/machines/${id}/metrics`),
|
||||
collectMetrics: (id: string) => req<MachineMetricsSimple>(`/machines/${id}/metrics/collect`, { method: "POST" }),
|
||||
analyzeRepositories: (id: string) => req<AptRepositoriesAnalysis>(`/machines/${id}/apt-repositories`, { method: "POST" }),
|
||||
@@ -193,6 +195,16 @@ export interface UpdateMachineBody {
|
||||
aptProxyUrl?: string | null;
|
||||
}
|
||||
|
||||
export interface ImportantMessageView {
|
||||
id: string;
|
||||
source: string;
|
||||
category: string;
|
||||
severity: "error" | "warning" | "info";
|
||||
message: string;
|
||||
packageName: string | null;
|
||||
lastSeenAt: string;
|
||||
}
|
||||
|
||||
export interface MachineHardwareView {
|
||||
osFamily: string;
|
||||
osVersion: string | null;
|
||||
|
||||
Reference in New Issue
Block a user