Role nd token Based Routing completed for Project,trash,home which is in Controller. Token, Auth Purpose,Rolebased middlewares created. Auth API,Project token Based API, Home Token Based API, Trash Token Based API In v1 AuthRoutes

This commit is contained in:
2025-05-19 16:06:09 +05:30
parent 2aa8c479fa
commit ac8de5d33d
28 changed files with 1748 additions and 224 deletions

12
.env
View File

@@ -18,3 +18,15 @@ MinIO_URL=185.100.212.76
MinIO_PORT=9999
MinIO_accessKey=sabarinathan
MinIO_secretKey=sabarinathan
# token
JWT_SECRET="DwinzoProject"
REFRESH_JWT_SECRET="RefreshDwinzoProject"
# redis
REDIS_ENV= true
REDIS_LOCAL =127.0.0.1
REDIS_PORT=6379
# REDIS_DOCKER =185.100.212.76
# REDIS_PORT=6666

119
package-lock.json generated
View File

@@ -14,11 +14,13 @@
"express": "^4.21.1",
"fs": "^0.0.1-security",
"http": "^0.0.1-security",
"ioredis": "^5.6.1",
"ip": "^2.0.1",
"jsonwebtoken": "^9.0.2",
"minio": "^8.0.5",
"mongoose": "^8.8.1",
"multer": "^1.4.5-lts.2",
"nodemailer": "^7.0.3",
"socket.io": "^4.8.1",
"swagger-autogen": "^2.23.7",
"swagger-ui-express": "^5.0.1"
@@ -32,6 +34,7 @@
"@types/mime-types": "^2.1.4",
"@types/multer": "^1.4.12",
"@types/node": "^22.9.0",
"@types/nodemailer": "^6.4.17",
"@types/swagger-ui-express": "^4.1.7",
"nodemon": "^3.1.7",
"ts-node": "^10.9.2",
@@ -50,6 +53,11 @@
"node": ">=12"
}
},
"node_modules/@ioredis/commands": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
@@ -239,6 +247,15 @@
"undici-types": "~6.20.0"
}
},
"node_modules/@types/nodemailer": {
"version": "6.4.17",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz",
"integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/qs": {
"version": "6.9.18",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
@@ -589,6 +606,14 @@
"fsevents": "~2.3.2"
}
},
"node_modules/cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -730,6 +755,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"engines": {
"node": ">=0.10"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -1235,6 +1268,50 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ioredis": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz",
"integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==",
"dependencies": {
"@ioredis/commands": "^1.1.1",
"cluster-key-slot": "^1.1.0",
"debug": "^4.3.4",
"denque": "^2.1.0",
"lodash.defaults": "^4.2.0",
"lodash.isarguments": "^3.1.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0",
"standard-as-callback": "^2.1.0"
},
"engines": {
"node": ">=12.22.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/ioredis"
}
},
"node_modules/ioredis/node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/ioredis/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/ip": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
@@ -1438,11 +1515,21 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
},
"node_modules/lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
@@ -1758,6 +1845,14 @@
"node": ">= 0.6"
}
},
"node_modules/nodemailer": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.3.tgz",
"integrity": "sha512-Ajq6Sz1x7cIK3pN6KesGTah+1gnwMnx5gKl3piQlQQE/PwyJ4Mbc8is2psWYxK3RJTVeqsDaCv8ZzXLCDHMTZw==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/nodemon": {
"version": "3.1.9",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
@@ -2006,6 +2101,25 @@
"node": ">=8.10.0"
}
},
"node_modules/redis-errors": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
"engines": {
"node": ">=4"
}
},
"node_modules/redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
"dependencies": {
"redis-errors": "^1.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -2335,6 +2449,11 @@
"node": ">=6"
}
},
"node_modules/standard-as-callback": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",

View File

@@ -18,11 +18,13 @@
"express": "^4.21.1",
"fs": "^0.0.1-security",
"http": "^0.0.1-security",
"ioredis": "^5.6.1",
"ip": "^2.0.1",
"jsonwebtoken": "^9.0.2",
"minio": "^8.0.5",
"mongoose": "^8.8.1",
"multer": "^1.4.5-lts.2",
"nodemailer": "^7.0.3",
"socket.io": "^4.8.1",
"swagger-autogen": "^2.23.7",
"swagger-ui-express": "^5.0.1"
@@ -36,6 +38,7 @@
"@types/mime-types": "^2.1.4",
"@types/multer": "^1.4.12",
"@types/node": "^22.9.0",
"@types/nodemailer": "^6.4.17",
"@types/swagger-ui-express": "^4.1.7",
"nodemon": "^3.1.7",
"ts-node": "^10.9.2",

View File

@@ -0,0 +1,245 @@
import { Request, Response } from "express";
import {
AuthLogin,
AuthLogout,
AuthSignup,
forgetPassword,
} from "../../../../shared/services/auth/authServices.ts";
export const SignupController = async (
req: Request,
res: Response
): Promise<void> => {
try {
console.log("req.body: ", req.body);
const { userName, Email, Password, profilePicture } = req.body;
if (!userName || !Email || !Password) {
res.status(400).json({
message: "All fields are required",
});
return;
}
const result = await AuthSignup(req.body);
switch (result.status) {
case "User already exists":
res.status(403).json({
message: "User already exists",
});
break;
case "Success":
res.status(201).json({
message: "New User created",
});
break;
default:
res.status(500).json({
message: "Internal server error",
});
break;
}
} catch (error) {
res.status(500).json({
message: "An unexpected error occurred",
});
return;
}
};
export const SignInController = async (
req: Request,
res: Response
): Promise<void> => {
try {
const { Email, Password, fingerprint } = req.body;
if (!fingerprint || !Email || !Password) {
res.status(400).json({
message: "All fields are required",
});
return;
}
const result = await AuthLogin(req.body);
switch (result.status) {
case "User Not Found!!! Kindly signup...":
res.status(404).json({
message: "User Not Found!!! Kindly signup...",
});
break;
case "Email & Password is invalid...Check the credentials":
res.status(400).json({
message: "Email & Password is invalid...Check the credentials",
});
break;
case "Already LoggedIn on another browser....Please logout!!!":
res.status(403).json({
message: "Already LoggedIn on another browser....Please logout!!!",
});
break;
case "User_Datas not found":
res.status(404).json({
message: "User_Datas not found",
});
break;
case "User update failed.":
res.status(400).json({
message: "User update failed.",
});
break;
case "Success":
res.status(200).json({
message: result.data,
});
break;
default:
res.status(500).json({
message: "Internal server error",
});
break;
}
} catch (error) {
res.status(500).json({
message: "An unexpected error occurred",
});
return;
}
};
export const SignOutController = async (
req: Request,
res: Response
): Promise<void> => {
try {
const { Email } = req.body;
if (!Email) {
res.status(400).json({
message: "Email field is Mandatory",
});
return;
}
const result = await AuthLogout(req.body);
switch (result.status) {
case "User not found":
res.status(404).json({
message: "User not found",
});
break;
case "Token not found":
res.status(404).json({
message: "Token not found",
});
break;
case "Success":
res.status(200).json({
message: "Logout Successfull",
});
break;
default:
res.status(500).json({
message: "Internal server error",
});
break;
}
} catch (error) {
res.status(500).json({
message: "An unexpected error occurred",
});
return;
}
};
export const ForgetPasswordController = async (
req: Request,
res: Response
): Promise<void> => {
try {
const { Email } = req.body;
if (!Email) {
res.status(400).json({
message: "Email field is Mandatory",
});
return;
}
const result = await forgetPassword(req.body);
switch (result.status) {
case "You can only reset your password once every 24 hours.":
res.status(400).json({
message: "You can only reset your password once every 24 hours.",
});
break;
case "Email not found":
res.status(404).json({
message: "Email not found",
});
break;
case "Success":
res.status(200).json({
message: "Password reset link sent successfully",
});
break;
default:
res.status(500).json({
message: "Internal server error",
});
break;
}
} catch (error) {
res.status(500).json({
message: "An unexpected error occurred",
});
return;
}
};
export const ResetPasswordController = async (
req: Request,
res: Response
): Promise<void> => {
try {
const { newPassword, resetToken, confirmPassword } = req.body;
if (!newPassword || !resetToken || !confirmPassword) {
res.status(400).json({
message: "All fields are Mandatory",
});
return;
}
const result = await forgetPassword(req.body);
switch (result.status) {
case "Invalid token payload.":
res.status(400).json({
message: "Invalid token payload.",
});
break;
case "Password mismatch":
res.status(400).json({
message: "Password mismatch",
});
break;
case "User not found":
res.status(404).json({
message: "User not found",
});
break;
case "Token is invalid or expired.":
res.status(404).json({
message: "Token is invalid or expired.",
});
break;
case "Success":
res.status(200).json({
message: "Password reset successfull!!",
});
break;
default:
res.status(500).json({
message: "Internal server error",
});
break;
}
} catch (error) {
res.status(500).json({
message: "An unexpected error occurred",
});
return;
}
};

