feat: add prisma
This commit is contained in:
parent
89c0874983
commit
6761bc5d08
1
.env.example
Normal file
1
.env.example
Normal file
@ -0,0 +1 @@
|
|||||||
|
DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
|
.env
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
|
10
package.json
10
package.json
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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
142
prisma/schema.prisma
Normal 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])
|
||||||
|
}
|
@ -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';
|
||||||
|
|
||||||
|
3
src/server/model/_client.ts
Normal file
3
src/server/model/_client.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
export const prisma = new PrismaClient();
|
46
src/server/model/user.ts
Normal file
46
src/server/model/user.ts
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user