create-ishvexa-app 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/index.js +62 -0
- package/package.json +14 -0
- package/templates/epms-app/.env +7 -0
- package/templates/epms-app/README.md +113 -0
- package/templates/epms-app/backend-mongodb/.env +3 -0
- package/templates/epms-app/backend-mongodb/.env.example +3 -0
- package/templates/epms-app/backend-mongodb/config/db.js +29 -0
- package/templates/epms-app/backend-mongodb/controllers/authController.js +93 -0
- package/templates/epms-app/backend-mongodb/controllers/departmentController.js +24 -0
- package/templates/epms-app/backend-mongodb/controllers/employeeController.js +39 -0
- package/templates/epms-app/backend-mongodb/controllers/reportController.js +57 -0
- package/templates/epms-app/backend-mongodb/controllers/salaryController.js +101 -0
- package/templates/epms-app/backend-mongodb/middleware/auth.js +16 -0
- package/templates/epms-app/backend-mongodb/models/Counter.js +13 -0
- package/templates/epms-app/backend-mongodb/models/Department.js +11 -0
- package/templates/epms-app/backend-mongodb/models/Employee.js +18 -0
- package/templates/epms-app/backend-mongodb/models/Salary.js +19 -0
- package/templates/epms-app/backend-mongodb/models/User.js +9 -0
- package/templates/epms-app/backend-mongodb/package-lock.json +1571 -0
- package/templates/epms-app/backend-mongodb/package.json +22 -0
- package/templates/epms-app/backend-mongodb/routes/authRoutes.js +8 -0
- package/templates/epms-app/backend-mongodb/routes/departmentRoutes.js +6 -0
- package/templates/epms-app/backend-mongodb/routes/employeeRoutes.js +6 -0
- package/templates/epms-app/backend-mongodb/routes/reportRoutes.js +5 -0
- package/templates/epms-app/backend-mongodb/routes/salaryRoutes.js +8 -0
- package/templates/epms-app/backend-mongodb/server.js +39 -0
- package/templates/epms-app/backend-mysql/.env +7 -0
- package/templates/epms-app/backend-mysql/.env.example +7 -0
- package/templates/epms-app/backend-mysql/config/db.js +33 -0
- package/templates/epms-app/backend-mysql/controllers/authController.js +98 -0
- package/templates/epms-app/backend-mysql/controllers/departmentController.js +25 -0
- package/templates/epms-app/backend-mysql/controllers/employeeController.js +39 -0
- package/templates/epms-app/backend-mysql/controllers/reportController.js +41 -0
- package/templates/epms-app/backend-mysql/controllers/salaryController.js +93 -0
- package/templates/epms-app/backend-mysql/database/schema.sql +7 -0
- package/templates/epms-app/backend-mysql/middleware/auth.js +16 -0
- package/templates/epms-app/backend-mysql/package-lock.json +1486 -0
- package/templates/epms-app/backend-mysql/package.json +23 -0
- package/templates/epms-app/backend-mysql/routes/authRoutes.js +8 -0
- package/templates/epms-app/backend-mysql/routes/departmentRoutes.js +6 -0
- package/templates/epms-app/backend-mysql/routes/employeeRoutes.js +6 -0
- package/templates/epms-app/backend-mysql/routes/reportRoutes.js +5 -0
- package/templates/epms-app/backend-mysql/routes/salaryRoutes.js +8 -0
- package/templates/epms-app/backend-mysql/server.js +39 -0
- package/templates/epms-app/frontend/README.md +16 -0
- package/templates/epms-app/frontend/eslint.config.js +21 -0
- package/templates/epms-app/frontend/index.html +12 -0
- package/templates/epms-app/frontend/package-lock.json +3033 -0
- package/templates/epms-app/frontend/package.json +23 -0
- package/templates/epms-app/frontend/public/favicon.svg +1 -0
- package/templates/epms-app/frontend/public/icons.svg +24 -0
- package/templates/epms-app/frontend/src/App.css +184 -0
- package/templates/epms-app/frontend/src/App.jsx +31 -0
- package/templates/epms-app/frontend/src/api/authApi.js +7 -0
- package/templates/epms-app/frontend/src/api/client.js +11 -0
- package/templates/epms-app/frontend/src/api/departmentApi.js +5 -0
- package/templates/epms-app/frontend/src/api/employeeApi.js +4 -0
- package/templates/epms-app/frontend/src/api/reportApi.js +4 -0
- package/templates/epms-app/frontend/src/api/salaryApi.js +6 -0
- package/templates/epms-app/frontend/src/api/sparePartsApi.js +3 -0
- package/templates/epms-app/frontend/src/api/usersApi.js +4 -0
- package/templates/epms-app/frontend/src/assets/hero.png +0 -0
- package/templates/epms-app/frontend/src/assets/react.svg +1 -0
- package/templates/epms-app/frontend/src/assets/vite.svg +1 -0
- package/templates/epms-app/frontend/src/components/AppLayout.jsx +49 -0
- package/templates/epms-app/frontend/src/context/AuthContext.jsx +41 -0
- package/templates/epms-app/frontend/src/hooks/.gitkeep +0 -0
- package/templates/epms-app/frontend/src/index.css +2 -0
- package/templates/epms-app/frontend/src/main.jsx +16 -0
- package/templates/epms-app/frontend/src/pages/DepartmentPage.jsx +165 -0
- package/templates/epms-app/frontend/src/pages/DepartmentsPage.jsx +119 -0
- package/templates/epms-app/frontend/src/pages/EmployeePage.jsx +212 -0
- package/templates/epms-app/frontend/src/pages/EmployeesPage.jsx +217 -0
- package/templates/epms-app/frontend/src/pages/ForgotPassword.jsx +103 -0
- package/templates/epms-app/frontend/src/pages/LoginPage.jsx +105 -0
- package/templates/epms-app/frontend/src/pages/RegisterPage.jsx +84 -0
- package/templates/epms-app/frontend/src/pages/ReportsPage.jsx +192 -0
- package/templates/epms-app/frontend/src/pages/ResetPasswordPage.jsx +83 -0
- package/templates/epms-app/frontend/src/pages/SalariesPage.jsx +274 -0
- package/templates/epms-app/frontend/src/pages/SalaryPage.jsx +254 -0
- package/templates/epms-app/frontend/vite.config.js +8 -0
- package/templates/lms-app/.env +9 -0
- package/templates/lms-app/README.md +89 -0
- package/templates/lms-app/backend-mongodb/.env +5 -0
- package/templates/lms-app/backend-mongodb/.env.example +5 -0
- package/templates/lms-app/backend-mongodb/package-lock.json +1583 -0
- package/templates/lms-app/backend-mongodb/package.json +26 -0
- package/templates/lms-app/backend-mongodb/src/config/db.js +10 -0
- package/templates/lms-app/backend-mongodb/src/config/env.js +28 -0
- package/templates/lms-app/backend-mongodb/src/controllers/authController.js +86 -0
- package/templates/lms-app/backend-mongodb/src/controllers/bookController.js +101 -0
- package/templates/lms-app/backend-mongodb/src/controllers/borrowController.js +106 -0
- package/templates/lms-app/backend-mongodb/src/controllers/dashboardController.js +40 -0
- package/templates/lms-app/backend-mongodb/src/controllers/reportController.js +47 -0
- package/templates/lms-app/backend-mongodb/src/controllers/studentController.js +92 -0
- package/templates/lms-app/backend-mongodb/src/ensureSeedData.js +72 -0
- package/templates/lms-app/backend-mongodb/src/middleware/auth.js +29 -0
- package/templates/lms-app/backend-mongodb/src/models/Book.js +14 -0
- package/templates/lms-app/backend-mongodb/src/models/Borrow.js +16 -0
- package/templates/lms-app/backend-mongodb/src/models/Student.js +14 -0
- package/templates/lms-app/backend-mongodb/src/models/User.js +13 -0
- package/templates/lms-app/backend-mongodb/src/routes/authRoutes.js +12 -0
- package/templates/lms-app/backend-mongodb/src/routes/bookRoutes.js +13 -0
- package/templates/lms-app/backend-mongodb/src/routes/borrowRoutes.js +11 -0
- package/templates/lms-app/backend-mongodb/src/routes/dashboardRoutes.js +9 -0
- package/templates/lms-app/backend-mongodb/src/routes/reportRoutes.js +12 -0
- package/templates/lms-app/backend-mongodb/src/routes/studentRoutes.js +13 -0
- package/templates/lms-app/backend-mongodb/src/seed.js +16 -0
- package/templates/lms-app/backend-mongodb/src/server.js +66 -0
- package/templates/lms-app/backend-mysql/.env +9 -0
- package/templates/lms-app/backend-mysql/.env.example +9 -0
- package/templates/lms-app/backend-mysql/database/schema.sql +45 -0
- package/templates/lms-app/backend-mysql/package-lock.json +1462 -0
- package/templates/lms-app/backend-mysql/package.json +23 -0
- package/templates/lms-app/backend-mysql/src/config/db.js +33 -0
- package/templates/lms-app/backend-mysql/src/config/env.js +21 -0
- package/templates/lms-app/backend-mysql/src/controllers/authController.js +87 -0
- package/templates/lms-app/backend-mysql/src/controllers/bookController.js +106 -0
- package/templates/lms-app/backend-mysql/src/controllers/borrowController.js +113 -0
- package/templates/lms-app/backend-mysql/src/controllers/dashboardController.js +33 -0
- package/templates/lms-app/backend-mysql/src/controllers/reportController.js +40 -0
- package/templates/lms-app/backend-mysql/src/controllers/studentController.js +95 -0
- package/templates/lms-app/backend-mysql/src/ensureSeedData.js +54 -0
- package/templates/lms-app/backend-mysql/src/middleware/auth.js +28 -0
- package/templates/lms-app/backend-mysql/src/routes/authRoutes.js +12 -0
- package/templates/lms-app/backend-mysql/src/routes/bookRoutes.js +13 -0
- package/templates/lms-app/backend-mysql/src/routes/borrowRoutes.js +11 -0
- package/templates/lms-app/backend-mysql/src/routes/dashboardRoutes.js +9 -0
- package/templates/lms-app/backend-mysql/src/routes/reportRoutes.js +12 -0
- package/templates/lms-app/backend-mysql/src/routes/studentRoutes.js +13 -0
- package/templates/lms-app/backend-mysql/src/server.js +69 -0
- package/templates/lms-app/backend-mysql/src/utils/mappers.js +73 -0
- package/templates/lms-app/frontend/.env.example +5 -0
- package/templates/lms-app/frontend/index.html +13 -0
- package/templates/lms-app/frontend/package-lock.json +1592 -0
- package/templates/lms-app/frontend/package.json +23 -0
- package/templates/lms-app/frontend/public/favicon.svg +4 -0
- package/templates/lms-app/frontend/src/App.jsx +107 -0
- package/templates/lms-app/frontend/src/api/authApi.js +5 -0
- package/templates/lms-app/frontend/src/api/booksApi.js +6 -0
- package/templates/lms-app/frontend/src/api/borrowsApi.js +5 -0
- package/templates/lms-app/frontend/src/api/client.js +8 -0
- package/templates/lms-app/frontend/src/api/dashboardApi.js +3 -0
- package/templates/lms-app/frontend/src/api/reportsApi.js +6 -0
- package/templates/lms-app/frontend/src/api/studentsApi.js +6 -0
- package/templates/lms-app/frontend/src/components/AppLayout.jsx +63 -0
- package/templates/lms-app/frontend/src/index.css +34 -0
- package/templates/lms-app/frontend/src/main.jsx +13 -0
- package/templates/lms-app/frontend/src/pages/BooksPage.jsx +206 -0
- package/templates/lms-app/frontend/src/pages/BorrowPage.jsx +134 -0
- package/templates/lms-app/frontend/src/pages/DashboardPage.jsx +42 -0
- package/templates/lms-app/frontend/src/pages/ForgotPassword.jsx +112 -0
- package/templates/lms-app/frontend/src/pages/LoginPage.jsx +71 -0
- package/templates/lms-app/frontend/src/pages/ReportsPage.jsx +176 -0
- package/templates/lms-app/frontend/src/pages/ReturnPage.jsx +75 -0
- package/templates/lms-app/frontend/src/pages/SearchPage.jsx +156 -0
- package/templates/lms-app/frontend/src/pages/StudentsPage.jsx +204 -0
- package/templates/lms-app/frontend/vite.config.js +26 -0
- package/templates/scms-app/.env +7 -0
- package/templates/scms-app/README.md +80 -0
- package/templates/scms-app/backend-mongodb/.env +3 -0
- package/templates/scms-app/backend-mongodb/.env.example +3 -0
- package/templates/scms-app/backend-mongodb/config/db.js +29 -0
- package/templates/scms-app/backend-mongodb/controllers/authController.js +93 -0
- package/templates/scms-app/backend-mongodb/controllers/deliveryController.js +65 -0
- package/templates/scms-app/backend-mongodb/controllers/reportController.js +51 -0
- package/templates/scms-app/backend-mongodb/controllers/shipmentController.js +65 -0
- package/templates/scms-app/backend-mongodb/controllers/supplierController.js +27 -0
- package/templates/scms-app/backend-mongodb/middleware/auth.js +16 -0
- package/templates/scms-app/backend-mongodb/models/Delivery.js +14 -0
- package/templates/scms-app/backend-mongodb/models/Shipment.js +14 -0
- package/templates/scms-app/backend-mongodb/models/Supplier.js +14 -0
- package/templates/scms-app/backend-mongodb/models/User.js +9 -0
- package/templates/scms-app/backend-mongodb/package-lock.json +1571 -0
- package/templates/scms-app/backend-mongodb/package.json +22 -0
- package/templates/scms-app/backend-mongodb/routes/authRoutes.js +8 -0
- package/templates/scms-app/backend-mongodb/routes/deliveryRoutes.js +8 -0
- package/templates/scms-app/backend-mongodb/routes/reportRoutes.js +5 -0
- package/templates/scms-app/backend-mongodb/routes/shipmentRoutes.js +8 -0
- package/templates/scms-app/backend-mongodb/routes/supplierRoutes.js +6 -0
- package/templates/scms-app/backend-mongodb/server.js +39 -0
- package/templates/scms-app/backend-mysql/.env +7 -0
- package/templates/scms-app/backend-mysql/.env.example +7 -0
- package/templates/scms-app/backend-mysql/config/db.js +33 -0
- package/templates/scms-app/backend-mysql/controllers/authController.js +98 -0
- package/templates/scms-app/backend-mysql/controllers/deliveryController.js +62 -0
- package/templates/scms-app/backend-mysql/controllers/reportController.js +39 -0
- package/templates/scms-app/backend-mysql/controllers/shipmentController.js +62 -0
- package/templates/scms-app/backend-mysql/controllers/supplierController.js +28 -0
- package/templates/scms-app/backend-mysql/database/schema.sql +7 -0
- package/templates/scms-app/backend-mysql/middleware/auth.js +16 -0
- package/templates/scms-app/backend-mysql/package-lock.json +1486 -0
- package/templates/scms-app/backend-mysql/package.json +23 -0
- package/templates/scms-app/backend-mysql/routes/authRoutes.js +8 -0
- package/templates/scms-app/backend-mysql/routes/deliveryRoutes.js +8 -0
- package/templates/scms-app/backend-mysql/routes/reportRoutes.js +5 -0
- package/templates/scms-app/backend-mysql/routes/shipmentRoutes.js +8 -0
- package/templates/scms-app/backend-mysql/routes/supplierRoutes.js +6 -0
- package/templates/scms-app/backend-mysql/server.js +39 -0
- package/templates/scms-app/frontend/index.html +12 -0
- package/templates/scms-app/frontend/package-lock.json +1634 -0
- package/templates/scms-app/frontend/package.json +23 -0
- package/templates/scms-app/frontend/src/App.jsx +31 -0
- package/templates/scms-app/frontend/src/api/client.js +11 -0
- package/templates/scms-app/frontend/src/components/AppLayout.jsx +49 -0
- package/templates/scms-app/frontend/src/context/AuthContext.jsx +41 -0
- package/templates/scms-app/frontend/src/hooks/.gitkeep +0 -0
- package/templates/scms-app/frontend/src/index.css +2 -0
- package/templates/scms-app/frontend/src/main.jsx +16 -0
- package/templates/scms-app/frontend/src/pages/DeliveriesPage.jsx +265 -0
- package/templates/scms-app/frontend/src/pages/ForgotPassword.jsx +103 -0
- package/templates/scms-app/frontend/src/pages/LoginPage.jsx +105 -0
- package/templates/scms-app/frontend/src/pages/ReportsPage.jsx +192 -0
- package/templates/scms-app/frontend/src/pages/ShipmentsPage.jsx +259 -0
- package/templates/scms-app/frontend/src/pages/SuppliersPage.jsx +168 -0
- package/templates/scms-app/frontend/vite.config.js +8 -0
- package/templates/sfms-app/.env +7 -0
- package/templates/sfms-app/README.md +72 -0
- package/templates/sfms-app/backend-mongodb/.env +3 -0
- package/templates/sfms-app/backend-mongodb/.env.example +3 -0
- package/templates/sfms-app/backend-mongodb/package-lock.json +1580 -0
- package/templates/sfms-app/backend-mongodb/package.json +23 -0
- package/templates/sfms-app/backend-mongodb/src/config/database.js +7 -0
- package/templates/sfms-app/backend-mongodb/src/config/env.js +35 -0
- package/templates/sfms-app/backend-mongodb/src/middleware/authMiddleware.js +32 -0
- package/templates/sfms-app/backend-mongodb/src/models/Payment.js +12 -0
- package/templates/sfms-app/backend-mongodb/src/models/Student.js +12 -0
- package/templates/sfms-app/backend-mongodb/src/models/User.js +14 -0
- package/templates/sfms-app/backend-mongodb/src/routes/authRoutes.js +140 -0
- package/templates/sfms-app/backend-mongodb/src/routes/paymentRoutes.js +117 -0
- package/templates/sfms-app/backend-mongodb/src/routes/reportRoutes.js +59 -0
- package/templates/sfms-app/backend-mongodb/src/routes/studentRoutes.js +79 -0
- package/templates/sfms-app/backend-mongodb/src/server.js +34 -0
- package/templates/sfms-app/backend-mysql/.env +7 -0
- package/templates/sfms-app/backend-mysql/.env.example +7 -0
- package/templates/sfms-app/backend-mysql/database/schema.sql +29 -0
- package/templates/sfms-app/backend-mysql/package-lock.json +1467 -0
- package/templates/sfms-app/backend-mysql/package.json +24 -0
- package/templates/sfms-app/backend-mysql/src/config/.gitkeep +0 -0
- package/templates/sfms-app/backend-mysql/src/config/db.js +31 -0
- package/templates/sfms-app/backend-mysql/src/config/env.js +20 -0
- package/templates/sfms-app/backend-mysql/src/middleware/.gitkeep +0 -0
- package/templates/sfms-app/backend-mysql/src/middleware/authMiddleware.js +26 -0
- package/templates/sfms-app/backend-mysql/src/models/.gitkeep +0 -0
- package/templates/sfms-app/backend-mysql/src/routes/.gitkeep +0 -0
- package/templates/sfms-app/backend-mysql/src/routes/authRoutes.js +131 -0
- package/templates/sfms-app/backend-mysql/src/routes/paymentRoutes.js +92 -0
- package/templates/sfms-app/backend-mysql/src/routes/reportRoutes.js +41 -0
- package/templates/sfms-app/backend-mysql/src/routes/studentRoutes.js +75 -0
- package/templates/sfms-app/backend-mysql/src/server.js +39 -0
- package/templates/sfms-app/backend-mysql/src/utils/mappers.js +43 -0
- package/templates/sfms-app/frontend/.env.example +9 -0
- package/templates/sfms-app/frontend/index.html +19 -0
- package/templates/sfms-app/frontend/package-lock.json +2667 -0
- package/templates/sfms-app/frontend/package.json +23 -0
- package/templates/sfms-app/frontend/postcss.config.js +6 -0
- package/templates/sfms-app/frontend/public/favicon.svg +4 -0
- package/templates/sfms-app/frontend/src/App.jsx +38 -0
- package/templates/sfms-app/frontend/src/api/apiClient.js +54 -0
- package/templates/sfms-app/frontend/src/components/AppLayout.jsx +61 -0
- package/templates/sfms-app/frontend/src/context/AuthContext.jsx +87 -0
- package/templates/sfms-app/frontend/src/index.css +7 -0
- package/templates/sfms-app/frontend/src/main.jsx +16 -0
- package/templates/sfms-app/frontend/src/pages/DashboardPage.jsx +78 -0
- package/templates/sfms-app/frontend/src/pages/ForgotPassword.jsx +114 -0
- package/templates/sfms-app/frontend/src/pages/LoginPage.jsx +141 -0
- package/templates/sfms-app/frontend/src/pages/PaymentsPage.jsx +309 -0
- package/templates/sfms-app/frontend/src/pages/ReportsPage.jsx +123 -0
- package/templates/sfms-app/frontend/src/pages/StudentsPage.jsx +281 -0
- package/templates/sfms-app/frontend/tailwind.config.js +21 -0
- package/templates/sfms-app/frontend/vite.config.js +61 -0
- package/templates/sims-app/README.md +138 -0
- package/templates/sims-app/backend/.env +4 -0
- package/templates/sims-app/backend/.env.example +4 -0
- package/templates/sims-app/backend/package.json +22 -0
- package/templates/sims-app/backend/src/config/db.js +9 -0
- package/templates/sims-app/backend/src/controllers/authController.js +115 -0
- package/templates/sims-app/backend/src/controllers/simsReportController.js +94 -0
- package/templates/sims-app/backend/src/controllers/sparePartController.js +41 -0
- package/templates/sims-app/backend/src/controllers/stockInController.js +45 -0
- package/templates/sims-app/backend/src/controllers/stockOutController.js +123 -0
- package/templates/sims-app/backend/src/middleware/auth.js +8 -0
- package/templates/sims-app/backend/src/models/SparePart.js +17 -0
- package/templates/sims-app/backend/src/models/StockIn.js +16 -0
- package/templates/sims-app/backend/src/models/StockOut.js +18 -0
- package/templates/sims-app/backend/src/models/User.js +12 -0
- package/templates/sims-app/backend/src/routes/authRoutes.js +12 -0
- package/templates/sims-app/backend/src/routes/simsReportRoutes.js +8 -0
- package/templates/sims-app/backend/src/routes/sparePartRoutes.js +8 -0
- package/templates/sims-app/backend/src/routes/stockInRoutes.js +8 -0
- package/templates/sims-app/backend/src/routes/stockOutRoutes.js +10 -0
- package/templates/sims-app/backend/src/server.js +62 -0
- package/templates/sims-app/backend/src/utils/passwordPolicy.js +10 -0
- package/templates/sims-app/backend/src/utils/sparePartHelpers.js +5 -0
- package/templates/sims-app/frontend/index.html +13 -0
- package/templates/sims-app/frontend/package.json +31 -0
- package/templates/sims-app/frontend/src/App.jsx +110 -0
- package/templates/sims-app/frontend/src/api/authApi.js +7 -0
- package/templates/sims-app/frontend/src/api/client.js +8 -0
- package/templates/sims-app/frontend/src/api/simsReportApi.js +5 -0
- package/templates/sims-app/frontend/src/api/sparePartsApi.js +4 -0
- package/templates/sims-app/frontend/src/api/stockInApi.js +4 -0
- package/templates/sims-app/frontend/src/api/stockOutApi.js +6 -0
- package/templates/sims-app/frontend/src/api/usersApi.js +3 -0
- package/templates/sims-app/frontend/src/components/AppLayout.jsx +47 -0
- package/templates/sims-app/frontend/src/index.css +7 -0
- package/templates/sims-app/frontend/src/main.jsx +13 -0
- package/templates/sims-app/frontend/src/pages/ForgotPassword.jsx +111 -0
- package/templates/sims-app/frontend/src/pages/LoginPage.jsx +71 -0
- package/templates/sims-app/frontend/src/pages/RegisterPage.jsx +99 -0
- package/templates/sims-app/frontend/src/pages/ReportsPage.jsx +120 -0
- package/templates/sims-app/frontend/src/pages/SparePartPage.jsx +148 -0
- package/templates/sims-app/frontend/src/pages/StockInPage.jsx +122 -0
- package/templates/sims-app/frontend/src/pages/StockOutPage.jsx +252 -0
- package/templates/sims-app/frontend/src/utils/passwordPolicy.js +8 -0
- package/templates/sims-app/frontend/vite.config.js +8 -0
- package/templates/smartshop-app/README.md +61 -0
- package/templates/smartshop-app/backend/.env +7 -0
- package/templates/smartshop-app/backend/.env.example +7 -0
- package/templates/smartshop-app/backend/database/schema.sql +46 -0
- package/templates/smartshop-app/backend/package-lock.json +1487 -0
- package/templates/smartshop-app/backend/package.json +26 -0
- package/templates/smartshop-app/backend/src/app.js +57 -0
- package/templates/smartshop-app/backend/src/config/db.js +52 -0
- package/templates/smartshop-app/backend/src/controllers/authController.js +98 -0
- package/templates/smartshop-app/backend/src/controllers/customerController.js +26 -0
- package/templates/smartshop-app/backend/src/controllers/productController.js +21 -0
- package/templates/smartshop-app/backend/src/controllers/reportController.js +29 -0
- package/templates/smartshop-app/backend/src/controllers/saleController.js +11 -0
- package/templates/smartshop-app/backend/src/middleware/authMiddleware.js +22 -0
- package/templates/smartshop-app/backend/src/models/authModel.js +24 -0
- package/templates/smartshop-app/backend/src/models/customerModel.js +43 -0
- package/templates/smartshop-app/backend/src/models/productModel.js +37 -0
- package/templates/smartshop-app/backend/src/models/reportModel.js +56 -0
- package/templates/smartshop-app/backend/src/models/saleModel.js +54 -0
- package/templates/smartshop-app/backend/src/routes/authRoutes.js +10 -0
- package/templates/smartshop-app/backend/src/routes/customerRoutes.js +16 -0
- package/templates/smartshop-app/backend/src/routes/productRoutes.js +16 -0
- package/templates/smartshop-app/backend/src/routes/reportRoutes.js +18 -0
- package/templates/smartshop-app/backend/src/routes/saleRoutes.js +9 -0
- package/templates/smartshop-app/backend/src/server.js +19 -0
- package/templates/smartshop-app/frontend/README.md +18 -0
- package/templates/smartshop-app/frontend/eslint.config.js +21 -0
- package/templates/smartshop-app/frontend/index.html +13 -0
- package/templates/smartshop-app/frontend/package-lock.json +3415 -0
- package/templates/smartshop-app/frontend/package.json +34 -0
- package/templates/smartshop-app/frontend/public/favicon.svg +1 -0
- package/templates/smartshop-app/frontend/public/icons.svg +24 -0
- package/templates/smartshop-app/frontend/src/App.css +184 -0
- package/templates/smartshop-app/frontend/src/App.jsx +41 -0
- package/templates/smartshop-app/frontend/src/assets/hero.png +0 -0
- package/templates/smartshop-app/frontend/src/assets/react.svg +1 -0
- package/templates/smartshop-app/frontend/src/assets/vite.svg +1 -0
- package/templates/smartshop-app/frontend/src/components/AppLayout.jsx +71 -0
- package/templates/smartshop-app/frontend/src/components/FormCard.jsx +12 -0
- package/templates/smartshop-app/frontend/src/components/StatCard.jsx +10 -0
- package/templates/smartshop-app/frontend/src/index.css +28 -0
- package/templates/smartshop-app/frontend/src/main.jsx +13 -0
- package/templates/smartshop-app/frontend/src/pages/CustomersPage.jsx +175 -0
- package/templates/smartshop-app/frontend/src/pages/DashboardPage.jsx +30 -0
- package/templates/smartshop-app/frontend/src/pages/ForgotPassword.jsx +102 -0
- package/templates/smartshop-app/frontend/src/pages/LoginPage.jsx +142 -0
- package/templates/smartshop-app/frontend/src/pages/ProductsPage.jsx +165 -0
- package/templates/smartshop-app/frontend/src/pages/ReportsPage.jsx +204 -0
- package/templates/smartshop-app/frontend/src/pages/SalesPage.jsx +153 -0
- package/templates/smartshop-app/frontend/src/services/api.js +15 -0
- package/templates/smartshop-app/frontend/vite.config.js +13 -0
- package/templates/srms-app/.env +7 -0
- package/templates/srms-app/README.md +82 -0
- package/templates/srms-app/backend-mongodb/.env +3 -0
- package/templates/srms-app/backend-mongodb/.env.example +3 -0
- package/templates/srms-app/backend-mongodb/config/db.js +29 -0
- package/templates/srms-app/backend-mongodb/controllers/authController.js +93 -0
- package/templates/srms-app/backend-mongodb/controllers/customerController.js +27 -0
- package/templates/srms-app/backend-mongodb/controllers/productController.js +26 -0
- package/templates/srms-app/backend-mongodb/controllers/reportController.js +44 -0
- package/templates/srms-app/backend-mongodb/controllers/saleController.js +72 -0
- package/templates/srms-app/backend-mongodb/middleware/auth.js +16 -0
- package/templates/srms-app/backend-mongodb/models/Customer.js +14 -0
- package/templates/srms-app/backend-mongodb/models/Product.js +13 -0
- package/templates/srms-app/backend-mongodb/models/Sale.js +15 -0
- package/templates/srms-app/backend-mongodb/models/User.js +9 -0
- package/templates/srms-app/backend-mongodb/package-lock.json +1571 -0
- package/templates/srms-app/backend-mongodb/package.json +22 -0
- package/templates/srms-app/backend-mongodb/routes/authRoutes.js +8 -0
- package/templates/srms-app/backend-mongodb/routes/customerRoutes.js +6 -0
- package/templates/srms-app/backend-mongodb/routes/productRoutes.js +6 -0
- package/templates/srms-app/backend-mongodb/routes/reportRoutes.js +5 -0
- package/templates/srms-app/backend-mongodb/routes/saleRoutes.js +8 -0
- package/templates/srms-app/backend-mongodb/server.js +39 -0
- package/templates/srms-app/backend-mysql/.env +7 -0
- package/templates/srms-app/backend-mysql/.env.example +7 -0
- package/templates/srms-app/backend-mysql/config/db.js +33 -0
- package/templates/srms-app/backend-mysql/controllers/authController.js +98 -0
- package/templates/srms-app/backend-mysql/controllers/customerController.js +28 -0
- package/templates/srms-app/backend-mysql/controllers/productController.js +27 -0
- package/templates/srms-app/backend-mysql/controllers/reportController.js +29 -0
- package/templates/srms-app/backend-mysql/controllers/saleController.js +68 -0
- package/templates/srms-app/backend-mysql/database/schema.sql +7 -0
- package/templates/srms-app/backend-mysql/middleware/auth.js +16 -0
- package/templates/srms-app/backend-mysql/package-lock.json +1486 -0
- package/templates/srms-app/backend-mysql/package.json +23 -0
- package/templates/srms-app/backend-mysql/routes/authRoutes.js +8 -0
- package/templates/srms-app/backend-mysql/routes/customerRoutes.js +6 -0
- package/templates/srms-app/backend-mysql/routes/productRoutes.js +6 -0
- package/templates/srms-app/backend-mysql/routes/reportRoutes.js +5 -0
- package/templates/srms-app/backend-mysql/routes/saleRoutes.js +8 -0
- package/templates/srms-app/backend-mysql/server.js +39 -0
- package/templates/srms-app/frontend/index.html +12 -0
- package/templates/srms-app/frontend/package-lock.json +1634 -0
- package/templates/srms-app/frontend/package.json +23 -0
- package/templates/srms-app/frontend/src/App.jsx +31 -0
- package/templates/srms-app/frontend/src/api/client.js +11 -0
- package/templates/srms-app/frontend/src/components/AppLayout.jsx +40 -0
- package/templates/srms-app/frontend/src/context/AuthContext.jsx +41 -0
- package/templates/srms-app/frontend/src/hooks/.gitkeep +0 -0
- package/templates/srms-app/frontend/src/index.css +2 -0
- package/templates/srms-app/frontend/src/main.jsx +16 -0
- package/templates/srms-app/frontend/src/pages/CustomersPage.jsx +160 -0
- package/templates/srms-app/frontend/src/pages/ForgotPassword.jsx +103 -0
- package/templates/srms-app/frontend/src/pages/LoginPage.jsx +105 -0
- package/templates/srms-app/frontend/src/pages/ProductsPage.jsx +158 -0
- package/templates/srms-app/frontend/src/pages/ReportsPage.jsx +192 -0
- package/templates/srms-app/frontend/src/pages/SalesPage.jsx +310 -0
- package/templates/srms-app/frontend/vite.config.js +8 -0
- package/templates/stockhub-sms-app/.env +7 -0
- package/templates/stockhub-sms-app/README.md +82 -0
- package/templates/stockhub-sms-app/backend-mongodb/.env +3 -0
- package/templates/stockhub-sms-app/backend-mongodb/.env.example +3 -0
- package/templates/stockhub-sms-app/backend-mongodb/config/db.js +29 -0
- package/templates/stockhub-sms-app/backend-mongodb/controllers/authController.js +93 -0
- package/templates/stockhub-sms-app/backend-mongodb/controllers/productController.js +29 -0
- package/templates/stockhub-sms-app/backend-mongodb/controllers/reportController.js +53 -0
- package/templates/stockhub-sms-app/backend-mongodb/controllers/transactionController.js +74 -0
- package/templates/stockhub-sms-app/backend-mongodb/controllers/warehouseController.js +25 -0
- package/templates/stockhub-sms-app/backend-mongodb/middleware/auth.js +16 -0
- package/templates/stockhub-sms-app/backend-mongodb/models/Counter.js +13 -0
- package/templates/stockhub-sms-app/backend-mongodb/models/Product.js +16 -0
- package/templates/stockhub-sms-app/backend-mongodb/models/StockTransaction.js +19 -0
- package/templates/stockhub-sms-app/backend-mongodb/models/User.js +9 -0
- package/templates/stockhub-sms-app/backend-mongodb/models/Warehouse.js +12 -0
- package/templates/stockhub-sms-app/backend-mongodb/package-lock.json +1571 -0
- package/templates/stockhub-sms-app/backend-mongodb/package.json +22 -0
- package/templates/stockhub-sms-app/backend-mongodb/routes/authRoutes.js +8 -0
- package/templates/stockhub-sms-app/backend-mongodb/routes/productRoutes.js +6 -0
- package/templates/stockhub-sms-app/backend-mongodb/routes/reportRoutes.js +5 -0
- package/templates/stockhub-sms-app/backend-mongodb/routes/transactionRoutes.js +8 -0
- package/templates/stockhub-sms-app/backend-mongodb/routes/warehouseRoutes.js +6 -0
- package/templates/stockhub-sms-app/backend-mongodb/server.js +39 -0
- package/templates/stockhub-sms-app/backend-mysql/.env +7 -0
- package/templates/stockhub-sms-app/backend-mysql/.env.example +7 -0
- package/templates/stockhub-sms-app/backend-mysql/config/db.js +33 -0
- package/templates/stockhub-sms-app/backend-mysql/controllers/authController.js +98 -0
- package/templates/stockhub-sms-app/backend-mysql/controllers/productController.js +30 -0
- package/templates/stockhub-sms-app/backend-mysql/controllers/reportController.js +53 -0
- package/templates/stockhub-sms-app/backend-mysql/controllers/transactionController.js +70 -0
- package/templates/stockhub-sms-app/backend-mysql/controllers/warehouseController.js +26 -0
- package/templates/stockhub-sms-app/backend-mysql/database/schema.sql +40 -0
- package/templates/stockhub-sms-app/backend-mysql/middleware/auth.js +16 -0
- package/templates/stockhub-sms-app/backend-mysql/package-lock.json +1486 -0
- package/templates/stockhub-sms-app/backend-mysql/package.json +23 -0
- package/templates/stockhub-sms-app/backend-mysql/routes/authRoutes.js +8 -0
- package/templates/stockhub-sms-app/backend-mysql/routes/productRoutes.js +6 -0
- package/templates/stockhub-sms-app/backend-mysql/routes/reportRoutes.js +5 -0
- package/templates/stockhub-sms-app/backend-mysql/routes/transactionRoutes.js +8 -0
- package/templates/stockhub-sms-app/backend-mysql/routes/warehouseRoutes.js +6 -0
- package/templates/stockhub-sms-app/backend-mysql/server.js +39 -0
- package/templates/stockhub-sms-app/frontend/index.html +12 -0
- package/templates/stockhub-sms-app/frontend/package-lock.json +1634 -0
- package/templates/stockhub-sms-app/frontend/package.json +23 -0
- package/templates/stockhub-sms-app/frontend/src/App.jsx +31 -0
- package/templates/stockhub-sms-app/frontend/src/api/client.js +11 -0
- package/templates/stockhub-sms-app/frontend/src/components/AppLayout.jsx +40 -0
- package/templates/stockhub-sms-app/frontend/src/context/AuthContext.jsx +41 -0
- package/templates/stockhub-sms-app/frontend/src/hooks/.gitkeep +0 -0
- package/templates/stockhub-sms-app/frontend/src/index.css +13 -0
- package/templates/stockhub-sms-app/frontend/src/main.jsx +16 -0
- package/templates/stockhub-sms-app/frontend/src/pages/ForgotPassword.jsx +103 -0
- package/templates/stockhub-sms-app/frontend/src/pages/LoginPage.jsx +105 -0
- package/templates/stockhub-sms-app/frontend/src/pages/ProductsPage.jsx +132 -0
- package/templates/stockhub-sms-app/frontend/src/pages/ReportsPage.jsx +177 -0
- package/templates/stockhub-sms-app/frontend/src/pages/TransactionsPage.jsx +270 -0
- package/templates/stockhub-sms-app/frontend/src/pages/WarehousesPage.jsx +116 -0
- package/templates/stockhub-sms-app/frontend/vite.config.js +8 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lms-backend-mysql",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Library Management System API — Express + MySQL",
|
|
5
|
+
"main": "src/server.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "nodemon src/server.js",
|
|
8
|
+
"start": "node src/server.js",
|
|
9
|
+
"db:init": "bash -c 'set -a && source .env && set +a && if [ -n \"$DB_PASSWORD\" ]; then mysql -h \"$DB_HOST\" -P \"$DB_PORT\" -u \"$DB_USER\" -p\"$DB_PASSWORD\" < database/schema.sql; else mysql -h \"$DB_HOST\" -P \"$DB_PORT\" -u \"$DB_USER\" < database/schema.sql; fi'"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"bcryptjs": "^3.0.3",
|
|
14
|
+
"cors": "^2.8.6",
|
|
15
|
+
"dotenv": "^17.4.2",
|
|
16
|
+
"express": "^5.2.1",
|
|
17
|
+
"express-session": "^1.19.0",
|
|
18
|
+
"mysql2": "^3.14.1"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"nodemon": "^3.1.14"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const mysql = require("mysql2/promise");
|
|
2
|
+
const dotenv = require("dotenv");
|
|
3
|
+
|
|
4
|
+
dotenv.config();
|
|
5
|
+
|
|
6
|
+
const pool = mysql.createPool({
|
|
7
|
+
host: process.env.DB_HOST || "localhost",
|
|
8
|
+
user: process.env.DB_USER || "student",
|
|
9
|
+
password: process.env.DB_PASSWORD || "",
|
|
10
|
+
database: process.env.DB_NAME || "LMS",
|
|
11
|
+
port: Number(process.env.DB_PORT || 3306),
|
|
12
|
+
waitForConnections: true,
|
|
13
|
+
connectionLimit: 10,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const query = async (sql, params = []) => {
|
|
17
|
+
const [rows] = await pool.execute(sql, params);
|
|
18
|
+
return rows;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
async function connectDatabase() {
|
|
22
|
+
try {
|
|
23
|
+
await query("SELECT 1");
|
|
24
|
+
console.log("Database Connected Successfully");
|
|
25
|
+
return true;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.log("Database Connection Failed");
|
|
28
|
+
console.error(error.message);
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = { query, connectDatabase, pool };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
function parsePort(raw, fallback) {
|
|
2
|
+
const n = Number(String(raw || "").trim());
|
|
3
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function getEnv() {
|
|
7
|
+
return {
|
|
8
|
+
port: parsePort(process.env.PORT || process.env.LMS_BACKEND_PORT, 5827),
|
|
9
|
+
sessionSecret:
|
|
10
|
+
process.env.SESSION_SECRET?.trim() ||
|
|
11
|
+
process.env.LMS_SESSION_SECRET?.trim() ||
|
|
12
|
+
"lms-dev-session-secret-change-in-production",
|
|
13
|
+
frontendUrl:
|
|
14
|
+
process.env.FRONTEND_URL?.trim() ||
|
|
15
|
+
process.env.LMS_FRONTEND_URL?.trim() ||
|
|
16
|
+
"http://localhost:5828",
|
|
17
|
+
nodeEnv: process.env.NODE_ENV || "development",
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { getEnv };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const bcrypt = require("bcryptjs");
|
|
2
|
+
const { query } = require("../config/db");
|
|
3
|
+
|
|
4
|
+
const normalize = (value) => String(value || "").trim().toLowerCase();
|
|
5
|
+
const normalizeEmail = (email) => String(email || "").trim().toLowerCase();
|
|
6
|
+
|
|
7
|
+
const login = async (req, res) => {
|
|
8
|
+
try {
|
|
9
|
+
const username = normalize(req.body.username);
|
|
10
|
+
const password = String(req.body.password || "");
|
|
11
|
+
if (!username || !password) {
|
|
12
|
+
return res.status(400).json({ message: "Username and password are required" });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const rows = await query("SELECT * FROM users WHERE username = ?", [username]);
|
|
16
|
+
const user = rows[0];
|
|
17
|
+
if (!user) {
|
|
18
|
+
return res.status(401).json({ message: "Invalid credentials" });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ok = await bcrypt.compare(password, user.password_hash);
|
|
22
|
+
if (!ok) {
|
|
23
|
+
return res.status(401).json({ message: "Invalid credentials" });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
req.session.userId = String(user.id);
|
|
27
|
+
req.session.username = user.username;
|
|
28
|
+
req.session.role = user.role;
|
|
29
|
+
return res.json({
|
|
30
|
+
message: "Login successful",
|
|
31
|
+
username: user.username,
|
|
32
|
+
role: user.role,
|
|
33
|
+
});
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return res.status(500).json({ message: error.message });
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const forgotPassword = async (req, res) => {
|
|
40
|
+
try {
|
|
41
|
+
const { email, newPassword, confirmPassword } = req.body;
|
|
42
|
+
if (!email) {
|
|
43
|
+
return res.status(400).json({ success: false, message: "Email is required." });
|
|
44
|
+
}
|
|
45
|
+
if (!newPassword) {
|
|
46
|
+
return res.status(400).json({ success: false, message: "New password is required." });
|
|
47
|
+
}
|
|
48
|
+
if (!confirmPassword) {
|
|
49
|
+
return res.status(400).json({ success: false, message: "Confirm password is required." });
|
|
50
|
+
}
|
|
51
|
+
if (newPassword !== confirmPassword) {
|
|
52
|
+
return res.status(400).json({
|
|
53
|
+
success: false,
|
|
54
|
+
message: "New password and confirm password must match.",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const emailNorm = normalizeEmail(email);
|
|
58
|
+
const rows = await query("SELECT id FROM users WHERE email = ?", [emailNorm]);
|
|
59
|
+
if (!rows.length) {
|
|
60
|
+
return res.status(404).json({ success: false, message: "User not found" });
|
|
61
|
+
}
|
|
62
|
+
const hash = await bcrypt.hash(newPassword, 10);
|
|
63
|
+
await query("UPDATE users SET password_hash = ? WHERE email = ?", [hash, emailNorm]);
|
|
64
|
+
return res.json({ success: true, message: "Password reset successfully" });
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return res.status(500).json({ success: false, message: error.message });
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const me = (req, res) => {
|
|
71
|
+
if (!req.session.userId) {
|
|
72
|
+
return res.status(401).json({ message: "Unauthorized" });
|
|
73
|
+
}
|
|
74
|
+
return res.json({
|
|
75
|
+
username: req.session.username,
|
|
76
|
+
role: req.session.role,
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const logout = (req, res) => {
|
|
81
|
+
req.session.destroy(() => {
|
|
82
|
+
res.clearCookie("connect.sid");
|
|
83
|
+
res.json({ message: "Logged out" });
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
module.exports = { login, forgotPassword, me, logout };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const { query } = require("../config/db");
|
|
2
|
+
const { mapBook } = require("../utils/mappers");
|
|
3
|
+
|
|
4
|
+
const listBooks = async (req, res) => {
|
|
5
|
+
try {
|
|
6
|
+
const q = String(req.query.title || "").trim();
|
|
7
|
+
const rows = q
|
|
8
|
+
? await query("SELECT * FROM books WHERE title LIKE ? ORDER BY title", [`%${q}%`])
|
|
9
|
+
: await query("SELECT * FROM books ORDER BY title");
|
|
10
|
+
return res.json(rows.map(mapBook));
|
|
11
|
+
} catch (error) {
|
|
12
|
+
return res.status(500).json({ message: error.message });
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const getBook = async (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const rows = await query("SELECT * FROM books WHERE id = ?", [req.params.id]);
|
|
19
|
+
if (!rows.length) return res.status(404).json({ message: "Book not found" });
|
|
20
|
+
return res.json(mapBook(rows[0]));
|
|
21
|
+
} catch (error) {
|
|
22
|
+
return res.status(500).json({ message: error.message });
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const createBook = async (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const { title, author, category, quantity, publishedYear } = req.body;
|
|
29
|
+
if (!title || !author || !category || quantity === undefined || !publishedYear) {
|
|
30
|
+
return res.status(400).json({ message: "All fields are required" });
|
|
31
|
+
}
|
|
32
|
+
const qty = Number(quantity);
|
|
33
|
+
if (!Number.isFinite(qty) || qty < 0) {
|
|
34
|
+
return res.status(400).json({ message: "Quantity must be a non-negative number" });
|
|
35
|
+
}
|
|
36
|
+
const year = Number(publishedYear);
|
|
37
|
+
if (!Number.isFinite(year) || year < 1000 || year > 9999) {
|
|
38
|
+
return res.status(400).json({ message: "Invalid published year" });
|
|
39
|
+
}
|
|
40
|
+
const result = await query(
|
|
41
|
+
"INSERT INTO books (title, author, category, quantity, published_year) VALUES (?, ?, ?, ?, ?)",
|
|
42
|
+
[String(title).trim(), String(author).trim(), String(category).trim(), qty, year]
|
|
43
|
+
);
|
|
44
|
+
const rows = await query("SELECT * FROM books WHERE id = ?", [result.insertId]);
|
|
45
|
+
return res.status(201).json(mapBook(rows[0]));
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return res.status(500).json({ message: error.message });
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const updateBook = async (req, res) => {
|
|
52
|
+
try {
|
|
53
|
+
const rows = await query("SELECT * FROM books WHERE id = ?", [req.params.id]);
|
|
54
|
+
if (!rows.length) return res.status(404).json({ message: "Book not found" });
|
|
55
|
+
|
|
56
|
+
const { title, author, category, quantity, publishedYear } = req.body;
|
|
57
|
+
const current = rows[0];
|
|
58
|
+
let qty = current.quantity;
|
|
59
|
+
let year = current.published_year;
|
|
60
|
+
|
|
61
|
+
if (quantity !== undefined) {
|
|
62
|
+
qty = Number(quantity);
|
|
63
|
+
if (!Number.isFinite(qty) || qty < 0) {
|
|
64
|
+
return res.status(400).json({ message: "Invalid quantity" });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (publishedYear !== undefined) {
|
|
68
|
+
year = Number(publishedYear);
|
|
69
|
+
if (!Number.isFinite(year) || year < 1000 || year > 9999) {
|
|
70
|
+
return res.status(400).json({ message: "Invalid published year" });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await query(
|
|
75
|
+
"UPDATE books SET title = ?, author = ?, category = ?, quantity = ?, published_year = ? WHERE id = ?",
|
|
76
|
+
[
|
|
77
|
+
title !== undefined ? String(title).trim() : current.title,
|
|
78
|
+
author !== undefined ? String(author).trim() : current.author,
|
|
79
|
+
category !== undefined ? String(category).trim() : current.category,
|
|
80
|
+
qty,
|
|
81
|
+
year,
|
|
82
|
+
req.params.id,
|
|
83
|
+
]
|
|
84
|
+
);
|
|
85
|
+
const updated = await query("SELECT * FROM books WHERE id = ?", [req.params.id]);
|
|
86
|
+
return res.json(mapBook(updated[0]));
|
|
87
|
+
} catch (error) {
|
|
88
|
+
return res.status(500).json({ message: error.message });
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const deleteBook = async (req, res) => {
|
|
93
|
+
try {
|
|
94
|
+
const active = await query("SELECT id FROM borrows WHERE book_id = ? AND returned_at IS NULL LIMIT 1", [req.params.id]);
|
|
95
|
+
if (active.length) {
|
|
96
|
+
return res.status(409).json({ message: "Book has active borrows" });
|
|
97
|
+
}
|
|
98
|
+
const result = await query("DELETE FROM books WHERE id = ?", [req.params.id]);
|
|
99
|
+
if (!result.affectedRows) return res.status(404).json({ message: "Book not found" });
|
|
100
|
+
return res.json({ message: "Deleted" });
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return res.status(500).json({ message: error.message });
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
module.exports = { listBooks, getBook, createBook, updateBook, deleteBook };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const { query, pool } = require("../config/db");
|
|
2
|
+
const { mapBorrowRow, borrowSelect } = require("../utils/mappers");
|
|
3
|
+
|
|
4
|
+
const startOfDay = (d) => {
|
|
5
|
+
const x = new Date(d);
|
|
6
|
+
x.setHours(0, 0, 0, 0);
|
|
7
|
+
return x;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const listBorrows = async (req, res) => {
|
|
11
|
+
try {
|
|
12
|
+
const status = String(req.query.status || "all").toLowerCase();
|
|
13
|
+
const dateFrom = req.query.dateFrom ? startOfDay(req.query.dateFrom) : null;
|
|
14
|
+
const dateTo = req.query.dateTo ? startOfDay(req.query.dateTo) : null;
|
|
15
|
+
if (dateTo) dateTo.setHours(23, 59, 59, 999);
|
|
16
|
+
|
|
17
|
+
const clauses = [];
|
|
18
|
+
const params = [];
|
|
19
|
+
if (status === "active") clauses.push("b.returned_at IS NULL");
|
|
20
|
+
else if (status === "returned") clauses.push("b.returned_at IS NOT NULL");
|
|
21
|
+
if (dateFrom) {
|
|
22
|
+
clauses.push("b.borrow_date >= ?");
|
|
23
|
+
params.push(dateFrom);
|
|
24
|
+
}
|
|
25
|
+
if (dateTo) {
|
|
26
|
+
clauses.push("b.borrow_date <= ?");
|
|
27
|
+
params.push(dateTo);
|
|
28
|
+
}
|
|
29
|
+
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
30
|
+
const rows = await query(`${borrowSelect} ${where} ORDER BY b.borrow_date DESC`, params);
|
|
31
|
+
return res.json(rows.map(mapBorrowRow));
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return res.status(500).json({ message: error.message });
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const createBorrow = async (req, res) => {
|
|
38
|
+
const conn = await pool.getConnection();
|
|
39
|
+
try {
|
|
40
|
+
const { studentId, bookId, borrowDate, returnDueDate } = req.body;
|
|
41
|
+
if (!studentId || !bookId || !borrowDate || !returnDueDate) {
|
|
42
|
+
return res.status(400).json({ message: "Student, book, borrow date and return date are required" });
|
|
43
|
+
}
|
|
44
|
+
const bd = new Date(borrowDate);
|
|
45
|
+
const rd = new Date(returnDueDate);
|
|
46
|
+
if (Number.isNaN(bd.getTime()) || Number.isNaN(rd.getTime())) {
|
|
47
|
+
return res.status(400).json({ message: "Invalid dates" });
|
|
48
|
+
}
|
|
49
|
+
if (rd < bd) {
|
|
50
|
+
return res.status(400).json({ message: "Return date must be on or after borrow date" });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const students = await query("SELECT id FROM students WHERE id = ?", [studentId]);
|
|
54
|
+
if (!students.length) return res.status(404).json({ message: "Student not found" });
|
|
55
|
+
|
|
56
|
+
await conn.beginTransaction();
|
|
57
|
+
const [books] = await conn.execute("SELECT id, quantity FROM books WHERE id = ? FOR UPDATE", [bookId]);
|
|
58
|
+
if (!books.length) {
|
|
59
|
+
await conn.rollback();
|
|
60
|
+
return res.status(404).json({ message: "Book not found" });
|
|
61
|
+
}
|
|
62
|
+
if (books[0].quantity < 1) {
|
|
63
|
+
await conn.rollback();
|
|
64
|
+
return res.status(409).json({ message: "No copies available to borrow" });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
await conn.execute("UPDATE books SET quantity = quantity - 1 WHERE id = ?", [bookId]);
|
|
68
|
+
const [insert] = await conn.execute(
|
|
69
|
+
"INSERT INTO borrows (student_id, book_id, borrow_date, return_due_date, returned_at) VALUES (?, ?, ?, ?, NULL)",
|
|
70
|
+
[studentId, bookId, bd, rd]
|
|
71
|
+
);
|
|
72
|
+
await conn.commit();
|
|
73
|
+
|
|
74
|
+
const rows = await query(`${borrowSelect} WHERE b.id = ?`, [insert.insertId]);
|
|
75
|
+
return res.status(201).json(mapBorrowRow(rows[0]));
|
|
76
|
+
} catch (error) {
|
|
77
|
+
await conn.rollback();
|
|
78
|
+
return res.status(500).json({ message: error.message });
|
|
79
|
+
} finally {
|
|
80
|
+
conn.release();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const returnBorrow = async (req, res) => {
|
|
85
|
+
const conn = await pool.getConnection();
|
|
86
|
+
try {
|
|
87
|
+
await conn.beginTransaction();
|
|
88
|
+
const [borrows] = await conn.execute("SELECT * FROM borrows WHERE id = ? FOR UPDATE", [req.params.id]);
|
|
89
|
+
const borrow = borrows[0];
|
|
90
|
+
if (!borrow) {
|
|
91
|
+
await conn.rollback();
|
|
92
|
+
return res.status(404).json({ message: "Borrow record not found" });
|
|
93
|
+
}
|
|
94
|
+
if (borrow.returned_at) {
|
|
95
|
+
await conn.rollback();
|
|
96
|
+
return res.status(409).json({ message: "Already returned" });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await conn.execute("UPDATE borrows SET returned_at = NOW() WHERE id = ?", [req.params.id]);
|
|
100
|
+
await conn.execute("UPDATE books SET quantity = quantity + 1 WHERE id = ?", [borrow.book_id]);
|
|
101
|
+
await conn.commit();
|
|
102
|
+
|
|
103
|
+
const rows = await query(`${borrowSelect} WHERE b.id = ?`, [req.params.id]);
|
|
104
|
+
return res.json(mapBorrowRow(rows[0]));
|
|
105
|
+
} catch (error) {
|
|
106
|
+
await conn.rollback();
|
|
107
|
+
return res.status(500).json({ message: error.message });
|
|
108
|
+
} finally {
|
|
109
|
+
conn.release();
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
module.exports = { listBorrows, createBorrow, returnBorrow };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const { query } = require("../config/db");
|
|
2
|
+
|
|
3
|
+
const stats = async (_req, res) => {
|
|
4
|
+
try {
|
|
5
|
+
const [{ totalBooks }] = await query("SELECT COUNT(*) AS totalBooks FROM books");
|
|
6
|
+
const [{ totalStudents }] = await query("SELECT COUNT(*) AS totalStudents FROM students");
|
|
7
|
+
const copies = await query("SELECT COALESCE(SUM(quantity), 0) AS sum FROM books");
|
|
8
|
+
const copiesInLibrary = copies[0]?.sum ?? 0;
|
|
9
|
+
|
|
10
|
+
const [{ borrowedActive }] = await query("SELECT COUNT(*) AS borrowedActive FROM borrows WHERE returned_at IS NULL");
|
|
11
|
+
const [{ returnedCount }] = await query("SELECT COUNT(*) AS returnedCount FROM borrows WHERE returned_at IS NOT NULL");
|
|
12
|
+
const [{ lateReturned }] = await query(
|
|
13
|
+
"SELECT COUNT(*) AS lateReturned FROM borrows WHERE returned_at IS NOT NULL AND returned_at > return_due_date"
|
|
14
|
+
);
|
|
15
|
+
const [{ overdueActive }] = await query(
|
|
16
|
+
"SELECT COUNT(*) AS overdueActive FROM borrows WHERE returned_at IS NULL AND return_due_date < NOW()"
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return res.json({
|
|
20
|
+
totalBookTitles: totalBooks,
|
|
21
|
+
totalCopiesInLibrary: copiesInLibrary,
|
|
22
|
+
totalStudents,
|
|
23
|
+
borrowedActive,
|
|
24
|
+
returnedCount,
|
|
25
|
+
lateReturned,
|
|
26
|
+
overdueActive,
|
|
27
|
+
});
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return res.status(500).json({ message: error.message });
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
module.exports = { stats };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const { query } = require("../config/db");
|
|
2
|
+
const { mapStudent, mapBook, mapBorrowRow, borrowSelect } = require("../utils/mappers");
|
|
3
|
+
|
|
4
|
+
const allStudents = async (_req, res) => {
|
|
5
|
+
try {
|
|
6
|
+
const rows = await query("SELECT * FROM students ORDER BY full_name");
|
|
7
|
+
return res.json({ generatedAt: new Date().toISOString(), rows: rows.map(mapStudent) });
|
|
8
|
+
} catch (error) {
|
|
9
|
+
return res.status(500).json({ message: error.message });
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const allBooks = async (_req, res) => {
|
|
14
|
+
try {
|
|
15
|
+
const rows = await query("SELECT * FROM books ORDER BY title");
|
|
16
|
+
return res.json({ generatedAt: new Date().toISOString(), rows: rows.map(mapBook) });
|
|
17
|
+
} catch (error) {
|
|
18
|
+
return res.status(500).json({ message: error.message });
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const borrowedReport = async (_req, res) => {
|
|
23
|
+
try {
|
|
24
|
+
const rows = await query(`${borrowSelect} WHERE b.returned_at IS NULL ORDER BY b.borrow_date DESC`);
|
|
25
|
+
return res.json({ generatedAt: new Date().toISOString(), rows: rows.map(mapBorrowRow) });
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return res.status(500).json({ message: error.message });
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const returnedReport = async (_req, res) => {
|
|
32
|
+
try {
|
|
33
|
+
const rows = await query(`${borrowSelect} WHERE b.returned_at IS NOT NULL ORDER BY b.returned_at DESC`);
|
|
34
|
+
return res.json({ generatedAt: new Date().toISOString(), rows: rows.map(mapBorrowRow) });
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return res.status(500).json({ message: error.message });
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
module.exports = { allStudents, allBooks, borrowedReport, returnedReport };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const { query } = require("../config/db");
|
|
2
|
+
const { mapStudent } = require("../utils/mappers");
|
|
3
|
+
|
|
4
|
+
const listStudents = async (req, res) => {
|
|
5
|
+
try {
|
|
6
|
+
const q = String(req.query.name || "").trim();
|
|
7
|
+
const rows = q
|
|
8
|
+
? await query("SELECT * FROM students WHERE full_name LIKE ? ORDER BY full_name", [`%${q}%`])
|
|
9
|
+
: await query("SELECT * FROM students ORDER BY full_name");
|
|
10
|
+
return res.json(rows.map(mapStudent));
|
|
11
|
+
} catch (error) {
|
|
12
|
+
return res.status(500).json({ message: error.message });
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const getStudent = async (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const rows = await query("SELECT * FROM students WHERE id = ?", [req.params.id]);
|
|
19
|
+
if (!rows.length) return res.status(404).json({ message: "Student not found" });
|
|
20
|
+
return res.json(mapStudent(rows[0]));
|
|
21
|
+
} catch (error) {
|
|
22
|
+
return res.status(500).json({ message: error.message });
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const createStudent = async (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const { fullName, gender, className, phone, email } = req.body;
|
|
29
|
+
if (!fullName || !gender || !className || !phone || !email) {
|
|
30
|
+
return res.status(400).json({ message: "All fields are required" });
|
|
31
|
+
}
|
|
32
|
+
if (!["Male", "Female", "Other"].includes(gender)) {
|
|
33
|
+
return res.status(400).json({ message: "Invalid gender" });
|
|
34
|
+
}
|
|
35
|
+
const result = await query(
|
|
36
|
+
"INSERT INTO students (full_name, gender, class_name, phone, email) VALUES (?, ?, ?, ?, ?)",
|
|
37
|
+
[String(fullName).trim(), gender, String(className).trim(), String(phone).trim(), String(email).trim().toLowerCase()]
|
|
38
|
+
);
|
|
39
|
+
const rows = await query("SELECT * FROM students WHERE id = ?", [result.insertId]);
|
|
40
|
+
return res.status(201).json(mapStudent(rows[0]));
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error.code === "ER_DUP_ENTRY") {
|
|
43
|
+
return res.status(409).json({ message: "Duplicate email" });
|
|
44
|
+
}
|
|
45
|
+
return res.status(500).json({ message: error.message });
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const updateStudent = async (req, res) => {
|
|
50
|
+
try {
|
|
51
|
+
const rows = await query("SELECT * FROM students WHERE id = ?", [req.params.id]);
|
|
52
|
+
if (!rows.length) return res.status(404).json({ message: "Student not found" });
|
|
53
|
+
|
|
54
|
+
const { fullName, gender, className, phone, email } = req.body;
|
|
55
|
+
const current = rows[0];
|
|
56
|
+
if (gender !== undefined && !["Male", "Female", "Other"].includes(gender)) {
|
|
57
|
+
return res.status(400).json({ message: "Invalid gender" });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
await query(
|
|
61
|
+
"UPDATE students SET full_name = ?, gender = ?, class_name = ?, phone = ?, email = ? WHERE id = ?",
|
|
62
|
+
[
|
|
63
|
+
fullName !== undefined ? String(fullName).trim() : current.full_name,
|
|
64
|
+
gender !== undefined ? gender : current.gender,
|
|
65
|
+
className !== undefined ? String(className).trim() : current.class_name,
|
|
66
|
+
phone !== undefined ? String(phone).trim() : current.phone,
|
|
67
|
+
email !== undefined ? String(email).trim().toLowerCase() : current.email,
|
|
68
|
+
req.params.id,
|
|
69
|
+
]
|
|
70
|
+
);
|
|
71
|
+
const updated = await query("SELECT * FROM students WHERE id = ?", [req.params.id]);
|
|
72
|
+
return res.json(mapStudent(updated[0]));
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (error.code === "ER_DUP_ENTRY") {
|
|
75
|
+
return res.status(409).json({ message: "Duplicate email" });
|
|
76
|
+
}
|
|
77
|
+
return res.status(500).json({ message: error.message });
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const deleteStudent = async (req, res) => {
|
|
82
|
+
try {
|
|
83
|
+
const active = await query("SELECT id FROM borrows WHERE student_id = ? AND returned_at IS NULL LIMIT 1", [req.params.id]);
|
|
84
|
+
if (active.length) {
|
|
85
|
+
return res.status(409).json({ message: "Student has active borrows; return books first" });
|
|
86
|
+
}
|
|
87
|
+
const result = await query("DELETE FROM students WHERE id = ?", [req.params.id]);
|
|
88
|
+
if (!result.affectedRows) return res.status(404).json({ message: "Student not found" });
|
|
89
|
+
return res.json({ message: "Deleted" });
|
|
90
|
+
} catch (error) {
|
|
91
|
+
return res.status(500).json({ message: error.message });
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
module.exports = { listStudents, getStudent, createStudent, updateStudent, deleteStudent };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const bcrypt = require("bcryptjs");
|
|
2
|
+
const { query } = require("./config/db");
|
|
3
|
+
|
|
4
|
+
async function ensureSeedData() {
|
|
5
|
+
const log = [];
|
|
6
|
+
|
|
7
|
+
const userPairs = [
|
|
8
|
+
{ username: "admin", email: "admin@exam.local", password: "admin123", role: "admin" },
|
|
9
|
+
{ username: "librarian", email: "librarian@exam.local", password: "librarian123", role: "librarian" },
|
|
10
|
+
];
|
|
11
|
+
let usersCreated = 0;
|
|
12
|
+
for (const { username, email, password, role } of userPairs) {
|
|
13
|
+
const exists = await query("SELECT id FROM users WHERE username = ?", [username]);
|
|
14
|
+
if (exists.length) continue;
|
|
15
|
+
await query("INSERT INTO users (username, email, password_hash, role) VALUES (?, ?, ?, ?)", [
|
|
16
|
+
username,
|
|
17
|
+
email,
|
|
18
|
+
await bcrypt.hash(password, 10),
|
|
19
|
+
role,
|
|
20
|
+
]);
|
|
21
|
+
usersCreated += 1;
|
|
22
|
+
}
|
|
23
|
+
if (usersCreated) log.push(`${usersCreated} user(s)`);
|
|
24
|
+
|
|
25
|
+
const studentCount = await query("SELECT COUNT(*) AS c FROM students");
|
|
26
|
+
if (studentCount[0].c === 0) {
|
|
27
|
+
await query(
|
|
28
|
+
"INSERT INTO students (full_name, gender, class_name, phone, email) VALUES (?, ?, ?, ?, ?), (?, ?, ?, ?, ?)",
|
|
29
|
+
[
|
|
30
|
+
"Marie Uwase", "Female", "Senior 3", "+250788100001", "marie.demo@school.test",
|
|
31
|
+
"David Nkurunziza", "Male", "Senior 2", "+250788100002", "david.demo@school.test",
|
|
32
|
+
]
|
|
33
|
+
);
|
|
34
|
+
log.push("2 students");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const bookCount = await query("SELECT COUNT(*) AS c FROM books");
|
|
38
|
+
if (bookCount[0].c === 0) {
|
|
39
|
+
await query(
|
|
40
|
+
"INSERT INTO books (title, author, category, quantity, published_year) VALUES (?, ?, ?, ?, ?), (?, ?, ?, ?, ?)",
|
|
41
|
+
[
|
|
42
|
+
"Computer Science Basics", "Teaching Team", "Textbook", 5, 2021,
|
|
43
|
+
"Stories for Young Readers", "A. Mukamana", "Literature", 8, 2019,
|
|
44
|
+
]
|
|
45
|
+
);
|
|
46
|
+
log.push("2 books");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (log.length) {
|
|
50
|
+
console.log(`LMS seed: created ${log.join(", ")}.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = ensureSeedData;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const requireAuth = (req, res, next) => {
|
|
2
|
+
if (!req.session.userId) {
|
|
3
|
+
return res.status(401).json({ message: "Unauthorized" });
|
|
4
|
+
}
|
|
5
|
+
return next();
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const requireAdmin = (req, res, next) => {
|
|
9
|
+
if (!req.session.userId) {
|
|
10
|
+
return res.status(401).json({ message: "Unauthorized" });
|
|
11
|
+
}
|
|
12
|
+
if (req.session.role !== "admin") {
|
|
13
|
+
return res.status(403).json({ message: "Admin only" });
|
|
14
|
+
}
|
|
15
|
+
return next();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const requireLibrarian = (req, res, next) => {
|
|
19
|
+
if (!req.session.userId) {
|
|
20
|
+
return res.status(401).json({ message: "Unauthorized" });
|
|
21
|
+
}
|
|
22
|
+
if (req.session.role !== "librarian" && req.session.role !== "admin") {
|
|
23
|
+
return res.status(403).json({ message: "Librarian or admin required" });
|
|
24
|
+
}
|
|
25
|
+
return next();
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
module.exports = { requireAuth, requireAdmin, requireLibrarian };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const authController = require("../controllers/authController");
|
|
3
|
+
const { requireAuth } = require("../middleware/auth");
|
|
4
|
+
|
|
5
|
+
const router = express.Router();
|
|
6
|
+
|
|
7
|
+
router.post("/login", authController.login);
|
|
8
|
+
router.post("/forgot-password", authController.forgotPassword);
|
|
9
|
+
router.get("/me", requireAuth, authController.me);
|
|
10
|
+
router.post("/logout", requireAuth, authController.logout);
|
|
11
|
+
|
|
12
|
+
module.exports = router;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const { requireAuth, requireAdmin } = require("../middleware/auth");
|
|
3
|
+
const bookController = require("../controllers/bookController");
|
|
4
|
+
|
|
5
|
+
const router = express.Router();
|
|
6
|
+
|
|
7
|
+
router.get("/", requireAuth, bookController.listBooks);
|
|
8
|
+
router.get("/:id", requireAuth, bookController.getBook);
|
|
9
|
+
router.post("/", requireAuth, requireAdmin, bookController.createBook);
|
|
10
|
+
router.put("/:id", requireAuth, requireAdmin, bookController.updateBook);
|
|
11
|
+
router.delete("/:id", requireAuth, requireAdmin, bookController.deleteBook);
|
|
12
|
+
|
|
13
|
+
module.exports = router;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const { requireAuth, requireLibrarian } = require("../middleware/auth");
|
|
3
|
+
const borrowController = require("../controllers/borrowController");
|
|
4
|
+
|
|
5
|
+
const router = express.Router();
|
|
6
|
+
|
|
7
|
+
router.get("/", requireAuth, requireLibrarian, borrowController.listBorrows);
|
|
8
|
+
router.post("/", requireAuth, requireLibrarian, borrowController.createBorrow);
|
|
9
|
+
router.patch("/:id/return", requireAuth, requireLibrarian, borrowController.returnBorrow);
|
|
10
|
+
|
|
11
|
+
module.exports = router;
|