feat: add prisma

This commit is contained in:
moonrailgun 2023-09-02 20:13:50 +08:00
parent 89c0874983
commit 6761bc5d08
8 changed files with 262 additions and 1 deletions

1
.env.example Normal file
View File

@ -0,0 +1 @@
DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
.env
# Logs # Logs
logs logs
*.log *.log

View File

@ -5,13 +5,19 @@
"scripts": { "scripts": {
"dev": "nodemon src/server/main.ts -w src/server", "dev": "nodemon src/server/main.ts -w src/server",
"start": "NODE_ENV=production ts-node src/server/main.ts", "start": "NODE_ENV=production ts-node src/server/main.ts",
"build": "vite build" "build": "vite build",
"db:push": "prisma db push",
"db:generate": "prisma generate",
"db:studio": "prisma studio"
}, },
"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",
"@prisma/client": "^5.2.0",
"antd": "^5.8.5", "antd": "^5.8.5",
"bcryptjs": "^2.4.3",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"dotenv": "^16.3.1",
"express": "^4.18.2", "express": "^4.18.2",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"react": "^18.2.0", "react": "^18.2.0",
@ -23,6 +29,7 @@
"vite-express": "^0.10.0" "vite-express": "^0.10.0"
}, },
"devDependencies": { "devDependencies": {
"@types/bcryptjs": "^2.4.3",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/lodash-es": "^4.17.9", "@types/lodash-es": "^4.17.9",
"@types/node": "^18.17.12", "@types/node": "^18.17.12",
@ -32,6 +39,7 @@
"autoprefixer": "^10.4.15", "autoprefixer": "^10.4.15",
"nodemon": "^2.0.22", "nodemon": "^2.0.22",
"postcss": "^8.4.29", "postcss": "^8.4.29",
"prisma": "^5.2.0",
"tailwindcss": "^3.3.3", "tailwindcss": "^3.3.3",
"vite": "^4.4.9" "vite": "^4.4.9"
} }

View File

