v1.0.22
This commit is contained in:
1 parent
aefa5e7925
commit
b55e1e5aad
107 files changed
+2193
-443
No files matched your search
@@ -13,10 +13,14 @@ export interface GridItem {
|
||||
|
||||
export interface DashboardPreferences {
|
||||
gridLayout: GridItem[];
|
||||
locked: boolean;
|
||||
viewMode: 'grid' | 'list';
|
||||
}
|
||||
|
||||
const defaultPreferences: DashboardPreferences = {
|
||||
gridLayout: []
|
||||
gridLayout: [],
|
||||
locked: false,
|
||||
viewMode: 'grid'
|
||||
};
|
||||
|
||||
// Environment info from API
|
||||
@@ -147,16 +151,20 @@ function createDashboardStore() {
|
||||
const data = await response.json();
|
||||
// Handle migration from old format
|
||||
if (data.gridLayout && Array.isArray(data.gridLayout)) {
|
||||
set({ gridLayout: data.gridLayout });
|
||||
set({
|
||||
gridLayout: data.gridLayout,
|
||||
locked: data.locked ?? false,
|
||||
viewMode: data.viewMode ?? 'grid'
|
||||
});
|
||||
} else {
|
||||
set({ gridLayout: [] });
|
||||
set(defaultPreferences);
|
||||
}
|
||||
} else {
|
||||
set({ gridLayout: [] });
|
||||
set(defaultPreferences);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load dashboard preferences:', error);
|
||||
set({ gridLayout: [] });
|
||||
set(defaultPreferences);
|
||||
} finally {
|
||||
// Always mark as initialized so saves can proceed
|
||||
initialized = true;
|
||||
@@ -206,6 +214,24 @@ function createDashboardStore() {
|
||||
return newPrefs;
|
||||
});
|
||||
},
|
||||
setLocked: (locked: boolean) => {
|
||||
update(prefs => {
|
||||
const newPrefs = { ...prefs, locked };
|
||||
if (initialized) {
|
||||
scheduleSave(newPrefs);
|
||||
}
|
||||
return newPrefs;
|
||||
});
|
||||
},
|
||||
setViewMode: (viewMode: 'grid' | 'list') => {
|
||||
update(prefs => {
|
||||
const newPrefs = { ...prefs, viewMode };
|
||||
if (initialized) {
|
||||
scheduleSave(newPrefs);
|
||||
}
|
||||
return newPrefs;
|
||||
});
|
||||
},
|
||||
reset: () => {
|
||||
initialized = false;
|
||||
set(defaultPreferences);
|
||||
|
||||
@@ -27,8 +27,11 @@ export interface AppSettings {
|
||||
eventPollInterval: number;
|
||||
metricsCollectionInterval: number;
|
||||
compactPorts: boolean;
|
||||
formatLogTimestamps: boolean;
|
||||
externalStackPaths: string[];
|
||||
primaryStackLocation: string | null;
|
||||
defaultGrypeImage: string;
|
||||
defaultTrivyImage: string;
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: AppSettings = {
|
||||
@@ -52,8 +55,11 @@ const DEFAULT_SETTINGS: AppSettings = {
|
||||
eventPollInterval: 60000,
|
||||
metricsCollectionInterval: 30000,
|
||||
compactPorts: false,
|
||||
formatLogTimestamps: false,
|
||||
externalStackPaths: [],
|
||||
primaryStackLocation: null
|
||||
primaryStackLocation: null,
|
||||
defaultGrypeImage: 'anchore/grype:v0.110.0',
|
||||
defaultTrivyImage: 'aquasec/trivy:0.69.3'
|
||||
};
|
||||
|
||||
// Create a writable store for app settings
|
||||
@@ -91,8 +97,11 @@ function createSettingsStore() {
|
||||
eventPollInterval: settings.eventPollInterval ?? DEFAULT_SETTINGS.eventPollInterval,
|
||||
metricsCollectionInterval: settings.metricsCollectionInterval ?? DEFAULT_SETTINGS.metricsCollectionInterval,
|
||||
compactPorts: settings.compactPorts ?? DEFAULT_SETTINGS.compactPorts,
|
||||
formatLogTimestamps: settings.formatLogTimestamps ?? DEFAULT_SETTINGS.formatLogTimestamps,
|
||||
externalStackPaths: settings.externalStackPaths ?? DEFAULT_SETTINGS.externalStackPaths,
|
||||
primaryStackLocation: settings.primaryStackLocation ?? DEFAULT_SETTINGS.primaryStackLocation
|
||||
primaryStackLocation: settings.primaryStackLocation ?? DEFAULT_SETTINGS.primaryStackLocation,
|
||||
defaultGrypeImage: settings.defaultGrypeImage ?? DEFAULT_SETTINGS.defaultGrypeImage,
|
||||
defaultTrivyImage: settings.defaultTrivyImage ?? DEFAULT_SETTINGS.defaultTrivyImage
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
@@ -133,8 +142,11 @@ function createSettingsStore() {
|
||||
eventPollInterval: updatedSettings.eventPollInterval ?? DEFAULT_SETTINGS.eventPollInterval,
|
||||
metricsCollectionInterval: updatedSettings.metricsCollectionInterval ?? DEFAULT_SETTINGS.metricsCollectionInterval,
|
||||
compactPorts: updatedSettings.compactPorts ?? DEFAULT_SETTINGS.compactPorts,
|
||||
formatLogTimestamps: updatedSettings.formatLogTimestamps ?? DEFAULT_SETTINGS.formatLogTimestamps,
|
||||
externalStackPaths: updatedSettings.externalStackPaths ?? DEFAULT_SETTINGS.externalStackPaths,
|
||||
primaryStackLocation: updatedSettings.primaryStackLocation ?? DEFAULT_SETTINGS.primaryStackLocation
|
||||
primaryStackLocation: updatedSettings.primaryStackLocation ?? DEFAULT_SETTINGS.primaryStackLocation,
|
||||
defaultGrypeImage: updatedSettings.defaultGrypeImage ?? DEFAULT_SETTINGS.defaultGrypeImage,
|
||||
defaultTrivyImage: updatedSettings.defaultTrivyImage ?? DEFAULT_SETTINGS.defaultTrivyImage
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -301,6 +313,13 @@ function createSettingsStore() {
|
||||
return newSettings;
|
||||
});
|
||||
},
|
||||
setFormatLogTimestamps: (value: boolean) => {
|
||||
update((current) => {
|
||||
const newSettings = { ...current, formatLogTimestamps: value };
|
||||
saveSettings({ formatLogTimestamps: value });
|
||||
return newSettings;
|
||||
});
|
||||
},
|
||||
setExternalStackPaths: (value: string[]) => {
|
||||
update((current) => {
|
||||
const newSettings = { ...current, externalStackPaths: value };
|
||||
@@ -315,6 +334,20 @@ function createSettingsStore() {
|
||||
return newSettings;
|
||||
});
|
||||
},
|
||||
setDefaultGrypeImage: (value: string) => {
|
||||
update((current) => {
|
||||
const newSettings = { ...current, defaultGrypeImage: value };
|
||||
saveSettings({ defaultGrypeImage: value });
|
||||
return newSettings;
|
||||
});
|
||||
},
|
||||
setDefaultTrivyImage: (value: string) => {
|
||||
update((current) => {
|
||||
const newSettings = { ...current, defaultTrivyImage: value };
|
||||
saveSettings({ defaultTrivyImage: value });
|
||||
return newSettings;
|
||||
});
|
||||
},
|
||||
// Manual refresh from database
|
||||
refresh: loadSettings
|
||||
};
|
||||
@@ -430,3 +463,19 @@ export function getTimeFormat(): TimeFormat {
|
||||
export function getDateFormat(): DateFormat {
|
||||
return cachedDateFormat;
|
||||
}
|
||||
|
||||
// Regex matching ISO 8601 timestamps at the start of log lines (after optional container prefix)
|
||||
// Matches: 2026-01-12T07:47:44.449821093Z or 2026-01-12T07:47:44Z
|
||||
const ISO_TIMESTAMP_RE = /(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.\d+)?Z/g;
|
||||
|
||||
/**
|
||||
* Replace ISO 8601 timestamps in log text with formatted local timestamps.
|
||||
* Uses the user's configured date/time format settings.
|
||||
*/
|
||||
export function formatLogTimestamps(text: string): string {
|
||||
return text.replace(ISO_TIMESTAMP_RE, (_match, dateTimePart) => {
|
||||
const d = new Date(_match);
|
||||
if (isNaN(d.getTime())) return _match;
|
||||
return `${formatDatePart(d)} ${formatTimePart(d, true)}`;
|
||||
});
|
||||
}
|
||||
@@ -264,7 +264,7 @@ function applyEditorFont(fontId: string) {
|
||||
document.documentElement.style.setProperty('--font-editor', fontMeta.family);
|
||||
}
|
||||
|
||||
// Load Google Font dynamically
|
||||
// Load bundled font CSS (fonts are bundled in static/fonts/)
|
||||
function loadGoogleFont(font: FontMeta) {
|
||||
if (!font.googleFont) return;
|
||||
|
||||
@@ -274,7 +274,7 @@ function loadGoogleFont(font: FontMeta) {
|
||||
const link = document.createElement('link');
|
||||
link.id = linkId;
|
||||
link.rel = 'stylesheet';
|
||||
link.href = `https://fonts.googleapis.com/css2?family=${font.googleFont}&display=swap`;
|
||||
link.href = `/fonts/${font.id}/font.css`;
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user