diff --git a/src/client/pages/Status.tsx b/src/client/pages/Status.tsx index 58ba197..3034f34 100644 --- a/src/client/pages/Status.tsx +++ b/src/client/pages/Status.tsx @@ -1,24 +1,28 @@ import React from 'react'; import { useParams } from 'react-router'; import { trpc } from '../api/trpc'; -import { Empty } from 'antd'; +import { Button, Empty } from 'antd'; import { MonitorHealthBar } from '../components/monitor/MonitorHealthBar'; -import { useUserStore } from '../store/user'; +import { useUserInfo } from '../store/user'; +import { ROLES } from '../../shared'; export const StatusPage: React.FC = React.memo(() => { const { slug } = useParams<{ slug: string }>(); - useUserStore(); const { data: info } = trpc.monitor.getPageInfo.useQuery({ slug: slug!, }); + const allowEdit = useAllowEdit(info?.workspaceId); + const monitorList = info?.monitorList ?? []; return (
{info?.title}
+
{allowEdit && }
+
Services
{info && ( @@ -44,3 +48,19 @@ export const StatusPage: React.FC = React.memo(() => { ); }); StatusPage.displayName = 'StatusPage'; + +function useAllowEdit(workspaceId?: string): boolean { + const userInfo = useUserInfo(); + + const { data: role } = trpc.workspace.getUserWorkspaceRole.useQuery( + { + workspaceId: workspaceId!, + userId: userInfo?.id!, + }, + { + enabled: !!userInfo?.id && !!workspaceId, + } + ); + + return role === ROLES.owner; +} diff --git a/src/server/middleware/workspace.ts b/src/server/middleware/workspace.ts index aee1a8f..547ea85 100644 --- a/src/server/middleware/workspace.ts +++ b/src/server/middleware/workspace.ts @@ -1,6 +1,6 @@ import { Handler } from 'express'; import { getWorkspaceUser } from '../model/workspace'; -import { ROLES } from '../utils/const'; +import { ROLES } from '../../shared'; export function workspacePermission(roles: ROLES[] = []): Handler { return async (req, res, next) => { diff --git a/src/server/model/user.ts b/src/server/model/user.ts index eef7cad..9b96c18 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, SYSTEM_ROLES } from '../utils/const'; +import { ROLES, SYSTEM_ROLES } from '../../shared'; import { jwtVerify } from '../middleware/auth'; import { TRPCError } from '@trpc/server'; import { Prisma } from '@prisma/client'; diff --git a/src/server/router/website.ts b/src/server/router/website.ts index bf2e56b..a8921e1 100644 --- a/src/server/router/website.ts +++ b/src/server/router/website.ts @@ -1,13 +1,14 @@ import { Router } from 'express'; import { body, validate } from '../middleware/validate'; import * as yup from 'yup'; -import { COLLECTION_TYPE, HOSTNAME_REGEX } from '../utils/const'; +import { COLLECTION_TYPE } from '../utils/const'; import { findSession, saveWebsiteEvent, saveWebsiteSessionData, } from '../model/website'; import { createToken } from '../utils/common'; +import { hostnameRegex } from '../../shared'; export const websiteRouter = Router(); @@ -23,7 +24,7 @@ websiteRouter.post( .object() .shape({ data: yup.object(), - hostname: yup.string().matches(HOSTNAME_REGEX).max(100), + hostname: yup.string().matches(hostnameRegex).max(100), language: yup.string().max(35), referrer: yup.string().max(500), screen: yup.string().max(11), diff --git a/src/server/router/workspace.ts b/src/server/router/workspace.ts index 6594a6a..10373c9 100644 --- a/src/server/router/workspace.ts +++ b/src/server/router/workspace.ts @@ -12,8 +12,8 @@ import { getWorkspaceWebsiteStats, } from '../model/workspace'; import { parseDateRange } from '../utils/common'; -import { ROLES } from '../utils/const'; import { QueryFilters } from '../utils/prisma'; +import { ROLES } from '../../shared'; export const workspaceRouter = Router(); diff --git a/src/server/trpc/routers/workspace.ts b/src/server/trpc/routers/workspace.ts index 74cd96f..1c11b20 100644 --- a/src/server/trpc/routers/workspace.ts +++ b/src/server/trpc/routers/workspace.ts @@ -1,10 +1,32 @@ -import { router, workspaceOwnerProcedure } from '../trpc'; +import { publicProcedure, router, workspaceOwnerProcedure } from '../trpc'; import { z } from 'zod'; import { prisma } from '../../model/_client'; import { workspaceDashboardLayoutSchema } from '../../model/_schema'; import { Prisma } from '@prisma/client'; export const workspaceRouter = router({ + getUserWorkspaceRole: publicProcedure + .input( + z.object({ + userId: z.string(), + workspaceId: z.string(), + }) + ) + .output(z.string().nullable()) + .query(async ({ input }) => { + const { userId, workspaceId } = input; + + const relation = await prisma.workspacesOnUsers.findUnique({ + where: { + userId_workspaceId: { + workspaceId, + userId, + }, + }, + }); + + return relation?.role ?? null; + }), updateDashboardOrder: workspaceOwnerProcedure .input( z.object({ diff --git a/src/server/trpc/trpc.ts b/src/server/trpc/trpc.ts index 0509f75..f009d60 100644 --- a/src/server/trpc/trpc.ts +++ b/src/server/trpc/trpc.ts @@ -3,7 +3,7 @@ import _ from 'lodash'; import { z } from 'zod'; import { jwtVerify } from '../middleware/auth'; import { getWorkspaceUser } from '../model/workspace'; -import { ROLES, SYSTEM_ROLES } from '../utils/const'; +import { ROLES, SYSTEM_ROLES } from '../../shared'; import type { IncomingMessage } from 'http'; import { OpenApiMeta } from 'trpc-openapi'; diff --git a/src/server/utils/const.ts b/src/server/utils/const.ts index 74fa3b3..75ade12 100644 --- a/src/server/utils/const.ts +++ b/src/server/utils/const.ts @@ -1,16 +1,3 @@ -export enum SYSTEM_ROLES { - admin = 'admin', - user = 'user', -} - -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])$/; - export const COLLECTION_TYPE = { event: 'event', identify: 'identify', diff --git a/src/server/utils/screenshot/website.ts b/src/server/utils/screenshot/website.ts index a006e94..907a8a4 100644 --- a/src/server/utils/screenshot/website.ts +++ b/src/server/utils/screenshot/website.ts @@ -1,6 +1,6 @@ import puppeteer from 'puppeteer'; import { jwtSign } from '../../middleware/auth'; -import { SYSTEM_ROLES } from '../const'; +import { SYSTEM_ROLES } from '../../../shared'; import { settings } from '../settings'; /** diff --git a/src/shared/index.ts b/src/shared/index.ts index 842b65e..cd36ae7 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -1,3 +1,4 @@ export * from './date'; export * from './server'; export * from './regex'; +export * from './role'; diff --git a/src/shared/regex.ts b/src/shared/regex.ts index 044ffa9..1f60db9 100644 --- a/src/shared/regex.ts +++ b/src/shared/regex.ts @@ -1,4 +1,4 @@ export const hostnameRegex = - /^(([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])$/; + /^(([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])$/; export const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; diff --git a/src/shared/role.ts b/src/shared/role.ts new file mode 100644 index 0000000..2939236 --- /dev/null +++ b/src/shared/role.ts @@ -0,0 +1,9 @@ +export enum SYSTEM_ROLES { + admin = 'admin', + user = 'user', +} + +export enum ROLES { + owner = 'owner', + readOnly = 'readOnly', +}