Files
permit-api/src/user/user.service.ts

196 lines
5.2 KiB
TypeScript

import mongoose from "mongoose";
import { generateId, generateToken } from "../utils/id";
import { CreateUserInput, UpdateUserInput, userModel } from "./user.schema";
import { sendMail } from "../utils/mail";
import { AuthenticatedUser } from "../auth";
import { createUserConfig } from "../userConfig/userConfig.service";
export const ErrUserNotFound = new Error("user not found");
export const ErrOpNotValid = new Error("operation is not valid");
export const ErrMissingOrdId = new Error(
"orgId is required when role is client"
);
export async function createUser(
input: CreateUserInput,
user: AuthenticatedUser
) {
if (
input.role === "superAdmin" ||
(input.role == "admin" && user.role != "superAdmin")
) {
throw ErrOpNotValid;
}
if (input.role == "client" && !input.orgId) {
throw ErrMissingOrdId;
}
const token = await generateToken();
const newUser = await userModel.create({
tenantId: user.tenantId,
pid: generateId(),
name: input.firstName + " " + input.lastName,
createdAt: new Date(),
createdBy: user.userId,
token: {
value: token,
expiry: new Date(Date.now() + 3600 * 48 * 1000),
},
status: "invited",
...input,
});
await createUserConfig(newUser.id, newUser.tenantId);
const sent = await sendMail(
input.email,
"You have been invited to Quicker Permtis.",
`Click <a href="${
process.env.SERVER_DOMAIN +
"/auth/webauthn/register?token=" +
token +
"&email=" +
newUser.email
}">here</a> to register.</p><p>The above link expires in 48 hours</p>`
);
return userModel
.findOne({ pid: newUser.pid })
.populate({ path: "orgId", select: "pid name avatar" });
}
export async function resetUser(userId: string, user: AuthenticatedUser) {
if (user.role !== "superAdmin") {
throw ErrOpNotValid;
}
const token = await generateToken();
const userInDb = await userModel.findOneAndUpdate(
{ pid: userId, tenantId: user.tenantId },
{
$set: {
token: {
value: token,
expiry: new Date(Date.now() + 3600 * 48 * 1000),
},
},
},
{ new: true }
);
if (!userInDb) {
throw ErrUserNotFound;
}
const sent = await sendMail(
userInDb.email,
"Quicker Permits account reset",
`<p>Click <a href="${
process.env.SERVER_DOMAIN +
"/auth/webauthn/register?token=" +
token +
"&email=" +
userInDb.email
}">here</a> to reset.</p><p>The above link expires in 48 hours</p>`
);
}
export async function getUser(userId: string) {
if (mongoose.Types.ObjectId.isValid(userId)) {
return await userModel
.findById(userId)
.populate({ path: "orgId", select: "_id pid name" });
}
return await userModel
.findOne({
$and: [{ pid: userId }],
})
.populate({ path: "orgId", select: "_id pid name" });
}
export async function getUserWithoutPopulate(userId: string) {
return await userModel.findById(userId);
}
export async function getUserByToken(token: string) {
return await userModel.findOne({ "token.value": token });
}
export async function getUserByEmail(email: string) {
return await userModel.findOne({ email: email });
}
export async function listUsers(user: AuthenticatedUser) {
if (user.role === "client") {
return await userModel
.find({
$and: [
{ tenantId: user.tenantId },
{
$or: [
{ orgId: user.orgId, role: "client" },
{ role: { $in: ["admin", "team", "superAdmin"] } },
],
},
{ dev: { $ne: true } },
],
})
.select(
"_id pid orgId firstName lastName name email role avatar status createdAt createdBy lastLogin"
)
.populate({ path: "orgId", select: "_id pid name avatar" })
.populate({ path: "createdBy", select: "_id pid name avatar" });
}
return await userModel
.find({ $and: [{ tenantId: user.tenantId }, { dev: { $ne: true } }] })
.select(
"_id pid orgId firstName lastName name email role avatar status createdAt createdBy lastLogin"
)
.populate({ path: "orgId", select: "_id pid name avatar" })
.populate({ path: "createdBy", select: "_id pid name avatar" });
}
export async function updateUser(
userId: string,
input: UpdateUserInput,
user: AuthenticatedUser
) {
if (
input.role === "superAdmin" ||
(input.role == "admin" && user.role != "superAdmin")
) {
throw ErrOpNotValid;
}
return await userModel
.findOneAndUpdate({ pid: userId }, input, {
new: true,
})
.select(
"_id pid orgId firstName lastName name email role avatar status createdAt createdBy lastLogin"
)
.populate({ path: "orgId", select: "_id pid name avatar" });
}
export async function updateUserInternal(
userId: string,
input: UpdateUserInput
) {
return await userModel
.findOneAndUpdate({ pid: userId }, input, {
new: true,
})
.select(
"_id pid orgId firstName lastName name email role avatar status createdAt createdBy lastLogin"
);
}
export async function deleteUser(userId: string, tenantId: string) {
return await userModel.deleteOne({
$and: [{ pid: userId }, { tenantId: tenantId }],
});
}