diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f9a3247..6631f0e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -28,8 +28,9 @@ model Workspace { createdAt DateTime? @default(now()) @db.Timestamptz(6) updatedAt DateTime? @updatedAt @db.Timestamptz(6) - users WorkspacesOnUsers[] - websites Website[] + users WorkspacesOnUsers[] + websites Website[] + notifications Notification[] // for user currentWorkspace selectedUsers User[] @@ -193,7 +194,7 @@ model TelemetryEvent { id String @id() @default(uuid()) @db.Uuid sessionId String @db.Uuid workspaceId String @db.Uuid - eventName String? @db.VarChar(100) + eventName String? @db.VarChar(100) urlOrigin String @db.VarChar(500) urlPath String @db.VarChar(500) createdAt DateTime? @default(now()) @db.Timestamptz(6) @@ -205,3 +206,16 @@ model TelemetryEvent { @@index([workspaceId]) @@index([workspaceId, createdAt]) } + +model Notification { + id String @id() @default(uuid()) @db.Uuid + workspaceId String @db.Uuid + name String @db.VarChar(100) + type String @db.VarChar(100) + payload Json @db.Json + createdAt DateTime? @default(now()) @db.Timestamptz(6) + + workspace Workspace @relation(fields: [workspaceId], references: [id]) + + @@index([workspaceId]) +} diff --git a/src/server/middleware/workspace.ts b/src/server/middleware/workspace.ts index 0375e78..aee1a8f 100644 --- a/src/server/middleware/workspace.ts +++ b/src/server/middleware/workspace.ts @@ -1,7 +1,8 @@ import { Handler } from 'express'; import { getWorkspaceUser } from '../model/workspace'; +import { ROLES } from '../utils/const'; -export function workspacePermission(roles: string[] = []): Handler { +export function workspacePermission(roles: ROLES[] = []): Handler { return async (req, res, next) => { const workspaceId = req.body.workspaceId ?? req.query.workspaceId ?? req.params.workspaceId; @@ -23,7 +24,7 @@ export function workspacePermission(roles: string[] = []): Handler { } if (Array.isArray(roles) && roles.length > 0) { - if (!roles.includes(info.role)) { + if (!roles.includes(info.role as ROLES)) { throw new Error( `Workspace roles not has this permission, need ${roles}` ); diff --git a/src/server/model/notification.ts b/src/server/model/notification.ts new file mode 100644 index 0000000..acd67e1 --- /dev/null +++ b/src/server/model/notification.ts @@ -0,0 +1,27 @@ +import { prisma } from './_client'; + +export function getWorkspaceNotifications(workspaceId: string) { + return prisma.notification.findMany({ + where: { + workspaceId, + }, + }); +} + +export async function createWorkspaceNotification( + workspaceId: string, + name: string, + type: string, + payload: Record +) { + const notification = await prisma.notification.create({ + data: { + workspaceId, + name, + type, + payload, + }, + }); + + return notification; +} diff --git a/src/server/model/user.ts b/src/server/model/user.ts index 34f99bc..913cca6 100644 --- a/src/server/model/user.ts +++ b/src/server/model/user.ts @@ -1,6 +1,6 @@ import { prisma } from './_client'; import bcryptjs from 'bcryptjs'; -import { ROLES } from '../utils/const'; +import { ROLES, SYSTEM_ROLES } from '../utils/const'; import { jwtVerify } from '../middleware/auth'; async function hashPassword(password: string) { @@ -59,7 +59,7 @@ export async function createAdminUser(username: string, password: string) { data: { username, password: await hashPassword(password), - role: ROLES.admin, + role: SYSTEM_ROLES.admin, workspaces: { create: [ { @@ -106,7 +106,7 @@ export async function createUser(username: string, password: string) { data: { username, password: await hashPassword(password), - role: ROLES.user, + role: SYSTEM_ROLES.user, workspaces: { create: [ { diff --git a/src/server/model/workspace.ts b/src/server/model/workspace.ts index 4f14c81..c64d601 100644 --- a/src/server/model/workspace.ts +++ b/src/server/model/workspace.ts @@ -195,7 +195,7 @@ export async function getWorkspaceWebsiteSession( export async function getWorkspaceWebsiteStats( websiteId: string, filters: QueryFilters -): any { +): Promise { const { filterQuery, joinSession, params } = await parseFilters(websiteId, { ...filters, }); diff --git a/src/server/router/notification.ts b/src/server/router/notification.ts new file mode 100644 index 0000000..2ce70bc --- /dev/null +++ b/src/server/router/notification.ts @@ -0,0 +1,47 @@ +import { Router } from 'express'; +import { auth } from '../middleware/auth'; +import { body, param, validate } from '../middleware/validate'; +import { workspacePermission } from '../middleware/workspace'; +import { + createWorkspaceNotification, + getWorkspaceNotifications, +} from '../model/notification'; +import { ROLES } from '../utils/const'; + +export const notificationRouter = Router(); + +notificationRouter.get( + '/list', + validate(param('workspaceId').isUUID()), + auth(), + workspacePermission(), + async (req, res) => { + const list = await getWorkspaceNotifications(req.params.workspaceId); + + res.json({ list }); + } +); + +notificationRouter.post( + '/create', + validate( + param('workspaceId').isUUID(), + body('name').isString(), + body('type').isString(), + body('payload').isObject() + ), + auth(), + workspacePermission([ROLES.owner]), + async (req, res) => { + const workspaceId = req.params.workspaceId; + const { name, type, payload } = req.body; + const notification = await createWorkspaceNotification( + workspaceId, + name, + type, + payload + ); + + res.json({ notification }); + } +); diff --git a/src/server/utils/const.ts b/src/server/utils/const.ts index 8b87d8f..34c76d8 100644 --- a/src/server/utils/const.ts +++ b/src/server/utils/const.ts @@ -1,12 +1,12 @@ -export const ROLES = { - // System Role - admin: 'admin', - user: 'user', +export enum SYSTEM_ROLES { + admin = 'admin', + user = 'user', +} - // Workspace Role - owner: 'owner', - readOnly: 'readOnly', -} as const; +export enum ROLES { + owner = 'owner', + readOnly = 'readOnly', +} export const HOSTNAME_REGEX = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/;