fix(observability): address final review findings

Critical fixes:
- Fix StaleContainer frontend type to match nested backend response shape
- Guard ContainerID[:12] slice against empty/short IDs in ListAllProxies

High-priority fixes:
- Support comma-separated severity/source in event log filtering (IN clause)
- Eliminate N+1 queries in ListAllProxies and FindStaleInstances (pre-load maps)
- Stop leaking internal error messages to API clients (use slog + generic msgs)
This commit is contained in:
2026-03-30 11:47:16 +03:00
parent 7c57c740b4
commit e0a648fb0c
8 changed files with 115 additions and 42 deletions
@@ -21,7 +21,7 @@
);
const displayName = $derived(
`${container.project_name}-${container.stage_name}-${container.image_tag}`
`${container.project_name}-${container.stage_name}-${container.instance.image_tag}`
);
function formatDate(iso: string): string {
@@ -59,14 +59,14 @@
<div class="mt-3 flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-[var(--text-secondary)]">
<span class="inline-flex items-center gap-1">
<IconTag size={12} />
{container.image_tag}
{container.instance.image_tag}
</span>
<span class="inline-flex items-center gap-1">
<IconClock size={12} />
{$t('stale.lastAlive')}: {formatDate(container.last_alive_at)}
{$t('stale.lastAlive')}: {formatDate(container.instance.last_alive_at)}
</span>
<span class="rounded bg-[var(--surface-card-hover)] px-1.5 py-0.5 font-mono text-[10px]">
{container.status}
{container.instance.status}
</span>
</div>
@@ -75,7 +75,7 @@
<button
type="button"
disabled={cleaning}
onclick={() => oncleanup(container.id)}
onclick={() => oncleanup(container.instance.id)}
class="inline-flex items-center gap-1.5 rounded-lg border border-[var(--color-danger)] px-3 py-1.5 text-xs font-medium text-[var(--color-danger)] transition-colors hover:bg-[var(--color-danger-light)] disabled:opacity-50 active:animate-press"
>
<IconTrash size={14} />
+14 -6
View File
@@ -209,15 +209,23 @@ export type ProxyHealthStatus = 'unknown' | 'healthy' | 'unhealthy';
/** A container detected as stale by the backend poller. */
export interface StaleContainer {
id: string;
instance: {
id: string;
stage_id: string;
project_id: string;
container_id: string;
image_tag: string;
subdomain: string;
npm_proxy_id: number;
status: string;
port: number;
last_alive_at: string;
created_at: string;
updated_at: string;
};
project_name: string;
stage_name: string;
image_tag: string;
container_id: string;
status: string;
last_alive_at: string;
days_stale: number;
created_at: string;
}
/** A single step in the validation pipeline. */