257718e9d6
* fix(ssl): Ensure edit form saves notification_id and template_id in DB. - The SSL edit form was not saving the `notification_id` and `template_id` fields to the PB database when re-assigning the Notification Channel and Alert Template. * chore(partners): add Cloudflare to Ecosystem & Community Partner section - Cloudflare has been added to the Ecosystem & Community Partner list to acknowledge their support and contribution. * ⏳ chore(project): update development status and progress - fix(ssl): ensure edit form saves notification_id and template_id in DB - feat(notifications): add Notifiarr notification channel (planned) * feat: Add Notifiarr to notification channels (UI) - Add Notifiarr as a new channel type in the notification settings. The Notifiarr channel will use the `api_token` field for API key storage in PB. * feat: Add channel_id field for Notifiarr - Add channel_id field for Notifiarr to store record of the discord channel_id into the PB. * feat: Implement Notifiarr notification service - The Notifiarr notification functionality has been implemented. The alert configuration channel will now use the `api_token` and `channel_id` field for send to the Notifiarr API endpoint. * ⏳ chore(project): update development status and progress - feat: Add channel_id field for Notifiarr - feat: Implement Notifiarr notification service Closes: #133 * feat: add SSL history and Notifiarr notification channel schema * Fix: Remove duplicate close button - Removed the duplicate close button from the test email dialog. * feat: Add Gotify to notification channels - Add Gotify as a new channel type in the notification settings. The Gotify channel will use the `api_token` and `server_url` field for API key storage in PB. * feat: Implement Gotify notification service - The Gotify notification functionality has been implemented. The alert configuration channel will now use the `api_token` and `server_url` field for send to the Gotify API endpoint. * ⏳ chore(project): update development status and progress - feat: Add server_url field for Gotify - feat: Implement Gotify notification service * refactory(i18n): Improve internationalization configuration and add new translations - Added multiple new translation items - Updated the two language files: en and zhcn - Added type definitions for new translation items in types/services.ts - Modified some components to use the new translation items * refactory(i18n): Added architecture import function translation for the "About" page - Added translation items related to schema import in English, Chinese, and type definition files - Replaced hard-coded text with new translation items in the AboutSystem component * refactory(i18n): Added architecture import function translation for the "About" page - Added translation items related to schema import in English, Chinese, and type definition files - Replaced hard-coded text with new translation items in the AboutSystem component * refactory(i18n): Add internationalization support for the service statistics card * feat: Add NTFY API token field for Token-based authentication to ntfy server - Add the api_token field for NTFY notifications in both the dialog and service to save it to PB. NTFY schema to include api_token field. * feat(auth): add token support for ntfy - Updated the NTFY service to support API token authentication by adding Authorization Bearer header when api_token is provided. Closes: #89 * refactor(i18n): Added Chinese translations for Create Event and Maintenance - Supplemented missing Chinese translations - Added translation entries related to Create Event and Maintenance in both English and Chinese translation files * chore: deactivate GitHub donation * refactor(i18n): Optimize internationalization configuration and add new translation items - Add internationalization support to the LoadingState component - Implement internationalization translations in the Profile page - Use internationalized text in the TestEmailDialog component - Update English and Chinese translation files by adding new translation entries * docs(readme): deactivate donations, accept only infra support (cloud, domain, hosting) * feat(i18n): Enhanced internationalization support with variable interpolation - Refactored the `t` function in LanguageContext to support multiple parameter combinations - Added variable interpolation feature, allowing variables in translated texts - Optimized translation logic to improve accuracy and performance - Enhanced type safety by using TypeScript generics to ensure correct typing * refactor(i18n): Optimize service pagination internationalization copy - Added service pagination related copy in English and Chinese translation files - Updated type definitions to include new translation fields - Modified the ServicesPagination component to use the newly added translation fields * docs(README_zhcn): Update documentation content and sponsorship policy - Update image links in the documentation - Revise the sponsorship policy to no longer accept individual sponsorships, only enterprise-level collaborations - Update the display method for supporters and partners - Add Cloudflare and DigitalOcean as new partners * feat: Integrate data retention service - Implemented the data retention service in Go (service-operation) that manages cleanup of old records based on configured retention periods. The service runs once per day to ensure outdated data is removed automatically. * refactor: Remove manual cleanup buttons from Data Retention Settings - The three manual cleanup buttons have been removed from the dashboard. - The data retention service now automatically runs once per day, cleaning up older data based on the configured retention settings. --------- Co-authored-by: YiZixuan <sqkkyzx@qq.com> Co-authored-by: YiZixuan <sqkkyzx@outlook.com>
277 lines
8.6 KiB
TypeScript
277 lines
8.6 KiB
TypeScript
|
|
import React, { useState, useEffect } from 'react';
|
|
import { Button } from "@/components/ui/button";
|
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
import { Loader2, Database, Trash2, AlertTriangle, Globe, Server } from "lucide-react";
|
|
import { useToast } from "@/hooks/use-toast";
|
|
import { useLanguage } from "@/contexts/LanguageContext";
|
|
import { authService } from "@/services/authService";
|
|
import { dataRetentionService } from "@/services/dataRetentionService";
|
|
|
|
interface RetentionSettings {
|
|
uptimeRetentionDays: number;
|
|
serverRetentionDays: number;
|
|
}
|
|
|
|
const DataRetentionSettings = () => {
|
|
const { t } = useLanguage();
|
|
const { toast } = useToast();
|
|
const [settings, setSettings] = useState<RetentionSettings>({
|
|
uptimeRetentionDays: 30,
|
|
serverRetentionDays: 30
|
|
});
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isSaving, setIsSaving] = useState(false);
|
|
const [isUptimeShrinking, setIsUptimeShrinking] = useState(false);
|
|
const [isServerShrinking, setIsServerShrinking] = useState(false);
|
|
const [isFullShrinking, setIsFullShrinking] = useState(false);
|
|
const [lastCleanup, setLastCleanup] = useState<string | null>(null);
|
|
|
|
// Check if user is super admin
|
|
const currentUser = authService.getCurrentUser();
|
|
const isSuperAdmin = currentUser?.role === "superadmin";
|
|
|
|
useEffect(() => {
|
|
if (isSuperAdmin) {
|
|
loadSettings();
|
|
}
|
|
}, [isSuperAdmin]);
|
|
|
|
const loadSettings = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
const result = await dataRetentionService.getRetentionSettings();
|
|
if (result) {
|
|
setSettings({
|
|
uptimeRetentionDays: result.uptimeRetentionDays || 30,
|
|
serverRetentionDays: result.serverRetentionDays || 30
|
|
});
|
|
setLastCleanup(result.lastCleanup);
|
|
}
|
|
} catch (error) {
|
|
toast({
|
|
title: "Error",
|
|
description: "Failed to load retention settings",
|
|
variant: "destructive",
|
|
});
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleSave = async () => {
|
|
try {
|
|
setIsSaving(true);
|
|
await dataRetentionService.updateRetentionSettings(settings);
|
|
toast({
|
|
title: "Settings saved",
|
|
description: "Data retention settings have been updated",
|
|
});
|
|
} catch (error) {
|
|
toast({
|
|
title: "Error",
|
|
description: "Failed to save retention settings",
|
|
variant: "destructive",
|
|
});
|
|
} finally {
|
|
setIsSaving(false);
|
|
}
|
|
};
|
|
|
|
const handleUptimeShrink = async () => {
|
|
try {
|
|
setIsUptimeShrinking(true);
|
|
const result = await dataRetentionService.manualUptimeCleanup();
|
|
|
|
toast({
|
|
title: "Uptime cleanup completed",
|
|
description: `Deleted ${result.deletedRecords} old uptime records`,
|
|
});
|
|
|
|
// Reload settings to get updated last cleanup time
|
|
await loadSettings();
|
|
} catch (error) {
|
|
toast({
|
|
title: "Error",
|
|
description: "Failed to perform uptime data cleanup",
|
|
variant: "destructive",
|
|
});
|
|
} finally {
|
|
setIsUptimeShrinking(false);
|
|
}
|
|
};
|
|
|
|
const handleServerShrink = async () => {
|
|
try {
|
|
setIsServerShrinking(true);
|
|
const result = await dataRetentionService.manualServerCleanup();
|
|
|
|
toast({
|
|
title: "Server cleanup completed",
|
|
description: `Deleted ${result.deletedRecords} old server records`,
|
|
});
|
|
|
|
// Reload settings to get updated last cleanup time
|
|
await loadSettings();
|
|
} catch (error) {
|
|
toast({
|
|
title: "Error",
|
|
description: "Failed to perform server data cleanup",
|
|
variant: "destructive",
|
|
});
|
|
} finally {
|
|
setIsServerShrinking(false);
|
|
}
|
|
};
|
|
|
|
const handleFullShrink = async () => {
|
|
try {
|
|
setIsFullShrinking(true);
|
|
const result = await dataRetentionService.manualCleanup();
|
|
|
|
toast({
|
|
title: "Database cleanup completed",
|
|
description: `Deleted ${result.deletedRecords} old records`,
|
|
});
|
|
|
|
// Reload settings to get updated last cleanup time
|
|
await loadSettings();
|
|
} catch (error) {
|
|
toast({
|
|
title: "Error",
|
|
description: "Failed to perform database cleanup",
|
|
variant: "destructive",
|
|
});
|
|
} finally {
|
|
setIsFullShrinking(false);
|
|
}
|
|
};
|
|
|
|
// Show permission notice for admin users
|
|
if (!isSuperAdmin) {
|
|
return (
|
|
<div className="p-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Database className="h-5 w-5" />
|
|
{t("dataRetention", "settings")}
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<Alert className="border-blue-200 bg-blue-50 dark:bg-blue-950 dark:border-blue-800">
|
|
<AlertTriangle className="h-5 w-5 text-blue-600 dark:text-blue-400" />
|
|
<AlertDescription className="text-blue-700 dark:text-blue-300">
|
|
<span className="font-medium">Permission Notice:</span> As an admin user, you do not have access to data retention settings. These settings can only be accessed and modified by Super Admins.
|
|
</AlertDescription>
|
|
</Alert>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="p-4 flex items-center justify-center">
|
|
<Loader2 className="h-6 w-6 animate-spin mr-2" />
|
|
Loading retention settings...
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="p-4 space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Database className="h-5 w-5" />
|
|
Data Retention Settings
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Configure how long monitoring data is kept in the system
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
<div className="space-y-4">
|
|
<div>
|
|
<Label htmlFor="uptimeRetention">Uptime Monitoring Retention (days)</Label>
|
|
<Input
|
|
id="uptimeRetention"
|
|
type="number"
|
|
min="1"
|
|
max="365"
|
|
value={settings.uptimeRetentionDays}
|
|
onChange={(e) => {
|
|
const value = e.target.value;
|
|
if (value === '' || isNaN(Number(value))) {
|
|
return;
|
|
}
|
|
setSettings(prev => ({
|
|
...prev,
|
|
uptimeRetentionDays: Number(value)
|
|
}));
|
|
}}
|
|
className="mt-1"
|
|
/>
|
|
<p className="text-sm text-muted-foreground mt-1">
|
|
Service uptime and incident data older than this will be automatically deleted
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="serverRetention">Server Monitoring Retention (days)</Label>
|
|
<Input
|
|
id="serverRetention"
|
|
type="number"
|
|
min="1"
|
|
max="365"
|
|
value={settings.serverRetentionDays}
|
|
onChange={(e) => {
|
|
const value = e.target.value;
|
|
if (value === '' || isNaN(Number(value))) {
|
|
return;
|
|
}
|
|
setSettings(prev => ({
|
|
...prev,
|
|
serverRetentionDays: Number(value)
|
|
}));
|
|
}}
|
|
className="mt-1"
|
|
/>
|
|
<p className="text-sm text-muted-foreground mt-1">
|
|
Server metrics and process data older than this will be automatically deleted
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{lastCleanup && (
|
|
<Alert>
|
|
<Database className="h-4 w-4" />
|
|
<AlertDescription>
|
|
Last automatic cleanup: {new Date(lastCleanup).toLocaleString()}
|
|
</AlertDescription>
|
|
</Alert>
|
|
)}
|
|
</CardContent>
|
|
<CardFooter className="flex justify-end">
|
|
<Button
|
|
onClick={handleSave}
|
|
disabled={isSaving}
|
|
className="flex items-center gap-2"
|
|
>
|
|
{isSaving ? (
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
) : null}
|
|
Save Changes
|
|
</Button>
|
|
</CardFooter>
|
|
</Card>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DataRetentionSettings; |