refactor: db id type replace uuid with cuid

This commit is contained in:
moonrailgun 2023-10-06 00:06:44 +08:00
parent b18611fa9a
commit 7f40a049c8
11 changed files with 71 additions and 63 deletions

View File

@ -12,7 +12,7 @@
<script> <script>
const el = document.createElement('script'); const el = document.createElement('script');
el.src = location.origin + '/tracker.js'; el.src = location.origin + '/tracker.js';
el.setAttribute('data-website-id', '1003c2f3-5cc0-482d-b081-53d8e4858b5d'); // For test el.setAttribute('data-website-id', 'clndcbgl90003vyf4uarkfl73'); // For test
document.head.append(el); document.head.append(el);
</script> </script>
</body> </body>

View File

@ -15,6 +15,7 @@
"dependencies": { "dependencies": {
"@ant-design/charts": "^1.4.2", "@ant-design/charts": "^1.4.2",
"@ant-design/icons": "^5.2.5", "@ant-design/icons": "^5.2.5",
"@paralleldrive/cuid2": "^2.2.2",
"@prisma/client": "^5.2.0", "@prisma/client": "^5.2.0",
"@tanstack/react-query": "^4.33.0", "@tanstack/react-query": "^4.33.0",
"@trpc/client": "^10.38.4", "@trpc/client": "^10.38.4",

14
pnpm-lock.yaml generated
View File

@ -7,6 +7,9 @@ dependencies:
'@ant-design/icons': '@ant-design/icons':
specifier: ^5.2.5 specifier: ^5.2.5
version: 5.2.5(react-dom@18.2.0)(react@18.2.0) version: 5.2.5(react-dom@18.2.0)(react@18.2.0)
'@paralleldrive/cuid2':
specifier: ^2.2.2
version: 2.2.2
'@prisma/client': '@prisma/client':
specifier: ^5.2.0 specifier: ^5.2.0
version: 5.2.0(prisma@5.2.0) version: 5.2.0(prisma@5.2.0)
@ -1651,6 +1654,11 @@ packages:
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
dev: false dev: false
/@noble/hashes@1.3.2:
resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==}
engines: {node: '>= 16'}
dev: false
/@nodelib/fs.scandir@2.1.5: /@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -1672,6 +1680,12 @@ packages:
fastq: 1.15.0 fastq: 1.15.0
dev: true dev: true
/@paralleldrive/cuid2@2.2.2:
resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
dependencies:
'@noble/hashes': 1.3.2
dev: false
/@prisma/client@5.2.0(prisma@5.2.0): /@prisma/client@5.2.0(prisma@5.2.0):
resolution: {integrity: sha512-AiTjJwR4J5Rh6Z/9ZKrBBLel3/5DzUNntMohOy7yObVnVoTNVFi2kvpLZlFuKO50d7yDspOtW6XBpiAd0BVXbQ==} resolution: {integrity: sha512-AiTjJwR4J5Rh6Z/9ZKrBBLel3/5DzUNntMohOy7yObVnVoTNVFi2kvpLZlFuKO50d7yDspOtW6XBpiAd0BVXbQ==}
engines: {node: '>=16.13'} engines: {node: '>=16.13'}

View File

@ -8,14 +8,14 @@ generator client {
} }
model User { model User {
id String @id @unique @default(uuid()) @db.Uuid id String @id @unique @default(cuid()) @db.VarChar(30)
username String @unique @db.VarChar(255) username String @unique @db.VarChar(255)
password String @db.VarChar(60) password String @db.VarChar(60)
role String @db.VarChar(50) role String @db.VarChar(50)
createdAt DateTime? @default(now()) @db.Timestamptz(6) createdAt DateTime? @default(now()) @db.Timestamptz(6)
updatedAt DateTime? @updatedAt @db.Timestamptz(6) updatedAt DateTime? @updatedAt @db.Timestamptz(6)
deletedAt DateTime? @db.Timestamptz(6) deletedAt DateTime? @db.Timestamptz(6)
currentWorkspaceId String? @db.Uuid currentWorkspaceId String? @db.VarChar(30)
currentWorkspace Workspace? @relation(fields: [currentWorkspaceId], references: [id]) currentWorkspace Workspace? @relation(fields: [currentWorkspaceId], references: [id])
@ -23,7 +23,7 @@ model User {
} }
model Workspace { model Workspace {
id String @id @unique @default(uuid()) @db.Uuid id String @id @unique @default(cuid()) @db.VarChar(30)
name String @db.VarChar(100) name String @db.VarChar(100)
createdAt DateTime? @default(now()) @db.Timestamptz(6) createdAt DateTime? @default(now()) @db.Timestamptz(6)
updatedAt DateTime? @updatedAt @db.Timestamptz(6) updatedAt DateTime? @updatedAt @db.Timestamptz(6)
@ -38,8 +38,8 @@ model Workspace {
} }
model WorkspacesOnUsers { model WorkspacesOnUsers {
userId String @db.Uuid userId String @db.VarChar(30)
workspaceId String @db.Uuid workspaceId String @db.VarChar(30)
role String @db.VarChar(100) role String @db.VarChar(100)
createdAt DateTime? @default(now()) @db.Timestamptz(6) createdAt DateTime? @default(now()) @db.Timestamptz(6)
updatedAt DateTime? @updatedAt @db.Timestamptz(6) updatedAt DateTime? @updatedAt @db.Timestamptz(6)
@ -53,12 +53,12 @@ model WorkspacesOnUsers {
} }
model Website { model Website {
id String @id @unique @default(uuid()) @db.Uuid id String @id @unique @default(cuid()) @db.VarChar(30)
name String @db.VarChar(100) name String @db.VarChar(100)
domain String? @db.VarChar(500) domain String? @db.VarChar(500)
shareId String? @unique @db.VarChar(50) shareId String? @unique @db.VarChar(50)
resetAt DateTime? @db.Timestamptz(6) resetAt DateTime? @db.Timestamptz(6)
workspaceId String @db.Uuid workspaceId String @db.VarChar(30)
createdAt DateTime? @default(now()) @db.Timestamptz(6) createdAt DateTime? @default(now()) @db.Timestamptz(6)
updatedAt DateTime? @updatedAt @db.Timestamptz(6) updatedAt DateTime? @updatedAt @db.Timestamptz(6)
deletedAt DateTime? @db.Timestamptz(6) deletedAt DateTime? @db.Timestamptz(6)
@ -76,7 +76,7 @@ model Website {
model WebsiteSession { model WebsiteSession {
id String @id @unique @db.Uuid id String @id @unique @db.Uuid
websiteId String @db.Uuid websiteId String @db.VarChar(30)
hostname String? @db.VarChar(100) hostname String? @db.VarChar(100)
browser String? @db.VarChar(20) browser String? @db.VarChar(20)
os String? @db.VarChar(20) os String? @db.VarChar(20)
@ -109,8 +109,8 @@ model WebsiteSession {
} }
model WebsiteEvent { model WebsiteEvent {
id String @id() @default(uuid()) @db.Uuid id String @id() @default(cuid()) @db.VarChar(30)
websiteId String @db.Uuid websiteId String @db.VarChar(30)
sessionId String @db.Uuid sessionId String @db.Uuid
urlPath String @db.VarChar(500) urlPath String @db.VarChar(500)
urlQuery String? @db.VarChar(500) urlQuery String? @db.VarChar(500)
@ -138,9 +138,9 @@ model WebsiteEvent {
} }
model WebsiteEventData { model WebsiteEventData {
id String @id() @default(uuid()) @db.Uuid id String @id() @default(cuid()) @db.VarChar(30)
websiteId String @db.Uuid websiteId String @db.VarChar(30)
websiteEventId String @db.Uuid websiteEventId String @db.VarChar(30)
eventKey String @db.VarChar(500) eventKey String @db.VarChar(500)
stringValue String? @db.VarChar(500) stringValue String? @db.VarChar(500)
numberValue Decimal? @db.Decimal(19, 4) numberValue Decimal? @db.Decimal(19, 4)
@ -159,8 +159,8 @@ model WebsiteEventData {
} }
model WebsiteSessionData { model WebsiteSessionData {
id String @id() @default(uuid()) @db.Uuid id String @id() @default(cuid()) @db.VarChar(30)
websiteId String @db.Uuid websiteId String @db.VarChar(30)
sessionId String @db.Uuid sessionId String @db.Uuid
stringValue String? @db.VarChar(500) stringValue String? @db.VarChar(500)
numberValue Decimal? @db.Decimal(19, 4) numberValue Decimal? @db.Decimal(19, 4)
@ -179,7 +179,7 @@ model WebsiteSessionData {
model TelemetrySession { model TelemetrySession {
id String @id @unique @db.Uuid id String @id @unique @db.Uuid
workspaceId String @db.Uuid workspaceId String @db.VarChar(30)
hostname String? @db.VarChar(100) hostname String? @db.VarChar(100)
browser String? @db.VarChar(20) browser String? @db.VarChar(20)
os String? @db.VarChar(20) os String? @db.VarChar(20)
@ -196,9 +196,9 @@ model TelemetrySession {
} }
model TelemetryEvent { model TelemetryEvent {
id String @id() @default(uuid()) @db.Uuid id String @id() @default(cuid()) @db.VarChar(30)
sessionId String @db.Uuid sessionId String @db.Uuid
workspaceId String @db.Uuid workspaceId String @db.VarChar(30)
eventName String? @db.VarChar(100) eventName String? @db.VarChar(100)
urlOrigin String @db.VarChar(500) urlOrigin String @db.VarChar(500)
urlPath String @db.VarChar(500) urlPath String @db.VarChar(500)
@ -213,8 +213,8 @@ model TelemetryEvent {
} }
model Notification { model Notification {
id String @id() @default(uuid()) @db.Uuid id String @id() @default(cuid()) @db.VarChar(30)
workspaceId String @db.Uuid workspaceId String @db.VarChar(30)
name String @db.VarChar(100) name String @db.VarChar(100)
type String @db.VarChar(100) type String @db.VarChar(100)
payload Json @db.Json payload Json @db.Json
@ -228,8 +228,8 @@ model Notification {
} }
model Monitor { model Monitor {
id String @id() @default(uuid()) @db.Uuid id String @id() @default(cuid()) @db.VarChar(30)
workspaceId String @db.Uuid workspaceId String @db.VarChar(30)
name String @db.VarChar(100) name String @db.VarChar(100)
type String @db.VarChar(100) type String @db.VarChar(100)
active Boolean @default(true) @db.Boolean active Boolean @default(true) @db.Boolean
@ -250,9 +250,9 @@ model Monitor {
} }
model MonitorEvent { model MonitorEvent {
id String @id @default(uuid()) @db.Uuid id String @id @default(cuid()) @db.VarChar(30)
message String @db.VarChar(500) message String @db.VarChar(500)
monitorId String @db.Uuid monitorId String @db.VarChar(30)
type String @db.VarChar(100) // UP or DOWN type String @db.VarChar(100) // UP or DOWN
createdAt DateTime? @default(now()) @db.Timestamptz(6) createdAt DateTime? @default(now()) @db.Timestamptz(6)
@ -260,8 +260,8 @@ model MonitorEvent {
} }
model MonitorData { model MonitorData {
id String @id @default(uuid()) @db.Uuid id String @id @default(cuid()) @db.VarChar(30)
monitorId String @db.Uuid monitorId String @db.VarChar(30)
value Int @default(0) @db.Integer // -1 means error value Int @default(0) @db.Integer // -1 means error
createdAt DateTime? @default(now()) @db.Timestamptz(6) createdAt DateTime? @default(now()) @db.Timestamptz(6)

View File

@ -1,5 +1,5 @@
import { Website, WebsiteSession } from '@prisma/client'; import { Website, WebsiteSession } from '@prisma/client';
import { flattenJSON, hashUuid, isUuid, parseToken } from '../utils/common'; import { flattenJSON, hashUuid, isCuid, parseToken } from '../utils/common';
import { prisma } from './_client'; import { prisma } from './_client';
import { Request } from 'express'; import { Request } from 'express';
import { getClientInfo } from '../utils/detect'; import { getClientInfo } from '../utils/detect';
@ -66,7 +66,7 @@ export async function findSession(req: Request): Promise<{
throw new Error('Invalid hostname.'); throw new Error('Invalid hostname.');
} }
if (!isUuid(websiteId)) { if (!isCuid(websiteId)) {
throw new Error('Invalid website ID.'); throw new Error('Invalid website ID.');
} }
@ -268,7 +268,7 @@ export async function getWebsiteOnlineUserCount(
const res = await prisma.$queryRaw< const res = await prisma.$queryRaw<
Ret[] Ret[]
>`SELECT count(distinct "sessionId") x FROM "WebsiteEvent" where "websiteId" = ${websiteId}::uuid AND "createdAt" >= ${startAt}`; >`SELECT count(distinct "sessionId") x FROM "WebsiteEvent" where "websiteId" = ${websiteId} AND "createdAt" >= ${startAt}`;
return res?.[0].x ?? 0; return res?.[0].x ?? 0;
} }

View File

@ -157,7 +157,7 @@ export async function getWorkspaceWebsitePageview(
count(1) y count(1) y
from "WebsiteEvent" from "WebsiteEvent"
${joinSession} ${joinSession}
where "WebsiteEvent"."websiteId" = ${params.websiteId}::uuid where "WebsiteEvent"."websiteId" = ${params.websiteId}
and "WebsiteEvent"."createdAt" between ${ and "WebsiteEvent"."createdAt" between ${
params.startDate params.startDate
}::timestamptz and ${params.endDate}::timestamptz }::timestamptz and ${params.endDate}::timestamptz
@ -182,7 +182,7 @@ export async function getWorkspaceWebsiteSession(
count(distinct "WebsiteEvent"."sessionId") y count(distinct "WebsiteEvent"."sessionId") y
from "WebsiteEvent" from "WebsiteEvent"
${joinSession} ${joinSession}
where "WebsiteEvent"."websiteId" = ${params.websiteId}::uuid where "WebsiteEvent"."websiteId" = ${params.websiteId}
and "WebsiteEvent"."createdAt" between ${ and "WebsiteEvent"."createdAt" between ${
params.startDate params.startDate
}::timestamptz and ${params.endDate}::timestamptz }::timestamptz and ${params.endDate}::timestamptz
@ -216,7 +216,7 @@ export async function getWorkspaceWebsiteStats(
join "Website" join "Website"
on "WebsiteEvent"."websiteId" = "Website"."id" on "WebsiteEvent"."websiteId" = "Website"."id"
${joinSession} ${joinSession}
where "Website"."id" = ${params.websiteId}::uuid where "Website"."id" = ${params.websiteId}
and "WebsiteEvent"."createdAt" between ${ and "WebsiteEvent"."createdAt" between ${
params.startDate params.startDate
}::timestamptz and ${params.endDate}::timestamptz }::timestamptz and ${params.endDate}::timestamptz

View File

@ -29,7 +29,7 @@ websiteRouter.post(
screen: yup.string().max(11), screen: yup.string().max(11),
title: yup.string().max(500), title: yup.string().max(500),
url: yup.string().max(500), url: yup.string().max(500),
website: yup.string().uuid().required(), website: yup.string().required(),
name: yup.string().max(50), name: yup.string().max(50),
}) })
.required() .required()

View File

@ -22,11 +22,7 @@ export const workspaceRouter = Router();
workspaceRouter.get( workspaceRouter.get(
'/websites', '/websites',
validate( validate(
query('workspaceId') query('workspaceId').isString().withMessage('workspaceId should be string')
.isString()
.withMessage('workspaceId should be string')
.isUUID()
.withMessage('workspaceId should be UUID')
), ),
auth(), auth(),
workspacePermission(), workspacePermission(),
@ -42,7 +38,7 @@ workspaceRouter.get(
workspaceRouter.post( workspaceRouter.post(
'/website', '/website',
validate( validate(
body('workspaceId').isUUID().withMessage('workspaceId should be UUID'), body('workspaceId').isString(),
body('name') body('name')
.isString() .isString()
.withMessage('name should be string') .withMessage('name should be string')
@ -67,10 +63,7 @@ workspaceRouter.post(
workspaceRouter.get( workspaceRouter.get(
'/website/:websiteId', '/website/:websiteId',
validate( validate(query('workspaceId').isString(), param('websiteId').isString()),
query('workspaceId').isUUID().withMessage('workspaceId should be UUID'),
param('websiteId').isUUID().withMessage('workspaceId should be UUID')
),
auth(), auth(),
workspacePermission(), workspacePermission(),
async (req, res) => { async (req, res) => {
@ -86,7 +79,7 @@ workspaceRouter.get(
workspaceRouter.post( workspaceRouter.post(
'/website/:websiteId', '/website/:websiteId',
validate( validate(
body('workspaceId').isUUID().withMessage('workspaceId should be UUID'), body('workspaceId').isString(),
param('websiteId') param('websiteId')
.isString() .isString()
.isUUID() .isUUID()
@ -122,10 +115,7 @@ workspaceRouter.post(
workspaceRouter.delete( workspaceRouter.delete(
'/:workspaceId/website/:websiteId', '/:workspaceId/website/:websiteId',
validate( validate(param('workspaceId').isString(), param('websiteId').isString()),
param('workspaceId').isUUID().withMessage('workspaceId should be UUID'),
param('websiteId').isUUID().withMessage('workspaceId should be UUID')
),
auth(), auth(),
workspacePermission([ROLES.owner]), workspacePermission([ROLES.owner]),
async (req, res) => { async (req, res) => {
@ -141,8 +131,8 @@ workspaceRouter.delete(
workspaceRouter.get( workspaceRouter.get(
'/:workspaceId/website/:websiteId/pageviews', '/:workspaceId/website/:websiteId/pageviews',
validate( validate(
param('workspaceId').isUUID().withMessage('workspaceId should be UUID'), param('workspaceId').isString(),
param('websiteId').isUUID().withMessage('workspaceId should be UUID'), param('websiteId').isString(),
query('startAt').isNumeric().withMessage('startAt should be number'), query('startAt').isNumeric().withMessage('startAt should be number'),
query('endAt').isNumeric().withMessage('startAt should be number') query('endAt').isNumeric().withMessage('startAt should be number')
), ),
@ -201,8 +191,8 @@ workspaceRouter.get(
workspaceRouter.get( workspaceRouter.get(
'/:workspaceId/website/:websiteId/stats', '/:workspaceId/website/:websiteId/stats',
validate( validate(
param('workspaceId').isUUID().withMessage('workspaceId should be UUID'), param('workspaceId').isString(),
param('websiteId').isUUID().withMessage('workspaceId should be UUID'), param('websiteId').isString(),
query('startAt').isNumeric().withMessage('startAt should be number'), query('startAt').isNumeric().withMessage('startAt should be number'),
query('endAt').isNumeric().withMessage('startAt should be number') query('endAt').isNumeric().withMessage('startAt should be number')
), ),

View File

@ -56,14 +56,14 @@ export const systemAdminProcedure = t.procedure.use(isSystemAdmin);
export const workspaceProcedure = protectProedure export const workspaceProcedure = protectProedure
.input( .input(
z.object({ z.object({
workspaceId: z.string().uuid(), workspaceId: z.string().cuid2(),
}) })
) )
.use(createWorkspacePermissionMiddleware()); .use(createWorkspacePermissionMiddleware());
export const workspaceOwnerProcedure = protectProedure export const workspaceOwnerProcedure = protectProedure
.input( .input(
z.object({ z.object({
workspaceId: z.string().uuid(), workspaceId: z.string().cuid2(),
}) })
) )
.use(createWorkspacePermissionMiddleware([ROLES.owner])); .use(createWorkspacePermissionMiddleware([ROLES.owner]));

View File

@ -7,6 +7,9 @@ import minMax from 'dayjs/plugin/minMax';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import _ from 'lodash'; import _ from 'lodash';
import { getWorkspaceWebsiteDateRange } from '../model/workspace'; import { getWorkspaceWebsiteDateRange } from '../model/workspace';
import { isCuid } from '@paralleldrive/cuid2';
export { isCuid };
dayjs.extend(minMax); dayjs.extend(minMax);

View File

@ -2,7 +2,7 @@ import { Server as SocketIOServer } from 'socket.io';
import { Server as HTTPServer } from 'http'; import { Server as HTTPServer } from 'http';
import { jwtVerify } from '../middleware/auth'; import { jwtVerify } from '../middleware/auth';
import { socketEventBus } from './shared'; import { socketEventBus } from './shared';
import { isUuid } from '../utils/common'; import { isCuid } from '../utils/common';
export function initSocketio(httpServer: HTTPServer) { export function initSocketio(httpServer: HTTPServer) {
const io = new SocketIOServer(httpServer, { const io = new SocketIOServer(httpServer, {
@ -17,7 +17,7 @@ export function initSocketio(httpServer: HTTPServer) {
io.of((name, auth, next) => { io.of((name, auth, next) => {
const workspaceId = name.replace(/^\//, ''); const workspaceId = name.replace(/^\//, '');
next(null, isUuid(workspaceId)); // or false, when the creation is denied next(null, isCuid(workspaceId)); // or false, when the creation is denied
}) })
.use(async (socket, next) => { .use(async (socket, next) => {
// Auth // Auth