View File

@@ -0,0 +1,31 @@
import { Request, Response } from "express";
import versionService from "../../../../shared/services/version/versionService.ts";
export const versioncontroller = async (
req: Request,
res: Response
): Promise<void> => {
try {
console.log("req.body: ", req.body);
const { projectId, userId, description, db } = req.body;
// if (!userName || !Email || !description) {
// res.status(400).json({
// message: "All fields are required",
// });
// return;
// }
const result = await versionService.saveCurrentStateAsVersion(
db,
projectId,
userId,
description
);
console.log(result);
} catch (error) {
res.status(500).json({
message: "An unexpected error occurred",
});
return;
}
};

View File

@@ -0,0 +1,83 @@
import express from "express";
import {
ForgetPasswordController,
ResetPasswordController,
SignInController,
SignOutController,
SignupController,
} from "../v1Controllers/authController/authControllers.ts";
import { versioncontroller } from "../v1Controllers/versionController/versioncontroller.ts";
import {
createProjectController,
GetProjects,
RemoveProject,
updateProjectController,
ViewData,
} from "../../controller/project/projectController.ts";
import { tokenValidator } from "../../../shared/utils/token.ts";
import authorizedRoles from "../../../shared/middleware/rbacMiddleware.ts";
import { recentDataController } from "../../controller/home/homeControllers.ts";
import {
GetTrashList,
RestoreTrash,
} from "../../controller/trash/trashcontrollers.ts";
const Authrouter = express.Router();
Authrouter.post("/Auth/signup", SignupController);
Authrouter.post("/Auth/login", SignInController);
Authrouter.post("/Auth/logout", SignOutController);
Authrouter.post("/Auth/forgetPassword", ForgetPasswordController);
Authrouter.post("/Auth/reset-password/:resetToken", ResetPasswordController);
Authrouter.post("/Auth/versionData", versioncontroller);
// project
Authrouter.post("/Auth/upsertProject", tokenValidator, createProjectController);
Authrouter.get(
"/Auth/Projects",
tokenValidator,
authorizedRoles("Admin", "User"),
GetProjects
);
Authrouter.patch(
"/Auth/Project/archive/:projectId",
tokenValidator,
authorizedRoles("Admin", "User"),
RemoveProject
);
Authrouter.patch(
"/Auth/Project/modify",
tokenValidator,
authorizedRoles("Admin", "User"),
updateProjectController
);
Authrouter.get(
"/Auth/Project/view",
tokenValidator,
authorizedRoles("Admin", "User"),
ViewData
);
//home-Page
Authrouter.get(
"/Auth/RecentlyViewed",
tokenValidator,
authorizedRoles("Admin", "User"),
recentDataController
);
//trash
Authrouter.get(
"/Auth/Trash/Lists",
tokenValidator,
authorizedRoles("Admin", "User"),
GetTrashList
);
Authrouter.patch(
"/Auth/restore",
tokenValidator,
authorizedRoles("Admin", "User"),
RestoreTrash
);
export default Authrouter;

View File

@@ -20,8 +20,10 @@ import productRouter from "./Routes/productRoutes.ts";
import projectRouter from "./Routes/projectRoutes.ts";
import trashRouter from "./Routes/trashRoutes.ts";
import homePageRouter from "./Routes/homepageRoutes.ts";
import redis from "../shared/redis/redis.ts";
import Authrouter from "./V1/v1Routes/authRoutes.ts";
// import productFlowRoutes from "./Routes/productFlowRouts.ts";
redis;
const app = express();
app.use(cors());
// const allowedOriginsDev = [
@@ -87,4 +89,8 @@ app.use("/api/v2", productRouter);
app.use("/api/v1", projectRouter);
app.use("/api/v1", trashRouter);
app.use("/api/v1", homePageRouter);
//New versions
app.use("/API/V1", Authrouter);
export default app;

View File

