Add restore messages to server notification templates
- Add new fields for restore messages to the Server Template Dialog to match the DB schema.
This commit is contained in:
@@ -93,6 +93,87 @@ export const ServerTemplateFields: React.FC<ServerTemplateFieldsProps> = ({ cont
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="pb-3">
|
||||||
|
<CardTitle className="text-sm font-medium">System Resource Restore Messages</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<FormField
|
||||||
|
control={control}
|
||||||
|
name="restore_cpu_message"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>CPU Restore Message</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="CPU usage on ${server_name} has returned to normal: ${cpu_usage}%"
|
||||||
|
className="min-h-20"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={control}
|
||||||
|
name="restore_ram_message"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>RAM Restore Message</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Memory usage on ${server_name} has returned to normal: ${ram_usage}%"
|
||||||
|
className="min-h-20"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={control}
|
||||||
|
name="restore_disk_message"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Disk Restore Message</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Disk usage on ${server_name} has returned to normal: ${disk_usage}%"
|
||||||
|
className="min-h-20"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={control}
|
||||||
|
name="restore_network_message"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Network Restore Message</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Network usage on ${server_name} has returned to normal: ${network_usage}%"
|
||||||
|
className="min-h-20"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="pt-6">
|
<CardContent className="pt-6">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
@@ -203,6 +284,42 @@ export const ServerTemplateFields: React.FC<ServerTemplateFieldsProps> = ({ cont
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={control}
|
||||||
|
name="restore_cpu_temp_message"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>CPU Temperature Restore Message</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="CPU temperature on ${server_name} has returned to normal: ${cpu_temp}°C"
|
||||||
|
className="min-h-20"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={control}
|
||||||
|
name="restore_disk_io_message"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Disk I/O Restore Message</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Disk I/O on ${server_name} has returned to normal: ${disk_io} MB/s"
|
||||||
|
className="min-h-20"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import * as z from "zod";
|
import * as z from "zod";
|
||||||
@@ -29,6 +28,12 @@ const serverTemplateSchema = z.object({
|
|||||||
paused_message: z.string().min(1, "Paused message is required"),
|
paused_message: z.string().min(1, "Paused message is required"),
|
||||||
cpu_temp_message: z.string().min(1, "CPU temperature message is required"),
|
cpu_temp_message: z.string().min(1, "CPU temperature message is required"),
|
||||||
disk_io_message: z.string().min(1, "Disk I/O message is required"),
|
disk_io_message: z.string().min(1, "Disk I/O message is required"),
|
||||||
|
restore_ram_message: z.string().optional(),
|
||||||
|
restore_cpu_message: z.string().optional(),
|
||||||
|
restore_disk_message: z.string().optional(),
|
||||||
|
restore_network_message: z.string().optional(),
|
||||||
|
restore_cpu_temp_message: z.string().optional(),
|
||||||
|
restore_disk_io_message: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Service template schema
|
// Service template schema
|
||||||
@@ -98,6 +103,12 @@ const getDefaultValues = (templateType: TemplateType): TemplateFormData => {
|
|||||||
paused_message: "Monitoring for server ${server_name} is paused",
|
paused_message: "Monitoring for server ${server_name} is paused",
|
||||||
cpu_temp_message: "CPU temperature on ${server_name} is ${cpu_temp}°C",
|
cpu_temp_message: "CPU temperature on ${server_name} is ${cpu_temp}°C",
|
||||||
disk_io_message: "Disk I/O on ${server_name} is ${disk_io} MB/s",
|
disk_io_message: "Disk I/O on ${server_name} is ${disk_io} MB/s",
|
||||||
|
restore_ram_message: "Memory usage on ${server_name} has returned to normal: ${ram_usage}%",
|
||||||
|
restore_cpu_message: "CPU usage on ${server_name} has returned to normal: ${cpu_usage}%",
|
||||||
|
restore_disk_message: "Disk usage on ${server_name} has returned to normal: ${disk_usage}%",
|
||||||
|
restore_network_message: "Network usage on ${server_name} has returned to normal: ${network_usage}%",
|
||||||
|
restore_cpu_temp_message: "CPU temperature on ${server_name} has returned to normal: ${cpu_temp}°C",
|
||||||
|
restore_disk_io_message: "Disk I/O on ${server_name} has returned to normal: ${disk_io} MB/s",
|
||||||
};
|
};
|
||||||
|
|
||||||
case 'service':
|
case 'service':
|
||||||
@@ -162,6 +173,7 @@ export const useTemplateForm = ({ templateId, templateType, open, onOpenChange,
|
|||||||
queryKey: ['template', templateId, templateType],
|
queryKey: ['template', templateId, templateType],
|
||||||
queryFn: () => {
|
queryFn: () => {
|
||||||
if (!templateId) return null;
|
if (!templateId) return null;
|
||||||
|
|
||||||
return templateService.getTemplate(templateId, templateType);
|
return templateService.getTemplate(templateId, templateType);
|
||||||
},
|
},
|
||||||
enabled: !!templateId && open,
|
enabled: !!templateId && open,
|
||||||
@@ -177,26 +189,47 @@ export const useTemplateForm = ({ templateId, templateType, open, onOpenChange,
|
|||||||
placeholder: (templateData as any).placeholder || "",
|
placeholder: (templateData as any).placeholder || "",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add template-specific fields with proper type conversion for server_threshold
|
// Define the expected fields for each template type
|
||||||
Object.keys(templateData).forEach(key => {
|
const expectedFields = {
|
||||||
if (!['id', 'collectionId', 'collectionName', 'created', 'updated', 'name'].includes(key)) {
|
server: [
|
||||||
let value = (templateData as any)[key];
|
'ram_message', 'cpu_message', 'disk_message', 'network_message',
|
||||||
|
'up_message', 'down_message', 'notification_id', 'warning_message',
|
||||||
|
'paused_message', 'cpu_temp_message', 'disk_io_message',
|
||||||
|
'restore_ram_message', 'restore_cpu_message', 'restore_disk_message',
|
||||||
|
'restore_network_message', 'restore_cpu_temp_message', 'restore_disk_io_message'
|
||||||
|
],
|
||||||
|
service: [
|
||||||
|
'up_message', 'down_message', 'maintenance_message', 'incident_message',
|
||||||
|
'resolved_message', 'warning_message'
|
||||||
|
],
|
||||||
|
ssl: ['expired', 'exiring_soon', 'warning'],
|
||||||
|
server_threshold: [
|
||||||
|
'cpu_threshold', 'ram_threshold', 'disk_threshold', 'network_threshold',
|
||||||
|
'notification_id', 'server_template_id'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
// Convert string values to numbers for server threshold fields
|
// Add template-specific fields
|
||||||
if (templateType === 'server_threshold' &&
|
const fieldsToProcess = expectedFields[templateType] || [];
|
||||||
['cpu_threshold', 'ram_threshold', 'disk_threshold', 'network_threshold'].includes(key)) {
|
fieldsToProcess.forEach(key => {
|
||||||
value = typeof value === 'string' ? Number(value) : value;
|
let value = (templateData as any)[key];
|
||||||
// Ensure valid number, fallback to default if invalid
|
|
||||||
if (isNaN(value)) {
|
// Convert string values to numbers for server threshold fields
|
||||||
const defaults = getDefaultValues(templateType) as any;
|
if (templateType === 'server_threshold' &&
|
||||||
value = defaults[key] || 0;
|
['cpu_threshold', 'ram_threshold', 'disk_threshold', 'network_threshold'].includes(key)) {
|
||||||
}
|
value = typeof value === 'string' ? Number(value) : value;
|
||||||
|
// Ensure valid number, fallback to default if invalid
|
||||||
|
if (isNaN(value)) {
|
||||||
|
const defaults = getDefaultValues(templateType) as any;
|
||||||
|
value = defaults[key] || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
formData[key] = value || "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the value, using empty string as fallback for string fields
|
||||||
|
formData[key] = value !== undefined && value !== null ? value : "";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("Final form data being set:", formData);
|
||||||
form.reset(formData);
|
form.reset(formData);
|
||||||
}
|
}
|
||||||
}, [templateData, open, form, templateType]);
|
}, [templateData, open, form, templateType]);
|
||||||
@@ -213,6 +246,7 @@ export const useTemplateForm = ({ templateId, templateType, open, onOpenChange,
|
|||||||
onSuccess();
|
onSuccess();
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
description: "Failed to create template. Please check your inputs and try again.",
|
description: "Failed to create template. Please check your inputs and try again.",
|
||||||
@@ -234,6 +268,7 @@ export const useTemplateForm = ({ templateId, templateType, open, onOpenChange,
|
|||||||
onSuccess();
|
onSuccess();
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
description: "Failed to update template. Please check your inputs and try again.",
|
description: "Failed to update template. Please check your inputs and try again.",
|
||||||
@@ -247,13 +282,16 @@ export const useTemplateForm = ({ templateId, templateType, open, onOpenChange,
|
|||||||
// Handle form submission
|
// Handle form submission
|
||||||
const onSubmit = (formData: TemplateFormData) => {
|
const onSubmit = (formData: TemplateFormData) => {
|
||||||
|
|
||||||
|
|
||||||
// Remove templateType from the data before sending to API
|
// Remove templateType from the data before sending to API
|
||||||
const { templateType: _, ...templateDataWithoutType } = formData;
|
const { templateType: _, ...templateDataWithoutType } = formData;
|
||||||
const completeData = templateDataWithoutType as AnyTemplateData;
|
const completeData = templateDataWithoutType as AnyTemplateData;
|
||||||
|
|
||||||
if (isEditMode && templateId) {
|
if (isEditMode && templateId) {
|
||||||
|
|
||||||
updateMutation.mutate({ id: templateId, data: completeData });
|
updateMutation.mutate({ id: templateId, data: completeData });
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
createMutation.mutate(completeData);
|
createMutation.mutate(completeData);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -261,6 +299,7 @@ export const useTemplateForm = ({ templateId, templateType, open, onOpenChange,
|
|||||||
// Reset form when dialog closes or template type changes
|
// Reset form when dialog closes or template type changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
|
console.log("Dialog closed, resetting form");
|
||||||
form.reset(getDefaultValues(templateType));
|
form.reset(getDefaultValues(templateType));
|
||||||
}
|
}
|
||||||
}, [open, form, templateType]);
|
}, [open, form, templateType]);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
|
|
||||||
export interface ServerNotificationTemplate {
|
export interface ServerNotificationTemplate {
|
||||||
@@ -17,6 +16,12 @@ export interface ServerNotificationTemplate {
|
|||||||
paused_message: string;
|
paused_message: string;
|
||||||
cpu_temp_message: string;
|
cpu_temp_message: string;
|
||||||
disk_io_message: string;
|
disk_io_message: string;
|
||||||
|
restore_ram_message: string;
|
||||||
|
restore_cpu_message: string;
|
||||||
|
restore_disk_message: string;
|
||||||
|
restore_network_message: string;
|
||||||
|
restore_disk_io_message: string;
|
||||||
|
restore_cpu_temp_message: string;
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
created: string;
|
created: string;
|
||||||
updated: string;
|
updated: string;
|
||||||
@@ -35,68 +40,74 @@ export interface CreateUpdateServerNotificationTemplateData {
|
|||||||
paused_message: string;
|
paused_message: string;
|
||||||
cpu_temp_message: string;
|
cpu_temp_message: string;
|
||||||
disk_io_message: string;
|
disk_io_message: string;
|
||||||
|
restore_ram_message: string;
|
||||||
|
restore_cpu_message: string;
|
||||||
|
restore_disk_message: string;
|
||||||
|
restore_network_message: string;
|
||||||
|
restore_disk_io_message: string;
|
||||||
|
restore_cpu_temp_message: string;
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const serverNotificationTemplateService = {
|
export const serverNotificationTemplateService = {
|
||||||
async getTemplates(): Promise<ServerNotificationTemplate[]> {
|
async getTemplates(): Promise<ServerNotificationTemplate[]> {
|
||||||
try {
|
try {
|
||||||
// console.log("Fetching server notification templates");
|
|
||||||
const response = await pb.collection('server_notification_templates').getList(1, 50, {
|
const response = await pb.collection('server_notification_templates').getList(1, 50, {
|
||||||
sort: '-created',
|
sort: '-created',
|
||||||
});
|
});
|
||||||
// console.log("Server notification templates response:", response);
|
|
||||||
return response.items as unknown as ServerNotificationTemplate[];
|
return response.items as unknown as ServerNotificationTemplate[];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("Error fetching server notification templates:", error);
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async getTemplate(id: string): Promise<ServerNotificationTemplate> {
|
async getTemplate(id: string): Promise<ServerNotificationTemplate> {
|
||||||
try {
|
try {
|
||||||
// console.log(`Fetching server notification template with id: ${id}`);
|
|
||||||
const response = await pb.collection('server_notification_templates').getOne(id);
|
const response = await pb.collection('server_notification_templates').getOne(id);
|
||||||
// console.log("Server notification template response:", response);
|
|
||||||
return response as unknown as ServerNotificationTemplate;
|
return response as unknown as ServerNotificationTemplate;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
/// console.error(`Error fetching server notification template with id ${id}:`, error);
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async createTemplate(data: CreateUpdateServerNotificationTemplateData): Promise<ServerNotificationTemplate> {
|
async createTemplate(data: CreateUpdateServerNotificationTemplateData): Promise<ServerNotificationTemplate> {
|
||||||
try {
|
try {
|
||||||
// console.log("Creating new server notification template with data:", data);
|
|
||||||
const response = await pb.collection('server_notification_templates').create(data);
|
const response = await pb.collection('server_notification_templates').create(data);
|
||||||
// console.log("Create server notification template response:", response);
|
|
||||||
return response as unknown as ServerNotificationTemplate;
|
return response as unknown as ServerNotificationTemplate;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("Error creating server notification template:", error);
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateTemplate(id: string, data: Partial<CreateUpdateServerNotificationTemplateData>): Promise<ServerNotificationTemplate> {
|
async updateTemplate(id: string, data: Partial<CreateUpdateServerNotificationTemplateData>): Promise<ServerNotificationTemplate> {
|
||||||
try {
|
try {
|
||||||
// console.log(`Updating server notification template with id: ${id}`, data);
|
|
||||||
const response = await pb.collection('server_notification_templates').update(id, data);
|
const response = await pb.collection('server_notification_templates').update(id, data);
|
||||||
// console.log("Update server notification template response:", response);
|
|
||||||
return response as unknown as ServerNotificationTemplate;
|
return response as unknown as ServerNotificationTemplate;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error(`Error updating server notification template with id ${id}:`, error);
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteTemplate(id: string): Promise<boolean> {
|
async deleteTemplate(id: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
// console.log(`Deleting server notification template with id: ${id}`);
|
|
||||||
await pb.collection('server_notification_templates').delete(id);
|
await pb.collection('server_notification_templates').delete(id);
|
||||||
// console.log("Server notification template deleted successfully");
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error(`Error deleting server notification template with id ${id}:`, error);
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user