alpe-temp 1.0.0
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/.env.example +13 -0
- package/backend-project/README.md +133 -0
- package/backend-project/package-lock.json +2559 -0
- package/backend-project/package.json +25 -0
- package/backend-project/server.js +28 -0
- package/backend-project/src/app.js +84 -0
- package/backend-project/src/config/app.config.js +72 -0
- package/backend-project/src/config/db.js +20 -0
- package/backend-project/src/config/env.js +19 -0
- package/backend-project/src/middleware/auth.middleware.js +33 -0
- package/backend-project/src/middleware/error.middleware.js +19 -0
- package/backend-project/src/modules/_example/example.controller.js +82 -0
- package/backend-project/src/modules/_example/example.model.js +47 -0
- package/backend-project/src/modules/_example/example.routes.js +43 -0
- package/backend-project/src/modules/_example/example.service.js +58 -0
- package/backend-project/src/modules/auth/auth.controller.js +47 -0
- package/backend-project/src/modules/auth/auth.routes.js +16 -0
- package/backend-project/src/modules/auth/auth.service.js +57 -0
- package/backend-project/src/modules/auth/user.model.js +41 -0
- package/backend-project/src/modules/department/department.controller.js +54 -0
- package/backend-project/src/modules/department/department.model.js +10 -0
- package/backend-project/src/modules/department/department.routes.js +15 -0
- package/backend-project/src/modules/department/department.service.js +29 -0
- package/backend-project/src/modules/employee/employee.controller.js +63 -0
- package/backend-project/src/modules/employee/employee.model.js +15 -0
- package/backend-project/src/modules/employee/employee.routes.js +16 -0
- package/backend-project/src/modules/employee/employee.service.js +30 -0
- package/backend-project/src/modules/excel/excel.controller.js +61 -0
- package/backend-project/src/modules/excel/excel.routes.js +13 -0
- package/backend-project/src/modules/excel/excel.service.js +303 -0
- package/backend-project/src/modules/reports/reports.controller.js +41 -0
- package/backend-project/src/modules/reports/reports.routes.js +10 -0
- package/backend-project/src/modules/salary/salary.controller.js +70 -0
- package/backend-project/src/modules/salary/salary.model.js +23 -0
- package/backend-project/src/modules/salary/salary.routes.js +16 -0
- package/backend-project/src/modules/salary/salary.service.js +44 -0
- package/backend-project/src/seed.js +36 -0
- package/backend-project/src/utils/response.js +35 -0
- package/backend-project/src/utils/token.js +27 -0
- package/bin/epms.js +161 -0
- package/frontend-project/README.md +16 -0
- package/frontend-project/dist/assets/index-B08ICGra.js +20 -0
- package/frontend-project/dist/assets/index-D_cqT2Z6.css +1 -0
- package/frontend-project/dist/car.jfif +0 -0
- package/frontend-project/dist/favicon.svg +1 -0
- package/frontend-project/dist/icons.svg +24 -0
- package/frontend-project/dist/index.html +14 -0
- package/frontend-project/dist/logo.png +0 -0
- package/frontend-project/eslint.config.js +21 -0
- package/frontend-project/index.html +13 -0
- package/frontend-project/package-lock.json +3660 -0
- package/frontend-project/package.json +33 -0
- package/frontend-project/postcss.config.js +6 -0
- package/frontend-project/tailwind.config.js +15 -0
- package/frontend-project/vite.config.js +8 -0
- package/package.json +41 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "modular-auth-backend",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Modular Node.js backend with auth and Excel export utility",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node server.js",
|
|
8
|
+
"dev": "nodemon server.js",
|
|
9
|
+
"seed": "node src/seed.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"bcryptjs": "^2.4.3",
|
|
13
|
+
"cors": "^2.8.5",
|
|
14
|
+
"dotenv": "^16.4.5",
|
|
15
|
+
"exceljs": "^4.4.0",
|
|
16
|
+
"express": "^4.19.2",
|
|
17
|
+
"mongoose": "^8.5.0",
|
|
18
|
+
"helmet": "^7.1.0",
|
|
19
|
+
"jsonwebtoken": "^9.0.2",
|
|
20
|
+
"uuid": "^10.0.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"nodemon": "^3.1.4"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const app = require('./src/app');
|
|
2
|
+
const env = require('./src/config/env');
|
|
3
|
+
|
|
4
|
+
const start = async () => {
|
|
5
|
+
await app.connectDB();
|
|
6
|
+
|
|
7
|
+
const server = app.listen(env.PORT, () => {
|
|
8
|
+
console.log(`\n Server running on http://localhost:${env.PORT}`);
|
|
9
|
+
console.log(` Environment : ${env.NODE_ENV}`);
|
|
10
|
+
console.log(` Health : http://localhost:${env.PORT}/health\n`);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const shutdown = (signal) => {
|
|
14
|
+
console.log(`\n${signal} received — shutting down gracefully...`);
|
|
15
|
+
server.close(() => {
|
|
16
|
+
console.log('Server closed.');
|
|
17
|
+
process.exit(0);
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
22
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
start().catch((err) => {
|
|
26
|
+
console.error('Failed to start server:', err.message);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
const express = require('express');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const helmet = require('helmet');
|
|
21
|
+
const cors = require('cors');
|
|
22
|
+
const db = require('./config/db');
|
|
23
|
+
const cfg = require('./config/app.config');
|
|
24
|
+
|
|
25
|
+
const { errorHandler, notFoundHandler } = require('./middleware/error.middleware');
|
|
26
|
+
|
|
27
|
+
const app = express();
|
|
28
|
+
|
|
29
|
+
// ─── GLOBAL MIDDLEWARE (applied to every request) ────────────────────────────
|
|
30
|
+
app.use(helmet()); // security headers
|
|
31
|
+
app.use(cors()); // allow cross-origin requests
|
|
32
|
+
app.use(express.json({ limit: '10mb' })); // parse JSON bodies
|
|
33
|
+
app.use(express.urlencoded({ extended: true }));
|
|
34
|
+
|
|
35
|
+
// ─── HEALTH CHECK ────────────────────────────────────────────────────────────
|
|
36
|
+
// Every scenario needs a health endpoint for deployment monitoring.
|
|
37
|
+
app.get('/health', (_req, res) =>
|
|
38
|
+
res.json({
|
|
39
|
+
status: 'ok',
|
|
40
|
+
timestamp: new Date().toISOString(),
|
|
41
|
+
modules: cfg.registeredRoutes.map((m) => m.name),
|
|
42
|
+
})
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
46
|
+
// ║ 📌 PATTERN: Register a new module ║
|
|
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'));
|
|
62
|
+
app.use('/api/departments', require('./modules/department/department.routes'));
|
|
63
|
+
app.use('/api/salaries', require('./modules/salary/salary.routes'));
|
|
64
|
+
app.use('/api/reports', require('./modules/reports/reports.routes'));
|
|
65
|
+
|
|
66
|
+
// ─── FRONTEND SPA (serves built React app) ────────────────────────────────────
|
|
67
|
+
const distPath = path.join(__dirname, '..', '..', 'frontend-project', 'dist');
|
|
68
|
+
app.use(express.static(distPath));
|
|
69
|
+
|
|
70
|
+
app.get('*', (req, res, next) => {
|
|
71
|
+
if (req.path.startsWith('/api')) return next();
|
|
72
|
+
res.sendFile(path.join(distPath, 'index.html'));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// ─── ERROR HANDLING (must be last) ───────────────────────────────────────────
|
|
76
|
+
// These catch any errors from routes above.
|
|
77
|
+
app.use(notFoundHandler); // handles 404 - route not found
|
|
78
|
+
app.use(errorHandler); // handles 500 - server errors
|
|
79
|
+
|
|
80
|
+
// ─── DATABASE CONNECTION ─────────────────────────────────────────────────────
|
|
81
|
+
// Called by server.js (not here) so the app is testable without a DB.
|
|
82
|
+
app.connectDB = () => db.connect();
|
|
83
|
+
|
|
84
|
+
module.exports = app;
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
const env = require('./env');
|
|
19
|
+
|
|
20
|
+
// ─── SERVER SETTINGS ─────────────────────────────────────────────────────────
|
|
21
|
+
const server = {
|
|
22
|
+
PORT: env.PORT,
|
|
23
|
+
NODE_ENV: env.NODE_ENV,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// ─── DATABASE ────────────────────────────────────────────────────────────────
|
|
27
|
+
const database = {
|
|
28
|
+
URI: env.MONGODB_URI,
|
|
29
|
+
};
|
|
30
|
+
|
|
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
|
+
const registeredRoutes = [
|
|
57
|
+
{ name: 'auth', routes: require('../modules/auth/auth.routes') },
|
|
58
|
+
{ name: 'employees', routes: require('../modules/employee/employee.routes') },
|
|
59
|
+
{ name: 'departments', routes: require('../modules/department/department.routes') },
|
|
60
|
+
{ name: 'salaries', routes: require('../modules/salary/salary.routes') },
|
|
61
|
+
{ name: 'reports', routes: require('../modules/reports/reports.routes') },
|
|
62
|
+
{ name: 'excel', routes: require('../modules/excel/excel.routes') },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
// ─── EXPORT EVERYTHING ───────────────────────────────────────────────────────
|
|
66
|
+
module.exports = {
|
|
67
|
+
server,
|
|
68
|
+
database,
|
|
69
|
+
jwt,
|
|
70
|
+
bcrypt,
|
|
71
|
+
registeredRoutes,
|
|
72
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
const env = require('./env');
|
|
3
|
+
|
|
4
|
+
const connect = async () => {
|
|
5
|
+
mongoose.connection.on('connected', () =>
|
|
6
|
+
console.log(`MongoDB connected → ${mongoose.connection.host}`)
|
|
7
|
+
);
|
|
8
|
+
mongoose.connection.on('error', (err) =>
|
|
9
|
+
console.error('MongoDB error:', err.message)
|
|
10
|
+
);
|
|
11
|
+
mongoose.connection.on('disconnected', () =>
|
|
12
|
+
console.warn('MongoDB disconnected')
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
await mongoose.connect(env.MONGODB_URI);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const disconnect = async () => mongoose.disconnect();
|
|
19
|
+
|
|
20
|
+
module.exports = { connect, disconnect };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require('dotenv').config();
|
|
2
|
+
|
|
3
|
+
const env = {
|
|
4
|
+
PORT: process.env.PORT || 3000,
|
|
5
|
+
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
6
|
+
|
|
7
|
+
MONGODB_URI: process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/auth_db',
|
|
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
|
+
|
|
14
|
+
BCRYPT_ROUNDS: parseInt(process.env.BCRYPT_ROUNDS || '12', 10),
|
|
15
|
+
|
|
16
|
+
EXCEL_OUTPUT_DIR: process.env.EXCEL_OUTPUT_DIR || './exports',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
module.exports = env;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const { verifyAccessToken } = require('../utils/token');
|
|
2
|
+
const res_ = require('../utils/response');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* authenticate — verifies the Bearer token and attaches `req.user`.
|
|
6
|
+
*/
|
|
7
|
+
const authenticate = (req, res, next) => {
|
|
8
|
+
const header = req.headers.authorization || '';
|
|
9
|
+
const token = header.startsWith('Bearer ') ? header.slice(7) : null;
|
|
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');
|
|
29
|
+
}
|
|
30
|
+
return next();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
module.exports = { authenticate, authorize };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const env = require('../config/env');
|
|
2
|
+
const res_ = require('../utils/response');
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line no-unused-vars
|
|
5
|
+
const errorHandler = (err, req, res, _next) => {
|
|
6
|
+
const statusCode = err.statusCode || 500;
|
|
7
|
+
const message = err.message || 'Internal server error';
|
|
8
|
+
|
|
9
|
+
if (env.NODE_ENV !== 'production') {
|
|
10
|
+
console.error(`[${req.method}] ${req.originalUrl} →`, err);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return res_.error(res, message, statusCode);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const notFoundHandler = (req, res) =>
|
|
17
|
+
res_.notFound(res, `Route ${req.method} ${req.originalUrl} not found`);
|
|
18
|
+
|
|
19
|
+
module.exports = { errorHandler, notFoundHandler };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// 📦 EXAMPLE CONTROLLER — Handles HTTP requests and sends responses
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// WHAT THIS FILE DOES:
|
|
6
|
+
// - Receives incoming HTTP requests (from routes)
|
|
7
|
+
// - Calls the service to do the actual work
|
|
8
|
+
// - Sends back a JSON response to the client
|
|
9
|
+
//
|
|
10
|
+
// HOW IT WORKS:
|
|
11
|
+
// 1. The route calls a controller function (e.g. create)
|
|
12
|
+
// 2. The controller reads the request data (req.body, req.params)
|
|
13
|
+
// 3. It calls the service to handle the database operation
|
|
14
|
+
// 4. It sends back a success or error response
|
|
15
|
+
//
|
|
16
|
+
// RESPONSE HELPERS (from utils/response.js):
|
|
17
|
+
// res_.success(res, data, message) → 200 OK
|
|
18
|
+
// res_.created(res, data, message) → 201 Created
|
|
19
|
+
// res_.error(res, message, statusCode) → any error
|
|
20
|
+
// res_.notFound(res, message) → 404
|
|
21
|
+
//
|
|
22
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
23
|
+
|
|
24
|
+
const ExampleService = require('./example.service');
|
|
25
|
+
const res_ = require('../../utils/response');
|
|
26
|
+
|
|
27
|
+
const ExampleController = {
|
|
28
|
+
// ─── POST /api/examples (add new) ────────────────────────────────────
|
|
29
|
+
async create(req, res) {
|
|
30
|
+
try {
|
|
31
|
+
const record = await ExampleService.create(req.body);
|
|
32
|
+
return res_.created(res, record, 'Record created');
|
|
33
|
+
} catch (err) {
|
|
34
|
+
return res_.error(res, err.message, err.statusCode || 500);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
// ─── GET /api/examples (list all) ────────────────────────────────────
|
|
39
|
+
async list(req, res) {
|
|
40
|
+
try {
|
|
41
|
+
const records = await ExampleService.list();
|
|
42
|
+
return res_.success(res, { records });
|
|
43
|
+
} catch (err) {
|
|
44
|
+
return res_.error(res, err.message, 500);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
// ─── GET /api/examples/:id (get one) ─────────────────────────────────
|
|
49
|
+
async getById(req, res) {
|
|
50
|
+
try {
|
|
51
|
+
const record = await ExampleService.getById(req.params.id);
|
|
52
|
+
if (!record) return res_.notFound(res, 'Record not found');
|
|
53
|
+
return res_.success(res, record);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
return res_.error(res, err.message, 500);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
// ─── PUT /api/examples/:id (update) ──────────────────────────────────
|
|
60
|
+
async update(req, res) {
|
|
61
|
+
try {
|
|
62
|
+
const record = await ExampleService.update(req.params.id, req.body);
|
|
63
|
+
if (!record) return res_.notFound(res, 'Record not found');
|
|
64
|
+
return res_.success(res, record, 'Record updated');
|
|
65
|
+
} catch (err) {
|
|
66
|
+
return res_.error(res, err.message, 500);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
// ─── DELETE /api/examples/:id (delete) ───────────────────────────────
|
|
71
|
+
async remove(req, res) {
|
|
72
|
+
try {
|
|
73
|
+
const record = await ExampleService.remove(req.params.id);
|
|
74
|
+
if (!record) return res_.notFound(res, 'Record not found');
|
|
75
|
+
return res_.success(res, null, 'Record deleted');
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return res_.error(res, err.message, 500);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
module.exports = ExampleController;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// 📦 EXAMPLE MODEL — Mongoose schema (database structure)
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// WHAT THIS FILE DOES:
|
|
6
|
+
// Defines the shape of your data in MongoDB.
|
|
7
|
+
// Each field = a column in the database.
|
|
8
|
+
//
|
|
9
|
+
// HOW TO USE:
|
|
10
|
+
// 1. Rename "Example" to your feature name (e.g. "Product")
|
|
11
|
+
// 2. Change the fields below to match your data
|
|
12
|
+
// 3. That's it! The model is ready to use in your service.
|
|
13
|
+
//
|
|
14
|
+
// EXAMPLE FIELDS (replace these):
|
|
15
|
+
// name: { type: String, required: true } ← text field
|
|
16
|
+
// price: { type: Number, default: 0 } ← number field
|
|
17
|
+
// category: { type: String, enum: ['a','b'] } ← dropdown/choice
|
|
18
|
+
// isActive: { type: Boolean, default: true } ← yes/no
|
|
19
|
+
// createdAt: added automatically by timestamps ← date
|
|
20
|
+
//
|
|
21
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
22
|
+
|
|
23
|
+
const mongoose = require('mongoose');
|
|
24
|
+
|
|
25
|
+
const exampleSchema = new mongoose.Schema(
|
|
26
|
+
{
|
|
27
|
+
// ── YOUR FIELDS HERE ────────────────────────────────────────────────
|
|
28
|
+
name: {
|
|
29
|
+
type: String,
|
|
30
|
+
required: true,
|
|
31
|
+
trim: true,
|
|
32
|
+
},
|
|
33
|
+
description: {
|
|
34
|
+
type: String,
|
|
35
|
+
trim: true,
|
|
36
|
+
},
|
|
37
|
+
status: {
|
|
38
|
+
type: String,
|
|
39
|
+
enum: ['active', 'inactive'],
|
|
40
|
+
default: 'active',
|
|
41
|
+
},
|
|
42
|
+
// Add more fields as needed
|
|
43
|
+
},
|
|
44
|
+
{ timestamps: true } // adds createdAt and updatedAt automatically
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
module.exports = mongoose.model('Example', exampleSchema);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// 📦 EXAMPLE ROUTES — Maps URLs to controller functions
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// WHAT THIS FILE DOES:
|
|
6
|
+
// - Connects a URL path to a controller function
|
|
7
|
+
// - Example: GET /api/examples → ExampleController.list
|
|
8
|
+
//
|
|
9
|
+
// HOW TO ADD A NEW ROUTE:
|
|
10
|
+
// router.get('/search', ExampleController.search) ← GET request
|
|
11
|
+
// router.post('/', ExampleController.create) ← POST request
|
|
12
|
+
// router.put('/:id', ExampleController.update) ← PUT request
|
|
13
|
+
// router.delete('/:id', ExampleController.remove) ← DELETE request
|
|
14
|
+
//
|
|
15
|
+
// COMMON HTTP METHODS:
|
|
16
|
+
// GET → fetch data
|
|
17
|
+
// POST → create new data
|
|
18
|
+
// PUT → update existing data
|
|
19
|
+
// DELETE → remove data
|
|
20
|
+
//
|
|
21
|
+
// AUTHENTICATION:
|
|
22
|
+
// - Add `authenticate` before any route to require login
|
|
23
|
+
// - Example: router.get('/', authenticate, ExampleController.list)
|
|
24
|
+
//
|
|
25
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
26
|
+
|
|
27
|
+
const { Router } = require('express');
|
|
28
|
+
const ExampleController = require('./example.controller');
|
|
29
|
+
const { authenticate } = require('../../middleware/auth.middleware');
|
|
30
|
+
|
|
31
|
+
const router = Router();
|
|
32
|
+
|
|
33
|
+
// ─── All routes require authentication ─────────────────────────────────────
|
|
34
|
+
router.use(authenticate);
|
|
35
|
+
|
|
36
|
+
// ─── CRUD ROUTES ────────────────────────────────────────────────────────────
|
|
37
|
+
router.post('/', ExampleController.create); // POST /api/examples
|
|
38
|
+
router.get('/', ExampleController.list); // GET /api/examples
|
|
39
|
+
router.get('/:id', ExampleController.getById); // GET /api/examples/:id
|
|
40
|
+
router.put('/:id', ExampleController.update); // PUT /api/examples/:id
|
|
41
|
+
router.delete('/:id', ExampleController.remove); // DELETE /api/examples/:id
|
|
42
|
+
|
|
43
|
+
module.exports = router;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// 📦 EXAMPLE SERVICE — Business logic (talks to database)
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// WHAT THIS FILE DOES:
|
|
6
|
+
// - Every function here does one job (create, list, get, update, delete)
|
|
7
|
+
// - The controller calls these functions
|
|
8
|
+
// - The model is used here to query MongoDB
|
|
9
|
+
//
|
|
10
|
+
// HOW TO ADD A NEW FUNCTION:
|
|
11
|
+
// Just add a new method to the object below.
|
|
12
|
+
// Example: async search(query) { ... }
|
|
13
|
+
//
|
|
14
|
+
// AVAILABLE FUNCTIONS (customize these):
|
|
15
|
+
// create(data) → adds a new record
|
|
16
|
+
// list() → gets all records
|
|
17
|
+
// getById(id) → gets one record by ID
|
|
18
|
+
// update(id, data) → updates a record
|
|
19
|
+
// remove(id) → deletes a record
|
|
20
|
+
//
|
|
21
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
22
|
+
|
|
23
|
+
const Example = require('./example.model');
|
|
24
|
+
|
|
25
|
+
const ExampleService = {
|
|
26
|
+
// ─── CREATE ───────────────────────────────────────────────────────────
|
|
27
|
+
async create(data) {
|
|
28
|
+
const record = await Example.create(data);
|
|
29
|
+
return record;
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
// ─── LIST ALL ─────────────────────────────────────────────────────────
|
|
33
|
+
async list() {
|
|
34
|
+
return Example.find().sort({ createdAt: -1 });
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
// ─── GET ONE ──────────────────────────────────────────────────────────
|
|
38
|
+
async getById(id) {
|
|
39
|
+
return Example.findById(id);
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// ─── UPDATE ───────────────────────────────────────────────────────────
|
|
43
|
+
async update(id, data) {
|
|
44
|
+
return Example.findByIdAndUpdate(id, data, { new: true });
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// ─── DELETE ───────────────────────────────────────────────────────────
|
|
48
|
+
async remove(id) {
|
|
49
|
+
return Example.findByIdAndDelete(id);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
// ─── COUNT ────────────────────────────────────────────────────────────
|
|
53
|
+
async count() {
|
|
54
|
+
return Example.countDocuments();
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
module.exports = ExampleService;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const AuthService = require('./auth.service');
|
|
2
|
+
const res_ = require('../../utils/response');
|
|
3
|
+
|
|
4
|
+
const AuthController = {
|
|
5
|
+
async signup(req, res) {
|
|
6
|
+
try {
|
|
7
|
+
const result = await AuthService.signup(req.body);
|
|
8
|
+
return res_.created(res, result, 'Account created successfully');
|
|
9
|
+
} catch (err) {
|
|
10
|
+
return res_.error(res, err.message, err.statusCode || 500);
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
async login(req, res) {
|
|
15
|
+
try {
|
|
16
|
+
const result = await AuthService.login(req.body);
|
|
17
|
+
return res_.success(res, result, 'Login successful');
|
|
18
|
+
} catch (err) {
|
|
19
|
+
return res_.error(res, err.message, err.statusCode || 500);
|
|
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');
|
|
27
|
+
} catch (err) {
|
|
28
|
+
return res_.error(res, err.message, err.statusCode || 500);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
async me(req, res) {
|
|
33
|
+
try {
|
|
34
|
+
const user = await AuthService.me(req.user.id);
|
|
35
|
+
return res_.success(res, user);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
return res_.error(res, err.message, err.statusCode || 500);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
logout(_req, res) {
|
|
42
|
+
// JWT logout is client-side. Add a token blocklist here for server-side invalidation.
|
|
43
|
+
return res_.success(res, null, 'Logged out successfully');
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
module.exports = AuthController;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const { Router } = require('express');
|
|
2
|
+
const AuthController = require('./auth.controller');
|
|
3
|
+
const { authenticate } = require('../../middleware/auth.middleware');
|
|
4
|
+
|
|
5
|
+
const router = Router();
|
|
6
|
+
|
|
7
|
+
// Public routes
|
|
8
|
+
router.post('/signup', AuthController.signup);
|
|
9
|
+
router.post('/login', AuthController.login);
|
|
10
|
+
router.post('/refresh', AuthController.refresh);
|
|
11
|
+
|
|
12
|
+
// Protected routes
|
|
13
|
+
router.get('/me', authenticate, AuthController.me);
|
|
14
|
+
router.post('/logout', authenticate, AuthController.logout);
|
|
15
|
+
|
|
16
|
+
module.exports = router;
|