alpe-temp 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/backend-project/package-lock.json +131 -0
- package/backend-project/package.json +3 -1
- package/backend-project/src/app.js +33 -55
- package/backend-project/src/config/app.config.js +1 -49
- package/backend-project/src/config/env.js +2 -10
- package/backend-project/src/middleware/auth.middleware.js +3 -26
- package/backend-project/src/modules/auth/auth.controller.js +15 -19
- package/backend-project/src/modules/auth/auth.routes.js +4 -8
- package/backend-project/src/modules/auth/auth.service.js +9 -31
- package/backend-project/src/modules/auth/user.model.js +10 -33
- package/backend-project/src/modules/department/department.controller.js +0 -4
- package/backend-project/src/modules/department/department.model.js +1 -4
- package/backend-project/src/modules/department/department.routes.js +0 -1
- package/backend-project/src/modules/department/department.service.js +1 -9
- package/backend-project/src/modules/employee/employee.controller.js +2 -10
- package/backend-project/src/modules/employee/employee.model.js +15 -9
- package/backend-project/src/modules/employee/employee.routes.js +4 -6
- package/backend-project/src/modules/employee/employee.service.js +20 -5
- package/backend-project/src/modules/position/position.controller.js +50 -0
- package/backend-project/src/modules/position/position.model.js +8 -0
- package/backend-project/src/modules/position/position.routes.js +14 -0
- package/backend-project/src/modules/position/position.service.js +21 -0
- package/backend-project/src/modules/reports/reports.controller.js +16 -28
- package/backend-project/src/modules/reports/reports.routes.js +2 -2
- package/backend-project/src/seed.js +69 -15
- package/backend-project/src/utils/token.js +1 -27
- package/frontend-project/dist/assets/index-BXwcQ8Za.css +1 -0
- package/frontend-project/dist/assets/index-Bo0aORq7.js +20 -0
- package/frontend-project/dist/index.html +3 -3
- package/frontend-project/index.html +1 -1
- package/frontend-project/src/Auth/Login.jsx +15 -25
- package/frontend-project/src/Auth/Register.jsx +92 -183
- package/frontend-project/src/Intro.jsx +4 -9
- package/frontend-project/src/LayOut.jsx +10 -23
- package/frontend-project/src/api/ApiClient.js +19 -60
- package/frontend-project/src/layouts/BottomNav.jsx +22 -105
- package/frontend-project/src/layouts/TopNav.jsx +19 -98
- package/frontend-project/src/layouts/useShell.js +30 -44
- package/frontend-project/src/main.jsx +2 -3
- package/frontend-project/src/pages/Department.jsx +21 -58
- package/frontend-project/src/pages/Employee.jsx +131 -113
- package/frontend-project/src/pages/Home.jsx +36 -36
- package/frontend-project/src/pages/Position.jsx +161 -0
- package/frontend-project/src/pages/Reports.jsx +81 -68
- package/package.json +4 -2
- package/server-test-err.txt +0 -0
- package/server-test-out.txt +0 -0
- package/backend-project/src/modules/_example/example.controller.js +0 -82
- package/backend-project/src/modules/_example/example.model.js +0 -47
- package/backend-project/src/modules/_example/example.routes.js +0 -43
- package/backend-project/src/modules/_example/example.service.js +0 -58
- package/backend-project/src/modules/excel/excel.controller.js +0 -61
- package/backend-project/src/modules/excel/excel.routes.js +0 -13
- package/backend-project/src/modules/excel/excel.service.js +0 -303
- package/backend-project/src/modules/salary/salary.controller.js +0 -70
- package/backend-project/src/modules/salary/salary.model.js +0 -23
- package/backend-project/src/modules/salary/salary.routes.js +0 -16
- package/backend-project/src/modules/salary/salary.service.js +0 -44
- package/frontend-project/dist/assets/index-B08ICGra.js +0 -20
- package/frontend-project/dist/assets/index-D_cqT2Z6.css +0 -1
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
"version": "1.0.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"bcryptjs": "^2.4.3",
|
|
12
|
+
"connect-mongo": "^6.0.0",
|
|
12
13
|
"cors": "^2.8.5",
|
|
13
14
|
"dotenv": "^16.4.5",
|
|
14
15
|
"exceljs": "^4.4.0",
|
|
15
16
|
"express": "^4.19.2",
|
|
17
|
+
"express-session": "^1.19.0",
|
|
16
18
|
"helmet": "^7.1.0",
|
|
17
19
|
"jsonwebtoken": "^9.0.2",
|
|
18
20
|
"mongoose": "^8.5.0",
|
|
@@ -183,6 +185,18 @@
|
|
|
183
185
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
|
184
186
|
"license": "MIT"
|
|
185
187
|
},
|
|
188
|
+
"node_modules/asn1.js": {
|
|
189
|
+
"version": "5.4.1",
|
|
190
|
+
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
|
|
191
|
+
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
|
|
192
|
+
"license": "MIT",
|
|
193
|
+
"dependencies": {
|
|
194
|
+
"bn.js": "^4.0.0",
|
|
195
|
+
"inherits": "^2.0.1",
|
|
196
|
+
"minimalistic-assert": "^1.0.0",
|
|
197
|
+
"safer-buffer": "^2.1.0"
|
|
198
|
+
}
|
|
199
|
+
},
|
|
186
200
|
"node_modules/async": {
|
|
187
201
|
"version": "3.2.6",
|
|
188
202
|
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
|
@@ -273,6 +287,12 @@
|
|
|
273
287
|
"integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==",
|
|
274
288
|
"license": "MIT"
|
|
275
289
|
},
|
|
290
|
+
"node_modules/bn.js": {
|
|
291
|
+
"version": "4.12.3",
|
|
292
|
+
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
|
|
293
|
+
"integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
|
|
294
|
+
"license": "MIT"
|
|
295
|
+
},
|
|
276
296
|
"node_modules/body-parser": {
|
|
277
297
|
"version": "1.20.5",
|
|
278
298
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
|
|
@@ -481,6 +501,46 @@
|
|
|
481
501
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
|
482
502
|
"license": "MIT"
|
|
483
503
|
},
|
|
504
|
+
"node_modules/connect-mongo": {
|
|
505
|
+
"version": "6.0.0",
|
|
506
|
+
"resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-6.0.0.tgz",
|
|
507
|
+
"integrity": "sha512-mHxfnTiWk7ZtxmHdcrFBKlr7fCtgGoFpx/oe9jFW0yb2NinagsxEeuol78nUWMpnWyYK0nnuXMlU9wrgUjTE6g==",
|
|
508
|
+
"license": "MIT",
|
|
509
|
+
"dependencies": {
|
|
510
|
+
"debug": "^4.4.3",
|
|
511
|
+
"kruptein": "3.0.8"
|
|
512
|
+
},
|
|
513
|
+
"engines": {
|
|
514
|
+
"node": ">=20.8.0"
|
|
515
|
+
},
|
|
516
|
+
"peerDependencies": {
|
|
517
|
+
"express-session": "^1.17.1",
|
|
518
|
+
"mongodb": ">=5.0.0"
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
"node_modules/connect-mongo/node_modules/debug": {
|
|
522
|
+
"version": "4.4.3",
|
|
523
|
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
|
524
|
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
|
525
|
+
"license": "MIT",
|
|
526
|
+
"dependencies": {
|
|
527
|
+
"ms": "^2.1.3"
|
|
528
|
+
},
|
|
529
|
+
"engines": {
|
|
530
|
+
"node": ">=6.0"
|
|
531
|
+
},
|
|
532
|
+
"peerDependenciesMeta": {
|
|
533
|
+
"supports-color": {
|
|
534
|
+
"optional": true
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
"node_modules/connect-mongo/node_modules/ms": {
|
|
539
|
+
"version": "2.1.3",
|
|
540
|
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
|
541
|
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
|
542
|
+
"license": "MIT"
|
|
543
|
+
},
|
|
484
544
|
"node_modules/content-disposition": {
|
|
485
545
|
"version": "0.5.4",
|
|
486
546
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
|
@@ -818,6 +878,29 @@
|
|
|
818
878
|
"url": "https://opencollective.com/express"
|
|
819
879
|
}
|
|
820
880
|
},
|
|
881
|
+
"node_modules/express-session": {
|
|
882
|
+
"version": "1.19.0",
|
|
883
|
+
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.19.0.tgz",
|
|
884
|
+
"integrity": "sha512-0csaMkGq+vaiZTmSMMGkfdCOabYv192VbytFypcvI0MANrp+4i/7yEkJ0sbAEhycQjntaKGzYfjfXQyVb7BHMA==",
|
|
885
|
+
"license": "MIT",
|
|
886
|
+
"dependencies": {
|
|
887
|
+
"cookie": "~0.7.2",
|
|
888
|
+
"cookie-signature": "~1.0.7",
|
|
889
|
+
"debug": "~2.6.9",
|
|
890
|
+
"depd": "~2.0.0",
|
|
891
|
+
"on-headers": "~1.1.0",
|
|
892
|
+
"parseurl": "~1.3.3",
|
|
893
|
+
"safe-buffer": "~5.2.1",
|
|
894
|
+
"uid-safe": "~2.1.5"
|
|
895
|
+
},
|
|
896
|
+
"engines": {
|
|
897
|
+
"node": ">= 0.8.0"
|
|
898
|
+
},
|
|
899
|
+
"funding": {
|
|
900
|
+
"type": "opencollective",
|
|
901
|
+
"url": "https://opencollective.com/express"
|
|
902
|
+
}
|
|
903
|
+
},
|
|
821
904
|
"node_modules/fast-csv": {
|
|
822
905
|
"version": "4.3.6",
|
|
823
906
|
"resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz",
|
|
@@ -1307,6 +1390,18 @@
|
|
|
1307
1390
|
"node": ">=12.0.0"
|
|
1308
1391
|
}
|
|
1309
1392
|
},
|
|
1393
|
+
"node_modules/kruptein": {
|
|
1394
|
+
"version": "3.0.8",
|
|
1395
|
+
"resolved": "https://registry.npmjs.org/kruptein/-/kruptein-3.0.8.tgz",
|
|
1396
|
+
"integrity": "sha512-0CyalFA0Cjp3jnziMp0u1uLZW2/ouhQ0mEMfYlroBXNe86na1RwAuwBcdRAegeWZNMfQy/G5fN47g/Axjtqrfw==",
|
|
1397
|
+
"license": "MIT",
|
|
1398
|
+
"dependencies": {
|
|
1399
|
+
"asn1.js": "^5.4.1"
|
|
1400
|
+
},
|
|
1401
|
+
"engines": {
|
|
1402
|
+
"node": ">8"
|
|
1403
|
+
}
|
|
1404
|
+
},
|
|
1310
1405
|
"node_modules/lazystream": {
|
|
1311
1406
|
"version": "1.0.1",
|
|
1312
1407
|
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
|
|
@@ -1548,6 +1643,12 @@
|
|
|
1548
1643
|
"node": ">= 0.6"
|
|
1549
1644
|
}
|
|
1550
1645
|
},
|
|
1646
|
+
"node_modules/minimalistic-assert": {
|
|
1647
|
+
"version": "1.0.1",
|
|
1648
|
+
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
|
1649
|
+
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
|
1650
|
+
"license": "ISC"
|
|
1651
|
+
},
|
|
1551
1652
|
"node_modules/minimatch": {
|
|
1552
1653
|
"version": "3.1.5",
|
|
1553
1654
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
|
@@ -1859,6 +1960,15 @@
|
|
|
1859
1960
|
"node": ">= 0.8"
|
|
1860
1961
|
}
|
|
1861
1962
|
},
|
|
1963
|
+
"node_modules/on-headers": {
|
|
1964
|
+
"version": "1.1.0",
|
|
1965
|
+
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
|
|
1966
|
+
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
|
|
1967
|
+
"license": "MIT",
|
|
1968
|
+
"engines": {
|
|
1969
|
+
"node": ">= 0.8"
|
|
1970
|
+
}
|
|
1971
|
+
},
|
|
1862
1972
|
"node_modules/once": {
|
|
1863
1973
|
"version": "1.4.0",
|
|
1864
1974
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
|
@@ -1961,6 +2071,15 @@
|
|
|
1961
2071
|
"url": "https://github.com/sponsors/ljharb"
|
|
1962
2072
|
}
|
|
1963
2073
|
},
|
|
2074
|
+
"node_modules/random-bytes": {
|
|
2075
|
+
"version": "1.0.0",
|
|
2076
|
+
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
|
2077
|
+
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
|
|
2078
|
+
"license": "MIT",
|
|
2079
|
+
"engines": {
|
|
2080
|
+
"node": ">= 0.8"
|
|
2081
|
+
}
|
|
2082
|
+
},
|
|
1964
2083
|
"node_modules/range-parser": {
|
|
1965
2084
|
"version": "1.2.1",
|
|
1966
2085
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
|
@@ -2384,6 +2503,18 @@
|
|
|
2384
2503
|
"node": ">= 0.6"
|
|
2385
2504
|
}
|
|
2386
2505
|
},
|
|
2506
|
+
"node_modules/uid-safe": {
|
|
2507
|
+
"version": "2.1.5",
|
|
2508
|
+
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
|
2509
|
+
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
|
|
2510
|
+
"license": "MIT",
|
|
2511
|
+
"dependencies": {
|
|
2512
|
+
"random-bytes": "~1.0.0"
|
|
2513
|
+
},
|
|
2514
|
+
"engines": {
|
|
2515
|
+
"node": ">= 0.8"
|
|
2516
|
+
}
|
|
2517
|
+
},
|
|
2387
2518
|
"node_modules/undefsafe": {
|
|
2388
2519
|
"version": "2.0.5",
|
|
2389
2520
|
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
|
@@ -10,13 +10,15 @@
|
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"bcryptjs": "^2.4.3",
|
|
13
|
+
"connect-mongo": "^6.0.0",
|
|
13
14
|
"cors": "^2.8.5",
|
|
14
15
|
"dotenv": "^16.4.5",
|
|
15
16
|
"exceljs": "^4.4.0",
|
|
16
17
|
"express": "^4.19.2",
|
|
17
|
-
"
|
|
18
|
+
"express-session": "^1.19.0",
|
|
18
19
|
"helmet": "^7.1.0",
|
|
19
20
|
"jsonwebtoken": "^9.0.2",
|
|
21
|
+
"mongoose": "^8.5.0",
|
|
20
22
|
"uuid": "^10.0.0"
|
|
21
23
|
},
|
|
22
24
|
"devDependencies": {
|
|
@@ -1,39 +1,37 @@
|
|
|
1
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
-
// 🚀 APP — Express application setup
|
|
3
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
4
|
-
//
|
|
5
|
-
// WHAT THIS FILE DOES:
|
|
6
|
-
// - Creates the Express app
|
|
7
|
-
// - Adds middleware (security, CORS, JSON parsing)
|
|
8
|
-
// - Registers all API module routes
|
|
9
|
-
// - Exports the app for server.js to use
|
|
10
|
-
//
|
|
11
|
-
// HOW TO ADD A NEW MODULE:
|
|
12
|
-
// 1. Add its routes to src/config/app.config.js (registeredRoutes array)
|
|
13
|
-
// 2. Add a use() line below (see the PATTERN section)
|
|
14
|
-
// 3. Done! Your new API is live.
|
|
15
|
-
//
|
|
16
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
17
|
-
|
|
18
1
|
const express = require('express');
|
|
19
|
-
const path
|
|
20
|
-
const helmet
|
|
21
|
-
const cors
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const helmet = require('helmet');
|
|
4
|
+
const cors = require('cors');
|
|
5
|
+
const session = require('express-session');
|
|
6
|
+
const { MongoStore } = require('connect-mongo');
|
|
7
|
+
const db = require('./config/db');
|
|
8
|
+
const cfg = require('./config/app.config');
|
|
9
|
+
const env = require('./config/env');
|
|
25
10
|
const { errorHandler, notFoundHandler } = require('./middleware/error.middleware');
|
|
26
11
|
|
|
27
12
|
const app = express();
|
|
28
13
|
|
|
29
|
-
|
|
30
|
-
app.use(
|
|
31
|
-
|
|
32
|
-
|
|
14
|
+
app.use(helmet({ contentSecurityPolicy: false }));
|
|
15
|
+
app.use(cors({
|
|
16
|
+
origin: process.env.NODE_ENV === 'production' ? false : 'http://localhost:5173',
|
|
17
|
+
credentials: true,
|
|
18
|
+
}));
|
|
19
|
+
app.use(express.json({ limit: '10mb' }));
|
|
33
20
|
app.use(express.urlencoded({ extended: true }));
|
|
34
21
|
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
app.use(session({
|
|
23
|
+
secret: env.SESSION_SECRET,
|
|
24
|
+
resave: false,
|
|
25
|
+
saveUninitialized: false,
|
|
26
|
+
store: MongoStore.create({ mongoUrl: env.MONGODB_URI, collectionName: 'sessions' }),
|
|
27
|
+
cookie: {
|
|
28
|
+
httpOnly: true,
|
|
29
|
+
secure: false,
|
|
30
|
+
sameSite: 'lax',
|
|
31
|
+
maxAge: 24 * 60 * 60 * 1000,
|
|
32
|
+
},
|
|
33
|
+
}));
|
|
34
|
+
|
|
37
35
|
app.get('/health', (_req, res) =>
|
|
38
36
|
res.json({
|
|
39
37
|
status: 'ok',
|
|
@@ -42,28 +40,12 @@ app.get('/health', (_req, res) =>
|
|
|
42
40
|
})
|
|
43
41
|
);
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// ║ ║
|
|
48
|
-
// ║ Step 1. In app.config.js, add to registeredRoutes: ║
|
|
49
|
-
// ║ { name: 'products', routes: require('./modules/product/product.routes') } ║
|
|
50
|
-
// ║ ║
|
|
51
|
-
// ║ Step 2. Add one line below: ║
|
|
52
|
-
// ║ app.use('/api/products', require('./modules/product/product.routes')); ║
|
|
53
|
-
// ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
54
|
-
|
|
55
|
-
// ─── MODULE ROUTES ───────────────────────────────────────────────────────────
|
|
56
|
-
// Each line starts a new API group under /api/...
|
|
57
|
-
// The route file inside each module handles its own sub-routes.
|
|
58
|
-
//
|
|
59
|
-
app.use('/api/auth', require('./modules/auth/auth.routes'));
|
|
60
|
-
app.use('/api/excel', require('./modules/excel/excel.routes'));
|
|
61
|
-
app.use('/api/employees', require('./modules/employee/employee.routes'));
|
|
43
|
+
app.use('/api/auth', require('./modules/auth/auth.routes'));
|
|
44
|
+
app.use('/api/employees', require('./modules/employee/employee.routes'));
|
|
62
45
|
app.use('/api/departments', require('./modules/department/department.routes'));
|
|
63
|
-
app.use('/api/
|
|
64
|
-
app.use('/api/reports',
|
|
46
|
+
app.use('/api/positions', require('./modules/position/position.routes'));
|
|
47
|
+
app.use('/api/reports', require('./modules/reports/reports.routes'));
|
|
65
48
|
|
|
66
|
-
// ─── FRONTEND SPA (serves built React app) ────────────────────────────────────
|
|
67
49
|
const distPath = path.join(__dirname, '..', '..', 'frontend-project', 'dist');
|
|
68
50
|
app.use(express.static(distPath));
|
|
69
51
|
|
|
@@ -72,13 +54,9 @@ app.get('*', (req, res, next) => {
|
|
|
72
54
|
res.sendFile(path.join(distPath, 'index.html'));
|
|
73
55
|
});
|
|
74
56
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
app.use(notFoundHandler); // handles 404 - route not found
|
|
78
|
-
app.use(errorHandler); // handles 500 - server errors
|
|
57
|
+
app.use(notFoundHandler);
|
|
58
|
+
app.use(errorHandler);
|
|
79
59
|
|
|
80
|
-
// ─── DATABASE CONNECTION ─────────────────────────────────────────────────────
|
|
81
|
-
// Called by server.js (not here) so the app is testable without a DB.
|
|
82
60
|
app.connectDB = () => db.connect();
|
|
83
61
|
|
|
84
62
|
module.exports = app;
|
|
@@ -1,72 +1,24 @@
|
|
|
1
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
-
// ⚙️ APP CONFIG — One place to control the whole backend
|
|
3
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
4
|
-
//
|
|
5
|
-
// WHAT THIS FILE DOES:
|
|
6
|
-
// - Loads environment variables from .env (via env.js)
|
|
7
|
-
// - Lists every registered module (so you can add/remove features)
|
|
8
|
-
// - Exports everything in one easy-to-find object
|
|
9
|
-
//
|
|
10
|
-
// HOW TO ADD A NEW FEATURE (e.g. "Products"):
|
|
11
|
-
// 1. Create folder: src/modules/product/
|
|
12
|
-
// 2. Copy files from src/modules/_example/ (rename to product.*)
|
|
13
|
-
// 3. Import routes below in this file (see REGISTERED MODULES)
|
|
14
|
-
// 4. Add route line in app.js (see instructions there)
|
|
15
|
-
//
|
|
16
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
17
|
-
|
|
18
1
|
const env = require('./env');
|
|
19
2
|
|
|
20
|
-
// ─── SERVER SETTINGS ─────────────────────────────────────────────────────────
|
|
21
3
|
const server = {
|
|
22
4
|
PORT: env.PORT,
|
|
23
5
|
NODE_ENV: env.NODE_ENV,
|
|
24
6
|
};
|
|
25
7
|
|
|
26
|
-
// ─── DATABASE ────────────────────────────────────────────────────────────────
|
|
27
8
|
const database = {
|
|
28
9
|
URI: env.MONGODB_URI,
|
|
29
10
|
};
|
|
30
11
|
|
|
31
|
-
// ─── JWT AUTHENTICATION ──────────────────────────────────────────────────────
|
|
32
|
-
const jwt = {
|
|
33
|
-
SECRET: env.JWT_SECRET,
|
|
34
|
-
EXPIRES_IN: env.JWT_EXPIRES_IN,
|
|
35
|
-
REFRESH_SECRET: env.JWT_REFRESH_SECRET,
|
|
36
|
-
REFRESH_EXPIRES_IN: env.JWT_REFRESH_EXPIRES_IN,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
// ─── BCRYPT ──────────────────────────────────────────────────────────────────
|
|
40
|
-
const bcrypt = {
|
|
41
|
-
ROUNDS: env.BCRYPT_ROUNDS,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// ─── REGISTERED MODULES ──────────────────────────────────────────────────────
|
|
45
|
-
// 🔵 To ADD a new module:
|
|
46
|
-
// 1. Import its routes: const productRoutes = require('../modules/product/product.routes');
|
|
47
|
-
// 2. Add to the list: { name: 'products', routes: productRoutes }
|
|
48
|
-
//
|
|
49
|
-
// 🔴 To REMOVE a module:
|
|
50
|
-
// Just delete its line from this array (and the import above).
|
|
51
|
-
//
|
|
52
|
-
// Every module in this list automatically gets:
|
|
53
|
-
// - Route registration in app.js
|
|
54
|
-
// - Listed in the /health endpoint for debugging
|
|
55
|
-
//
|
|
56
12
|
const registeredRoutes = [
|
|
57
13
|
{ name: 'auth', routes: require('../modules/auth/auth.routes') },
|
|
58
14
|
{ name: 'employees', routes: require('../modules/employee/employee.routes') },
|
|
59
15
|
{ name: 'departments', routes: require('../modules/department/department.routes') },
|
|
60
|
-
{ name: '
|
|
16
|
+
{ name: 'positions', routes: require('../modules/position/position.routes') },
|
|
61
17
|
{ name: 'reports', routes: require('../modules/reports/reports.routes') },
|
|
62
|
-
{ name: 'excel', routes: require('../modules/excel/excel.routes') },
|
|
63
18
|
];
|
|
64
19
|
|
|
65
|
-
// ─── EXPORT EVERYTHING ───────────────────────────────────────────────────────
|
|
66
20
|
module.exports = {
|
|
67
21
|
server,
|
|
68
22
|
database,
|
|
69
|
-
jwt,
|
|
70
|
-
bcrypt,
|
|
71
23
|
registeredRoutes,
|
|
72
24
|
};
|
|
@@ -3,17 +3,9 @@ require('dotenv').config();
|
|
|
3
3
|
const env = {
|
|
4
4
|
PORT: process.env.PORT || 3000,
|
|
5
5
|
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
JWT_SECRET: process.env.JWT_SECRET || 'change-this-secret-in-production',
|
|
10
|
-
JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '7d',
|
|
11
|
-
JWT_REFRESH_SECRET: process.env.JWT_REFRESH_SECRET || 'change-this-refresh-secret',
|
|
12
|
-
JWT_REFRESH_EXPIRES_IN: process.env.JWT_REFRESH_EXPIRES_IN || '30d',
|
|
13
|
-
|
|
6
|
+
MONGODB_URI: process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/HRMS',
|
|
7
|
+
SESSION_SECRET: process.env.SESSION_SECRET || 'hrms-session-secret-change-in-production',
|
|
14
8
|
BCRYPT_ROUNDS: parseInt(process.env.BCRYPT_ROUNDS || '12', 10),
|
|
15
|
-
|
|
16
|
-
EXCEL_OUTPUT_DIR: process.env.EXCEL_OUTPUT_DIR || './exports',
|
|
17
9
|
};
|
|
18
10
|
|
|
19
11
|
module.exports = env;
|
|
@@ -1,33 +1,10 @@
|
|
|
1
|
-
const { verifyAccessToken } = require('../utils/token');
|
|
2
1
|
const res_ = require('../utils/response');
|
|
3
2
|
|
|
4
|
-
/**
|
|
5
|
-
* authenticate — verifies the Bearer token and attaches `req.user`.
|
|
6
|
-
*/
|
|
7
3
|
const authenticate = (req, res, next) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (!token) return res_.unauthorized(res, 'No token provided');
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
req.user = verifyAccessToken(token);
|
|
15
|
-
return next();
|
|
16
|
-
} catch (err) {
|
|
17
|
-
const message = err.name === 'TokenExpiredError' ? 'Token expired' : 'Invalid token';
|
|
18
|
-
return res_.unauthorized(res, message);
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* authorize — gates a route to specific roles.
|
|
24
|
-
* Usage: router.delete('/admin', authenticate, authorize('admin'), handler)
|
|
25
|
-
*/
|
|
26
|
-
const authorize = (...roles) => (req, res, next) => {
|
|
27
|
-
if (!roles.includes(req.user?.role)) {
|
|
28
|
-
return res_.forbidden(res, 'Insufficient permissions');
|
|
4
|
+
if (!req.session || !req.session.userId) {
|
|
5
|
+
return res_.unauthorized(res, 'Please log in first');
|
|
29
6
|
}
|
|
30
7
|
return next();
|
|
31
8
|
};
|
|
32
9
|
|
|
33
|
-
module.exports = { authenticate
|
|
10
|
+
module.exports = { authenticate };
|
|
@@ -2,10 +2,10 @@ const AuthService = require('./auth.service');
|
|
|
2
2
|
const res_ = require('../../utils/response');
|
|
3
3
|
|
|
4
4
|
const AuthController = {
|
|
5
|
-
async
|
|
5
|
+
async register(req, res) {
|
|
6
6
|
try {
|
|
7
|
-
const
|
|
8
|
-
return res_.created(res,
|
|
7
|
+
const user = await AuthService.createUser(req.body);
|
|
8
|
+
return res_.created(res, user, 'User account created');
|
|
9
9
|
} catch (err) {
|
|
10
10
|
return res_.error(res, err.message, err.statusCode || 500);
|
|
11
11
|
}
|
|
@@ -13,17 +13,10 @@ const AuthController = {
|
|
|
13
13
|
|
|
14
14
|
async login(req, res) {
|
|
15
15
|
try {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return res_.
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
async refresh(req, res) {
|
|
24
|
-
try {
|
|
25
|
-
const result = await AuthService.refresh(req.body.refreshToken);
|
|
26
|
-
return res_.success(res, result, 'Tokens refreshed');
|
|
16
|
+
const user = await AuthService.login(req.body);
|
|
17
|
+
req.session.userId = user.id;
|
|
18
|
+
req.session.userName = user.userName;
|
|
19
|
+
return res_.success(res, { user }, 'Login successful');
|
|
27
20
|
} catch (err) {
|
|
28
21
|
return res_.error(res, err.message, err.statusCode || 500);
|
|
29
22
|
}
|
|
@@ -31,16 +24,19 @@ const AuthController = {
|
|
|
31
24
|
|
|
32
25
|
async me(req, res) {
|
|
33
26
|
try {
|
|
34
|
-
const user = await AuthService.me(req.
|
|
35
|
-
return res_.success(res, user);
|
|
27
|
+
const user = await AuthService.me(req.session.userId);
|
|
28
|
+
return res_.success(res, { user });
|
|
36
29
|
} catch (err) {
|
|
37
30
|
return res_.error(res, err.message, err.statusCode || 500);
|
|
38
31
|
}
|
|
39
32
|
},
|
|
40
33
|
|
|
41
|
-
logout(
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
async logout(req, res) {
|
|
35
|
+
req.session.destroy((err) => {
|
|
36
|
+
if (err) return res_.error(res, 'Logout failed', 500);
|
|
37
|
+
res.clearCookie('connect.sid');
|
|
38
|
+
return res_.success(res, null, 'Logged out successfully');
|
|
39
|
+
});
|
|
44
40
|
},
|
|
45
41
|
};
|
|
46
42
|
|
|
@@ -4,13 +4,9 @@ const { authenticate } = require('../../middleware/auth.middleware');
|
|
|
4
4
|
|
|
5
5
|
const router = Router();
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
router.post('/
|
|
9
|
-
router.
|
|
10
|
-
router.post('/
|
|
11
|
-
|
|
12
|
-
// Protected routes
|
|
13
|
-
router.get('/me', authenticate, AuthController.me);
|
|
14
|
-
router.post('/logout', authenticate, AuthController.logout);
|
|
7
|
+
router.post('/register', AuthController.register);
|
|
8
|
+
router.post('/login', AuthController.login);
|
|
9
|
+
router.get('/me', authenticate, AuthController.me);
|
|
10
|
+
router.post('/logout', authenticate, AuthController.logout);
|
|
15
11
|
|
|
16
12
|
module.exports = router;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const bcrypt = require('bcryptjs');
|
|
2
2
|
const User = require('./user.model');
|
|
3
3
|
const env = require('../../config/env');
|
|
4
|
-
const { generateTokens, verifyRefreshToken } = require('../../utils/token');
|
|
5
4
|
|
|
6
5
|
const fail = (message, statusCode) => {
|
|
7
6
|
const err = new Error(message);
|
|
@@ -10,45 +9,24 @@ const fail = (message, statusCode) => {
|
|
|
10
9
|
};
|
|
11
10
|
|
|
12
11
|
const AuthService = {
|
|
13
|
-
async
|
|
14
|
-
const existing = await User.findOne({
|
|
15
|
-
if (existing) fail('
|
|
16
|
-
|
|
12
|
+
async createUser({ userName, password }) {
|
|
13
|
+
const existing = await User.findOne({ userName });
|
|
14
|
+
if (existing) fail('Username is already taken', 409);
|
|
17
15
|
const hashed = await bcrypt.hash(password, env.BCRYPT_ROUNDS);
|
|
18
|
-
const user = await User.create({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return { user: user.toSafeObject(), ...tokens };
|
|
16
|
+
const user = await User.create({ userName, password: hashed });
|
|
17
|
+
return user.toSafeObject();
|
|
22
18
|
},
|
|
23
19
|
|
|
24
|
-
async login({
|
|
25
|
-
const user = await User.findOne({
|
|
20
|
+
async login({ userName, password }) {
|
|
21
|
+
const user = await User.findOne({ userName }).populate('employee');
|
|
26
22
|
if (!user) fail('Invalid credentials', 401);
|
|
27
|
-
|
|
28
23
|
const valid = await bcrypt.compare(password, user.password);
|
|
29
24
|
if (!valid) fail('Invalid credentials', 401);
|
|
30
|
-
|
|
31
|
-
const tokens = generateTokens(user.toSafeObject());
|
|
32
|
-
return { user: user.toSafeObject(), ...tokens };
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
async refresh(refreshToken) {
|
|
36
|
-
let payload;
|
|
37
|
-
try {
|
|
38
|
-
payload = verifyRefreshToken(refreshToken);
|
|
39
|
-
} catch {
|
|
40
|
-
fail('Invalid or expired refresh token', 401);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const user = await User.findById(payload.id);
|
|
44
|
-
if (!user) fail('User not found', 401);
|
|
45
|
-
|
|
46
|
-
const tokens = generateTokens(user.toSafeObject());
|
|
47
|
-
return { user: user.toSafeObject(), ...tokens };
|
|
25
|
+
return user.toSafeObject();
|
|
48
26
|
},
|
|
49
27
|
|
|
50
28
|
async me(userId) {
|
|
51
|
-
const user = await User.findById(userId);
|
|
29
|
+
const user = await User.findById(userId).populate('employee');
|
|
52
30
|
if (!user) fail('User not found', 404);
|
|
53
31
|
return user.toSafeObject();
|
|
54
32
|
},
|