@ -7,12 +7,21 @@ 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)
'@prisma/client':
specifier: ^5.2.0
version: 5.2.0(prisma@5.2.0)
antd: antd:
specifier: ^5.8.5 specifier: ^5.8.5
version: 5.8.5(react-dom@18.2.0)(react@18.2.0) version: 5.8.5(react-dom@18.2.0)(react@18.2.0)
bcryptjs:
specifier: ^2.4.3
version: 2.4.3
clsx: clsx:
specifier: ^2.0.0 specifier: ^2.0.0
version: 2.0.0 version: 2.0.0
dotenv:
specifier: ^16.3.1
version: 16.3.1
express: express:
specifier: ^4.18.2 specifier: ^4.18.2
version: 4.18.2 version: 4.18.2
@ -42,6 +51,9 @@ dependencies:
version: 0.10.0 version: 0.10.0
devDependencies: devDependencies:
'@types/bcryptjs':
specifier: ^2.4.3
version: 2.4.3
'@types/express': '@types/express':
specifier: ^4.17.17 specifier: ^4.17.17
version: 4.17.17 version: 4.17.17
@ -69,6 +81,9 @@ devDependencies:
postcss: postcss:
specifier: ^8.4.29 specifier: ^8.4.29
version: 8.4.29 version: 8.4.29
prisma:
specifier: ^5.2.0
version: 5.2.0
tailwindcss: tailwindcss:
specifier: ^3.3.3 specifier: ^3.3.3
version: 3.3.3(ts-node@10.9.1) version: 3.3.3(ts-node@10.9.1)
@ -1519,6 +1534,28 @@ packages:
fastq: 1.15.0 fastq: 1.15.0
dev: true dev: true
/@prisma/client@5.2.0(prisma@5.2.0):
resolution: {integrity: sha512-AiTjJwR4J5Rh6Z/9ZKrBBLel3/5DzUNntMohOy7yObVnVoTNVFi2kvpLZlFuKO50d7yDspOtW6XBpiAd0BVXbQ==}
engines: {node: '>=16.13'}
requiresBuild: true
peerDependencies:
prisma: '*'
peerDependenciesMeta:
prisma:
optional: true
dependencies:
'@prisma/engines-version': 5.2.0-25.2804dc98259d2ea960602aca6b8e7fdc03c1758f
prisma: 5.2.0
dev: false
/@prisma/engines-version@5.2.0-25.2804dc98259d2ea960602aca6b8e7fdc03c1758f:
resolution: {integrity: sha512-jsnKT5JIDIE01lAeCj2ghY9IwxkedhKNvxQeoyLs6dr4ZXynetD0vTy7u6wMJt8vVPv8I5DPy/I4CFaoXAgbtg==}
dev: false
/@prisma/engines@5.2.0:
resolution: {integrity: sha512-dT7FOLUCdZmq+AunLqB1Iz+ZH/IIS1Fz2THmKZQ6aFONrQD/BQ5ecJ7g2wGS2OgyUFf4OaLam6/bxmgdOBDqig==}
requiresBuild: true
/@probe.gl/env@3.6.0: /@probe.gl/env@3.6.0:
resolution: {integrity: sha512-4tTZYUg/8BICC3Yyb9rOeoKeijKbZHRXBEKObrfPmX4sQmYB15ZOUpoVBhAyJkOYVAM8EkPci6Uw5dLCwx2BEQ==} resolution: {integrity: sha512-4tTZYUg/8BICC3Yyb9rOeoKeijKbZHRXBEKObrfPmX4sQmYB15ZOUpoVBhAyJkOYVAM8EkPci6Uw5dLCwx2BEQ==}
dependencies: dependencies:
@ -1710,6 +1747,10 @@ packages:
resolution: {integrity: sha512-q/qvE40hkg9gcfFBR5JwlWepK+eh2RB93dwUEHtNID+nx+UPsBBK2ilzYtP8QOutw89eR2F0PQ0RfypFSSdz2w==} resolution: {integrity: sha512-q/qvE40hkg9gcfFBR5JwlWepK+eh2RB93dwUEHtNID+nx+UPsBBK2ilzYtP8QOutw89eR2F0PQ0RfypFSSdz2w==}
dev: false dev: false
/@types/bcryptjs@2.4.3:
resolution: {integrity: sha512-XTnH9E/rp51aKGsiMtQCHV/owDLW2E9QAxI7ItpWWm6Gi6XO1e4o3VuEYDma0lbitj1vNOBj05Qk8l2BYoiN4A==}
dev: true
/@types/body-parser@1.19.2: /@types/body-parser@1.19.2:
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
dependencies: dependencies:
@ -2077,6 +2118,10 @@ packages:
/balanced-match@1.0.2: /balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
/bcryptjs@2.4.3:
resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==}
dev: false
/binary-extensions@2.2.0: /binary-extensions@2.2.0:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -2536,6 +2581,11 @@ packages:
resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==} resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==}
dev: false dev: false
/dotenv@16.3.1:
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
engines: {node: '>=12'}
dev: false
/dotignore@0.1.2: /dotignore@0.1.2:
resolution: {integrity: sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==} resolution: {integrity: sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==}
hasBin: true hasBin: true
@ -3774,6 +3824,14 @@ packages:
resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==}
dev: false dev: false
/prisma@5.2.0:
resolution: {integrity: sha512-FfFlpjVCkZwrqxDnP4smlNYSH1so+CbfjgdpioFzGGqlQAEm6VHAYSzV7jJgC3ebtY9dNOhDMS2+4/1DDSM7bQ==}
engines: {node: '>=16.13'}
hasBin: true
requiresBuild: true
dependencies:
'@prisma/engines': 5.2.0
/probe.gl@3.6.0: /probe.gl@3.6.0:
resolution: {integrity: sha512-19JydJWI7+DtR4feV+pu4Mn1I5TAc0xojuxVgZdXIyfmTLfUaFnk4OloWK1bKbPtkgGKLr2lnbnCXmpZEcEp9g==} resolution: {integrity: sha512-19JydJWI7+DtR4feV+pu4Mn1I5TAc0xojuxVgZdXIyfmTLfUaFnk4OloWK1bKbPtkgGKLr2lnbnCXmpZEcEp9g==}
dependencies: dependencies:

142
prisma/schema.prisma Normal file
View File

