mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-12 03:36:59 +00:00
feat(app-store): editorial hero, category rail, security scan signal per tile (#679)
* feat(app-store): editorial hero, category rail, security scan signal per tile Rework the App Store view into an editorial layout with a 180px category rail, a featured-template hero, and compact tiles that surface star counts and vulnerability scan status at a glance. The deploy sheet splits into Essentials (one-click deploy) and Advanced (full port/volume/env control) tabs. Backend enriches /api/templates with scan_status, scan_cve_count, and a featured flag computed from the highest-star template with recorded stars. Scan lookups use a single batched SQL query to avoid N+1 round-trips. * fix(app-store): split firstSentence helper into its own module The firstSentence helper lived alongside the TemplateLogo component, which violates react-refresh/only-export-components — a file that exports a component must not also export non-component values, or Fast Refresh cannot establish an HMR boundary. Move the helper into appstore/util.ts and update the two import sites.
This commit is contained in:
@@ -2508,6 +2508,44 @@ export class DatabaseService {
|
||||
);
|
||||
}
|
||||
|
||||
public getLatestScanSummaryByImageRefs(
|
||||
nodeId: number,
|
||||
imageRefs: string[],
|
||||
): Map<string, { total: number; critical: number; high: number; scannedAt: number }> {
|
||||
const summary = new Map<string, { total: number; critical: number; high: number; scannedAt: number }>();
|
||||
if (imageRefs.length === 0) return summary;
|
||||
|
||||
const placeholders = imageRefs.map(() => '?').join(',');
|
||||
const rows = this.db
|
||||
.prepare(
|
||||
`SELECT image_ref, total_vulnerabilities, critical_count, high_count, scanned_at
|
||||
FROM vulnerability_scans v1
|
||||
WHERE node_id = ?
|
||||
AND image_ref IN (${placeholders})
|
||||
AND scanned_at = (
|
||||
SELECT MAX(scanned_at) FROM vulnerability_scans v2
|
||||
WHERE v2.node_id = v1.node_id AND v2.image_ref = v1.image_ref
|
||||
)`,
|
||||
)
|
||||
.all(nodeId, ...imageRefs) as Array<{
|
||||
image_ref: string;
|
||||
total_vulnerabilities: number;
|
||||
critical_count: number;
|
||||
high_count: number;
|
||||
scanned_at: number;
|
||||
}>;
|
||||
|
||||
for (const row of rows) {
|
||||
summary.set(row.image_ref, {
|
||||
total: row.total_vulnerabilities,
|
||||
critical: row.critical_count,
|
||||
high: row.high_count,
|
||||
scannedAt: row.scanned_at,
|
||||
});
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
public getLatestScanByDigest(digest: string, scannersUsed?: string): VulnerabilityScan | null {
|
||||
if (!digest) return null;
|
||||
if (scannersUsed) {
|
||||
|
||||
Reference in New Issue
Block a user