133 lines
3.7 KiB
TypeScript
133 lines
3.7 KiB
TypeScript
// src/modules/user/service/user.service.ts
|
|
import bcrypt from "bcryptjs";
|
|
import { Op } from "sequelize";
|
|
import { User } from "../../../models/User";
|
|
|
|
type CreateUserDTO = {
|
|
fullname: string;
|
|
email: string;
|
|
password: string;
|
|
roleId: string;
|
|
isActive?: boolean;
|
|
};
|
|
|
|
type UpdateUserDTO = Partial<{
|
|
fullname: string;
|
|
email: string;
|
|
password: string;
|
|
roleId: string;
|
|
isActive: boolean;
|
|
}>;
|
|
|
|
class UserServiceClass {
|
|
private async hashPassword(password: string) {
|
|
const saltRounds = 10;
|
|
return await bcrypt.hash(password, saltRounds);
|
|
}
|
|
|
|
private sanitize(user: User) {
|
|
const json = user.toJSON() as any;
|
|
delete json.password;
|
|
return json;
|
|
}
|
|
|
|
async create(data: CreateUserDTO) {
|
|
const exists = await User.findOne({ where: { email: data.email } });
|
|
if (exists) throw new Error("ایمیل تکراری است");
|
|
|
|
const hashed = await this.hashPassword(data.password);
|
|
|
|
const user = await User.create({
|
|
fullname: data.fullname,
|
|
email: data.email,
|
|
password: hashed,
|
|
roleId: data.roleId,
|
|
isActive: data.isActive ?? true,
|
|
});
|
|
|
|
return this.sanitize(user);
|
|
}
|
|
|
|
async getAll(query?: { search?: string; isActive?: boolean }) {
|
|
const where: any = {};
|
|
|
|
if (typeof query?.isActive === "boolean") where.isActive = query.isActive;
|
|
|
|
if (query?.search) {
|
|
where[Op.or] = [
|
|
{ fullname: { [Op.iLike]: `%${query.search}%` } },
|
|
{ email: { [Op.iLike]: `%${query.search}%` } },
|
|
];
|
|
}
|
|
|
|
const users = await User.findAll({
|
|
where,
|
|
order: [["createdAt", "DESC"]],
|
|
// اگر relation Role را attach کردهاید:
|
|
// include: [{ association: "role" }],
|
|
});
|
|
|
|
return users.map((u:any) => this.sanitize(u));
|
|
}
|
|
|
|
async getById(id: string) {
|
|
const user = await User.findByPk(id);
|
|
if (!user) throw new Error("کاربر یافت نشد");
|
|
return this.sanitize(user);
|
|
}
|
|
|
|
async update(id: string, data: UpdateUserDTO) {
|
|
const user = await User.findByPk(id);
|
|
if (!user) throw new Error("کاربر یافت نشد");
|
|
|
|
// اگر ایمیل تغییر میکند، تکراری نبودن را چک کن
|
|
if (data.email && data.email !== user.email) {
|
|
const exists = await User.findOne({ where: { email: data.email } });
|
|
if (exists) throw new Error("ایمیل تکراری است");
|
|
}
|
|
|
|
const updatePayload: any = { ...data };
|
|
|
|
if (typeof data.password === "string" && data.password.trim().length > 0) {
|
|
updatePayload.password = await this.hashPassword(data.password);
|
|
} else {
|
|
delete updatePayload.password;
|
|
}
|
|
|
|
await user.update(updatePayload);
|
|
return this.sanitize(user);
|
|
}
|
|
|
|
async toggleActive(id: string, isActive: boolean) {
|
|
const user = await User.findByPk(id);
|
|
if (!user) throw new Error("کاربر یافت نشد");
|
|
|
|
await user.update({ isActive });
|
|
return this.sanitize(user);
|
|
}
|
|
|
|
async remove(id: string) {
|
|
const user = await User.findByPk(id);
|
|
if (!user) throw new Error("کاربر یافت نشد");
|
|
await user.destroy();
|
|
return true;
|
|
}
|
|
|
|
// Auth: لاگین با ایمیل و پسورد
|
|
async login(email: string, password: string) {
|
|
const user = await User.findOne({ where: { email } });
|
|
if (!user) throw new Error("ایمیل یا رمز عبور اشتباه است");
|
|
|
|
if (!user.isActive) throw new Error("حساب کاربری غیرفعال است");
|
|
|
|
const ok = await bcrypt.compare(password, user.password);
|
|
if (!ok) throw new Error("ایمیل یا رمز عبور اشتباه است");
|
|
|
|
// اینجا معمولاً JWT صادر میکنید؛ من فقط user sanitize برمیگردونم
|
|
return this.sanitize(user);
|
|
}
|
|
}
|
|
|
|
const UserService = new UserServiceClass()
|
|
export default UserService;
|