@@ -1,19 +1,20 @@
import { Request, Response } from "express";
import { RecentlyAdded, searchProject, searchTrashProject } from "../../../shared/services/home/homeService.ts";
import { AuthenticatedRequest } from "../../../shared/utils/token.ts";
export const recentDataController = async (
req: Request,
req: AuthenticatedRequest,
res: Response
): Promise<void> => {
try {
const { userId, organization } = req.params;
if (!userId || !organization) {
const { userId, organization,role } = req.user||{};
if (!userId || !organization||!role) {
res.status(400).json({
message: "All fields are required",
});
return;
}
const result = await RecentlyAdded({ userId, organization });
const result = await RecentlyAdded({ userId, organization,role });
switch (result.status) {
case "User not found":

View File

@@ -6,20 +6,27 @@ import {
updateProject,
viewProject,
} from "../../../shared/services/project/project-Services.ts";
import { AuthenticatedRequest } from "../../../shared/utils/token.ts";
export const createProjectController = async (
req: Request,
req: AuthenticatedRequest,
res: Response
): Promise<void> => {
try {
const { projectUuid, userId, thumbnail, organization } = req.body;
if (!projectUuid || !userId || !thumbnail || !organization) {
const { userId, organization } = req.user || {};
console.log("req.user: ", req.user);
const { projectUuid, thumbnail } = req.body;
if (!req.user || !req.user.userId || !req.user.organization) {
res.status(401).json({ message: "Unauthorized" });
return;
}
if (!projectUuid || !thumbnail) {
res.status(400).json({
message: "All fields are required",
});
return;
}
const result = await createProject(req.body);
const result = await createProject({ ...req.body, userId, organization });
switch (result.status) {
case "project_exists":
@@ -54,18 +61,19 @@ export const createProjectController = async (
}
};
export const GetProjects = async (
req: Request,
req: AuthenticatedRequest,
res: Response
): Promise<void> => {
try {
const { userId, organization } = req.params;
if (!userId || !organization) {
const { userId, organization, role } = req.user || {};
// const { userId, organization } = req.params;
if (!userId || !organization || !role) {
res.status(400).json({
message: "All fields are required",
});
return;
}
const result = await GetAllProjects({ userId, organization });
const result = await GetAllProjects({ userId, organization, role });
switch (result?.status) {
case "User not found":
res.status(404).json({
@@ -92,19 +100,34 @@ export const GetProjects = async (
}
};
export const RemoveProject = async (
req: Request,
req: AuthenticatedRequest,
res: Response
): Promise<void> => {
try {
const { projectId } = req.params;
const { organization, userId } = req.body;
if (!projectId || !organization || !userId) {
// const { organization, userId } = req.body;
const { organization, userId, role } = req.user || {};
if (
!req.user ||
!req.user.userId ||
!req.user.organization ||
!req.user.role
) {
res.status(401).json({ message: "Unauthorized" });
return;
}
if (!projectId || !organization || !userId || !role) {
res.status(400).json({
message: "All fields are required",
});
return;
}
const result = await DeleteProject({ projectId, organization, userId });
const result = await DeleteProject({
projectId,
organization,
userId,
role,
});
switch (result?.status) {
case "Project not found":
res.status(404).json({
@@ -135,13 +158,13 @@ export const RemoveProject = async (
}
};
export const updateProjectController = async (
req: Request,
req: AuthenticatedRequest,
res: Response
): Promise<void> => {
try {
const { projectId, organization, projectName, thumbnail, userId } =
req.body;
if (!userId || !organization || !projectId) {
const { userId, organization, role } = req.user || {};
const { projectId, projectName, thumbnail } = req.body;
if (!userId || !organization || !projectId || !role) {
res.status(400).json({
message: "All fields are required",
});
@@ -153,6 +176,7 @@ export const updateProjectController = async (
userId,
projectName,
thumbnail,
role,
});
switch (result?.status) {
case "Project not found":
@@ -184,14 +208,25 @@ export const updateProjectController = async (
return;
}
};
export const ViewData = async (req: Request, res: Response): Promise<void> => {
export const ViewData = async (
req: AuthenticatedRequest,
res: Response
): Promise<void> => {
try {
const { projectId, organization, userId } = req.query as {
organization: string;
const { organization, userId, role } = req.user || {};
if (
!req.user ||
!req.user.userId ||
!req.user.organization ||
!req.user.role
) {
res.status(401).json({ message: "Unauthorized" });
return;
}
const { projectId } = req.query as {
projectId: string;
userId: string;
};
if (!userId || !organization || !projectId) {
if (!userId || !organization || !projectId || !role) {
res.status(400).json({
message: "All fields are required",
});
@@ -201,6 +236,7 @@ export const ViewData = async (req: Request, res: Response): Promise<void> => {
projectId,
organization,
userId,
role,
});
switch (result?.status) {
case "Project not found":

View File

@@ -3,20 +3,21 @@ import {
TrashDatas,
RestoreTrashData,
} from "../../../shared/services/trash/trashService.ts";
import { AuthenticatedRequest } from "../../../shared/utils/token.ts";
export const GetTrashList = async (
req: Request,
req: AuthenticatedRequest,
res: Response
): Promise<void> => {
try {
const { organization } = req.query as { organization: string };
if (!organization) {
const { organization, role, userId } = req.user || {};
if (!organization || !role || !userId) {
res.status(400).json({
message: "All fields are required",
});
return;
}
const result = await TrashDatas({ organization });
const result = await TrashDatas({ organization, role, userId });
switch (result.status) {
case "Trash is Empty":
@@ -28,7 +29,6 @@ export const GetTrashList = async (
case "Success":
res.status(200).json({
// message: "Project created Successfully",
TrashDatas: result.ListDatas,
});
break;
@@ -47,22 +47,26 @@ export const GetTrashList = async (
};
export const RestoreTrash = async (
req: Request,
req: AuthenticatedRequest,
res: Response
): Promise<void> => {
try {
const { organization, projectId } = req.query as {
organization: string;
const { organization, role, userId } = req.user || {};
const { projectId } = req.query as {
projectId: string;
};
console.log("organization: ", organization);
if (!organization || !projectId) {
if (!organization || !projectId || !role || !userId) {
res.status(400).json({
message: "All fields are required",
});
return;
}
const result = await RestoreTrashData({ organization, projectId });
const result = await RestoreTrashData({
organization,
projectId,
role,
userId,
});
switch (result.status) {
case "Project not found":

View File

@@ -1,6 +1,6 @@
import { Request, Response } from "express";
import userModel from "../../shared/model/user-Model.ts";
import {hashGenerate,hashValidator} from "../../shared/security/Hasing.ts"
import {hashGenerate,hashValidator} from "../../shared/utils/Hasing.ts"
let serverAlive = true;
export class User {

View File

@@ -3,14 +3,14 @@ import MainModel from "../../connect/mongoose.ts";
import { User } from "./userAuthModel.ts";
export interface Token extends Document {
userId: User["_id"];
isArchieve: Boolean;
isArchive: Boolean;
refreshToken: string;
resetTokenExpiry?: Date;
resetToken: string;
}
const tokenSchema: Schema = new Schema({
userId: { type: Schema.Types.ObjectId, ref: "User" },
isArchieve: { type: Boolean, default: false },
isArchive: { type: Boolean, default: false },
token: { type: String },
refreshToken: { type: String },
tokenCreatedAt: { type: Date },

View File

@@ -3,41 +3,44 @@ import MainModel from "../../connect/mongoose.ts";
import { User } from "./userAuthModel.ts";
export interface UserData extends Document {
userId: User["_id"];
isShare: Boolean;
activeStatus: string;
notificationEnable: boolean;
About: string;
isArchieve: boolean;
Role: string;
Profilepicture: string;
isArchive: boolean;
role: string;
profilePicture: string;
recentlyViewed: string[];
CheckIn: number;
CheckOut: number;
}
const UserDataSchema: Schema = new Schema({
userId: { type: Schema.Types.ObjectId, ref: "User" },
isArchieve: { type: Boolean, default: false },
isArchive: { type: Boolean, default: false },
notificationEnable: { type: Boolean, default: false },
About: {
type: String,
},
Role: {
role: {
type: String,
default: "User",
enum: ["User", "Admin"],
},
isShare: {
type: Boolean,
default: false,
},
activeStatus: {
type: String,
enum: ["online", "offline"],
default: "offline",
},
recentlyViewed: {
type: [String],
default: [],
},
Profilepicture: {
profilePicture: {
type: String,
// default: "default-profile-picture.jpg"
},
CheckIn: {
type: Number,
},
CheckOut: {
type: Number,
},
});
const UsersDataModel = (db: any) => {

View File

@@ -1,15 +1,15 @@
import { Schema, Document } from "mongoose";
import MainModel from "../../connect/mongoose.ts";
export interface User extends Document {
Username: string;
userName: string;
Email: string;
Password: string;
isArchieve: boolean;
isArchive: boolean;
visitorBrowserID: string;
lastPasswordReset: number;
}
const signupschema: Schema = new Schema({
Username: {
const AuthSchema: Schema = new Schema({
userName: {
type: String,
required: true,
},
@@ -23,12 +23,12 @@ const signupschema: Schema = new Schema({
min: 8,
// required: true,
},
isArchieve: { type: Boolean, default: false },
isArchive: { type: Boolean, default: false },
lastPasswordReset: { type: Number },
visitorBrowserID: { type: String },
});
const userModel = (db: any) => {
return MainModel(db, "User", signupschema, "User");
const AuthModel = (db: any) => {
return MainModel(db, "UserAuth", AuthSchema, "UserAuth");
};
export default userModel;
export default AuthModel;

View File

@@ -0,0 +1,52 @@
import mongoose, { Document, Schema } from "mongoose";
import MainModel from "../../connect/mongoose.ts";
export interface IShare extends Document {
Createdby: {
userId: mongoose.Types.ObjectId;
Email: string;
};
isActive: boolean;
Share_People: [
{
userId: mongoose.Types.ObjectId;
Email: string;
AccessPoint: string;
}
];
Description: string;
createdAt: number;
projectId: mongoose.Types.ObjectId;
}
const shareSchema: Schema = new Schema({
CreatedBy: {
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
Email: { type: String, ref: "User", required: true },
},
isActive: { type: Boolean, default: false },
Share_People: [
{
userId: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
Email: { type: String, ref: "User", required: true },
AccessPoint: {
type: String,
default: "Can view",
enum: ["Can view", "Can edit", "Can comment"],
},
},
],
createdAt: {
type: Number,
default: Date.now(),
},
projectId: { type: mongoose.Schema.Types.ObjectId, ref: "Scene" },
});
const shareModel = (db: any) => {
return MainModel(db, "Shared", shareSchema, "Shared");
};
export default shareModel;

View File

@@ -0,0 +1,13 @@
import { Response, Request, NextFunction } from "express";
import { AuthenticatedRequest } from "../../shared/utils/token.ts";
type Role = "Admin" | "User";
const authorizedRoles = (...allowedRoles: Role[]) => {
return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
if (!req.user || !allowedRoles.includes(req.user.role as Role)) {
res.status(403).json({ message: "Access Denied" });
return;
}
next();
};
};
export default authorizedRoles;

21
src/shared/redis/redis.ts Normal file
View File

@@ -0,0 +1,21 @@
import Redis from "ioredis";
import * as dotenv from "dotenv";
dotenv.config();
const redis = new Redis.default({
host:
process.env.REDIS_ENV === "true"
? process.env.REDIS_DOCKER
: process.env.REDIS_LOCAL,
port: parseInt(process.env.REDIS_PORT || "6379"),
password: "",
db: 0,
});
redis.on("connect", () => {
console.log(`Connected to Redis to ${redis.options.port}`);
});
redis.on("error", (err: unknown) => {
console.error("Redis connection error:", err);
});
export default redis;

View File

@@ -1,38 +0,0 @@
import { Request, Response, NextFunction } from 'express';
import * as Jwt from 'jsonwebtoken'; // Correct way to import jsonwebtoken
// Define a new interface extending Request
interface AuthenticatedRequest extends Request {
user?: {
email: string;
// Add more fields as needed based on your JWT payload
};
}
const tokenGenerator = (email: string) => {
const token = Jwt.sign({ email: email }, "Milestone", {
expiresIn: "3hours",
});
return token;
};
const tokenValidator = (req: AuthenticatedRequest, res: Response, next: NextFunction): void => {
const token: string | undefined = req.headers.token as string | undefined;
if (!token) {
res.status(403).json({
msg: "No token present",
});
return; // Make sure to return after sending a response
}
try {
const decoded = Jwt.verify(token,"Milestone") as { email: string }; // adjust if your JWT payload has more fields
req.user = decoded;
next();
} catch (err) {
res.status(401).json({
msg: "Invalid Token",
});
}
};
export { tokenValidator,tokenGenerator };

View File

@@ -0,0 +1,466 @@
import AuthModel from "../../V1Models/Auth/userAuthModel.ts";
import UsersDataModel from "../../V1Models/Auth/user.ts";
import nodemailer from "nodemailer";
import tokenType from "../../V1Models/Auth/tokenModel.ts";
import Jwt from "jsonwebtoken";
import { hashValidator, hashGenerate } from "../../utils/Hasing.ts";
import redis from "../../redis/redis.ts";
import { tokenGenerator, tokenRefreshGenerator } from "../../utils/token.ts";
interface Iserviceuser {
userName: string;
Email: string;
Password: string;
profilePicture: string;
}
interface IloginUser {
Email: string;
Password: string;
fingerprint: string;
}
interface IresetToken {
resetToken: string;
newPassword: string;
confirmPassword: string;
}
interface IUser {
Email: string;
userName: string;
_id: string;
}
const jwt_secret = process.env.JWT_SECRET as string;
export function extractOrg(Email: string) {
return Email.split("@")[1].split(".")[0];
}
async function findExistingUserEmail(Email: string) {
const organization = extractOrg(Email);
const existingUser = await AuthModel(organization).findOne({
Email: Email,
isArchive: false,
});
return existingUser;
}
export const AuthSignup = async (
data: Iserviceuser
): Promise<{
status: string;
}> => {
const { userName, Email, Password, profilePicture } = data;
try {
let role;
const caseChange = Email.toLowerCase();
const organization = extractOrg(caseChange);
const Existing_User = await findExistingUserEmail(caseChange);
if (Existing_User) {
return { status: "User already exists" };
} else {
const hashPassword = await hashGenerate(Password);
const userCount = await AuthModel(organization).countDocuments({});
role = userCount === 0 ? "Admin" : "User";
const isShare = "true";
const newuser = await AuthModel(organization).create({
userName: userName,
Email: caseChange,
Password: hashPassword,
});
const UserDatas = await UsersDataModel(organization).create({
userId: newuser._id,
role: role,
isShare: isShare,
profilePicture: profilePicture,
});
return { status: "Success" };
}
} catch (error: unknown) {
if (error instanceof Error) {
return {
status: error.message,
};
} else {
return {
status: "An unexpected error occurred",
};
}
}
};
export const AuthLogin = async (
data: IloginUser
): Promise<{ status: string; data?: Object }> => {
try {
const { Email, Password, fingerprint } = data;
const caseChange = Email.toLowerCase();
const organization = extractOrg(caseChange);
const Existing_User = await findExistingUserEmail(caseChange);
if (!Existing_User) return { status: "User Not Found!!! Kindly signup..." };
else {
const existingMail = await getUserFromCacheOrDB(caseChange);
const checkPassword = await hashValidator(
Password,
existingMail.Password
);
if (!checkPassword)
return {
status: "Email & Password is invalid...Check the credentials",
};
const browserID = existingMail.visitorBrowserID;
if (browserID && browserID !== fingerprint) {
await Promise.all([
redis.del(`token:${caseChange}`),
redis.del(`user:${caseChange}`),
]);
await AuthModel(organization).updateOne(
{ Email: caseChange },
{ visitorBrowserID: "" }
);
await tokenType(organization).updateOne(
{ userId: Existing_User._id },
{ refreshToken: "" }
);
return {
status: "Already LoggedIn on another browser....Please logout!!!",
};
}
const UserData = await UsersDataModel(organization).findOne({
userId: existingMail._id,
isArchive: false,
});
if (!UserData)
return {
status: "User_Datas not found",
};
const redisTokenRaw = await redis.get(`token:${caseChange}`);
if (redisTokenRaw) {
const cachedTokens = JSON.parse(redisTokenRaw);
try {
Jwt.verify(cachedTokens.token, jwt_secret);
return {
status: "Success",
data: {
message: "login successfull",
email: existingMail.Email,
name: existingMail.userName,
userId: existingMail._id,
isShare: UserData.isShare,
token: cachedTokens.token,
refreshToken: cachedTokens.refreshToken,
},
};
} catch (err) {
console.log("Access token expired. Generating new...");
}
}
const tokenValidation = tokenGenerator(
existingMail.Email,
UserData.role,
existingMail._id,
organization
);
const refreshTokenvalidation = tokenRefreshGenerator(
existingMail.Email,
UserData.role,
existingMail._id,
organization
);
await handleTokenCache(
existingMail._id.toString(),
existingMail.Email,
tokenValidation,
refreshTokenvalidation
);
const updatedUser = await AuthModel(organization)
.findByIdAndUpdate(
existingMail._id,
{ visitorBrowserID: fingerprint },
{ new: true }
)
.select("-__v -Profilepicture");
if (!updatedUser)
return {
status: "User update failed.",
};
await redis.setex(
`user:${existingMail.Email}`,
3600,
JSON.stringify(updatedUser)
);
const finalResult = {
message: "login successfull",
email: existingMail.Email,
name: existingMail.userName,
userId: existingMail._id,
isShare: UserData.isShare,
// updatedUser: updatedUser as IUser,
token: tokenValidation,
refreshToken: refreshTokenvalidation,
};
return {
status: "Success",
data: finalResult,
};
}
} catch (error: unknown) {
if (error instanceof Error) {
return {
status: error.message,
};
} else {
return {
status: "An unexpected error occurred",
};
}
}
};
export const AuthLogout = async ({
Email,
}: Iserviceuser): Promise<{ status: string }> => {
try {
const caseChange = Email.toLowerCase();
const organization = extractOrg(caseChange);
const Existing_User = await findExistingUserEmail(caseChange);
if (!Existing_User) return { status: "User not found" };
const tokenData = await tokenType(organization).findOne({
userId: Existing_User._id,
isArchive: false,
});
if (!tokenData) return { status: "Token not found" };
await Promise.all([
redis.del(`token:${caseChange}`),
redis.del(`user:${caseChange}`),
]);
tokenData.refreshToken = "";
await tokenData.save();
Existing_User.visitorBrowserID = "";
await Existing_User.save();
return { status: "Success" };
} catch (error: unknown) {
if (error instanceof Error) {
return {
status: error.message,
};
} else {
return {
status: "An unexpected error occurred",
};
}
}
};
export const forgetPassword = async ({
Email,
}: Iserviceuser): Promise<{ status: string }> => {
try {
const caseChange = Email.toLowerCase();
const organization = extractOrg(caseChange);
const Existing_User = await findExistingUserEmail(caseChange);
if (Existing_User) {
if (Existing_User.lastPasswordReset) {
const lastPasswordReset = Existing_User.lastPasswordReset;
const now = Date.now();
const timeDiff = now - lastPasswordReset;
const diffInHours = Math.floor(timeDiff / (1000 * 60 * 60));
if (diffInHours < 24)
return {
status: "You can only reset your password once every 24 hours.",
};
}
const transport = nodemailer.createTransport({
service: "gmail",
secure: true,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
},
});
const resetToken = tokenGenerator(
Email,
Existing_User.Role as string,
Existing_User._id as string,
organization
);
const userTokenData = await tokenType(organization).findOne({
Email: Email,
isArchive: false,
});
if (!userTokenData) {
await tokenType(organization).create({
Email: Existing_User.Email,
userId: Existing_User._id,
resetToken: resetToken,
resetTokenExpiry: Date.now(),
});
} else {
userTokenData.resetToken = resetToken;
userTokenData.resetTokenExpiry = new Date();
await userTokenData.save();
}
const Receiver = {
from: process.env.EMAIL_USER,
to: Email,
subject: "Password Reset Request",
text: `Click the below link to generate the new password \n ${
process.env.CLIENT_URL
}/reset-password/${tokenGenerator(
Email,
Existing_User.Role as string,
Existing_User._id as string,
organization
)}`,
};
await transport.sendMail(Receiver);
return { status: "Success" };
} else {
return { status: "Email not found" };
}
} catch (error: unknown) {
if (error instanceof Error) {
return {
status: error.message,
};
} else {
return {
status: "An unexpected error occurred",
};
}
}
};
export const resetPassword = async ({
resetToken,
newPassword,
confirmPassword,
}: IresetToken): Promise<{ status: string }> => {
try {
const decoded = Jwt.verify(resetToken, "Milestone");
if (typeof decoded === "string" || !("email" in decoded))
return { status: "Invalid token payload." };
const Email = decoded.email;
const organization = extractOrg(Email);
if (newPassword !== confirmPassword) return { status: "Password mismatch" };
const userData = await AuthModel(organization).findOne({
Email: Email,
isArchive: false,
});
const userTokenData = await tokenType(organization).findOne({
Email: Email,
isArchive: false,
});
if (!userData || !userTokenData) return { status: "User not found" };
else {
const tokenexpiry = userTokenData.resetTokenExpiry as Date;
const now = Date.now();
const tokenAge = tokenexpiry ? now - tokenexpiry.getTime() : 0;
const TOKEN_EXPIRATION_TIME = 15 * 60 * 1000;
if (!tokenexpiry || tokenAge > TOKEN_EXPIRATION_TIME)
return { status: "Token is invalid or expired." };
const hashPassword = await hashGenerate(newPassword);
const lastPasswordReset = Date.now();
await AuthModel(organization).findByIdAndUpdate(
userData._id,
{ Password: hashPassword, lastPasswordReset: lastPasswordReset },
{ new: true }
);
userTokenData.resetToken = "";
userTokenData.resetTokenExpiry = undefined;
await userTokenData.save();
await ResetFromCacheOrDB(Email);
return { status: "Success" };
}
} catch (error: unknown) {
if (error instanceof Error) {
return {
status: error.message,
};
} else {
return {
status: "An unexpected error occurred",
};
}
}
};
async function getUserFromCacheOrDB(Email: string) {
const redisUserKey = `user:${Email}`;
try {
const cachedUser = await redis.get(redisUserKey);
if (cachedUser) return JSON.parse(cachedUser);
const Existing_User = await findExistingUserEmail(Email);
if (Existing_User) {
await redis.setex(redisUserKey, 3600, JSON.stringify(Existing_User));
}
return Existing_User;
} catch (error) {
return error;
}
}
async function ResetFromCacheOrDB(Email: string) {
const redisUserKey = `user:${Email}`;
try {
const organization = extractOrg(Email);
const cachedUser = await redis.get(redisUserKey);
if (cachedUser) {
const user = await AuthModel(organization)
.findOne({
Email: Email,
isArchive: false,
})
.lean()
.select("-__v -Profilepicture");
if (user) {
await redis.setex(redisUserKey, 3600, JSON.stringify(user));
}
return user;
}
} catch (error) {
return error;
}
}
async function handleTokenCache(
userId: string,
Email: string,
token: string,
refreshToken: string
) {
const redisTokenKey = `token:${Email}`;
try {
const organization = extractOrg(Email);
const tokenPayload = {
token,
refreshToken,
userId,
Email,
};
await redis.setex(redisTokenKey, 3600, JSON.stringify(tokenPayload));
let tokenDoc = await tokenType(organization).findOne({
userId,
isArchive: false,
});
if (!tokenDoc) {
tokenDoc = await tokenType(organization).create({ userId, refreshToken });
} else {
await tokenType(organization).findByIdAndUpdate(
tokenDoc._id,
{ refreshToken },
{ new: true }
);
}
return tokenPayload;
} catch (error) {
return error;
}
}

View File

@@ -0,0 +1,155 @@
import projectModel from "../../model/project/project-model.ts";
import userModel from "../../model/user-Model.ts";
import versionModel from "../../model/version/versionModel.ts";
import UsersDataModel from "../../V1Models/Auth/user.ts";
import cameraModel from "../../V1Models/Builder/cameraModel.ts";
import { existingProjectById } from "../helpers/ProjecthelperFn.ts";
interface IcameraData {
userId: string;
position: Object;
target: Object;
rotation: Object;
organization: string;
projectId: string;
versionId: string;
}
interface IgetCameras {
organization: string;
userId?: string;
}
export const SetCamera = async (
data: IcameraData
): Promise<{ status: string; data?: Object }> => {
try {
const {
userId,
position,
target,
rotation,
organization,
projectId,
versionId,
} = data;
const LivingProject = await existingProjectById(
projectId,
organization,
userId
);
if (!LivingProject) return { status: "Project not found" };
const existingCamera = await cameraModel(organization).findOne({
userId: userId,
});
if (existingCamera) {
const updateCamera = await cameraModel(organization).findOneAndUpdate(
{
userId: userId,
projectId: projectId,
versionId: versionId,
isArchive: false,
},
{ position: position, target: target, rotation: rotation },
{ new: true }
);
return {
status: "Update Success",
data: updateCamera,
};
} else {
const newCamera = await cameraModel(organization).create({
userId,
projectId,
versionId,
position,
target,
rotation,
});
return {
status: "Creation Success",
data: newCamera,
};
}
} catch (error: unknown) {
if (error instanceof Error) {
return {
status: error.message,
};
} else {
return {
status: "An unexpected error occurred",
};
}
}
};
export const GetCamers = async (
data: IgetCameras
): Promise<{ status: string; data?: Object }> => {
const { userId, organization } = data;
try {
const findCamera = await cameraModel(organization).findOne({
userId: userId,
});
if (!findCamera) {
return { status: "user not found" };
} else {
return { status: "Success", data: findCamera };
}
} catch (error: unknown) {
if (error instanceof Error) {
return {
status: error.message,
};
} else {
return {
status: "An unexpected error occurred",
};
}
}
};
export const onlineActiveDatas = async (
data: IgetCameras
): Promise<{ status: string; data?: Object }> => {
const { organization } = data;
try {
const findactiveUsers = await UsersDataModel(organization).find({
activeStatus: "online",
});
const cameraDataPromises = findactiveUsers.map(async (activeUser: any) => {
const cameraData = await cameraModel(organization)
.findOne({ userId: activeUser._id })
.select("position target rotation -_id");
if (cameraData) {
return {
position: cameraData.position,
target: cameraData.target,
rotation: cameraData.rotation,
userData: {
_id: activeUser._id,
userName: activeUser.userName,
email: activeUser.email,
activeStatus: activeUser.activeStatus,
},
};
}
return null;
});
const cameraDatas = (await Promise.all(cameraDataPromises)).filter(
(singledata: any) => singledata !== null
);
return { status: "Success", data: cameraDatas };
} catch (error: unknown) {
if (error instanceof Error) {
return {
status: error.message,
};
} else {
return {
status: "An unexpected error occurred",
};
}
}
};

View File

@@ -2,6 +2,7 @@ import projectModel from "../../model/project/project-model.ts";
import userModel from "../../model/user-Model.ts";
import { Types } from "mongoose";
import versionModel from "../../model/version/versionModel.ts";
import AuthModel from "../../V1Models/Auth/userAuthModel.ts";
export const existingProject = async (
projectUuid: string,
organization: string,
@@ -20,7 +21,7 @@ export const existingUser = async (userId: string, organization: string) => {
console.log("Invalid ObjectId format");
return null;
}
const userData = await userModel(organization).findOne({
const userData = await AuthModel(organization).findOne({
_id: userId,
});
return userData;
@@ -76,3 +77,15 @@ export const generateUntitledProjectName = async (
return newNumber === 0 ? "Untitled" : `Untitled ${newNumber}`;
};
export const existingProjectById = async (
projectId: string,
organization: string,
userId: string
) => {
const projectData = await projectModel(organization).findOne({
_id: projectId,
createdBy: userId,
isArchive: false,
});
return projectData;
};

View File

@@ -1,10 +1,12 @@
import projectModel from "../../model/project/project-model.ts";
import userModel from "../../model/user-Model.ts";
import UsersDataModel from "../../V1Models/Auth/user.ts";
import { existingUser } from "../helpers/ProjecthelperFn.ts";
interface IRecentData {
organization: string;
userId: string;
role: string;
}
interface IProject {
_id: string;
@@ -19,26 +21,31 @@ interface searchProjectInterface {
userId: string;
organization: string;
}
interface RoleFilter {
isArchive: boolean;
createdBy?: string;
}
export const RecentlyAdded = async (data: IRecentData) => {
try {
const { userId, organization } = data;
const { userId, organization, role } = data;
const userExisting = await existingUser(userId, organization);
if (!userExisting) return { status: "User not found" };
const userRecentData = await userModel(organization)
.findOne({ _id: userId })
const userRecentData = await UsersDataModel(organization)
.findOne({ userId: userId, isArchive: false })
.populate({
path: "recentlyViewed",
model: projectModel(organization),
select: "_id",
});
let filter = { isArchive: false } as RoleFilter;
if (role === "User") {
filter.createdBy = userId;
}
const populatedProjects = userRecentData.recentlyViewed as IProject[];
const RecentDatas = await Promise.all(
populatedProjects.map(async (project) => {
const projectExisting = await projectModel(organization)
.findOne({
_id: project._id,
isArchive: false,
})
.findOne(filter)
.select("_id projectName createdBy thumbnail createdAt isViewed");
return projectExisting;
})
@@ -57,26 +64,29 @@ export const searchProject = async (data: searchProjectInterface) => {
if (!userExisting) return { status: "User not found" };
const findprojectName = await projectModel(organization).find({
projectName: { $regex: `${searchName}`, $options: "i" }, // 'i' makes it case-insensitive
isArchive: false,
})
if (!findprojectName||findprojectName.length===0) return { status: "Project not found" }
isArchive: false,
});
if (!findprojectName || findprojectName.length === 0)
return { status: "Project not found" };
return { status: "Success", data: findprojectName };
} catch (error: unknown) {
return { status: error };
}
}
};
export const searchTrashProject = async (data: searchProjectInterface) => {
try {
const { userId, organization, searchName } = data;
const userExisting = await existingUser(userId, organization);
if (!userExisting) return { status: "User not found" };
const findprojectName = await projectModel(organization).find({
projectName: { $regex: `${searchName}`, $options: "i" },
isArchive: true,isDeleted:false
})
if (!findprojectName||findprojectName.length===0) return { status: "Project not found" }
projectName: { $regex: `${searchName}`, $options: "i" },
isArchive: true,
isDeleted: false,
});
if (!findprojectName || findprojectName.length === 0)
return { status: "Project not found" };
return { status: "Success", data: findprojectName };
} catch (error: unknown) {
return { status: error };
}
}
};

View File

@@ -1,6 +1,8 @@
import { ObjectId } from "mongoose";
import projectModel from "../../model/project/project-model.ts";
import userModel from "../../model/user-Model.ts";
import versionModel from "../../model/version/versionModel.ts";
import { AuthenticatedRequest } from "../../utils/token.ts";
import {
existingProject,
existingUser,
@@ -8,6 +10,7 @@ import {
previousVersion,
generateUntitledProjectName,
} from "../helpers/ProjecthelperFn.ts";
import UsersDataModel from "../../V1Models/Auth/user.ts";
interface CreateProjectInput {
projectName: string;
projectUuid: string;
@@ -23,26 +26,32 @@ interface updateProjectInput {
thumbnail?: string;
sharedUsers?: string[];
organization: string;
role: string;
}
interface GetProjectsInterface {
userId: string;
organization: string;
role: string;
}
interface ProjectInterface {
projectId: string;
userId: string;
organization: string;
role: string;
}
interface RoleFilter {
isArchive: boolean;
createdBy?: string;
}
export const createProject = async (data: CreateProjectInput) => {
try {
const { userId, thumbnail, sharedUsers, organization, projectUuid } = data;
const { thumbnail, sharedUsers, projectUuid, userId, organization } = data;
const userExisting = await existingUser(userId, organization);
if (!userExisting) {
return {
status: "user_not_found",
};
}
}
const projectExisting = await existingProject(
projectUuid,
organization,
@@ -93,13 +102,15 @@ export const createProject = async (data: CreateProjectInput) => {
export const GetAllProjects = async (data: GetProjectsInterface) => {
try {
const { userId, organization } = data;
const { userId, organization, role } = data;
await existingUser(userId, organization);
if (!existingUser) return { status: "User not found" };
let filter = { isArchive: false } as RoleFilter;
if (role === "User") {
filter.createdBy = userId;
}
const projectDatas = await projectModel(organization)
.find({
isArchive: false,
})
.find(filter)
.select("_id projectName createdBy thumbnail createdAt projectUuid");
if (projectDatas) return { status: "Success", Datas: projectDatas };
} catch (error: unknown) {
@@ -109,41 +120,42 @@ export const GetAllProjects = async (data: GetProjectsInterface) => {
export const DeleteProject = async (data: ProjectInterface) => {
try {
const { projectId, organization, userId } = data;
const { projectId, organization, userId, role } = data;
const ExistingUser = await existingUser(userId, organization);
if (!ExistingUser) return { status: "User not found" };
const existingProject = await projectModel(organization).findOne({
_id: projectId,
createdBy: userId,
isArchive: false,
});
let filter = { _id: projectId, isArchive: false } as RoleFilter;
if (role === "User") {
filter.createdBy = userId;
}
const existingProject = await projectModel(organization).findOne(filter);
if (!existingProject) return { status: "Project not found" };
const updateProject = await projectModel(organization).findOneAndUpdate(
{ _id: projectId, isArchive: false },
filter,
{ isArchive: true, DeletedAt: new Date() },
{ new: true }
);
if (updateProject) return { status: "Success",project: updateProject };
if (updateProject) return { status: "Success", project: updateProject };
} catch (error: unknown) {
return { status: error };
}
};
export const updateProject = async (data: updateProjectInput) => {
try {
const { projectId, organization, userId, projectName, thumbnail } = data;
const { projectId, organization, userId, projectName, thumbnail, role } =
data;
const ExistingUser = await existingUser(userId, organization);
if (!ExistingUser) return { status: "User not found" };
const existingProject = await projectModel(organization).findOne({
_id: projectId,
createdBy: userId,
isArchive: false,
});
let filter = { _id: projectId, isArchive: false } as RoleFilter;
if (role === "User") {
filter.createdBy = userId;
}
const existingProject = await projectModel(organization).findOne(filter);
if (!existingProject) return { status: "Project not found" };
if (projectName !== undefined) projectName;
if (thumbnail !== undefined) thumbnail;
const updateProject = await projectModel(organization)
.findOneAndUpdate(
{ _id: projectId, isArchive: false },
filter,
{ projectName: projectName, thumbnail: thumbnail },
{ new: true }
)
@@ -156,18 +168,23 @@ export const updateProject = async (data: updateProjectInput) => {
const maxLength: number = 6;
export const viewProject = async (data: ProjectInterface) => {
try {
const { projectId, organization, userId } = data;
const { projectId, organization, userId, role } = data;
const userExisting = await existingUser(userId, organization);
if (!userExisting) return { status: "User not found" };
const existingProject = await projectModel(organization).findOne({
_id: projectId,
createdBy: userId,
const RecentUserDoc = await UsersDataModel(organization).findOne({
userId: userId,
isArchive: false,
});
let filter = { _id: projectId, isArchive: false } as RoleFilter;
if (role === "User") {
filter.createdBy = userId;
}
const existingProject = await projectModel(organization).findOne(filter);
if (!existingProject) return { status: "Project not found" };
const newArr = userExisting?.recentlyViewed || [];
if (userExisting?.recentlyViewed.length === 0) {
const newArr = RecentUserDoc?.recentlyViewed || [];
if (RecentUserDoc?.recentlyViewed.length === 0) {
newArr.push(projectId);
await RecentUserDoc.save();
} else {
const index = newArr.indexOf(projectId);
if (index !== -1) {
@@ -179,21 +196,13 @@ export const viewProject = async (data: ProjectInterface) => {
newArr.pop();
}
}
await userModel(organization).updateOne(
await UsersDataModel(organization).updateOne(
{ _id: userId },
{ recentlyViewed: newArr },
{ new: true }
);
const projectData = await projectModel(organization)
.findOneAndUpdate(
{
_id: projectId,
createdBy: userId,
isArchive: false,
},
{ isViewed: Date.now() },
{ new: true }
)
.findOneAndUpdate(filter, { isViewed: Date.now() }, { new: true })
.select("_id projectName createdBy thumbnail createdAt");
return { status: "Success", data: projectData };
} catch (error: unknown) {

View File

@@ -1,19 +1,27 @@
import projectModel from "../../model/project/project-model.ts";
interface IOrg {
organization: string;
role: string;
userId: string;
}
interface IRestore {
projectId: string;
organization: string;
role: string;
userId: string;
}
interface RoleFilter {
isArchive: boolean;
createdBy?: string;
}
export const TrashDatas = async (data: IOrg) => {
try {
const { organization } = data;
const TrashLists = await projectModel(organization).find({
isArchive: true,
isDeleted: false,
});
const { organization, role, userId } = data;
let filter = { isArchive: true, isDeleted: false } as RoleFilter;
if (role === "User") {
filter.createdBy = userId;
}
const TrashLists = await projectModel(organization).find(filter);
if (!TrashLists) return { staus: "Trash is Empty" };
const TrashDocs: any[] = [];
for (const Trash of TrashLists) {
@@ -47,14 +55,15 @@ export const TrashDatas = async (data: IOrg) => {
};
export const RestoreTrashData = async (data: IRestore) => {
try {
const { projectId, organization } = data;
const findProject = await projectModel(organization).findOne({
_id: projectId,
isArchive: true,
});
const { projectId, organization, role, userId } = data;
let filter = { isArchive: true, _id: projectId } as RoleFilter;
if (role === "User") {
filter.createdBy = userId;
}
const findProject = await projectModel(organization).findOne(filter);
if (!findProject) return { status: "Project not found" };
const restoreData = await projectModel(organization).findOneAndUpdate(
{ _id: projectId, isArchive: true },
filter,
{ isArchive: false, DeletedAt: null },
{ new: true }
);

View File

@@ -0,0 +1,127 @@
import projectModel from "../../model/project/project-model.ts";
import assetModel from "../../V1Models/Builder/assetModel.ts";
import versionModel from "../../model/version/versionModel.ts";
class VersionService {
async getCurrentVersion(db: string, projectId: string) {
const project = await projectModel(db).findById(projectId);
if (!project) throw new Error("Project not found");
return {
versionNumber: parseFloat(project.Present_version || "0.0"),
versionString: project.Present_version || "0.0",
};
}
async createNewVersion(
db: string,
projectId: string,
userId: string,
description?: string
) {
const project = await projectModel(db).findById(projectId);
if (!project) throw new Error("Project not found");
const { versionNumber } = await this.getCurrentVersion(db, projectId);
const newVersion = parseFloat((versionNumber + 0.1).toFixed(1));
const versionName = `Version ${newVersion.toFixed(1)}`;
const version = await versionModel(db).create({
versionName,
version: newVersion,
projectId: project._id,
createdBy: userId,
description,
});
await projectModel(db).findByIdAndUpdate(projectId, {
Present_version: newVersion.toFixed(1),
total_versions: newVersion.toFixed(1),
});
return version;
}
async saveCurrentStateAsVersion(
db: string,
projectId: string,
userId: string,
description?: string
) {
// Create new version
const newVersion = await this.createNewVersion(
db,
projectId,
userId,
description
);
// Get all assets from previous version
const previousVersion = parseFloat((newVersion.version - 0.1).toFixed(1));
const previousVersionDoc = await versionModel(db).findOne({
projectId,
version: previousVersion,
});
console.log('previousVersionDoc: ', previousVersionDoc);
let previousAssets = [];
if (previousVersionDoc) {
previousAssets = await assetModel(db).find({
projectId,
versionId: previousVersionDoc._id,
isArchive: false,
});
}
// Copy assets to new version
const newAssets = await Promise.all(
previousAssets.map(async (asset) => {
console.log('previousAssets: ', previousAssets);
const newAsset = { ...asset.toObject(), versionId: newVersion._id };
delete newAsset._id;
return await assetModel(db).create(newAsset);
})
);
return {
version: newVersion,
assets: newAssets,
};
}
async getVersionHistory(db: string, projectId: string) {
const versions = await versionModel(db)
.find({ projectId, isArchive: false })
.sort({ version: 1 })
.populate("createdBy", "name email");
const versionHistory = await Promise.all(
versions.map(async (version) => {
const assets = await assetModel(db).find({
projectId,
versionId: version._id,
isArchive: false,
});
const assetCounts = assets.reduce((acc, asset) => {
acc[asset.type] = (acc[asset.type] || 0) + 1;
return acc;
}, {});
return {
version: version.version.toFixed(1),
versionName: version.versionName,
createdAt: version.createdAt,
createdBy: version.createdBy,
description: version.description,
assets: assetCounts,
totalAssets: assets.length,
};
})
);
return versionHistory;
}
}
export default new VersionService();

View File

@@ -1,24 +1,25 @@
import bcrypt from 'bcryptjs';
const saltRounds = 10;
export const hashGenerate = async (Password:string) => {
try {
const salt = await bcrypt.genSalt(saltRounds);
const hash = await bcrypt.hash(Password, salt);
return hash;
} catch (error) {
return error;
}
};
export const hashValidator = async (password:string, hashedPassword:string) => {
try {
const result = await bcrypt.compare(password, hashedPassword);
return result;
} catch (error) {
return false;
}
};
;
import bcrypt from "bcryptjs";
const saltRounds = 10;
export const hashGenerate = async (Password: string) => {
try {
const salt = await bcrypt.genSalt(saltRounds);
const hash = await bcrypt.hash(Password, salt);
return hash;
} catch (error) {
return error;
}
};
export const hashValidator = async (
password: string,
hashedPassword: string
) => {
try {
const result = await bcrypt.compare(password, hashedPassword);
return result;
} catch (error) {
return false;
}
};

View File

@@ -1,34 +1,34 @@
import { MongoClient } from 'mongodb'
export default async function mongoAdminCreation() {
const uri = process.env.MONGO_URI!; // Replace with your MongoDB URI
const client = new MongoClient(uri);
const user = {
user:"admin",
pwd: process.env.MONGO_PASSWORD!,
roles: [{ role: "root", db: process.env.MONGO_AUTH_DB || "admin" }],
};
try {
await client.connect();
const db = client.db('admin'); // Specify the actual database where the user should be created
// Check if the user already exists
const userExists = await db.collection('system.users').findOne({ user: user.user});
if (userExists) {
console.log(`User ${user} already exists`);
return; // Exit if the user already exists
}
// Create the user
await db.command({ createUser: user.user, pwd: user.pwd, roles: user.roles });
console.log("User created successfully!")
} catch (error) {
console.error("Error creating user:",error);
} finally {
await client.close();
}
}
import { MongoClient } from 'mongodb'
export default async function mongoAdminCreation() {
const uri = process.env.MONGO_URI!; // Replace with your MongoDB URI
const client = new MongoClient(uri);
const user = {
user:"admin",
pwd: process.env.MONGO_PASSWORD!,
roles: [{ role: "root", db: process.env.MONGO_AUTH_DB || "admin" }],
};
try {
await client.connect();
const db = client.db('admin'); // Specify the actual database where the user should be created
// Check if the user already exists
const userExists = await db.collection('system.users').findOne({ user: user.user});
if (userExists) {
console.log(`User ${user} already exists`);
return; // Exit if the user already exists
}
// Create the user
await db.command({ createUser: user.user, pwd: user.pwd, roles: user.roles });
console.log("User created successfully!")
} catch (error) {
console.error("Error creating user:",error);
} finally {
await client.close();
}
}
// mongoAdminCreation

143
src/shared/utils/token.ts Normal file
View File

@@ -0,0 +1,143 @@
import { Request, Response, NextFunction } from "express";
import Jwt from "jsonwebtoken";
import dotenv from "dotenv";
import { extractOrg } from "../services/auth/authServices.ts";
import AuthModel from "../V1Models/Auth/userAuthModel.ts";
dotenv.config();
export interface AuthenticatedRequest extends Request {
user?: {
Email: string;
role: string;
userId: string;
organization: string;
};
}
const jwt_secret = process.env.JWT_SECRET as string;
const refresh_jwt_secret = process.env.REFRESH_JWT_SECRET as string;
const tokenGenerator = (
Email: string,
role: string,
userId: string,
organization: string
) => {
const token = Jwt.sign(
{ Email: Email, role: role, userId, organization: organization },
jwt_secret,
{
expiresIn: "3h",
}
);
return token;
};
const tokenRefreshGenerator = (
Email: string,
role: string,
userId: string,
organization: string
) => {
const token = Jwt.sign(
{ Email: Email, role: role, userId, organization: organization },
refresh_jwt_secret,
{
expiresIn: "30d",
}
);
return token;
};
const tokenValidator = async (
req: AuthenticatedRequest,
res: Response,
next: NextFunction
): Promise<void> => {
const token: string | undefined = req.headers.token as string | undefined;
const refresh_token = req.headers["refresh_token"] as string | undefined;
if (!token) {
res.status(403).json({
msg: "No token present",
});
return;
}
try {
const decoded = Jwt.verify(token, jwt_secret) as {
Email: string;
role: string;
userId: string;
organization: string;
};
if (!decoded) {
res.status(403).json({
success: false,
status: 403,
message: "Invalid Token",
});
return;
}
req.user = decoded;
next();
} catch (err) {
// res.status(401).json({
// msg: "Invalid Token",
// });
if (!refresh_token) {
res.status(403).json({
success: false,
status: 403,
message: "No refresh token present",
});
return;
}
try {
const decodedRefresh = Jwt.verify(refresh_token, refresh_jwt_secret) as {
Email: string;
role: string;
userId: string;
organization: string;
};
if (!decodedRefresh) {
res.status(403).json({
success: false,
status: 403,
message: "Invalid Token",
});
return;
}
const newAccessToken = tokenGenerator(
decodedRefresh.Email,
decodedRefresh.role,
decodedRefresh.userId,
decodedRefresh.organization
);
res.setHeader("x-access-token", newAccessToken);
req.user = decodedRefresh;
return next();
} catch (err) {
const decodedAny = Jwt.decode(token || refresh_token) as {
Email?: string;
role: string;
userId: string;
organization: string;
};
if (decodedAny?.Email) {
const organization = extractOrg(decodedAny?.Email);
const user = await AuthModel(organization).findOne({
Email: decodedAny.Email,
isArchieve: false,
});
if (user) {
user.visitorBrowserID = "";
await user.save();
}
}
res.status(403).json({
success: false,
status: 403,
message: "Invalid Token",
});
return;
}
}
};
export { tokenValidator, tokenGenerator, tokenRefreshGenerator };