Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit 39ef34f

Browse files
committed
init
1 parent 687a9d0 commit 39ef34f

File tree

11 files changed

+492
-29
lines changed

11 files changed

+492
-29
lines changed

client/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
"framer-motion": "^11.11.1",
5050
"js-cookie": "^3.0.5",
5151
"jsonwebtoken": "^9.0.2",
52-
"jspdf": "^1.4.0",
5352
"jspdf-autotable": "^3.8.4",
5453
"jwt-decode": "^4.0.0",
5554
"leaflet": "^1.9.4",

client/src/components/permissionConfig.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ const accessPermissions = {
3838
'/recoverytuts', '/registernew', '/PendingRequest', '/AccessReview',
3939
'/monitoring', '/profile', '/Settings', '/changepass', '/freightaudit',
4040
'/financialanalytics', '/invoice', '/shipment', '/settings', '/request',
41-
'/logistic1/index','./usermanagement'
41+
'/logistic1/index','./usermanagement','/cron'
4242
]
4343
},
4444
admin: {
4545
HR: ['/hrdash', '/worker', '/jobposting', '/payroll', '/profile', '/Settings', '/changepass', '/request'],
4646
Core: ['/coredash', '/shipment', '/customer', '/monthly', '/daily', '/breakdown', '/profile', '/Settings', '/changepass'],
4747
Finance: ['/financedash', '/freight/transaction', '/oversales', '/freightaudit', '/financialanalytics', '/invoice', '/profile', '/Settings', '/changepass'],
4848
Logistics: ['/logisticdash', '/shipment', '/customer', '/monthly', '/daily', '/breakdown', '/logistic1/index', '/logistic1/pin', '/profile', '/Settings', '/changepass'],
49-
Administrative: ['/employeedash', '/useractivity/index', '/restore', '/registernew', '/PendingRequest', '/AccessReview', '/profile', '/Settings', '/changepass']
49+
Administrative: ['/employeedash', '/useractivity/index', '/restore', '/registernew', '/PendingRequest', '/AccessReview', '/profile', '/Settings', '/changepass','/cron']
5050
},
5151
Manager: {
5252
HR: ['/hrdash', '/worker', '/jobposting', '/profile', '/Settings', '/changepass'],

client/src/routes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const Chatbox = React.lazy(() => import('./views/pages/scene/chatbox'));
1212
const Register = React.lazy(() => import('./views/pages/register/registerNew'));
1313

1414
const Request = React.lazy(()=> import ('./views/pages/scene/Request'));
15+
const Cron = React.lazy(()=> import ('./views/pages/scene/cron'))
1516

1617
const Monitoring = React.lazy(()=> import ('./views/pages/Security/monitoring'))
1718
const UserManager = React.lazy(() => import ('./views/pages/Security/UserManagement'))
@@ -73,6 +74,7 @@ const routes = [
7374
{ path: '/registerNew', name: 'Register New Users', element: <ProtectedRoute><Register /> </ProtectedRoute>},
7475

7576
{ path: '/request', name: 'Request', element: <ProtectedRoute><Request /> </ProtectedRoute>},
77+
{ path: '/cron', name: 'Cron', element: <ProtectedRoute><Cron/> </ProtectedRoute>},
7678

7779
{ path: '/customer', name: 'Customer', element: <ProtectedRoute><Customers /></ProtectedRoute> },
7880
{ path: '/worker', name: 'Employees', element: <ProtectedRoute><Worker /></ProtectedRoute> },
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
import React, { useState, useEffect } from 'react';
2+
import axiosInstance from './../../../utils/axiosInstance'; // Using the custom axios instance
3+
import {
4+
CButton,
5+
CForm,
6+
CFormInput,
7+
CFormLabel,
8+
CCard,
9+
CCardBody,
10+
CCardHeader,
11+
CAlert,
12+
CFormSelect
13+
} from '@coreui/react';
14+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
15+
import { faFolderOpen, faClock, faPlay } from '@fortawesome/free-solid-svg-icons';
16+
17+
const BackupManagerFrontend = () => {
18+
const [backupDir, setBackupDir] = useState('');
19+
const [hours, setHours] = useState('02');
20+
const [minutes, setMinutes] = useState('00');
21+
const [ampm, setAmPm] = useState('AM');
22+
const [feedback, setFeedback] = useState({ message: '', type: '' });
23+
const [isLoading, setIsLoading] = useState(false);
24+
25+
// Clear feedback message after 5 seconds
26+
useEffect(() => {
27+
if (feedback.message) {
28+
const timer = setTimeout(() => {
29+
setFeedback({ message: '', type: '' });
30+
}, 5000);
31+
return () => clearTimeout(timer);
32+
}
33+
}, [feedback]);
34+
35+
// Fetch saved configuration on component mount
36+
useEffect(() => {
37+
const fetchConfig = async () => {
38+
try {
39+
setIsLoading(true);
40+
const response = await axiosInstance.get('/backupauto/config');
41+
const config = response.data;
42+
43+
// Set backup directory
44+
if (config.backupDir) {
45+
setBackupDir(config.backupDir);
46+
}
47+
48+
// Parse cron schedule to get hours and minutes
49+
if (config.cronSchedule) {
50+
// Cron format: minutes hours * * *
51+
const cronParts = config.cronSchedule.split(' ');
52+
const minutes = cronParts[0];
53+
const hours = cronParts[1];
54+
55+
// Convert 24-hour format to 12-hour format
56+
let hour = parseInt(hours, 10);
57+
let period = 'AM';
58+
59+
if (hour >= 12) {
60+
period = 'PM';
61+
hour = hour === 12 ? 12 : hour - 12;
62+
}
63+
hour = hour === 0 ? 12 : hour;
64+
65+
setHours(hour.toString().padStart(2, '0'));
66+
setMinutes(minutes.padStart(2, '0'));
67+
setAmPm(period);
68+
}
69+
} catch (error) {
70+
console.error('Error fetching backup configuration:', error);
71+
showFeedback('Failed to load saved configuration', 'danger');
72+
} finally {
73+
setIsLoading(false);
74+
}
75+
};
76+
77+
fetchConfig();
78+
}, []);
79+
80+
// Generate hours options (1-12)
81+
const hoursOptions = Array.from({ length: 12 }, (_, i) => {
82+
const hour = i + 1;
83+
return { value: hour.toString().padStart(2, '0'), label: hour.toString().padStart(2, '0') };
84+
});
85+
86+
// Generate minutes options (00-59)
87+
const minutesOptions = Array.from({ length: 60 }, (_, i) => {
88+
const minute = i;
89+
return { value: minute.toString().padStart(2, '0'), label: minute.toString().padStart(2, '0') };
90+
});
91+
92+
const showFeedback = (message, type) => {
93+
setFeedback({ message, type });
94+
};
95+
96+
// Convert 12-hour format to 24-hour for cron
97+
const convertTo24HourFormat = () => {
98+
let hour = parseInt(hours, 10);
99+
100+
if (ampm === 'PM' && hour < 12) {
101+
hour += 12;
102+
} else if (ampm === 'AM' && hour === 12) {
103+
hour = 0;
104+
}
105+
106+
return {
107+
hours: hour.toString().padStart(2, '0'),
108+
minutes: minutes
109+
};
110+
};
111+
112+
const handleUpdateDirectory = async () => {
113+
if (!backupDir.trim()) {
114+
showFeedback('Please enter a valid backup directory path', 'danger');
115+
return;
116+
}
117+
118+
setIsLoading(true);
119+
try {
120+
const response = await axiosInstance.post('/backupauto/update-directory', { backupDir });
121+
showFeedback(response.data, 'success');
122+
} catch (error) {
123+
showFeedback(`Error updating directory: ${error.response?.data || error.message}`, 'danger');
124+
} finally {
125+
setIsLoading(false);
126+
}
127+
};
128+
129+
const handleUpdateSchedule = async () => {
130+
setIsLoading(true);
131+
try {
132+
// Convert to 24-hour format for cron
133+
const time24 = convertTo24HourFormat();
134+
135+
// Create cron schedule - minutes hours * * *
136+
const cronSchedule = `${time24.minutes} ${time24.hours} * * *`;
137+
138+
const response = await axiosInstance.post('/backupauto/update-schedule', { cronSchedule });
139+
showFeedback(response.data, 'success');
140+
} catch (error) {
141+
showFeedback(`Error updating schedule: ${error.response?.data || error.message}`, 'danger');
142+
} finally {
143+
setIsLoading(false);
144+
}
145+
};
146+
147+
const handleManualBackup = async () => {
148+
if (!backupDir.trim()) {
149+
showFeedback('Please configure a backup directory first', 'warning');
150+
return;
151+
}
152+
153+
setIsLoading(true);
154+
try {
155+
const response = await axiosInstance.post('/backupauto/backup');
156+
showFeedback(response.data, 'success');
157+
} catch (error) {
158+
showFeedback(`Error triggering backup: ${error.response?.data || error.message}`, 'danger');
159+
} finally {
160+
setIsLoading(false);
161+
}
162+
};
163+
164+
return (
165+
<div className="p-5">
166+
<h2 className="text-2xl mb-4">Backup Manager</h2>
167+
168+
{feedback.message && (
169+
<CAlert color={feedback.type} className="mb-4">
170+
{feedback.message}
171+
</CAlert>
172+
)}
173+
174+
<CCard className="mb-4">
175+
<CCardHeader>
176+
<div>Backup Configuration</div>
177+
</CCardHeader>
178+
<CCardBody>
179+
<CForm className="mb-4">
180+
<CFormLabel>Backup Directory</CFormLabel>
181+
<div className="d-flex">
182+
<CFormInput
183+
type="text"
184+
value={backupDir}
185+
onChange={(e) => setBackupDir(e.target.value)}
186+
placeholder="Enter backup directory path"
187+
disabled={isLoading}
188+
/>
189+
<CButton
190+
color="primary"
191+
onClick={handleUpdateDirectory}
192+
className="ms-2"
193+
disabled={isLoading}
194+
>
195+
<FontAwesomeIcon icon={faFolderOpen} /> Set Directory
196+
</CButton>
197+
</div>
198+
</CForm>
199+
200+
<CForm className="mb-4">
201+
<CFormLabel>Backup Time</CFormLabel>
202+
<div className="d-flex align-items-center">
203+
<div className="d-flex" style={{ width: '220px' }}>
204+
<CFormSelect
205+
value={hours}
206+
onChange={(e) => setHours(e.target.value)}
207+
disabled={isLoading}
208+
className="me-1"
209+
style={{ width: '70px' }}
210+
>
211+
{hoursOptions.map(option => (
212+
<option key={option.value} value={option.value}>{option.label}</option>
213+
))}
214+
</CFormSelect>
215+
<span className="mx-1 mt-2">:</span>
216+
<CFormSelect
217+
value={minutes}
218+
onChange={(e) => setMinutes(e.target.value)}
219+
disabled={isLoading}
220+
className="me-1"
221+
style={{ width: '70px' }}
222+
>
223+
{minutesOptions.map(option => (
224+
<option key={option.value} value={option.value}>{option.label}</option>
225+
))}
226+
</CFormSelect>
227+
<CFormSelect
228+
value={ampm}
229+
onChange={(e) => setAmPm(e.target.value)}
230+
disabled={isLoading}
231+
style={{ width: '70px' }}
232+
>
233+
<option value="AM">AM</option>
234+
<option value="PM">PM</option>
235+
</CFormSelect>
236+
</div>
237+
<CButton
238+
color="success"
239+
onClick={handleUpdateSchedule}
240+
className="ms-3"
241+
disabled={isLoading}
242+
>
243+
<FontAwesomeIcon icon={faClock} /> Set Schedule
244+
</CButton>
245+
</div>
246+
<small className="text-muted mt-2 d-block">
247+
Current schedule: Daily at {hours}:{minutes} {ampm}
248+
</small>
249+
</CForm>
250+
251+
<CButton
252+
color="danger"
253+
onClick={handleManualBackup}
254+
disabled={isLoading}
255+
className="mt-3"
256+
>
257+
<FontAwesomeIcon icon={faPlay} /> Trigger Backup Now
258+
</CButton>
259+
</CCardBody>
260+
</CCard>
261+
</div>
262+
);
263+
};
264+
265+
export default BackupManagerFrontend;

client/src/views/pages/scene/recovery.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import React, { useState, useEffect } from 'react';
1+
import React, { useState, useEffect} from 'react';
2+
import { useNavigate } from 'react-router-dom';
3+
24
import {
35
CContainer, CButton, CRow, CCol, CForm, CFormLabel, CFormInput,
46
CListGroup, CListGroupItem, CCard, CCardBody, CCardHeader,
@@ -18,7 +20,7 @@ const RecoveryPage = () => {
1820
const [backupInProgress, setBackupInProgress] = useState(false);
1921
const [error, setError] = useState('');
2022
const [directorySet, setDirectorySet] = useState(false);
21-
23+
const navigate = useNavigate()
2224
// Restore saved directory on component mount
2325
useEffect(() => {
2426
const savedDirectory = localStorage.getItem('backupDirectory');
@@ -164,15 +166,21 @@ const RecoveryPage = () => {
164166
setLoading(false);
165167
}
166168
};
167-
169+
const handlesched = async () => {
170+
navigate('/cron'); // Replace '/cron' with the correct route you want to navigate to
171+
};
172+
168173
return (
169174
<CContainer>
170175
{error && <CAlert color="danger" className="mt-3">{error}</CAlert>}
171176

172177
<CRow className="mb-4 mt-3">
173178
<CCol>
174179
<CCard>
175-
<CCardHeader>Backup Configuration</CCardHeader>
180+
<CCardHeader className="d-flex justify-content-between align-items-center">
181+
<div>Backup Configuration</div>
182+
<CButton className="ms-auto"color='primary' onClick={handlesched}>Schedule Backup</CButton>
183+
</CCardHeader>
176184
<CCardBody>
177185
<CForm>
178186
<CFormLabel>Set Backup Directory</CFormLabel>

server/backupConfig.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"backupDir": "C:\\Users\\ryans\\OneDrive\\Desktop\\New folder (3)",
3+
"cronSchedule": "46 20 * * *"
4+
}

server/controllers/management.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
1-
import path from 'path';
2-
import { exec } from 'child_process';
3-
import { fileURLToPath } from 'url';
4-
import fs from 'fs';
5-
import User from '../model/User.js';
6-
7-
8-
const __filename = fileURLToPath(import.meta.url);
9-
const __dirname = path.dirname(__filename);
10-
11-
const BACKUP_DIR = path.resolve(__dirname, 'C:/Users/ryans/OneDrive/Desktop/capstone/shesh/admin/backup');
1+
import cron from "node-cron";
2+
import { exec } from "child_process";
3+
import path from "path";
4+
import fs from "fs";
5+
import express from "express";
6+
import dotenv from "dotenv";
127

138

149

0 commit comments

Comments
 (0)