auth service
This commit is contained in:
1 parent
a3e24b5420
commit
9248870716
29 files changed
+394
-288
No files matched your search
@@ -132,22 +132,6 @@ class AuthController {
|
||||
});
|
||||
res.status(200).json({ message: "Logout successful" });
|
||||
};
|
||||
|
||||
cleanup = async (req: Request, res: Response) => {
|
||||
try {
|
||||
await this.authService.cleanup();
|
||||
res.status(200).json({ message: "Cleanup successful" });
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
cleanMonitors = async (req: Request, res: Response) => {
|
||||
try {
|
||||
await this.authService.cleanMonitors();
|
||||
res.status(200).json({ message: "Monitors cleanup successful" });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: "Internal server error" });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default AuthController;
|
||||
@@ -73,7 +73,10 @@ class RecoveryController implements IRecoveryController {
|
||||
if (!recoveryToken) {
|
||||
throw new ApiError("Invalid recovery token", 400);
|
||||
}
|
||||
await this.authService.changePassword(recoveryToken.userId, password);
|
||||
await this.authService.changePassword(
|
||||
recoveryToken.userId.toString(),
|
||||
password
|
||||
);
|
||||
await this.recoveryService.delete(recoveryToken._id);
|
||||
|
||||
const user = await User.findById(recoveryToken.userId);
|
||||
|
||||
@@ -3,6 +3,18 @@ import mongoose, { Schema, Document, Types } from "mongoose";
|
||||
export const ScopeTypes = ["organization", "team"] as const;
|
||||
export type ScopeType = (typeof ScopeTypes)[number];
|
||||
|
||||
type IdLike = string | Types.ObjectId;
|
||||
|
||||
export type RoleLike = {
|
||||
_id: IdLike;
|
||||
organizationId: IdLike;
|
||||
name: string;
|
||||
scope: ScopeType;
|
||||
permissions: string[];
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export interface IRole extends Document {
|
||||
_id: Types.ObjectId;
|
||||
organizationId: Types.ObjectId;
|
||||
@@ -15,7 +27,7 @@ export interface IRole extends Document {
|
||||
|
||||
const roleSchema = new Schema<IRole>(
|
||||
{
|
||||
organizationId: { type: Schema.Types.ObjectId, ref: "Org" },
|
||||
organizationId: { type: Schema.Types.ObjectId, ref: "Org", required: true },
|
||||
name: { type: String, required: true },
|
||||
scope: { type: String, enum: ScopeTypes, required: true },
|
||||
permissions: { type: [String], default: [] },
|
||||
|
||||
@@ -2,43 +2,6 @@ import mongoose, { Schema, Document, Types } from "mongoose";
|
||||
import { IRole, ITeam } from "@/db/models/index.js";
|
||||
import { Entitlements } from "@/types/entitlements.js";
|
||||
|
||||
export interface ITokenizedUser {
|
||||
sub: string;
|
||||
email: string;
|
||||
orgId: string;
|
||||
}
|
||||
export interface IUserContext {
|
||||
sub: string;
|
||||
email: string;
|
||||
orgId: string;
|
||||
teamIds?: string[];
|
||||
teams?: ITeam[];
|
||||
currentTeamId?: string;
|
||||
roles?: {
|
||||
orgRole?: IRole;
|
||||
teamRole: IRole;
|
||||
};
|
||||
entitlements: Entitlements;
|
||||
}
|
||||
|
||||
export interface IUserReturnable {
|
||||
id: string;
|
||||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
org: {
|
||||
name: string;
|
||||
planKey: string;
|
||||
permissions: string[];
|
||||
};
|
||||
teams: {
|
||||
id: string;
|
||||
name: string;
|
||||
permissions: string[];
|
||||
}[];
|
||||
entitlements: Entitlements;
|
||||
}
|
||||
|
||||
export interface IUser extends Document {
|
||||
_id: Types.ObjectId;
|
||||
firstName: string;
|
||||
|
||||
@@ -7,13 +7,7 @@ export { MonitorStats } from "./monitors/MonitorStats.js";
|
||||
export type { IMonitorStats } from "./monitors/MonitorStats.js";
|
||||
|
||||
export { User } from "./auth/User.js";
|
||||
export type {
|
||||
IUser,
|
||||
ITokenizedUser,
|
||||
IUserReturnable,
|
||||
IUserContext,
|
||||
IUserProfile,
|
||||
} from "./auth/User.js";
|
||||
export type { IUser, IUserProfile } from "./auth/User.js";
|
||||
|
||||
export {
|
||||
NotificationChannel,
|
||||
@@ -25,7 +19,7 @@ export { Invite } from "./auth/Invite.js";
|
||||
export type { IInvite } from "./auth/Invite.js";
|
||||
|
||||
export { Role } from "./auth/Role.js";
|
||||
export type { IRole } from "./auth/Role.js";
|
||||
export type { IRole, RoleLike } from "./auth/Role.js";
|
||||
|
||||
export { Maintenance } from "./maintenance/Maintenance.js";
|
||||
export type { IMaintenance } from "./maintenance/Maintenance.js";
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
MongoInviteRepository,
|
||||
MongoTeamMembershipRepository,
|
||||
MongoOrgRepository,
|
||||
MongoOrgMembershipRepository,
|
||||
} from "@/repositories/index.js";
|
||||
|
||||
export const initServices = async () => {
|
||||
@@ -56,7 +57,8 @@ export const initServices = async () => {
|
||||
const teamRepository = new MongoTeamRepository();
|
||||
const inviteRepository = new MongoInviteRepository();
|
||||
const teamMembershipRepository = new MongoTeamMembershipRepository();
|
||||
const mongoOrgRepository = new MongoOrgRepository();
|
||||
const orgRepository = new MongoOrgRepository();
|
||||
const orgMembershipRepository = new MongoOrgMembershipRepository();
|
||||
|
||||
const checkService = new CheckService(checksRepository, monitorRepository);
|
||||
const inviteService = new InviteService(
|
||||
@@ -112,7 +114,9 @@ export const initServices = async () => {
|
||||
entitlementsProvider,
|
||||
monitorRepository,
|
||||
userRepository,
|
||||
mongoOrgRepository,
|
||||
roleRepository,
|
||||
orgRepository,
|
||||
orgMembershipRepository,
|
||||
teamRepository,
|
||||
teamMembershipRepository
|
||||
);
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { OrgMembership } from "@/types/domain/index.js";
|
||||
|
||||
export interface IOrgMembershipRepository {
|
||||
// create
|
||||
create(orgMembershipData: Partial<OrgMembership>): Promise<OrgMembership>;
|
||||
// single fetch
|
||||
findByUserId(userId: string): Promise<OrgMembership | null>;
|
||||
// collection fetch
|
||||
// update
|
||||
update(orgMembership: Partial<OrgMembership>): Promise<OrgMembership | null>;
|
||||
// delete
|
||||
deleteById(membershipId: string): Promise<boolean>;
|
||||
}
|
||||
@@ -14,6 +14,39 @@ class MongoOrgMembershipRepository implements IOrgMembershipRepository {
|
||||
updatedAt: doc.updatedAt,
|
||||
};
|
||||
};
|
||||
|
||||
create = async (orgMembershipData: Partial<OrgMembershipEntity>) => {
|
||||
const membership = await OrgMembership.create(orgMembershipData);
|
||||
return this.toEntity(membership);
|
||||
};
|
||||
|
||||
findByUserId = async (userId: string) => {
|
||||
const orgMembership = await OrgMembership.findOne({
|
||||
userId,
|
||||
});
|
||||
if (!orgMembership) return null;
|
||||
return this.toEntity(orgMembership);
|
||||
};
|
||||
|
||||
update = async (orgMembership: Partial<OrgMembershipEntity>) => {
|
||||
const updated = await OrgMembership.findOneAndUpdate(
|
||||
{ _id: orgMembership.id },
|
||||
{
|
||||
$set: {
|
||||
...orgMembership,
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
},
|
||||
{ new: true }
|
||||
);
|
||||
if (!updated) return null;
|
||||
return this.toEntity(updated);
|
||||
};
|
||||
|
||||
deleteById = async (membershipId: string) => {
|
||||
const result = await OrgMembership.deleteOne({ _id: membershipId });
|
||||
return result.deletedCount === 1;
|
||||
};
|
||||
}
|
||||
|
||||
export default MongoOrgMembershipRepository;
|
||||
@@ -4,7 +4,10 @@ export interface IOrgRepository {
|
||||
// create
|
||||
create(orgData: Partial<Org>): Promise<Org>;
|
||||
// single fetch
|
||||
findById(orgId: string): Promise<Org | null>;
|
||||
// collection fetch
|
||||
// update
|
||||
updateById(orgId: string, orgData: Partial<Org>): Promise<Org | null>;
|
||||
// delete
|
||||
deleteById(orgId: string): Promise<boolean>;
|
||||
}
|
||||
@@ -29,6 +29,27 @@ class MongoOrgRepository implements IOrgRepository {
|
||||
const org = await Org.create(orgData);
|
||||
return this.toEntity(org);
|
||||
};
|
||||
|
||||
findById = async (orgId: string) => {
|
||||
const org = await Org.findById(orgId);
|
||||
if (!org) return null;
|
||||
return this.toEntity(org);
|
||||
};
|
||||
|
||||
updateById = async (orgId: string, orgData: Partial<OrgEntity>) => {
|
||||
const updated = await Org.findOneAndUpdate(
|
||||
{ _id: orgId },
|
||||
{ $set: orgData },
|
||||
{ new: true }
|
||||
);
|
||||
if (!updated) return null;
|
||||
return this.toEntity(updated);
|
||||
};
|
||||
|
||||
deleteById = async (orgId: string) => {
|
||||
const result = await Org.deleteOne({ _id: orgId });
|
||||
return result.deletedCount === 1;
|
||||
};
|
||||
}
|
||||
|
||||
export default MongoOrgRepository;
|
||||
@@ -2,10 +2,11 @@ import { Role } from "@/types/domain/index.js";
|
||||
|
||||
export interface IRoleRepository {
|
||||
// create
|
||||
createMany(rolesData: Role[]): Promise<Role[]>;
|
||||
createMany(rolesData: Array<Partial<Role>>): Promise<Role[]>;
|
||||
// single fetch
|
||||
findById(roleId: string, orgId: string): Promise<Role | null>;
|
||||
// collection fetch
|
||||
// update
|
||||
// delete
|
||||
deleteMany(roleIds: string[]): Promise<number>;
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Types } from "mongoose";
|
||||
import type { IRoleRepository } from "./IRoleRepository.js";
|
||||
import { Role } from "@/db/models/index.js";
|
||||
import type { IRole } from "@/db/models/index.js";
|
||||
import type { RoleLike, IRole } from "@/db/models/index.js";
|
||||
import type { Role as RoleEntity } from "@/types/domain/index.js";
|
||||
|
||||
class MongoRoleRepository implements IRoleRepository {
|
||||
private toEntity = (doc: IRole): RoleEntity => {
|
||||
private toEntity = (doc: RoleLike): RoleEntity => {
|
||||
return {
|
||||
id: doc._id.toString(),
|
||||
organizationId: doc.organizationId.toString(),
|
||||
@@ -18,24 +17,18 @@ class MongoRoleRepository implements IRoleRepository {
|
||||
};
|
||||
|
||||
createMany = async (rolesData: RoleEntity[]): Promise<RoleEntity[]> => {
|
||||
const docs = rolesData.map((role) => ({
|
||||
...role,
|
||||
organizationId: role.organizationId
|
||||
? new Types.ObjectId(role.organizationId)
|
||||
: undefined,
|
||||
}));
|
||||
|
||||
const insertedRoles = await Role.insertMany(docs);
|
||||
const insertedRoles = await Role.insertMany(rolesData);
|
||||
return insertedRoles.map((r) => {
|
||||
return this.toEntity({
|
||||
_id: r._id,
|
||||
organizationId: r.organizationId || new Types.ObjectId(),
|
||||
const e = {
|
||||
_id: r._id.toString(),
|
||||
organizationId: r.organizationId.toString(),
|
||||
name: r.name,
|
||||
scope: r.scope,
|
||||
permissions: r.permissions,
|
||||
createdAt: r.createdAt,
|
||||
updatedAt: r.updatedAt,
|
||||
});
|
||||
};
|
||||
return this.toEntity(e);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -52,6 +45,11 @@ class MongoRoleRepository implements IRoleRepository {
|
||||
}
|
||||
return this.toEntity(role);
|
||||
};
|
||||
|
||||
deleteMany = async (roleIds: string[]) => {
|
||||
const result = await Role.deleteMany({ _id: { $in: roleIds } });
|
||||
return result.deletedCount ?? 0;
|
||||
};
|
||||
}
|
||||
|
||||
export default MongoRoleRepository;
|
||||
@@ -1,10 +1,17 @@
|
||||
import { TeamMembership } from "@/types/domain/index.js";
|
||||
import {
|
||||
TeamMembership,
|
||||
TeamMembershipWithDetails,
|
||||
} from "@/types/domain/index.js";
|
||||
export interface ITeamMembershipRepository {
|
||||
// create
|
||||
create(membership: Partial<TeamMembership>): Promise<TeamMembership>;
|
||||
// single fetch
|
||||
findByUserId(userId: string, teamId: string): Promise<TeamMembership | null>;
|
||||
findByUserIdWithRole(
|
||||
userId: string
|
||||
): Promise<TeamMembershipWithDetails[] | null>;
|
||||
// collection fetch
|
||||
// update
|
||||
// delete
|
||||
deleteById(membershipId: string): Promise<boolean>;
|
||||
}
|
||||
@@ -1,6 +1,15 @@
|
||||
import { type ITeamMembership, TeamMembership } from "@/db/models/index.js";
|
||||
import type { ITeamMembershipRepository } from "@/repositories/index.js";
|
||||
import { TeamMembership as TeamMembershipEntity } from "@/types/domain/index.js";
|
||||
import {
|
||||
TeamMembership as TeamMembershipEntity,
|
||||
TeamMembershipWithDetails,
|
||||
} from "@/types/domain/index.js";
|
||||
import type { IRole } from "@/db/models/index.js";
|
||||
|
||||
type TeamMembershipPopulated = Omit<ITeamMembership, "roleId"> & {
|
||||
roleId: IRole;
|
||||
};
|
||||
|
||||
class MongoTeamMembershipRepository implements ITeamMembershipRepository {
|
||||
private toEntity = (doc: ITeamMembership): TeamMembershipEntity => {
|
||||
return {
|
||||
@@ -13,6 +22,28 @@ class MongoTeamMembershipRepository implements ITeamMembershipRepository {
|
||||
updatedAt: doc.updatedAt,
|
||||
};
|
||||
};
|
||||
|
||||
private toEntityWithDetails = (
|
||||
doc: TeamMembershipPopulated
|
||||
): TeamMembershipWithDetails => {
|
||||
return {
|
||||
id: doc._id.toString(),
|
||||
orgId: doc.orgId.toString(),
|
||||
teamId: doc.teamId.toString(),
|
||||
userId: doc.userId.toString(),
|
||||
roleId: doc.roleId.toString(),
|
||||
role: {
|
||||
id: doc.roleId._id.toString(),
|
||||
organizationId: doc.roleId.organizationId.toString(),
|
||||
name: doc.roleId.name,
|
||||
permissions: doc.roleId.permissions,
|
||||
createdAt: doc.roleId.createdAt,
|
||||
updatedAt: doc.roleId.updatedAt,
|
||||
},
|
||||
createdAt: doc.createdAt,
|
||||
updatedAt: doc.updatedAt,
|
||||
};
|
||||
};
|
||||
create = async (membership: Partial<TeamMembershipEntity>) => {
|
||||
const created = await TeamMembership.create(membership);
|
||||
return this.toEntity(created);
|
||||
@@ -26,6 +57,20 @@ class MongoTeamMembershipRepository implements ITeamMembershipRepository {
|
||||
if (!membership) return null;
|
||||
return this.toEntity(membership);
|
||||
};
|
||||
|
||||
findByUserIdWithRole = async (userId: string) => {
|
||||
const teamMembershipsWithRole = await TeamMembership.find({
|
||||
userId: userId,
|
||||
})
|
||||
.populate<{ roleId: IRole }>("roleId")
|
||||
.lean<TeamMembershipPopulated[]>();
|
||||
return teamMembershipsWithRole.map(this.toEntityWithDetails);
|
||||
};
|
||||
|
||||
deleteById = async (membershipId: string) => {
|
||||
const result = await TeamMembership.deleteOne({ _id: membershipId });
|
||||
return result.deletedCount === 1;
|
||||
};
|
||||
}
|
||||
|
||||
export default MongoTeamMembershipRepository;
|
||||
@@ -6,6 +6,8 @@ export interface ITeamRepository {
|
||||
// single fetch
|
||||
findById(teamId: string, orgId: string): Promise<Team | null>;
|
||||
// collection fetch
|
||||
findManyById(teamIds: string[]): Promise<Team[]>;
|
||||
// update
|
||||
// delete
|
||||
deleteById(teamId: string): Promise<boolean>;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { ITeamRepository } from "@/repositories/index.js";
|
||||
import { type ITeam, Team } from "@/db/models/index.js";
|
||||
import { Team as TeamEntity } from "@/types/domain/index.js";
|
||||
|
||||
class MongoTeamRepository implements ITeamRepository {
|
||||
private toEntity = (doc: ITeam): TeamEntity => {
|
||||
return {
|
||||
@@ -29,6 +30,16 @@ class MongoTeamRepository implements ITeamRepository {
|
||||
}
|
||||
return this.toEntity(team);
|
||||
};
|
||||
|
||||
findManyById = async (teamIds: string[]) => {
|
||||
const teams = await Team.find({ _id: { $in: teamIds } });
|
||||
return teams.map(this.toEntity);
|
||||
};
|
||||
|
||||
deleteById = async (teamId: string) => {
|
||||
const result = await Team.deleteOne({ _id: teamId });
|
||||
return result.deletedCount === 1;
|
||||
};
|
||||
}
|
||||
|
||||
export default MongoTeamRepository;
|
||||
@@ -6,5 +6,10 @@ export interface IUserRepository {
|
||||
findByEmail: (email: string) => Promise<User | null>;
|
||||
// collection fetch
|
||||
// update
|
||||
updateById: (
|
||||
userId: string,
|
||||
updateData: Partial<User>
|
||||
) => Promise<User | null>;
|
||||
// delete
|
||||
deleteById(userId: string): Promise<boolean>;
|
||||
}
|
||||
@@ -27,6 +27,21 @@ class MongoUserRepository implements IUserRepository {
|
||||
if (!user) return null;
|
||||
return this.toEntity(user);
|
||||
};
|
||||
|
||||
updateById = async (userId: string, updateData: Partial<UserEntity>) => {
|
||||
const result = await User.findOneAndUpdate(
|
||||
{ _id: userId },
|
||||
{ $set: updateData },
|
||||
{ new: true }
|
||||
);
|
||||
if (!result) return null;
|
||||
return this.toEntity(result);
|
||||
};
|
||||
|
||||
deleteById = async (userId: string) => {
|
||||
const result = await User.deleteOne({ _id: userId });
|
||||
return result.deletedCount === 1;
|
||||
};
|
||||
}
|
||||
|
||||
export default MongoUserRepository;
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Router } from "express";
|
||||
|
||||
import { AuthController } from "@/controllers/index.js";
|
||||
import { verifyToken } from "@/middleware/VerifyToken.js";
|
||||
import { validateBody } from "@/middleware/validation.js";
|
||||
import {
|
||||
registerSchema,
|
||||
@@ -36,8 +35,6 @@ class AuthRoutes {
|
||||
);
|
||||
this.router.post("/logout", this.controller.logout);
|
||||
// this.router.get("/me", verifyToken, this.controller.me);
|
||||
this.router.post("/cleanup", this.controller.cleanup);
|
||||
this.router.post("/cleanup-monitors", this.controller.cleanMonitors);
|
||||
};
|
||||
|
||||
getRouter() {
|
||||
|
||||
@@ -1,20 +1,4 @@
|
||||
import bcrypt from "bcryptjs";
|
||||
import {
|
||||
User,
|
||||
Org,
|
||||
OrgMembership,
|
||||
Team,
|
||||
ITeam,
|
||||
IUserReturnable,
|
||||
TeamMembership,
|
||||
Role,
|
||||
IRole,
|
||||
IUser,
|
||||
ITokenizedUser,
|
||||
Check,
|
||||
NotificationChannel,
|
||||
} from "@/db/models/index.js";
|
||||
import { Types } from "mongoose";
|
||||
import ApiError from "@/utils/ApiError.js";
|
||||
import { IJobQueue } from "../infrastructure/JobQueue.js";
|
||||
import { hashPassword } from "@/utils/JWTUtils.js";
|
||||
@@ -26,11 +10,18 @@ import type {
|
||||
IMonitorRepository,
|
||||
IUserRepository,
|
||||
IOrgRepository,
|
||||
IRoleRepository,
|
||||
ITeamRepository,
|
||||
IOrgMembershipRepository,
|
||||
ITeamMembershipRepository,
|
||||
} from "@/repositories/index.js";
|
||||
import type { Invite } from "@/types/domain/index.js";
|
||||
import mongoose from "mongoose";
|
||||
import type {
|
||||
User,
|
||||
UserReturnable,
|
||||
TokenizedUser,
|
||||
Team,
|
||||
Invite,
|
||||
} from "@/types/domain/index.js";
|
||||
|
||||
const SERVICE_NAME = "AuthService";
|
||||
|
||||
@@ -46,29 +37,26 @@ export type LoginData = {
|
||||
password: string;
|
||||
};
|
||||
|
||||
export type AuthResult = Partial<ITokenizedUser>;
|
||||
export type AuthResult = Partial<TokenizedUser>;
|
||||
|
||||
export interface IAuthService {
|
||||
register(signupData: RegisterData): Promise<{
|
||||
tokenizedUser: ITokenizedUser;
|
||||
returnableUser: IUserReturnable;
|
||||
tokenizedUser: TokenizedUser;
|
||||
returnableUser: UserReturnable;
|
||||
}>;
|
||||
registerWithInvite(
|
||||
invite: Invite,
|
||||
signupData: RegisterData
|
||||
): Promise<{
|
||||
tokenizedUser: ITokenizedUser;
|
||||
returnableUser: IUserReturnable;
|
||||
tokenizedUser: TokenizedUser;
|
||||
returnableUser: UserReturnable;
|
||||
}>;
|
||||
login(loginData: LoginData): Promise<{
|
||||
tokenizedUser: ITokenizedUser;
|
||||
returnableUser: IUserReturnable;
|
||||
tokenizedUser: TokenizedUser;
|
||||
returnableUser: UserReturnable;
|
||||
}>;
|
||||
getTeamIds(userId: string): Promise<string[]>;
|
||||
getTeams(teamIds: string[]): Promise<ITeam[]>;
|
||||
cleanup(): Promise<void>;
|
||||
cleanMonitors(): Promise<void>;
|
||||
changePassword(userId: Types.ObjectId, newPassword: string): Promise<boolean>;
|
||||
getTeams(teamIds: string[]): Promise<Team[]>;
|
||||
changePassword(userId: string, newPassword: string): Promise<boolean>;
|
||||
}
|
||||
|
||||
class AuthService implements IAuthService {
|
||||
@@ -77,7 +65,9 @@ class AuthService implements IAuthService {
|
||||
private entitlementsProvider: IEntitlementsProvider;
|
||||
private monitorRepository: IMonitorRepository;
|
||||
private userRepository: IUserRepository;
|
||||
private roleRepository: IRoleRepository;
|
||||
private orgRepository: IOrgRepository;
|
||||
private orgMembershipRepository: IOrgMembershipRepository;
|
||||
private teamRepository: ITeamRepository;
|
||||
private teamMembershipRepository: ITeamMembershipRepository;
|
||||
|
||||
@@ -86,7 +76,9 @@ class AuthService implements IAuthService {
|
||||
entitlementsProvider: IEntitlementsProvider,
|
||||
monitorRepository: IMonitorRepository,
|
||||
userRepository: IUserRepository,
|
||||
roleRepository: IRoleRepository,
|
||||
orgRepository: IOrgRepository,
|
||||
orgMembershipRepository: IOrgMembershipRepository,
|
||||
teamRepository: ITeamRepository,
|
||||
teamMembershipRepository: ITeamMembershipRepository
|
||||
) {
|
||||
@@ -96,8 +88,10 @@ class AuthService implements IAuthService {
|
||||
this.monitorRepository = monitorRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.orgRepository = orgRepository;
|
||||
this.roleRepository = roleRepository;
|
||||
this.teamRepository = teamRepository;
|
||||
this.teamMembershipRepository = teamMembershipRepository;
|
||||
this.orgMembershipRepository = orgMembershipRepository;
|
||||
}
|
||||
|
||||
async register(signupData: RegisterData) {
|
||||
@@ -113,7 +107,7 @@ class AuthService implements IAuthService {
|
||||
};
|
||||
|
||||
try {
|
||||
const newUserData: Partial<IUser> = {
|
||||
const newUserData: Partial<User> = {
|
||||
firstName: signupData.firstName,
|
||||
lastName: signupData.lastName,
|
||||
email: signupData.email,
|
||||
@@ -132,7 +126,7 @@ class AuthService implements IAuthService {
|
||||
|
||||
created.org = org.id;
|
||||
|
||||
const roles = await Role.insertMany([
|
||||
const roles = await this.roleRepository.createMany([
|
||||
{
|
||||
organizationId: org.id,
|
||||
name: "Org Admin",
|
||||
@@ -184,14 +178,14 @@ class AuthService implements IAuthService {
|
||||
},
|
||||
]);
|
||||
|
||||
created.roles = roles.map((r) => r._id);
|
||||
created.roles = roles.map((r) => r.id);
|
||||
|
||||
const membership = await OrgMembership.create({
|
||||
const membership = await this.orgMembershipRepository.create({
|
||||
userId: user.id,
|
||||
orgId: org.id,
|
||||
roleId: roles[0]?._id,
|
||||
roleId: roles[0]?.id,
|
||||
});
|
||||
created.orgMembership = membership._id;
|
||||
created.orgMembership = membership.id;
|
||||
|
||||
const team = await this.teamRepository.create({
|
||||
name: "Default Team",
|
||||
@@ -205,11 +199,11 @@ class AuthService implements IAuthService {
|
||||
orgId: org.id,
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
roleId: roles[2]?._id.toString(),
|
||||
roleId: roles[2]?.id.toString(),
|
||||
});
|
||||
created.teamMembership = teamMembership.id;
|
||||
|
||||
const tokenizedUser: ITokenizedUser = {
|
||||
const tokenizedUser: TokenizedUser = {
|
||||
sub: user.id,
|
||||
email: user.email,
|
||||
orgId: org.id,
|
||||
@@ -224,7 +218,7 @@ class AuthService implements IAuthService {
|
||||
const entitlements: Entitlements =
|
||||
await this.entitlementsProvider.getForOrg(org.id);
|
||||
|
||||
const returnableUser: IUserReturnable = {
|
||||
const returnableUser: UserReturnable = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
firstName: user.firstName,
|
||||
@@ -240,12 +234,12 @@ class AuthService implements IAuthService {
|
||||
|
||||
return { tokenizedUser, returnableUser };
|
||||
} catch (error) {
|
||||
await TeamMembership.deleteOne({ _id: created.teamMembership });
|
||||
await OrgMembership.deleteOne({ _id: created.orgMembership });
|
||||
await Team.deleteOne({ _id: created.team });
|
||||
await Role.deleteMany({ _id: { $in: created.roles } });
|
||||
if (created.org) await Org.findByIdAndDelete(created.org);
|
||||
if (created.user) await User.findByIdAndDelete(created.user);
|
||||
await this.teamMembershipRepository.deleteById(created.teamMembership);
|
||||
await this.orgMembershipRepository.deleteById(created.orgMembership);
|
||||
await this.teamRepository.deleteById(created.team);
|
||||
await this.roleRepository.deleteMany(created.roles);
|
||||
if (created.org) await this.orgRepository.deleteById(created.org);
|
||||
if (created.user) await this.userRepository.deleteById(created.user);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -271,100 +265,102 @@ class AuthService implements IAuthService {
|
||||
throw new ApiError("No organization ID", 400);
|
||||
}
|
||||
// Check for org
|
||||
const org = await Org.findById(orgId);
|
||||
const org = await this.orgRepository.findById(orgId);
|
||||
if (!org) {
|
||||
throw new ApiError("Organization not found", 404);
|
||||
}
|
||||
|
||||
// Get or Create user
|
||||
let user = await User.findOne({ email });
|
||||
let user = await this.userRepository.findByEmail(email);
|
||||
if (!user) {
|
||||
const passwordHash = await hashPassword(signupData.password);
|
||||
user = await User.create({
|
||||
user = await this.userRepository.create({
|
||||
email,
|
||||
firstName: signupData.firstName,
|
||||
lastName: signupData.lastName,
|
||||
passwordHash,
|
||||
});
|
||||
created.user = user._id;
|
||||
created.user = user.id;
|
||||
}
|
||||
|
||||
const team = await Team.findById(teamId);
|
||||
const team = await this.teamRepository.findById(teamId, org.id);
|
||||
if (!team) {
|
||||
throw new ApiError("Team not found", 404);
|
||||
}
|
||||
|
||||
// Get or create orgMembership
|
||||
let orgMembership = await OrgMembership.findOne({
|
||||
userId: user._id,
|
||||
orgId,
|
||||
});
|
||||
let orgMembership = await this.orgMembershipRepository.findByUserId(
|
||||
user.id
|
||||
);
|
||||
|
||||
if (orgMembership && !orgMembership.roleId && orgRoleId) {
|
||||
orgMembership.roleId = new mongoose.Types.ObjectId(orgRoleId);
|
||||
await orgMembership.save();
|
||||
orgMembership.roleId = orgRoleId;
|
||||
await this.orgMembershipRepository.update(orgMembership);
|
||||
}
|
||||
if (!orgMembership) {
|
||||
orgMembership = await OrgMembership.create({
|
||||
orgMembership = await this.orgMembershipRepository.create({
|
||||
orgId,
|
||||
userId: user._id,
|
||||
userId: user.id,
|
||||
...(orgRoleId ? { roleId: orgRoleId } : {}),
|
||||
});
|
||||
created.orgMembership = orgMembership._id;
|
||||
created.orgMembership = orgMembership.id;
|
||||
}
|
||||
|
||||
// Create TeamMembership
|
||||
let teamMembership = await TeamMembership.findOne({
|
||||
userId: user._id,
|
||||
teamId,
|
||||
});
|
||||
let teamMembership = await this.teamMembershipRepository.findByUserId(
|
||||
user.id,
|
||||
teamId
|
||||
);
|
||||
if (teamMembership) {
|
||||
throw new ApiError("User is already a member of the team", 400);
|
||||
}
|
||||
teamMembership = await TeamMembership.create({
|
||||
teamMembership = await this.teamMembershipRepository.create({
|
||||
orgId,
|
||||
teamId,
|
||||
userId: user._id,
|
||||
userId: user.id,
|
||||
roleId: teamRoleId,
|
||||
});
|
||||
created.teamMembership = teamMembership._id;
|
||||
created.teamMembership = teamMembership.id;
|
||||
|
||||
// Get data for return
|
||||
const teamMembershipWithRoles = await TeamMembership.find({
|
||||
userId: user._id,
|
||||
})
|
||||
.populate<{ roleId: IRole }>("roleId")
|
||||
.lean();
|
||||
|
||||
const teamIds = teamMembershipWithRoles.map((tm) => tm.teamId.toString());
|
||||
const teamMembershipsWithRole =
|
||||
await this.teamMembershipRepository.findByUserIdWithRole(user.id);
|
||||
if (!teamMembershipsWithRole) {
|
||||
throw new ApiError("Team memberships not found for user", 404);
|
||||
}
|
||||
const teamIds = teamMembershipsWithRole.map((tm) => tm.teamId);
|
||||
|
||||
const teams = await this.getTeams(teamIds);
|
||||
const teamMap = new Map(teams.map((t) => [t._id.toString(), t]));
|
||||
const teamMap = new Map(teams.map((t) => [t.id, t]));
|
||||
|
||||
const tokenizedUser: ITokenizedUser = {
|
||||
sub: user._id.toString(),
|
||||
const tokenizedUser: TokenizedUser = {
|
||||
sub: user.id,
|
||||
email: user.email,
|
||||
orgId: orgId.toString(),
|
||||
orgId: orgId,
|
||||
};
|
||||
|
||||
const returnableTeams = teamMembershipWithRoles.map((tm) => {
|
||||
const team = teamMap.get(tm.teamId.toString());
|
||||
const returnableTeams = teamMembershipsWithRole.map((tm) => {
|
||||
const team = teamMap.get(tm.teamId);
|
||||
if (!team) {
|
||||
throw new ApiError("Team not found for membership");
|
||||
}
|
||||
return {
|
||||
id: team._id.toString(),
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
permissions: tm.roleId?.permissions ?? [],
|
||||
permissions: tm.role?.permissions ?? [],
|
||||
};
|
||||
});
|
||||
|
||||
const orgRole = await Role.findById(orgMembership.roleId);
|
||||
const orgRole = await this.roleRepository.findById(
|
||||
orgMembership.roleId || "",
|
||||
org.id
|
||||
);
|
||||
const orgPermissions = orgRole?.permissions || [];
|
||||
|
||||
const entitlements: Entitlements =
|
||||
await this.entitlementsProvider.getForOrg(org._id.toString());
|
||||
const returnableUser: IUserReturnable = {
|
||||
id: user._id.toString(),
|
||||
await this.entitlementsProvider.getForOrg(org.id);
|
||||
const returnableUser: UserReturnable = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
@@ -383,26 +379,26 @@ class AuthService implements IAuthService {
|
||||
};
|
||||
} catch (error) {
|
||||
if (created.orgMembership) {
|
||||
await OrgMembership.deleteOne({ _id: created.orgMembership });
|
||||
await this.orgMembershipRepository.deleteById(created.orgMembership);
|
||||
}
|
||||
if (created.teamMembership) {
|
||||
await TeamMembership.deleteOne({ _id: created.teamMembership });
|
||||
await this.teamMembershipRepository.deleteById(created.teamMembership);
|
||||
}
|
||||
if (created.user) {
|
||||
await User.findByIdAndDelete(created.user);
|
||||
await this.userRepository.deleteById(created.user);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
async login(loginData: LoginData): Promise<{
|
||||
tokenizedUser: ITokenizedUser;
|
||||
returnableUser: IUserReturnable;
|
||||
tokenizedUser: TokenizedUser;
|
||||
returnableUser: UserReturnable;
|
||||
}> {
|
||||
const { email, password } = loginData;
|
||||
|
||||
// Find user by email
|
||||
const user = await User.findOne({ email });
|
||||
const user = await this.userRepository.findByEmail(email);
|
||||
|
||||
if (!user) {
|
||||
throw new ApiError("Invalid email or password");
|
||||
@@ -415,15 +411,16 @@ class AuthService implements IAuthService {
|
||||
}
|
||||
|
||||
// Find OrgMembership
|
||||
const orgMembership = await OrgMembership.findOne({
|
||||
userId: user._id,
|
||||
}).lean();
|
||||
const orgMembership = await this.orgMembershipRepository.findByUserId(
|
||||
user.id
|
||||
);
|
||||
|
||||
if (!orgMembership) {
|
||||
throw new ApiError("User is not part of any organization");
|
||||
}
|
||||
|
||||
// Find org and roles
|
||||
let org = await Org.findById(orgMembership.orgId).lean();
|
||||
let org = await this.orgRepository.findById(orgMembership.orgId);
|
||||
|
||||
if (!org) {
|
||||
throw new ApiError("Organization not found");
|
||||
@@ -431,50 +428,53 @@ class AuthService implements IAuthService {
|
||||
|
||||
// Update org entitlements
|
||||
const planKey = org.entitlements.plan;
|
||||
org = await Org.findOneAndUpdate(
|
||||
{ _id: org._id },
|
||||
{ $set: { entitlements: Plans[planKey] } },
|
||||
{ new: true }
|
||||
);
|
||||
org = await this.orgRepository.updateById(org.id, {
|
||||
entitlements: Plans[planKey],
|
||||
});
|
||||
|
||||
if (!org) {
|
||||
throw new ApiError("Organization entitlement error");
|
||||
}
|
||||
|
||||
const orgRoles = await Role.findById(orgMembership.roleId).lean();
|
||||
const orgRoles = await this.roleRepository.findById(
|
||||
orgMembership.roleId || "",
|
||||
org.id
|
||||
);
|
||||
|
||||
// Get teams
|
||||
const teamMemberships = await TeamMembership.find({ userId: user._id })
|
||||
.populate<{ roleId: IRole }>("roleId")
|
||||
.lean();
|
||||
const teamMemberships =
|
||||
await this.teamMembershipRepository.findByUserIdWithRole(user.id);
|
||||
if (!teamMemberships) {
|
||||
throw new ApiError("Team memberships not found for user", 404);
|
||||
}
|
||||
|
||||
const teamIds = teamMemberships.map((tm) => tm.teamId.toString());
|
||||
const teamIds = teamMemberships.map((tm) => tm.teamId);
|
||||
|
||||
const teams = await this.getTeams(teamIds);
|
||||
const teamMap = new Map(teams.map((t) => [t._id.toString(), t]));
|
||||
const teamMap = new Map(teams.map((t) => [t.id, t]));
|
||||
const returnableTeams = teamMemberships.map((tm) => {
|
||||
const team = teamMap.get(tm.teamId.toString());
|
||||
const team = teamMap.get(tm.teamId);
|
||||
if (!team) {
|
||||
throw new ApiError("Team not found for membership");
|
||||
}
|
||||
return {
|
||||
id: team._id.toString(),
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
permissions: tm.roleId?.permissions ?? [],
|
||||
permissions: tm.role?.permissions ?? [],
|
||||
};
|
||||
});
|
||||
|
||||
const tokenizedUser: ITokenizedUser = {
|
||||
sub: user._id.toString(),
|
||||
const tokenizedUser: TokenizedUser = {
|
||||
sub: user.id,
|
||||
email: user.email,
|
||||
orgId: orgMembership.orgId.toString(),
|
||||
orgId: orgMembership.orgId,
|
||||
};
|
||||
|
||||
const entitlements: Entitlements =
|
||||
await this.entitlementsProvider.getForOrg(org._id.toString());
|
||||
await this.entitlementsProvider.getForOrg(org.id);
|
||||
|
||||
const returnableUser: IUserReturnable = {
|
||||
id: user._id.toString(),
|
||||
const returnableUser: UserReturnable = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
@@ -493,46 +493,20 @@ class AuthService implements IAuthService {
|
||||
};
|
||||
}
|
||||
|
||||
async getTeamIds(userId: string): Promise<string[]> {
|
||||
const teamMemberships = await TeamMembership.find({ userId })
|
||||
.select("teamId")
|
||||
.lean();
|
||||
const teamIds = teamMemberships.map((t) => t.teamId.toString());
|
||||
return teamIds;
|
||||
}
|
||||
|
||||
async getTeams(teamIds: string[]): Promise<ITeam[]> {
|
||||
const teams = await Team.find({ _id: { $in: teamIds } }).select(
|
||||
"_id, name"
|
||||
);
|
||||
return teams;
|
||||
}
|
||||
|
||||
async cleanup() {
|
||||
await User.deleteMany({});
|
||||
await Role.deleteMany({});
|
||||
await this.monitorRepository.deleteAll();
|
||||
await Check.deleteMany({});
|
||||
await NotificationChannel.deleteMany({});
|
||||
await this.jobQueue.flush();
|
||||
}
|
||||
|
||||
async cleanMonitors() {
|
||||
this.monitorRepository.deleteAll();
|
||||
await Check.deleteMany({});
|
||||
async getTeams(teamIds: string[]): Promise<Team[]> {
|
||||
return await this.teamRepository.findManyById(teamIds);
|
||||
}
|
||||
|
||||
changePassword = async (
|
||||
userId: Types.ObjectId,
|
||||
userId: string,
|
||||
newPassword: string
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const passwordHash = await hashPassword(newPassword);
|
||||
const result = await User.updateOne(
|
||||
{ _id: userId },
|
||||
{ $set: { passwordHash } }
|
||||
);
|
||||
return result.modifiedCount > 0;
|
||||
const result = await this.userRepository.updateById(userId, {
|
||||
passwordHash,
|
||||
});
|
||||
return Boolean(result);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -10,10 +10,7 @@ import ApiError from "@/utils/ApiError.js";
|
||||
import { getChildLogger } from "@/logger/Logger.js";
|
||||
import { getStartDate } from "@/utils/TimeUtils.js";
|
||||
|
||||
import type {
|
||||
CheckEntity,
|
||||
ICheckLighthouseFields,
|
||||
} from "@/types/domain/index.js";
|
||||
import type { CheckEntity } from "@/types/domain/index.js";
|
||||
import type {
|
||||
IChecksRepository,
|
||||
IMonitorRepository,
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import {
|
||||
Monitor,
|
||||
IUserContext,
|
||||
IMaintenance,
|
||||
Maintenance,
|
||||
} from "@/db/models/index.js";
|
||||
import { Monitor, IMaintenance, Maintenance } from "@/db/models/index.js";
|
||||
import ApiError from "@/utils/ApiError.js";
|
||||
import { MaintenanceRepeats } from "@/types/domain/index.js";
|
||||
import { MaintenanceRepeats, UserContext } from "@/types/domain/index.js";
|
||||
|
||||
const SERVICE_NAME = "MaintenanceService";
|
||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
@@ -13,19 +8,19 @@ const WEEK_MS = 7 * DAY_MS;
|
||||
|
||||
export interface IMaintenanceService {
|
||||
create: (
|
||||
userContext: IUserContext,
|
||||
userContext: UserContext,
|
||||
maintenance: IMaintenance
|
||||
) => Promise<IMaintenance>;
|
||||
getAll: (teamId: string) => Promise<IMaintenance[]>;
|
||||
get: (teamId: string, id: string) => Promise<IMaintenance>;
|
||||
toggleActive: (
|
||||
teamId: string,
|
||||
userContext: IUserContext,
|
||||
userContext: UserContext,
|
||||
id: string
|
||||
) => Promise<IMaintenance>;
|
||||
update: (
|
||||
teamId: string,
|
||||
userContext: IUserContext,
|
||||
userContext: UserContext,
|
||||
id: string,
|
||||
updateData: Partial<IMaintenance>
|
||||
) => Promise<IMaintenance>;
|
||||
@@ -47,7 +42,7 @@ class MaintenanceService implements IMaintenanceService {
|
||||
this.lastRefresh = 0;
|
||||
}
|
||||
|
||||
create = async (userContext: IUserContext, maintenanceData: IMaintenance) => {
|
||||
create = async (userContext: UserContext, maintenanceData: IMaintenance) => {
|
||||
// Make sure monitors belong to current team
|
||||
const monitorIds = maintenanceData.monitors || [];
|
||||
const count = await Monitor.countDocuments({
|
||||
@@ -86,7 +81,7 @@ class MaintenanceService implements IMaintenanceService {
|
||||
|
||||
toggleActive = async (
|
||||
teamId: string,
|
||||
userContext: IUserContext,
|
||||
userContext: UserContext,
|
||||
id: string
|
||||
) => {
|
||||
const updatedMaintenance = await Maintenance.findOneAndUpdate(
|
||||
@@ -110,7 +105,7 @@ class MaintenanceService implements IMaintenanceService {
|
||||
|
||||
update = async (
|
||||
teamId: string,
|
||||
userContext: IUserContext,
|
||||
userContext: UserContext,
|
||||
id: string,
|
||||
updateData: Partial<IMaintenance>
|
||||
) => {
|
||||
|
||||
@@ -3,8 +3,6 @@ import {
|
||||
Org,
|
||||
OrgMembership,
|
||||
Team,
|
||||
ITeam,
|
||||
IUserReturnable,
|
||||
TeamMembership,
|
||||
Role,
|
||||
IRole,
|
||||
@@ -12,9 +10,10 @@ import {
|
||||
import ApiError from "@/utils/ApiError.js";
|
||||
import type { IEntitlementsProvider } from "@/services/system/EntitlementsService.js";
|
||||
import type { Entitlements } from "@/types/entitlements.js";
|
||||
import type { UserReturnable } from "@/types/domain/index.js";
|
||||
|
||||
export interface IMeService {
|
||||
me(userId: string): Promise<IUserReturnable>;
|
||||
me(userId: string): Promise<UserReturnable>;
|
||||
getEntitlements(orgId: string): Promise<Entitlements>;
|
||||
getPermissions(user: any): Promise<{
|
||||
org: string[];
|
||||
@@ -32,7 +31,7 @@ class MeService implements IMeService {
|
||||
this.entitlementsProvider = entitlementsProvider;
|
||||
}
|
||||
|
||||
me = async (userId: string): Promise<IUserReturnable> => {
|
||||
me = async (userId: string): Promise<UserReturnable> => {
|
||||
// Need to get teamIds
|
||||
const user = await User.findById(userId);
|
||||
if (!user) {
|
||||
@@ -85,7 +84,7 @@ class MeService implements IMeService {
|
||||
const entitlements: Entitlements =
|
||||
await this.entitlementsProvider.getForOrg(org._id.toString());
|
||||
|
||||
const returnableUser: IUserReturnable = {
|
||||
const returnableUser: UserReturnable = {
|
||||
id: user._id.toString(),
|
||||
email: user.email,
|
||||
firstName: user.firstName,
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
import {
|
||||
IUserContext,
|
||||
INotificationChannel,
|
||||
NotificationChannel,
|
||||
Monitor,
|
||||
} from "@/db/models/index.js";
|
||||
import ApiError from "@/utils/ApiError.js";
|
||||
import type { UserContext } from "@/types/domain/index.js";
|
||||
|
||||
const SERVICE_NAME = "NotificationChannelService";
|
||||
|
||||
export interface INotificationChannelService {
|
||||
create: (
|
||||
tokenizedUser: IUserContext,
|
||||
tokenizedUser: UserContext,
|
||||
notificationChannel: INotificationChannel
|
||||
) => Promise<INotificationChannel>;
|
||||
getAll: (teamId: string) => Promise<INotificationChannel[]>;
|
||||
get: (teamId: string, id: string) => Promise<INotificationChannel>;
|
||||
toggleActive: (
|
||||
teamId: string,
|
||||
tokenizedUser: IUserContext,
|
||||
tokenizedUser: UserContext,
|
||||
id: string
|
||||
) => Promise<INotificationChannel>;
|
||||
update: (
|
||||
teamId: string,
|
||||
tokenizedUser: IUserContext,
|
||||
tokenizedUser: UserContext,
|
||||
id: string,
|
||||
updateData: Partial<INotificationChannel>
|
||||
) => Promise<INotificationChannel>;
|
||||
@@ -39,7 +39,7 @@ class NotificationChannelService implements INotificationChannelService {
|
||||
}
|
||||
|
||||
create = async (
|
||||
userContext: IUserContext,
|
||||
userContext: UserContext,
|
||||
notificationChannelData: INotificationChannel
|
||||
) => {
|
||||
const data: INotificationChannel = {
|
||||
@@ -68,7 +68,7 @@ class NotificationChannelService implements INotificationChannelService {
|
||||
|
||||
toggleActive = async (
|
||||
teamId: string,
|
||||
userContext: IUserContext,
|
||||
userContext: UserContext,
|
||||
id: string
|
||||
) => {
|
||||
const updatedChannel = await NotificationChannel.findOneAndUpdate(
|
||||
@@ -92,7 +92,7 @@ class NotificationChannelService implements INotificationChannelService {
|
||||
|
||||
update = async (
|
||||
teamId: string,
|
||||
userContext: IUserContext,
|
||||
userContext: UserContext,
|
||||
id: string,
|
||||
updateData: Partial<INotificationChannel>
|
||||
) => {
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
import {
|
||||
IUserContext,
|
||||
IStatusPage,
|
||||
StatusPage,
|
||||
Monitor,
|
||||
Check,
|
||||
IMonitor,
|
||||
} from "@/db/models/index.js";
|
||||
import { IStatusPage, StatusPage, Monitor, Check } from "@/db/models/index.js";
|
||||
import ApiError from "@/utils/ApiError.js";
|
||||
import { count } from "node:console";
|
||||
|
||||
import type { UserContext } from "@/types/domain/index.js";
|
||||
const SERVICE_NAME = "StatusPageService";
|
||||
|
||||
export interface IStatusPageService {
|
||||
create: (
|
||||
tokenizedUser: IUserContext,
|
||||
tokenizedUser: UserContext,
|
||||
statusPage: IStatusPage
|
||||
) => Promise<IStatusPage>;
|
||||
getAll: (
|
||||
@@ -32,7 +24,7 @@ export interface IStatusPageService {
|
||||
) => Promise<{ statusPage: IStatusPage; checksMap: Record<string, any[]> }>;
|
||||
update: (
|
||||
teamId: string,
|
||||
tokenizedUser: IUserContext,
|
||||
tokenizedUser: UserContext,
|
||||
id: string,
|
||||
updateData: Partial<IStatusPage>
|
||||
) => Promise<IStatusPage>;
|
||||
@@ -46,7 +38,7 @@ class StatusPageService implements IStatusPageService {
|
||||
this.SERVICE_NAME = SERVICE_NAME;
|
||||
}
|
||||
|
||||
create = async (userContext: IUserContext, statusPageData: IStatusPage) => {
|
||||
create = async (userContext: UserContext, statusPageData: IStatusPage) => {
|
||||
const monitorIds = statusPageData.monitors || [];
|
||||
const count = await Monitor.countDocuments({
|
||||
_id: { $in: monitorIds },
|
||||
@@ -153,7 +145,7 @@ class StatusPageService implements IStatusPageService {
|
||||
|
||||
update = async (
|
||||
teamId: string,
|
||||
userContext: IUserContext,
|
||||
userContext: UserContext,
|
||||
id: string,
|
||||
updateData: Partial<IStatusPage>
|
||||
) => {
|
||||
|
||||
@@ -2,18 +2,14 @@ import mongoose from "mongoose";
|
||||
import {
|
||||
Monitor,
|
||||
Role,
|
||||
IRole,
|
||||
ITeam,
|
||||
Team,
|
||||
TeamMembership,
|
||||
OrgMembership,
|
||||
IUserContext,
|
||||
} from "@/db/models/index.js";
|
||||
import type { IMonitorRepository } from "@/repositories/index.js";
|
||||
import type { IJobQueue } from "../infrastructure/JobQueue.js";
|
||||
import ApiError from "@/utils/ApiError.js";
|
||||
import { PERMISSIONS } from "@/types/permissions.js";
|
||||
|
||||
import type { UserContext } from "@/types/domain/index.js";
|
||||
const SERVICE_NAME = "TeamService";
|
||||
export interface ITeamService {
|
||||
create: (
|
||||
@@ -26,7 +22,7 @@ export interface ITeamService {
|
||||
getJoined: (teamId: string, orgId: string) => Promise<Partial<ITeam[]>>;
|
||||
get: (teamId: string, orgId: string) => Promise<ITeam>;
|
||||
update: (
|
||||
user: IUserContext,
|
||||
user: UserContext,
|
||||
teamId: string,
|
||||
teamData: Partial<ITeam>
|
||||
) => Promise<ITeam>;
|
||||
@@ -115,7 +111,7 @@ class TeamService implements ITeamService {
|
||||
};
|
||||
|
||||
update = async (
|
||||
user: IUserContext,
|
||||
user: UserContext,
|
||||
teamId: string,
|
||||
teamData: Partial<ITeam>
|
||||
) => {
|
||||
|
||||
@@ -7,3 +7,14 @@ export interface TeamMembership {
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface TeamMembershipWithDetails extends TeamMembership {
|
||||
role: {
|
||||
id: string;
|
||||
organizationId: string;
|
||||
name: string;
|
||||
permissions: string[];
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Team, Role } from "@/types/domain/index.js";
|
||||
import type { Entitlements } from "@/types/entitlements.js";
|
||||
export interface User {
|
||||
id: string;
|
||||
firstName: string;
|
||||
@@ -8,3 +10,40 @@ export interface User {
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface TokenizedUser {
|
||||
sub: string;
|
||||
email: string;
|
||||
orgId: string;
|
||||
}
|
||||
export interface UserContext {
|
||||
sub: string;
|
||||
email: string;
|
||||
orgId: string;
|
||||
teamIds?: string[];
|
||||
teams?: Team[];
|
||||
currentTeamId?: string;
|
||||
roles?: {
|
||||
orgRole?: Role;
|
||||
teamRole: Role;
|
||||
};
|
||||
entitlements: Entitlements;
|
||||
}
|
||||
|
||||
export interface UserReturnable {
|
||||
id: string;
|
||||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
org: {
|
||||
name: string;
|
||||
planKey: string;
|
||||
permissions: string[];
|
||||
};
|
||||
teams: {
|
||||
id: string;
|
||||
name: string;
|
||||
permissions: string[];
|
||||
}[];
|
||||
entitlements: Entitlements;
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import { AuthResult } from "@/services/business/AuthService.js";
|
||||
import { IUserContext } from "@/db/models/index.js";
|
||||
import bcrypt from "bcryptjs";
|
||||
|
||||
import type { UserContext } from "@/types/domain/index.js";
|
||||
export const encode = (data: AuthResult): string => {
|
||||
const secret = process.env.JWT_SECRET;
|
||||
if (!secret) {
|
||||
@@ -12,12 +11,12 @@ export const encode = (data: AuthResult): string => {
|
||||
return token;
|
||||
};
|
||||
|
||||
export const decode = (token: string): IUserContext => {
|
||||
export const decode = (token: string): UserContext => {
|
||||
const secret = process.env.JWT_SECRET;
|
||||
if (!secret) {
|
||||
throw new Error("JWT_SECRET is not defined");
|
||||
}
|
||||
const decoded = jwt.verify(token, secret) as IUserContext;
|
||||
const decoded = jwt.verify(token, secret) as UserContext;
|
||||
return decoded;
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user