feat: overhaul public landing page and backup tooling

This commit is contained in:
2025-09-19 16:07:43 +02:00
parent ad9c6d63d3
commit 2a4d38813f
72 changed files with 4332 additions and 1466 deletions
+3 -1
View File
@@ -58,6 +58,8 @@ export interface AnalyticsData {
event_name: string;
slug: string;
views: number;
downloads?: number;
uniqueVisitors?: number;
}>;
devices: {
desktop: number;
@@ -129,4 +131,4 @@ export const adminService = {
async changePassword(data: { currentPassword: string; newPassword: string }): Promise<void> {
await api.post('/admin/auth/change-password', data);
}
};
};
+8 -1
View File
@@ -21,6 +21,7 @@ export interface PhotoFeedback {
feedback_type: 'rating' | 'like' | 'comment' | 'favorite';
rating?: number;
comment_text?: string;
comment?: string;
guest_name?: string;
guest_email?: string;
is_approved: boolean;
@@ -29,6 +30,7 @@ export interface PhotoFeedback {
updated_at?: string;
filename?: string;
path?: string;
photo_filename?: string;
is_mine?: boolean;
}
@@ -50,6 +52,11 @@ export interface FeedbackResponse {
feedback: PhotoFeedback[];
summary: FeedbackSummary;
my_feedback: MyFeedback;
pagination?: {
page: number;
per_page: number;
total: number;
};
}
export interface FeedbackAnalytics {
@@ -193,4 +200,4 @@ class FeedbackService {
}
}
export const feedbackService = new FeedbackService();
export const feedbackService = new FeedbackService();
+1 -1
View File
@@ -26,7 +26,7 @@ export interface PhotoFilters {
category_id?: number | null;
type?: string;
search?: string;
sort?: 'date' | 'name' | 'size';
sort?: 'date' | 'name' | 'size' | 'rating';
order?: 'asc' | 'desc';
}
@@ -119,8 +119,6 @@ class SecureTokenService {
* Clear expired tokens from cache
*/
clearExpiredTokens(): void {
const now = Date.now();
for (const [key, entry] of this.tokenCache.entries()) {
if (!this.isTokenValid(entry)) {
this.tokenCache.delete(key);
+50 -2
View File
@@ -53,6 +53,18 @@ export interface StorageInfo {
size: number;
}>;
storage_limit: number;
storage_soft_limit: number;
configured_soft_limit: number | null;
recommended_soft_limit: number | null;
soft_limit_configured: boolean;
disk_total: number | null;
disk_free: number | null;
disk_available: number | null;
disk_total_raw: number | null;
disk_free_raw: number | null;
disk_available_raw: number | null;
disk_metrics_reliable: boolean;
disk_override_source: 'env' | 'settings' | null;
}
export interface SystemStatus {
@@ -102,6 +114,20 @@ export interface SystemStatus {
timestamp: string;
}
export interface PublicSiteBranding {
companyName: string | null;
companyTagline: string | null;
supportEmail: string | null;
logoUrl: string | null;
footerText: string | null;
colors: {
primary: string;
accent: string;
background: string;
text: string;
};
}
export const settingsService = {
// Get all settings
async getAllSettings(): Promise<Record<string, any>> {
@@ -120,6 +146,28 @@ export const settingsService = {
await api.put('/admin/settings/branding', settings);
},
async getPublicSiteDefaults(): Promise<{ html: string; css: string; baseCss: string; branding?: PublicSiteBranding; meta?: { title?: string } }> {
const response = await api.get('/admin/settings/public-site/default');
return response.data;
},
async resetPublicSite(): Promise<{ html: string; css: string; baseCss?: string; branding?: PublicSiteBranding }> {
const response = await api.post('/admin/settings/public-site/reset');
return response.data;
},
async updatePublicSite(settings: {
enabled: boolean;
html: string;
css: string;
}): Promise<void> {
await api.put('/admin/settings/general', {
general_public_site_enabled: settings.enabled,
general_public_site_html: settings.html,
general_public_site_custom_css: settings.css,
});
},
// Upload logo
async uploadLogo(file: File): Promise<string> {
const formData = new FormData();
@@ -184,7 +232,7 @@ export const settingsService = {
// Determine the endpoint based on setting keys
const firstKey = Object.keys(settings)[0];
let endpoint = '/admin/settings/general';
if (firstKey?.startsWith('security_')) {
endpoint = '/admin/settings/security';
} else if (firstKey?.startsWith('analytics_')) {
@@ -250,4 +298,4 @@ export const settingsService = {
const response = await api.get<PasswordComplexitySettings>('/admin/settings/password/complexity');
return response.data;
}
};
};