mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: Add Status Page Subscriber Notification Template and related services
- Introduced `StatusPageSubscriberNotificationTemplate` model for managing custom notification templates for status page subscribers. - Created `StatusPageSubscriberNotificationTemplateStatusPage` model to link notification templates to specific status pages. - Implemented services for managing notification templates and their associations with status pages. - Added permissions for creating, reading, updating, and deleting notification templates and their links. - Developed frontend component for displaying and managing subscriber notification templates in the dashboard. - Defined enums for notification event types and methods to standardize template usage.
This commit is contained in:
@@ -123,6 +123,8 @@ import StatusPageResource from "./StatusPageResource";
|
||||
import StatusPageSCIM from "./StatusPageSCIM";
|
||||
import StatusPageSSO from "./StatusPageSso";
|
||||
import StatusPageSubscriber from "./StatusPageSubscriber";
|
||||
import StatusPageSubscriberNotificationTemplate from "./StatusPageSubscriberNotificationTemplate";
|
||||
import StatusPageSubscriberNotificationTemplateStatusPage from "./StatusPageSubscriberNotificationTemplateStatusPage";
|
||||
// Team
|
||||
import Team from "./Team";
|
||||
import TeamMember from "./TeamMember";
|
||||
@@ -265,6 +267,8 @@ const AllModelTypes: Array<{
|
||||
StatusPageAnnouncement,
|
||||
StatusPageAnnouncementTemplate,
|
||||
StatusPageSubscriber,
|
||||
StatusPageSubscriberNotificationTemplate,
|
||||
StatusPageSubscriberNotificationTemplateStatusPage,
|
||||
StatusPageFooterLink,
|
||||
StatusPageHeaderLink,
|
||||
StatusPagePrivateUser,
|
||||
|
||||
@@ -0,0 +1,459 @@
|
||||
import Project from "./Project";
|
||||
import User from "./User";
|
||||
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
|
||||
import Route from "../../Types/API/Route";
|
||||
import { PlanType } from "../../Types/Billing/SubscriptionPlan";
|
||||
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
|
||||
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
|
||||
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
|
||||
import ColumnLength from "../../Types/Database/ColumnLength";
|
||||
import ColumnType from "../../Types/Database/ColumnType";
|
||||
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
||||
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
||||
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
||||
import TableColumn from "../../Types/Database/TableColumn";
|
||||
import TableColumnType from "../../Types/Database/TableColumnType";
|
||||
import TableMetadata from "../../Types/Database/TableMetadata";
|
||||
import TenantColumn from "../../Types/Database/TenantColumn";
|
||||
import IconProp from "../../Types/Icon/IconProp";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import Permission from "../../Types/Permission";
|
||||
import StatusPageSubscriberNotificationEventType from "../../Types/StatusPage/StatusPageSubscriberNotificationEventType";
|
||||
import StatusPageSubscriberNotificationMethod from "../../Types/StatusPage/StatusPageSubscriberNotificationMethod";
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
||||
|
||||
@EnableDocumentation()
|
||||
@TenantColumn("projectId")
|
||||
@TableBillingAccessControl({
|
||||
create: PlanType.Scale,
|
||||
read: PlanType.Scale,
|
||||
update: PlanType.Scale,
|
||||
delete: PlanType.Free,
|
||||
})
|
||||
@TableAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
delete: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.DeleteStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
})
|
||||
@EnableWorkflow({
|
||||
create: true,
|
||||
delete: true,
|
||||
update: true,
|
||||
read: true,
|
||||
})
|
||||
@CrudApiEndpoint(new Route("/status-page-subscriber-notification-template"))
|
||||
@TableMetadata({
|
||||
tableName: "StatusPageSubscriberNotificationTemplate",
|
||||
singularName: "Subscriber Notification Template",
|
||||
pluralName: "Subscriber Notification Templates",
|
||||
icon: IconProp.Email,
|
||||
tableDescription:
|
||||
"Manage custom notification templates for status page subscribers. These templates can be used to customize the notifications sent to subscribers via Email, SMS, Slack, Microsoft Teams, and Webhooks.",
|
||||
})
|
||||
@Entity({
|
||||
name: "StatusPageSubscriberNotificationTemplate",
|
||||
})
|
||||
export default class StatusPageSubscriberNotificationTemplate extends BaseModel {
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
manyToOneRelationColumn: "projectId",
|
||||
type: TableColumnType.Entity,
|
||||
modelType: Project,
|
||||
title: "Project",
|
||||
description: "Relation to Project Resource in which this object belongs",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return Project;
|
||||
},
|
||||
{
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "CASCADE",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "projectId" })
|
||||
public project?: Project = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@Index()
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
required: true,
|
||||
canReadOnRelationQuery: true,
|
||||
title: "Project ID",
|
||||
description: "ID of your OneUptime Project in which this object belongs",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: false,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public projectId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
type: TableColumnType.ShortText,
|
||||
title: "Template Name",
|
||||
description: "A friendly name for this notification template",
|
||||
canReadOnRelationQuery: true,
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
})
|
||||
public templateName?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
required: false,
|
||||
type: TableColumnType.LongText,
|
||||
title: "Template Description",
|
||||
description: "A description for this notification template",
|
||||
})
|
||||
@Column({
|
||||
nullable: true,
|
||||
type: ColumnType.LongText,
|
||||
length: ColumnLength.LongText,
|
||||
})
|
||||
public templateDescription?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
})
|
||||
@Index()
|
||||
@TableColumn({
|
||||
required: true,
|
||||
type: TableColumnType.ShortText,
|
||||
title: "Event Type",
|
||||
description:
|
||||
"The type of event this template is for (e.g., Incident Created, Announcement Created)",
|
||||
canReadOnRelationQuery: true,
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
})
|
||||
public eventType?: StatusPageSubscriberNotificationEventType = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
})
|
||||
@Index()
|
||||
@TableColumn({
|
||||
required: true,
|
||||
type: TableColumnType.ShortText,
|
||||
title: "Notification Method",
|
||||
description:
|
||||
"The notification method this template is for (Email, SMS, Slack, Microsoft Teams, Webhook)",
|
||||
canReadOnRelationQuery: true,
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
})
|
||||
public notificationMethod?: StatusPageSubscriberNotificationMethod = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
required: false,
|
||||
type: TableColumnType.ShortText,
|
||||
title: "Subject (Email only)",
|
||||
description: "The subject line for email notifications. Only used for Email notification method.",
|
||||
})
|
||||
@Column({
|
||||
nullable: true,
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
})
|
||||
public emailSubject?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
type: TableColumnType.HTML,
|
||||
title: "Template Body",
|
||||
description:
|
||||
"The template body content. For Email: HTML template. For SMS: Plain text. For Slack/Teams: Markdown.",
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
type: ColumnType.HTML,
|
||||
})
|
||||
public templateBody?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
manyToOneRelationColumn: "createdByUserId",
|
||||
type: TableColumnType.Entity,
|
||||
modelType: User,
|
||||
title: "Created by User",
|
||||
description:
|
||||
"Relation to User who created this object (if this object was created by a User)",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return User;
|
||||
},
|
||||
{
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "SET NULL",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "createdByUserId" })
|
||||
public createdByUser?: User = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
title: "Created by User ID",
|
||||
description:
|
||||
"User ID who created this object (if this object was created by a User)",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: true,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public createdByUserId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
manyToOneRelationColumn: "deletedByUserId",
|
||||
type: TableColumnType.Entity,
|
||||
title: "Deleted by User",
|
||||
modelType: User,
|
||||
description:
|
||||
"Relation to User who deleted this object (if this object was deleted by a User)",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return User;
|
||||
},
|
||||
{
|
||||
cascade: false,
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "SET NULL",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "deletedByUserId" })
|
||||
public deletedByUser?: User = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
title: "Deleted by User ID",
|
||||
description:
|
||||
"User ID who deleted this object (if this object was deleted by a User)",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: true,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public deletedByUserId?: ObjectID = undefined;
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
import Project from "./Project";
|
||||
import StatusPage from "./StatusPage";
|
||||
import StatusPageSubscriberNotificationTemplate from "./StatusPageSubscriberNotificationTemplate";
|
||||
import User from "./User";
|
||||
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
|
||||
import Route from "../../Types/API/Route";
|
||||
import { PlanType } from "../../Types/Billing/SubscriptionPlan";
|
||||
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
|
||||
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
|
||||
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
|
||||
import ColumnType from "../../Types/Database/ColumnType";
|
||||
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
||||
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
||||
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
||||
import TableColumn from "../../Types/Database/TableColumn";
|
||||
import TableColumnType from "../../Types/Database/TableColumnType";
|
||||
import TableMetadata from "../../Types/Database/TableMetadata";
|
||||
import TenantColumn from "../../Types/Database/TenantColumn";
|
||||
import IconProp from "../../Types/Icon/IconProp";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import Permission from "../../Types/Permission";
|
||||
import CanAccessIfCanReadOn from "../../Types/Database/CanAccessIfCanReadOn";
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
||||
|
||||
@EnableDocumentation()
|
||||
@TenantColumn("projectId")
|
||||
@CanAccessIfCanReadOn("statusPage")
|
||||
@TableBillingAccessControl({
|
||||
create: PlanType.Scale,
|
||||
read: PlanType.Scale,
|
||||
update: PlanType.Scale,
|
||||
delete: PlanType.Scale,
|
||||
})
|
||||
@TableAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
delete: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.DeleteStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
})
|
||||
@EnableWorkflow({
|
||||
create: true,
|
||||
delete: true,
|
||||
update: true,
|
||||
read: true,
|
||||
})
|
||||
@CrudApiEndpoint(new Route("/status-page-subscriber-notification-template-status-page"))
|
||||
@TableMetadata({
|
||||
tableName: "StatusPageSubscriberNotificationTemplateStatusPage",
|
||||
singularName: "Status Page Notification Template Link",
|
||||
pluralName: "Status Page Notification Template Links",
|
||||
icon: IconProp.Link,
|
||||
tableDescription:
|
||||
"Links subscriber notification templates to specific status pages. This allows you to use different notification templates for different status pages.",
|
||||
})
|
||||
@Entity({
|
||||
name: "StatusPageSubscriberNotificationTemplateStatusPage",
|
||||
})
|
||||
export default class StatusPageSubscriberNotificationTemplateStatusPage extends BaseModel {
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
manyToOneRelationColumn: "projectId",
|
||||
type: TableColumnType.Entity,
|
||||
modelType: Project,
|
||||
title: "Project",
|
||||
description: "Relation to Project Resource in which this object belongs",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return Project;
|
||||
},
|
||||
{
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "CASCADE",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "projectId" })
|
||||
public project?: Project = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@Index()
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
required: true,
|
||||
canReadOnRelationQuery: true,
|
||||
title: "Project ID",
|
||||
description: "ID of your OneUptime Project in which this object belongs",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: false,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public projectId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
manyToOneRelationColumn: "statusPageId",
|
||||
type: TableColumnType.Entity,
|
||||
modelType: StatusPage,
|
||||
title: "Status Page",
|
||||
description: "Status Page this template is linked to",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return StatusPage;
|
||||
},
|
||||
{
|
||||
eager: false,
|
||||
nullable: false,
|
||||
onDelete: "CASCADE",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "statusPageId" })
|
||||
public statusPage?: StatusPage = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@Index()
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
required: true,
|
||||
canReadOnRelationQuery: true,
|
||||
title: "Status Page ID",
|
||||
description: "ID of the Status Page this template is linked to",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: false,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public statusPageId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
manyToOneRelationColumn: "statusPageSubscriberNotificationTemplateId",
|
||||
type: TableColumnType.Entity,
|
||||
modelType: StatusPageSubscriberNotificationTemplate,
|
||||
title: "Notification Template",
|
||||
description: "The notification template to use for this status page",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return StatusPageSubscriberNotificationTemplate;
|
||||
},
|
||||
{
|
||||
eager: false,
|
||||
nullable: false,
|
||||
onDelete: "CASCADE",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "statusPageSubscriberNotificationTemplateId" })
|
||||
public statusPageSubscriberNotificationTemplate?: StatusPageSubscriberNotificationTemplate = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
})
|
||||
@Index()
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
required: true,
|
||||
canReadOnRelationQuery: true,
|
||||
title: "Notification Template ID",
|
||||
description: "ID of the notification template linked to this status page",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: false,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public statusPageSubscriberNotificationTemplateId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
manyToOneRelationColumn: "createdByUserId",
|
||||
type: TableColumnType.Entity,
|
||||
modelType: User,
|
||||
title: "Created by User",
|
||||
description:
|
||||
"Relation to User who created this object (if this object was created by a User)",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return User;
|
||||
},
|
||||
{
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "SET NULL",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "createdByUserId" })
|
||||
public createdByUser?: User = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
title: "Created by User ID",
|
||||
description:
|
||||
"User ID who created this object (if this object was created by a User)",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: true,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public createdByUserId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
manyToOneRelationColumn: "deletedByUserId",
|
||||
type: TableColumnType.Entity,
|
||||
title: "Deleted by User",
|
||||
modelType: User,
|
||||
description:
|
||||
"Relation to User who deleted this object (if this object was deleted by a User)",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return User;
|
||||
},
|
||||
{
|
||||
cascade: false,
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "SET NULL",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "deletedByUserId" })
|
||||
public deletedByUser?: User = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
title: "Deleted by User ID",
|
||||
description:
|
||||
"User ID who deleted this object (if this object was deleted by a User)",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: true,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public deletedByUserId?: ObjectID = undefined;
|
||||
}
|
||||
@@ -112,6 +112,8 @@ import StatusPageResourceService from "./StatusPageResourceService";
|
||||
import StatusPageService from "./StatusPageService";
|
||||
import StatusPageSsoService from "./StatusPageSsoService";
|
||||
import StatusPageSubscriberService from "./StatusPageSubscriberService";
|
||||
import StatusPageSubscriberNotificationTemplateService from "./StatusPageSubscriberNotificationTemplateService";
|
||||
import StatusPageSubscriberNotificationTemplateStatusPageService from "./StatusPageSubscriberNotificationTemplateStatusPageService";
|
||||
import TeamMemberService from "./TeamMemberService";
|
||||
import TeamPermissionService from "./TeamPermissionService";
|
||||
import TeamComplianceSettingService from "./TeamComplianceSettingService";
|
||||
@@ -273,6 +275,8 @@ const services: Array<BaseService> = [
|
||||
StatusPageService,
|
||||
StatusPageSsoService,
|
||||
StatusPageSubscriberService,
|
||||
StatusPageSubscriberNotificationTemplateService,
|
||||
StatusPageSubscriberNotificationTemplateStatusPageService,
|
||||
StatusPageHistoryChartBarColorRuleService,
|
||||
|
||||
TeamMemberService,
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
import DatabaseService from "./DatabaseService";
|
||||
import Model from "../../Models/DatabaseModels/StatusPageSubscriberNotificationTemplate";
|
||||
import StatusPageSubscriberNotificationTemplateStatusPage from "../../Models/DatabaseModels/StatusPageSubscriberNotificationTemplateStatusPage";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import StatusPageSubscriberNotificationEventType from "../../Types/StatusPage/StatusPageSubscriberNotificationEventType";
|
||||
import StatusPageSubscriberNotificationMethod from "../../Types/StatusPage/StatusPageSubscriberNotificationMethod";
|
||||
import StatusPageSubscriberNotificationTemplateStatusPageService from "./StatusPageSubscriberNotificationTemplateStatusPageService";
|
||||
import BadDataException from "../../Types/Exception/BadDataException";
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor() {
|
||||
super(Model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template for a specific status page, event type, and notification method.
|
||||
* Returns null if no custom template is found (caller should use default template).
|
||||
*/
|
||||
public async getTemplateForStatusPage(data: {
|
||||
statusPageId: ObjectID;
|
||||
eventType: StatusPageSubscriberNotificationEventType;
|
||||
notificationMethod: StatusPageSubscriberNotificationMethod;
|
||||
}): Promise<Model | null> {
|
||||
const { statusPageId, eventType, notificationMethod } = data;
|
||||
|
||||
// First find the template link for this status page
|
||||
const templateLinks: Array<StatusPageSubscriberNotificationTemplateStatusPage> =
|
||||
await StatusPageSubscriberNotificationTemplateStatusPageService.findBy({
|
||||
query: {
|
||||
statusPageId: statusPageId,
|
||||
},
|
||||
select: {
|
||||
statusPageSubscriberNotificationTemplateId: true,
|
||||
},
|
||||
skip: 0,
|
||||
limit: 100,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (templateLinks.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the template IDs
|
||||
const templateIds: Array<ObjectID> = templateLinks
|
||||
.map((link: StatusPageSubscriberNotificationTemplateStatusPage) => {
|
||||
return link.statusPageSubscriberNotificationTemplateId;
|
||||
})
|
||||
.filter((id: ObjectID | undefined): id is ObjectID => {
|
||||
return id !== undefined;
|
||||
});
|
||||
|
||||
if (templateIds.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the specific template matching the event type and notification method
|
||||
const templates = await this.findBy({
|
||||
query: {
|
||||
eventType: eventType,
|
||||
notificationMethod: notificationMethod,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
templateName: true,
|
||||
templateBody: true,
|
||||
emailSubject: true,
|
||||
eventType: true,
|
||||
notificationMethod: true,
|
||||
},
|
||||
skip: 0,
|
||||
limit: 100,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Find a template that matches one of the linked template IDs
|
||||
for (const template of templates) {
|
||||
if (templateIds.some((id: ObjectID) => { return id.toString() === template._id?.toString(); })) {
|
||||
return template;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available variables for a specific event type.
|
||||
* These variables can be used in templates with {{variableName}} syntax.
|
||||
*/
|
||||
public static getAvailableVariablesForEventType(
|
||||
eventType: StatusPageSubscriberNotificationEventType,
|
||||
): Array<{ name: string; description: string }> {
|
||||
const commonVariables = [
|
||||
{ name: "statusPageName", description: "Name of the status page" },
|
||||
{ name: "statusPageUrl", description: "URL of the status page" },
|
||||
{ name: "unsubscribeUrl", description: "URL to unsubscribe from notifications" },
|
||||
{ name: "resourcesAffected", description: "List of affected resources" },
|
||||
];
|
||||
|
||||
switch (eventType) {
|
||||
case StatusPageSubscriberNotificationEventType.SubscriberIncidentCreated:
|
||||
return [
|
||||
...commonVariables,
|
||||
{ name: "incidentTitle", description: "Title of the incident" },
|
||||
{ name: "incidentDescription", description: "Description of the incident" },
|
||||
{ name: "incidentSeverity", description: "Severity of the incident" },
|
||||
{ name: "detailsUrl", description: "URL to view incident details" },
|
||||
];
|
||||
|
||||
case StatusPageSubscriberNotificationEventType.SubscriberIncidentStateChanged:
|
||||
return [
|
||||
...commonVariables,
|
||||
{ name: "incidentTitle", description: "Title of the incident" },
|
||||
{ name: "incidentDescription", description: "Description of the incident" },
|
||||
{ name: "incidentSeverity", description: "Severity of the incident" },
|
||||
{ name: "incidentState", description: "Current state of the incident" },
|
||||
{ name: "detailsUrl", description: "URL to view incident details" },
|
||||
];
|
||||
|
||||
case StatusPageSubscriberNotificationEventType.SubscriberIncidentNoteCreated:
|
||||
return [
|
||||
...commonVariables,
|
||||
{ name: "incidentTitle", description: "Title of the incident" },
|
||||
{ name: "incidentSeverity", description: "Severity of the incident" },
|
||||
{ name: "incidentState", description: "Current state of the incident" },
|
||||
{ name: "postedAt", description: "When the note was posted" },
|
||||
{ name: "note", description: "Content of the note" },
|
||||
{ name: "detailsUrl", description: "URL to view incident details" },
|
||||
];
|
||||
|
||||
case StatusPageSubscriberNotificationEventType.SubscriberAnnouncementCreated:
|
||||
return [
|
||||
...commonVariables,
|
||||
{ name: "announcementTitle", description: "Title of the announcement" },
|
||||
{ name: "announcementDescription", description: "Description of the announcement" },
|
||||
{ name: "detailsUrl", description: "URL to view announcement details" },
|
||||
];
|
||||
|
||||
case StatusPageSubscriberNotificationEventType.SubscriberScheduledMaintenanceCreated:
|
||||
return [
|
||||
...commonVariables,
|
||||
{ name: "scheduledMaintenanceTitle", description: "Title of the scheduled maintenance" },
|
||||
{ name: "scheduledMaintenanceDescription", description: "Description of the scheduled maintenance" },
|
||||
{ name: "scheduledStartTime", description: "When the maintenance is scheduled to start" },
|
||||
{ name: "scheduledEndTime", description: "When the maintenance is scheduled to end" },
|
||||
{ name: "detailsUrl", description: "URL to view scheduled maintenance details" },
|
||||
];
|
||||
|
||||
case StatusPageSubscriberNotificationEventType.SubscriberScheduledMaintenanceStateChanged:
|
||||
return [
|
||||
...commonVariables,
|
||||
{ name: "scheduledMaintenanceTitle", description: "Title of the scheduled maintenance" },
|
||||
{ name: "scheduledMaintenanceDescription", description: "Description of the scheduled maintenance" },
|
||||
{ name: "scheduledMaintenanceState", description: "Current state of the scheduled maintenance" },
|
||||
{ name: "detailsUrl", description: "URL to view scheduled maintenance details" },
|
||||
];
|
||||
|
||||
case StatusPageSubscriberNotificationEventType.SubscriberScheduledMaintenanceNoteCreated:
|
||||
return [
|
||||
...commonVariables,
|
||||
{ name: "scheduledMaintenanceTitle", description: "Title of the scheduled maintenance" },
|
||||
{ name: "scheduledMaintenanceState", description: "Current state of the scheduled maintenance" },
|
||||
{ name: "postedAt", description: "When the note was posted" },
|
||||
{ name: "note", description: "Content of the note" },
|
||||
{ name: "detailsUrl", description: "URL to view scheduled maintenance details" },
|
||||
];
|
||||
|
||||
default:
|
||||
throw new BadDataException(`Unknown event type: ${eventType}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a template with the given variables.
|
||||
* Replaces {{variableName}} with the actual values.
|
||||
*/
|
||||
public static compileTemplate(
|
||||
template: string,
|
||||
variables: Record<string, string>,
|
||||
): string {
|
||||
let compiledTemplate = template;
|
||||
|
||||
for (const [key, value] of Object.entries(variables)) {
|
||||
const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
|
||||
compiledTemplate = compiledTemplate.replace(regex, value || "");
|
||||
}
|
||||
|
||||
return compiledTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Service();
|
||||
@@ -0,0 +1,10 @@
|
||||
import DatabaseService from "./DatabaseService";
|
||||
import Model from "../../Models/DatabaseModels/StatusPageSubscriberNotificationTemplateStatusPage";
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor() {
|
||||
super(Model);
|
||||
}
|
||||
}
|
||||
|
||||
export default new Service();
|
||||
@@ -476,6 +476,18 @@ enum Permission {
|
||||
ReadStatusPageAnnouncementTemplate = "ReadStatusPageAnnouncementTemplate",
|
||||
DeleteStatusPageAnnouncementTemplate = "DeleteStatusPageAnnouncementTemplate",
|
||||
|
||||
// Status Page Subscriber Notification Template Permissions (Owner + Admin Permission by default)
|
||||
CreateStatusPageSubscriberNotificationTemplate = "CreateStatusPageSubscriberNotificationTemplate",
|
||||
EditStatusPageSubscriberNotificationTemplate = "EditStatusPageSubscriberNotificationTemplate",
|
||||
ReadStatusPageSubscriberNotificationTemplate = "ReadStatusPageSubscriberNotificationTemplate",
|
||||
DeleteStatusPageSubscriberNotificationTemplate = "DeleteStatusPageSubscriberNotificationTemplate",
|
||||
|
||||
// Status Page Subscriber Notification Template Status Page Permissions (Owner + Admin Permission by default)
|
||||
CreateStatusPageSubscriberNotificationTemplateStatusPage = "CreateStatusPageSubscriberNotificationTemplateStatusPage",
|
||||
EditStatusPageSubscriberNotificationTemplateStatusPage = "EditStatusPageSubscriberNotificationTemplateStatusPage",
|
||||
ReadStatusPageSubscriberNotificationTemplateStatusPage = "ReadStatusPageSubscriberNotificationTemplateStatusPage",
|
||||
DeleteStatusPageSubscriberNotificationTemplateStatusPage = "DeleteStatusPageSubscriberNotificationTemplateStatusPage",
|
||||
|
||||
// Resource Permissions (Team Permission)
|
||||
CreateIncidentInternalNote = "CreateIncidentInternalNote",
|
||||
EditIncidentInternalNote = "EditIncidentInternalNote",
|
||||
@@ -1501,6 +1513,72 @@ export class PermissionHelper {
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CreateStatusPageSubscriberNotificationTemplate,
|
||||
title: "Create Status Page Subscriber Notification Template",
|
||||
description:
|
||||
"This permission can create Status Page Subscriber Notification Templates in this project.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.DeleteStatusPageSubscriberNotificationTemplate,
|
||||
title: "Delete Status Page Subscriber Notification Template",
|
||||
description:
|
||||
"This permission can delete Status Page Subscriber Notification Templates of this project.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.EditStatusPageSubscriberNotificationTemplate,
|
||||
title: "Edit Status Page Subscriber Notification Template",
|
||||
description:
|
||||
"This permission can edit Status Page Subscriber Notification Templates of this project.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.ReadStatusPageSubscriberNotificationTemplate,
|
||||
title: "Read Status Page Subscriber Notification Template",
|
||||
description:
|
||||
"This permission can read Status Page Subscriber Notification Templates of this project.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CreateStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
title: "Create Status Page Subscriber Notification Template Link",
|
||||
description:
|
||||
"This permission can create Status Page Subscriber Notification Template Links in this project.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.DeleteStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
title: "Delete Status Page Subscriber Notification Template Link",
|
||||
description:
|
||||
"This permission can delete Status Page Subscriber Notification Template Links of this project.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.EditStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
title: "Edit Status Page Subscriber Notification Template Link",
|
||||
description:
|
||||
"This permission can edit Status Page Subscriber Notification Template Links of this project.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.ReadStatusPageSubscriberNotificationTemplateStatusPage,
|
||||
title: "Read Status Page Subscriber Notification Template Link",
|
||||
description:
|
||||
"This permission can read Status Page Subscriber Notification Template Links of this project.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CreateProjectDomain,
|
||||
title: "Create Domain",
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Different types of notification events for Status Page Subscribers
|
||||
// Each event type has different variables available for templates
|
||||
|
||||
enum StatusPageSubscriberNotificationEventType {
|
||||
// Incident related events
|
||||
SubscriberIncidentCreated = "Subscriber Incident Created",
|
||||
SubscriberIncidentStateChanged = "Subscriber Incident State Changed",
|
||||
SubscriberIncidentNoteCreated = "Subscriber Incident Note Created",
|
||||
|
||||
// Announcement related events
|
||||
SubscriberAnnouncementCreated = "Subscriber Announcement Created",
|
||||
|
||||
// Scheduled Maintenance related events
|
||||
SubscriberScheduledMaintenanceCreated = "Subscriber Scheduled Maintenance Created",
|
||||
SubscriberScheduledMaintenanceStateChanged = "Subscriber Scheduled Maintenance State Changed",
|
||||
SubscriberScheduledMaintenanceNoteCreated = "Subscriber Scheduled Maintenance Note Created",
|
||||
}
|
||||
|
||||
export default StatusPageSubscriberNotificationEventType;
|
||||
@@ -0,0 +1,12 @@
|
||||
// Notification methods for Status Page Subscribers
|
||||
// Different methods require different template formats
|
||||
|
||||
enum StatusPageSubscriberNotificationMethod {
|
||||
Email = "Email",
|
||||
SMS = "SMS",
|
||||
Slack = "Slack",
|
||||
MicrosoftTeams = "Microsoft Teams",
|
||||
Webhook = "Webhook",
|
||||
}
|
||||
|
||||
export default StatusPageSubscriberNotificationMethod;
|
||||
@@ -0,0 +1,212 @@
|
||||
import ProjectUtil from "Common/UI/Utils/Project";
|
||||
import ModelTable from "Common/UI/Components/ModelTable/ModelTable";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import StatusPageSubscriberNotificationTemplate from "Common/Models/DatabaseModels/StatusPageSubscriberNotificationTemplate";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import Query from "Common/Types/BaseDatabase/Query";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import { ModalWidth } from "Common/UI/Components/Modal/Modal";
|
||||
import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import DropdownUtil from "Common/UI/Utils/Dropdown";
|
||||
import StatusPageSubscriberNotificationEventType from "Common/Types/StatusPage/StatusPageSubscriberNotificationEventType";
|
||||
import StatusPageSubscriberNotificationMethod from "Common/Types/StatusPage/StatusPageSubscriberNotificationMethod";
|
||||
import Pill from "Common/UI/Components/Pill/Pill";
|
||||
import { Green500, Yellow500, Blue500, Purple500, Cyan500 } from "Common/Types/BrandColors";
|
||||
import Color from "Common/Types/Color";
|
||||
|
||||
export interface ComponentProps {
|
||||
query?: Query<StatusPageSubscriberNotificationTemplate> | undefined;
|
||||
title?: string;
|
||||
description?: string;
|
||||
disableCreate?: boolean | undefined;
|
||||
}
|
||||
|
||||
const SubscriberNotificationTemplateTable: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
const getMethodColor = (method: StatusPageSubscriberNotificationMethod | undefined): Color => {
|
||||
switch (method) {
|
||||
case StatusPageSubscriberNotificationMethod.Email:
|
||||
return Green500;
|
||||
case StatusPageSubscriberNotificationMethod.SMS:
|
||||
return Yellow500;
|
||||
case StatusPageSubscriberNotificationMethod.Slack:
|
||||
return Purple500;
|
||||
case StatusPageSubscriberNotificationMethod.MicrosoftTeams:
|
||||
return Blue500;
|
||||
case StatusPageSubscriberNotificationMethod.Webhook:
|
||||
return Cyan500;
|
||||
default:
|
||||
return Green500;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ModelTable<StatusPageSubscriberNotificationTemplate>
|
||||
modelType={StatusPageSubscriberNotificationTemplate}
|
||||
userPreferencesKey="status-page-subscriber-notification-templates-table"
|
||||
id="table-status-page-subscriber-notification-templates"
|
||||
isDeleteable={true}
|
||||
isCreateable={!props.disableCreate}
|
||||
showViewIdButton={true}
|
||||
isEditable={true}
|
||||
name="Status Page > Subscriber Notification Templates"
|
||||
isViewable={true}
|
||||
query={{
|
||||
...(props.query || {}),
|
||||
projectId: ProjectUtil.getCurrentProjectId()!,
|
||||
}}
|
||||
cardProps={{
|
||||
title: props.title || "Subscriber Notification Templates",
|
||||
description:
|
||||
props.description ||
|
||||
"Create and manage custom notification templates for status page subscribers. These templates can be used to customize emails, SMS, Slack, and Microsoft Teams notifications.",
|
||||
}}
|
||||
noItemsMessage={"No subscriber notification templates found."}
|
||||
createEditModalWidth={ModalWidth.Large}
|
||||
showRefreshButton={true}
|
||||
viewPageRoute={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.STATUS_PAGE_SUBSCRIBER_TEMPLATES] as Route,
|
||||
)}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
templateName: true,
|
||||
},
|
||||
title: "Template Name",
|
||||
description: "A friendly name for this notification template.",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: "My Email Template",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
templateDescription: true,
|
||||
},
|
||||
title: "Template Description",
|
||||
description: "A description for this notification template.",
|
||||
fieldType: FormFieldSchemaType.LongText,
|
||||
required: false,
|
||||
placeholder: "Description of what this template is for...",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
eventType: true,
|
||||
},
|
||||
title: "Event Type",
|
||||
description: "The type of event this template is for.",
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
required: true,
|
||||
placeholder: "Select Event Type",
|
||||
dropdownOptions: DropdownUtil.getDropdownOptionsFromEnum(
|
||||
StatusPageSubscriberNotificationEventType,
|
||||
),
|
||||
},
|
||||
{
|
||||
field: {
|
||||
notificationMethod: true,
|
||||
},
|
||||
title: "Notification Method",
|
||||
description:
|
||||
"The notification method this template is for. Email uses HTML, SMS uses plain text, Slack/Teams use Markdown.",
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
required: true,
|
||||
placeholder: "Select Notification Method",
|
||||
dropdownOptions: DropdownUtil.getDropdownOptionsFromEnum(
|
||||
StatusPageSubscriberNotificationMethod,
|
||||
),
|
||||
},
|
||||
{
|
||||
field: {
|
||||
emailSubject: true,
|
||||
},
|
||||
title: "Email Subject (Email only)",
|
||||
description:
|
||||
"The subject line for email notifications. Only used when notification method is Email. You can use template variables like {{incidentTitle}}.",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
placeholder: "[{{statusPageName}}] {{incidentTitle}}",
|
||||
showIf: (values: any) => {
|
||||
return values.notificationMethod === StatusPageSubscriberNotificationMethod.Email;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
templateBody: true,
|
||||
},
|
||||
title: "Template Body",
|
||||
description:
|
||||
"The template content. Use {{variableName}} for variables. For Email: use HTML. For SMS: use plain text. For Slack/Teams: use Markdown.",
|
||||
fieldType: FormFieldSchemaType.HTML,
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
filters={[
|
||||
{
|
||||
field: {
|
||||
templateName: true,
|
||||
},
|
||||
title: "Template Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
eventType: true,
|
||||
},
|
||||
title: "Event Type",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
notificationMethod: true,
|
||||
},
|
||||
title: "Notification Method",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
templateName: true,
|
||||
},
|
||||
title: "Template Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
eventType: true,
|
||||
},
|
||||
title: "Event Type",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
notificationMethod: true,
|
||||
},
|
||||
title: "Notification Method",
|
||||
type: FieldType.Element,
|
||||
getElement: (item: StatusPageSubscriberNotificationTemplate) => {
|
||||
return (
|
||||
<Pill
|
||||
text={item.notificationMethod || "Unknown"}
|
||||
color={getMethodColor(item.notificationMethod)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
templateDescription: true,
|
||||
},
|
||||
title: "Description",
|
||||
type: FieldType.Text,
|
||||
noValueMessage: "-",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SubscriberNotificationTemplateTable;
|
||||
Reference in New Issue
Block a user