first commit
This commit is contained in:
1
.env.development
Normal file
1
.env.development
Normal file
@@ -0,0 +1 @@
|
||||
JWT_SECRET=a4f6c1f6b9b5c1c7c3a63cce9f9c5b7d6f8e3c7c2a0f7a1d9e0b4c8d2a1f5e6c3b2d9f0a6e7c4b1d8f3a2c5e6d9b7a0
|
||||
1
.env.production
Normal file
1
.env.production
Normal file
@@ -0,0 +1 @@
|
||||
JWT_SECRET=a4f6c1f6b9b5c1c7c3a63cce9f9c5b7d6f8e3c7c2a0f7a1d9e0b4c8d2a1f5e6c3b2d9f0a6e7c4b1d8f3a2c5e6d9b7a0
|
||||
8
.sequelizerc
Normal file
8
.sequelizerc
Normal file
@@ -0,0 +1,8 @@
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
config: path.resolve("sequelize.config.js"),
|
||||
"models-path": path.resolve("src/models"),
|
||||
"seeders-path": path.resolve("seeders"),
|
||||
"migrations-path": path.resolve("migrations")
|
||||
};
|
||||
2
index.ts
Normal file
2
index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import ServerApplication from "./src/app";
|
||||
new ServerApplication();
|
||||
22
migrations/20260520050834-create-tables.js
Normal file
22
migrations/20260520050834-create-tables.js
Normal file
@@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
/**
|
||||
* Add altering commands here.
|
||||
*
|
||||
* Example:
|
||||
* await queryInterface.createTable('users', { id: Sequelize.INTEGER });
|
||||
*/
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
/**
|
||||
* Add reverting commands here.
|
||||
*
|
||||
* Example:
|
||||
* await queryInterface.dropTable('users');
|
||||
*/
|
||||
}
|
||||
};
|
||||
22
migrations/20260526040515-create-tables.js
Normal file
22
migrations/20260526040515-create-tables.js
Normal file
@@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
/**
|
||||
* Add altering commands here.
|
||||
*
|
||||
* Example:
|
||||
* await queryInterface.createTable('users', { id: Sequelize.INTEGER });
|
||||
*/
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
/**
|
||||
* Add reverting commands here.
|
||||
*
|
||||
* Example:
|
||||
* await queryInterface.dropTable('users');
|
||||
*/
|
||||
}
|
||||
};
|
||||
3496
package-lock.json
generated
Normal file
3496
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
48
package.json
Normal file
48
package.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
"dev": "nodemon index.ts",
|
||||
"build": "tsc",
|
||||
"start": "node index.ts",
|
||||
"type-check": "tsc --noEmit",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"auto-bind": "^4.0.0",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.6",
|
||||
"csurf": "^1.11.0",
|
||||
"express": "^5.2.1",
|
||||
"express-rate-limit": "^8.5.2",
|
||||
"helmet": "^8.1.0",
|
||||
"hpp": "^0.2.3",
|
||||
"http-errors": "^2.0.1",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"multer": "^2.1.1",
|
||||
"pg": "^8.20.0",
|
||||
"pg-hstore": "^2.3.4",
|
||||
"sequelize": "^6.37.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cookie-parser": "^1.4.10",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/csurf": "^1.11.5",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/hpp": "^0.2.7",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/node": "^25.9.0",
|
||||
"dotenv": "^17.4.2",
|
||||
"nodemon": "^3.1.14",
|
||||
"sequelize-cli": "^6.6.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
}
|
||||
135
seeders/rbac.seed.ts
Normal file
135
seeders/rbac.seed.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { sequelize } from "../src/models";
|
||||
import { Permission } from "../src/models/Permission";
|
||||
import { Role } from "../src/models/Role";
|
||||
|
||||
|
||||
export async function seedRBAC() {
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
|
||||
/**
|
||||
* PERMISSIONS
|
||||
*/
|
||||
|
||||
const permissionsList = [
|
||||
"VIEW_APPLICANTS",
|
||||
"CREATE_APPLICANT",
|
||||
"EDIT_APPLICANT",
|
||||
"DELETE_APPLICANT",
|
||||
|
||||
"EXPORT_APPLICANTS",
|
||||
|
||||
"VIEW_USERS",
|
||||
"CREATE_USER",
|
||||
"EDIT_USER",
|
||||
"DELETE_USER",
|
||||
|
||||
"VIEW_ROLES",
|
||||
"MANAGE_ROLES",
|
||||
|
||||
"SYSTEM_SETTINGS"
|
||||
];
|
||||
|
||||
const permissions: any = {};
|
||||
|
||||
for (const perm of permissionsList) {
|
||||
const [permission] = await Permission.findOrCreate({
|
||||
where: { name: perm },
|
||||
defaults: { name: perm },
|
||||
transaction
|
||||
});
|
||||
|
||||
permissions[perm] = permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* ROLES
|
||||
*/
|
||||
|
||||
const rolesData = [
|
||||
{
|
||||
name: "SUPER_ADMIN",
|
||||
description: "دسترسی کامل به کل سیستم"
|
||||
},
|
||||
{
|
||||
name: "ADMIN",
|
||||
description: "مدیریت کاربران و رزومهها"
|
||||
},
|
||||
{
|
||||
name: "HR",
|
||||
description: "کارشناس منابع انسانی"
|
||||
},
|
||||
{
|
||||
name: "VIEWER",
|
||||
description: "فقط مشاهده رزومهها"
|
||||
}
|
||||
];
|
||||
|
||||
const roles: any = {};
|
||||
|
||||
for (const roleData of rolesData) {
|
||||
const [role] = await Role.findOrCreate({
|
||||
where: { name: roleData.name },
|
||||
defaults: roleData,
|
||||
transaction
|
||||
});
|
||||
|
||||
roles[roleData.name] = role;
|
||||
}
|
||||
|
||||
/**
|
||||
* ROLE PERMISSIONS
|
||||
*/
|
||||
|
||||
await roles.SUPER_ADMIN.setPermissions(Object.values(permissions), {
|
||||
transaction
|
||||
});
|
||||
|
||||
await roles.ADMIN.setPermissions(
|
||||
[
|
||||
permissions.VIEW_APPLICANTS,
|
||||
permissions.CREATE_APPLICANT,
|
||||
permissions.EDIT_APPLICANT,
|
||||
permissions.DELETE_APPLICANT,
|
||||
permissions.EXPORT_APPLICANTS,
|
||||
|
||||
permissions.VIEW_USERS,
|
||||
permissions.CREATE_USER,
|
||||
permissions.EDIT_USER,
|
||||
|
||||
permissions.VIEW_ROLES
|
||||
],
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
await roles.HR.setPermissions(
|
||||
[
|
||||
permissions.VIEW_APPLICANTS,
|
||||
permissions.CREATE_APPLICANT,
|
||||
permissions.EDIT_APPLICANT,
|
||||
permissions.EXPORT_APPLICANTS
|
||||
],
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
await roles.VIEWER.setPermissions(
|
||||
[
|
||||
permissions.VIEW_APPLICANTS
|
||||
],
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
console.log("✅ RBAC seed completed");
|
||||
|
||||
} catch (error) {
|
||||
|
||||
await transaction.rollback();
|
||||
|
||||
console.error("❌ RBAC seed failed:", error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
18
sequelize.config.js
Normal file
18
sequelize.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
require("ts-node/register");
|
||||
|
||||
module.exports = {
|
||||
development: {
|
||||
username: "postgres",
|
||||
password: "root",
|
||||
database: "employee_form",
|
||||
host: "127.0.0.1",
|
||||
dialect: "postgres",
|
||||
},
|
||||
production: {
|
||||
username: "postgres",
|
||||
password: "root",
|
||||
database: "employee_form_prod",
|
||||
host: "127.0.0.1",
|
||||
dialect: "postgres",
|
||||
},
|
||||
};
|
||||
96
src/app.ts
Normal file
96
src/app.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import path from "node:path";
|
||||
import cookieParser from "cookie-parser";
|
||||
import createHttpError from "http-errors";
|
||||
// import { ServerErrorsObject, ServerResponse } from "./core/types";
|
||||
// import { secureApp } from "./config/secure-app";
|
||||
// import mainRouter from "./core/router/main.router";
|
||||
import dotenv from "dotenv";
|
||||
import { secureApp } from "./core/config/secure-app";
|
||||
import initDB from "./core/config/db-connection";
|
||||
import { ServerErrorsObject, ServerResponse } from "./core/types";
|
||||
import { seedRBAC } from "../seeders/rbac.seed";
|
||||
import mainRouter from "./core/router/main.router";
|
||||
|
||||
// import { seedDepartments } from "./seeders/department.seed";
|
||||
// import { seedUsers } from "./seeders/user.seed";
|
||||
// import { seedRoles } from "./seeders/role.seed";
|
||||
const express = require("express") as typeof import("express");
|
||||
dotenv.config();
|
||||
|
||||
export default class ServerApplication {
|
||||
#PORT = 4000;
|
||||
#APP = express();
|
||||
constructor() {
|
||||
this.serverConfiguration();
|
||||
this.StartApplication();
|
||||
this.InitClientSession();
|
||||
this.RoutesConfiguration();
|
||||
this.ErrorHandlingConfiguration();
|
||||
}
|
||||
|
||||
async serverConfiguration() {
|
||||
// this.#APP.use(secureApp);
|
||||
this.#APP.use(express.json());
|
||||
this.#APP.use(express.urlencoded({ extended: true }));
|
||||
this.#APP.set("json spaces", 2);
|
||||
this.#APP.use(
|
||||
"/media/images",
|
||||
express.static(path.join(__dirname, "..", "media", "images")),
|
||||
);
|
||||
this.#APP.use(
|
||||
"/media/videos",
|
||||
express.static(path.join(__dirname, "..", "media", "videos")),
|
||||
);
|
||||
}
|
||||
async StartApplication() {
|
||||
await initDB();
|
||||
// await seedRBAC();
|
||||
|
||||
|
||||
|
||||
this.#APP.listen(this.#PORT, () => {
|
||||
console.log(
|
||||
`Server Running on PORT ${this.#PORT} url : ${"http://localhost:"}${
|
||||
this.#PORT
|
||||
}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
InitClientSession() {
|
||||
this.#APP.use(cookieParser(process.env.COOKIE_PARSER_SECRET_KEY));
|
||||
}
|
||||
RoutesConfiguration() {
|
||||
// this.#APP.get("/", (req, res) => res.send(""));
|
||||
|
||||
this.#APP.use("/api/v1", mainRouter);
|
||||
|
||||
}
|
||||
ErrorHandlingConfiguration() {
|
||||
this.#APP.use((req: any, res: Response, next: NextFunction) => {
|
||||
next(createHttpError.NotFound("این آدرس یافت نشد"));
|
||||
});
|
||||
this.#APP.use(
|
||||
async (error: any, req: any, res: ServerResponse, next: NextFunction) => {
|
||||
// await ErrorLog.create({
|
||||
// message: error.message,
|
||||
// stack: error.stack,
|
||||
// severity: "HIGH",
|
||||
// });
|
||||
// console.log(error);
|
||||
const serverError = createHttpError.InternalServerError();
|
||||
const statusCode = error.status || serverError.status;
|
||||
const message: string = error.message || serverError.message;
|
||||
|
||||
const errorObject: ServerErrorsObject = {
|
||||
status: statusCode,
|
||||
error: {
|
||||
message,
|
||||
},
|
||||
};
|
||||
|
||||
return res.status(statusCode).json(errorObject);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
16
src/config/database.ts
Normal file
16
src/config/database.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// import { Sequelize } from "sequelize";
|
||||
|
||||
// const sequelize = new Sequelize(
|
||||
// "employee_form",
|
||||
// "postgres",
|
||||
// "root",
|
||||
// {
|
||||
// host: "127.0.0.1",
|
||||
// dialect: "postgres",
|
||||
// logging: false
|
||||
// }
|
||||
// );
|
||||
|
||||
|
||||
|
||||
// export default sequelize;
|
||||
15
src/core/config/db-connection.ts
Normal file
15
src/core/config/db-connection.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { sequelize } from "../../models";
|
||||
|
||||
async function initDB(): Promise<void> {
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
|
||||
await sequelize.sync({ alter: true });
|
||||
console.log("✅ Database synced successfully");
|
||||
} catch (error) {
|
||||
console.error("❌ Database sync failed:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
export default initDB;
|
||||
29
src/core/config/secure-app.ts
Normal file
29
src/core/config/secure-app.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import helmet from "helmet";
|
||||
import rateLimit from "express-rate-limit";
|
||||
import cors from "cors";
|
||||
import cookieParser from "cookie-parser";
|
||||
import csurf from "csurf";
|
||||
import hpp from "hpp";
|
||||
import { cors_option, helmet_option, limiter_option } from "./server-configuration";
|
||||
|
||||
export const rateLimiter = rateLimit(limiter_option);
|
||||
|
||||
export const corsOptions = cors(cors_option);
|
||||
|
||||
export const securityHeaders = helmet(helmet_option);
|
||||
|
||||
export const csrfProtection = [
|
||||
cookieParser(),
|
||||
csurf({cookie: {httpOnly: true, secure: true, sameSite: "strict"}}),
|
||||
];
|
||||
|
||||
export const sanitizeData = [
|
||||
hpp(),
|
||||
];
|
||||
|
||||
export const secureApp = [
|
||||
corsOptions,
|
||||
rateLimiter,
|
||||
securityHeaders,
|
||||
...sanitizeData,
|
||||
];
|
||||
90
src/core/config/server-configuration.ts
Normal file
90
src/core/config/server-configuration.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { CorsOptions } from "cors";
|
||||
import dotenv from "dotenv";
|
||||
import { NextFunction } from "express";
|
||||
import { Options } from "express-rate-limit";
|
||||
import { HelmetOptions } from "helmet";
|
||||
import createHttpError from "http-errors";
|
||||
|
||||
import { Request } from "express";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const multer = require("multer");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
export const config = {
|
||||
port: process.env.PORT || 3500,
|
||||
db: {
|
||||
host: process.env.DB_HOST,
|
||||
port: Number(process.env.DB_PORT),
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
},
|
||||
jwtSecret: process.env.JWT_SECRET || "secret",
|
||||
};
|
||||
|
||||
export const limiter_option: Partial<Options> = {
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 4000,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
|
||||
handler: (req: any, res: any, next: NextFunction) => {
|
||||
next(
|
||||
new createHttpError.TooManyRequests(
|
||||
"تعداد درخواست شما بیشتر از حد مجاز است ، در زمان دیگری مجدد درخواست دهید",
|
||||
),
|
||||
);
|
||||
},
|
||||
// message: {error: "Too many requests, please try again later."},
|
||||
};
|
||||
|
||||
export const helmet_option: HelmetOptions = {
|
||||
contentSecurityPolicy: {
|
||||
useDefaults: true,
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
// scriptSrc: ["'self'", "'unsafe-inline'", "https://trusted.cdn.com"],
|
||||
// styleSrc: ["'self'", "'unsafe-inline'"],
|
||||
// imgSrc: ["'self'", "data:", "https:"],
|
||||
connectSrc: ["'self'"],
|
||||
// fontSrc: ["'self'", "https://fonts.gstatic.com"],
|
||||
objectSrc: ["'none'"],
|
||||
upgradeInsecureRequests: [], // تبدیل اتومات http به https
|
||||
},
|
||||
},
|
||||
crossOriginEmbedderPolicy: true,
|
||||
crossOriginResourcePolicy: { policy: "same-origin" },
|
||||
frameguard: { action: "deny" }, // جلوگیری از Clickjacking
|
||||
referrerPolicy: { policy: "no-referrer" }, // جلوگیری از لو رفتن referrer
|
||||
xssFilter: true, // فعال کردن فیلتر XSS
|
||||
hsts: { maxAge: 63072000, includeSubDomains: true, preload: true }, // HSTS
|
||||
};
|
||||
|
||||
export const cors_option: CorsOptions = {
|
||||
origin: true,
|
||||
credentials: true,
|
||||
allowedHeaders: [
|
||||
"Content-Type",
|
||||
"Authorization",
|
||||
"x-upload-token", // 👈 اینو اضافه کن
|
||||
],
|
||||
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
|
||||
maxAge: 600,
|
||||
// origin:["http://localhost:3000"]
|
||||
};
|
||||
|
||||
async function createDirectoryRoute(req: Request<any>) {
|
||||
const date = new Date();
|
||||
|
||||
const directory = path.join(__dirname, "..", "..", "..", "public", "images");
|
||||
req.body.fileUploadPath = path.join(directory, "original");
|
||||
fs.mkdirSync(directory, { recursive: true });
|
||||
return directory;
|
||||
}
|
||||
|
||||
const storage = multer.memoryStorage(); // Keep files in memory (instead of disk)
|
||||
const uploadFile = multer({ storage: storage });
|
||||
|
||||
export { uploadFile };
|
||||
1
src/core/constant/index.ts
Normal file
1
src/core/constant/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const TOKEN_NAME = 'tid'
|
||||
7
src/core/constant/permission.ts
Normal file
7
src/core/constant/permission.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const Permissions = [
|
||||
"VIEW_APPLICANTS",
|
||||
"EDIT_APPLICANT",
|
||||
"DELETE_APPLICANT",
|
||||
"EXPORT_APPLICANTS",
|
||||
"MANAGE_USERS",
|
||||
];
|
||||
8
src/core/controller/main.controller.ts
Normal file
8
src/core/controller/main.controller.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import autoBind from "auto-bind";
|
||||
|
||||
export class Controller {
|
||||
constructor() {
|
||||
autoBind(this);
|
||||
}
|
||||
}
|
||||
|
||||
0
src/core/messages/alerts.ts
Normal file
0
src/core/messages/alerts.ts
Normal file
6
src/core/messages/errors.ts
Normal file
6
src/core/messages/errors.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const GlobalErrorMessages = Object.freeze({
|
||||
server: {
|
||||
internal: "متاسفانه خطايي رخ داده است",
|
||||
},
|
||||
notFound: "يافت نشد",
|
||||
});
|
||||
0
src/core/messages/success.ts
Normal file
0
src/core/messages/success.ts
Normal file
47
src/core/middleware/auth.middleware.ts
Normal file
47
src/core/middleware/auth.middleware.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { User } from "../../models/User";
|
||||
import { Role } from "../../models/Role";
|
||||
import { Permission } from "../../models/Permission";
|
||||
|
||||
/**
|
||||
* Middleware: استخراج اطلاعات کاربر از توکن JWT
|
||||
* توکن باید در Header ارسال شود: Authorization: Bearer <token>
|
||||
*/
|
||||
export async function requireAuth(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
return res.status(401).json({ message: "توکن احراز هویت ارسال نشده است." });
|
||||
}
|
||||
|
||||
const token = authHeader.split(" ")[1];
|
||||
|
||||
try {
|
||||
// بررسی صحت و امضای JWT
|
||||
const decoded: any = jwt.verify(token, process.env.JWT_SECRET!);
|
||||
|
||||
// پیدا کردن یوزر و نقشها از دیتابیس
|
||||
const user = await User.findByPk(decoded.userId, {
|
||||
include: [
|
||||
{
|
||||
model: Role,
|
||||
include: [Permission],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: "کاربر یافت نشد." });
|
||||
}
|
||||
|
||||
// تزریق یوزر در request برای استفادههای بعدی
|
||||
(req as any).user = user;
|
||||
next();
|
||||
} catch (err) {
|
||||
return res.status(401).json({ message: "توکن نامعتبر یا منقضی است." });
|
||||
}
|
||||
}
|
||||
24
src/core/middleware/require-permission.middleware.ts
Normal file
24
src/core/middleware/require-permission.middleware.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { NextFunction } from "express";
|
||||
import { ServerResponse } from "../types";
|
||||
|
||||
export function requirePermission(...permissions: string[]) {
|
||||
return (req: Request, res: ServerResponse, next: NextFunction) => {
|
||||
const user = (req as any).user;
|
||||
if (!user)
|
||||
return res
|
||||
.status(401)
|
||||
.json({ status: 401, data: {}, message: "احراز هویت انجام نشده است." });
|
||||
|
||||
const userPermissions =
|
||||
user.Role?.Permissions?.map((p: any) => p.name) || [];
|
||||
|
||||
const hasPerm = permissions.every((p) => userPermissions.includes(p));
|
||||
if (!hasPerm) {
|
||||
return res
|
||||
.status(403)
|
||||
.json({ status: 403, data: {}, message: "دسترسی شما کافی نیست." });
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
25
src/core/middleware/require-role.middleware.ts
Normal file
25
src/core/middleware/require-role.middleware.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { NextFunction } from "express";
|
||||
import { ServerResponse } from "../types";
|
||||
|
||||
export function requireRole(...roles: string[]) {
|
||||
return (req: Request, res: ServerResponse, next: NextFunction) => {
|
||||
const user = (req as any).user;
|
||||
if (!user)
|
||||
return res
|
||||
.status(401)
|
||||
.json({ status: 401, data: {}, message: "احراز هویت انجام نشده است." });
|
||||
|
||||
const userRole = user.Role?.name;
|
||||
if (!userRole || !roles.includes(userRole)) {
|
||||
return res
|
||||
.status(403)
|
||||
.json({
|
||||
status: 403,
|
||||
data: {},
|
||||
message: "شما دسترسی لازم برای این عملیات را ندارید.",
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
12
src/core/router/main.router.ts
Normal file
12
src/core/router/main.router.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Router } from 'express';
|
||||
import AuthRouter from '../../modules/auth/router/auth.routes';
|
||||
|
||||
const mainRouter = Router();
|
||||
|
||||
|
||||
// mainRouter.use('/user',userRouter)
|
||||
// mainRouter.use('/applicant',applicantRouter)
|
||||
mainRouter.use('/auth',AuthRouter)
|
||||
// mainRouter.use('/user',userRouter)
|
||||
|
||||
export default mainRouter;
|
||||
20
src/core/types/index.ts
Normal file
20
src/core/types/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Response } from "express";
|
||||
|
||||
export interface ServerResponseObject {
|
||||
status: number;
|
||||
data: any;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface ServerErrorsObject {
|
||||
status: number;
|
||||
data?: any;
|
||||
error: {
|
||||
message: string;
|
||||
description?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type ServerResponse = Response<
|
||||
ServerResponseObject | ServerErrorsObject
|
||||
>;
|
||||
39
src/models/Applicant.ts
Normal file
39
src/models/Applicant.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Model, DataTypes, Sequelize, Optional } from "sequelize";
|
||||
|
||||
export interface ApplicantAttributes {
|
||||
id: string;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export interface ApplicantCreationAttributes
|
||||
extends Optional<ApplicantAttributes, "id" | "createdAt" | "updatedAt"> {}
|
||||
|
||||
export class Applicant
|
||||
extends Model<ApplicantAttributes, ApplicantCreationAttributes>
|
||||
implements ApplicantAttributes
|
||||
{
|
||||
public id!: string;
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
static initModel(sequelize: Sequelize): typeof Applicant {
|
||||
Applicant.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
}
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "applicants",
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
return Applicant;
|
||||
}
|
||||
}
|
||||
40
src/models/Center.ts
Normal file
40
src/models/Center.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { DataTypes, Model, Sequelize } from "sequelize";
|
||||
|
||||
export class Center extends Model {
|
||||
public id!: string;
|
||||
public name!: string;
|
||||
public address!: string;
|
||||
public isUrgent!: boolean;
|
||||
|
||||
static initModel(sequelize: Sequelize) {
|
||||
Center.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
address: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
},
|
||||
isUrgent: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
allowNull: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "centers",
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
return Center;
|
||||
}
|
||||
}
|
||||
46
src/models/ComputerSkill.ts
Normal file
46
src/models/ComputerSkill.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { DataTypes, Model, Sequelize } from "sequelize";
|
||||
|
||||
const skillLevels = [
|
||||
"NONE",
|
||||
"VERY_WEAK",
|
||||
"WEAK",
|
||||
"AVERAGE",
|
||||
"GOOD",
|
||||
"VERY_GOOD",
|
||||
"EXCELLENT",
|
||||
];
|
||||
|
||||
export class ComputerSkill extends Model {
|
||||
static initModel(sequelize: Sequelize) {
|
||||
ComputerSkill.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
applicantId: { type: DataTypes.UUID, allowNull: false },
|
||||
|
||||
pcUsage: { type: DataTypes.ENUM(...skillLevels), defaultValue: "NONE" },
|
||||
word: { type: DataTypes.ENUM(...skillLevels), defaultValue: "NONE" },
|
||||
excel: { type: DataTypes.ENUM(...skillLevels), defaultValue: "NONE" },
|
||||
powerPoint: {
|
||||
type: DataTypes.ENUM(...skillLevels),
|
||||
defaultValue: "NONE",
|
||||
},
|
||||
|
||||
rahkaran: {
|
||||
type: DataTypes.ENUM(...skillLevels),
|
||||
defaultValue: "NONE",
|
||||
},
|
||||
kasra: { type: DataTypes.ENUM(...skillLevels), defaultValue: "NONE" },
|
||||
didgah: { type: DataTypes.ENUM(...skillLevels), defaultValue: "NONE" },
|
||||
his: { type: DataTypes.ENUM(...skillLevels), defaultValue: "NONE" },
|
||||
|
||||
otherSoftware: { type: DataTypes.TEXT }, // توضیحات سایر نرمافزارها
|
||||
},
|
||||
{ sequelize, tableName: "computer_skills", timestamps: true },
|
||||
);
|
||||
return ComputerSkill;
|
||||
}
|
||||
}
|
||||
26
src/models/Course.ts
Normal file
26
src/models/Course.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Model, DataTypes, Sequelize, Optional } from "sequelize";
|
||||
|
||||
export interface CourseAttributes {
|
||||
id: string;
|
||||
applicantId: string;
|
||||
title: string;
|
||||
institution: string;
|
||||
year: number;
|
||||
duration: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export class Course extends Model<CourseAttributes, Optional<CourseAttributes, "id">> {
|
||||
static initModel(sequelize: Sequelize) {
|
||||
Course.init({
|
||||
id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true },
|
||||
applicantId: { type: DataTypes.UUID, allowNull: false },
|
||||
title: { type: DataTypes.STRING, allowNull: false },
|
||||
institution: { type: DataTypes.STRING },
|
||||
year: { type: DataTypes.INTEGER },
|
||||
duration: { type: DataTypes.STRING },
|
||||
description: { type: DataTypes.TEXT }
|
||||
}, { sequelize, tableName: "courses", timestamps: true });
|
||||
return Course;
|
||||
}
|
||||
}
|
||||
114
src/models/Education.ts
Normal file
114
src/models/Education.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { Model, DataTypes, Sequelize, Optional } from "sequelize";
|
||||
|
||||
export interface EducationAttributes {
|
||||
id: string;
|
||||
applicantId: string;
|
||||
|
||||
degree: string; // مقطع تحصیلی
|
||||
field: string; // رشته تحصیلی
|
||||
university: string; // دانشگاه یا موسسه
|
||||
|
||||
startYear?: number;
|
||||
endYear?: number;
|
||||
|
||||
gpa?: number; // معدل
|
||||
|
||||
description?: string;
|
||||
certificateImageId?: string; // آیدی تصویر مدرک تحصیلی (FK به جدول فایلها)
|
||||
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export type EducationCreationAttributes = Optional<
|
||||
EducationAttributes,
|
||||
"id" | "createdAt" | "updatedAt" | "certificateImageId"
|
||||
>;
|
||||
|
||||
export class Education
|
||||
extends Model<EducationAttributes, EducationCreationAttributes>
|
||||
implements EducationAttributes
|
||||
{
|
||||
public id!: string;
|
||||
public applicantId!: string;
|
||||
|
||||
public degree!: string;
|
||||
public field!: string;
|
||||
public university!: string;
|
||||
|
||||
public startYear?: number;
|
||||
public endYear?: number;
|
||||
|
||||
public gpa?: number;
|
||||
|
||||
public description?: string;
|
||||
public certificateImageId?: string;
|
||||
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
static initModel(sequelize: Sequelize): typeof Education {
|
||||
Education.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
applicantId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
degree: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
field: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
university: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
startYear: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
endYear: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
gpa: {
|
||||
type: DataTypes.FLOAT,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
certificateImageId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: true
|
||||
}
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "educations",
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
return Education;
|
||||
}
|
||||
}
|
||||
76
src/models/File.ts
Normal file
76
src/models/File.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { Model, DataTypes, Sequelize, Optional } from "sequelize";
|
||||
|
||||
export interface FileAttributes {
|
||||
id: string;
|
||||
|
||||
fileName: string;
|
||||
originalName: string;
|
||||
mimeType: string;
|
||||
|
||||
path: string;
|
||||
size: number;
|
||||
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export interface FileCreationAttributes
|
||||
extends Optional<FileAttributes, "id"> {}
|
||||
|
||||
export class File
|
||||
extends Model<FileAttributes, FileCreationAttributes>
|
||||
implements FileAttributes
|
||||
{
|
||||
public id!: string;
|
||||
|
||||
public fileName!: string;
|
||||
public originalName!: string;
|
||||
public mimeType!: string;
|
||||
|
||||
public path!: string;
|
||||
public size!: number;
|
||||
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
static initModel(sequelize: Sequelize): typeof File {
|
||||
File.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true
|
||||
},
|
||||
|
||||
fileName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
originalName: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
|
||||
mimeType: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
|
||||
path: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
size: {
|
||||
type: DataTypes.INTEGER
|
||||
}
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "files",
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
return File;
|
||||
}
|
||||
}
|
||||
119
src/models/Identity.ts
Normal file
119
src/models/Identity.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { Model, DataTypes, Sequelize, Optional } from "sequelize";
|
||||
|
||||
export interface IdentityAttributes {
|
||||
id: string;
|
||||
applicantId: string;
|
||||
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
fatherName: string;
|
||||
nationalCode: string;
|
||||
|
||||
birthDate: Date;
|
||||
birthPlace: string;
|
||||
|
||||
gender: string;
|
||||
religion: string;
|
||||
nationality: string;
|
||||
|
||||
profilePhotoId?: string;
|
||||
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export interface IdentityCreationAttributes
|
||||
extends Optional<IdentityAttributes, "id" | "profilePhotoId"> {}
|
||||
|
||||
export class Identity
|
||||
extends Model<IdentityAttributes, IdentityCreationAttributes>
|
||||
implements IdentityAttributes
|
||||
{
|
||||
public id!: string;
|
||||
public applicantId!: string;
|
||||
|
||||
public firstName!: string;
|
||||
public lastName!: string;
|
||||
public fatherName!: string;
|
||||
public nationalCode!: string;
|
||||
|
||||
public birthDate!: Date;
|
||||
public birthPlace!: string;
|
||||
|
||||
public gender!: string;
|
||||
public religion!: string;
|
||||
public nationality!: string;
|
||||
|
||||
public profilePhotoId?: string;
|
||||
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
static initModel(sequelize: Sequelize): typeof Identity {
|
||||
Identity.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true
|
||||
},
|
||||
|
||||
applicantId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
firstName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
lastName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
fatherName: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
|
||||
nationalCode: {
|
||||
type: DataTypes.STRING(10),
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
birthDate: {
|
||||
type: DataTypes.DATEONLY
|
||||
},
|
||||
|
||||
birthPlace: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
|
||||
gender: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
|
||||
religion: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
|
||||
nationality: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
|
||||
profilePhotoId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: true
|
||||
}
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "identities",
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
return Identity;
|
||||
}
|
||||
}
|
||||
43
src/models/JobCategory.ts
Normal file
43
src/models/JobCategory.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { DataTypes, Model, Sequelize } from "sequelize";
|
||||
|
||||
export class JobCategory extends Model {
|
||||
public id!: string;
|
||||
public centerId!: string;
|
||||
public name!: string;
|
||||
public isUrgent!: boolean;
|
||||
|
||||
static initModel(sequelize: Sequelize) {
|
||||
JobCategory.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
centerId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: 'centers',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
isUrgent: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
allowNull: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "job_categories",
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
return JobCategory;
|
||||
}
|
||||
}
|
||||
79
src/models/JobInfo.ts
Normal file
79
src/models/JobInfo.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { DataTypes, Model, Sequelize } from "sequelize";
|
||||
|
||||
export class JobInfo extends Model {
|
||||
public id!: string;
|
||||
public applicantId!: string;
|
||||
public readyToWorkDate!: Date; // تاریخ آمادگی برای شروع
|
||||
public isCurrentEmployee!: boolean; // از پرسنل حال حاضر هستم یا خیر
|
||||
public hasPastCooperation!: boolean; // سابقه همکاری در گذشته
|
||||
public isCurrentlyEmployed!: boolean; // در حال حاضر مشغول به کار هستم یا خیر
|
||||
public dualJobInterest!: boolean; // تمایل به دو جا کار بودن
|
||||
public retirementStatus!: "None" | "Retired" | "Redeemed"; // بازنشسته / بازخرید / هیچکدام
|
||||
public isMilitary!: boolean; // نظامی بودن
|
||||
public hasInsurance!: boolean; // سابقه بیمه دارد یا خیر
|
||||
public insuranceType?: string; // نوع بیمه (تامین اجتماعی، خدمات درمانی و...)
|
||||
public totalInsuranceYears!: number; // جمع سالهای سابقه بیمه
|
||||
|
||||
static initModel(sequelize: Sequelize) {
|
||||
JobInfo.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
applicantId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
unique: true, // معمولاً برای هر متقاضی یک رکورد وضعیت کاری وجود دارد
|
||||
},
|
||||
readyToWorkDate: {
|
||||
type: DataTypes.DATEONLY,
|
||||
allowNull: false,
|
||||
},
|
||||
isCurrentEmployee: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
},
|
||||
hasPastCooperation: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
},
|
||||
isCurrentlyEmployed: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
},
|
||||
dualJobInterest: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
},
|
||||
retirementStatus: {
|
||||
type: DataTypes.ENUM("None", "Retired", "Redeemed"),
|
||||
defaultValue: "None",
|
||||
},
|
||||
isMilitary: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
},
|
||||
hasInsurance: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
},
|
||||
insuranceType: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
totalInsuranceYears: {
|
||||
type: DataTypes.DECIMAL(4, 1), // مثلاً 10.5 سال سابقه
|
||||
defaultValue: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "work_experiences",
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
return JobInfo;
|
||||
}
|
||||
}
|
||||
83
src/models/JobRequest.ts
Normal file
83
src/models/JobRequest.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { DataTypes, Model, Sequelize } from "sequelize";
|
||||
|
||||
export class JobRequest extends Model {
|
||||
// تعریف فیلدها برای شناسایی توسط TypeScript
|
||||
public id!: string;
|
||||
public applicantId!: string;
|
||||
public centerId!: string;
|
||||
public categoryId!: string;
|
||||
public jobTitle!: string;
|
||||
public description?: string;
|
||||
public employmentType!: "Full-Time" | "Part-Time" | "Contract";
|
||||
public requestedSalary!: number;
|
||||
public extraDescription?: string;
|
||||
|
||||
// فیلدهای زمانبندی (اگر timestamps: true باشد)
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
static initModel(sequelize: Sequelize) {
|
||||
JobRequest.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
applicantId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
},
|
||||
centerId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: "centers", // نام جدول مراکز
|
||||
key: "id",
|
||||
},
|
||||
},
|
||||
categoryId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: "job_categories", // نام جدول رستهها
|
||||
key: "id",
|
||||
},
|
||||
},
|
||||
jobTitle: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
},
|
||||
employmentType: {
|
||||
// تعریف نوع همکاری به صورت ENUM
|
||||
type: DataTypes.ENUM("Full-Time", "Part-Time", "Contract"),
|
||||
allowNull: false,
|
||||
},
|
||||
requestedSalary: {
|
||||
// استفاده از BIGINT چون مبالغ ریالی معمولاً طولانی هستند
|
||||
type: DataTypes.BIGINT,
|
||||
allowNull: false,
|
||||
get() {
|
||||
// تبدیل رشته خروجی از دیتابیس به عدد در جاوااسکریپت
|
||||
const value = this.getDataValue("requestedSalary");
|
||||
return value ? parseInt(value, 10) : 0;
|
||||
},
|
||||
},
|
||||
extraDescription: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "job_infos", // همان نامی که خودتان انتخاب کردید
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
return JobRequest;
|
||||
}
|
||||
}
|
||||
16
src/models/LanguageSkill.ts
Normal file
16
src/models/LanguageSkill.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { DataTypes, Model, Sequelize } from "sequelize";
|
||||
|
||||
export class LanguageSkill extends Model {
|
||||
static initModel(sequelize: Sequelize) {
|
||||
LanguageSkill.init({
|
||||
id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true },
|
||||
applicantId: { type: DataTypes.UUID, allowNull: false },
|
||||
languageName: { type: DataTypes.STRING }, // انگلیسی، عربی، گویش خاص و ...
|
||||
proficiency: { type: DataTypes.STRING }, // میزان تسلط
|
||||
hasCertificate: { type: DataTypes.BOOLEAN, defaultValue: false },
|
||||
certificateType: { type: DataTypes.STRING },
|
||||
description: { type: DataTypes.TEXT }
|
||||
}, { sequelize, tableName: "language_skills", timestamps: true });
|
||||
return LanguageSkill;
|
||||
}
|
||||
}
|
||||
51
src/models/Permission.ts
Normal file
51
src/models/Permission.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
// src/models/permission.model.ts
|
||||
|
||||
import {
|
||||
Model,
|
||||
DataTypes,
|
||||
Optional,
|
||||
Sequelize
|
||||
} from "sequelize";
|
||||
|
||||
interface PermissionAttributes {
|
||||
id: string;
|
||||
name: string;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
type PermissionCreationAttributes = Optional<PermissionAttributes, "id">;
|
||||
|
||||
export class Permission extends Model<
|
||||
PermissionAttributes,
|
||||
PermissionCreationAttributes
|
||||
> implements PermissionAttributes {
|
||||
public id!: string;
|
||||
public name!: string;
|
||||
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
static initModel(sequelize: Sequelize): typeof Permission {
|
||||
Permission.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "permissions",
|
||||
}
|
||||
);
|
||||
|
||||
return Permission;
|
||||
}
|
||||
}
|
||||
188
src/models/PersonalInfo.ts
Normal file
188
src/models/PersonalInfo.ts
Normal file
@@ -0,0 +1,188 @@
|
||||
import { Model, DataTypes, Sequelize, Optional } from "sequelize";
|
||||
|
||||
export interface PersonalInfoAttributes {
|
||||
id: string;
|
||||
applicantId: string;
|
||||
|
||||
maritalStatus?: string;
|
||||
militaryStatus?: string;
|
||||
|
||||
fatherEducation?: string;
|
||||
fatherJob?: string;
|
||||
|
||||
motherEducation?: string;
|
||||
motherJob?: string;
|
||||
|
||||
housingStatus?: string;
|
||||
city?: string;
|
||||
address?: string;
|
||||
|
||||
homePhone?: string;
|
||||
mobilePhone?: string;
|
||||
emergencyPhone?: string;
|
||||
email?: string;
|
||||
|
||||
residenceDuration?: number;
|
||||
|
||||
isVeteran?: boolean;
|
||||
|
||||
hasCriminalRecord?: boolean;
|
||||
criminalDescription?: string;
|
||||
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export type PersonalInfoCreationAttributes = Optional<
|
||||
PersonalInfoAttributes,
|
||||
"id" | "createdAt" | "updatedAt"
|
||||
>;
|
||||
|
||||
export class PersonalInfo
|
||||
extends Model<PersonalInfoAttributes, PersonalInfoCreationAttributes>
|
||||
implements PersonalInfoAttributes
|
||||
{
|
||||
public id!: string;
|
||||
public applicantId!: string;
|
||||
|
||||
public maritalStatus?: string;
|
||||
public militaryStatus?: string;
|
||||
|
||||
public fatherEducation?: string;
|
||||
public fatherJob?: string;
|
||||
|
||||
public motherEducation?: string;
|
||||
public motherJob?: string;
|
||||
|
||||
public housingStatus?: string;
|
||||
public city?: string;
|
||||
public address?: string;
|
||||
|
||||
public homePhone?: string;
|
||||
public mobilePhone?: string;
|
||||
public emergencyPhone?: string;
|
||||
public email?: string;
|
||||
|
||||
public residenceDuration?: number;
|
||||
|
||||
public isVeteran?: boolean;
|
||||
|
||||
public hasCriminalRecord?: boolean;
|
||||
public criminalDescription?: string;
|
||||
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
static initModel(sequelize: Sequelize): typeof PersonalInfo {
|
||||
PersonalInfo.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
applicantId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
maritalStatus: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
militaryStatus: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
fatherEducation: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
fatherJob: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
motherEducation: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
motherJob: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
housingStatus: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
city: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
address: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
homePhone: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
mobilePhone: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
emergencyPhone: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
validate: {
|
||||
isEmail: true
|
||||
}
|
||||
},
|
||||
|
||||
residenceDuration: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
isVeteran: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false
|
||||
},
|
||||
|
||||
hasCriminalRecord: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false
|
||||
},
|
||||
|
||||
criminalDescription: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true
|
||||
}
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "personal_infos",
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
return PersonalInfo;
|
||||
}
|
||||
}
|
||||
139
src/models/PhysicalInfo.ts
Normal file
139
src/models/PhysicalInfo.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { Model, DataTypes, Sequelize, Optional } from "sequelize";
|
||||
|
||||
export interface PhysicalInfoAttributes {
|
||||
id: string;
|
||||
applicantId: string;
|
||||
|
||||
bloodType?: string;
|
||||
height?: number; // به سانتیمتر
|
||||
weight?: number; // به کیلوگرم
|
||||
bmi?: number;
|
||||
|
||||
hasDisability: boolean;
|
||||
disabilityDescription?: string;
|
||||
|
||||
hasChronicDisease: boolean;
|
||||
chronicDiseaseDescription?: string;
|
||||
|
||||
surgeryHistory?: string;
|
||||
medications?: string;
|
||||
|
||||
specialMark?: string; // علامت مشخصه
|
||||
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export type PhysicalInfoCreationAttributes = Optional<
|
||||
PhysicalInfoAttributes,
|
||||
"id" | "createdAt" | "updatedAt" | "hasDisability" | "hasChronicDisease"
|
||||
>;
|
||||
|
||||
export class PhysicalInfo
|
||||
extends Model<PhysicalInfoAttributes, PhysicalInfoCreationAttributes>
|
||||
implements PhysicalInfoAttributes
|
||||
{
|
||||
public id!: string;
|
||||
public applicantId!: string;
|
||||
|
||||
public bloodType?: string;
|
||||
public height?: number;
|
||||
public weight?: number;
|
||||
public bmi?: number;
|
||||
|
||||
public hasDisability!: boolean;
|
||||
public disabilityDescription?: string;
|
||||
|
||||
public hasChronicDisease!: boolean;
|
||||
public chronicDiseaseDescription?: string;
|
||||
|
||||
public surgeryHistory?: string;
|
||||
public medications?: string;
|
||||
|
||||
public specialMark?: string;
|
||||
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
static initModel(sequelize: Sequelize): typeof PhysicalInfo {
|
||||
PhysicalInfo.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
applicantId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
bloodType: {
|
||||
type: DataTypes.STRING(10),
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
height: {
|
||||
type: DataTypes.FLOAT,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
weight: {
|
||||
type: DataTypes.FLOAT,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
bmi: {
|
||||
type: DataTypes.FLOAT,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
hasDisability: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
disabilityDescription: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
hasChronicDisease: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
chronicDiseaseDescription: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
surgeryHistory: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
medications: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
specialMark: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
}
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "physical_infos",
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
return PhysicalInfo;
|
||||
}
|
||||
}
|
||||
73
src/models/Referral.ts
Normal file
73
src/models/Referral.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { DataTypes, Model, Sequelize } from "sequelize";
|
||||
|
||||
export class Referral extends Model {
|
||||
public id!: string;
|
||||
public applicantId!: string;
|
||||
public firstName!: string;
|
||||
public lastName!: string;
|
||||
public relationship!: string; // نسبت
|
||||
public acquaintanceDuration!: string; // مدت زمان آشنایی (مثلاً: ۵ سال)
|
||||
public acquaintanceType!: "Direct" | "Indirect"; // نوع آشنایی: مستقیم / غیرمستقیم
|
||||
public jobTitle!: string; // شغل معرف
|
||||
public workplaceName!: string; // نام محل کار معرف
|
||||
public phoneNumber!: string; // تلفن تماس
|
||||
|
||||
static initModel(sequelize: Sequelize) {
|
||||
Referral.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
applicantId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: "applicants", // یا هر نامی که برای جدول متقاضی دارید
|
||||
key: "id",
|
||||
},
|
||||
},
|
||||
firstName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
lastName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
relationship: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
acquaintanceDuration: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
acquaintanceType: {
|
||||
type: DataTypes.ENUM("Direct", "Indirect"),
|
||||
allowNull: false,
|
||||
defaultValue: "Direct",
|
||||
},
|
||||
jobTitle: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
workplaceName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
phoneNumber: {
|
||||
type: DataTypes.STRING(15),
|
||||
allowNull: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "referrals",
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
return Referral;
|
||||
}
|
||||
}
|
||||
53
src/models/Relation.ts
Normal file
53
src/models/Relation.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { DataTypes, Model, Sequelize } from "sequelize";
|
||||
|
||||
export class Relation extends Model {
|
||||
static initModel(sequelize: Sequelize) {
|
||||
Relation.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
applicantId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: "applicants",
|
||||
key: "id",
|
||||
},
|
||||
},
|
||||
firstName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
lastName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
relationship: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
jobTitle: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
workplaceName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
phoneNumber: {
|
||||
type: DataTypes.STRING(15),
|
||||
allowNull: false,
|
||||
},
|
||||
order: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false, // عدد 1 برای آشنای اول و 2 برای آشنای دوم
|
||||
},
|
||||
},
|
||||
{ sequelize, tableName: "relations", timestamps: true },
|
||||
);
|
||||
return Relation;
|
||||
}
|
||||
}
|
||||
56
src/models/Role.ts
Normal file
56
src/models/Role.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
// src/models/role.model.ts
|
||||
|
||||
import {
|
||||
Model,
|
||||
DataTypes,
|
||||
Optional,
|
||||
Sequelize
|
||||
} from "sequelize";
|
||||
|
||||
interface RoleAttributes {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
type RoleCreationAttributes = Optional<RoleAttributes, "id">;
|
||||
|
||||
export class Role extends Model<RoleAttributes, RoleCreationAttributes>
|
||||
implements RoleAttributes {
|
||||
public id!: string;
|
||||
public name!: string;
|
||||
public description?: string;
|
||||
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
static initModel(sequelize: Sequelize): typeof Role {
|
||||
Role.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "roles",
|
||||
timestamps:true,
|
||||
}
|
||||
);
|
||||
|
||||
return Role;
|
||||
}
|
||||
}
|
||||
72
src/models/User.ts
Normal file
72
src/models/User.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
// src/models/user.model.ts
|
||||
|
||||
import { Model, DataTypes, Optional, Sequelize } from "sequelize";
|
||||
import { Role } from "./Role";
|
||||
|
||||
interface UserAttributes {
|
||||
id: string;
|
||||
fullname: string;
|
||||
email: string;
|
||||
password: string;
|
||||
roleId: string;
|
||||
isActive: boolean;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
type UserCreationAttributes = Optional<UserAttributes, "id" | "isActive">;
|
||||
|
||||
export class User
|
||||
extends Model<UserAttributes, UserCreationAttributes>
|
||||
implements UserAttributes
|
||||
{
|
||||
public id!: string;
|
||||
public fullname!: string;
|
||||
public email!: string;
|
||||
public password!: string;
|
||||
public roleId!: string;
|
||||
public isActive!: boolean;
|
||||
public role?: Role;
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
static initModel(sequelize: Sequelize): typeof User {
|
||||
User.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
fullname: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
},
|
||||
password: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
roleId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
},
|
||||
isActive: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "users",
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
|
||||
return User;
|
||||
}
|
||||
}
|
||||
25
src/models/WorkExperience.ts
Normal file
25
src/models/WorkExperience.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { DataTypes, Model, Sequelize } from "sequelize";
|
||||
|
||||
export class WorkExperience extends Model {
|
||||
static initModel(sequelize: Sequelize) {
|
||||
WorkExperience.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
applicantId: { type: DataTypes.UUID, allowNull: false },
|
||||
hasNoExperience: { type: DataTypes.BOOLEAN, defaultValue: false },
|
||||
companyName: { type: DataTypes.STRING },
|
||||
lastPosition: { type: DataTypes.STRING },
|
||||
startYear: { type: DataTypes.INTEGER },
|
||||
endYear: { type: DataTypes.INTEGER },
|
||||
leavingReason: { type: DataTypes.STRING },
|
||||
description: { type: DataTypes.TEXT },
|
||||
},
|
||||
{ sequelize, tableName: "work_experiences", timestamps: true },
|
||||
);
|
||||
return WorkExperience;
|
||||
}
|
||||
}
|
||||
228
src/models/index.ts
Normal file
228
src/models/index.ts
Normal file
@@ -0,0 +1,228 @@
|
||||
import { Sequelize } from "sequelize";
|
||||
import { Applicant } from "./Applicant";
|
||||
import { Identity } from "./Identity";
|
||||
import { PersonalInfo } from "./PersonalInfo";
|
||||
import { PhysicalInfo } from "./PhysicalInfo";
|
||||
import { Education } from "./Education";
|
||||
import { Course } from "./Course";
|
||||
import { ComputerSkill } from "./ComputerSkill";
|
||||
import { LanguageSkill } from "./LanguageSkill";
|
||||
import { WorkExperience } from "./WorkExperience";
|
||||
import { Relation } from "./Relation";
|
||||
import { File } from "./File";
|
||||
import { Role } from "./Role";
|
||||
import { User } from "./User";
|
||||
import { Permission } from "./Permission";
|
||||
import { Referral } from "./Referral";
|
||||
import { JobInfo } from "./JobInfo";
|
||||
import { JobRequest } from "./JobRequest";
|
||||
import { Center } from "./Center";
|
||||
import { JobCategory } from "./JobCategory";
|
||||
|
||||
// تنظیمات اتصال به دیتابیس
|
||||
const sequelize = new Sequelize("employee_form", "postgres", "root", {
|
||||
host: "127.0.0.1",
|
||||
dialect: "postgres",
|
||||
logging: false,
|
||||
});
|
||||
|
||||
// ۱. مقداردهی اولیه مدلها (Initialization)
|
||||
const models = {
|
||||
// استخدامی
|
||||
Applicant: Applicant.initModel(sequelize),
|
||||
Center: Center.initModel(sequelize),
|
||||
Identity: Identity.initModel(sequelize),
|
||||
PersonalInfo: PersonalInfo.initModel(sequelize),
|
||||
PhysicalInfo: PhysicalInfo.initModel(sequelize),
|
||||
Education: Education.initModel(sequelize),
|
||||
Course: Course.initModel(sequelize),
|
||||
ComputerSkill: ComputerSkill.initModel(sequelize),
|
||||
LanguageSkill: LanguageSkill.initModel(sequelize),
|
||||
WorkExperience: WorkExperience.initModel(sequelize),
|
||||
JobInfo: JobInfo.initModel(sequelize),
|
||||
Relation: Relation.initModel(sequelize),
|
||||
File: File.initModel(sequelize),
|
||||
User: User.initModel(sequelize),
|
||||
Role: Role.initModel(sequelize),
|
||||
Permission: Permission.initModel(sequelize),
|
||||
Referral: Referral.initModel(sequelize),
|
||||
JobCategory: JobCategory.initModel(sequelize),
|
||||
JobRequest: JobRequest.initModel(sequelize),
|
||||
};
|
||||
// Role.hasMany(User, { foreignKey: "roleId" });
|
||||
// User.belongsTo(Role, { foreignKey: "roleId" });
|
||||
|
||||
// Role.belongsToMany(Permission, {
|
||||
// through: "role_permissions",
|
||||
// foreignKey: "roleId",
|
||||
// });
|
||||
|
||||
// Permission.belongsToMany(Role, {
|
||||
// through: "role_permissions",
|
||||
// foreignKey: "permissionId",
|
||||
// });
|
||||
|
||||
User.belongsTo(Role, {
|
||||
foreignKey: "roleId",
|
||||
as: "role",
|
||||
});
|
||||
|
||||
Role.hasMany(User, {
|
||||
foreignKey: "roleId",
|
||||
as: "users",
|
||||
});
|
||||
|
||||
Role.belongsToMany(Permission, {
|
||||
through: "role_permissions",
|
||||
foreignKey: "roleId",
|
||||
otherKey: "permissionId",
|
||||
as: "permissions",
|
||||
});
|
||||
|
||||
Permission.belongsToMany(Role, {
|
||||
through: "role_permissions",
|
||||
foreignKey: "permissionId",
|
||||
otherKey: "roleId",
|
||||
as: "roles",
|
||||
});
|
||||
Applicant.hasOne(Identity, {
|
||||
foreignKey: "applicantId",
|
||||
as: "identity",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
|
||||
Identity.belongsTo(Applicant, {
|
||||
foreignKey: "applicantId",
|
||||
as: "applicant",
|
||||
});
|
||||
Applicant.hasOne(PersonalInfo, {
|
||||
foreignKey: "applicantId",
|
||||
as: "personalInfo",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
|
||||
PersonalInfo.belongsTo(Applicant, {
|
||||
foreignKey: "applicantId",
|
||||
as: "applicant",
|
||||
});
|
||||
Applicant.hasOne(PhysicalInfo, {
|
||||
foreignKey: "applicantId",
|
||||
as: "physicalInfo",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
|
||||
PhysicalInfo.belongsTo(Applicant, {
|
||||
foreignKey: "applicantId",
|
||||
as: "applicant",
|
||||
});
|
||||
Applicant.hasMany(Education, {
|
||||
foreignKey: "applicantId",
|
||||
as: "educations",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
|
||||
Education.belongsTo(Applicant, {
|
||||
foreignKey: "applicantId",
|
||||
as: "applicant",
|
||||
});
|
||||
Applicant.hasMany(Course, {
|
||||
foreignKey: "applicantId",
|
||||
as: "courses",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
|
||||
Course.belongsTo(Applicant, {
|
||||
foreignKey: "applicantId",
|
||||
as: "applicant",
|
||||
});
|
||||
Applicant.hasMany(ComputerSkill, {
|
||||
foreignKey: "applicantId",
|
||||
as: "computerSkills",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
|
||||
ComputerSkill.belongsTo(Applicant, {
|
||||
foreignKey: "applicantId",
|
||||
as: "applicant",
|
||||
});
|
||||
Applicant.hasMany(LanguageSkill, {
|
||||
foreignKey: "applicantId",
|
||||
as: "languageSkills",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
|
||||
LanguageSkill.belongsTo(Applicant, {
|
||||
foreignKey: "applicantId",
|
||||
as: "applicant",
|
||||
});
|
||||
Applicant.hasMany(WorkExperience, {
|
||||
foreignKey: "applicantId",
|
||||
as: "workExperiences",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
|
||||
WorkExperience.belongsTo(Applicant, {
|
||||
foreignKey: "applicantId",
|
||||
as: "applicant",
|
||||
});
|
||||
Applicant.hasOne(JobInfo, {
|
||||
foreignKey: "applicantId",
|
||||
as: "jobInfo",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
|
||||
JobInfo.belongsTo(Applicant, {
|
||||
foreignKey: "applicantId",
|
||||
as: "applicant",
|
||||
});
|
||||
Applicant.hasMany(Relation, {
|
||||
foreignKey: "applicantId",
|
||||
as: "relations",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
|
||||
Relation.belongsTo(Applicant, {
|
||||
foreignKey: "applicantId",
|
||||
as: "applicant",
|
||||
});
|
||||
Applicant.hasMany(File, {
|
||||
foreignKey: "applicantId",
|
||||
as: "files",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
|
||||
File.belongsTo(Applicant, {
|
||||
foreignKey: "applicantId",
|
||||
as: "applicant",
|
||||
});
|
||||
|
||||
Applicant.hasMany(Referral, { foreignKey: "applicantId", as: "referrals" });
|
||||
Referral.belongsTo(Applicant, { foreignKey: "applicantId" });
|
||||
|
||||
Applicant.hasOne(JobInfo, {
|
||||
foreignKey: "applicantId",
|
||||
as: "workStatus",
|
||||
});
|
||||
JobInfo.belongsTo(Applicant, { foreignKey: "applicantId" });
|
||||
|
||||
// مثال رابطه در فایل اصلی مدلها
|
||||
JobRequest.belongsTo(JobCategory, { foreignKey: "categoryId", as: "category" });
|
||||
// در فایل تعریف روابط:
|
||||
Center.hasMany(JobCategory, { foreignKey: "centerId", as: "categories" });
|
||||
JobCategory.belongsTo(Center, { foreignKey: "centerId" });
|
||||
|
||||
JobCategory.hasMany(JobRequest, { foreignKey: "categoryId" });
|
||||
Applicant.hasMany(JobRequest, { foreignKey: "applicantId", as: "jobRequests" });
|
||||
JobRequest.belongsTo(Applicant, { foreignKey: "applicantId", as: "applicant" });
|
||||
Center.hasMany(JobRequest, { foreignKey: "centerId", as: "applications" });
|
||||
JobRequest.belongsTo(Center, { foreignKey: "centerId", as: "center" });
|
||||
// ۲. برقراری روابط (Associations)
|
||||
// این حلقه متد associate را در هر کلاسی که تعریف شده باشد فراخوانی میکند
|
||||
Object.values(models).forEach((model: any) => {
|
||||
if (typeof model.associate === "function") {
|
||||
model.associate(models);
|
||||
}
|
||||
});
|
||||
|
||||
export { sequelize };
|
||||
export default models;
|
||||
74
src/modules/auth/controller/auth.controller.ts
Normal file
74
src/modules/auth/controller/auth.controller.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { NextFunction } from "express";
|
||||
import { Controller } from "../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../core/types";
|
||||
import AuthService from "../service/auth.service";
|
||||
import { TOKEN_NAME } from "../../../core/constant";
|
||||
import { GlobalErrorMessages } from "../../../core/messages/errors";
|
||||
|
||||
class AuthControllerClass extends Controller {
|
||||
#service;
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = AuthService;
|
||||
}
|
||||
|
||||
// login function for users that controlling this form
|
||||
async userLogin(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const data = await this.#service.usersLogin(
|
||||
req?.body?.email,
|
||||
req?.body?.password,
|
||||
);
|
||||
res.cookie(TOKEN_NAME, data.token, {
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
sameSite: "lax",
|
||||
maxAge: 24 * 60 * 60 * 1000,
|
||||
});
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "با موفقيت وارد شديد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
async applicantLogin(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const data = await this.#service.applicantLogin(
|
||||
req?.body?.nationalCode,
|
||||
);
|
||||
res.cookie(TOKEN_NAME, data.token, {
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
sameSite: "lax",
|
||||
maxAge: 24 * 60 * 60 * 1000,
|
||||
});
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "با موفقيت وارد شديد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
async userLogout(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
res.clearCookie(TOKEN_NAME);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: {},
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(GlobalErrorMessages.server.internal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const AuthController = new AuthControllerClass();
|
||||
|
||||
export default AuthController;
|
||||
14
src/modules/auth/messages/auth.messages.ts
Normal file
14
src/modules/auth/messages/auth.messages.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export const authErrorMessages = Object.freeze({
|
||||
notFound:{
|
||||
user:"كاربر يافت نشد",
|
||||
applicant:"متقاضي يافت نشد"
|
||||
},
|
||||
dosentMatch :{
|
||||
email:"ايميل اشتباه است",
|
||||
password:"پسورد اشتباه است",
|
||||
},
|
||||
login:{
|
||||
invalidData:'ايميل و يا رمز عبور اشتباه است'
|
||||
},
|
||||
logout:'خروج از حساب با خطا مواجه شده است'
|
||||
})
|
||||
13
src/modules/auth/router/auth.routes.ts
Normal file
13
src/modules/auth/router/auth.routes.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Router } from "express";
|
||||
import AuthController from "../controller/auth.controller";
|
||||
|
||||
const AuthRouter = Router();
|
||||
|
||||
// authentication applicants
|
||||
AuthRouter.post("/applicant/login", AuthController.applicantLogin);
|
||||
// AuthRouter.post('/applicant/logout',AuthController.applicantLogout)
|
||||
|
||||
// authentication users
|
||||
AuthRouter.post("/user/login", AuthController.userLogin);
|
||||
AuthRouter.post("/user/logout", AuthController.userLogout);
|
||||
export default AuthRouter;
|
||||
84
src/modules/auth/service/auth.service.ts
Normal file
84
src/modules/auth/service/auth.service.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import createHttpError from "http-errors";
|
||||
import { Controller } from "../../../core/controller/main.controller";
|
||||
import { User } from "../../../models/User";
|
||||
import bcrypt from "bcryptjs";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { Role } from "../../../models/Role";
|
||||
import { Applicant } from "../../../models/Applicant";
|
||||
import { Identity } from "../../../models/Identity";
|
||||
import { authErrorMessages } from "../messages/auth.messages";
|
||||
class AuthServiceClass extends Controller {
|
||||
async usersLogin(email: string, password: string) {
|
||||
try {
|
||||
// ۱. پیدا کردن کاربر به همراه نقش
|
||||
const user = await User.findOne({
|
||||
where: { email },
|
||||
include: [
|
||||
{
|
||||
model: Role,
|
||||
as: "role",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!user)
|
||||
throw new createHttpError.Unauthorized("ایمیل یا رمز عبور اشتباه است.");
|
||||
|
||||
// ۲. چک کردن پسورد
|
||||
const isMatch = await bcrypt.compare(password, user.password);
|
||||
if (!isMatch)
|
||||
throw new createHttpError.Unauthorized("ایمیل یا رمز عبور اشتباه است.");
|
||||
|
||||
// ۳. صدور توکن
|
||||
const token = jwt.sign(
|
||||
{ userId: user.id },
|
||||
process.env.JWT_SECRET || "secret",
|
||||
{ expiresIn: "24h" }, // طول عمر توکن
|
||||
);
|
||||
|
||||
return {
|
||||
token,
|
||||
user: { id: user.id, fullname: user.fullname, role: user },
|
||||
};
|
||||
} catch (err) {
|
||||
throw new createHttpError.InternalServerError("خطای سرور");
|
||||
}
|
||||
}
|
||||
|
||||
async applicantLogin(nationalCode: string) {
|
||||
try {
|
||||
const identity = await Identity.findOne({
|
||||
where: { nationalCode },
|
||||
include: [
|
||||
{
|
||||
model: Applicant,
|
||||
as: "applicant",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!identity?.applicantId) {
|
||||
throw new createHttpError.NotFound(
|
||||
authErrorMessages.notFound.applicant,
|
||||
);
|
||||
}
|
||||
|
||||
const token = jwt.sign(
|
||||
{ userId: identity.applicantId },
|
||||
process.env.JWT_SECRET || "secret",
|
||||
{ expiresIn: "24h" }, // طول عمر توکن
|
||||
);
|
||||
|
||||
return {
|
||||
token,
|
||||
applicant: { id: identity.applicantId, fullname: `${identity.firstName} ${identity.lastName}`, role: identity },
|
||||
};
|
||||
} catch (error) {
|
||||
throw new createHttpError.InternalServerError("خطای سرور");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const AuthService = new AuthServiceClass();
|
||||
|
||||
export default AuthService;
|
||||
0
src/modules/file/controller/file.controller.ts
Normal file
0
src/modules/file/controller/file.controller.ts
Normal file
0
src/modules/file/service/file.service.ts
Normal file
0
src/modules/file/service/file.service.ts
Normal file
81
src/modules/forms/center/controller/center.controller.ts
Normal file
81
src/modules/forms/center/controller/center.controller.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { NextFunction } from "express";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../../core/types";
|
||||
import CenterService from "../service/center.service";
|
||||
|
||||
class CenterControllerClass extends Controller {
|
||||
#service;
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = CenterService;
|
||||
}
|
||||
async getAll(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const centers = await this.#service.getAll();
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: centers,
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
async getById(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const id = req?.params?.id;
|
||||
|
||||
const centers = await this.#service.getById(id);
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: centers,
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
await this.#service.create(req.body);
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: {},
|
||||
message: "ساخته شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const id = req?.params?.id;
|
||||
await this.#service.update(id, req.body);
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: {},
|
||||
message: "ويرايش شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
async delete(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const id = req?.params?.id;
|
||||
|
||||
await this.#service.delete(id);
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: {},
|
||||
message: "حذف شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CenterController = new CenterControllerClass();
|
||||
|
||||
export default CenterController;
|
||||
12
src/modules/forms/center/routes/center.routes.ts
Normal file
12
src/modules/forms/center/routes/center.routes.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Router } from "express";
|
||||
import CenterController from "../controller/center.controller";
|
||||
|
||||
const CenterRouter = Router();
|
||||
|
||||
CenterRouter.post("/", CenterController.create); // ایجاد
|
||||
CenterRouter.get("/", CenterController.getAll); // لیست همه
|
||||
CenterRouter.get("/:id", CenterController.getById); // مشاهده یکی
|
||||
CenterRouter.put("/:id", CenterController.update); // ویرایش
|
||||
CenterRouter.delete("/:id", CenterController.delete); // حذف
|
||||
|
||||
export default CenterRouter;
|
||||
51
src/modules/forms/center/service/center.service.ts
Normal file
51
src/modules/forms/center/service/center.service.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import createHttpError from "http-errors";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { GlobalErrorMessages } from "../../../../core/messages/errors";
|
||||
import { Center } from "../../../../models/Center";
|
||||
import { JobCategory } from "../../../../models/JobCategory";
|
||||
|
||||
class CenterServiceClass extends Controller {
|
||||
// ایجاد مرکز جدید
|
||||
async create(data: any) {
|
||||
return await Center.create(data);
|
||||
}
|
||||
|
||||
// دریافت لیست تمام مراکز (همراه با رستههای شغلی مرتبط)
|
||||
async getAll() {
|
||||
return await Center.findAll({
|
||||
include: [{ model: JobCategory, as: "categories" }],
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
}
|
||||
|
||||
// دریافت یک مرکز خاص با ID
|
||||
async getById(id: string) {
|
||||
const center = await Center.findByPk(id, {
|
||||
include: [{ model: JobCategory, as: "categories" }],
|
||||
});
|
||||
if (!center) throw new Error("مرکز مورد نظر یافت نشد.");
|
||||
return center;
|
||||
}
|
||||
|
||||
// بروزرسانی اطلاعات مرکز
|
||||
async update(id: string, data: any) {
|
||||
const center = await this.getById(id);
|
||||
return await center.update(data);
|
||||
}
|
||||
|
||||
// حذف مرکز
|
||||
async delete(id: string) {
|
||||
const center = await this.getById(id);
|
||||
await center.destroy();
|
||||
return { message: "مرکز با موفقیت حذف شد." };
|
||||
}
|
||||
|
||||
// متد کمکی برای گرفتن مراکز فوری (Urgent)
|
||||
async getUrgentCenters() {
|
||||
return await Center.findAll({ where: { isUrgent: true } });
|
||||
}
|
||||
}
|
||||
|
||||
const CenterService = new CenterServiceClass();
|
||||
|
||||
export default CenterService;
|
||||
61
src/modules/forms/course/controller/courseForm.controller.ts
Normal file
61
src/modules/forms/course/controller/courseForm.controller.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../../core/types";
|
||||
import { NextFunction } from "express";
|
||||
import CourseInfoService from "../service/courseForm.service";
|
||||
|
||||
class CourseInfoControllerClass extends Controller {
|
||||
#service;
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = CourseInfoService;
|
||||
}
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const course = await this.#service.create(req.body);
|
||||
res.status(201).json({ status: 200, data: course });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
async getById(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const course = await this.#service.getById(req.params.id);
|
||||
res.json({ status: 200, data: course });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
async list(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { applicantId } = req.query as { applicantId?: string };
|
||||
const courses = await this.#service.list(applicantId);
|
||||
res.json({ status: 200, data: courses });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const updated = await this.#service.update(req.params.id, req.body);
|
||||
res.json({ status: 200, data: updated });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
async delete(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const result = await this.#service.delete(req.params.id);
|
||||
res.json({ status: 200, data: result });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CourseInfoController = new CourseInfoControllerClass();
|
||||
|
||||
export default CourseInfoController;
|
||||
11
src/modules/forms/course/index.routes.ts
Normal file
11
src/modules/forms/course/index.routes.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Router } from "express";
|
||||
import CourseInfoController from "./controller/courseForm.controller";
|
||||
|
||||
const CourseInfoRouter = Router();
|
||||
CourseInfoRouter.post("/create", CourseInfoController.create);
|
||||
// CourseInfoRouter.get("/all", CourseInfoController.getAll);
|
||||
CourseInfoRouter.get("/get/:id", CourseInfoController.getById);
|
||||
CourseInfoRouter.put("/update/:id", CourseInfoController.update);
|
||||
CourseInfoRouter.delete("/remove/:id", CourseInfoController.delete);
|
||||
|
||||
export default CourseInfoRouter;
|
||||
49
src/modules/forms/course/service/courseForm.service.ts
Normal file
49
src/modules/forms/course/service/courseForm.service.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import createHttpError from "http-errors";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { Course } from "../../../../models/Course";
|
||||
|
||||
export interface CoursePayload {
|
||||
applicantId: string;
|
||||
degree: string;
|
||||
field: string;
|
||||
university: string;
|
||||
startYear?: number | null;
|
||||
endYear?: number | null;
|
||||
gpa?: number | null;
|
||||
description?: string | null;
|
||||
certificateImageId?: string | null;
|
||||
}
|
||||
|
||||
class CourseServiceClass extends Controller {
|
||||
async create(data: any) {
|
||||
return await Course.create(data);
|
||||
}
|
||||
|
||||
async getById(id: string) {
|
||||
const course = await Course.findByPk(id);
|
||||
if (!course) throw new createHttpError.NotFound("دوره یافت نشد");
|
||||
return course;
|
||||
}
|
||||
|
||||
async list(applicantId?: string) {
|
||||
const where = applicantId ? { applicantId } : {};
|
||||
return await Course.findAll({ where });
|
||||
}
|
||||
|
||||
async update(id: string, data: any) {
|
||||
const [affectedRows] = await Course.update(data, { where: { id } });
|
||||
if (affectedRows === 0)
|
||||
throw new createHttpError.NotFound("دوره برای ویرایش یافت نشد");
|
||||
return await Course.findByPk(id);
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
const deleted = await Course.destroy({ where: { id } });
|
||||
if (!deleted) throw new createHttpError.NotFound("دوره برای حذف یافت نشد");
|
||||
return { message: "دوره با موفقیت حذف شد" };
|
||||
}
|
||||
}
|
||||
|
||||
const CourseService = new CourseServiceClass();
|
||||
|
||||
export default CourseService;
|
||||
0
src/modules/forms/course/validation/index.ts
Normal file
0
src/modules/forms/course/validation/index.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../../core/types";
|
||||
import { NextFunction } from "express";
|
||||
import EducationInfoService from "../service/educationForm.service";
|
||||
|
||||
class EducationInfoControllerClass extends Controller {
|
||||
#service;
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = EducationInfoService;
|
||||
}
|
||||
// POST /educations
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const result = await this.#service.create(req.body);
|
||||
res.status(201).json({ status: 201, data: result });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
// GET /educations/:id
|
||||
async getById(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const result = await this.#service.getById(id);
|
||||
res.json({ status: 201, data: result });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
// GET /educations?applicantId=...
|
||||
async list(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { applicantId } = req.query;
|
||||
const result = await this.#service.list({
|
||||
applicantId: applicantId as string | undefined,
|
||||
});
|
||||
res.json({ status: 201, data: result });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT /educations/:id
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const result = await this.#service.update(id, req.body);
|
||||
res.json({ status: 201, data: result });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE /educations/:id
|
||||
async delete(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const result = await this.#service.delete(id);
|
||||
res.json({ status: 201, data: result });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const EducationInfoController = new EducationInfoControllerClass();
|
||||
|
||||
export default EducationInfoController;
|
||||
11
src/modules/forms/education/index.routes.ts
Normal file
11
src/modules/forms/education/index.routes.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Router } from "express";
|
||||
import EducationInfoController from "./controller/educationForm.controller";
|
||||
|
||||
const EducationInfoRouter = Router();
|
||||
EducationInfoRouter.post("/create", EducationInfoController.create);
|
||||
// EducationInfoRouter.get("/all", EducationInfoController.getAll);
|
||||
EducationInfoRouter.get("/get/:id", EducationInfoController.getById);
|
||||
EducationInfoRouter.put("/update/:id", EducationInfoController.update);
|
||||
EducationInfoRouter.delete("/remove/:id", EducationInfoController.delete);
|
||||
|
||||
export default EducationInfoRouter;
|
||||
120
src/modules/forms/education/service/educationForm.service.ts
Normal file
120
src/modules/forms/education/service/educationForm.service.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import createHttpError from "http-errors";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { Education } from "../../../../models/Education";
|
||||
|
||||
|
||||
export interface EducationPayload {
|
||||
applicantId: string;
|
||||
degree: string;
|
||||
field: string;
|
||||
university: string;
|
||||
startYear?: number | null;
|
||||
endYear?: number | null;
|
||||
gpa?: number | null;
|
||||
description?: string | null;
|
||||
certificateImageId?: string | null;
|
||||
}
|
||||
|
||||
class EducationServiceClass extends Controller {
|
||||
// ایجاد رکورد جدید
|
||||
async create(data: EducationPayload) {
|
||||
try {
|
||||
const education = await Education.create({
|
||||
applicantId: data.applicantId,
|
||||
degree: data.degree,
|
||||
field: data.field,
|
||||
university: data.university,
|
||||
startYear: data.startYear ?? undefined,
|
||||
endYear: data.endYear ?? undefined,
|
||||
gpa: data.gpa ?? undefined,
|
||||
description: data.description ?? "",
|
||||
certificateImageId: data.certificateImageId ?? undefined,
|
||||
});
|
||||
|
||||
return education;
|
||||
} catch (err) {
|
||||
throw new createHttpError.InternalServerError(
|
||||
"خطا در ایجاد سابقه تحصیلی",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// گرفتن یک رکورد با id
|
||||
async getById(id: string) {
|
||||
const education = await Education.findByPk(id);
|
||||
|
||||
if (!education) {
|
||||
throw new createHttpError.NotFound("سابقه تحصیلی پیدا نشد");
|
||||
}
|
||||
|
||||
return education;
|
||||
}
|
||||
|
||||
// لیست سوابق، مثلا بر اساس applicantId
|
||||
async list(filter?: { applicantId?: string }) {
|
||||
const where: any = {};
|
||||
|
||||
if (filter?.applicantId) {
|
||||
where.applicantId = filter.applicantId;
|
||||
}
|
||||
|
||||
const list = await Education.findAll({ where });
|
||||
return list;
|
||||
}
|
||||
|
||||
// بروزرسانی
|
||||
async update(id: string, data: Partial<EducationPayload>) {
|
||||
const education = await Education.findByPk(id);
|
||||
|
||||
if (!education) {
|
||||
throw new createHttpError.NotFound("سابقه تحصیلی پیدا نشد");
|
||||
}
|
||||
|
||||
try {
|
||||
await education.update({
|
||||
applicantId: data.applicantId ?? education.applicantId,
|
||||
degree: data.degree ?? education.degree,
|
||||
field: data.field ?? education.field,
|
||||
university: data.university ?? education.university,
|
||||
startYear:
|
||||
typeof data.startYear === "number"
|
||||
? data.startYear
|
||||
: education.startYear,
|
||||
endYear:
|
||||
typeof data.endYear === "number" ? data.endYear : education.endYear,
|
||||
gpa: typeof data.gpa === "number" ? data.gpa : education.gpa,
|
||||
description: data.description ?? education.description,
|
||||
certificateImageId:
|
||||
data.certificateImageId ?? education.certificateImageId,
|
||||
});
|
||||
|
||||
return education;
|
||||
} catch (err) {
|
||||
throw new createHttpError.InternalServerError(
|
||||
"خطا در بروزرسانی سابقه تحصیلی",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// حذف
|
||||
async delete(id: string) {
|
||||
const education = await Education.findByPk(id);
|
||||
|
||||
if (!education) {
|
||||
throw new createHttpError.NotFound("سابقه تحصیلی پیدا نشد");
|
||||
}
|
||||
|
||||
try {
|
||||
await education.destroy();
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
throw new createHttpError.InternalServerError(
|
||||
"خطا در حذف سابقه تحصیلی",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const EducationService = new EducationServiceClass();
|
||||
|
||||
export default EducationService;
|
||||
0
src/modules/forms/education/validation/index.ts
Normal file
0
src/modules/forms/education/validation/index.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { NextFunction } from "express";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../../core/types";
|
||||
import IdentityFormService from "../service/identityForm.service";
|
||||
|
||||
class IdentityFormControllerClass extends Controller {
|
||||
#service;
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = IdentityFormService;
|
||||
}
|
||||
|
||||
async getById(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const id = req?.params?.id;
|
||||
const data = await this.#service.getById(id);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
// create and import identity form (step 1 in system)
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
await this.#service.create(req.body);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: {},
|
||||
message: "ثبت شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
// update identity form
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const id = req?.params?.id;
|
||||
await this.#service.update(id, req.body);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: {},
|
||||
message: "ويرايش شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const IdentityFormController = new IdentityFormControllerClass();
|
||||
|
||||
export default IdentityFormController;
|
||||
11
src/modules/forms/identity/index.routes.ts
Normal file
11
src/modules/forms/identity/index.routes.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Router } from "express";
|
||||
import IdentityFormController from "./controller/identityForm.controller";
|
||||
|
||||
const identityRouter = Router();
|
||||
|
||||
identityRouter.post("/create", IdentityFormController.create);
|
||||
// identityRouter.get("/all", IdentityFormController.getAll);
|
||||
identityRouter.get("/get/:id", IdentityFormController.getById);
|
||||
identityRouter.put("/update/:id", IdentityFormController.update);
|
||||
// identityRouter.delete("/remove/:id", IdentityFormController.remove);
|
||||
export default identityRouter;
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
86
src/modules/forms/identity/service/identityForm.service.ts
Normal file
86
src/modules/forms/identity/service/identityForm.service.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import createHttpError from "http-errors";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { GlobalErrorMessages } from "../../../../core/messages/errors";
|
||||
import { Identity } from "../../../../models/Identity";
|
||||
import { Applicant } from "../../../../models/Applicant";
|
||||
import { UUIDV4 } from "sequelize";
|
||||
import { Center } from "../../../../models/Center";
|
||||
|
||||
class IdentityFormServiceClass extends Controller {
|
||||
async getById(id: string) {
|
||||
try {
|
||||
const data = await Identity.findOne({ where: { id } });
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
throw new createHttpError.InternalServerError(
|
||||
GlobalErrorMessages.server.internal,
|
||||
);
|
||||
}
|
||||
}
|
||||
async create(data: any) {
|
||||
let applicantId = null;
|
||||
|
||||
try {
|
||||
applicantId = await Applicant.create({
|
||||
id: UUIDV4().toString({}),
|
||||
});
|
||||
} catch (error) {
|
||||
throw new createHttpError.InternalServerError(
|
||||
GlobalErrorMessages.server.internal,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await Identity.create({
|
||||
applicantId: applicantId.id,
|
||||
birthDate: data.birthDate,
|
||||
birthPlace: data.birthPlace,
|
||||
fatherName: data.fatherName,
|
||||
firstName: data.firstName,
|
||||
nationalCode: data.nationalCode,
|
||||
gender: data.gender,
|
||||
lastName: data.lastName,
|
||||
nationality: data.nationality,
|
||||
religion: data.religion,
|
||||
profilePhotoId: data.profilePhotoId,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new createHttpError.InternalServerError(
|
||||
GlobalErrorMessages.server.internal,
|
||||
);
|
||||
}
|
||||
}
|
||||
async update(formID: string, data: any) {
|
||||
const [affectedRows] = await Identity.update(
|
||||
{
|
||||
birthDate: data.birthDate,
|
||||
birthPlace: data.birthPlace,
|
||||
fatherName: data.fatherName,
|
||||
firstName: data.firstName,
|
||||
nationalCode: data.nationalCode,
|
||||
gender: data.gender,
|
||||
lastName: data.lastName,
|
||||
nationality: data.nationality,
|
||||
religion: data.religion,
|
||||
profilePhotoId: data.profilePhotoId,
|
||||
},
|
||||
{
|
||||
where: {
|
||||
id: formID, // اعمال شرط برای آپدیت فقط همین فرم خاص
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// بررسی اینکه آیا ردیفی پیدا و آپدیت شده است یا خیر
|
||||
if (affectedRows === 0) {
|
||||
throw new createHttpError.NotFound("فرم پیدا نشد");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const IdentityFormService = new IdentityFormServiceClass();
|
||||
|
||||
export default IdentityFormService;
|
||||
16
src/modules/forms/index.routes.ts
Normal file
16
src/modules/forms/index.routes.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Router } from "express";
|
||||
import PersonalInfoRouter from "./personalInfo/index.routes";
|
||||
import identityRouter from "./identity/index.routes";
|
||||
import PhysicalInfoRouter from "./physicalInfo/index.routes";
|
||||
import EducationInfoRouter from "./education/index.routes";
|
||||
import CourseInfoRouter from "./course/index.routes";
|
||||
|
||||
const formRouter = Router();
|
||||
|
||||
formRouter.use("/identity", identityRouter);
|
||||
formRouter.use("/personal-info", PersonalInfoRouter);
|
||||
formRouter.use("/physical-info", PhysicalInfoRouter);
|
||||
formRouter.use("/education", EducationInfoRouter);
|
||||
formRouter.use("/course", CourseInfoRouter);
|
||||
|
||||
export default formRouter;
|
||||
@@ -0,0 +1,79 @@
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../../core/types";
|
||||
import { NextFunction } from "express";
|
||||
import PersonalInfoService from "../service/personalInfoForm.service";
|
||||
|
||||
class PersonalInfoControllerClass extends Controller {
|
||||
#service;
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = PersonalInfoService;
|
||||
}
|
||||
// POST /personal-infos
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const result = await this.#service.create(req.body);
|
||||
res.status(201).json({ status: 201, data: result });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// GET /personal-infos/:id
|
||||
async getById(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const result = await this.#service.findById(id);
|
||||
res.status(200).json({ status: 200, data: result });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// GET /personal-infos
|
||||
async getAll(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const result = await this.#service.findAll();
|
||||
res.status(200).json({ status: 200, data: result });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// GET /personal-infos/applicant/:applicantId
|
||||
async getByApplicantId(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { applicantId } = req.params;
|
||||
const result = await this.#service.findByApplicantId(applicantId);
|
||||
res.status(200).json({ status: 200, data: result });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT /personal-infos/:id
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const result = await this.#service.update(id, req.body);
|
||||
res.status(200).json({ status: 200, data: result });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE /personal-infos/:id
|
||||
async remove(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const result = await this.#service.delete(id);
|
||||
res.status(200).json({ status: 200, data: result });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PersonalInfoController = new PersonalInfoControllerClass();
|
||||
|
||||
export default PersonalInfoController;
|
||||
11
src/modules/forms/personalInfo/index.routes.ts
Normal file
11
src/modules/forms/personalInfo/index.routes.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Router } from "express";
|
||||
import PersonalInfoController from "./controller/personalInfoForm.controller";
|
||||
|
||||
const PersonalInfoRouter = Router();
|
||||
PersonalInfoRouter.post("/create", PersonalInfoController.create);
|
||||
PersonalInfoRouter.get("/all", PersonalInfoController.getAll);
|
||||
PersonalInfoRouter.get("/get/:id", PersonalInfoController.getById);
|
||||
PersonalInfoRouter.put("/update/:id", PersonalInfoController.update);
|
||||
PersonalInfoRouter.delete("/remove/:id", PersonalInfoController.remove);
|
||||
|
||||
export default PersonalInfoRouter;
|
||||
@@ -0,0 +1,119 @@
|
||||
import createHttpError from "http-errors";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { PersonalInfo } from "../../../../models/PersonalInfo";
|
||||
|
||||
class PersonalInfoServiceClass extends Controller {
|
||||
async create(data: any) {
|
||||
try {
|
||||
const record = await PersonalInfo.create({
|
||||
applicantId: data.applicantId,
|
||||
maritalStatus: data.maritalStatus,
|
||||
militaryStatus: data.militaryStatus,
|
||||
fatherEducation: data.fatherEducation,
|
||||
fatherJob: data.fatherJob,
|
||||
motherEducation: data.motherEducation,
|
||||
motherJob: data.motherJob,
|
||||
housingStatus: data.housingStatus,
|
||||
city: data.city,
|
||||
address: data.address,
|
||||
homePhone: data.homePhone,
|
||||
mobilePhone: data.mobilePhone,
|
||||
emergencyPhone: data.emergencyPhone,
|
||||
email: data.email,
|
||||
residenceDuration: data.residenceDuration,
|
||||
isVeteran: data.isVeteran,
|
||||
hasCriminalRecord: data.hasCriminalRecord,
|
||||
criminalDescription: data.criminalDescription,
|
||||
});
|
||||
|
||||
return record;
|
||||
} catch (error: any) {
|
||||
throw new createHttpError.InternalServerError(
|
||||
error.message || "خطا در ایجاد اطلاعات شخصی",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async findById(id: string) {
|
||||
const record = await PersonalInfo.findByPk(id);
|
||||
|
||||
if (!record) {
|
||||
throw new createHttpError.NotFound("اطلاعات شخصی پیدا نشد");
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
// اگر لازم داری همه رکوردها رو بگیری
|
||||
async findAll() {
|
||||
const records = await PersonalInfo.findAll();
|
||||
return records;
|
||||
}
|
||||
|
||||
// مثلا گرفتن بر اساس applicantId
|
||||
async findByApplicantId(applicantId: string) {
|
||||
const record = await PersonalInfo.findOne({
|
||||
where: { applicantId },
|
||||
});
|
||||
|
||||
if (!record) {
|
||||
throw new createHttpError.NotFound(
|
||||
"اطلاعات شخصی برای این متقاضی پیدا نشد",
|
||||
);
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
async update(id: string, data: any) {
|
||||
const [affectedRows] = await PersonalInfo.update(
|
||||
{
|
||||
applicantId: data.applicantId,
|
||||
maritalStatus: data.maritalStatus,
|
||||
militaryStatus: data.militaryStatus,
|
||||
fatherEducation: data.fatherEducation,
|
||||
fatherJob: data.fatherJob,
|
||||
motherEducation: data.motherEducation,
|
||||
motherJob: data.motherJob,
|
||||
housingStatus: data.housingStatus,
|
||||
city: data.city,
|
||||
address: data.address,
|
||||
homePhone: data.homePhone,
|
||||
mobilePhone: data.mobilePhone,
|
||||
emergencyPhone: data.emergencyPhone,
|
||||
email: data.email,
|
||||
residenceDuration: data.residenceDuration,
|
||||
isVeteran: data.isVeteran,
|
||||
hasCriminalRecord: data.hasCriminalRecord,
|
||||
criminalDescription: data.criminalDescription,
|
||||
},
|
||||
{
|
||||
where: { id },
|
||||
},
|
||||
);
|
||||
|
||||
if (affectedRows === 0) {
|
||||
throw new createHttpError.NotFound("اطلاعات شخصی برای ویرایش پیدا نشد");
|
||||
}
|
||||
|
||||
// برگردوندن رکورد آپدیتشده
|
||||
const updated = await PersonalInfo.findByPk(id);
|
||||
return updated;
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
const deletedCount = await PersonalInfo.destroy({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (deletedCount === 0) {
|
||||
throw new createHttpError.NotFound("اطلاعات شخصی برای حذف پیدا نشد");
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
|
||||
const PersonalInfoService = new PersonalInfoServiceClass();
|
||||
|
||||
export default PersonalInfoService;
|
||||
0
src/modules/forms/personalInfo/validation/index.ts
Normal file
0
src/modules/forms/personalInfo/validation/index.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../../core/types";
|
||||
import { NextFunction } from "express";
|
||||
import PhysicalInfoService from "../service/physicalInfoForm.service";
|
||||
|
||||
class PhysicalInfoControllerClass extends Controller {
|
||||
#service;
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = PhysicalInfoService;
|
||||
}
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const info = await this.#service.create(req.body);
|
||||
res.status(201).json({ status: 201, data: info });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getById(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const info = await this.#service.getById(req.params.id);
|
||||
res.json({ status: 200, data: info });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const updatedInfo = await this.#service.update(req.params.id, req.body);
|
||||
res.json({ status: 200, data: updatedInfo });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async delete(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const result = await this.#service.delete(req.params.id);
|
||||
res.json({ status: 200, data: result });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PhysicalInfoController = new PhysicalInfoControllerClass();
|
||||
|
||||
export default PhysicalInfoController;
|
||||
11
src/modules/forms/physicalInfo/index.routes.ts
Normal file
11
src/modules/forms/physicalInfo/index.routes.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Router } from "express";
|
||||
import PhysicalInfoController from "./controller/physicalInfoForm.controller";
|
||||
|
||||
const PhysicalInfoRouter = Router();
|
||||
PhysicalInfoRouter.post("/create", PhysicalInfoController.create);
|
||||
// PhysicalInfoRouter.get("/all", PhysicalInfoController.getAll);
|
||||
PhysicalInfoRouter.get("/get/:id", PhysicalInfoController.getById);
|
||||
PhysicalInfoRouter.put("/update/:id", PhysicalInfoController.update);
|
||||
PhysicalInfoRouter.delete("/remove/:id", PhysicalInfoController.delete);
|
||||
|
||||
export default PhysicalInfoRouter;
|
||||
@@ -0,0 +1,43 @@
|
||||
import createHttpError from "http-errors";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { PhysicalInfo } from "../../../../models/PhysicalInfo";
|
||||
|
||||
class PhysicalInfoServiceClass extends Controller {
|
||||
async create(data: any) {
|
||||
return await PhysicalInfo.create(data);
|
||||
}
|
||||
|
||||
async getById(id: string) {
|
||||
const info = await PhysicalInfo.findByPk(id);
|
||||
if (!info) throw new createHttpError.NotFound("اطلاعات جسمانی یافت نشد");
|
||||
return info;
|
||||
}
|
||||
|
||||
async update(id: string, data: any) {
|
||||
const [affectedRows] = await PhysicalInfo.update(data, {
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (affectedRows === 0) {
|
||||
throw new createHttpError.NotFound("اطلاعات جسمانی برای ویرایش یافت نشد");
|
||||
}
|
||||
|
||||
return await PhysicalInfo.findByPk(id);
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
const deleted = await PhysicalInfo.destroy({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!deleted) {
|
||||
throw new createHttpError.NotFound("اطلاعات جسمانی برای حذف یافت نشد");
|
||||
}
|
||||
|
||||
return { message: "با موفقیت حذف شد" };
|
||||
}
|
||||
}
|
||||
|
||||
const PhysicalInfoService = new PhysicalInfoServiceClass();
|
||||
|
||||
export default PhysicalInfoService;
|
||||
0
src/modules/forms/physicalInfo/validation/index.ts
Normal file
0
src/modules/forms/physicalInfo/validation/index.ts
Normal file
11
tsconfig.json
Normal file
11
tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2016",
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
|
||||
"outDir": "dist"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user