refactor
This commit is contained in:
1 parent
c1bb1fd29d
commit
c42932a7b3
15 files changed
+217
-76
No files matched your search
@@ -9,10 +9,7 @@ export type { IMonitorStats } from "./monitors/MonitorStats.js";
|
||||
export { User } from "./auth/User.js";
|
||||
export type { IUser, IUserProfile } from "./auth/User.js";
|
||||
|
||||
export {
|
||||
NotificationChannel,
|
||||
ChannelTypes,
|
||||
} from "./notification-channel/NotificationChannel.js";
|
||||
export { NotificationChannel } from "./notification-channel/NotificationChannel.js";
|
||||
export type { INotificationChannel } from "./notification-channel/NotificationChannel.js";
|
||||
|
||||
export { Invite } from "./auth/Invite.js";
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import mongoose, { Schema, Types } from "mongoose";
|
||||
export const ChannelTypes = ["email", "slack", "discord", "webhook"] as const;
|
||||
export type ChannelType = (typeof ChannelTypes)[number];
|
||||
|
||||
export interface INotificationChannelConfig {
|
||||
url?: string; // For webhook, slack, discord
|
||||
emailAddress?: string; // For email
|
||||
}
|
||||
|
||||
import {
|
||||
type ChannelType,
|
||||
ChannelTypes,
|
||||
NotificationChannelConfig,
|
||||
} from "@/types/domain/index.js";
|
||||
export interface INotificationChannel {
|
||||
_id: Types.ObjectId;
|
||||
orgId: Types.ObjectId;
|
||||
teamId: Types.ObjectId;
|
||||
name: string;
|
||||
type: ChannelType;
|
||||
config: INotificationChannelConfig;
|
||||
config: NotificationChannelConfig;
|
||||
isActive: boolean;
|
||||
createdBy: Types.ObjectId;
|
||||
updatedBy: Types.ObjectId;
|
||||
@@ -21,7 +18,7 @@ export interface INotificationChannel {
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
const NotificationChannelConfigSchema = new Schema<INotificationChannelConfig>(
|
||||
const NotificationChannelConfigSchema = new Schema<NotificationChannelConfig>(
|
||||
{
|
||||
url: { type: String, required: false },
|
||||
emailAddress: { type: String, required: false },
|
||||
|
||||
@@ -46,6 +46,7 @@ import {
|
||||
MongoOrgRepository,
|
||||
MongoOrgMembershipRepository,
|
||||
MongoMaintenanceRepository,
|
||||
MongoNotificationChannelRepository,
|
||||
} from "@/repositories/index.js";
|
||||
|
||||
export const initServices = async () => {
|
||||
@@ -61,6 +62,7 @@ export const initServices = async () => {
|
||||
const orgRepository = new MongoOrgRepository();
|
||||
const orgMembershipRepository = new MongoOrgMembershipRepository();
|
||||
const maintenanceRepository = new MongoMaintenanceRepository();
|
||||
const notificationChannelRepository = new MongoNotificationChannelRepository();
|
||||
|
||||
const checkService = new CheckService(checksRepository, monitorRepository);
|
||||
const inviteService = new InviteService(
|
||||
@@ -78,7 +80,10 @@ export const initServices = async () => {
|
||||
monitorRepository,
|
||||
monitorStatsRepository
|
||||
);
|
||||
const notificationChannelService = new NotificationChannelService();
|
||||
const notificationChannelService = new NotificationChannelService(
|
||||
notificationChannelRepository,
|
||||
monitorRepository
|
||||
);
|
||||
const userService = new UserService();
|
||||
const discordService = new DiscordService();
|
||||
const slackService = new SlackService();
|
||||
|
||||
@@ -39,3 +39,6 @@ export { default as MongoMaintenanceRepository } from "@/repositories/maintenanc
|
||||
|
||||
export type { INotificationChannelRepository } from "@/repositories/notification-channels/INotificationChannelRepository.js";
|
||||
export { default as MongoNotificationChannelRepository } from "@/repositories/notification-channels/MongoNotificationChannelRepository.js";
|
||||
|
||||
export type { IRecoveryTokenRepistory } from "@/repositories/recovery-token/IRecoveryTokenRepistory.js";
|
||||
export { default as MongoRecoveryTokenRepository } from "@/repositories/recovery-token/MongoRecoveryTokenRepository.js";
|
||||
@@ -85,4 +85,9 @@ export interface IMonitorRepository {
|
||||
// Counts
|
||||
countByOrgId(orgId: string): Promise<number>;
|
||||
countByIdAndTeamId(monitorIds: string[], teamId: string): Promise<number>;
|
||||
|
||||
// Other
|
||||
removeNotificationChannelFromMonitors(
|
||||
notificationId: string
|
||||
): Promise<boolean>;
|
||||
}
|
||||
@@ -239,6 +239,15 @@ class MongoMonitorRepository implements IMonitorRepository {
|
||||
teamId: teamId,
|
||||
});
|
||||
};
|
||||
|
||||
// Other
|
||||
removeNotificationChannelFromMonitors = async (notificationId: string) => {
|
||||
const result = await Monitor.updateMany(
|
||||
{ notificationChannels: notificationId },
|
||||
{ $pull: { notificationChannels: notificationId } }
|
||||
);
|
||||
return result.modifiedCount > 0;
|
||||
};
|
||||
}
|
||||
|
||||
export default MongoMonitorRepository;
|
||||
@@ -1,7 +1,24 @@
|
||||
import type { NotificationChannel } from "@/types/domain/index.js";
|
||||
|
||||
export interface INotificationChannelRepository {
|
||||
// create
|
||||
create: (data: Partial<NotificationChannel>) => Promise<NotificationChannel>;
|
||||
// single fetch
|
||||
findById: (
|
||||
notificationChannelId: string,
|
||||
teamId: string
|
||||
) => Promise<NotificationChannel | null>;
|
||||
// collection fetch
|
||||
findByTeamId: (teamId: string) => Promise<NotificationChannel[]>;
|
||||
// update
|
||||
update: (
|
||||
notificationChannelId: string,
|
||||
teamid: string,
|
||||
data: Partial<NotificationChannel>
|
||||
) => Promise<NotificationChannel | null>;
|
||||
// delete
|
||||
deleteById: (
|
||||
notificationChannelId: string,
|
||||
teamId: string
|
||||
) => Promise<boolean>;
|
||||
}
|
||||
+66
-1
@@ -1,5 +1,70 @@
|
||||
import type { INotificationChannel } from "@/db/models/index.js";
|
||||
import { NotificationChannel } from "@/db/models/index.js";
|
||||
import type { NotificationChannel as NotificationChannelEntity } from "@/types/domain/index.js";
|
||||
|
||||
import { INotificationChannelRepository } from "@/repositories/index.js";
|
||||
|
||||
class MongoNotificationRepository implements INotificationChannelRepository {}
|
||||
class MongoNotificationRepository implements INotificationChannelRepository {
|
||||
private toEntity = (doc: INotificationChannel): NotificationChannelEntity => {
|
||||
return {
|
||||
id: doc._id.toString(),
|
||||
orgId: doc.orgId.toString(),
|
||||
teamId: doc.teamId.toString(),
|
||||
name: doc.name,
|
||||
type: doc.type,
|
||||
config: doc.config,
|
||||
isActive: doc.isActive,
|
||||
createdBy: doc.createdBy.toString(),
|
||||
updatedBy: doc.updatedBy.toString(),
|
||||
createdAt: doc.createdAt,
|
||||
updatedAt: doc.updatedAt,
|
||||
};
|
||||
};
|
||||
create = async (data: Partial<NotificationChannelEntity>) => {
|
||||
const created = await NotificationChannel.create(data);
|
||||
return this.toEntity(created);
|
||||
};
|
||||
|
||||
findById = async (notificationChannelId: string, teamId: string) => {
|
||||
const channel = await NotificationChannel.findOne({
|
||||
_id: notificationChannelId,
|
||||
teamId,
|
||||
});
|
||||
|
||||
if (!channel) return null;
|
||||
return this.toEntity(channel);
|
||||
};
|
||||
|
||||
findByTeamId = async (teamId: string) => {
|
||||
const channels = await NotificationChannel.find({ teamId });
|
||||
return channels.map(this.toEntity);
|
||||
};
|
||||
|
||||
update = async (
|
||||
notificationChannelId: string,
|
||||
teamId: string,
|
||||
data: Partial<NotificationChannelEntity>
|
||||
) => {
|
||||
const updatedChannel = await NotificationChannel.findOneAndUpdate(
|
||||
{ _id: notificationChannelId, teamId },
|
||||
[
|
||||
{
|
||||
$set: data,
|
||||
},
|
||||
],
|
||||
{ new: true, runValidators: true }
|
||||
);
|
||||
|
||||
if (!updatedChannel) return null;
|
||||
return this.toEntity(updatedChannel);
|
||||
};
|
||||
deleteById = async (notificationChannelId: string, teamId: string) => {
|
||||
const result = await NotificationChannel.deleteOne({
|
||||
_id: notificationChannelId,
|
||||
teamId,
|
||||
});
|
||||
return result.deletedCount === 1;
|
||||
};
|
||||
}
|
||||
|
||||
export default MongoNotificationRepository;
|
||||
@@ -0,0 +1 @@
|
||||
export interface IRecoveryTokenRepistory {}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { IRecoveryTokenRepistory } from "@/repositories/index.js";
|
||||
|
||||
class MongoRecoveryTokenRepository implements IRecoveryTokenRepistory {}
|
||||
|
||||
export default MongoRecoveryTokenRepository;
|
||||
@@ -1,61 +1,70 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
import {
|
||||
INotificationChannel,
|
||||
NotificationChannel,
|
||||
Monitor,
|
||||
} from "@/db/models/index.js";
|
||||
import ApiError from "@/utils/ApiError.js";
|
||||
import type { UserContext } from "@/types/domain/index.js";
|
||||
import {
|
||||
INotificationChannelRepository,
|
||||
IMonitorRepository,
|
||||
} from "@/repositories/index.js";
|
||||
import {
|
||||
type UserContext,
|
||||
NotificationChannel as NotificationChannelEntity,
|
||||
} from "@/types/domain/index.js";
|
||||
|
||||
const SERVICE_NAME = "NotificationChannelService";
|
||||
|
||||
export interface INotificationChannelService {
|
||||
create: (
|
||||
tokenizedUser: UserContext,
|
||||
notificationChannel: INotificationChannel
|
||||
) => Promise<INotificationChannel>;
|
||||
getAll: (teamId: string) => Promise<INotificationChannel[]>;
|
||||
get: (teamId: string, id: string) => Promise<INotificationChannel>;
|
||||
notificationChannel: NotificationChannelEntity
|
||||
) => Promise<NotificationChannelEntity>;
|
||||
getAll: (teamId: string) => Promise<NotificationChannelEntity[]>;
|
||||
get: (teamId: string, id: string) => Promise<NotificationChannelEntity>;
|
||||
toggleActive: (
|
||||
teamId: string,
|
||||
tokenizedUser: UserContext,
|
||||
id: string
|
||||
) => Promise<INotificationChannel>;
|
||||
) => Promise<NotificationChannelEntity>;
|
||||
update: (
|
||||
teamId: string,
|
||||
tokenizedUser: UserContext,
|
||||
id: string,
|
||||
updateData: Partial<INotificationChannel>
|
||||
) => Promise<INotificationChannel>;
|
||||
updateData: Partial<NotificationChannelEntity>
|
||||
) => Promise<NotificationChannelEntity>;
|
||||
delete: (teamId: string, id: string) => Promise<boolean>;
|
||||
}
|
||||
|
||||
class NotificationChannelService implements INotificationChannelService {
|
||||
public SERVICE_NAME: string;
|
||||
|
||||
constructor() {
|
||||
private notificationChannelRepository: INotificationChannelRepository;
|
||||
private monitorRepository: IMonitorRepository;
|
||||
constructor(
|
||||
notificationChannelRepository: INotificationChannelRepository,
|
||||
monitorRepository: IMonitorRepository
|
||||
) {
|
||||
this.SERVICE_NAME = SERVICE_NAME;
|
||||
this.notificationChannelRepository = notificationChannelRepository;
|
||||
this.monitorRepository = monitorRepository;
|
||||
}
|
||||
|
||||
create = async (
|
||||
userContext: UserContext,
|
||||
notificationChannelData: INotificationChannel
|
||||
notificationChannelData: NotificationChannelEntity
|
||||
) => {
|
||||
const data: INotificationChannel = {
|
||||
...notificationChannelData,
|
||||
orgId: new mongoose.Types.ObjectId(userContext.orgId),
|
||||
teamId: new mongoose.Types.ObjectId(userContext.currentTeamId),
|
||||
createdBy: new mongoose.Types.ObjectId(userContext.sub),
|
||||
updatedBy: new mongoose.Types.ObjectId(userContext.sub),
|
||||
};
|
||||
|
||||
const notificationChannel = await NotificationChannel.create(data);
|
||||
const notificationChannel = await this.notificationChannelRepository.create(
|
||||
{
|
||||
...notificationChannelData,
|
||||
orgId: userContext.orgId,
|
||||
teamId: userContext.currentTeamId || "",
|
||||
createdBy: userContext.sub,
|
||||
updatedBy: userContext.sub,
|
||||
}
|
||||
);
|
||||
return notificationChannel;
|
||||
};
|
||||
|
||||
get = async (teamId: string, id: string) => {
|
||||
const channel = await NotificationChannel.findOne({ _id: id, teamId });
|
||||
const channel = await this.notificationChannelRepository.findById(
|
||||
id,
|
||||
teamId
|
||||
);
|
||||
if (!channel) {
|
||||
throw new ApiError("Notification channel not found", 404);
|
||||
}
|
||||
@@ -63,7 +72,7 @@ class NotificationChannelService implements INotificationChannelService {
|
||||
};
|
||||
|
||||
getAll = async (teamId: string) => {
|
||||
return NotificationChannel.find({ teamId });
|
||||
return await this.notificationChannelRepository.findByTeamId(teamId);
|
||||
};
|
||||
|
||||
toggleActive = async (
|
||||
@@ -71,18 +80,14 @@ class NotificationChannelService implements INotificationChannelService {
|
||||
userContext: UserContext,
|
||||
id: string
|
||||
) => {
|
||||
const updatedChannel = await NotificationChannel.findOneAndUpdate(
|
||||
{ _id: id, teamId },
|
||||
[
|
||||
{
|
||||
$set: {
|
||||
isActive: { $not: "$isActive" },
|
||||
updatedBy: userContext.sub,
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
},
|
||||
],
|
||||
{ new: true }
|
||||
const updatedChannel = await this.notificationChannelRepository.update(
|
||||
id,
|
||||
teamId,
|
||||
{
|
||||
isActive: { $not: "$isActive" },
|
||||
updatedBy: userContext.sub,
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
);
|
||||
if (!updatedChannel) {
|
||||
throw new ApiError("Notification channel not found", 404);
|
||||
@@ -94,18 +99,16 @@ class NotificationChannelService implements INotificationChannelService {
|
||||
teamId: string,
|
||||
userContext: UserContext,
|
||||
id: string,
|
||||
updateData: Partial<INotificationChannel>
|
||||
updateData: Partial<NotificationChannelEntity>
|
||||
) => {
|
||||
const updatedChannel = await NotificationChannel.findOneAndUpdate(
|
||||
{ _id: id, teamId },
|
||||
const updatedChannel = await this.notificationChannelRepository.update(
|
||||
id,
|
||||
teamId,
|
||||
{
|
||||
$set: {
|
||||
...updateData,
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userContext.sub,
|
||||
},
|
||||
},
|
||||
{ new: true, runValidators: true }
|
||||
...updateData,
|
||||
updatedAt: new Date(),
|
||||
updatedBy: userContext.sub,
|
||||
}
|
||||
);
|
||||
|
||||
if (!updatedChannel) {
|
||||
@@ -115,17 +118,20 @@ class NotificationChannelService implements INotificationChannelService {
|
||||
return updatedChannel;
|
||||
};
|
||||
|
||||
delete = async (teamId: string, id: string) => {
|
||||
const result = await NotificationChannel.deleteOne({ _id: id, teamId });
|
||||
if (!result.deletedCount) {
|
||||
delete = async (teamId: string, notificationChannelId: string) => {
|
||||
const didDelete = await this.notificationChannelRepository.deleteById(
|
||||
notificationChannelId,
|
||||
teamId
|
||||
);
|
||||
if (!didDelete) {
|
||||
throw new ApiError("Notification channel not found", 404);
|
||||
}
|
||||
|
||||
await Monitor.updateMany(
|
||||
{ notificationChannels: id },
|
||||
{ $pull: { notificationChannels: id } }
|
||||
await this.monitorRepository.removeNotificationChannelFromMonitors(
|
||||
notificationChannelId
|
||||
);
|
||||
return result.deletedCount === 1;
|
||||
|
||||
return didDelete;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -11,3 +11,6 @@ export * from "@/types/domain/org.js";
|
||||
export * from "@/types/domain/orgMembership.js";
|
||||
export * from "@/types/domain/maintenance.js";
|
||||
export * from "@/types/domain/statsHourly.js";
|
||||
export * from "@/types/domain/notificationChannel.js";
|
||||
export * from "@/types/domain/statsDaily.js";
|
||||
export * from "@/types/domain/recoveryToken.js";
|
||||
@@ -0,0 +1,21 @@
|
||||
export const ChannelTypes = ["email", "slack", "discord", "webhook"] as const;
|
||||
export type ChannelType = (typeof ChannelTypes)[number];
|
||||
|
||||
export interface NotificationChannelConfig {
|
||||
url?: string;
|
||||
emailAddress?: string;
|
||||
}
|
||||
|
||||
export interface NotificationChannel {
|
||||
id: string;
|
||||
orgId: string;
|
||||
teamId: string;
|
||||
name: string;
|
||||
type: ChannelType;
|
||||
config: NotificationChannelConfig;
|
||||
isActive: boolean | unknown;
|
||||
createdBy: string;
|
||||
updatedBy: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface RecoveryToken {
|
||||
id: string;
|
||||
userId: string;
|
||||
tokenHash: string;
|
||||
expiry: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { ChannelTypes } from "@/db/models/index.js";
|
||||
import { ChannelTypes } from "@/types/domain/index.js";
|
||||
import { urlRegex } from "./common.js";
|
||||
|
||||
export const notificationChannelSchema = z
|
||||
@@ -101,4 +101,3 @@ export const notificationPatchSchema = z
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user