refactor: update role's position and add status page edit check

This commit is contained in:
moonrailgun 2023-12-16 13:56:28 +08:00
parent 824d5cd14e
commit bb83f4b56d
12 changed files with 65 additions and 25 deletions

View File

@ -1,24 +1,28 @@
import React from 'react'; import React from 'react';
import { useParams } from 'react-router'; import { useParams } from 'react-router';
import { trpc } from '../api/trpc'; import { trpc } from '../api/trpc';
import { Empty } from 'antd'; import { Button, Empty } from 'antd';
import { MonitorHealthBar } from '../components/monitor/MonitorHealthBar'; 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(() => { export const StatusPage: React.FC = React.memo(() => {
const { slug } = useParams<{ slug: string }>(); const { slug } = useParams<{ slug: string }>();
useUserStore();
const { data: info } = trpc.monitor.getPageInfo.useQuery({ const { data: info } = trpc.monitor.getPageInfo.useQuery({
slug: slug!, slug: slug!,
}); });
const allowEdit = useAllowEdit(info?.workspaceId);
const monitorList = info?.monitorList ?? []; const monitorList = info?.monitorList ?? [];
return ( return (
<div className="w-4/5 mx-auto py-8 "> <div className="w-4/5 mx-auto py-8 ">
<div className="text-2xl mb-4">{info?.title}</div> <div className="text-2xl mb-4">{info?.title}</div>
<div>{allowEdit && <Button type="primary">Edit</Button>}</div>
<div className="text-lg mb-2">Services</div> <div className="text-lg mb-2">Services</div>
{info && ( {info && (
@ -44,3 +48,19 @@ export const StatusPage: React.FC = React.memo(() => {
); );
}); });
StatusPage.displayName = 'StatusPage'; 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;
}

View File

@ -1,6 +1,6 @@
import { Handler } from 'express'; import { Handler } from 'express';
import { getWorkspaceUser } from '../model/workspace'; import { getWorkspaceUser } from '../model/workspace';
import { ROLES } from '../utils/const'; import { ROLES } from '../../shared';
export function workspacePermission(roles: ROLES[] = []): Handler { export function workspacePermission(roles: ROLES[] = []): Handler {
return async (req, res, next) => { return async (req, res, next) => {

View File

@ -1,6 +1,6 @@
import { prisma } from './_client'; import { prisma } from './_client';
import bcryptjs from 'bcryptjs'; import bcryptjs from 'bcryptjs';
import { ROLES, SYSTEM_ROLES } from '../utils/const'; import { ROLES, SYSTEM_ROLES } from '../../shared';
import { jwtVerify } from '../middleware/auth'; import { jwtVerify } from '../middleware/auth';
import { TRPCError } from '@trpc/server'; import { TRPCError } from '@trpc/server';
import { Prisma } from '@prisma/client'; import { Prisma } from '@prisma/client';

View File

@ -1,13 +1,14 @@
import { Router } from 'express'; import { Router } from 'express';
import { body, validate } from '../middleware/validate'; import { body, validate } from '../middleware/validate';
import * as yup from 'yup'; import * as yup from 'yup';
import { COLLECTION_TYPE, HOSTNAME_REGEX } from '../utils/const'; import { COLLECTION_TYPE } from '../utils/const';
import { import {
findSession, findSession,
saveWebsiteEvent, saveWebsiteEvent,
saveWebsiteSessionData, saveWebsiteSessionData,
} from '../model/website'; } from '../model/website';
import { createToken } from '../utils/common'; import { createToken } from '../utils/common';
import { hostnameRegex } from '../../shared';
export const websiteRouter = Router(); export const websiteRouter = Router();
@ -23,7 +24,7 @@ websiteRouter.post(
.object() .object()
.shape({ .shape({
data: yup.object(), data: yup.object(),
hostname: yup.string().matches(HOSTNAME_REGEX).max(100), hostname: yup.string().matches(hostnameRegex).max(100),
language: yup.string().max(35), language: yup.string().max(35),
referrer: yup.string().max(500), referrer: yup.string().max(500),
screen: yup.string().max(11), screen: yup.string().max(11),

View File

@ -12,8 +12,8 @@ import {
getWorkspaceWebsiteStats, getWorkspaceWebsiteStats,
} from '../model/workspace'; } from '../model/workspace';
import { parseDateRange } from '../utils/common'; import { parseDateRange } from '../utils/common';
import { ROLES } from '../utils/const';
import { QueryFilters } from '../utils/prisma'; import { QueryFilters } from '../utils/prisma';
import { ROLES } from '../../shared';
export const workspaceRouter = Router(); export const workspaceRouter = Router();

View File

@ -1,10 +1,32 @@
import { router, workspaceOwnerProcedure } from '../trpc'; import { publicProcedure, router, workspaceOwnerProcedure } from '../trpc';
import { z } from 'zod'; import { z } from 'zod';
import { prisma } from '../../model/_client'; import { prisma } from '../../model/_client';
import { workspaceDashboardLayoutSchema } from '../../model/_schema'; import { workspaceDashboardLayoutSchema } from '../../model/_schema';
import { Prisma } from '@prisma/client'; import { Prisma } from '@prisma/client';
export const workspaceRouter = router({ 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 updateDashboardOrder: workspaceOwnerProcedure
.input( .input(
z.object({ z.object({

View File

@ -3,7 +3,7 @@ import _ from 'lodash';
import { z } from 'zod'; import { z } from 'zod';
import { jwtVerify } from '../middleware/auth'; import { jwtVerify } from '../middleware/auth';
import { getWorkspaceUser } from '../model/workspace'; import { getWorkspaceUser } from '../model/workspace';
import { ROLES, SYSTEM_ROLES } from '../utils/const'; import { ROLES, SYSTEM_ROLES } from '../../shared';
import type { IncomingMessage } from 'http'; import type { IncomingMessage } from 'http';
import { OpenApiMeta } from 'trpc-openapi'; import { OpenApiMeta } from 'trpc-openapi';

View File

@ -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 = { export const COLLECTION_TYPE = {
event: 'event', event: 'event',
identify: 'identify', identify: 'identify',

View File

@ -1,6 +1,6 @@
import puppeteer from 'puppeteer'; import puppeteer from 'puppeteer';
import { jwtSign } from '../../middleware/auth'; import { jwtSign } from '../../middleware/auth';
import { SYSTEM_ROLES } from '../const'; import { SYSTEM_ROLES } from '../../../shared';
import { settings } from '../settings'; import { settings } from '../settings';
/** /**

View File

@ -1,3 +1,4 @@
export * from './date'; export * from './date';
export * from './server'; export * from './server';
export * from './regex'; export * from './regex';
export * from './role';

View File

@ -1,4 +1,4 @@
export const hostnameRegex = 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]+)*$/; export const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;

9
src/shared/role.ts Normal file
View File

@ -0,0 +1,9 @@
export enum SYSTEM_ROLES {
admin = 'admin',
user = 'user',
}
export enum ROLES {
owner = 'owner',
readOnly = 'readOnly',
}