Merge pull request #65 from operacle/develop
feat: Implement notification channel based on status(multi selection) and Refactor Split ServiceForm into smaller components.
This commit is contained in:
@@ -11,19 +11,19 @@ const api = {
|
||||
* Handle API requests
|
||||
*/
|
||||
async handleRequest(path, method, body) {
|
||||
console.log(`API request: ${method} ${path}`, body);
|
||||
// console.log(`API request: ${method} ${path}`, body);
|
||||
|
||||
// Route to the appropriate handler
|
||||
if (path === '/api/realtime') {
|
||||
console.log("Routing to realtime handler");
|
||||
// console.log("Routing to realtime handler");
|
||||
return await realtime(body);
|
||||
} else if (path === '/api/settings' || path.startsWith('/api/settings/')) {
|
||||
console.log("Routing to settings handler");
|
||||
// console.log("Routing to settings handler");
|
||||
return await settingsApi(body, path);
|
||||
}
|
||||
|
||||
// Return 404 for unknown routes
|
||||
console.error(`Endpoint not found: ${path}`);
|
||||
// console.error(`Endpoint not found: ${path}`);
|
||||
return {
|
||||
status: 404,
|
||||
json: {
|
||||
@@ -40,7 +40,7 @@ const originalFetch = window.fetch;
|
||||
window.fetch = async (url, options = {}) => {
|
||||
// Check if this is an API request to our mock endpoints
|
||||
if (typeof url === 'string' && url.startsWith('/api/')) {
|
||||
console.log('Intercepting API request:', url, options);
|
||||
// console.log('Intercepting API request:', url, options);
|
||||
|
||||
try {
|
||||
let body = {};
|
||||
|
||||
@@ -8,7 +8,7 @@ import { testEmail } from './actions/testEmail';
|
||||
* Settings API handler
|
||||
*/
|
||||
const settingsApi = async (body: any, path?: string) => {
|
||||
console.log('Settings API called with path:', path, 'body:', body);
|
||||
// console.log('Settings API called with path:', path, 'body:', body);
|
||||
|
||||
// Handle test email endpoint specifically
|
||||
if (path === '/api/settings/test/email') {
|
||||
@@ -18,7 +18,7 @@ const settingsApi = async (body: any, path?: string) => {
|
||||
|
||||
// Handle regular settings API with action-based routing
|
||||
const action = body?.action;
|
||||
console.log('Settings API called with action:', action, 'data:', body?.data);
|
||||
// console.log('Settings API called with action:', action, 'data:', body?.data);
|
||||
|
||||
switch (action) {
|
||||
case 'getSettings':
|
||||
|
||||
@@ -53,7 +53,7 @@ export const Header = ({
|
||||
// Log avatar data for debugging
|
||||
useEffect(() => {
|
||||
if (currentUser) {
|
||||
console.log("Avatar URL in Header:", currentUser.avatar);
|
||||
// console.log("Avatar URL in Header:", currentUser.avatar);
|
||||
}
|
||||
}, [currentUser]);
|
||||
|
||||
@@ -66,7 +66,7 @@ export const Header = ({
|
||||
} else {
|
||||
avatarUrl = currentUser.avatar;
|
||||
}
|
||||
console.log("Final avatar URL:", avatarUrl);
|
||||
// console.log("Final avatar URL:", avatarUrl);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -22,12 +22,12 @@ export const ScheduleIncidentContent = () => {
|
||||
|
||||
// Initialize maintenance notifications when the component mounts
|
||||
useEffect(() => {
|
||||
console.log("Initializing maintenance notifications");
|
||||
// console.log("Initializing maintenance notifications");
|
||||
initMaintenanceNotifications();
|
||||
|
||||
// Clean up when the component unmounts
|
||||
return () => {
|
||||
console.log("Cleaning up maintenance notifications");
|
||||
// console.log("Cleaning up maintenance notifications");
|
||||
stopMaintenanceNotifications();
|
||||
};
|
||||
}, []);
|
||||
@@ -43,7 +43,7 @@ export const ScheduleIncidentContent = () => {
|
||||
const handleMaintenanceCreated = () => {
|
||||
// Refresh data by incrementing the refresh trigger
|
||||
const newTriggerValue = refreshTrigger + 1;
|
||||
console.log("Maintenance created, refreshing data with new trigger value:", newTriggerValue);
|
||||
// console.log("Maintenance created, refreshing data with new trigger value:", newTriggerValue);
|
||||
setRefreshTrigger(newTriggerValue);
|
||||
|
||||
// Show success toast
|
||||
@@ -56,7 +56,7 @@ export const ScheduleIncidentContent = () => {
|
||||
const handleIncidentCreated = () => {
|
||||
// Refresh data by incrementing the refresh trigger
|
||||
const newTriggerValue = incidentRefreshTrigger + 1;
|
||||
console.log("Incident created, refreshing data with new trigger value:", newTriggerValue);
|
||||
// console.log("Incident created, refreshing data with new trigger value:", newTriggerValue);
|
||||
setIncidentRefreshTrigger(newTriggerValue);
|
||||
|
||||
// Show success toast
|
||||
|
||||
@@ -22,13 +22,13 @@ export const useIncidentData = ({ refreshTrigger = 0 }: UseIncidentDataProps) =>
|
||||
const fetchIncidentData = useCallback(async (force = false) => {
|
||||
// Skip if already fetching
|
||||
if (isFetchingRef.current) {
|
||||
console.log('Already fetching data, skipping additional request');
|
||||
// console.log('Already fetching data, skipping additional request');
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if not forced and already initialized
|
||||
if (initialized && !force) {
|
||||
console.log('Data already initialized and no force refresh, skipping fetch');
|
||||
// console.log('Data already initialized and no force refresh, skipping fetch');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,22 +46,22 @@ export const useIncidentData = ({ refreshTrigger = 0 }: UseIncidentDataProps) =>
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
console.log(`Fetching incident data (force=${force})`);
|
||||
// console.log(`Fetching incident data (force=${force})`);
|
||||
const allIncidents = await incidentService.getAllIncidents(force);
|
||||
|
||||
if (Array.isArray(allIncidents)) {
|
||||
setIncidents(allIncidents);
|
||||
console.log(`Successfully set ${allIncidents.length} incidents to state`);
|
||||
// console.log(`Successfully set ${allIncidents.length} incidents to state`);
|
||||
} else {
|
||||
setIncidents([]);
|
||||
console.warn('No incidents returned from service');
|
||||
// console.warn('No incidents returned from service');
|
||||
}
|
||||
|
||||
setInitialized(true);
|
||||
setLoading(false);
|
||||
setIsRefreshing(false);
|
||||
} catch (error) {
|
||||
console.error('Error fetching incident data:', error);
|
||||
// console.error('Error fetching incident data:', error);
|
||||
setError('Failed to load incident data. Please try again later.');
|
||||
setIncidents([]);
|
||||
setInitialized(true);
|
||||
@@ -79,7 +79,7 @@ export const useIncidentData = ({ refreshTrigger = 0 }: UseIncidentDataProps) =>
|
||||
useEffect(() => {
|
||||
// Skip if the refresh trigger hasn't changed (prevents duplicate effect calls)
|
||||
if (refreshTrigger === lastRefreshTriggerRef.current && initialized) {
|
||||
console.log('Refresh trigger unchanged, skipping fetch');
|
||||
// console.log('Refresh trigger unchanged, skipping fetch');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ export const useIncidentData = ({ refreshTrigger = 0 }: UseIncidentDataProps) =>
|
||||
const abortController = new AbortController();
|
||||
let isMounted = true;
|
||||
|
||||
console.log(`useIncidentData effect running, refreshTrigger: ${refreshTrigger}`);
|
||||
// console.log(`useIncidentData effect running, refreshTrigger: ${refreshTrigger}`);
|
||||
|
||||
// Use a longer delay to ensure we don't trigger too many API calls
|
||||
const fetchTimer = setTimeout(() => {
|
||||
@@ -101,7 +101,7 @@ export const useIncidentData = ({ refreshTrigger = 0 }: UseIncidentDataProps) =>
|
||||
|
||||
// Cleanup function to abort any in-flight requests and clear timers
|
||||
return () => {
|
||||
console.log('Cleaning up incident data fetch effect');
|
||||
// console.log('Cleaning up incident data fetch effect');
|
||||
isMounted = false;
|
||||
clearTimeout(fetchTimer);
|
||||
abortController.abort();
|
||||
@@ -112,7 +112,7 @@ export const useIncidentData = ({ refreshTrigger = 0 }: UseIncidentDataProps) =>
|
||||
const incidentData = useMemo(() => {
|
||||
if (!initialized || incidents.length === 0) return [];
|
||||
|
||||
console.log(`Filtering incidents by: ${filter}`);
|
||||
// console.log(`Filtering incidents by: ${filter}`);
|
||||
|
||||
if (filter === "unresolved") {
|
||||
return incidents.filter(item => {
|
||||
|
||||
+20
-20
@@ -27,7 +27,7 @@ export const MaintenanceNotificationSettingsField = () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const channels = await alertConfigService.getAlertConfigurations();
|
||||
console.log("Fetched notification channels for form:", channels);
|
||||
// console.log("Fetched notification channels for form:", channels);
|
||||
|
||||
// Only show enabled channels
|
||||
const enabledChannels = channels.filter(channel => channel.enabled);
|
||||
@@ -38,18 +38,18 @@ export const MaintenanceNotificationSettingsField = () => {
|
||||
const currentChannel = getValues('notification_channel_id');
|
||||
const shouldNotify = getValues('notify_subscribers');
|
||||
|
||||
console.log("Current notification values:", {
|
||||
currentChannel,
|
||||
shouldNotify,
|
||||
availableChannels: enabledChannels.length
|
||||
});
|
||||
// console.log("Current notification values:", {
|
||||
// currentChannel,
|
||||
// shouldNotify,
|
||||
// availableChannels: enabledChannels.length
|
||||
// });
|
||||
|
||||
if (shouldNotify && (!currentChannel || currentChannel === 'none') && enabledChannels.length > 0) {
|
||||
console.log("Setting default notification channel:", enabledChannels[0].id);
|
||||
// console.log("Setting default notification channel:", enabledChannels[0].id);
|
||||
setValue('notification_channel_id', enabledChannels[0].id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching notification channels:', error);
|
||||
// console.error('Error fetching notification channels:', error);
|
||||
toast({
|
||||
title: t('error'),
|
||||
description: t('errorFetchingNotificationChannels'),
|
||||
@@ -64,12 +64,12 @@ export const MaintenanceNotificationSettingsField = () => {
|
||||
}, [t, toast, setValue, getValues]);
|
||||
|
||||
// Log value changes for debugging
|
||||
useEffect(() => {
|
||||
console.log("Current notification settings:", {
|
||||
channel_id: getValues('notification_channel_id'),
|
||||
notify: notifySubscribers
|
||||
});
|
||||
}, [notifySubscribers, notificationChannelId, getValues]);
|
||||
// useEffect(() => {
|
||||
// console.log("Current notification settings:", {
|
||||
// channel_id: getValues('notification_channel_id'),
|
||||
// notify: notifySubscribers
|
||||
// });
|
||||
// }, [notifySubscribers, notificationChannelId, getValues]);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -98,7 +98,7 @@ export const MaintenanceNotificationSettingsField = () => {
|
||||
checked={field.value}
|
||||
onCheckedChange={(checked) => {
|
||||
field.onChange(checked);
|
||||
console.log("Notification toggle changed to:", checked);
|
||||
// console.log("Notification toggle changed to:", checked);
|
||||
// If notifications are disabled, also clear the notification channel
|
||||
if (!checked) {
|
||||
setValue('notification_channel_id', '');
|
||||
@@ -120,10 +120,10 @@ export const MaintenanceNotificationSettingsField = () => {
|
||||
// Make sure to handle both empty string and "none" as special cases
|
||||
const displayValue = field.value || "";
|
||||
|
||||
console.log("Rendering notification channel field with value:", {
|
||||
fieldValue: field.value,
|
||||
displayValue
|
||||
});
|
||||
// console.log("Rendering notification channel field with value:", {
|
||||
// fieldValue: field.value,
|
||||
// displayValue
|
||||
// });
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
@@ -135,7 +135,7 @@ export const MaintenanceNotificationSettingsField = () => {
|
||||
<Select
|
||||
value={displayValue}
|
||||
onValueChange={(value) => {
|
||||
console.log("Setting notification channel to:", value);
|
||||
// console.log("Setting notification channel to:", value);
|
||||
field.onChange(value === "none" ? "" : value);
|
||||
}}
|
||||
disabled={!notifySubscribers}
|
||||
|
||||
+10
-10
@@ -31,17 +31,17 @@ export const AssignedUsersField = () => {
|
||||
// Ensure assigned_users is initialized as an array
|
||||
useEffect(() => {
|
||||
const currentValue = form.getValues('assigned_users');
|
||||
console.log("Initial assigned_users value:", currentValue);
|
||||
// console.log("Initial assigned_users value:", currentValue);
|
||||
|
||||
// Initialize as empty array if no value or invalid value
|
||||
if (!currentValue || !Array.isArray(currentValue)) {
|
||||
console.log("Initializing assigned_users as empty array");
|
||||
// console.log("Initializing assigned_users as empty array");
|
||||
form.setValue('assigned_users', [], { shouldValidate: false, shouldDirty: true });
|
||||
}
|
||||
}, [form]);
|
||||
|
||||
console.log("Current form values:", form.getValues());
|
||||
console.log("Current assigned_users:", form.getValues('assigned_users'));
|
||||
//console.log("Current form values:", form.getValues());
|
||||
//console.log("Current assigned_users:", form.getValues('assigned_users'));
|
||||
|
||||
// Fetch users for the assignment dropdown
|
||||
const { data: users = [], isLoading } = useQuery({
|
||||
@@ -49,10 +49,10 @@ export const AssignedUsersField = () => {
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const usersList = await userService.getUsers();
|
||||
console.log("Fetched users for assignment:", usersList);
|
||||
// console.log("Fetched users for assignment:", usersList);
|
||||
return Array.isArray(usersList) ? usersList : [];
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch users:", error);
|
||||
// console.error("Failed to fetch users:", error);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
@@ -64,7 +64,7 @@ export const AssignedUsersField = () => {
|
||||
? form.watch('assigned_users')
|
||||
: [];
|
||||
|
||||
console.log("Selected user IDs:", selectedUserIds);
|
||||
// console.log("Selected user IDs:", selectedUserIds);
|
||||
|
||||
// Function to add a user
|
||||
const addUser = (userId: string) => {
|
||||
@@ -73,7 +73,7 @@ export const AssignedUsersField = () => {
|
||||
: [];
|
||||
|
||||
if (!currentValues.includes(userId)) {
|
||||
console.log("Adding user:", userId);
|
||||
// console.log("Adding user:", userId);
|
||||
form.setValue('assigned_users', [...currentValues, userId], { shouldValidate: true, shouldDirty: true });
|
||||
}
|
||||
};
|
||||
@@ -84,7 +84,7 @@ export const AssignedUsersField = () => {
|
||||
? [...form.getValues('assigned_users')]
|
||||
: [];
|
||||
|
||||
console.log("Removing user:", userId);
|
||||
// console.log("Removing user:", userId);
|
||||
form.setValue(
|
||||
'assigned_users',
|
||||
currentValues.filter(id => id !== userId),
|
||||
@@ -94,7 +94,7 @@ export const AssignedUsersField = () => {
|
||||
|
||||
// Get selected users data
|
||||
const selectedUsers = users.filter(user => selectedUserIds.includes(user.id));
|
||||
console.log("Matched selected users:", selectedUsers);
|
||||
// console.log("Matched selected users:", selectedUsers);
|
||||
|
||||
// Function to get user initials from name
|
||||
const getUserInitials = (user: any): string => {
|
||||
|
||||
@@ -184,7 +184,7 @@ export function ResponseTimeChart({ uptimeData }: ResponseTimeChartProps) {
|
||||
if (data?.agent_id && data.agent_id !== '1') {
|
||||
label = `${regionName} (${data.agent_id})`;
|
||||
} else if (regionName === 'Default' && data?.agent_id === '1') {
|
||||
label = `Default (Agent 1)`;
|
||||
label = `Default System Check (Agent 1)`;
|
||||
}
|
||||
|
||||
const colorIndex = index % modernColors.length;
|
||||
|
||||
+6
-6
@@ -22,12 +22,12 @@ export const useRealTimeUpdates = ({
|
||||
useEffect(() => {
|
||||
if (!serviceId) return;
|
||||
|
||||
console.log(`Setting up real-time updates for service: ${serviceId}`);
|
||||
// console.log(`Setting up real-time updates for service: ${serviceId}`);
|
||||
|
||||
try {
|
||||
// Subscribe to the service record for real-time updates
|
||||
const subscription = pb.collection('services').subscribe(serviceId, function(e) {
|
||||
console.log("Service updated:", e.record);
|
||||
// console.log("Service updated:", e.record);
|
||||
|
||||
// Update our local state with the new data
|
||||
if (e.record) {
|
||||
@@ -47,7 +47,7 @@ export const useRealTimeUpdates = ({
|
||||
// Subscribe to uptime data updates
|
||||
const uptimeSubscription = pb.collection('uptime_data').subscribe('*', function(e) {
|
||||
if (e.record && e.record.service_id === serviceId) {
|
||||
console.log("New uptime data:", e.record);
|
||||
// console.log("New uptime data:", e.record);
|
||||
|
||||
// Add the new uptime data to our list if it's within the selected date range
|
||||
const timestamp = new Date(e.record.timestamp);
|
||||
@@ -73,16 +73,16 @@ export const useRealTimeUpdates = ({
|
||||
|
||||
// Clean up the subscriptions
|
||||
return () => {
|
||||
console.log(`Cleaning up subscriptions for service: ${serviceId}`);
|
||||
// console.log(`Cleaning up subscriptions for service: ${serviceId}`);
|
||||
try {
|
||||
pb.collection('services').unsubscribe(serviceId);
|
||||
pb.collection('uptime_data').unsubscribe('*');
|
||||
} catch (error) {
|
||||
console.error("Error cleaning up subscriptions:", error);
|
||||
// console.error("Error cleaning up subscriptions:", error);
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error setting up real-time updates:", error);
|
||||
// console.error("Error setting up real-time updates:", error);
|
||||
}
|
||||
}, [serviceId, startDate, endDate, setService, setUptimeData]);
|
||||
};
|
||||
+18
-18
@@ -38,7 +38,7 @@ export const useServiceData = (serviceId: string | undefined, startDate: Date, e
|
||||
description: `Service status changed to ${newStatus}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to update service status:", error);
|
||||
// console.error("Failed to update service status:", error);
|
||||
setService(prevService => prevService);
|
||||
|
||||
toast({
|
||||
@@ -52,12 +52,12 @@ export const useServiceData = (serviceId: string | undefined, startDate: Date, e
|
||||
const fetchUptimeData = async (serviceId: string, start: Date, end: Date, selectedRange?: DateRangeOption | string, regionalAgent?: string) => {
|
||||
try {
|
||||
if (!service) {
|
||||
console.log('No service data available for uptime fetch');
|
||||
// console.log('No service data available for uptime fetch');
|
||||
return [];
|
||||
}
|
||||
|
||||
const currentAgent = regionalAgent || selectedRegionalAgent;
|
||||
console.log(`Fetching uptime data: ${start.toISOString()} to ${end.toISOString()} for range: ${selectedRange}, service type: ${service.type}, regional agent: ${currentAgent}`);
|
||||
// console.log(`Fetching uptime data: ${start.toISOString()} to ${end.toISOString()} for range: ${selectedRange}, service type: ${service.type}, regional agent: ${currentAgent}`);
|
||||
|
||||
// Clear existing data immediately when switching agents
|
||||
setUptimeData([]);
|
||||
@@ -70,17 +70,17 @@ export const useServiceData = (serviceId: string | undefined, startDate: Date, e
|
||||
limit = 400;
|
||||
}
|
||||
|
||||
console.log(`Using limit ${limit} for range ${selectedRange}`);
|
||||
// console.log(`Using limit ${limit} for range ${selectedRange}`);
|
||||
|
||||
let history: UptimeData[] = [];
|
||||
|
||||
if (currentAgent === "all") {
|
||||
// Fetch data from all sources (default + all online regional agents)
|
||||
console.log(`Fetching data from all monitoring sources`);
|
||||
// console.log(`Fetching data from all monitoring sources`);
|
||||
|
||||
// Fetch default monitoring data
|
||||
const defaultData = await uptimeService.getUptimeHistory(serviceId, limit, start, end, service.type);
|
||||
console.log(`Retrieved ${defaultData.length} default monitoring records`);
|
||||
// console.log(`Retrieved ${defaultData.length} default monitoring records`);
|
||||
|
||||
// Mark default data with source identifier
|
||||
const markedDefaultData = defaultData.map(record => ({
|
||||
@@ -100,7 +100,7 @@ export const useServiceData = (serviceId: string | undefined, startDate: Date, e
|
||||
const regionalData = await uptimeService.getUptimeHistoryByRegionalAgent(
|
||||
serviceId, limit, start, end, service.type, agent.region_name, agent.agent_id
|
||||
);
|
||||
console.log(`Retrieved ${regionalData.length} records from ${agent.region_name}`);
|
||||
// console.log(`Retrieved ${regionalData.length} records from ${agent.region_name}`);
|
||||
|
||||
// Mark regional data with source identifier
|
||||
const markedRegionalData = regionalData.map(record => ({
|
||||
@@ -112,18 +112,18 @@ export const useServiceData = (serviceId: string | undefined, startDate: Date, e
|
||||
|
||||
history = [...history, ...markedRegionalData];
|
||||
} catch (error) {
|
||||
console.error(`Error fetching data from ${agent.region_name}:`, error);
|
||||
// console.error(`Error fetching data from ${agent.region_name}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Total combined records: ${history.length}`);
|
||||
// console.log(`Total combined records: ${history.length}`);
|
||||
|
||||
} else {
|
||||
// Fetch regional agent specific data
|
||||
const [regionName, agentId] = currentAgent.split("|");
|
||||
console.log(`Fetching regional agent data for region: ${regionName}, agent: ${agentId} from ${service.type} collection`);
|
||||
// console.log(`Fetching regional agent data for region: ${regionName}, agent: ${agentId} from ${service.type} collection`);
|
||||
history = await uptimeService.getUptimeHistoryByRegionalAgent(serviceId, limit, start, end, service.type, regionName, agentId);
|
||||
console.log(`Retrieved ${history.length} regional monitoring records`);
|
||||
// console.log(`Retrieved ${history.length} regional monitoring records`);
|
||||
}
|
||||
|
||||
// Sort by timestamp (newest first)
|
||||
@@ -131,11 +131,11 @@ export const useServiceData = (serviceId: string | undefined, startDate: Date, e
|
||||
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
||||
);
|
||||
|
||||
console.log(`Final dataset: ${filteredHistory.length} records for ${currentAgent === "all" ? "all sources" : "regional"} monitoring`);
|
||||
//console.log(`Final dataset: ${filteredHistory.length} records for ${currentAgent === "all" ? "all sources" : "regional"} monitoring`);
|
||||
setUptimeData(filteredHistory);
|
||||
return filteredHistory;
|
||||
} catch (error) {
|
||||
console.error("Error fetching uptime data:", error);
|
||||
// console.error("Error fetching uptime data:", error);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error",
|
||||
@@ -146,7 +146,7 @@ export const useServiceData = (serviceId: string | undefined, startDate: Date, e
|
||||
};
|
||||
|
||||
const handleRegionalAgentChange = (agent: string) => {
|
||||
console.log(`Regional agent changed from ${selectedRegionalAgent} to: ${agent}`);
|
||||
// console.log(`Regional agent changed from ${selectedRegionalAgent} to: ${agent}`);
|
||||
|
||||
// Clear data immediately when switching
|
||||
setUptimeData([]);
|
||||
@@ -154,7 +154,7 @@ export const useServiceData = (serviceId: string | undefined, startDate: Date, e
|
||||
|
||||
// Refetch data with new agent selection
|
||||
if (serviceId && !isLoading && service) {
|
||||
console.log(`Refetching data for new agent: ${agent}`);
|
||||
// console.log(`Refetching data for new agent: ${agent}`);
|
||||
fetchUptimeData(serviceId, startDate, endDate, '24h', agent);
|
||||
}
|
||||
};
|
||||
@@ -196,13 +196,13 @@ export const useServiceData = (serviceId: string | undefined, startDate: Date, e
|
||||
alerts: serviceData.alerts || "unmuted"
|
||||
};
|
||||
|
||||
console.log(`Loaded service: ${formattedService.name} (${formattedService.type})`);
|
||||
// console.log(`Loaded service: ${formattedService.name} (${formattedService.type})`);
|
||||
setService(formattedService);
|
||||
|
||||
// Small delay to ensure state is updated before fetching uptime data
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
} catch (error) {
|
||||
console.error("Error fetching service:", error);
|
||||
// console.error("Error fetching service:", error);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error",
|
||||
@@ -220,7 +220,7 @@ export const useServiceData = (serviceId: string | undefined, startDate: Date, e
|
||||
// Update data when date range changes or when service is loaded
|
||||
useEffect(() => {
|
||||
if (serviceId && !isLoading && service) {
|
||||
console.log(`Date range changed or service loaded, refetching data for ${serviceId}: ${startDate.toISOString()} to ${endDate.toISOString()}`);
|
||||
// console.log(`Date range changed or service loaded, refetching data for ${serviceId}: ${startDate.toISOString()} to ${endDate.toISOString()}`);
|
||||
fetchUptimeData(serviceId, startDate, endDate, '24h', selectedRegionalAgent);
|
||||
}
|
||||
}, [startDate, endDate, serviceId, isLoading, service, selectedRegionalAgent, regionalAgents]);
|
||||
|
||||
@@ -1,228 +1,3 @@
|
||||
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { serviceSchema, ServiceFormData } from "./add-service/types";
|
||||
import { ServiceBasicFields } from "./add-service/ServiceBasicFields";
|
||||
import { ServiceTypeField } from "./add-service/ServiceTypeField";
|
||||
import { ServiceConfigFields } from "./add-service/ServiceConfigFields";
|
||||
import { ServiceNotificationFields } from "./add-service/ServiceNotificationFields";
|
||||
import { ServiceFormActions } from "./add-service/ServiceFormActions";
|
||||
import { serviceService } from "@/services/serviceService";
|
||||
import { Service } from "@/types/service.types";
|
||||
import { ServiceRegionalFields } from "./add-service/ServiceRegionalFields";
|
||||
|
||||
interface ServiceFormProps {
|
||||
onSuccess: () => void;
|
||||
onCancel: () => void;
|
||||
initialData?: Service | null;
|
||||
isEdit?: boolean;
|
||||
onSubmitStart?: () => void;
|
||||
}
|
||||
|
||||
export function ServiceForm({
|
||||
onSuccess,
|
||||
onCancel,
|
||||
initialData,
|
||||
isEdit = false,
|
||||
onSubmitStart
|
||||
}: ServiceFormProps) {
|
||||
const { toast } = useToast();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Initialize form with default values
|
||||
const form = useForm<ServiceFormData>({
|
||||
resolver: zodResolver(serviceSchema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
type: "http",
|
||||
url: "",
|
||||
port: "",
|
||||
interval: "60",
|
||||
retries: "3",
|
||||
notificationChannel: "",
|
||||
alertTemplate: "",
|
||||
regionalMonitoringEnabled: false,
|
||||
regionalAgent: "",
|
||||
},
|
||||
mode: "onBlur",
|
||||
});
|
||||
|
||||
// Populate form when initialData changes (separate from initialization)
|
||||
useEffect(() => {
|
||||
if (initialData && isEdit) {
|
||||
// Ensure the type is one of the allowed values
|
||||
const serviceType = (initialData.type || "http").toLowerCase();
|
||||
const validType = ["http", "ping", "tcp", "dns"].includes(serviceType)
|
||||
? serviceType as "http" | "ping" | "tcp" | "dns"
|
||||
: "http";
|
||||
|
||||
// For PING services, use host field; for DNS use domain field; for TCP use host field; others use url
|
||||
let urlValue = "";
|
||||
let portValue = "";
|
||||
|
||||
if (validType === "ping") {
|
||||
urlValue = initialData.host || "";
|
||||
} else if (validType === "dns") {
|
||||
urlValue = initialData.domain || "";
|
||||
} else if (validType === "tcp") {
|
||||
urlValue = initialData.host || "";
|
||||
portValue = String(initialData.port || "");
|
||||
} else {
|
||||
urlValue = initialData.url || "";
|
||||
}
|
||||
|
||||
// Handle regional monitoring data - check regional_status field
|
||||
const isRegionalEnabled = initialData.regional_status === "enabled";
|
||||
const regionalAgent = isRegionalEnabled && initialData.region_name && initialData.agent_id
|
||||
? `${initialData.region_name}|${initialData.agent_id}`
|
||||
: "";
|
||||
|
||||
// Reset the form with initial data values
|
||||
form.reset({
|
||||
name: initialData.name || "",
|
||||
type: validType,
|
||||
url: urlValue,
|
||||
port: portValue,
|
||||
interval: String(initialData.interval || 60),
|
||||
retries: String(initialData.retries || 3),
|
||||
notificationChannel: initialData.notificationChannel === "none" ? "" : initialData.notificationChannel || "",
|
||||
alertTemplate: initialData.alertTemplate === "default" ? "" : initialData.alertTemplate || "",
|
||||
regionalMonitoringEnabled: isRegionalEnabled,
|
||||
regionalAgent: regionalAgent,
|
||||
});
|
||||
|
||||
// Log for debugging
|
||||
console.log("Populating form with data:", {
|
||||
type: validType,
|
||||
url: urlValue,
|
||||
port: portValue,
|
||||
regionalAgent,
|
||||
regionalMonitoringEnabled: isRegionalEnabled,
|
||||
regional_status: initialData.regional_status,
|
||||
region_name: initialData.region_name,
|
||||
agent_id: initialData.agent_id
|
||||
});
|
||||
}
|
||||
}, [initialData, isEdit, form]);
|
||||
|
||||
const handleSubmit = async (data: ServiceFormData) => {
|
||||
if (isSubmitting) return;
|
||||
|
||||
setIsSubmitting(true);
|
||||
if (onSubmitStart) onSubmitStart();
|
||||
|
||||
try {
|
||||
console.log("Form data being submitted:", data); // Debug log for submitted data
|
||||
|
||||
// Parse regional agent selection
|
||||
let regionName = "";
|
||||
let agentId = "";
|
||||
let regionalStatus: "enabled" | "disabled" = "disabled";
|
||||
|
||||
// Set regional status and agent data based on form values
|
||||
if (data.regionalMonitoringEnabled) {
|
||||
regionalStatus = "enabled";
|
||||
if (data.regionalAgent && data.regionalAgent !== "") {
|
||||
const [parsedRegionName, parsedAgentId] = data.regionalAgent.split("|");
|
||||
regionName = parsedRegionName || "";
|
||||
agentId = parsedAgentId || "";
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare service data with proper field mapping
|
||||
const serviceData = {
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
interval: parseInt(data.interval),
|
||||
retries: parseInt(data.retries),
|
||||
notificationChannel: data.notificationChannel === "none" ? "" : data.notificationChannel,
|
||||
alertTemplate: data.alertTemplate === "default" ? "" : data.alertTemplate,
|
||||
// Use regional_status field instead of regionalMonitoringEnabled
|
||||
regionalStatus: regionalStatus,
|
||||
regionName: regionName,
|
||||
agentId: agentId,
|
||||
// Map the URL field to appropriate database field based on service type
|
||||
...(data.type === "dns"
|
||||
? { domain: data.url, url: "", host: "", port: undefined } // DNS: store in domain field
|
||||
: data.type === "ping"
|
||||
? { host: data.url, url: "", domain: "", port: undefined } // PING: store in host field
|
||||
: data.type === "tcp"
|
||||
? { host: data.url, port: parseInt(data.port || "80"), url: "", domain: "" } // TCP: store in host and port fields
|
||||
: { url: data.url, domain: "", host: "", port: undefined } // HTTP: store in url field
|
||||
)
|
||||
};
|
||||
|
||||
console.log("Service data being sent:", serviceData);
|
||||
|
||||
if (isEdit && initialData) {
|
||||
// Update existing service
|
||||
await serviceService.updateService(initialData.id, serviceData);
|
||||
|
||||
toast({
|
||||
title: "Service updated",
|
||||
description: `${data.name} has been updated successfully.`,
|
||||
});
|
||||
} else {
|
||||
// Create new service
|
||||
await serviceService.createService(serviceData);
|
||||
|
||||
toast({
|
||||
title: "Service created",
|
||||
description: `${data.name} has been added to monitoring.`,
|
||||
});
|
||||
}
|
||||
|
||||
onSuccess();
|
||||
if (!isEdit) {
|
||||
form.reset();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error ${isEdit ? 'updating' : 'creating'} service:`, error);
|
||||
toast({
|
||||
title: `Failed to ${isEdit ? 'update' : 'create'} service`,
|
||||
description: `An error occurred while ${isEdit ? 'updating' : 'creating'} the service.`,
|
||||
variant: "destructive",
|
||||
});
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6 pb-6">
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-sm font-medium text-muted-foreground border-b pb-2">Basic Information</h3>
|
||||
<ServiceBasicFields form={form} />
|
||||
<ServiceTypeField form={form} />
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-sm font-medium text-muted-foreground border-b pb-2">Configuration</h3>
|
||||
<ServiceConfigFields form={form} />
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-sm font-medium text-muted-foreground border-b pb-2">Regional Monitoring</h3>
|
||||
<ServiceRegionalFields form={form} />
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-sm font-medium text-muted-foreground border-b pb-2">Notifications</h3>
|
||||
<ServiceNotificationFields form={form} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ServiceFormActions
|
||||
isSubmitting={isSubmitting}
|
||||
onCancel={onCancel}
|
||||
submitLabel={isEdit ? "Update Service" : "Create Service"}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
// Re-export the ServiceForm component from the add-service directory
|
||||
export { ServiceForm } from './add-service/ServiceForm';
|
||||
@@ -28,7 +28,7 @@ export function ServiceMonitoringButton({ service, onStatusChange }: ServiceMoni
|
||||
|
||||
if (isMonitoring) {
|
||||
// Pause monitoring
|
||||
console.log(`Pausing monitoring for service ${service.id} (${service.name})`);
|
||||
// console.log(`Pausing monitoring for service ${service.id} (${service.name})`);
|
||||
await serviceService.pauseMonitoring(service.id);
|
||||
setIsMonitoring(false);
|
||||
|
||||
@@ -36,7 +36,7 @@ export function ServiceMonitoringButton({ service, onStatusChange }: ServiceMoni
|
||||
|
||||
// Send notification for paused status (only here, not in pauseMonitoring.ts)
|
||||
if (service.alerts !== "muted") {
|
||||
console.log("Sending pause notification from UI component");
|
||||
// console.log("Sending pause notification from UI component");
|
||||
// IMPORTANT: Direct call to the notification service to ensure a message is sent
|
||||
await notificationService.sendNotification({
|
||||
service: service,
|
||||
@@ -51,7 +51,7 @@ export function ServiceMonitoringButton({ service, onStatusChange }: ServiceMoni
|
||||
});
|
||||
} else {
|
||||
// Start/resume monitoring
|
||||
console.log(`Starting monitoring for service ${service.id} (${service.name})`);
|
||||
// console.log(`Starting monitoring for service ${service.id} (${service.name})`);
|
||||
|
||||
// First ensure we update the status in the database to not be paused anymore
|
||||
await serviceService.resumeMonitoring(service.id);
|
||||
@@ -66,7 +66,7 @@ export function ServiceMonitoringButton({ service, onStatusChange }: ServiceMoni
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error toggling monitoring:", error);
|
||||
// console.error("Error toggling monitoring:", error);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error",
|
||||
|
||||
@@ -56,7 +56,7 @@ export const UptimeBar = ({ uptime, status, serviceId, interval, serviceType }:
|
||||
);
|
||||
}
|
||||
|
||||
console.log('UptimeBar rendering consolidated items:', consolidatedItems.length);
|
||||
// console.log('UptimeBar rendering consolidated items:', consolidatedItems.length);
|
||||
|
||||
return (
|
||||
<div className="w-52">
|
||||
@@ -78,9 +78,9 @@ export const UptimeBar = ({ uptime, status, serviceId, interval, serviceType }:
|
||||
statuses.includes('paused') ? 'paused' : 'unknown';
|
||||
}
|
||||
|
||||
console.log(`Bar ${index} - Timestamp: ${slot.timestamp}, Items: ${slot.items.length}, Primary Status: ${primaryStatus}, Has Valid Data: ${hasValidData}`);
|
||||
// console.log(`Bar ${index} - Timestamp: ${slot.timestamp}, Items: ${slot.items.length}, Primary Status: ${primaryStatus}, Has Valid Data: ${hasValidData}`);
|
||||
slot.items.forEach((item, itemIndex) => {
|
||||
console.log(` Item ${itemIndex}: Source=${item.source}, Status=${item.status}, ResponseTime=${item.responseTime}, IsDefault=${item.isDefault}`);
|
||||
// console.log(` Item ${itemIndex}: Source=${item.source}, Status=${item.status}, ResponseTime=${item.responseTime}, IsDefault=${item.isDefault}`);
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { serviceSchema, ServiceFormData } from "./types";
|
||||
import { ServiceBasicFields } from "./ServiceBasicFields";
|
||||
import { ServiceTypeField } from "./ServiceTypeField";
|
||||
@@ -14,6 +13,7 @@ import { ServiceFormActions } from "./ServiceFormActions";
|
||||
import { serviceService } from "@/services/serviceService";
|
||||
import { Service } from "@/types/service.types";
|
||||
import { ServiceRegionalFields } from "./ServiceRegionalFields";
|
||||
import { getServiceFormDefaults, mapServiceToFormData, mapFormDataToServiceData } from "./serviceFormUtils";
|
||||
|
||||
interface ServiceFormProps {
|
||||
onSuccess: () => void;
|
||||
@@ -36,73 +36,28 @@ export function ServiceForm({
|
||||
// Initialize form with default values
|
||||
const form = useForm<ServiceFormData>({
|
||||
resolver: zodResolver(serviceSchema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
type: "http",
|
||||
url: "",
|
||||
port: "",
|
||||
interval: "60",
|
||||
retries: "3",
|
||||
notificationChannel: "",
|
||||
alertTemplate: "",
|
||||
regionalMonitoringEnabled: false,
|
||||
regionalAgent: "",
|
||||
},
|
||||
defaultValues: getServiceFormDefaults(),
|
||||
mode: "onBlur",
|
||||
});
|
||||
|
||||
// Populate form when initialData changes (separate from initialization)
|
||||
useEffect(() => {
|
||||
if (initialData && isEdit) {
|
||||
// Ensure the type is one of the allowed values
|
||||
const serviceType = (initialData.type || "http").toLowerCase();
|
||||
const validType = ["http", "ping", "tcp", "dns"].includes(serviceType)
|
||||
? serviceType as "http" | "ping" | "tcp" | "dns"
|
||||
: "http";
|
||||
|
||||
// For PING services, use host field; for DNS use domain field; for TCP use host field; others use url
|
||||
let urlValue = "";
|
||||
let portValue = "";
|
||||
|
||||
if (validType === "ping") {
|
||||
urlValue = initialData.host || "";
|
||||
} else if (validType === "dns") {
|
||||
urlValue = initialData.domain || "";
|
||||
} else if (validType === "tcp") {
|
||||
urlValue = initialData.host || "";
|
||||
portValue = String(initialData.port || "");
|
||||
} else {
|
||||
urlValue = initialData.url || "";
|
||||
}
|
||||
|
||||
// Handle regional monitoring data - ensure proper assignment display
|
||||
const regionalAgent = initialData.region_name && initialData.agent_id
|
||||
? `${initialData.region_name}|${initialData.agent_id}`
|
||||
: "";
|
||||
|
||||
// Reset the form with initial data values
|
||||
form.reset({
|
||||
name: initialData.name || "",
|
||||
type: validType,
|
||||
url: urlValue,
|
||||
port: portValue,
|
||||
interval: String(initialData.interval || 60),
|
||||
retries: String(initialData.retries || 3),
|
||||
notificationChannel: initialData.notificationChannel || "",
|
||||
alertTemplate: initialData.alertTemplate || "",
|
||||
regionalMonitoringEnabled: Boolean(initialData.regional_monitoring_enabled),
|
||||
regionalAgent: regionalAgent,
|
||||
});
|
||||
const formData = mapServiceToFormData(initialData);
|
||||
form.reset(formData);
|
||||
|
||||
// Log for debugging
|
||||
console.log("Populating form with data:", {
|
||||
type: validType,
|
||||
url: urlValue,
|
||||
port: portValue,
|
||||
regionalAgent,
|
||||
regionalMonitoringEnabled: Boolean(initialData.regional_monitoring_enabled),
|
||||
type: formData.type,
|
||||
url: formData.url,
|
||||
port: formData.port,
|
||||
regionalAgent: formData.regionalAgent,
|
||||
regionalMonitoringEnabled: formData.regionalMonitoringEnabled,
|
||||
regional_status: initialData.regional_status,
|
||||
region_name: initialData.region_name,
|
||||
agent_id: initialData.agent_id
|
||||
agent_id: initialData.agent_id,
|
||||
notification_status: initialData.notification_status,
|
||||
notificationChannels: formData.notificationChannels
|
||||
});
|
||||
}
|
||||
}, [initialData, isEdit, form]);
|
||||
@@ -114,42 +69,9 @@ export function ServiceForm({
|
||||
if (onSubmitStart) onSubmitStart();
|
||||
|
||||
try {
|
||||
console.log("Form data being submitted:", data); // Debug log for submitted data
|
||||
console.log("Form data being submitted:", data);
|
||||
|
||||
// Parse regional agent selection
|
||||
let regionName = "";
|
||||
let agentId = "";
|
||||
|
||||
// Only set region and agent if regional monitoring is enabled AND an agent is selected (not unassign)
|
||||
if (data.regionalMonitoringEnabled && data.regionalAgent && data.regionalAgent !== "") {
|
||||
const [parsedRegionName, parsedAgentId] = data.regionalAgent.split("|");
|
||||
regionName = parsedRegionName || "";
|
||||
agentId = parsedAgentId || "";
|
||||
}
|
||||
|
||||
// Prepare service data with proper field mapping
|
||||
const serviceData = {
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
interval: parseInt(data.interval),
|
||||
retries: parseInt(data.retries),
|
||||
notificationChannel: data.notificationChannel || undefined,
|
||||
alertTemplate: data.alertTemplate || undefined,
|
||||
regionalMonitoringEnabled: data.regionalMonitoringEnabled || false,
|
||||
// Always set region_name and agent_id - empty strings when unassigned
|
||||
regionName: regionName,
|
||||
agentId: agentId,
|
||||
// Map the URL field to appropriate database field based on service type
|
||||
...(data.type === "dns"
|
||||
? { domain: data.url, url: "", host: "", port: undefined } // DNS: store in domain field
|
||||
: data.type === "ping"
|
||||
? { host: data.url, url: "", domain: "", port: undefined } // PING: store in host field
|
||||
: data.type === "tcp"
|
||||
? { host: data.url, port: parseInt(data.port || "80"), url: "", domain: "" } // TCP: store in host and port fields
|
||||
: { url: data.url, domain: "", host: "", port: undefined } // HTTP: store in url field
|
||||
)
|
||||
};
|
||||
|
||||
const serviceData = mapFormDataToServiceData(data);
|
||||
console.log("Service data being sent:", serviceData);
|
||||
|
||||
if (isEdit && initialData) {
|
||||
@@ -188,7 +110,7 @@ export function ServiceForm({
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6 pb-4">
|
||||
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6 pb-6">
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-sm font-medium text-muted-foreground border-b pb-2">Basic Information</h3>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
|
||||
import { FormControl, FormField, FormItem, FormLabel } from "@/components/ui/form";
|
||||
import { FormControl, FormField, FormItem, FormLabel, FormDescription } from "@/components/ui/form";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { X } from "lucide-react";
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
import { ServiceFormData } from "./types";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { templateService } from "@/services/templateService";
|
||||
import { alertConfigService, AlertConfiguration } from "@/services/alertConfigService";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
@@ -16,11 +18,13 @@ export function ServiceNotificationFields({ form }: ServiceNotificationFieldsPro
|
||||
const [alertConfigs, setAlertConfigs] = useState<AlertConfiguration[]>([]);
|
||||
|
||||
// Get the current form values for debugging
|
||||
const notificationChannel = form.watch("notificationChannel");
|
||||
const notificationStatus = form.watch("notificationStatus");
|
||||
const notificationChannels = form.watch("notificationChannels") || [];
|
||||
const alertTemplate = form.watch("alertTemplate");
|
||||
|
||||
console.log("Current notification values:", {
|
||||
notificationChannel,
|
||||
notificationStatus,
|
||||
notificationChannels,
|
||||
alertTemplate
|
||||
});
|
||||
|
||||
@@ -30,12 +34,6 @@ export function ServiceNotificationFields({ form }: ServiceNotificationFieldsPro
|
||||
queryFn: () => alertConfigService.getAlertConfigurations(),
|
||||
});
|
||||
|
||||
// Fetch templates for template selection
|
||||
const { data: templates } = useQuery({
|
||||
queryKey: ['templates'],
|
||||
queryFn: () => templateService.getTemplates(),
|
||||
});
|
||||
|
||||
// Update alert configs when data is loaded
|
||||
useEffect(() => {
|
||||
if (alertConfigsData) {
|
||||
@@ -51,53 +49,110 @@ export function ServiceNotificationFields({ form }: ServiceNotificationFieldsPro
|
||||
// Log when form values change to debug
|
||||
useEffect(() => {
|
||||
console.log("Notification values changed:", {
|
||||
notificationChannel: form.getValues("notificationChannel"),
|
||||
alertTemplate: form.getValues("alertTemplate")
|
||||
notificationStatus: form.getValues("notificationStatus"),
|
||||
notificationChannels: form.getValues("notificationChannels")
|
||||
});
|
||||
}, [form.watch("notificationChannel"), form.watch("alertTemplate")]);
|
||||
}, [form.watch("notificationStatus"), form.watch("notificationChannels")]);
|
||||
|
||||
const handleChannelAdd = (channelId: string) => {
|
||||
const currentChannels = form.getValues("notificationChannels") || [];
|
||||
if (!currentChannels.includes(channelId)) {
|
||||
form.setValue("notificationChannels", [...currentChannels, channelId]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChannelRemove = (channelId: string) => {
|
||||
const currentChannels = form.getValues("notificationChannels") || [];
|
||||
form.setValue("notificationChannels", currentChannels.filter(id => id !== channelId));
|
||||
};
|
||||
|
||||
const getSelectedChannelNames = () => {
|
||||
return (notificationChannels || []).map(channelId => {
|
||||
const config = alertConfigs.find(c => c.id === channelId);
|
||||
return config ? `${config.notify_name} (${config.notification_type})` : channelId;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="notificationChannel"
|
||||
render={({ field }) => {
|
||||
// Important: We need to preserve the actual value for notification channel
|
||||
const fieldValue = field.value || "";
|
||||
const displayValue = fieldValue === "" ? "none" : fieldValue;
|
||||
|
||||
console.log("Rendering notification channel field with value:", {
|
||||
fieldValue,
|
||||
displayValue
|
||||
});
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>Notification Channel</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
console.log("Notification channel changed to:", value);
|
||||
field.onChange(value === "none" ? "" : value);
|
||||
}}
|
||||
value={displayValue}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select a notification channel" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">None</SelectItem>
|
||||
{alertConfigs.map((config) => (
|
||||
name="notificationStatus"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel className="text-base">
|
||||
Enable Notifications
|
||||
</FormLabel>
|
||||
<FormDescription>
|
||||
Enable or disable notifications for this service
|
||||
</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value === "enabled"}
|
||||
onCheckedChange={(checked) => {
|
||||
field.onChange(checked ? "enabled" : "disabled");
|
||||
// Clear notification channels when disabled
|
||||
if (!checked) {
|
||||
form.setValue("notificationChannels", []);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="notificationChannels"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Notification Channels</FormLabel>
|
||||
<FormDescription>
|
||||
{notificationStatus === "enabled"
|
||||
? "Select notification channels for this service"
|
||||
: "Enable notifications first to select channels"}
|
||||
</FormDescription>
|
||||
|
||||
{/* Display selected channels as badges */}
|
||||
{notificationChannels && notificationChannels.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 mb-2">
|
||||
{getSelectedChannelNames().map((channelName, index) => (
|
||||
<Badge key={notificationChannels[index]} variant="secondary" className="flex items-center gap-1">
|
||||
{channelName}
|
||||
<X
|
||||
className="h-3 w-3 cursor-pointer"
|
||||
onClick={() => handleChannelRemove(notificationChannels[index])}
|
||||
/>
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormControl>
|
||||
<Select
|
||||
onValueChange={handleChannelAdd}
|
||||
disabled={notificationStatus !== "enabled"}
|
||||
value="" // Always reset to empty after selection
|
||||
>
|
||||
<SelectTrigger className={notificationStatus !== "enabled" ? 'opacity-50' : ''}>
|
||||
<SelectValue placeholder="Add a notification channel" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{alertConfigs
|
||||
.filter(config => !notificationChannels?.includes(config.id || ""))
|
||||
.map((config) => (
|
||||
<SelectItem key={config.id} value={config.id || ""}>
|
||||
{config.notify_name} ({config.notification_type})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
@@ -118,24 +173,26 @@ export function ServiceNotificationFields({ form }: ServiceNotificationFieldsPro
|
||||
field.onChange(value === "default" ? "" : value);
|
||||
}}
|
||||
value={displayValue}
|
||||
disabled={notificationStatus !== "enabled"}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectTrigger className={notificationStatus !== "enabled" ? 'opacity-50' : ''}>
|
||||
<SelectValue placeholder="Select an alert template" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="default">Default</SelectItem>
|
||||
{templates?.map((template) => (
|
||||
<SelectItem key={template.id} value={template.id}>
|
||||
{template.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
{/* Add templates here when available */}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{notificationStatus === "enabled"
|
||||
? "Choose a template for alert messages"
|
||||
: "Enable notifications first to select template"}
|
||||
</FormDescription>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
|
||||
import { Service } from "@/types/service.types";
|
||||
import { ServiceFormData } from "./types";
|
||||
|
||||
export const getServiceFormDefaults = (): ServiceFormData => ({
|
||||
name: "",
|
||||
type: "http",
|
||||
url: "",
|
||||
port: "",
|
||||
interval: "60",
|
||||
retries: "3",
|
||||
notificationStatus: "disabled",
|
||||
notificationChannels: [],
|
||||
alertTemplate: "",
|
||||
regionalMonitoringEnabled: false,
|
||||
regionalAgent: "",
|
||||
});
|
||||
|
||||
export const mapServiceToFormData = (service: Service): ServiceFormData => {
|
||||
// Ensure the type is one of the allowed values
|
||||
const serviceType = (service.type || "http").toLowerCase();
|
||||
const validType = ["http", "ping", "tcp", "dns"].includes(serviceType)
|
||||
? serviceType as "http" | "ping" | "tcp" | "dns"
|
||||
: "http";
|
||||
|
||||
// For PING services, use host field; for DNS use domain field; for TCP use host field; others use url
|
||||
let urlValue = "";
|
||||
let portValue = "";
|
||||
|
||||
if (validType === "ping") {
|
||||
urlValue = service.host || "";
|
||||
} else if (validType === "dns") {
|
||||
urlValue = service.domain || "";
|
||||
} else if (validType === "tcp") {
|
||||
urlValue = service.host || "";
|
||||
portValue = String(service.port || "");
|
||||
} else {
|
||||
urlValue = service.url || "";
|
||||
}
|
||||
|
||||
// Handle regional monitoring data - check regional_status field
|
||||
const isRegionalEnabled = service.regional_status === "enabled";
|
||||
const regionalAgent = isRegionalEnabled && service.region_name && service.agent_id
|
||||
? `${service.region_name}|${service.agent_id}`
|
||||
: "";
|
||||
|
||||
// Handle notification channels - convert notification_channel and notificationChannel to array
|
||||
const notificationChannels: string[] = [];
|
||||
|
||||
// Check for notification_channel field (from database)
|
||||
if (service.notification_channel) {
|
||||
notificationChannels.push(service.notification_channel);
|
||||
}
|
||||
|
||||
// Also check for notificationChannel field (backward compatibility)
|
||||
if (service.notificationChannel && !notificationChannels.includes(service.notificationChannel)) {
|
||||
notificationChannels.push(service.notificationChannel);
|
||||
}
|
||||
|
||||
console.log("Mapping service to form data:", {
|
||||
serviceName: service.name,
|
||||
notification_status: service.notification_status,
|
||||
notification_channel: service.notification_channel,
|
||||
notificationChannel: service.notificationChannel,
|
||||
mappedChannels: notificationChannels
|
||||
});
|
||||
|
||||
return {
|
||||
name: service.name || "",
|
||||
type: validType,
|
||||
url: urlValue,
|
||||
port: portValue,
|
||||
interval: String(service.interval || 60),
|
||||
retries: String(service.retries || 3),
|
||||
notificationStatus: service.notification_status || "disabled",
|
||||
notificationChannels: notificationChannels,
|
||||
alertTemplate: service.alertTemplate === "default" ? "" : service.alertTemplate || "",
|
||||
regionalMonitoringEnabled: isRegionalEnabled,
|
||||
regionalAgent: regionalAgent,
|
||||
};
|
||||
};
|
||||
|
||||
export const mapFormDataToServiceData = (data: ServiceFormData) => {
|
||||
// Parse regional agent selection
|
||||
let regionName = "";
|
||||
let agentId = "";
|
||||
let regionalStatus: "enabled" | "disabled" = "disabled";
|
||||
|
||||
// Set regional status and agent data based on form values
|
||||
if (data.regionalMonitoringEnabled) {
|
||||
regionalStatus = "enabled";
|
||||
if (data.regionalAgent && data.regionalAgent !== "") {
|
||||
const [parsedRegionName, parsedAgentId] = data.regionalAgent.split("|");
|
||||
regionName = parsedRegionName || "";
|
||||
agentId = parsedAgentId || "";
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare service data with proper field mapping
|
||||
return {
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
interval: parseInt(data.interval),
|
||||
retries: parseInt(data.retries),
|
||||
notificationStatus: data.notificationStatus || "disabled",
|
||||
notificationChannels: data.notificationChannels || [],
|
||||
alertTemplate: data.alertTemplate === "default" ? "" : data.alertTemplate,
|
||||
// Use regional_status field instead of regionalMonitoringEnabled
|
||||
regionalStatus: regionalStatus,
|
||||
regionName: regionName,
|
||||
agentId: agentId,
|
||||
// Map the URL field to appropriate database field based on service type
|
||||
...(data.type === "dns"
|
||||
? { domain: data.url, url: "", host: "", port: undefined } // DNS: store in domain field
|
||||
: data.type === "ping"
|
||||
? { host: data.url, url: "", domain: "", port: undefined } // PING: store in host field
|
||||
: data.type === "tcp"
|
||||
? { host: data.url, port: parseInt(data.port || "80"), url: "", domain: "" } // TCP: store in host and port fields
|
||||
: { url: data.url, domain: "", host: "", port: undefined } // HTTP: store in url field
|
||||
)
|
||||
};
|
||||
};
|
||||
@@ -11,11 +11,12 @@ export const serviceSchema = z.object({
|
||||
port: z.string().optional(),
|
||||
interval: z.string(),
|
||||
retries: z.string(),
|
||||
notificationChannel: z.string().optional(),
|
||||
notificationStatus: z.enum(["enabled", "disabled"]).optional(),
|
||||
notificationChannels: z.array(z.string()).optional(),
|
||||
alertTemplate: z.string().optional(),
|
||||
// Regional monitoring fields
|
||||
regionalMonitoringEnabled: z.boolean().optional(),
|
||||
regionalAgent: z.string().optional(),
|
||||
});
|
||||
|
||||
export type ServiceFormData = z.infer<typeof serviceSchema>;
|
||||
export type ServiceFormData = z.infer<typeof serviceSchema>;
|
||||
|
||||
@@ -46,23 +46,23 @@ export const useConsolidatedUptimeData = ({ serviceId, serviceType, status, inte
|
||||
queryKey: ['consolidatedUptimeHistory', serviceId, serviceType],
|
||||
queryFn: async () => {
|
||||
if (!serviceId) {
|
||||
console.log('No serviceId provided, skipping fetch');
|
||||
// console.log('No serviceId provided, skipping fetch');
|
||||
return [];
|
||||
}
|
||||
console.log(`Fetching consolidated uptime data for service ${serviceId} of type ${serviceType}`);
|
||||
// console.log(`Fetching consolidated uptime data for service ${serviceId} of type ${serviceType}`);
|
||||
|
||||
// Get ALL uptime history data - this should include both default and regional data
|
||||
const rawData = await uptimeService.getUptimeHistory(serviceId, 100, undefined, undefined, serviceType);
|
||||
|
||||
console.log(`Retrieved ${rawData.length} total records for service ${serviceId}`);
|
||||
console.log('Raw data sample:', rawData.slice(0, 5).map(d => ({
|
||||
timestamp: d.timestamp,
|
||||
region_name: d.region_name,
|
||||
agent_id: d.agent_id,
|
||||
status: d.status,
|
||||
service_id: d.service_id,
|
||||
response_time: d.responseTime
|
||||
})));
|
||||
// console.log(`Retrieved ${rawData.length} total records for service ${serviceId}`);
|
||||
// console.log('Raw data sample:', rawData.slice(0, 5).map(d => ({
|
||||
// timestamp: d.timestamp,
|
||||
// region_name: d.region_name,
|
||||
// agent_id: d.agent_id,
|
||||
// status: d.status,
|
||||
// service_id: d.service_id,
|
||||
// response_time: d.responseTime
|
||||
// })));
|
||||
|
||||
return rawData;
|
||||
},
|
||||
@@ -78,7 +78,7 @@ export const useConsolidatedUptimeData = ({ serviceId, serviceType, status, inte
|
||||
const processConsolidatedData = (data: UptimeData[]): ConsolidatedTimeSlot[] => {
|
||||
if (!data || data.length === 0) return [];
|
||||
|
||||
console.log(`Processing ${data.length} uptime records for consolidation`);
|
||||
// console.log(`Processing ${data.length} uptime records for consolidation`);
|
||||
|
||||
// Create a map to group records by normalized timestamp (minute precision)
|
||||
const timeSlotMap = new Map<string, Array<UptimeData & { source: string; isDefault: boolean }>>();
|
||||
@@ -99,17 +99,17 @@ export const useConsolidatedUptimeData = ({ serviceId, serviceType, status, inte
|
||||
// Regional monitoring data
|
||||
sourceName = `${regionName} (Agent ${agentId})`;
|
||||
isDefault = false;
|
||||
console.log(`Found regional data: ${sourceName} for normalized timestamp ${normalizedTimestamp}`);
|
||||
// console.log(`Found regional data: ${sourceName} for normalized timestamp ${normalizedTimestamp}`);
|
||||
} else if (agentId && !regionName) {
|
||||
// Default monitoring with specific agent
|
||||
sourceName = `Default (Agent ${agentId})`;
|
||||
isDefault = true;
|
||||
console.log(`Found default monitoring: ${sourceName} for normalized timestamp ${normalizedTimestamp}`);
|
||||
// console.log(`Found default monitoring: ${sourceName} for normalized timestamp ${normalizedTimestamp}`);
|
||||
} else {
|
||||
// Default monitoring fallback
|
||||
sourceName = 'Default (Agent 1)';
|
||||
sourceName = 'Default System Check (Agent 1)';
|
||||
isDefault = true;
|
||||
console.log(`Using fallback default monitoring for normalized timestamp ${normalizedTimestamp}`);
|
||||
// console.log(`Using fallback default monitoring for normalized timestamp ${normalizedTimestamp}`);
|
||||
}
|
||||
|
||||
// Get or create the array for this normalized timestamp
|
||||
@@ -130,9 +130,9 @@ export const useConsolidatedUptimeData = ({ serviceId, serviceType, status, inte
|
||||
isDefault
|
||||
});
|
||||
|
||||
console.log(`Added record to normalized timestamp ${normalizedTimestamp}: Source=${sourceName}, Status=${record.status}, IsDefault=${isDefault}, ResponseTime=${record.responseTime}ms`);
|
||||
// console.log(`Added record to normalized timestamp ${normalizedTimestamp}: Source=${sourceName}, Status=${record.status}, IsDefault=${isDefault}, ResponseTime=${record.responseTime}ms`);
|
||||
} else {
|
||||
console.log(`Skipping duplicate record for source ${sourceName} at timestamp ${normalizedTimestamp}`);
|
||||
// console.log(`Skipping duplicate record for source ${sourceName} at timestamp ${normalizedTimestamp}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -150,13 +150,13 @@ export const useConsolidatedUptimeData = ({ serviceId, serviceType, status, inte
|
||||
.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
|
||||
.slice(0, 20); // Take the most recent 20 time slots
|
||||
|
||||
console.log(`Created ${consolidatedTimeline.length} consolidated time slots with normalized timestamps`);
|
||||
consolidatedTimeline.forEach((slot, index) => {
|
||||
console.log(`Slot ${index} - Normalized Timestamp: ${slot.timestamp}, Items: ${slot.items.length}`);
|
||||
slot.items.forEach((item, itemIndex) => {
|
||||
console.log(` Item ${itemIndex}: Source=${item.source}, Status=${item.status}, ResponseTime=${item.responseTime}ms, IsDefault=${item.isDefault}`);
|
||||
});
|
||||
});
|
||||
// console.log(`Created ${consolidatedTimeline.length} consolidated time slots with normalized timestamps`);
|
||||
// consolidatedTimeline.forEach((slot, index) => {
|
||||
// console.log(`Slot ${index} - Normalized Timestamp: ${slot.timestamp}, Items: ${slot.items.length}`);
|
||||
// slot.items.forEach((item, itemIndex) => {
|
||||
// console.log(` Item ${itemIndex}: Source=${item.source}, Status=${item.status}, ResponseTime=${item.responseTime}ms, IsDefault=${item.isDefault}`);
|
||||
// });
|
||||
// });
|
||||
|
||||
return consolidatedTimeline;
|
||||
};
|
||||
@@ -164,12 +164,12 @@ export const useConsolidatedUptimeData = ({ serviceId, serviceType, status, inte
|
||||
// Update consolidated items when data changes
|
||||
useEffect(() => {
|
||||
if (uptimeData && uptimeData.length > 0) {
|
||||
console.log(`Processing consolidated uptime data for service ${serviceId}`);
|
||||
// console.log(`Processing consolidated uptime data for service ${serviceId}`);
|
||||
const processedData = processConsolidatedData(uptimeData);
|
||||
|
||||
// If service is currently paused, override ONLY the latest (first) bar with paused status
|
||||
if (status === "paused" && processedData.length > 0) {
|
||||
console.log(`Service ${serviceId} is paused, overriding latest bar with paused status`);
|
||||
// console.log(`Service ${serviceId} is paused, overriding latest bar with paused status`);
|
||||
|
||||
// Create a paused entry for the latest timestamp
|
||||
const latestTimestamp = new Date();
|
||||
@@ -191,11 +191,11 @@ export const useConsolidatedUptimeData = ({ serviceId, serviceType, status, inte
|
||||
|
||||
// Replace the first item with paused status, keep the rest as historical data
|
||||
const updatedData = [pausedSlot, ...processedData.slice(0, 19)];
|
||||
console.log(`Updated data with paused latest bar, total bars: ${updatedData.length}`);
|
||||
// console.log(`Updated data with paused latest bar, total bars: ${updatedData.length}`);
|
||||
setConsolidatedItems(updatedData);
|
||||
} else {
|
||||
// Service is active (up/down/warning) - merge real data with any existing paused bars
|
||||
console.log(`Service ${serviceId} is active with status: ${status}, merging with existing paused bars`);
|
||||
// console.log(`Service ${serviceId} is active with status: ${status}, merging with existing paused bars`);
|
||||
|
||||
// Get existing paused bars from current state
|
||||
const existingPausedBars = consolidatedItems.filter(slot =>
|
||||
@@ -213,7 +213,7 @@ export const useConsolidatedUptimeData = ({ serviceId, serviceType, status, inte
|
||||
const hasConflict = processedData.some(slot => slot.timestamp === pausedSlot.timestamp);
|
||||
if (!hasConflict) {
|
||||
mergedData.push(pausedSlot);
|
||||
console.log(`Preserved paused bar at timestamp: ${pausedSlot.timestamp}`);
|
||||
// console.log(`Preserved paused bar at timestamp: ${pausedSlot.timestamp}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -222,12 +222,12 @@ export const useConsolidatedUptimeData = ({ serviceId, serviceType, status, inte
|
||||
.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
|
||||
.slice(0, 20);
|
||||
|
||||
console.log(`Final merged data has ${finalData.length} slots`);
|
||||
// console.log(`Final merged data has ${finalData.length} slots`);
|
||||
setConsolidatedItems(finalData);
|
||||
}
|
||||
} else if (!serviceId || (uptimeData && uptimeData.length === 0)) {
|
||||
// Generate placeholder data when no real data is available
|
||||
console.log(`No uptime data available for service ${serviceId}, generating placeholder with status: ${status}`);
|
||||
// console.log(`No uptime data available for service ${serviceId}, generating placeholder with status: ${status}`);
|
||||
|
||||
// Use the actual service status for placeholder data
|
||||
const statusValue = status === "paused" ? "paused" :
|
||||
@@ -252,7 +252,7 @@ export const useConsolidatedUptimeData = ({ serviceId, serviceType, status, inte
|
||||
};
|
||||
});
|
||||
|
||||
console.log(`Generated ${placeholderHistory.length} placeholder slots with status: ${statusValue}`);
|
||||
// console.log(`Generated ${placeholderHistory.length} placeholder slots with status: ${statusValue}`);
|
||||
setConsolidatedItems(placeholderHistory);
|
||||
}
|
||||
}, [uptimeData, serviceId, status, interval, consolidatedItems]);
|
||||
|
||||
@@ -23,7 +23,7 @@ export function useServiceActions(initialServices: Service[]) {
|
||||
};
|
||||
|
||||
const handleViewDetail = (service: Service) => {
|
||||
console.log(`Navigating to service detail for service ID: ${service.id}`);
|
||||
// console.log(`Navigating to service detail for service ID: ${service.id}`);
|
||||
navigate(`/service/${service.id}`);
|
||||
};
|
||||
|
||||
@@ -115,7 +115,7 @@ export function useServiceActions(initialServices: Service[]) {
|
||||
|
||||
setSelectedService(null);
|
||||
} catch (error) {
|
||||
console.error("Error deleting service:", error);
|
||||
// console.error("Error deleting service:", error);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error",
|
||||
@@ -134,7 +134,7 @@ export function useServiceActions(initialServices: Service[]) {
|
||||
// Toggle the mute alerts status for this specific service
|
||||
const newMuteStatus = !isMuted;
|
||||
|
||||
console.log(`${newMuteStatus ? "Muting" : "Unmuting"} alerts for service ${service.id} (${service.name})`);
|
||||
// console.log(`${newMuteStatus ? "Muting" : "Unmuting"} alerts for service ${service.id} (${service.name})`);
|
||||
|
||||
// First update the local state immediately for better UI responsiveness
|
||||
// Using proper type casting to ensure TypeScript knows we're creating valid Service objects
|
||||
@@ -165,7 +165,7 @@ export function useServiceActions(initialServices: Service[]) {
|
||||
await queryClient.invalidateQueries({ queryKey: ["services"] });
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error updating alert settings:", error);
|
||||
// console.error("Error updating alert settings:", error);
|
||||
|
||||
// Revert the local state change if the server update failed
|
||||
const revertedServices = services.map(s => {
|
||||
|
||||
@@ -27,8 +27,8 @@ export function LatestChecksTable({ uptimeData }: { uptimeData: UptimeData[] })
|
||||
// Filter incidents by status
|
||||
const incidents = useMemo(() => {
|
||||
const statusChanges = getStatusChangeEvents(uptimeData);
|
||||
console.log(`Total status changes: ${statusChanges.length}`);
|
||||
console.log(`Status types in incidents: ${[...new Set(statusChanges.map(i => i.status))].join(', ')}`);
|
||||
// console.log(`Total status changes: ${statusChanges.length}`);
|
||||
// console.log(`Status types in incidents: ${[...new Set(statusChanges.map(i => i.status))].join(', ')}`);
|
||||
|
||||
if (statusFilter === "all") return statusChanges;
|
||||
|
||||
@@ -58,7 +58,7 @@ export function LatestChecksTable({ uptimeData }: { uptimeData: UptimeData[] })
|
||||
// Calculate items per page for pagination display
|
||||
const itemsPerPage = pageSize === "all" ? incidents.length : parseInt(pageSize, 10);
|
||||
|
||||
console.log(`Status Filter: ${statusFilter}, Incidents: ${incidents.length}, Includes paused: ${incidents.some(i => i.status === 'paused')}`);
|
||||
// console.log(`Status Filter: ${statusFilter}, Incidents: ${incidents.length}, Includes paused: ${incidents.some(i => i.status === 'paused')}`);
|
||||
|
||||
return (
|
||||
<Card className={`mb-6 transition-colors ${theme === 'dark' ? 'bg-card border-border' : 'bg-white border-gray-200'}`}>
|
||||
|
||||
@@ -68,7 +68,7 @@ export const getStatusChangeEvents = (uptimeData: UptimeData[]): UptimeData[] =>
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Found ${statusChanges.length} status changes, including paused status changes: ${statusChanges.some(i => i.status === 'paused')}`);
|
||||
// console.log(`Found ${statusChanges.length} status changes, including paused status changes: ${statusChanges.some(i => i.status === 'paused')}`);
|
||||
|
||||
return statusChanges;
|
||||
};
|
||||
|
||||
@@ -13,3 +13,4 @@ export * from './ServicesTableView';
|
||||
export * from './ServiceDeleteDialog';
|
||||
export * from './ServiceHistoryDialog';
|
||||
export * from './ServiceEditDialog';
|
||||
export * from './ServiceForm';
|
||||
@@ -40,11 +40,11 @@ export const TemplateDialog: React.FC<TemplateDialogProps> = ({
|
||||
// For debugging purposes
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
console.log("Template dialog opened. Edit mode:", isEditMode, "Template ID:", templateId);
|
||||
// console.log("Template dialog opened. Edit mode:", isEditMode, "Template ID:", templateId);
|
||||
|
||||
// Log form values when they change
|
||||
const subscription = form.watch((value) => {
|
||||
console.log("Current form values:", value);
|
||||
// console.log("Current form values:", value);
|
||||
});
|
||||
|
||||
return () => subscription.unsubscribe();
|
||||
|
||||
@@ -54,7 +54,7 @@ export const AddSSLCertificateForm = ({
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const configs = await alertConfigService.getAlertConfigurations();
|
||||
console.log("Fetched notification channels:", configs);
|
||||
// console.log("Fetched notification channels:", configs);
|
||||
// Only include enabled channels
|
||||
const enabledConfigs = configs.filter(config => {
|
||||
// Handle the possibility of enabled being a string
|
||||
@@ -66,7 +66,7 @@ export const AddSSLCertificateForm = ({
|
||||
});
|
||||
setAlertConfigs(enabledConfigs);
|
||||
} catch (error) {
|
||||
console.error("Error fetching notification channels:", error);
|
||||
// console.error("Error fetching notification channels:", error);
|
||||
toast.error(t('failedToLoadCertificates'));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -90,7 +90,7 @@ export const AddSSLCertificateForm = ({
|
||||
await onSubmit(certData);
|
||||
form.reset();
|
||||
} catch (error) {
|
||||
console.error("Error adding SSL certificate:", error);
|
||||
// console.error("Error adding SSL certificate:", error);
|
||||
toast.error(t('failedToAddCertificate'));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -52,7 +52,7 @@ export const EditSSLCertificateForm = ({ certificate, onSubmit, onCancel, isPend
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const configs = await alertConfigService.getAlertConfigurations();
|
||||
console.log("Fetched notification channels:", configs);
|
||||
// console.log("Fetched notification channels:", configs);
|
||||
// Only include enabled channels
|
||||
const enabledConfigs = configs.filter(config => {
|
||||
// Handle the possibility of enabled being a string
|
||||
@@ -64,7 +64,7 @@ export const EditSSLCertificateForm = ({ certificate, onSubmit, onCancel, isPend
|
||||
});
|
||||
setAlertConfigs(enabledConfigs);
|
||||
} catch (error) {
|
||||
console.error("Error fetching notification channels:", error);
|
||||
// console.error("Error fetching notification channels:", error);
|
||||
toast.error(t('failedToLoadCertificates'));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -30,12 +30,12 @@ export const SSLDomainContent = () => {
|
||||
queryKey: ['ssl-certificates'],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
console.log("Fetching SSL certificates from SSLDomainContent...");
|
||||
// console.log("Fetching SSL certificates from SSLDomainContent...");
|
||||
const result = await fetchSSLCertificates();
|
||||
console.log("Received SSL certificates:", result);
|
||||
// console.log("Received SSL certificates:", result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("Error fetching certificates:", error);
|
||||
// console.error("Error fetching certificates:", error);
|
||||
toast.error(t('failedToLoadCertificates'));
|
||||
throw error;
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export const SSLDomainContent = () => {
|
||||
toast.success(t('sslCertificateAdded'));
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error("Error adding SSL certificate:", error);
|
||||
// console.error("Error adding SSL certificate:", error);
|
||||
toast.error(error instanceof Error ? error.message : t('failedToAddCertificate'));
|
||||
}
|
||||
});
|
||||
@@ -61,7 +61,7 @@ export const SSLDomainContent = () => {
|
||||
// Edit certificate mutation - Updated to ensure thresholds are properly updated
|
||||
const editMutation = useMutation({
|
||||
mutationFn: async (certificate: SSLCertificate) => {
|
||||
console.log("Updating certificate with data:", certificate);
|
||||
// console.log("Updating certificate with data:", certificate);
|
||||
|
||||
// Create the update data object
|
||||
const updateData = {
|
||||
@@ -70,12 +70,12 @@ export const SSLDomainContent = () => {
|
||||
notification_channel: certificate.notification_channel,
|
||||
};
|
||||
|
||||
console.log("Update data to be sent:", updateData);
|
||||
// console.log("Update data to be sent:", updateData);
|
||||
|
||||
// Update certificate in the database using PocketBase directly
|
||||
const updated = await pb.collection('ssl_certificates').update(certificate.id, updateData);
|
||||
|
||||
console.log("PocketBase update response:", updated);
|
||||
// console.log("PocketBase update response:", updated);
|
||||
|
||||
// After updating the settings, refresh the certificate to ensure it's up to date
|
||||
// This will also check if notification needs to be sent based on updated thresholds
|
||||
@@ -90,7 +90,7 @@ export const SSLDomainContent = () => {
|
||||
toast.success(t('sslCertificateUpdated'));
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error("Error updating SSL certificate:", error);
|
||||
// console.error("Error updating SSL certificate:", error);
|
||||
toast.error(error instanceof Error ? error.message : t('failedToUpdateCertificate'));
|
||||
}
|
||||
});
|
||||
@@ -103,7 +103,7 @@ export const SSLDomainContent = () => {
|
||||
toast.success(t('sslCertificateDeleted'));
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error("Error deleting SSL certificate:", error);
|
||||
// console.error("Error deleting SSL certificate:", error);
|
||||
toast.error(error instanceof Error ? error.message : t('failedToDeleteCertificate'));
|
||||
}
|
||||
});
|
||||
@@ -117,7 +117,7 @@ export const SSLDomainContent = () => {
|
||||
toast.success(t('sslCertificateRefreshed').replace('{domain}', data.domain));
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error("Error refreshing SSL certificate:", error);
|
||||
// console.error("Error refreshing SSL certificate:", error);
|
||||
|
||||
let errorMessage = t('failedToCheckCertificate');
|
||||
|
||||
@@ -149,7 +149,7 @@ export const SSLDomainContent = () => {
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error("Error refreshing all certificates:", error);
|
||||
// console.error("Error refreshing all certificates:", error);
|
||||
toast.error(t('failedToCheckCertificate'));
|
||||
setIsRefreshingAll(false);
|
||||
|
||||
@@ -174,7 +174,7 @@ export const SSLDomainContent = () => {
|
||||
};
|
||||
|
||||
const handleUpdateCertificate = (certificate: SSLCertificate) => {
|
||||
console.log("Handling certificate update with data:", certificate);
|
||||
// console.log("Handling certificate update with data:", certificate);
|
||||
editMutation.mutate(certificate);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export function useSystemSettings() {
|
||||
queryKey: ['generalSettings'],
|
||||
queryFn: async (): Promise<GeneralSettings | null> => {
|
||||
try {
|
||||
console.log('Fetching settings from API...');
|
||||
// console.log('Fetching settings from API...');
|
||||
const response = await fetch('/api/settings', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -38,14 +38,14 @@ export function useSystemSettings() {
|
||||
body: JSON.stringify({ action: 'getSettings' })
|
||||
});
|
||||
|
||||
console.log('API response status:', response.status);
|
||||
// console.log('API response status:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result: ApiResponse = await response.json();
|
||||
console.log('API response data:', result);
|
||||
// console.log('API response data:', result);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.message || 'Failed to fetch settings');
|
||||
@@ -53,7 +53,7 @@ export function useSystemSettings() {
|
||||
|
||||
return result.data || null;
|
||||
} catch (error) {
|
||||
console.error('Error fetching settings:', error);
|
||||
// console.error('Error fetching settings:', error);
|
||||
toast({
|
||||
title: t("errorFetchingSettings", "settings"),
|
||||
description: error instanceof Error ? error.message : String(error),
|
||||
@@ -68,7 +68,7 @@ export function useSystemSettings() {
|
||||
// Update settings mutation
|
||||
const updateSettingsMutation = useMutation({
|
||||
mutationFn: async (updatedSettings: GeneralSettings): Promise<GeneralSettings> => {
|
||||
console.log('Updating settings:', updatedSettings);
|
||||
// console.log('Updating settings:', updatedSettings);
|
||||
const response = await fetch('/api/settings', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -101,7 +101,7 @@ export function useSystemSettings() {
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error('Error updating settings:', error);
|
||||
// console.error('Error updating settings:', error);
|
||||
toast({
|
||||
title: t("errorSavingSettings", "settings"),
|
||||
description: error instanceof Error ? error.message : String(error),
|
||||
@@ -113,7 +113,7 @@ export function useSystemSettings() {
|
||||
// Test email connection
|
||||
const testEmailConnectionMutation = useMutation({
|
||||
mutationFn: async (smtpConfig: any): Promise<{success: boolean, message: string}> => {
|
||||
console.log('Testing email connection:', smtpConfig);
|
||||
// console.log('Testing email connection:', smtpConfig);
|
||||
const response = await fetch('/api/settings', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -143,7 +143,7 @@ export function useSystemSettings() {
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error('Error testing connection:', error);
|
||||
// console.error('Error testing connection:', error);
|
||||
toast({
|
||||
title: t("connectionFailed", "settings"),
|
||||
description: error instanceof Error ? error.message : String(error),
|
||||
|
||||
@@ -20,7 +20,7 @@ const Dashboard = () => {
|
||||
|
||||
// For debugging user data
|
||||
useEffect(() => {
|
||||
console.log("Current user data:", currentUser);
|
||||
// console.log("Current user data:", currentUser);
|
||||
}, [currentUser]);
|
||||
|
||||
// Handle logout
|
||||
@@ -40,7 +40,7 @@ const Dashboard = () => {
|
||||
useEffect(() => {
|
||||
const startActiveServices = async () => {
|
||||
await serviceService.startAllActiveServices();
|
||||
console.log("Active services monitoring started");
|
||||
// console.log("Active services monitoring started");
|
||||
};
|
||||
|
||||
startActiveServices();
|
||||
|
||||
@@ -26,13 +26,13 @@ const SslDomain = () => {
|
||||
const { data: certificates = [], isLoading, error, refetch } = useQuery({
|
||||
queryKey: ['ssl-certificates'],
|
||||
queryFn: async () => {
|
||||
console.log("Fetching SSL certificates from SslDomain page...");
|
||||
// console.log("Fetching SSL certificates from SslDomain page...");
|
||||
try {
|
||||
const result = await fetchSSLCertificates();
|
||||
console.log("SSL certificates fetch successful, count:", result.length);
|
||||
// console.log("SSL certificates fetch successful, count:", result.length);
|
||||
return result;
|
||||
} catch (err) {
|
||||
console.error("Error fetching SSL certificates from page:", err);
|
||||
// console.error("Error fetching SSL certificates from page:", err);
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
@@ -46,7 +46,7 @@ const SslDomain = () => {
|
||||
const checkCertificates = async () => {
|
||||
// Check if we should run daily check
|
||||
if (shouldRunDailyCheck()) {
|
||||
console.log("Running daily SSL certificate check...");
|
||||
// console.log("Running daily SSL certificate check...");
|
||||
await checkAllCertificatesAndNotify();
|
||||
// Refresh certificate list after daily check
|
||||
refetch();
|
||||
|
||||
@@ -22,13 +22,13 @@ export interface AlertConfiguration {
|
||||
|
||||
export const alertConfigService = {
|
||||
async getAlertConfigurations(): Promise<AlertConfiguration[]> {
|
||||
console.info("Fetching alert configurations");
|
||||
// console.info("Fetching alert configurations");
|
||||
try {
|
||||
const response = await pb.collection('alert_configurations').getList(1, 50);
|
||||
console.info("Alert configurations response:", response);
|
||||
// console.info("Alert configurations response:", response);
|
||||
return response.items as AlertConfiguration[];
|
||||
} catch (error) {
|
||||
console.error("Error fetching alert configurations:", error);
|
||||
// console.error("Error fetching alert configurations:", error);
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to load notification settings",
|
||||
@@ -39,17 +39,17 @@ export const alertConfigService = {
|
||||
},
|
||||
|
||||
async createAlertConfiguration(config: Omit<AlertConfiguration, 'id' | 'collectionId' | 'collectionName' | 'created' | 'updated'>): Promise<AlertConfiguration | null> {
|
||||
console.info("Creating alert configuration:", config);
|
||||
// console.info("Creating alert configuration:", config);
|
||||
try {
|
||||
const result = await pb.collection('alert_configurations').create(config);
|
||||
console.info("Alert configuration created:", result);
|
||||
// console.info("Alert configuration created:", result);
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Notification settings saved successfully",
|
||||
});
|
||||
return result as AlertConfiguration;
|
||||
} catch (error) {
|
||||
console.error("Error creating alert configuration:", error);
|
||||
// console.error("Error creating alert configuration:", error);
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to save notification settings",
|
||||
@@ -60,17 +60,17 @@ export const alertConfigService = {
|
||||
},
|
||||
|
||||
async updateAlertConfiguration(id: string, config: Partial<AlertConfiguration>): Promise<AlertConfiguration | null> {
|
||||
console.info(`Updating alert configuration ${id}:`, config);
|
||||
// console.info(`Updating alert configuration ${id}:`, config);
|
||||
try {
|
||||
const result = await pb.collection('alert_configurations').update(id, config);
|
||||
console.info("Alert configuration updated:", result);
|
||||
// console.info("Alert configuration updated:", result);
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Notification settings updated successfully",
|
||||
});
|
||||
return result as AlertConfiguration;
|
||||
} catch (error) {
|
||||
console.error("Error updating alert configuration:", error);
|
||||
// console.error("Error updating alert configuration:", error);
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to update notification settings",
|
||||
@@ -81,17 +81,17 @@ export const alertConfigService = {
|
||||
},
|
||||
|
||||
async deleteAlertConfiguration(id: string): Promise<boolean> {
|
||||
console.info(`Deleting alert configuration ${id}`);
|
||||
// console.info(`Deleting alert configuration ${id}`);
|
||||
try {
|
||||
await pb.collection('alert_configurations').delete(id);
|
||||
console.info("Alert configuration deleted");
|
||||
// console.info("Alert configuration deleted");
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Notification channel removed",
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error deleting alert configuration:", error);
|
||||
// console.error("Error deleting alert configuration:", error);
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to remove notification channel",
|
||||
|
||||
@@ -19,7 +19,7 @@ export const authService = {
|
||||
try {
|
||||
// First try to login as a regular admin user
|
||||
try {
|
||||
console.log("Attempting to login as admin user");
|
||||
// console.log("Attempting to login as admin user");
|
||||
const authData = await pb.collection('users').authWithPassword(email, password);
|
||||
|
||||
return {
|
||||
@@ -30,7 +30,7 @@ export const authService = {
|
||||
role: authData.record.role || "admin"
|
||||
};
|
||||
} catch (error) {
|
||||
console.log("Failed to login as admin, trying as superadmin", error);
|
||||
// console.log("Failed to login as admin, trying as superadmin", error);
|
||||
|
||||
// If regular user login fails, try superadmin
|
||||
const authData = await pb.collection('_superusers').authWithPassword(email, password);
|
||||
@@ -44,7 +44,7 @@ export const authService = {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error);
|
||||
// console.error('Login failed:', error);
|
||||
throw new Error('Authentication failed. Please check your credentials.');
|
||||
}
|
||||
},
|
||||
@@ -63,7 +63,7 @@ export const authService = {
|
||||
if (!userData) return null;
|
||||
|
||||
// Log the full user data for debugging
|
||||
console.log("Raw user data from authStore:", userData);
|
||||
//console.log("Raw user data from authStore:", userData);
|
||||
|
||||
// Determine if this is a superadmin by checking the collection name
|
||||
const isSuperAdmin = userData.collectionName === '_superusers';
|
||||
@@ -90,13 +90,13 @@ export const authService = {
|
||||
try {
|
||||
// Fetch the latest user data from the server
|
||||
const userId = (pb.authStore.model as any).id;
|
||||
console.log("Refreshing user data for ID:", userId);
|
||||
// console.log("Refreshing user data for ID:", userId);
|
||||
|
||||
// Use the getOne method directly to refresh the auth store
|
||||
await pb.collection('users').getOne(userId);
|
||||
console.log("User data refreshed successfully");
|
||||
// console.log("User data refreshed successfully");
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh user data:', error);
|
||||
// console.error('Failed to refresh user data:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -3,12 +3,12 @@ import { IncidentItem } from './types';
|
||||
|
||||
// Export functions to update and invalidate the cache
|
||||
export const updateCache = (data: IncidentItem[]) => {
|
||||
console.log(`Updating cache with ${data.length} incidents`);
|
||||
// console.log(`Updating cache with ${data.length} incidents`);
|
||||
// The actual cache is now maintained in incidentFetch.ts
|
||||
};
|
||||
|
||||
// Reset cache and request state
|
||||
export const invalidateCache = () => {
|
||||
console.log('Invalidating incidents cache');
|
||||
// console.log('Invalidating incidents cache');
|
||||
// The invalidation is handled in incidentFetch.ts implementation
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export const isCacheValid = (): boolean => {
|
||||
export const getAllIncidents = async (forceRefresh = false): Promise<IncidentItem[]> => {
|
||||
// If a request is in progress, wait for it to complete rather than making a new one
|
||||
if (isRequestInProgress) {
|
||||
console.log('Request already in progress, waiting for completion');
|
||||
// console.log('Request already in progress, waiting for completion');
|
||||
try {
|
||||
if (pendingRequest) {
|
||||
await pendingRequest;
|
||||
@@ -39,12 +39,12 @@ export const getAllIncidents = async (forceRefresh = false): Promise<IncidentIte
|
||||
|
||||
// Use cache if available, not expired, and not forced refresh
|
||||
if (!forceRefresh && isCacheValid()) {
|
||||
console.log('Using cached incidents data from', new Date(incidentsCache!.timestamp).toLocaleTimeString());
|
||||
// console.log('Using cached incidents data from', new Date(incidentsCache!.timestamp).toLocaleTimeString());
|
||||
return incidentsCache!.data;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('Fetching all incidents from API...');
|
||||
// console.log('Fetching all incidents from API...');
|
||||
isRequestInProgress = true;
|
||||
|
||||
// Implement timeout for the request
|
||||
@@ -72,7 +72,7 @@ export const getAllIncidents = async (forceRefresh = false): Promise<IncidentIte
|
||||
isRequestInProgress = false;
|
||||
|
||||
if (!result || !result.items) {
|
||||
console.warn('No incidents found in API response');
|
||||
// console.warn('No incidents found in API response');
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ export const getAllIncidents = async (forceRefresh = false): Promise<IncidentIte
|
||||
// Update cache
|
||||
updateCache(normalizedItems);
|
||||
|
||||
console.log(`Fetched ${normalizedItems.length} incidents at ${new Date().toLocaleTimeString()}`);
|
||||
// console.log(`Fetched ${normalizedItems.length} incidents at ${new Date().toLocaleTimeString()}`);
|
||||
return normalizedItems;
|
||||
} catch (error) {
|
||||
if ((error as any)?.isAbort) {
|
||||
@@ -89,7 +89,7 @@ export const getAllIncidents = async (forceRefresh = false): Promise<IncidentIte
|
||||
return incidentsCache?.data || [];
|
||||
}
|
||||
|
||||
console.error('Error fetching incidents:', error);
|
||||
// console.error('Error fetching incidents:', error);
|
||||
|
||||
// Clear states to allow retry
|
||||
pendingRequest = null;
|
||||
@@ -102,7 +102,7 @@ export const getAllIncidents = async (forceRefresh = false): Promise<IncidentIte
|
||||
|
||||
// Still return cached data even on error
|
||||
if (incidentsCache) {
|
||||
console.log('Returning stale cached data after error');
|
||||
// console.log('Returning stale cached data after error');
|
||||
return incidentsCache.data;
|
||||
}
|
||||
|
||||
@@ -113,13 +113,13 @@ export const getAllIncidents = async (forceRefresh = false): Promise<IncidentIte
|
||||
// Get incident by id
|
||||
export const getIncidentById = async (id: string): Promise<IncidentItem | null> => {
|
||||
try {
|
||||
console.log(`Fetching incident with ID: ${id}`);
|
||||
// console.log(`Fetching incident with ID: ${id}`);
|
||||
|
||||
// First check if the incident exists in the cache
|
||||
if (isCacheValid() && incidentsCache) {
|
||||
const cachedIncident = incidentsCache.data.find(incident => incident.id === id);
|
||||
if (cachedIncident) {
|
||||
console.log('Incident found in cache');
|
||||
// console.log('Incident found in cache');
|
||||
return cachedIncident;
|
||||
}
|
||||
}
|
||||
@@ -136,7 +136,7 @@ export const getIncidentById = async (id: string): Promise<IncidentItem | null>
|
||||
return normalizedIncident;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error fetching incident with ID ${id}:`, error);
|
||||
// console.error(`Error fetching incident with ID ${id}:`, error);
|
||||
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to fetch incident: ${error.message}`);
|
||||
|
||||
@@ -16,20 +16,20 @@ export const maintenanceNotificationService = {
|
||||
*/
|
||||
async sendMaintenanceNotification({ maintenance, notificationType }: NotificationParams): Promise<boolean> {
|
||||
try {
|
||||
console.log(`Preparing to send ${notificationType} notification for maintenance: ${maintenance.title}`);
|
||||
// console.log(`Preparing to send ${notificationType} notification for maintenance: ${maintenance.title}`);
|
||||
|
||||
// Get notification channel ID - try both fields
|
||||
let notificationChannelId = maintenance.notification_channel_id;
|
||||
|
||||
// If notification_channel_id is empty, try to use notification_id
|
||||
if (!notificationChannelId && maintenance.notification_id) {
|
||||
console.log(`No notification_channel_id found, using notification_id: ${maintenance.notification_id}`);
|
||||
// console.log(`No notification_channel_id found, using notification_id: ${maintenance.notification_id}`);
|
||||
notificationChannelId = maintenance.notification_id;
|
||||
}
|
||||
|
||||
// Check if maintenance has notification channel configured
|
||||
if (!notificationChannelId) {
|
||||
console.log("No notification channel configured for this maintenance");
|
||||
// console.log("No notification channel configured for this maintenance");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -41,19 +41,19 @@ export const maintenanceNotificationService = {
|
||||
const config = await pb.collection('alert_configurations').getOne(notificationChannelId);
|
||||
notificationConfig = config as unknown as AlertConfiguration;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch notification configuration:", error);
|
||||
// console.error("Failed to fetch notification configuration:", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!notificationConfig.enabled) {
|
||||
console.log("Notification channel is disabled");
|
||||
// console.log("Notification channel is disabled");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create notification message based on type
|
||||
const message = this.generateMaintenanceMessage(maintenance, notificationType);
|
||||
|
||||
console.log(`Sending ${notificationConfig.notification_type} notification with message: ${message}`);
|
||||
// console.log(`Sending ${notificationConfig.notification_type} notification with message: ${message}`);
|
||||
|
||||
// Send notification based on channel type
|
||||
if (notificationConfig.notification_type === 'telegram') {
|
||||
@@ -62,10 +62,10 @@ export const maintenanceNotificationService = {
|
||||
|
||||
// Add more notification types here as needed
|
||||
|
||||
console.log(`Unsupported notification type: ${notificationConfig.notification_type}`);
|
||||
// console.log(`Unsupported notification type: ${notificationConfig.notification_type}`);
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.error("Error sending maintenance notification:", error);
|
||||
// console.error("Error sending maintenance notification:", error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
@@ -115,12 +115,12 @@ export const maintenanceNotificationService = {
|
||||
|
||||
// Set up scheduled maintenance notifications
|
||||
export const setupMaintenanceNotificationsScheduler = () => {
|
||||
console.log("Setting up maintenance notifications scheduler");
|
||||
// console.log("Setting up maintenance notifications scheduler");
|
||||
|
||||
// Check every minute for maintenance that needs notifications
|
||||
const checkInterval = setInterval(async () => {
|
||||
try {
|
||||
console.log("Checking for maintenance notifications to send...");
|
||||
// console.log("Checking for maintenance notifications to send...");
|
||||
const now = new Date();
|
||||
|
||||
// Fetch upcoming and ongoing maintenance
|
||||
@@ -143,7 +143,7 @@ export const setupMaintenanceNotificationsScheduler = () => {
|
||||
startTime <= new Date(now.getTime() + 60000) &&
|
||||
startTime > new Date(now.getTime() - 60000)) {
|
||||
|
||||
console.log(`Maintenance ${maintenance.title} is starting now, sending notification`);
|
||||
// console.log(`Maintenance ${maintenance.title} is starting now, sending notification`);
|
||||
await maintenanceNotificationService.sendMaintenanceNotification({
|
||||
maintenance,
|
||||
notificationType: 'start'
|
||||
@@ -160,7 +160,7 @@ export const setupMaintenanceNotificationsScheduler = () => {
|
||||
endTime <= new Date(now.getTime() + 60000) &&
|
||||
endTime > new Date(now.getTime() - 60000)) {
|
||||
|
||||
console.log(`Maintenance ${maintenance.title} is ending now, sending notification`);
|
||||
// console.log(`Maintenance ${maintenance.title} is ending now, sending notification`);
|
||||
await maintenanceNotificationService.sendMaintenanceNotification({
|
||||
maintenance,
|
||||
notificationType: 'end'
|
||||
@@ -173,7 +173,7 @@ export const setupMaintenanceNotificationsScheduler = () => {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error checking maintenance notifications:", error);
|
||||
// console.error("Error checking maintenance notifications:", error);
|
||||
}
|
||||
}, 60000); // Check every minute
|
||||
|
||||
@@ -186,7 +186,7 @@ let notificationScheduler: NodeJS.Timeout | null = null;
|
||||
export const initMaintenanceNotifications = () => {
|
||||
if (notificationScheduler === null) {
|
||||
notificationScheduler = setupMaintenanceNotificationsScheduler();
|
||||
console.log("Maintenance notifications scheduler initialized");
|
||||
// console.log("Maintenance notifications scheduler initialized");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -194,6 +194,6 @@ export const stopMaintenanceNotifications = () => {
|
||||
if (notificationScheduler !== null) {
|
||||
clearInterval(notificationScheduler);
|
||||
notificationScheduler = null;
|
||||
console.log("Maintenance notifications scheduler stopped");
|
||||
// console.log("Maintenance notifications scheduler stopped");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,13 +12,13 @@ export async function startAllActiveServices(): Promise<void> {
|
||||
filter: 'status != "paused"'
|
||||
});
|
||||
|
||||
console.log(`Starting monitoring for ${result.items.length} active services`);
|
||||
// console.log(`Starting monitoring for ${result.items.length} active services`);
|
||||
|
||||
// Start monitoring each active service
|
||||
for (const service of result.items) {
|
||||
await startMonitoringService(service.id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error starting all active services:", error);
|
||||
// console.error("Error starting all active services:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export async function startMonitoringService(serviceId: string): Promise<void> {
|
||||
try {
|
||||
// First check if the service is already being monitored
|
||||
if (monitoringIntervals.has(serviceId)) {
|
||||
console.log(`Service ${serviceId} is already being monitored`);
|
||||
// console.log(`Service ${serviceId} is already being monitored`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@ export async function startMonitoringService(serviceId: string): Promise<void> {
|
||||
|
||||
// If service was manually paused, don't auto-resume
|
||||
if (service.status === "paused") {
|
||||
console.log(`Service ${serviceId} (${service.name}) is paused. Not starting monitoring.`);
|
||||
// console.log(`Service ${serviceId} (${service.name}) is paused. Not starting monitoring.`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Starting monitoring for service ${serviceId} (${service.name})`);
|
||||
// console.log(`Starting monitoring for service ${serviceId} (${service.name})`);
|
||||
|
||||
// Update the service status to active/up in the database
|
||||
await pb.collection('services').update(serviceId, {
|
||||
@@ -32,18 +32,18 @@ export async function startMonitoringService(serviceId: string): Promise<void> {
|
||||
// The actual service checking is now handled by the Go microservice
|
||||
// This frontend service just tracks the monitoring state
|
||||
const intervalMs = (service.heartbeat_interval || 60) * 1000;
|
||||
console.log(`Service ${service.name} monitoring delegated to backend service`);
|
||||
// console.log(`Service ${service.name} monitoring delegated to backend service`);
|
||||
|
||||
// Store a placeholder interval to track that this service is being monitored
|
||||
const intervalId = window.setInterval(() => {
|
||||
console.log(`Monitoring active for service ${service.name} (handled by backend)`);
|
||||
// console.log(`Monitoring active for service ${service.name} (handled by backend)`);
|
||||
}, intervalMs);
|
||||
|
||||
// Store the interval ID for this service
|
||||
monitoringIntervals.set(serviceId, intervalId);
|
||||
|
||||
console.log(`Monitoring registered for service ${serviceId}`);
|
||||
// console.log(`Monitoring registered for service ${serviceId}`);
|
||||
} catch (error) {
|
||||
console.error("Error starting service monitoring:", error);
|
||||
// console.error("Error starting service monitoring:", error);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { pb } from '@/lib/pocketbase';
|
||||
import { Service, CreateServiceParams, UptimeData } from '@/types/service.types';
|
||||
import { monitoringService } from './monitoring';
|
||||
@@ -28,6 +27,7 @@ export const serviceService = {
|
||||
interval: item.heartbeat_interval || item.interval || 60,
|
||||
retries: item.max_retries || item.retries || 3,
|
||||
notificationChannel: item.notification_id,
|
||||
notification_status: item.notification_status || "disabled",
|
||||
alertTemplate: item.template_id,
|
||||
muteAlerts: item.alerts === "muted", // Convert string to boolean for compatibility
|
||||
alerts: item.alerts || "unmuted", // Store actual database field
|
||||
@@ -39,12 +39,12 @@ export const serviceService = {
|
||||
regional_monitoring_enabled: item.regional_status === "enabled", // Backward compatibility
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("Error fetching services:", error);
|
||||
// console.error("Error fetching services:", error);
|
||||
throw new Error('Failed to load services data.');
|
||||
}
|
||||
},
|
||||
|
||||
async createService(params: CreateServiceParams): Promise<Service> {
|
||||
async createService(params: any): Promise<Service> {
|
||||
try {
|
||||
// Convert service type to lowercase to avoid validation issues
|
||||
const serviceType = params.type.toLowerCase();
|
||||
@@ -61,7 +61,10 @@ export const serviceService = {
|
||||
last_checked: new Date().toLocaleString(),
|
||||
heartbeat_interval: params.interval,
|
||||
max_retries: params.retries,
|
||||
notification_id: params.notificationChannel,
|
||||
notification_status: params.notificationStatus || "disabled",
|
||||
notification_id: params.notificationChannels && params.notificationChannels.length > 0
|
||||
? params.notificationChannels[0] // Store first channel for backward compatibility
|
||||
: null,
|
||||
template_id: params.alertTemplate,
|
||||
// Regional monitoring fields - use regional_status
|
||||
regional_status: params.regionalStatus || "disabled",
|
||||
@@ -98,6 +101,7 @@ export const serviceService = {
|
||||
interval: record.heartbeat_interval || 60,
|
||||
retries: record.max_retries || 3,
|
||||
notificationChannel: record.notification_id,
|
||||
notification_status: record.notification_status || "disabled",
|
||||
alertTemplate: record.template_id,
|
||||
regional_status: record.regional_status || "disabled",
|
||||
regional_monitoring_enabled: record.regional_status === "enabled",
|
||||
@@ -115,7 +119,7 @@ export const serviceService = {
|
||||
}
|
||||
},
|
||||
|
||||
async updateService(id: string, params: CreateServiceParams): Promise<Service> {
|
||||
async updateService(id: string, params: any): Promise<Service> {
|
||||
try {
|
||||
// Convert service type to lowercase to avoid validation issues
|
||||
const serviceType = params.type.toLowerCase();
|
||||
@@ -128,7 +132,10 @@ export const serviceService = {
|
||||
service_type: serviceType,
|
||||
heartbeat_interval: params.interval,
|
||||
max_retries: params.retries,
|
||||
notification_id: params.notificationChannel || null,
|
||||
notification_status: params.notificationStatus || "disabled",
|
||||
notification_id: params.notificationChannels && params.notificationChannels.length > 0
|
||||
? params.notificationChannels[0] // Store first channel for backward compatibility
|
||||
: null,
|
||||
template_id: params.alertTemplate || null,
|
||||
// Regional monitoring fields - use regional_status
|
||||
regional_status: params.regionalStatus || "disabled",
|
||||
@@ -172,6 +179,7 @@ export const serviceService = {
|
||||
interval: record.heartbeat_interval || 60,
|
||||
retries: record.max_retries || 3,
|
||||
notificationChannel: record.notification_id,
|
||||
notification_status: record.notification_status || "disabled",
|
||||
alertTemplate: record.template_id,
|
||||
regional_status: record.regional_status || "disabled",
|
||||
regional_monitoring_enabled: record.regional_status === "enabled",
|
||||
|
||||
@@ -8,7 +8,7 @@ import { toast } from "sonner";
|
||||
*/
|
||||
export const fetchSSLCertificates = async (): Promise<SSLCertificate[]> => {
|
||||
try {
|
||||
console.log("Fetching SSL certificates from PocketBase...");
|
||||
// console.log("Fetching SSL certificates from PocketBase...");
|
||||
|
||||
// Using the direct API path to fetch SSL certificates
|
||||
const endpoint = "/api/collections/ssl_certificates/records";
|
||||
@@ -23,7 +23,7 @@ export const fetchSSLCertificates = async (): Promise<SSLCertificate[]> => {
|
||||
const queryString = new URLSearchParams(params as any).toString();
|
||||
const fullEndpoint = `${endpoint}?${queryString}`;
|
||||
|
||||
console.log("Fetching SSL certificates from:", fullEndpoint);
|
||||
// console.log("Fetching SSL certificates from:", fullEndpoint);
|
||||
const response = await pb.send(fullEndpoint, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -39,14 +39,14 @@ export const fetchSSLCertificates = async (): Promise<SSLCertificate[]> => {
|
||||
throw new Error("Invalid response format from PocketBase API");
|
||||
}
|
||||
|
||||
console.log("Received SSL certificates:", response.items.length);
|
||||
// console.log("Received SSL certificates:", response.items.length);
|
||||
|
||||
// Map items to SSLCertificate[] type with validation
|
||||
return response.items.map(item => {
|
||||
const cert = item as SSLCertificate;
|
||||
|
||||
// Log certificate details for debugging
|
||||
console.log(`Certificate for ${cert.domain}: Issued by ${cert.issuer_o}, Days left: ${cert.days_left}`);
|
||||
// console.log(`Certificate for ${cert.domain}: Issued by ${cert.issuer_o}, Days left: ${cert.days_left}`);
|
||||
|
||||
// Ensure dates are valid
|
||||
try {
|
||||
@@ -54,7 +54,7 @@ export const fetchSSLCertificates = async (): Promise<SSLCertificate[]> => {
|
||||
if (cert.valid_till) new Date(cert.valid_till).toISOString();
|
||||
if (cert.last_notified) new Date(cert.last_notified).toISOString();
|
||||
} catch (e) {
|
||||
console.warn("Invalid date found in certificate", cert.id, e);
|
||||
// console.warn("Invalid date found in certificate", cert.id, e);
|
||||
// Fix invalid dates if needed
|
||||
if (cert.valid_from && isNaN(new Date(cert.valid_from).getTime())) {
|
||||
cert.valid_from = new Date().toISOString();
|
||||
@@ -74,7 +74,7 @@ export const fetchSSLCertificates = async (): Promise<SSLCertificate[]> => {
|
||||
const diffTime = expirationDate.getTime() - currentDate.getTime();
|
||||
cert.days_left = Math.ceil(diffTime / (1000 * 3600 * 24));
|
||||
} catch (e) {
|
||||
console.warn("Error calculating days_left for certificate", cert.id, e);
|
||||
// console.warn("Error calculating days_left for certificate", cert.id, e);
|
||||
cert.days_left = 0;
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ export const fetchSSLCertificates = async (): Promise<SSLCertificate[]> => {
|
||||
return cert;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error fetching SSL certificates:", error);
|
||||
// console.error("Error fetching SSL certificates:", error);
|
||||
toast.error("Failed to fetch SSL certificates. Please try again later.");
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ const getCollectionForServiceType = (serviceType: string): string => {
|
||||
export const uptimeService = {
|
||||
async recordUptimeData(data: UptimeData): Promise<void> {
|
||||
try {
|
||||
console.log(`Recording uptime data for service ${data.serviceId || data.service_id}: Status ${data.status}, Response time: ${data.responseTime}ms`);
|
||||
// console.log(`Recording uptime data for service ${data.serviceId || data.service_id}: Status ${data.status}, Response time: ${data.responseTime}ms`);
|
||||
|
||||
const options = {
|
||||
$autoCancel: false,
|
||||
@@ -50,9 +50,9 @@ export const uptimeService = {
|
||||
const keysToDelete = Array.from(uptimeCache.keys()).filter(key => key.includes(`uptime_${serviceId}`));
|
||||
keysToDelete.forEach(key => uptimeCache.delete(key));
|
||||
|
||||
console.log(`Uptime data recorded successfully with ID: ${record.id}`);
|
||||
// console.log(`Uptime data recorded successfully with ID: ${record.id}`);
|
||||
} catch (error) {
|
||||
console.error("Error recording uptime data:", error);
|
||||
// console.error("Error recording uptime data:", error);
|
||||
throw new Error(`Failed to record uptime data: ${error}`);
|
||||
}
|
||||
},
|
||||
@@ -66,7 +66,7 @@ export const uptimeService = {
|
||||
): Promise<UptimeData[]> {
|
||||
try {
|
||||
if (!serviceId) {
|
||||
console.log('No serviceId provided to getUptimeHistory');
|
||||
// console.log('No serviceId provided to getUptimeHistory');
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -75,13 +75,13 @@ export const uptimeService = {
|
||||
// Check cache
|
||||
const cached = uptimeCache.get(cacheKey);
|
||||
if (cached && (Date.now() - cached.timestamp) < cached.expiresIn) {
|
||||
console.log(`Using cached uptime history for service ${serviceId}`);
|
||||
// console.log(`Using cached uptime history for service ${serviceId}`);
|
||||
return cached.data;
|
||||
}
|
||||
|
||||
// Determine the correct collection based on service type
|
||||
const collection = serviceType ? getCollectionForServiceType(serviceType) : 'uptime_data';
|
||||
console.log(`Fetching default uptime history for service ${serviceId} from collection ${collection}, limit: ${limit}`);
|
||||
// console.log(`Fetching default uptime history for service ${serviceId} from collection ${collection}, limit: ${limit}`);
|
||||
|
||||
// Build filter to get records for specific service_id
|
||||
let filter = `service_id='${serviceId}'`;
|
||||
@@ -91,7 +91,7 @@ export const uptimeService = {
|
||||
const startUTC = startDate.toISOString();
|
||||
const endUTC = endDate.toISOString();
|
||||
|
||||
console.log(`Date filter: ${startUTC} to ${endUTC}`);
|
||||
// console.log(`Date filter: ${startUTC} to ${endUTC}`);
|
||||
filter += ` && timestamp >= "${startUTC}" && timestamp <= "${endUTC}"`;
|
||||
}
|
||||
|
||||
@@ -102,16 +102,16 @@ export const uptimeService = {
|
||||
$cancelKey: `uptime_history_${serviceId}_${Date.now()}`
|
||||
};
|
||||
|
||||
console.log(`Filter query for default data: ${filter} on collection: ${collection}`);
|
||||
// console.log(`Filter query for default data: ${filter} on collection: ${collection}`);
|
||||
|
||||
const response = await pb.collection(collection).getList(1, limit, options);
|
||||
|
||||
console.log(`Fetched ${response.items.length} records for service ${serviceId} from ${collection}`);
|
||||
// console.log(`Fetched ${response.items.length} records for service ${serviceId} from ${collection}`);
|
||||
|
||||
if (response.items.length > 0) {
|
||||
console.log(`Date range in results: ${response.items[response.items.length - 1].timestamp} to ${response.items[0].timestamp}`);
|
||||
// console.log(`Date range in results: ${response.items[response.items.length - 1].timestamp} to ${response.items[0].timestamp}`);
|
||||
} else {
|
||||
console.log(`No records found for service_id '${serviceId}' in collection: ${collection}`);
|
||||
// console.log(`No records found for service_id '${serviceId}' in collection: ${collection}`);
|
||||
}
|
||||
|
||||
// Transform the response items to UptimeData format
|
||||
@@ -137,18 +137,18 @@ export const uptimeService = {
|
||||
|
||||
return uptimeData;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching uptime history for service ${serviceId}:`, error);
|
||||
// console.error(`Error fetching uptime history for service ${serviceId}:`, error);
|
||||
|
||||
// Try to return cached data as fallback
|
||||
const cacheKey = `uptime_${serviceId}_${limit}_${startDate?.toISOString() || ''}_${endDate?.toISOString() || ''}_${serviceType || 'default'}_default`;
|
||||
const cached = uptimeCache.get(cacheKey);
|
||||
if (cached) {
|
||||
console.log(`Using expired cached data for service ${serviceId} due to fetch error`);
|
||||
// console.log(`Using expired cached data for service ${serviceId} due to fetch error`);
|
||||
return cached.data;
|
||||
}
|
||||
|
||||
// Return empty array instead of throwing to prevent UI crashes
|
||||
console.log(`Returning empty array for service ${serviceId} due to fetch error`);
|
||||
// console.log(`Returning empty array for service ${serviceId} due to fetch error`);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
@@ -164,7 +164,7 @@ export const uptimeService = {
|
||||
): Promise<UptimeData[]> {
|
||||
try {
|
||||
if (!regionName || !agentId) {
|
||||
console.log('No region name or agent ID provided for regional query');
|
||||
// console.log('No region name or agent ID provided for regional query');
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -173,13 +173,13 @@ export const uptimeService = {
|
||||
// Check cache
|
||||
const cached = uptimeCache.get(cacheKey);
|
||||
if (cached && (Date.now() - cached.timestamp) < cached.expiresIn) {
|
||||
console.log(`Using cached regional uptime history for service ${serviceId}`);
|
||||
// console.log(`Using cached regional uptime history for service ${serviceId}`);
|
||||
return cached.data;
|
||||
}
|
||||
|
||||
// Determine the correct collection based on service type
|
||||
const collection = serviceType ? getCollectionForServiceType(serviceType) : 'uptime_data';
|
||||
console.log(`Fetching regional uptime history from collection: ${collection} for service: ${serviceId}, region: ${regionName}, agent: ${agentId}`);
|
||||
// console.log(`Fetching regional uptime history from collection: ${collection} for service: ${serviceId}, region: ${regionName}, agent: ${agentId}`);
|
||||
|
||||
// Build filter for regional agent data
|
||||
let filter = `service_id="${serviceId}" && region_name="${regionName}" && agent_id="${agentId}"`;
|
||||
@@ -190,7 +190,7 @@ export const uptimeService = {
|
||||
filter += ` && timestamp>="${startISO}" && timestamp<="${endISO}"`;
|
||||
}
|
||||
|
||||
console.log(`Regional filter query: ${filter} on collection: ${collection}`);
|
||||
// console.log(`Regional filter query: ${filter} on collection: ${collection}`);
|
||||
|
||||
const records = await pb.collection(collection).getList(1, limit, {
|
||||
sort: '-timestamp',
|
||||
@@ -199,7 +199,7 @@ export const uptimeService = {
|
||||
$cancelKey: `regional_uptime_history_${serviceId}_${Date.now()}`
|
||||
});
|
||||
|
||||
console.log(`Retrieved ${records.items.length} regional uptime records from ${collection} for region ${regionName}, agent ${agentId}`);
|
||||
// console.log(`Retrieved ${records.items.length} regional uptime records from ${collection} for region ${regionName}, agent ${agentId}`);
|
||||
|
||||
const uptimeData = records.items.map(item => ({
|
||||
id: item.id,
|
||||
@@ -224,7 +224,7 @@ export const uptimeService = {
|
||||
return uptimeData;
|
||||
} catch (error) {
|
||||
const collectionForError = serviceType ? getCollectionForServiceType(serviceType) : 'uptime_data';
|
||||
console.error(`Error fetching regional uptime history from ${collectionForError}:`, error);
|
||||
// console.error(`Error fetching regional uptime history from ${collectionForError}:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ const convertToUserType = (record: any, role: string = "admin"): User => {
|
||||
export const userService = {
|
||||
async getUsers(): Promise<User[] | null> {
|
||||
try {
|
||||
console.log("Calling getUsers API");
|
||||
// console.log("Calling getUsers API");
|
||||
|
||||
// Get both regular users and superadmins
|
||||
const regularUsers = await pb.collection('users').getList(1, 50, {
|
||||
@@ -71,9 +71,9 @@ export const userService = {
|
||||
superadminUsers = await pb.collection('_superusers').getList(1, 50, {
|
||||
sort: 'created',
|
||||
});
|
||||
console.log("Successfully fetched superadmin users:", superadminUsers);
|
||||
// console.log("Successfully fetched superadmin users:", superadminUsers);
|
||||
} catch (error) {
|
||||
console.log("No superadmin collection or access rights:", error);
|
||||
// console.log("No superadmin collection or access rights:", error);
|
||||
}
|
||||
|
||||
// Combine both user types and mark superadmins
|
||||
@@ -82,39 +82,39 @@ export const userService = {
|
||||
...superadminUsers.items.map((user: any) => convertToUserType(user, "superadmin"))
|
||||
];
|
||||
|
||||
console.log("Combined users list:", allUsers);
|
||||
// console.log("Combined users list:", allUsers);
|
||||
|
||||
return allUsers;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch users:", error);
|
||||
// console.error("Failed to fetch users:", error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async getUser(id: string): Promise<User | null> {
|
||||
try {
|
||||
console.log(`Fetching user with ID: ${id}`);
|
||||
// console.log(`Fetching user with ID: ${id}`);
|
||||
|
||||
// Try fetching from regular users first
|
||||
try {
|
||||
const user = await pb.collection('users').getOne(id);
|
||||
console.log("User fetch result (regular user):", user);
|
||||
// console.log("User fetch result (regular user):", user);
|
||||
return convertToUserType(user, user.role || "admin");
|
||||
} catch (error) {
|
||||
console.log("User not found in regular users, trying superadmin collection");
|
||||
// console.log("User not found in regular users, trying superadmin collection");
|
||||
}
|
||||
|
||||
// If not found, try in superadmins
|
||||
try {
|
||||
const user = await pb.collection('_superusers').getOne(id);
|
||||
console.log("User fetch result (superadmin):", user);
|
||||
// console.log("User fetch result (superadmin):", user);
|
||||
return convertToUserType(user, "superadmin");
|
||||
} catch (error) {
|
||||
console.log("User not found in superadmin collection either");
|
||||
// console.log("User not found in superadmin collection either");
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch user ${id}:`, error);
|
||||
// console.error(`Failed to fetch user ${id}:`, error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
@@ -130,11 +130,11 @@ export const userService = {
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Updating user with clean data:", cleanData);
|
||||
// console.log("Updating user with clean data:", cleanData);
|
||||
|
||||
// If there's nothing to update, return the current user
|
||||
if (Object.keys(cleanData).length === 0) {
|
||||
console.log("No changes to update");
|
||||
// console.log("No changes to update");
|
||||
const currentUser = await this.getUser(id);
|
||||
return currentUser;
|
||||
}
|
||||
@@ -197,16 +197,16 @@ export const userService = {
|
||||
await pb.collection('_superusers').delete(id);
|
||||
updatedUser = convertToUserType(newRegularUser, "admin");
|
||||
}
|
||||
console.log("User transferred between collections due to role change");
|
||||
// console.log("User transferred between collections due to role change");
|
||||
|
||||
} catch (error) {
|
||||
console.error("Failed to transfer user between collections:", error);
|
||||
// console.error("Failed to transfer user between collections:", error);
|
||||
throw new Error("Failed to change user role: " + (error instanceof Error ? error.message : "Unknown error"));
|
||||
}
|
||||
} else {
|
||||
// Regular update without changing collections
|
||||
if (Object.keys(cleanData).length > 0) {
|
||||
console.log("Final update payload to PocketBase:", cleanData);
|
||||
// console.log("Final update payload to PocketBase:", cleanData);
|
||||
|
||||
try {
|
||||
// Use the appropriate collection
|
||||
@@ -214,15 +214,15 @@ export const userService = {
|
||||
const updatedRecord = await pb.collection(collection).update(id, cleanData);
|
||||
updatedUser = convertToUserType(updatedRecord, isCurrentlySuperadmin ? "superadmin" : "admin");
|
||||
|
||||
console.log("PocketBase update response:", updatedUser);
|
||||
// console.log("PocketBase update response:", updatedUser);
|
||||
|
||||
// If email was updated successfully, show success message
|
||||
if (hasEmailChange) {
|
||||
console.log("Email updated successfully to:", emailToUpdate);
|
||||
// console.log("Email updated successfully to:", emailToUpdate);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error updating user:", error);
|
||||
// console.error("Error updating user:", error);
|
||||
|
||||
// Provide more specific error messages for email issues
|
||||
if (hasEmailChange && error instanceof Error) {
|
||||
@@ -241,7 +241,7 @@ export const userService = {
|
||||
|
||||
return updatedUser;
|
||||
} catch (error) {
|
||||
console.error("Failed to update user:", error);
|
||||
// console.error("Failed to update user:", error);
|
||||
throw error; // Re-throw to handle in the component
|
||||
}
|
||||
},
|
||||
@@ -253,7 +253,7 @@ export const userService = {
|
||||
await pb.collection('users').delete(id);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log("User not found in regular users, trying superadmin collection");
|
||||
// console.log("User not found in regular users, trying superadmin collection");
|
||||
}
|
||||
|
||||
// If not found, try deleting from superadmin collection
|
||||
@@ -261,11 +261,11 @@ export const userService = {
|
||||
await pb.collection('_superusers').delete(id);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Failed to delete user from either collection:", error);
|
||||
// console.error("Failed to delete user from either collection:", error);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to delete user:", error);
|
||||
// console.error("Failed to delete user:", error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
@@ -282,7 +282,7 @@ export const userService = {
|
||||
if (cleanData.avatar.startsWith('http') ||
|
||||
cleanData.avatar.startsWith('/upload/') ||
|
||||
cleanData.avatar.includes('api.dicebear.com')) {
|
||||
console.log("Removing avatar URL for new user creation:", cleanData.avatar);
|
||||
// console.log("Removing avatar URL for new user creation:", cleanData.avatar);
|
||||
delete cleanData.avatar;
|
||||
}
|
||||
}
|
||||
@@ -291,18 +291,18 @@ export const userService = {
|
||||
const isSuperAdmin = cleanData.role === "superadmin";
|
||||
const collection = isSuperAdmin ? '_superusers' : 'users';
|
||||
|
||||
console.log(`Creating new user in ${collection} collection with data:`, {
|
||||
...cleanData,
|
||||
password: "[REDACTED]",
|
||||
passwordConfirm: "[REDACTED]"
|
||||
});
|
||||
/// console.log(`Creating new user in ${collection} collection with data:`, {
|
||||
// ...cleanData,
|
||||
// password: "[REDACTED]",
|
||||
// passwordConfirm: "[REDACTED]"
|
||||
// });
|
||||
|
||||
// Create the user in the appropriate collection
|
||||
const result = await pb.collection(collection).create(cleanData);
|
||||
|
||||
return convertToUserType(result, isSuperAdmin ? "superadmin" : "admin");
|
||||
} catch (error) {
|
||||
console.error("Failed to create user:", error);
|
||||
// console.error("Failed to create user:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ export interface Service {
|
||||
updated?: string;
|
||||
notification_channel?: string;
|
||||
notificationChannel?: string; // Keep for backward compatibility
|
||||
notification_status?: "enabled" | "disabled"; // Add notification_status field
|
||||
alertTemplate?: string;
|
||||
alerts?: "muted" | "unmuted"; // Make sure alerts is properly typed as union
|
||||
muteAlerts?: boolean; // Keep this to avoid breaking existing code
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/// <reference path="../pb_data/types.d.ts" />
|
||||
migrate((app) => {
|
||||
const collection = app.findCollectionByNameOrId("pbc_1168766540")
|
||||
|
||||
// remove field
|
||||
collection.fields.removeById("text1404634243")
|
||||
|
||||
// update field
|
||||
collection.fields.addAt(15, new Field({
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text258142582",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "region_name",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
}))
|
||||
|
||||
return app.save(collection)
|
||||
}, (app) => {
|
||||
const collection = app.findCollectionByNameOrId("pbc_1168766540")
|
||||
|
||||
// add field
|
||||
collection.fields.addAt(17, new Field({
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text1404634243",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "agent_ip_address",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
}))
|
||||
|
||||
// update field
|
||||
collection.fields.addAt(15, new Field({
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text258142582",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "region",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
}))
|
||||
|
||||
return app.save(collection)
|
||||
})
|
||||
@@ -0,0 +1,42 @@
|
||||
/// <reference path="../pb_data/types.d.ts" />
|
||||
migrate((app) => {
|
||||
const collection = app.findCollectionByNameOrId("pbc_171698555")
|
||||
|
||||
// update field
|
||||
collection.fields.addAt(1, new Field({
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text258142582",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "region_name",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
}))
|
||||
|
||||
return app.save(collection)
|
||||
}, (app) => {
|
||||
const collection = app.findCollectionByNameOrId("pbc_171698555")
|
||||
|
||||
// update field
|
||||
collection.fields.addAt(1, new Field({
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text258142582",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "region",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
}))
|
||||
|
||||
return app.save(collection)
|
||||
})
|
||||
@@ -0,0 +1,137 @@
|
||||
/// <reference path="../pb_data/types.d.ts" />
|
||||
migrate((app) => {
|
||||
const collection = app.findCollectionByNameOrId("pbc_2018671343");
|
||||
|
||||
return app.delete(collection);
|
||||
}, (app) => {
|
||||
const collection = new Collection({
|
||||
"createRule": "",
|
||||
"deleteRule": "",
|
||||
"fields": [
|
||||
{
|
||||
"autogeneratePattern": "[a-z0-9]{15}",
|
||||
"hidden": false,
|
||||
"id": "text3208210256",
|
||||
"max": 15,
|
||||
"min": 15,
|
||||
"name": "id",
|
||||
"pattern": "^[a-z0-9]+$",
|
||||
"presentable": false,
|
||||
"primaryKey": true,
|
||||
"required": true,
|
||||
"system": true,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text407168695",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "server_id",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text1431356653",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "pid",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text1579384326",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "name",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number1391558374",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "memory_percent",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number2651913466",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "cpu_percent",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text2063623452",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "status",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "autodate2990389176",
|
||||
"name": "created",
|
||||
"onCreate": true,
|
||||
"onUpdate": false,
|
||||
"presentable": false,
|
||||
"system": false,
|
||||
"type": "autodate"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "autodate3332085495",
|
||||
"name": "updated",
|
||||
"onCreate": true,
|
||||
"onUpdate": true,
|
||||
"presentable": false,
|
||||
"system": false,
|
||||
"type": "autodate"
|
||||
}
|
||||
],
|
||||
"id": "pbc_2018671343",
|
||||
"indexes": [],
|
||||
"listRule": "",
|
||||
"name": "server_processes",
|
||||
"system": false,
|
||||
"type": "base",
|
||||
"updateRule": "",
|
||||
"viewRule": ""
|
||||
});
|
||||
|
||||
return app.save(collection);
|
||||
})
|
||||
@@ -0,0 +1,170 @@
|
||||
/// <reference path="../pb_data/types.d.ts" />
|
||||
migrate((app) => {
|
||||
const collection = app.findCollectionByNameOrId("pbc_3414192583");
|
||||
|
||||
return app.delete(collection);
|
||||
}, (app) => {
|
||||
const collection = new Collection({
|
||||
"createRule": "",
|
||||
"deleteRule": "",
|
||||
"fields": [
|
||||
{
|
||||
"autogeneratePattern": "[a-z0-9]{15}",
|
||||
"hidden": false,
|
||||
"id": "text3208210256",
|
||||
"max": 15,
|
||||
"min": 15,
|
||||
"name": "id",
|
||||
"pattern": "^[a-z0-9]+$",
|
||||
"presentable": false,
|
||||
"primaryKey": true,
|
||||
"required": true,
|
||||
"system": true,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text407168695",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "server_id",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text3065852031",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "message",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text887233555",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "notification_type",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "date3805952114",
|
||||
"max": "",
|
||||
"min": "",
|
||||
"name": "read_at",
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number1377725610",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "cpu_threshold",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number3087505384",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "ram_threshold",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number2833320134",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "disk_threshold",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number1826572927",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "network_threshold",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number2184331740",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "notification_config_id",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "autodate2990389176",
|
||||
"name": "created",
|
||||
"onCreate": true,
|
||||
"onUpdate": false,
|
||||
"presentable": false,
|
||||
"system": false,
|
||||
"type": "autodate"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "autodate3332085495",
|
||||
"name": "updated",
|
||||
"onCreate": true,
|
||||
"onUpdate": true,
|
||||
"presentable": false,
|
||||
"system": false,
|
||||
"type": "autodate"
|
||||
}
|
||||
],
|
||||
"id": "pbc_3414192583",
|
||||
"indexes": [],
|
||||
"listRule": "",
|
||||
"name": "server_notifications",
|
||||
"system": false,
|
||||
"type": "base",
|
||||
"updateRule": "",
|
||||
"viewRule": ""
|
||||
});
|
||||
|
||||
return app.save(collection);
|
||||
})
|
||||
@@ -0,0 +1,28 @@
|
||||
/// <reference path="../pb_data/types.d.ts" />
|
||||
migrate((app) => {
|
||||
const collection = app.findCollectionByNameOrId("pbc_171698555")
|
||||
|
||||
// update collection data
|
||||
unmarshal({
|
||||
"createRule": "",
|
||||
"deleteRule": "",
|
||||
"listRule": "",
|
||||
"updateRule": "",
|
||||
"viewRule": ""
|
||||
}, collection)
|
||||
|
||||
return app.save(collection)
|
||||
}, (app) => {
|
||||
const collection = app.findCollectionByNameOrId("pbc_171698555")
|
||||
|
||||
// update collection data
|
||||
unmarshal({
|
||||
"createRule": null,
|
||||
"deleteRule": null,
|
||||
"listRule": null,
|
||||
"updateRule": null,
|
||||
"viewRule": null
|
||||
}, collection)
|
||||
|
||||
return app.save(collection)
|
||||
})
|
||||
Reference in New Issue
Block a user