Merge pull request #25 from operacle/develop
feat: Auto logout after password change
This commit is contained in:
@@ -85,7 +85,8 @@ services:
|
|||||||
- [ ] Infrastructure Server Monitoring
|
- [ ] Infrastructure Server Monitoring
|
||||||
- [ ] Operational Status / Public Status Pages
|
- [ ] Operational Status / Public Status Pages
|
||||||
- [ ] Uptime monitoring (TCP, PING, DNS)
|
- [ ] Uptime monitoring (TCP, PING, DNS)
|
||||||
- [ ] User Permission Roles & Service Group
|
- [x] System Setting Panel and Mail Settings
|
||||||
|
- [x] User Permission Roles
|
||||||
- [ ] Notifications (Email/Slack/Discord/Signal)
|
- [ ] Notifications (Email/Slack/Discord/Signal)
|
||||||
- [x] Open-source release with full documentation
|
- [x] Open-source release with full documentation
|
||||||
|
|
||||||
@@ -122,4 +123,8 @@ CheckCle is released under the MIT License.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Star History
|
||||||
|
|
||||||
|
[](https://www.star-history.com/#operacle/checkcle&Date)
|
||||||
|
|
||||||
Stay informed, stay online, with CheckCle! 🌐
|
Stay informed, stay online, with CheckCle! 🌐
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
@@ -10,6 +9,7 @@ import { useToast } from "@/hooks/use-toast";
|
|||||||
import { Eye, EyeOff } from "lucide-react";
|
import { Eye, EyeOff } from "lucide-react";
|
||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import { authService } from "@/services/authService";
|
import { authService } from "@/services/authService";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
// Password change form schema
|
// Password change form schema
|
||||||
const passwordFormSchema = z.object({
|
const passwordFormSchema = z.object({
|
||||||
@@ -34,6 +34,7 @@ export function ChangePasswordForm({ userId }: ChangePasswordFormProps) {
|
|||||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||||
|
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const form = useForm<PasswordFormValues>({
|
const form = useForm<PasswordFormValues>({
|
||||||
resolver: zodResolver(passwordFormSchema),
|
resolver: zodResolver(passwordFormSchema),
|
||||||
@@ -44,12 +45,35 @@ export function ChangePasswordForm({ userId }: ChangePasswordFormProps) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Function to determine which collection the user belongs to
|
||||||
|
const getUserCollection = async (userId: string): Promise<string> => {
|
||||||
|
try {
|
||||||
|
// First try to find the user in the regular users collection
|
||||||
|
await pb.collection('users').getOne(userId);
|
||||||
|
return 'users';
|
||||||
|
} catch (error) {
|
||||||
|
try {
|
||||||
|
// If not found, try the superadmin collection
|
||||||
|
await pb.collection('_superusers').getOne(userId);
|
||||||
|
return '_superusers';
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('User not found in any collection');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async function onSubmit(data: PasswordFormValues) {
|
async function onSubmit(data: PasswordFormValues) {
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log("Starting password change for user:", userId);
|
||||||
|
|
||||||
|
// Determine which collection the user belongs to
|
||||||
|
const collection = await getUserCollection(userId);
|
||||||
|
console.log("User found in collection:", collection);
|
||||||
|
|
||||||
// PocketBase requires the old password along with the new one
|
// PocketBase requires the old password along with the new one
|
||||||
await pb.collection('users').update(userId, {
|
await pb.collection(collection).update(userId, {
|
||||||
oldPassword: data.currentPassword,
|
oldPassword: data.currentPassword,
|
||||||
password: data.newPassword,
|
password: data.newPassword,
|
||||||
passwordConfirm: data.confirmPassword,
|
passwordConfirm: data.confirmPassword,
|
||||||
@@ -60,17 +84,31 @@ export function ChangePasswordForm({ userId }: ChangePasswordFormProps) {
|
|||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "Password updated",
|
title: "Password updated",
|
||||||
description: "Your password has been changed successfully.",
|
description: "Your password has been changed successfully. You will be logged out in 3 seconds for security.",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset the form
|
// Reset the form
|
||||||
form.reset();
|
form.reset();
|
||||||
|
|
||||||
|
// Auto logout after successful password change
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log("Auto logout after password change");
|
||||||
|
authService.logout();
|
||||||
|
navigate("/login");
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Password change error:", error);
|
console.error("Password change error:", error);
|
||||||
|
|
||||||
let errorMessage = "Failed to update password. Please try again.";
|
let errorMessage = "Failed to update password. Please try again.";
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
errorMessage = error.message;
|
if (error.message.includes("Failed to authenticate")) {
|
||||||
|
errorMessage = "Current password is incorrect. Please try again.";
|
||||||
|
} else if (error.message.includes("User not found")) {
|
||||||
|
errorMessage = "User account not found. Please contact your administrator.";
|
||||||
|
} else {
|
||||||
|
errorMessage = error.message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Switch } from "@/components/ui/switch";
|
|
||||||
import { FormControl, FormField, FormItem, FormLabel } from "@/components/ui/form";
|
import { FormControl, FormField, FormItem, FormLabel } from "@/components/ui/form";
|
||||||
import { useLanguage } from "@/contexts/LanguageContext";
|
import { useLanguage } from "@/contexts/LanguageContext";
|
||||||
import { SettingsTabProps } from "./types";
|
import { SettingsTabProps } from "./types";
|
||||||
@@ -48,7 +47,7 @@ const SystemSettingsTab: React.FC<SettingsTabProps> = ({ form, isEditing, settin
|
|||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
disabled={!isEditing}
|
disabled={!isEditing}
|
||||||
placeholder="https://localhost:8090"
|
placeholder="https://pb-api.k8sops.asia"
|
||||||
className="bg-background border-input text-foreground placeholder:text-muted-foreground disabled:bg-muted disabled:text-muted-foreground"
|
className="bg-background border-input text-foreground placeholder:text-muted-foreground disabled:bg-muted disabled:text-muted-foreground"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -57,72 +56,6 @@ const SystemSettingsTab: React.FC<SettingsTabProps> = ({ form, isEditing, settin
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="meta.senderName"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel className="text-sm font-medium text-foreground">
|
|
||||||
{t("senderName", "settings")}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
disabled={!isEditing}
|
|
||||||
placeholder="System Administrator"
|
|
||||||
className="bg-background border-input text-foreground placeholder:text-muted-foreground disabled:bg-muted disabled:text-muted-foreground"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="meta.senderAddress"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel className="text-sm font-medium text-foreground">
|
|
||||||
{t("senderEmail", "settings")}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
disabled={!isEditing}
|
|
||||||
placeholder="admin@example.com"
|
|
||||||
type="email"
|
|
||||||
className="bg-background border-input text-foreground placeholder:text-muted-foreground disabled:bg-muted disabled:text-muted-foreground"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center space-x-3 pt-4">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="meta.hideControls"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
|
||||||
<FormControl>
|
|
||||||
<Switch
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
disabled={!isEditing}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel className="text-sm font-medium text-foreground cursor-pointer">
|
|
||||||
{t("hideControls", "settings")}
|
|
||||||
</FormLabel>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ export const aboutTranslations: AboutTranslations = {
|
|||||||
viewDocumentation: "Dokumentation ansehen",
|
viewDocumentation: "Dokumentation ansehen",
|
||||||
followOnX: "Auf X folgen",
|
followOnX: "Auf X folgen",
|
||||||
joinDiscord: "Discord beitreten",
|
joinDiscord: "Discord beitreten",
|
||||||
quickActions: "Quick Actions",
|
quickActions: "Schnelle Aktionen",
|
||||||
quickActionsDescription: "Access common monitoring operations and features quickly. Select an action below to get started.",
|
quickActionsDescription: "Greifen Sie schnell auf gängige Überwachungsvorgänge und -funktionen zu. Wählen Sie unten eine Aktion aus, um loszulegen.",
|
||||||
quickTips: "Quick Tips",
|
quickTips: "Schnelle Tipps",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,19 +11,19 @@ export const commonTranslations: CommonTranslations = {
|
|||||||
goodMorning: "Guten Morgen",
|
goodMorning: "Guten Morgen",
|
||||||
goodAfternoon: "Guten Nachmittag",
|
goodAfternoon: "Guten Nachmittag",
|
||||||
goodEvening: "Guten Abend",
|
goodEvening: "Guten Abend",
|
||||||
profile: "Profile",
|
profile: "Profil",
|
||||||
settings: "Settings",
|
settings: "Einstellungen",
|
||||||
documentation: "Documentation",
|
documentation: "Dokumentation",
|
||||||
notifications: "Notifications",
|
notifications: "Benachrichtigungen",
|
||||||
close: "Close",
|
close: "Schließen",
|
||||||
cancel: "Cancel",
|
cancel: "Abbrechen",
|
||||||
view: "View",
|
view: "Anzeigen",
|
||||||
edit: "Edit",
|
edit: "Bearbeiten",
|
||||||
delete: "Delete",
|
delete: "Löschen",
|
||||||
status: "Status",
|
status: "Status",
|
||||||
time: "Time",
|
time: "Zeit",
|
||||||
title: "Title",
|
title: "Titel",
|
||||||
description: "Description",
|
description: "Beschreibung",
|
||||||
success: "Success",
|
success: "Erfolg",
|
||||||
error: "Error",
|
error: "Fehler",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,55 +1,54 @@
|
|||||||
|
|
||||||
import { IncidentTranslations } from '../types/incident';
|
import { IncidentTranslations } from '../types/incident';
|
||||||
|
|
||||||
export const incidentTranslations: IncidentTranslations = {
|
export const incidentTranslations: IncidentTranslations = {
|
||||||
incidentManagement: 'Incident Management',
|
incidentManagement: 'Vorfallmanagement',
|
||||||
incidentsManagementDesc: 'Track and manage service incidents and their resolutions',
|
incidentsManagementDesc: 'Verfolgen und Verwalten von Servicevorfällen und deren Lösungen',
|
||||||
unresolvedIncidents: 'Unresolved',
|
unresolvedIncidents: 'Ungelöst',
|
||||||
resolvedIncidents: 'Resolved',
|
resolvedIncidents: 'Gelöst',
|
||||||
activeIncidents: 'Active Incidents',
|
activeIncidents: 'Aktive Vorfälle',
|
||||||
criticalIssues: 'Critical Issues',
|
criticalIssues: 'Kritische Probleme',
|
||||||
avgResolutionTime: 'Avg. Resolution Time',
|
avgResolutionTime: 'Durchschn. Lösungszeit',
|
||||||
noIncidents: 'No Active Incidents',
|
noIncidents: 'Keine aktiven Vorfälle',
|
||||||
createIncident: 'Create Incident',
|
createIncident: 'Vorfall erstellen',
|
||||||
investigating: 'Investigating',
|
investigating: 'Untersuchung läuft',
|
||||||
identified: 'Identified',
|
identified: 'Identifiziert',
|
||||||
monitoring: 'Monitoring',
|
monitoring: 'Überwachung',
|
||||||
resolved: 'Resolved',
|
resolved: 'Gelöst',
|
||||||
scheduleIncidentManagement: 'Schedule & Incident Management',
|
scheduleIncidentManagement: 'Zeitplanung & Vorfallmanagement',
|
||||||
incidentName: 'Incident Name',
|
incidentName: 'Vorfallname',
|
||||||
incidentStatus: 'Incident Status',
|
incidentStatus: 'Vorfallstatus',
|
||||||
highPriority: 'High Priority',
|
highPriority: 'Hohe Priorität',
|
||||||
configurationSettings: 'Configuration Settings',
|
configurationSettings: 'Konfigurationseinstellungen',
|
||||||
incidentCreatedSuccess: 'Incident created successfully',
|
incidentCreatedSuccess: 'Vorfall erfolgreich erstellt',
|
||||||
basicInfo: 'Basic Information',
|
basicInfo: 'Grundinformationen',
|
||||||
serviceId: 'Service ID',
|
serviceId: 'Service-ID',
|
||||||
assignedTo: 'Assigned To',
|
assignedTo: 'Zugewiesen an',
|
||||||
unassigned: 'Unassigned',
|
unassigned: 'Nicht zugewiesen',
|
||||||
timeline: 'Timeline',
|
timeline: 'Zeitverlauf',
|
||||||
incidentTime: 'Incident Time',
|
incidentTime: 'Vorfallszeit',
|
||||||
resolutionTime: 'Resolution Time',
|
resolutionTime: 'Lösungszeit',
|
||||||
systems: 'Systems',
|
systems: 'Systeme',
|
||||||
noSystems: 'No systems affected',
|
noSystems: 'Keine betroffenen Systeme',
|
||||||
impactAnalysis: 'Impact Analysis',
|
impactAnalysis: 'Auswirkungsanalyse',
|
||||||
rootCause: 'Root Cause',
|
rootCause: 'Ursache',
|
||||||
resolutionSteps: 'Resolution Steps',
|
resolutionSteps: 'Lösungsschritte',
|
||||||
lessonsLearned: 'Lessons Learned',
|
lessonsLearned: 'Erkenntnisse',
|
||||||
resolutionDetails: 'Resolution Details',
|
resolutionDetails: 'Details zur Lösung',
|
||||||
assignment: 'Assignment',
|
assignment: 'Zuweisung',
|
||||||
download: 'Download',
|
download: 'Herunterladen',
|
||||||
downloadPdf: 'Download PDF',
|
downloadPdf: 'PDF herunterladen',
|
||||||
print: 'Print',
|
print: 'Drucken',
|
||||||
confidentialNote: 'This document is confidential and intended for internal use only.',
|
confidentialNote: 'Dieses Dokument ist vertraulich und nur für den internen Gebrauch bestimmt.',
|
||||||
generatedOn: 'Generated on',
|
generatedOn: 'Erstellt am',
|
||||||
enterResolutionSteps: 'Enter steps taken to resolve the incident',
|
enterResolutionSteps: 'Geben Sie die zur Lösung ergriffenen Schritte ein',
|
||||||
enterLessonsLearned: 'Enter lessons learned from this incident',
|
enterLessonsLearned: 'Geben Sie die aus diesem Vorfall gewonnenen Erkenntnisse ein',
|
||||||
editIncident: 'Edit Incident',
|
editIncident: 'Vorfall bearbeiten',
|
||||||
editIncidentDesc: 'Update details for this incident',
|
editIncidentDesc: 'Details zu diesem Vorfall aktualisieren',
|
||||||
updating: 'Updating...',
|
updating: 'Aktualisiere...',
|
||||||
update: 'Update',
|
update: 'Aktualisieren',
|
||||||
create: 'Create',
|
create: 'Erstellen',
|
||||||
creating: 'Creating...',
|
creating: 'Erstelle...',
|
||||||
configuration: 'Configuration',
|
configuration: 'Konfiguration',
|
||||||
failedToUpdateStatus: 'Failed to update status',
|
failedToUpdateStatus: 'Fehler beim Aktualisieren des Status',
|
||||||
inProgress: 'In Progress',
|
inProgress: 'In Bearbeitung',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,68 +1,67 @@
|
|||||||
|
|
||||||
import { MaintenanceTranslations } from '../types/maintenance';
|
import { MaintenanceTranslations } from '../types/maintenance';
|
||||||
|
|
||||||
export const maintenanceTranslations: MaintenanceTranslations = {
|
export const maintenanceTranslations: MaintenanceTranslations = {
|
||||||
scheduledMaintenance: 'Scheduled Maintenance',
|
scheduledMaintenance: 'Geplante Wartung',
|
||||||
scheduledMaintenanceDesc: 'View and manage planned maintenance windows for your systems and services',
|
scheduledMaintenanceDesc: 'Anzeigen und Verwalten geplanter Wartungsfenster für Ihre Systeme und Dienste',
|
||||||
upcomingMaintenance: 'Upcoming',
|
upcomingMaintenance: 'Bevorstehend',
|
||||||
ongoingMaintenance: 'Ongoing',
|
ongoingMaintenance: 'Laufend',
|
||||||
completedMaintenance: 'Completed',
|
completedMaintenance: 'Abgeschlossen',
|
||||||
createMaintenanceWindow: 'Create Maintenance',
|
createMaintenanceWindow: 'Wartung erstellen',
|
||||||
totalScheduledHours: 'Total Scheduled Hours',
|
totalScheduledHours: 'Geplante Gesamtstunden',
|
||||||
maintenanceName: 'Maintenance Name',
|
maintenanceName: 'Wartungsname',
|
||||||
maintenanceStatus: 'Status',
|
maintenanceStatus: 'Status',
|
||||||
scheduledStart: 'Scheduled Start',
|
scheduledStart: 'Geplanter Start',
|
||||||
scheduledEnd: 'Scheduled End',
|
scheduledEnd: 'Geplantes Ende',
|
||||||
affectedServices: 'Affected Services',
|
affectedServices: 'Betroffene Dienste',
|
||||||
impact: 'Impact',
|
impact: 'Auswirkung',
|
||||||
minor: 'Minor',
|
minor: 'Geringfügig',
|
||||||
major: 'Major',
|
major: 'Gravierend',
|
||||||
critical: 'Critical',
|
critical: 'Kritisch',
|
||||||
none: 'None',
|
none: 'Keine',
|
||||||
actions: 'Actions',
|
actions: 'Aktionen',
|
||||||
scheduled: 'Scheduled',
|
scheduled: 'Geplant',
|
||||||
inprogress: 'In Progress',
|
inprogress: 'In Bearbeitung',
|
||||||
completed: 'Completed',
|
completed: 'Abgeschlossen',
|
||||||
cancelled: 'Cancelled',
|
cancelled: 'Abgebrochen',
|
||||||
markAsInProgress: 'Mark as In Progress',
|
markAsInProgress: 'Als in Bearbeitung markieren',
|
||||||
markAsCompleted: 'Mark as Completed',
|
markAsCompleted: 'Als abgeschlossen markieren',
|
||||||
markAsCancelled: 'Mark as Cancelled',
|
markAsCancelled: 'Als abgebrochen markieren',
|
||||||
confirmDelete: 'Confirm Delete',
|
confirmDelete: 'Löschen bestätigen',
|
||||||
deleteMaintenanceConfirmation: 'Are you sure you want to delete this maintenance window?',
|
deleteMaintenanceConfirmation: 'Möchten Sie dieses Wartungsfenster wirklich löschen?',
|
||||||
thisActionCannotBeUndone: 'This action cannot be undone.',
|
thisActionCannotBeUndone: 'Diese Aktion kann nicht rückgängig gemacht werden.',
|
||||||
maintenanceDeleted: 'Maintenance Deleted',
|
maintenanceDeleted: 'Wartung gelöscht',
|
||||||
maintenanceDeletedDesc: 'The maintenance window has been deleted successfully.',
|
maintenanceDeletedDesc: 'Das Wartungsfenster wurde erfolgreich gelöscht.',
|
||||||
errorDeletingMaintenance: 'There was an error deleting the maintenance window.',
|
errorDeletingMaintenance: 'Fehler beim Löschen des Wartungsfensters.',
|
||||||
statusUpdated: 'Status Updated',
|
statusUpdated: 'Status aktualisiert',
|
||||||
maintenanceStatusUpdated: 'Maintenance status has been updated successfully.',
|
maintenanceStatusUpdated: 'Wartungsstatus wurde erfolgreich aktualisiert.',
|
||||||
errorUpdatingMaintenanceStatus: 'There was an error updating the maintenance status.',
|
errorUpdatingMaintenanceStatus: 'Fehler beim Aktualisieren des Wartungsstatus.',
|
||||||
createMaintenance: 'Create Maintenance',
|
createMaintenance: 'Wartung erstellen',
|
||||||
createMaintenanceDesc: 'Schedule a new maintenance window for your services',
|
createMaintenanceDesc: 'Planen Sie ein neues Wartungsfenster für Ihre Dienste',
|
||||||
enterTitle: 'Enter maintenance title',
|
enterTitle: 'Wartungstitel eingeben',
|
||||||
enterDescription: 'Enter detailed description of the maintenance',
|
enterDescription: 'Geben Sie eine detaillierte Beschreibung der Wartung ein',
|
||||||
startTime: 'Start Time',
|
startTime: 'Startzeit',
|
||||||
endTime: 'End Time',
|
endTime: 'Endzeit',
|
||||||
selectDate: 'Select a date',
|
selectDate: 'Datum auswählen',
|
||||||
enterAffectedServices: 'Enter affected services',
|
enterAffectedServices: 'Betroffene Dienste eingeben',
|
||||||
separateServicesWithComma: 'Separate multiple services with commas',
|
separateServicesWithComma: 'Mehrere Dienste durch Kommas trennen',
|
||||||
priority: 'Priority',
|
priority: 'Priorität',
|
||||||
selectPriority: 'Select priority',
|
selectPriority: 'Priorität auswählen',
|
||||||
selectStatus: 'Select status',
|
selectStatus: 'Status auswählen',
|
||||||
selectImpact: 'Select impact',
|
selectImpact: 'Auswirkung auswählen',
|
||||||
notifySubscribers: 'Notify Subscribers',
|
notifySubscribers: 'Abonnenten benachrichtigen',
|
||||||
notifySubscribersDesc: 'Send notifications to all subscribers when this maintenance starts',
|
notifySubscribersDesc: 'Beim Start dieser Wartung eine Benachrichtigung an alle Abonnenten senden',
|
||||||
maintenanceCreated: 'Maintenance Created',
|
maintenanceCreated: 'Wartung erstellt',
|
||||||
maintenanceCreatedDesc: 'The maintenance window has been scheduled successfully.',
|
maintenanceCreatedDesc: 'Das Wartungsfenster wurde erfolgreich geplant.',
|
||||||
errorCreatingMaintenance: 'There was an error creating the maintenance window.',
|
errorCreatingMaintenance: 'Fehler beim Erstellen des Wartungsfensters.',
|
||||||
errorFetchingMaintenanceData: 'There was an error fetching maintenance data.',
|
errorFetchingMaintenanceData: 'Fehler beim Abrufen der Wartungsdaten.',
|
||||||
low: 'Low',
|
low: 'Niedrig',
|
||||||
medium: 'Medium',
|
medium: 'Mittel',
|
||||||
high: 'High',
|
high: 'Hoch',
|
||||||
created: 'Created',
|
created: 'Erstellt',
|
||||||
lastUpdated: 'Last Updated',
|
lastUpdated: 'Zuletzt aktualisiert',
|
||||||
subscribersWillBeNotified: 'Subscribers will be notified when maintenance begins',
|
subscribersWillBeNotified: 'Abonnenten werden benachrichtigt, wenn die Wartung beginnt',
|
||||||
noNotifications: 'No notifications will be sent',
|
noNotifications: 'Es werden keine Benachrichtigungen gesendet',
|
||||||
noScheduledMaintenance: 'No Scheduled Maintenance',
|
noScheduledMaintenance: 'Keine geplanten Wartungen',
|
||||||
noMaintenanceWindows: 'There are no maintenance windows for this period. Create one by clicking the "Create Maintenance" button.',
|
noMaintenanceWindows: 'Für diesen Zeitraum sind keine Wartungsfenster vorhanden. Erstellen Sie eines über die Schaltfläche „Wartung erstellen“.',
|
||||||
maintenanceCreatedSuccess: 'Maintenance window created successfully',
|
maintenanceCreatedSuccess: 'Wartungsfenster erfolgreich erstellt',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ export const servicesTranslations: ServicesTranslations = {
|
|||||||
responseTime: "Antwortzeit",
|
responseTime: "Antwortzeit",
|
||||||
uptime: "Betriebszeit",
|
uptime: "Betriebszeit",
|
||||||
lastChecked: "Zuletzt überprüft",
|
lastChecked: "Zuletzt überprüft",
|
||||||
noServices: "No services match your filter criteria.",
|
noServices: "Keine Dienste entsprechen Ihren Filterkriterien.",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,35 +1,34 @@
|
|||||||
|
|
||||||
import { SettingsTranslations } from '../types/settings';
|
import { SettingsTranslations } from '../types/settings';
|
||||||
|
|
||||||
export const settingsTranslations: SettingsTranslations = {
|
export const settingsTranslations: SettingsTranslations = {
|
||||||
// Tabs
|
// Tabs
|
||||||
systemSettings: "System Settings",
|
systemSettings: "Systemeinstellungen",
|
||||||
mailSettings: "Mail Settings",
|
mailSettings: "E-Mail-Einstellungen",
|
||||||
|
|
||||||
// System Settings
|
// System Settings
|
||||||
appName: "Application Name",
|
appName: "Anwendungsname",
|
||||||
appURL: "Application URL",
|
appURL: "Anwendungs-URL",
|
||||||
senderName: "Sender Name",
|
senderName: "Absendername",
|
||||||
senderEmail: "Sender Email Address",
|
senderEmail: "Absender-E-Mail-Adresse",
|
||||||
hideControls: "Hide Controls",
|
hideControls: "Steuerelemente ausblenden",
|
||||||
|
|
||||||
// Mail Settings
|
// Mail Settings
|
||||||
smtpSettings: "SMTP Configuration",
|
smtpSettings: "SMTP-Konfiguration",
|
||||||
smtpEnabled: "Enable SMTP",
|
smtpEnabled: "SMTP aktivieren",
|
||||||
smtpHost: "SMTP Host",
|
smtpHost: "SMTP-Host",
|
||||||
smtpPort: "SMTP Port",
|
smtpPort: "SMTP-Port",
|
||||||
smtpUsername: "SMTP Username",
|
smtpUsername: "SMTP-Benutzername",
|
||||||
smtpAuthMethod: "Authentication Method",
|
smtpAuthMethod: "Authentifizierungsmethode",
|
||||||
enableTLS: "Enable TLS",
|
enableTLS: "TLS aktivieren",
|
||||||
localName: "Local Name",
|
localName: "Lokaler Name",
|
||||||
|
|
||||||
// Actions and status
|
// Actions and status
|
||||||
save: "Save Changes",
|
save: "Änderungen speichern",
|
||||||
saving: "Saving...",
|
saving: "Speichere...",
|
||||||
settingsUpdated: "Settings updated successfully",
|
settingsUpdated: "Einstellungen erfolgreich aktualisiert",
|
||||||
errorSavingSettings: "Error saving settings",
|
errorSavingSettings: "Fehler beim Speichern der Einstellungen",
|
||||||
testConnection: "Test Connection",
|
testConnection: "Verbindung testen",
|
||||||
testingConnection: "Testing Connection...",
|
testingConnection: "Verbindung wird getestet...",
|
||||||
connectionSuccess: "Connection successful",
|
connectionSuccess: "Verbindung erfolgreich",
|
||||||
connectionFailed: "Connection failed"
|
connectionFailed: "Verbindung fehlgeschlagen"
|
||||||
};
|
};
|
||||||
@@ -43,65 +43,66 @@ export const sslTranslations: SSLTranslations = {
|
|||||||
expirationDate: "Ablaufdatum",
|
expirationDate: "Ablaufdatum",
|
||||||
daysLeft: "Verbleibende Tage",
|
daysLeft: "Verbleibende Tage",
|
||||||
status: "Status",
|
status: "Status",
|
||||||
lastNotified: "Last Notified",
|
lastNotified: "Zuletzt benachrichtigt",
|
||||||
actions: "Actions",
|
actions: "Aktionen",
|
||||||
validFrom: "Valid From",
|
validFrom: "Gültig von",
|
||||||
validUntil: "Valid Until",
|
validUntil: "Gültig bis",
|
||||||
validityDays: "Validity Days",
|
validityDays: "Gültigkeitsdauer Tage",
|
||||||
organization: "Organization",
|
organization: "Organisation",
|
||||||
commonName: "Common Name",
|
commonName: "Allgemeiner Name",
|
||||||
serialNumber: "Serial Number",
|
serialNumber: "Seriennummer",
|
||||||
algorithm: "Algorithm",
|
algorithm: "Algorithmus",
|
||||||
subjectAltNames: "Subject Alternative Names",
|
subjectAltNames: "Thema Alternative Bezeichnungen",
|
||||||
|
|
||||||
// Buttons and actions
|
// Buttons and actions
|
||||||
addDomain: "Add Domain",
|
addDomain: "Domain hinzufügen",
|
||||||
refreshAll: "Refresh All",
|
refreshAll: "Alle aktualisieren",
|
||||||
cancel: "Cancel",
|
cancel: "Abbrechen",
|
||||||
addCertificate: "Add Certificate",
|
addCertificate: "Zertifikat hinzufügen",
|
||||||
check: "Check",
|
check: "Prüfen",
|
||||||
view: "View",
|
view: "Ansehen",
|
||||||
edit: "Edit",
|
edit: "Bearbeiten",
|
||||||
delete: "Delete",
|
delete: "Löschen",
|
||||||
close: "Close",
|
close: "Schließen",
|
||||||
saveChanges: "Save Changes",
|
saveChanges: "Änderungen speichern",
|
||||||
updating: "Updating",
|
updating: "Aktualisiere",
|
||||||
|
|
||||||
// Sections in detail view
|
// Sections in detail view
|
||||||
basicInformation: "Basic Information",
|
basicInformation: "Basisinformationen",
|
||||||
validity: "Validity",
|
validity: "Gültigkeit",
|
||||||
issuerInfo: "Issuer Information",
|
issuerInfo: "Informationen zum Aussteller",
|
||||||
technicalDetails: "Technical Details",
|
technicalDetails: "Technische Details",
|
||||||
monitoringConfig: "Monitoring Configuration",
|
monitoringConfig: "Monitoring-Konfiguration",
|
||||||
recordInfo: "Record Information",
|
recordInfo: "Aufzeichnungsinformationen",
|
||||||
|
|
||||||
// Notifications and messages
|
// Notifications and messages
|
||||||
sslCertificateAdded: "SSL Certificate added successfully",
|
sslCertificateAdded: "SSL-Zertifikat erfolgreich hinzugefügt",
|
||||||
sslCertificateUpdated: "SSL Certificate updated successfully",
|
sslCertificateUpdated: "SSL-Zertifikat erfolgreich aktualisiert",
|
||||||
sslCertificateDeleted: "SSL Certificate deleted successfully",
|
sslCertificateDeleted: "SSL-Zertifikat erfolgreich gelöscht",
|
||||||
sslCertificateRefreshed: "SSL Certificate for {domain} refreshed successfully",
|
sslCertificateRefreshed: "SSL-Zertifikat für {domain} erfolgreich aktualisiert",
|
||||||
allCertificatesRefreshed: "All {count} certificates refreshed successfully",
|
allCertificatesRefreshed: "Alle {count} Zertifikate wurden erfolgreich aktualisiert",
|
||||||
someCertificatesFailed: "{success} certificates refreshed, {failed} failed",
|
someCertificatesFailed: "{success} Zertifikate aktualisiert, {failed} fehlgeschlagen",
|
||||||
failedToAddCertificate: "Failed to add SSL certificate",
|
failedToAddCertificate: "Fehler beim Hinzufügen des SSL-Zertifikats",
|
||||||
failedToLoadCertificates: "Failed to load SSL certificates",
|
failedToLoadCertificates: "Fehler beim Laden der SSL-Zertifikate",
|
||||||
failedToUpdateCertificate: "Failed to update SSL certificate",
|
failedToUpdateCertificate: "Fehler beim Aktualisieren des SSL-Zertifikats",
|
||||||
failedToDeleteCertificate: "Failed to delete SSL certificate",
|
failedToDeleteCertificate: "Fehler beim Löschen des SSL-Zertifikats",
|
||||||
failedToCheckCertificate: "Failed to check SSL certificate",
|
failedToCheckCertificate: "Fehler beim Prüfen des SSL-Zertifikats",
|
||||||
noCertificatesToRefresh: "No certificates to refresh",
|
noCertificatesToRefresh: "Keine Zertifikate zum Aktualisieren vorhanden",
|
||||||
startingRefreshAll: "Starting refresh of {count} certificates",
|
startingRefreshAll: "Starte Aktualisierung von {count} Zertifikaten",
|
||||||
checkingSSLCertificate: "Checking SSL certificate...",
|
checkingSSLCertificate: "SSL-Zertifikat wird überprüft...",
|
||||||
deleteConfirmation: "Are you sure you want to delete the certificate for",
|
deleteConfirmation: "Möchten Sie das Zertifikat für wirklich löschen?",
|
||||||
deleteWarning: "This action cannot be undone. This will permanently delete the certificate.",
|
deleteWarning: "Diese Aktion kann nicht rückgängig gemacht werden. Das Zertifikat wird dauerhaft gelöscht.",
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
unknown: "Unknown",
|
unknown: "Unbekannt",
|
||||||
never: "Never",
|
never: "Nie",
|
||||||
none: "None",
|
none: "Keine",
|
||||||
loadingChannels: "Loading channels...",
|
loadingChannels: "Lade Kanäle...",
|
||||||
noChannelsFound: "No notification channels found",
|
noChannelsFound: "Keine Benachrichtigungskanäle gefunden",
|
||||||
noSSLCertificates: "No SSL certificates found",
|
noSSLCertificates: "Keine SSL-Zertifikate gefunden",
|
||||||
created: "Created",
|
created: "Erstellt",
|
||||||
lastUpdated: "Last Updated",
|
lastUpdated: "Zuletzt aktualisiert",
|
||||||
lastNotification: "Last Notification",
|
lastNotification: "Letzte Benachrichtigung",
|
||||||
collectionId: "Collection ID"
|
collectionId: "Sammlungs-ID"
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|||||||
+43
-66
@@ -2,82 +2,59 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "🚀 Starting Checkcle Installation..."
|
REPO_URL="https://github.com/operacle/checkcle.git"
|
||||||
|
CLONE_DIR="/opt/checkcle"
|
||||||
|
DATA_DIR="/opt/pb_data"
|
||||||
|
PORT=8090
|
||||||
|
|
||||||
# Check if npm is installed
|
echo "🚀 Installing Checkcle from $REPO_URL"
|
||||||
if ! command -v npm &> /dev/null; then
|
|
||||||
echo "📦 npm is not installed. Installing Node.js and npm..."
|
|
||||||
|
|
||||||
# Install Node.js and npm (Ubuntu/Debian-based systems)
|
# Step 1: Check if port 8090 is already in use
|
||||||
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
|
if lsof -i :"$PORT" &>/dev/null; then
|
||||||
sudo apt-get install -y nodejs
|
echo "❗ ERROR: Port $PORT is already in use. Please free the port or change the Docker Compose configuration."
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Clone the repo
|
# Step 2: Ensure Docker is installed
|
||||||
if [ ! -d "checkcle" ]; then
|
if ! command -v docker &> /dev/null; then
|
||||||
echo "📁 Cloning repository..."
|
echo "🔧 Docker not found. Installing Docker..."
|
||||||
git clone https://github.com/operacle/checkcle.git
|
curl -fsSL https://get.docker.com | sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd checkcle
|
# Step 3: Ensure Docker Compose v2 is available
|
||||||
|
if ! docker compose version &> /dev/null; then
|
||||||
|
echo "❗ Docker Compose v2 not found. Please install Docker Compose v2 and rerun this script."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Set up paths
|
# Step 4: Clone the repository
|
||||||
PROJECT_DIR=$(pwd)
|
if [ -d "$CLONE_DIR" ]; then
|
||||||
APP_DIR="$PROJECT_DIR/application"
|
echo "📁 Directory $CLONE_DIR already exists. Pulling latest changes..."
|
||||||
PB_DIR="$PROJECT_DIR/server"
|
git -C "$CLONE_DIR" pull
|
||||||
PB_BINARY="$PB_DIR/pocketbase"
|
else
|
||||||
|
echo "📥 Cloning repo to $CLONE_DIR"
|
||||||
|
git clone "$REPO_URL" "$CLONE_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
# Install web dependencies
|
# Step 5: Create data volume directory if it doesn’t exist
|
||||||
echo "📦 Installing Web Application dependencies..."
|
if [ ! -d "$DATA_DIR" ]; then
|
||||||
cd "$APP_DIR"
|
echo "📁 Creating data volume directory at $DATA_DIR"
|
||||||
npm install
|
sudo mkdir -p "$DATA_DIR"
|
||||||
|
sudo chown "$(whoami)":"$(whoami)" "$DATA_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
# Create systemd service for frontend
|
# Step 6: Start the service
|
||||||
echo "🛠️ Setting up systemd service for Web Application..."
|
cd "$CLONE_DIR"
|
||||||
sudo tee /etc/systemd/system/checkcle-web.service > /dev/null <<EOF
|
echo "📦 Starting Checkcle service with Docker Compose..."
|
||||||
[Unit]
|
docker compose up -d
|
||||||
Description=Checkcle Web Application
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
WorkingDirectory=$APP_DIR
|
|
||||||
ExecStart=/usr/bin/npm run dev
|
|
||||||
Restart=on-failure
|
|
||||||
User=$USER
|
|
||||||
Environment=NODE_ENV=development
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Create systemd service for PocketBase
|
|
||||||
echo "🛠️ Setting up systemd service for PocketBase..."
|
|
||||||
sudo tee /etc/systemd/system/checkcle-pb.service > /dev/null <<EOF
|
|
||||||
[Unit]
|
|
||||||
Description=Checkcle PocketBase Server
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
WorkingDirectory=$PB_DIR
|
|
||||||
ExecStart=$PB_BINARY serve --dir pb_data
|
|
||||||
Restart=on-failure
|
|
||||||
User=$USER
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Reload systemd and start services
|
|
||||||
echo "🔄 Enabling and starting services..."
|
|
||||||
sudo systemctl daemon-reexec
|
|
||||||
sudo systemctl daemon-reload
|
|
||||||
sudo systemctl enable --now checkcle-web
|
|
||||||
sudo systemctl enable --now checkcle-pb
|
|
||||||
|
|
||||||
|
# Step 7: Show success output
|
||||||
echo ""
|
echo ""
|
||||||
echo "✅ All done!"
|
echo "✅ Checkcle has been successfully installed and started."
|
||||||
echo "🌐 Admin Panel: http://0.0.0.0:8090"
|
echo ""
|
||||||
|
echo "🛠️ Admin Web Management"
|
||||||
|
echo "🔗 Default URL: http://0.0.0.0:$PORT"
|
||||||
echo "👤 User: admin@example.com"
|
echo "👤 User: admin@example.com"
|
||||||
echo "🔑 Passwd: Admin123456"
|
echo "🔑 Passwd: Admin123456"
|
||||||
|
echo ""
|
||||||
|
echo "📌 Make sure port $PORT is accessible from your host system or cloud firewall."
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user