@ -0,0 +1,142 @@
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @unique @db.Uuid @default(uuid())
username String @unique @db.VarChar(255)
password String @db.VarChar(60)
role String @db.VarChar(50)
createdAt DateTime? @default(now()) @db.Timestamptz(6)
updatedAt DateTime? @updatedAt @db.Timestamptz(6)
deletedAt DateTime? @db.Timestamptz(6)
website Website[]
}
model Website {
id String @id @unique @db.Uuid @default(uuid())
name String @db.VarChar(100)
domain String? @db.VarChar(500)
shareId String? @unique @db.VarChar(50)
resetAt DateTime? @db.Timestamptz(6)
userId String? @db.Uuid
createdAt DateTime? @default(now()) @db.Timestamptz(6)
updatedAt DateTime? @updatedAt @db.Timestamptz(6)
deletedAt DateTime? @db.Timestamptz(6)
user User? @relation(fields: [userId], references: [id])
eventData EventData[]
sessionData SessionData[]
@@index([userId])
@@index([createdAt])
@@index([shareId])
}
model Session {
id String @id @unique @db.Uuid
websiteId String @db.Uuid
hostname String? @db.VarChar(100)
browser String? @db.VarChar(20)
os String? @db.VarChar(20)
device String? @db.VarChar(20)
screen String? @db.VarChar(11)
language String? @db.VarChar(35)
country String? @db.Char(2)
subdivision1 String? @db.VarChar(20)
subdivision2 String? @db.VarChar(50)
city String? @db.VarChar(50)
createdAt DateTime? @default(now()) @db.Timestamptz(6)
websiteEvent WebsiteEvent[]
sessionData SessionData[]
@@index([createdAt])
@@index([websiteId])
@@index([websiteId, createdAt])
@@index([websiteId, createdAt, hostname])
@@index([websiteId, createdAt, browser])
@@index([websiteId, createdAt, os])
@@index([websiteId, createdAt, device])
@@index([websiteId, createdAt, screen])
@@index([websiteId, createdAt, language])
@@index([websiteId, createdAt, country])
@@index([websiteId, createdAt, subdivision1])
@@index([websiteId, createdAt, city])
}
model WebsiteEvent {
id String @id() @db.Uuid
websiteId String @db.Uuid
sessionId String @db.Uuid
createdAt DateTime? @default(now()) @db.Timestamptz(6)
urlPath String @db.VarChar(500)
urlQuery String? @db.VarChar(500)
referrerPath String? @db.VarChar(500)
referrerQuery String? @db.VarChar(500)
referrerDomain String? @db.VarChar(500)
pageTitle String? @db.VarChar(500)
eventType Int @default(1) @db.Integer
eventName String? @db.VarChar(50)
eventData EventData[]
session Session @relation(fields: [sessionId], references: [id])
@@index([createdAt])
@@index([sessionId])
@@index([websiteId])
@@index([websiteId, createdAt])
@@index([websiteId, createdAt, urlPath])
@@index([websiteId, createdAt, urlQuery])
@@index([websiteId, createdAt, referrerDomain])
@@index([websiteId, createdAt, pageTitle])
@@index([websiteId, createdAt, eventName])
@@index([websiteId, sessionId, createdAt])
}
model EventData {
id String @id() @db.Uuid
websiteId String @db.Uuid
websiteEventId String @db.Uuid
eventKey String @db.VarChar(500)
stringValue String? @db.VarChar(500)
numberValue Decimal? @db.Decimal(19, 4)
dateValue DateTime? @db.Timestamptz(6)
dataType Int @db.Integer
createdAt DateTime? @default(now()) @db.Timestamptz(6)
website Website @relation(fields: [websiteId], references: [id])
websiteEvent WebsiteEvent @relation(fields: [websiteEventId], references: [id])
@@index([createdAt])
@@index([websiteId])
@@index([websiteEventId])
@@index([websiteId, createdAt])
@@index([websiteId, createdAt, eventKey])
}
model SessionData {
id String @id() @db.Uuid
websiteId String @db.Uuid
sessionId String @db.Uuid
sessionKey String @db.VarChar(500)
stringValue String? @db.VarChar(500)
numberValue Decimal? @db.Decimal(19, 4)
dateValue DateTime? @db.Timestamptz(6)
dataType Int @db.Integer
createdAt DateTime? @default(now()) @db.Timestamptz(6)
deletedAt DateTime? @default(now()) @db.Timestamptz(6)
website Website @relation(fields: [websiteId], references: [id])
session Session @relation(fields: [sessionId], references: [id])
@@index([createdAt])
@@index([websiteId])
@@index([sessionId])
}

View File

@ -1,3 +1,4 @@
import 'dotenv/config';
import express from 'express'; import express from 'express';
import ViteExpress from 'vite-express'; import ViteExpress from 'vite-express';

View File

@ -0,0 +1,3 @@
import { PrismaClient } from '@prisma/client';
export const prisma = new PrismaClient();

46
src/server/model/user.ts Normal file
View File

@ -0,0 +1,46 @@
import { prisma } from './_client';
import bcryptjs from 'bcryptjs';
async function hashPassword(password: string) {
return await bcryptjs.hash(password, 10);
}
/**
* Create User
*/
export async function createAdminUser(username: string, password: string) {
const count = await prisma.user.count();
if (count > 0) {
throw new Error('Create Admin User Just Only allow in non people exist');
}
await prisma.user.create({
data: {
username,
password: await hashPassword(password),
role: 'admin',
},
});
}
export async function createUser(username: string, password: string) {
await prisma.user.create({
data: {
username,
password: await hashPassword(password),
role: 'normal',
},
});
}
export async function authUser(username: string, password: string) {
const user = await prisma.user.findFirstOrThrow({
where: {
username,
password: await hashPassword(password),
},
});
return user;
}