mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-19 07:58:46 -05:00
Merge branch 'fix/responsive-layout-overflow' into feat/user-service-ts
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
min-height: 100vh;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* TODO go for this approach for responsiveness. The aside needs to be taken care of */
|
||||
@@ -30,4 +32,6 @@
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
@@ -32,11 +32,13 @@ class AuthController {
|
||||
|
||||
registerUser = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (req.body?.email) {
|
||||
req.body.email = req.body.email?.toLowerCase();
|
||||
const newUser = req.body.user;
|
||||
const newUserToken = req.body.token;
|
||||
if (newUser?.email) {
|
||||
newUser.email = newUser.email.toLowerCase();
|
||||
}
|
||||
await registrationBodyValidation.validateAsync(req.body);
|
||||
const { user, token } = await this.userService.registerUser(req.body, req.file);
|
||||
await registrationBodyValidation.validateAsync(newUser);
|
||||
const { user, token } = await this.userService.registerUser(newUser, newUserToken, req.file);
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
msg: "User registered successfully",
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
import mongoose from "mongoose";
|
||||
import bcrypt from "bcryptjs";
|
||||
import Monitor from "./Monitor.js";
|
||||
import Team from "./Team.js";
|
||||
import Notification from "./Notification.js";
|
||||
|
||||
const UserSchema = mongoose.Schema(
|
||||
{
|
||||
firstName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
lastName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
avatarImage: {
|
||||
type: String,
|
||||
},
|
||||
profileImage: {
|
||||
data: Buffer,
|
||||
contentType: String,
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isVerified: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
role: {
|
||||
type: [String],
|
||||
default: "user",
|
||||
enum: ["user", "admin", "superadmin", "demo"],
|
||||
},
|
||||
teamId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "Team",
|
||||
immutable: true,
|
||||
},
|
||||
checkTTL: {
|
||||
type: Number,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
UserSchema.pre("save", function (next) {
|
||||
if (!this.isModified("password")) {
|
||||
return next();
|
||||
}
|
||||
const salt = bcrypt.genSaltSync(10);
|
||||
this.password = bcrypt.hashSync(this.password, salt);
|
||||
next();
|
||||
});
|
||||
|
||||
UserSchema.pre("findOneAndUpdate", function (next) {
|
||||
const update = this.getUpdate();
|
||||
if ("password" in update) {
|
||||
const salt = bcrypt.genSaltSync(10);
|
||||
update.password = bcrypt.hashSync(update.password, salt);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
UserSchema.pre("findOneAndDelete", async function (next) {
|
||||
try {
|
||||
const userToDelete = await this.model.findOne(this.getFilter());
|
||||
if (!userToDelete) return next();
|
||||
if (userToDelete.role.includes("superadmin")) {
|
||||
await Team.deleteOne({ _id: userToDelete.teamId });
|
||||
await Monitor.deleteMany({ userId: userToDelete._id });
|
||||
await this.model.deleteMany({
|
||||
teamId: userToDelete.teamId,
|
||||
_id: { $ne: userToDelete._id },
|
||||
});
|
||||
await Notification.deleteMany({ teamId: userToDelete.teamId });
|
||||
}
|
||||
next();
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
UserSchema.methods.comparePassword = async function (submittedPassword) {
|
||||
const res = await bcrypt.compare(submittedPassword, this.password);
|
||||
return res;
|
||||
};
|
||||
|
||||
const User = mongoose.model("User", UserSchema);
|
||||
|
||||
export default User;
|
||||
@@ -0,0 +1,95 @@
|
||||
import { Schema, model, type Types } from "mongoose";
|
||||
import bcrypt from "bcryptjs";
|
||||
import type { User, UserProfileImage, UserRole } from "@/types/index.js";
|
||||
import { MonitorModel } from "@/db/models/index.js";
|
||||
import Team from "./Team.js";
|
||||
import Notification from "./Notification.js";
|
||||
|
||||
type UserDocumentBase = Omit<User, "id" | "teamId" | "createdAt" | "updatedAt"> & {
|
||||
teamId?: Types.ObjectId;
|
||||
profileImage?: Required<UserProfileImage>;
|
||||
};
|
||||
|
||||
interface UserDocument extends UserDocumentBase {
|
||||
_id: Types.ObjectId;
|
||||
teamId?: Types.ObjectId;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
const profileImageSchema = new Schema<Required<UserProfileImage>>(
|
||||
{
|
||||
data: { type: Buffer },
|
||||
contentType: { type: String },
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const UserSchema = new Schema<UserDocument>(
|
||||
{
|
||||
firstName: { type: String, required: true },
|
||||
lastName: { type: String, required: true },
|
||||
email: { type: String, required: true, unique: true },
|
||||
password: { type: String, required: true },
|
||||
avatarImage: { type: String },
|
||||
profileImage: { type: profileImageSchema },
|
||||
isActive: { type: Boolean, default: true },
|
||||
isVerified: { type: Boolean, default: false },
|
||||
role: {
|
||||
type: [String],
|
||||
enum: ["user", "admin", "superadmin", "demo" satisfies UserRole],
|
||||
default: ["user"],
|
||||
},
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Team",
|
||||
immutable: true,
|
||||
},
|
||||
checkTTL: { type: Number },
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
UserSchema.pre("save", function (next) {
|
||||
if (!this.isModified("password")) {
|
||||
return next();
|
||||
}
|
||||
const salt = bcrypt.genSaltSync(10);
|
||||
this.password = bcrypt.hashSync(this.password, salt);
|
||||
next();
|
||||
});
|
||||
|
||||
UserSchema.pre("findOneAndUpdate", function (next) {
|
||||
const update = this.getUpdate();
|
||||
if (update && "password" in update) {
|
||||
const salt = bcrypt.genSaltSync(10);
|
||||
(update as any).password = bcrypt.hashSync((update as any).password, salt);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
UserSchema.pre("findOneAndDelete", async function (next) {
|
||||
try {
|
||||
const userToDelete = await this.model.findOne(this.getFilter());
|
||||
if (!userToDelete) return next();
|
||||
if (userToDelete.role.includes("superadmin")) {
|
||||
await Team.deleteOne({ _id: userToDelete.teamId });
|
||||
await MonitorModel.deleteMany({ userId: userToDelete._id });
|
||||
await this.model.deleteMany({ teamId: userToDelete.teamId, _id: { $ne: userToDelete._id } });
|
||||
await Notification.deleteMany({ teamId: userToDelete.teamId });
|
||||
}
|
||||
next();
|
||||
} catch (error) {
|
||||
next(error as Error);
|
||||
}
|
||||
});
|
||||
|
||||
UserSchema.methods.comparePassword = async function (submittedPassword: string) {
|
||||
return bcrypt.compare(submittedPassword, this.password);
|
||||
};
|
||||
|
||||
const UserModel = model<UserDocument>("User", UserSchema);
|
||||
|
||||
export type { UserDocument };
|
||||
export { UserModel };
|
||||
export default UserModel;
|
||||
@@ -9,3 +9,6 @@ export { default as MonitorStatsModel } from "@/db/models/MonitorStats.js";
|
||||
|
||||
export * from "@/db/models/StatusPage.js";
|
||||
export { default as StatusPageModel } from "@/db/models/StatusPage.js";
|
||||
|
||||
export * from "@/db/models/User.js";
|
||||
export { default as UserModel } from "@/db/models/User.js";
|
||||
|
||||
@@ -9,3 +9,6 @@ export { default as MongoMonitorStatsRepository } from "@/repositories/monitor-s
|
||||
|
||||
export * from "@/repositories/status-pages/IStatusPagesRepository.js";
|
||||
export { default as MongoStatusPagesRepository } from "@/repositories/status-pages/MongoStatusPagesRepository.js";
|
||||
|
||||
export * from "@/repositories/users/IUserRepository.js";
|
||||
export { default as MongoUserRepository } from "@/repositories/users/MongoUserRepository.js";
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { User } from "@/types/index.js";
|
||||
|
||||
export interface IUsersRepository {
|
||||
// create
|
||||
// fetch
|
||||
// update
|
||||
// delete
|
||||
// other
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { IUsersRepository } from "@/repositories/index.js";
|
||||
class MongoUserRepository implements IUsersRepository {}
|
||||
|
||||
export default MongoUserRepository;
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IMonitorsRepository } from "@/repositories/index.js";
|
||||
import type { User } from "@/types/index.js";
|
||||
|
||||
const SERVICE_NAME = "userService";
|
||||
|
||||
@@ -62,12 +63,12 @@ class UserService {
|
||||
return this.jwt.sign(payloadData, tokenSecret, { expiresIn: tokenTTL });
|
||||
};
|
||||
|
||||
registerUser = async (user: any, file: any) => {
|
||||
registerUser = async (user: Partial<User>, inviteToken: string, file: any) => {
|
||||
// Create a new user
|
||||
// If superAdmin exists, a token should be attached to all further register requests
|
||||
const superAdminExists = await this.db.userModule.checkSuperadmin();
|
||||
if (superAdminExists) {
|
||||
const invitedUser = await this.db.inviteModule.getInviteTokenAndDelete(user.inviteToken);
|
||||
const invitedUser = await this.db.inviteModule.getInviteTokenAndDelete(inviteToken);
|
||||
user.role = invitedUser.role;
|
||||
user.teamId = invitedUser.teamId;
|
||||
} else {
|
||||
|
||||
@@ -3,3 +3,4 @@ export * from "@/types/monitor.js";
|
||||
export * from "@/types/monitorStats.js";
|
||||
export * from "@/types/statusPage.js";
|
||||
export * from "@/types/network.js";
|
||||
export * from "@/types/user.js";
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
export const UserRoles = ["user", "admin", "superadmin", "demo"] as const;
|
||||
export type UserRole = (typeof UserRoles)[number];
|
||||
|
||||
export interface UserProfileImage {
|
||||
data?: Buffer;
|
||||
contentType?: string;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
password: string;
|
||||
avatarImage?: string;
|
||||
profileImage?: UserProfileImage;
|
||||
isActive: boolean;
|
||||
isVerified: boolean;
|
||||
role: UserRole[];
|
||||
teamId: string;
|
||||
checkTTL?: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
Reference in New Issue
Block a user