Implement backend for login users on app

This commit is contained in:
Rayan Konecny 2026-04-27 20:33:09 -03:00
parent 060bc58918
commit 63c9f720ad
24 changed files with 5222 additions and 21 deletions

321
README.md
View file

@ -1,9 +1,320 @@
## Compilar projeto no AVD Android na VM
# Toptran
- Ligue o dispositivo android
Aplicação full-stack composta por um backend REST em Node.js/Express com autenticação JWT e um aplicativo mobile em React Native/Expo.
Abra o cmd e rode o comando
---
1. ssh -R 5555:localhost:5555 dev@175.15.15.93
## Estrutura do projeto
2. adb connect localhost:5555
```
toptran/
├── backend/ → API REST (Node.js, Express, Prisma, PostgreSQL)
└── mobile/ → App mobile (React Native, Expo)
```
---
## Backend
### Tecnologias
| Pacote | Versão | Função |
|---|---|---|
| Node.js | LTS | Runtime |
| Express | 5 | Framework HTTP |
| TypeScript | 5 | Tipagem |
| Prisma | 7 | ORM |
| PostgreSQL | — | Banco de dados |
| bcryptjs | — | Hash de senha |
| jsonwebtoken | — | Access token (JWT) |
| zod | — | Validação de input |
| @prisma/adapter-pg | — | Driver nativo do Prisma 7 |
### Arquitetura em camadas
```
src/
├── server.ts → Inicialização do Express e registro das rotas
├── lib/
│ └── prisma.ts → Singleton do PrismaClient com adapter PG
├── repositories/
│ ├── users.repository.ts → Queries de usuários no Prisma
│ └── tokens.repository.ts → Queries de tokens no Prisma
├── services/
│ ├── auth.service.ts → Lógica de registro, login, logout e JWT
│ └── users.service.ts → Lógica de leitura, atualização e remoção de usuários
├── controllers/
│ ├── auth.controller.ts → Recebe requests e chama auth.service
│ └── users.controller.ts → Recebe requests e chama users.service
├── middlewares/
│ └── auth.middleware.ts → Valida o Bearer token nas rotas protegidas
└── routes/
├── auth.routes.ts → Rotas públicas de autenticação
└── users.routes.ts → Rotas protegidas de usuário
```
**Fluxo de uma requisição:**
```
Request → routes → middleware (se protegida) → controller → service → repository → Prisma → PostgreSQL
```
### Modelos do banco de dados
```prisma
model users {
id String @id
name String
email String @unique
password String
createdAt DateTime @default(now())
updatedAt DateTime
tokens tokens[]
}
model tokens {
id String @id
token String @unique
type TokenType
userId String
expiresAt DateTime
createdAt DateTime @default(now())
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
}
enum TokenType {
REFRESH
}
```
### Variáveis de ambiente
Crie o arquivo `backend/.env` com as variáveis abaixo:
```env
DB_USER=postgres
DB_PASSWORD=postgres
DB_HOST=localhost
DB_PORT=5432
DB_NAME=toptran
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/toptran"
PORT=4000
JWT_SECRET=sua_chave_secreta_aqui
```
> `JWT_SECRET` é obrigatório em produção. O access token expira em **15 minutos** e o refresh token em **7 dias**.
### Instalação e execução
```bash
cd backend
# Instalar dependências
npm install
# Gerar cliente Prisma
npx prisma generate
# Rodar migrations
npx prisma migrate deploy
# Iniciar em modo desenvolvimento
npm run dev
# Build de produção
npm run build
npm start
```
### API Reference
#### Autenticação — `/auth`
> Rotas públicas, não requerem token.
**POST `/auth/register`**
Cria uma nova conta de usuário.
```json
// Request body
{
"name": "João Silva",
"email": "joao@email.com",
"password": "minhasenha"
}
// Response 201
{
"id": "uuid",
"name": "João Silva",
"email": "joao@email.com"
}
```
---
**POST `/auth/login`**
Autentica o usuário e retorna os tokens.
```json
// Request body
{
"email": "joao@email.com",
"password": "minhasenha"
}
// Response 200
{
"accessToken": "eyJ...",
"refreshToken": "uuid"
}
```
---
**POST `/auth/logout`**
Revoga o refresh token.
```json
// Request body
{
"refreshToken": "uuid"
}
// Response 204 (sem body)
```
---
#### Usuários — `/users`
> Rotas protegidas. Enviar o header `Authorization: Bearer <accessToken>`.
**GET `/users/me`**
Retorna os dados do usuário autenticado.
```json
// Response 200
{
"id": "uuid",
"name": "João Silva",
"email": "joao@email.com",
"createdAt": "2026-04-27T00:00:00.000Z"
}
```
---
**PUT `/users/me`**
Atualiza nome, e-mail e/ou senha. Todos os campos são opcionais.
```json
// Request body
{
"name": "João Atualizado",
"email": "novo@email.com",
"password": "novasenha"
}
// Response 200
{
"id": "uuid",
"name": "João Atualizado",
"email": "novo@email.com"
}
```
---
**DELETE `/users/me`**
Remove a conta do usuário autenticado.
```
// Response 204 (sem body)
```
---
#### Respostas de erro
| Status | Situação |
|---|---|
| 400 | Input inválido (falha na validação Zod) |
| 401 | Token ausente, inválido ou expirado |
| 404 | Recurso não encontrado |
| 409 | E-mail já cadastrado |
---
### Deploy com Docker / Podman
```bash
cd backend
# Build da imagem
podman build -t top-tran-backend .
# Subir com compose
podman-compose up -d
```
O container expõe a porta `3000` e o serviço é reiniciado automaticamente em caso de falha.
---
## Mobile
### Tecnologias
| Pacote | Versão | Função |
|---|---|---|
| React Native | 0.81.5 | Framework mobile |
| Expo | 54 | Plataforma e toolchain |
| Expo Router | 6 | Navegação baseada em arquivos |
| Axios | 1 | Cliente HTTP |
| TypeScript | 5 | Tipagem |
### Telas
| Arquivo | Rota | Descrição |
|---|---|---|
| `src/app/index.tsx` | `/` | Tela de login |
| `src/app/signup.tsx` | `/signup` | Tela de cadastro |
### Execução
```bash
cd mobile
# Instalar dependências
npm install
# Iniciar o servidor Expo
npm start
# Abrir no Android
npm run android
# Abrir no iOS
npm run ios
```
### Conectar ao dispositivo Android via AVD na VM
1. Ligue o dispositivo Android (AVD)
2. No terminal local, crie o túnel SSH reverso:
```bash
ssh -R 5555:localhost:5555 dev@175.15.15.93
```
3. Conecte o ADB ao dispositivo tunelado:
```bash
adb connect localhost:5555
```

View file

@ -9,13 +9,21 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@prisma/adapter-pg": "^7.8.0",
"@prisma/client": "^7.8.0",
"@types/pg": "^8.20.0",
"bcryptjs": "^3.0.3",
"cors": "^2.8.6",
"express": "^5.2.1"
"express": "^5.2.1",
"jsonwebtoken": "^9.0.3",
"pg": "^8.20.0",
"zod": "^4.3.6"
},
"devDependencies": {
"@types/bcryptjs": "^2.4.6",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
"@types/jsonwebtoken": "^9.0.10",
"@types/node": "^25.6.0",
"prisma": "^7.8.0",
"ts-node-dev": "^2.0.0",
@ -555,6 +563,18 @@
"devOptional": true,
"license": "MIT"
},
"node_modules/@prisma/adapter-pg": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-7.8.0.tgz",
"integrity": "sha512-ygb3UkerK3v8MDpXVgCISdRNDozpxh6+JVJgiIGbSr5KBgz10LLf5ejUskPGoXlsIjxsOu6nuy1JVQr2EKGSlg==",
"license": "Apache-2.0",
"dependencies": {
"@prisma/driver-adapter-utils": "7.8.0",
"@types/pg": "^8.16.0",
"pg": "^8.16.3",
"postgres-array": "3.0.4"
}
},
"node_modules/@prisma/client": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.8.0.tgz",
@ -602,7 +622,6 @@
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.8.0.tgz",
"integrity": "sha512-p+QZReysDUqXC+mk17q9a+Y/qzh4c2KYliDK30buYUyfrGeTGSyfmc0AIrJRhZJrLHhRiJa9Au/J72h3C+szvA==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/dev": {
@ -631,6 +650,15 @@
"zeptomatch": "2.1.0"
}
},
"node_modules/@prisma/driver-adapter-utils": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.8.0.tgz",
"integrity": "sha512-/Q13o0ZT0rjc1Xk0Q9KhZYwuq2EW/vSbWUBKfgEKkaCuB/Sg6bqnjmTZqC5cD4d6y1vfFAEwBRzfzoSMIVJ55A==",
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "7.8.0"
}
},
"node_modules/@prisma/engines": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.8.0.tgz",
@ -927,6 +955,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/bcryptjs": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz",
"integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/body-parser": {
"version": "1.19.6",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
@ -990,16 +1025,44 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/jsonwebtoken": {
"version": "9.0.10",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
"integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/ms": "*",
"@types/node": "*"
}
},
"node_modules/@types/ms": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.6.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
"integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.19.0"
}
},
"node_modules/@types/pg": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz",
"integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==",
"license": "MIT",
"dependencies": {
"@types/node": "*",
"pg-protocol": "*",
"pg-types": "^2.2.0"
}
},
"node_modules/@types/qs": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz",
@ -1154,6 +1217,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/bcryptjs": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz",
"integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==",
"license": "BSD-3-Clause",
"bin": {
"bcrypt": "bin/bcrypt"
}
},
"node_modules/better-result": {
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/better-result/-/better-result-2.8.2.tgz",
@ -1222,6 +1294,12 @@
"node": ">=8"
}
},
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
"license": "BSD-3-Clause"
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@ -1572,6 +1650,15 @@
"xtend": "^4.0.0"
}
},
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -2253,6 +2340,91 @@
"devOptional": true,
"license": "MIT"
},
"node_modules/jsonwebtoken": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
"integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
"license": "MIT",
"dependencies": {
"jws": "^4.0.1",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"ms": "^2.1.1",
"semver": "^7.5.4"
},
"engines": {
"node": ">=12",
"npm": ">=6"
}
},
"node_modules/jwa": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jws": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"license": "MIT",
"dependencies": {
"jwa": "^2.0.1",
"safe-buffer": "^5.0.1"
}
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
"license": "MIT"
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
"license": "MIT"
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
"license": "MIT"
},
"node_modules/lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
"license": "MIT"
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"license": "MIT"
},
"node_modules/lodash.isstring": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
"license": "MIT"
},
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
"license": "MIT"
},
"node_modules/long": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
@ -2542,6 +2714,104 @@
"devOptional": true,
"license": "MIT"
},
"node_modules/pg": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz",
"integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==",
"license": "MIT",
"dependencies": {
"pg-connection-string": "^2.12.0",
"pg-pool": "^3.13.0",
"pg-protocol": "^1.13.0",
"pg-types": "2.2.0",
"pgpass": "1.0.5"
},
"engines": {
"node": ">= 16.0.0"
},
"optionalDependencies": {
"pg-cloudflare": "^1.3.0"
},
"peerDependencies": {
"pg-native": ">=3.0.1"
},
"peerDependenciesMeta": {
"pg-native": {
"optional": true
}
}
},
"node_modules/pg-cloudflare": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz",
"integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==",
"license": "MIT",
"optional": true
},
"node_modules/pg-connection-string": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz",
"integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==",
"license": "MIT"
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"license": "ISC",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-pool": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz",
"integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==",
"license": "MIT",
"peerDependencies": {
"pg": ">=8.0"
}
},
"node_modules/pg-protocol": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz",
"integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==",
"license": "MIT"
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"license": "MIT",
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/pg-types/node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"license": "MIT",
"dependencies": {
"split2": "^4.1.0"
}
},
"node_modules/picomatch": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
@ -2581,6 +2851,45 @@
"url": "https://github.com/sponsors/porsager"
}
},
"node_modules/postgres-array": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz",
"integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==",
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/postgres-bytea": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
"integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"license": "MIT",
"dependencies": {
"xtend": "^4.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prisma": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-7.8.0.tgz",
@ -2844,6 +3153,26 @@
"node": ">= 18"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@ -2858,6 +3187,18 @@
"license": "MIT",
"peer": true
},
"node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/send": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
@ -3044,6 +3385,15 @@
"source-map": "^0.6.0"
}
},
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"license": "ISC",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/sqlstring": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
@ -3280,7 +3630,6 @@
"version": "7.19.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
"dev": true,
"license": "MIT"
},
"node_modules/unpipe": {
@ -3349,7 +3698,6 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4"
@ -3375,6 +3723,15 @@
"grammex": "^3.1.11",
"graphmatch": "^1.1.0"
}
},
"node_modules/zod": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

View file

@ -14,16 +14,24 @@
"license": "ISC",
"type": "module",
"devDependencies": {
"@types/bcryptjs": "^2.4.6",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
"@types/jsonwebtoken": "^9.0.10",
"@types/node": "^25.6.0",
"prisma": "^7.8.0",
"ts-node-dev": "^2.0.0",
"tsx": "^4.21.0"
},
"dependencies": {
"@prisma/adapter-pg": "^7.8.0",
"@prisma/client": "^7.8.0",
"@types/pg": "^8.20.0",
"bcryptjs": "^3.0.3",
"cors": "^2.8.6",
"express": "^5.2.1"
"express": "^5.2.1",
"jsonwebtoken": "^9.0.3",
"pg": "^8.20.0",
"zod": "^4.3.6"
}
}

View file

@ -0,0 +1,43 @@
import { Request, Response } from 'express';
import * as authService from '../services/auth.service.js';
export async function register(req: Request, res: Response) {
const parsed = authService.registerSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ error: parsed.error.flatten() });
return;
}
try {
const user = await authService.register(parsed.data);
res.status(201).json(user);
} catch (err: any) {
res.status(409).json({ error: err.message });
}
}
export async function login(req: Request, res: Response) {
const parsed = authService.loginSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ error: parsed.error.flatten() });
return;
}
try {
const tokens = await authService.login(parsed.data);
res.json(tokens);
} catch (err: any) {
res.status(401).json({ error: err.message });
}
}
export async function logout(req: Request, res: Response) {
const { refreshToken } = req.body;
if (!refreshToken) {
res.status(400).json({ error: 'refreshToken is required' });
return;
}
await authService.logout(refreshToken);
res.status(204).send();
}

View file

@ -0,0 +1,36 @@
import { Response } from 'express';
import { AuthRequest } from '../middlewares/auth.middleware.js';
import * as usersService from '../services/users.service.js';
export async function getMe(req: AuthRequest, res: Response) {
try {
const user = await usersService.getUser(req.userId!);
res.json(user);
} catch (err: any) {
res.status(404).json({ error: err.message });
}
}
export async function updateMe(req: AuthRequest, res: Response) {
const parsed = usersService.updateUserSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ error: parsed.error.flatten() });
return;
}
try {
const user = await usersService.updateUser(req.userId!, parsed.data);
res.json(user);
} catch (err: any) {
res.status(400).json({ error: err.message });
}
}
export async function deleteMe(req: AuthRequest, res: Response) {
try {
await usersService.deleteUser(req.userId!);
res.status(204).send();
} catch (err: any) {
res.status(404).json({ error: err.message });
}
}

View file

@ -0,0 +1,29 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This file should be your main import to use Prisma-related types and utilities in a browser.
* Use it to get access to models, enums, and input types.
*
* This file does not contain a `PrismaClient` class, nor several other helpers that are intended as server-side only.
* See `client.ts` for the standard, server-side entry point.
*
* 🟢 You can import this file directly.
*/
import * as Prisma from './internal/prismaNamespaceBrowser.js'
export { Prisma }
export * as $Enums from './enums.js'
export * from './enums.js';
/**
* Model tokens
*
*/
export type tokens = Prisma.tokensModel
/**
* Model users
*
*/
export type users = Prisma.usersModel

View file

@ -0,0 +1,53 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.
* If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.
*
* 🟢 You can import this file directly.
*/
import * as process from 'node:process'
import * as path from 'node:path'
import { fileURLToPath } from 'node:url'
globalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url))
import * as runtime from "@prisma/client/runtime/client"
import * as $Enums from "./enums.js"
import * as $Class from "./internal/class.js"
import * as Prisma from "./internal/prismaNamespace.js"
export * as $Enums from './enums.js'
export * from "./enums.js"
/**
* ## Prisma Client
*
* Type-safe database client for TypeScript
* @example
* ```
* const prisma = new PrismaClient({
* adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
* })
* // Fetch zero or more Tokens
* const tokens = await prisma.tokens.findMany()
* ```
*
* Read more in our [docs](https://pris.ly/d/client).
*/
export const PrismaClient = $Class.getPrismaClientClass()
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
export { Prisma }
/**
* Model tokens
*
*/
export type tokens = Prisma.tokensModel
/**
* Model users
*
*/
export type users = Prisma.usersModel

View file

@ -0,0 +1,176 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This file exports various common sort, input & filter types that are not directly linked to a particular model.
*
* 🟢 You can import this file directly.
*/
import type * as runtime from "@prisma/client/runtime/client"
import * as $Enums from "./enums.js"
import type * as Prisma from "./internal/prismaNamespace.js"
export type StringFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
mode?: Prisma.QueryMode
not?: Prisma.NestedStringFilter<$PrismaModel> | string
}
export type EnumTokenTypeFilter<$PrismaModel = never> = {
equals?: $Enums.TokenType | Prisma.EnumTokenTypeFieldRefInput<$PrismaModel>
in?: $Enums.TokenType[] | Prisma.ListEnumTokenTypeFieldRefInput<$PrismaModel>
notIn?: $Enums.TokenType[] | Prisma.ListEnumTokenTypeFieldRefInput<$PrismaModel>
not?: Prisma.NestedEnumTokenTypeFilter<$PrismaModel> | $Enums.TokenType
}
export type DateTimeFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
}
export type StringWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
mode?: Prisma.QueryMode
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedStringFilter<$PrismaModel>
_max?: Prisma.NestedStringFilter<$PrismaModel>
}
export type EnumTokenTypeWithAggregatesFilter<$PrismaModel = never> = {
equals?: $Enums.TokenType | Prisma.EnumTokenTypeFieldRefInput<$PrismaModel>
in?: $Enums.TokenType[] | Prisma.ListEnumTokenTypeFieldRefInput<$PrismaModel>
notIn?: $Enums.TokenType[] | Prisma.ListEnumTokenTypeFieldRefInput<$PrismaModel>
not?: Prisma.NestedEnumTokenTypeWithAggregatesFilter<$PrismaModel> | $Enums.TokenType
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedEnumTokenTypeFilter<$PrismaModel>
_max?: Prisma.NestedEnumTokenTypeFilter<$PrismaModel>
}
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
}
export type NestedStringFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
not?: Prisma.NestedStringFilter<$PrismaModel> | string
}
export type NestedEnumTokenTypeFilter<$PrismaModel = never> = {
equals?: $Enums.TokenType | Prisma.EnumTokenTypeFieldRefInput<$PrismaModel>
in?: $Enums.TokenType[] | Prisma.ListEnumTokenTypeFieldRefInput<$PrismaModel>
notIn?: $Enums.TokenType[] | Prisma.ListEnumTokenTypeFieldRefInput<$PrismaModel>
not?: Prisma.NestedEnumTokenTypeFilter<$PrismaModel> | $Enums.TokenType
}
export type NestedDateTimeFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
}
export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedStringFilter<$PrismaModel>
_max?: Prisma.NestedStringFilter<$PrismaModel>
}
export type NestedIntFilter<$PrismaModel = never> = {
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
not?: Prisma.NestedIntFilter<$PrismaModel> | number
}
export type NestedEnumTokenTypeWithAggregatesFilter<$PrismaModel = never> = {
equals?: $Enums.TokenType | Prisma.EnumTokenTypeFieldRefInput<$PrismaModel>
in?: $Enums.TokenType[] | Prisma.ListEnumTokenTypeFieldRefInput<$PrismaModel>
notIn?: $Enums.TokenType[] | Prisma.ListEnumTokenTypeFieldRefInput<$PrismaModel>
not?: Prisma.NestedEnumTokenTypeWithAggregatesFilter<$PrismaModel> | $Enums.TokenType
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedEnumTokenTypeFilter<$PrismaModel>
_max?: Prisma.NestedEnumTokenTypeFilter<$PrismaModel>
}
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
}

View file

@ -0,0 +1,16 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This file exports all enum related types from the schema.
*
* 🟢 You can import this file directly.
*/
export const TokenType = {
REFRESH: 'REFRESH'
} as const
export type TokenType = (typeof TokenType)[keyof typeof TokenType]

View file

@ -0,0 +1,214 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* WARNING: This is an internal file that is subject to change!
*
* 🛑 Under no circumstances should you import this file directly! 🛑
*
* Please import the `PrismaClient` class from the `client.ts` file instead.
*/
import * as runtime from "@prisma/client/runtime/client"
import type * as Prisma from "./prismaNamespace.js"
const config: runtime.GetPrismaClientConfig = {
"previewFeatures": [],
"clientVersion": "7.8.0",
"engineVersion": "3c6e192761c0362d496ed980de936e2f3cebcd3a",
"activeProvider": "postgresql",
"inlineSchema": "generator client {\n provider = \"prisma-client\"\n output = \"../src/generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel tokens {\n id String @id\n token String @unique\n type TokenType\n userId String\n expiresAt DateTime\n createdAt DateTime @default(now())\n users users @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel users {\n id String @id\n name String\n email String @unique\n password String\n createdAt DateTime @default(now())\n updatedAt DateTime\n tokens tokens[]\n}\n\nenum TokenType {\n REFRESH\n}\n",
"runtimeDataModel": {
"models": {},
"enums": {},
"types": {}
},
"parameterizationSchema": {
"strings": [],
"graph": ""
}
}
config.runtimeDataModel = JSON.parse("{\"models\":{\"tokens\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"enum\",\"type\":\"TokenType\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"users\",\"kind\":\"object\",\"type\":\"users\",\"relationName\":\"tokensTousers\"}],\"dbName\":null},\"users\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"tokens\",\"kind\":\"object\",\"type\":\"tokens\",\"relationName\":\"tokensTousers\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
config.parameterizationSchema = {
strings: JSON.parse("[\"where\",\"orderBy\",\"cursor\",\"tokens\",\"_count\",\"users\",\"tokens.findUnique\",\"tokens.findUniqueOrThrow\",\"tokens.findFirst\",\"tokens.findFirstOrThrow\",\"tokens.findMany\",\"data\",\"tokens.createOne\",\"tokens.createMany\",\"tokens.createManyAndReturn\",\"tokens.updateOne\",\"tokens.updateMany\",\"tokens.updateManyAndReturn\",\"create\",\"update\",\"tokens.upsertOne\",\"tokens.deleteOne\",\"tokens.deleteMany\",\"having\",\"_min\",\"_max\",\"tokens.groupBy\",\"tokens.aggregate\",\"users.findUnique\",\"users.findUniqueOrThrow\",\"users.findFirst\",\"users.findFirstOrThrow\",\"users.findMany\",\"users.createOne\",\"users.createMany\",\"users.createManyAndReturn\",\"users.updateOne\",\"users.updateMany\",\"users.updateManyAndReturn\",\"users.upsertOne\",\"users.deleteOne\",\"users.deleteMany\",\"users.groupBy\",\"users.aggregate\",\"AND\",\"OR\",\"NOT\",\"id\",\"name\",\"email\",\"password\",\"createdAt\",\"updatedAt\",\"equals\",\"in\",\"notIn\",\"lt\",\"lte\",\"gt\",\"gte\",\"not\",\"contains\",\"startsWith\",\"endsWith\",\"every\",\"some\",\"none\",\"token\",\"TokenType\",\"type\",\"userId\",\"expiresAt\",\"is\",\"isNot\",\"connectOrCreate\",\"upsert\",\"createMany\",\"set\",\"disconnect\",\"delete\",\"connect\",\"updateMany\",\"deleteMany\"]"),
graph: "aBIgCgUAAEcAICwAAEUAMC0AAAMAEC4AAEUAMC8BAAAAATNAAD8AIUMBAAAAAUUAAEZFIkYBAD4AIUdAAD8AIQEAAAABACAKBQAARwAgLAAARQAwLQAAAwAQLgAARQAwLwEAPgAhM0AAPwAhQwEAPgAhRQAARkUiRgEAPgAhR0AAPwAhAQUAAGIAIAMAAAADACABAAAEADACAAABACABAAAAAwAgAQAAAAEAIAMAAAADACABAAAEADACAAABACADAAAAAwAgAQAABAAwAgAAAQAgAwAAAAMAIAEAAAQAMAIAAAEAIAcFAABhACAvAQAAAAEzQAAAAAFDAQAAAAFFAAAARQJGAQAAAAFHQAAAAAEBCwAACwAgBi8BAAAAATNAAAAAAUMBAAAAAUUAAABFAkYBAAAAAUdAAAAAAQELAAANADABCwAADQAwBwUAAGAAIC8BAEsAITNAAEwAIUMBAEsAIUUAAFhFIkYBAEsAIUdAAEwAIQIAAAABACALAAAQACAGLwEASwAhM0AATAAhQwEASwAhRQAAWEUiRgEASwAhR0AATAAhAgAAAAMAIAsAABIAIAIAAAADACALAAASACADAAAAAQAgEgAACwAgEwAAEAAgAQAAAAEAIAEAAAADACADBAAAXQAgGAAAXwAgGQAAXgAgCSwAAEEAMC0AABkAEC4AAEEAMC8BADYAITNAADcAIUMBADYAIUUAAEJFIkYBADYAIUdAADcAIQMAAAADACABAAAYADAXAAAZACADAAAAAwAgAQAABAAwAgAAAQAgCgMAAEAAICwAAD0AMC0AAB8AEC4AAD0AMC8BAAAAATABAD4AITEBAAAAATIBAD4AITNAAD8AITRAAD8AIQEAAAAcACABAAAAHAAgCgMAAEAAICwAAD0AMC0AAB8AEC4AAD0AMC8BAD4AITABAD4AITEBAD4AITIBAD4AITNAAD8AITRAAD8AIQEDAABcACADAAAAHwAgAQAAIAAwAgAAHAAgAwAAAB8AIAEAACAAMAIAABwAIAMAAAAfACABAAAgADACAAAcACAHAwAAWwAgLwEAAAABMAEAAAABMQEAAAABMgEAAAABM0AAAAABNEAAAAABAQsAACQAIAYvAQAAAAEwAQAAAAExAQAAAAEyAQAAAAEzQAAAAAE0QAAAAAEBCwAAJgAwAQsAACYAMAcDAABNACAvAQBLACEwAQBLACExAQBLACEyAQBLACEzQABMACE0QABMACECAAAAHAAgCwAAKQAgBi8BAEsAITABAEsAITEBAEsAITIBAEsAITNAAEwAITRAAEwAIQIAAAAfACALAAArACACAAAAHwAgCwAAKwAgAwAAABwAIBIAACQAIBMAACkAIAEAAAAcACABAAAAHwAgAwQAAEgAIBgAAEoAIBkAAEkAIAksAAA1ADAtAAAyABAuAAA1ADAvAQA2ACEwAQA2ACExAQA2ACEyAQA2ACEzQAA3ACE0QAA3ACEDAAAAHwAgAQAAMQAwFwAAMgAgAwAAAB8AIAEAACAAMAIAABwAIAksAAA1ADAtAAAyABAuAAA1ADAvAQA2ACEwAQA2ACExAQA2ACEyAQA2ACEzQAA3ACE0QAA3ACEOBAAAOQAgGAAAPAAgGQAAPAAgNQEAAAABNgEAAAAENwEAAAAEOAEAAAABOQEAAAABOgEAAAABOwEAAAABPAEAOwAhPQEAAAABPgEAAAABPwEAAAABCwQAADkAIBgAADoAIBkAADoAIDVAAAAAATZAAAAABDdAAAAABDhAAAAAATlAAAAAATpAAAAAATtAAAAAATxAADgAIQsEAAA5ACAYAAA6ACAZAAA6ACA1QAAAAAE2QAAAAAQ3QAAAAAQ4QAAAAAE5QAAAAAE6QAAAAAE7QAAAAAE8QAA4ACEINQIAAAABNgIAAAAENwIAAAAEOAIAAAABOQIAAAABOgIAAAABOwIAAAABPAIAOQAhCDVAAAAAATZAAAAABDdAAAAABDhAAAAAATlAAAAAATpAAAAAATtAAAAAATxAADoAIQ4EAAA5ACAYAAA8ACAZAAA8ACA1AQAAAAE2AQAAAAQ3AQAAAAQ4AQAAAAE5AQAAAAE6AQAAAAE7AQAAAAE8AQA7ACE9AQAAAAE-AQAAAAE_AQAAAAELNQEAAAABNgEAAAAENwEAAAAEOAEAAAABOQEAAAABOgEAAAABOwEAAAABPAEAPAAhPQEAAAABPgEAAAABPwEAAAABCgMAAEAAICwAAD0AMC0AAB8AEC4AAD0AMC8BAD4AITABAD4AITEBAD4AITIBAD4AITNAAD8AITRAAD8AIQs1AQAAAAE2AQAAAAQ3AQAAAAQ4AQAAAAE5AQAAAAE6AQAAAAE7AQAAAAE8AQA8ACE9AQAAAAE-AQAAAAE_AQAAAAEINUAAAAABNkAAAAAEN0AAAAAEOEAAAAABOUAAAAABOkAAAAABO0AAAAABPEAAOgAhA0AAAAMAIEEAAAMAIEIAAAMAIAksAABBADAtAAAZABAuAABBADAvAQA2ACEzQAA3ACFDAQA2ACFFAABCRSJGAQA2ACFHQAA3ACEHBAAAOQAgGAAARAAgGQAARAAgNQAAAEUCNgAAAEUINwAAAEUIPAAAQ0UiBwQAADkAIBgAAEQAIBkAAEQAIDUAAABFAjYAAABFCDcAAABFCDwAAENFIgQ1AAAARQI2AAAARQg3AAAARQg8AABERSIKBQAARwAgLAAARQAwLQAAAwAQLgAARQAwLwEAPgAhM0AAPwAhQwEAPgAhRQAARkUiRgEAPgAhR0AAPwAhBDUAAABFAjYAAABFCDcAAABFCDwAAERFIgwDAABAACAsAAA9ADAtAAAfABAuAAA9ADAvAQA-ACEwAQA-ACExAQA-ACEyAQA-ACEzQAA_ACE0QAA_ACFIAAAfACBJAAAfACAAAAABTQEAAAABAU1AAAAAAQsSAABOADATAABTADBKAABPADBLAABQADBMAABRACBNAABSADBOAABSADBPAABSADBQAABSADBRAABUADBSAABVADAFLwEAAAABM0AAAAABQwEAAAABRQAAAEUCR0AAAAABAgAAAAEAIBIAAFoAIAMAAAABACASAABaACATAABZACABCwAAaAAwCgUAAEcAICwAAEUAMC0AAAMAEC4AAEUAMC8BAAAAATNAAD8AIUMBAAAAAUUAAEZFIkYBAD4AIUdAAD8AIQIAAAABACALAABZACACAAAAVgAgCwAAVwAgCSwAAFUAMC0AAFYAEC4AAFUAMC8BAD4AITNAAD8AIUMBAD4AIUUAAEZFIkYBAD4AIUdAAD8AIQksAABVADAtAABWABAuAABVADAvAQA-ACEzQAA_ACFDAQA-ACFFAABGRSJGAQA-ACFHQAA_ACEFLwEASwAhM0AATAAhQwEASwAhRQAAWEUiR0AATAAhAU0AAABFAgUvAQBLACEzQABMACFDAQBLACFFAABYRSJHQABMACEFLwEAAAABM0AAAAABQwEAAAABRQAAAEUCR0AAAAABBBIAAE4AMEoAAE8AMEwAAFEAIFAAAFIAMAAAAAAFEgAAYwAgEwAAZgAgSgAAZAAgSwAAZQAgUAAAHAAgAxIAAGMAIEoAAGQAIFAAABwAIAEDAABcACAGLwEAAAABMAEAAAABMQEAAAABMgEAAAABM0AAAAABNEAAAAABAgAAABwAIBIAAGMAIAMAAAAfACASAABjACATAABnACAIAAAAHwAgCwAAZwAgLwEASwAhMAEASwAhMQEASwAhMgEASwAhM0AATAAhNEAATAAhBi8BAEsAITABAEsAITEBAEsAITIBAEsAITNAAEwAITRAAEwAIQUvAQAAAAEzQAAAAAFDAQAAAAFFAAAARQJHQAAAAAEBBQACAgMFAQQAAwEDBgAAAQUAAgEFAAIDBAAIGAAJGQAKAAAAAwQACBgACRkACgAAAwQADxgAEBkAEQAAAAMEAA8YABAZABEGAgEHBwEICAEJCQEKCgEMDAENDgQODwUPEQEQEwQRFAYUFQEVFgEWFwQaGgcbGwscHQIdHgIeIQIfIgIgIwIhJQIiJwQjKAwkKgIlLAQmLQ0nLgIoLwIpMAQqMw4rNBI"
}
async function decodeBase64AsWasm(wasmBase64: string): Promise<WebAssembly.Module> {
const { Buffer } = await import('node:buffer')
const wasmArray = Buffer.from(wasmBase64, 'base64')
return new WebAssembly.Module(wasmArray)
}
config.compilerWasm = {
getRuntime: async () => await import("@prisma/client/runtime/query_compiler_fast_bg.postgresql.mjs"),
getQueryCompilerWasmModule: async () => {
const { wasm } = await import("@prisma/client/runtime/query_compiler_fast_bg.postgresql.wasm-base64.mjs")
return await decodeBase64AsWasm(wasm)
},
importName: "./query_compiler_fast_bg.js"
}
export type LogOptions<ClientOptions extends Prisma.PrismaClientOptions> =
'log' extends keyof ClientOptions ? ClientOptions['log'] extends Array<Prisma.LogLevel | Prisma.LogDefinition> ? Prisma.GetEvents<ClientOptions['log']> : never : never
export interface PrismaClientConstructor {
/**
* ## Prisma Client
*
* Type-safe database client for TypeScript
* @example
* ```
* const prisma = new PrismaClient({
* adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
* })
* // Fetch zero or more Tokens
* const tokens = await prisma.tokens.findMany()
* ```
*
* Read more in our [docs](https://pris.ly/d/client).
*/
new <
Options extends Prisma.PrismaClientOptions = Prisma.PrismaClientOptions,
LogOpts extends LogOptions<Options> = LogOptions<Options>,
OmitOpts extends Prisma.PrismaClientOptions['omit'] = Options extends { omit: infer U } ? U : Prisma.PrismaClientOptions['omit'],
ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs
>(options: Prisma.Subset<Options, Prisma.PrismaClientOptions> ): PrismaClient<LogOpts, OmitOpts, ExtArgs>
}
/**
* ## Prisma Client
*
* Type-safe database client for TypeScript
* @example
* ```
* const prisma = new PrismaClient({
* adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
* })
* // Fetch zero or more Tokens
* const tokens = await prisma.tokens.findMany()
* ```
*
* Read more in our [docs](https://pris.ly/d/client).
*/
export interface PrismaClient<
in LogOpts extends Prisma.LogLevel = never,
in out OmitOpts extends Prisma.PrismaClientOptions['omit'] = undefined,
in out ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs
> {
[K: symbol]: { types: Prisma.TypeMap<ExtArgs>['other'] }
$on<V extends LogOpts>(eventType: V, callback: (event: V extends 'query' ? Prisma.QueryEvent : Prisma.LogEvent) => void): PrismaClient;
/**
* Connect with the database
*/
$connect(): runtime.Types.Utils.JsPromise<void>;
/**
* Disconnect from the database
*/
$disconnect(): runtime.Types.Utils.JsPromise<void>;
/**
* Executes a prepared raw query and returns the number of affected rows.
* @example
* ```
* const result = await prisma.$executeRaw`UPDATE User SET cool = ${true} WHERE email = ${'user@email.com'};`
* ```
*
* Read more in our [docs](https://pris.ly/d/raw-queries).
*/
$executeRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<number>;
/**
* Executes a raw query and returns the number of affected rows.
* Susceptible to SQL injections, see documentation.
* @example
* ```
* const result = await prisma.$executeRawUnsafe('UPDATE User SET cool = $1 WHERE email = $2 ;', true, 'user@email.com')
* ```
*
* Read more in our [docs](https://pris.ly/d/raw-queries).
*/
$executeRawUnsafe<T = unknown>(query: string, ...values: any[]): Prisma.PrismaPromise<number>;
/**
* Performs a prepared raw query and returns the `SELECT` data.
* @example
* ```
* const result = await prisma.$queryRaw`SELECT * FROM User WHERE id = ${1} OR email = ${'user@email.com'};`
* ```
*
* Read more in our [docs](https://pris.ly/d/raw-queries).
*/
$queryRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<T>;
/**
* Performs a raw query and returns the `SELECT` data.
* Susceptible to SQL injections, see documentation.
* @example
* ```
* const result = await prisma.$queryRawUnsafe('SELECT * FROM User WHERE id = $1 OR email = $2;', 1, 'user@email.com')
* ```
*
* Read more in our [docs](https://pris.ly/d/raw-queries).
*/
$queryRawUnsafe<T = unknown>(query: string, ...values: any[]): Prisma.PrismaPromise<T>;
/**
* Allows the running of a sequence of read/write operations that are guaranteed to either succeed or fail as a whole.
* @example
* ```
* const [george, bob, alice] = await prisma.$transaction([
* prisma.user.create({ data: { name: 'George' } }),
* prisma.user.create({ data: { name: 'Bob' } }),
* prisma.user.create({ data: { name: 'Alice' } }),
* ])
* ```
*
* Read more in our [docs](https://www.prisma.io/docs/orm/prisma-client/queries/transactions).
*/
$transaction<P extends Prisma.PrismaPromise<any>[]>(arg: [...P], options?: { maxWait?: number, timeout?: number, isolationLevel?: Prisma.TransactionIsolationLevel }): runtime.Types.Utils.JsPromise<runtime.Types.Utils.UnwrapTuple<P>>
$transaction<R>(fn: (prisma: Omit<PrismaClient, runtime.ITXClientDenyList>) => runtime.Types.Utils.JsPromise<R>, options?: { maxWait?: number, timeout?: number, isolationLevel?: Prisma.TransactionIsolationLevel }): runtime.Types.Utils.JsPromise<R>
$extends: runtime.Types.Extensions.ExtendsHook<"extends", Prisma.TypeMapCb<OmitOpts>, ExtArgs, runtime.Types.Utils.Call<Prisma.TypeMapCb<OmitOpts>, {
extArgs: ExtArgs
}>>
/**
* `prisma.tokens`: Exposes CRUD operations for the **tokens** model.
* Example usage:
* ```ts
* // Fetch zero or more Tokens
* const tokens = await prisma.tokens.findMany()
* ```
*/
get tokens(): Prisma.tokensDelegate<ExtArgs, { omit: OmitOpts }>;
/**
* `prisma.users`: Exposes CRUD operations for the **users** model.
* Example usage:
* ```ts
* // Fetch zero or more Users
* const users = await prisma.users.findMany()
* ```
*/
get users(): Prisma.usersDelegate<ExtArgs, { omit: OmitOpts }>;
}
export function getPrismaClientClass(): PrismaClientConstructor {
return runtime.getPrismaClient(config) as unknown as PrismaClientConstructor
}

View file

@ -0,0 +1,870 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* WARNING: This is an internal file that is subject to change!
*
* 🛑 Under no circumstances should you import this file directly! 🛑
*
* All exports from this file are wrapped under a `Prisma` namespace object in the client.ts file.
* While this enables partial backward compatibility, it is not part of the stable public API.
*
* If you are looking for your Models, Enums, and Input Types, please import them from the respective
* model files in the `model` directory!
*/
import * as runtime from "@prisma/client/runtime/client"
import type * as Prisma from "../models.js"
import { type PrismaClient } from "./class.js"
export type * from '../models.js'
export type DMMF = typeof runtime.DMMF
export type PrismaPromise<T> = runtime.Types.Public.PrismaPromise<T>
/**
* Prisma Errors
*/
export const PrismaClientKnownRequestError = runtime.PrismaClientKnownRequestError
export type PrismaClientKnownRequestError = runtime.PrismaClientKnownRequestError
export const PrismaClientUnknownRequestError = runtime.PrismaClientUnknownRequestError
export type PrismaClientUnknownRequestError = runtime.PrismaClientUnknownRequestError
export const PrismaClientRustPanicError = runtime.PrismaClientRustPanicError
export type PrismaClientRustPanicError = runtime.PrismaClientRustPanicError
export const PrismaClientInitializationError = runtime.PrismaClientInitializationError
export type PrismaClientInitializationError = runtime.PrismaClientInitializationError
export const PrismaClientValidationError = runtime.PrismaClientValidationError
export type PrismaClientValidationError = runtime.PrismaClientValidationError
/**
* Re-export of sql-template-tag
*/
export const sql = runtime.sqltag
export const empty = runtime.empty
export const join = runtime.join
export const raw = runtime.raw
export const Sql = runtime.Sql
export type Sql = runtime.Sql
/**
* Decimal.js
*/
export const Decimal = runtime.Decimal
export type Decimal = runtime.Decimal
export type DecimalJsLike = runtime.DecimalJsLike
/**
* Extensions
*/
export type Extension = runtime.Types.Extensions.UserArgs
export const getExtensionContext = runtime.Extensions.getExtensionContext
export type Args<T, F extends runtime.Operation> = runtime.Types.Public.Args<T, F>
export type Payload<T, F extends runtime.Operation = never> = runtime.Types.Public.Payload<T, F>
export type Result<T, A, F extends runtime.Operation> = runtime.Types.Public.Result<T, A, F>
export type Exact<A, W> = runtime.Types.Public.Exact<A, W>
export type PrismaVersion = {
client: string
engine: string
}
/**
* Prisma Client JS version: 7.8.0
* Query Engine version: 3c6e192761c0362d496ed980de936e2f3cebcd3a
*/
export const prismaVersion: PrismaVersion = {
client: "7.8.0",
engine: "3c6e192761c0362d496ed980de936e2f3cebcd3a"
}
/**
* Utility Types
*/
export type Bytes = runtime.Bytes
export type JsonObject = runtime.JsonObject
export type JsonArray = runtime.JsonArray
export type JsonValue = runtime.JsonValue
export type InputJsonObject = runtime.InputJsonObject
export type InputJsonArray = runtime.InputJsonArray
export type InputJsonValue = runtime.InputJsonValue
export const NullTypes = {
DbNull: runtime.NullTypes.DbNull as (new (secret: never) => typeof runtime.DbNull),
JsonNull: runtime.NullTypes.JsonNull as (new (secret: never) => typeof runtime.JsonNull),
AnyNull: runtime.NullTypes.AnyNull as (new (secret: never) => typeof runtime.AnyNull),
}
/**
* Helper for filtering JSON entries that have `null` on the database (empty on the db)
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const DbNull = runtime.DbNull
/**
* Helper for filtering JSON entries that have JSON `null` values (not empty on the db)
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const JsonNull = runtime.JsonNull
/**
* Helper for filtering JSON entries that are `Prisma.DbNull` or `Prisma.JsonNull`
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const AnyNull = runtime.AnyNull
type SelectAndInclude = {
select: any
include: any
}
type SelectAndOmit = {
select: any
omit: any
}
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Prisma__Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
export type Enumerable<T> = T | Array<T>;
/**
* Subset
* @desc From `T` pick properties that exist in `U`. Simple version of Intersection
*/
export type Subset<T, U> = {
[key in keyof T]: key extends keyof U ? T[key] : never;
};
/**
* SelectSubset
* @desc From `T` pick properties that exist in `U`. Simple version of Intersection.
* Additionally, it validates, if both select and include are present. If the case, it errors.
*/
export type SelectSubset<T, U> = {
[key in keyof T]: key extends keyof U ? T[key] : never
} &
(T extends SelectAndInclude
? 'Please either choose `select` or `include`.'
: T extends SelectAndOmit
? 'Please either choose `select` or `omit`.'
: {})
/**
* Subset + Intersection
* @desc From `T` pick properties that exist in `U` and intersect `K`
*/
export type SubsetIntersection<T, U, K> = {
[key in keyof T]: key extends keyof U ? T[key] : never
} &
K
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
/**
* XOR is needed to have a real mutually exclusive union type
* https://stackoverflow.com/questions/42123407/does-typescript-support-mutually-exclusive-types
*/
export type XOR<T, U> =
T extends object ?
U extends object ?
(Without<T, U> & U) | (Without<U, T> & T)
: U : T
/**
* Is T a Record?
*/
type IsObject<T extends any> = T extends Array<any>
? False
: T extends Date
? False
: T extends Uint8Array
? False
: T extends BigInt
? False
: T extends object
? True
: False
/**
* If it's T[], return T
*/
export type UnEnumerate<T extends unknown> = T extends Array<infer U> ? U : T
/**
* From ts-toolbelt
*/
type __Either<O extends object, K extends Key> = Omit<O, K> &
{
// Merge all but K
[P in K]: Prisma__Pick<O, P & keyof O> // With K possibilities
}[K]
type EitherStrict<O extends object, K extends Key> = Strict<__Either<O, K>>
type EitherLoose<O extends object, K extends Key> = ComputeRaw<__Either<O, K>>
type _Either<
O extends object,
K extends Key,
strict extends Boolean
> = {
1: EitherStrict<O, K>
0: EitherLoose<O, K>
}[strict]
export type Either<
O extends object,
K extends Key,
strict extends Boolean = 1
> = O extends unknown ? _Either<O, K, strict> : never
export type Union = any
export type PatchUndefined<O extends object, O1 extends object> = {
[K in keyof O]: O[K] extends undefined ? At<O1, K> : O[K]
} & {}
/** Helper Types for "Merge" **/
export type IntersectOf<U extends Union> = (
U extends unknown ? (k: U) => void : never
) extends (k: infer I) => void
? I
: never
export type Overwrite<O extends object, O1 extends object> = {
[K in keyof O]: K extends keyof O1 ? O1[K] : O[K];
} & {};
type _Merge<U extends object> = IntersectOf<Overwrite<U, {
[K in keyof U]-?: At<U, K>;
}>>;
type Key = string | number | symbol;
type AtStrict<O extends object, K extends Key> = O[K & keyof O];
type AtLoose<O extends object, K extends Key> = O extends unknown ? AtStrict<O, K> : never;
export type At<O extends object, K extends Key, strict extends Boolean = 1> = {
1: AtStrict<O, K>;
0: AtLoose<O, K>;
}[strict];
export type ComputeRaw<A extends any> = A extends Function ? A : {
[K in keyof A]: A[K];
} & {};
export type OptionalFlat<O> = {
[K in keyof O]?: O[K];
} & {};
type _Record<K extends keyof any, T> = {
[P in K]: T;
};
// cause typescript not to expand types and preserve names
type NoExpand<T> = T extends unknown ? T : never;
// this type assumes the passed object is entirely optional
export type AtLeast<O extends object, K extends string> = NoExpand<
O extends unknown
? | (K extends keyof O ? { [P in K]: O[P] } & O : O)
| {[P in keyof O as P extends K ? P : never]-?: O[P]} & O
: never>;
type _Strict<U, _U = U> = U extends unknown ? U & OptionalFlat<_Record<Exclude<Keys<_U>, keyof U>, never>> : never;
export type Strict<U extends object> = ComputeRaw<_Strict<U>>;
/** End Helper Types for "Merge" **/
export type Merge<U extends object> = ComputeRaw<_Merge<Strict<U>>>;
export type Boolean = True | False
export type True = 1
export type False = 0
export type Not<B extends Boolean> = {
0: 1
1: 0
}[B]
export type Extends<A1 extends any, A2 extends any> = [A1] extends [never]
? 0 // anything `never` is false
: A1 extends A2
? 1
: 0
export type Has<U extends Union, U1 extends Union> = Not<
Extends<Exclude<U1, U>, U1>
>
export type Or<B1 extends Boolean, B2 extends Boolean> = {
0: {
0: 0
1: 1
}
1: {
0: 1
1: 1
}
}[B1][B2]
export type Keys<U extends Union> = U extends unknown ? keyof U : never
export type GetScalarType<T, O> = O extends object ? {
[P in keyof T]: P extends keyof O
? O[P]
: never
} : never
type FieldPaths<
T,
U = Omit<T, '_avg' | '_sum' | '_count' | '_min' | '_max'>
> = IsObject<T> extends True ? U : T
export type GetHavingFields<T> = {
[K in keyof T]: Or<
Or<Extends<'OR', K>, Extends<'AND', K>>,
Extends<'NOT', K>
> extends True
? // infer is only needed to not hit TS limit
// based on the brilliant idea of Pierre-Antoine Mills
// https://github.com/microsoft/TypeScript/issues/30188#issuecomment-478938437
T[K] extends infer TK
? GetHavingFields<UnEnumerate<TK> extends object ? Merge<UnEnumerate<TK>> : never>
: never
: {} extends FieldPaths<T[K]>
? never
: K
}[keyof T]
/**
* Convert tuple to union
*/
type _TupleToUnion<T> = T extends (infer E)[] ? E : never
type TupleToUnion<K extends readonly any[]> = _TupleToUnion<K>
export type MaybeTupleToUnion<T> = T extends any[] ? TupleToUnion<T> : T
/**
* Like `Pick`, but additionally can also accept an array of keys
*/
export type PickEnumerable<T, K extends Enumerable<keyof T> | keyof T> = Prisma__Pick<T, MaybeTupleToUnion<K>>
/**
* Exclude all keys with underscores
*/
export type ExcludeUnderscoreKeys<T extends string> = T extends `_${string}` ? never : T
export type FieldRef<Model, FieldType> = runtime.FieldRef<Model, FieldType>
type FieldRefInputType<Model, FieldType> = Model extends never ? never : FieldRef<Model, FieldType>
export const ModelName = {
tokens: 'tokens',
users: 'users'
} as const
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
export interface TypeMapCb<GlobalOmitOptions = {}> extends runtime.Types.Utils.Fn<{extArgs: runtime.Types.Extensions.InternalArgs }, runtime.Types.Utils.Record<string, any>> {
returns: TypeMap<this['params']['extArgs'], GlobalOmitOptions>
}
export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> = {
globalOmitOptions: {
omit: GlobalOmitOptions
}
meta: {
modelProps: "tokens" | "users"
txIsolationLevel: TransactionIsolationLevel
}
model: {
tokens: {
payload: Prisma.$tokensPayload<ExtArgs>
fields: Prisma.tokensFieldRefs
operations: {
findUnique: {
args: Prisma.tokensFindUniqueArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$tokensPayload> | null
}
findUniqueOrThrow: {
args: Prisma.tokensFindUniqueOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$tokensPayload>
}
findFirst: {
args: Prisma.tokensFindFirstArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$tokensPayload> | null
}
findFirstOrThrow: {
args: Prisma.tokensFindFirstOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$tokensPayload>
}
findMany: {
args: Prisma.tokensFindManyArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$tokensPayload>[]
}
create: {
args: Prisma.tokensCreateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$tokensPayload>
}
createMany: {
args: Prisma.tokensCreateManyArgs<ExtArgs>
result: BatchPayload
}
createManyAndReturn: {
args: Prisma.tokensCreateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$tokensPayload>[]
}
delete: {
args: Prisma.tokensDeleteArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$tokensPayload>
}
update: {
args: Prisma.tokensUpdateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$tokensPayload>
}
deleteMany: {
args: Prisma.tokensDeleteManyArgs<ExtArgs>
result: BatchPayload
}
updateMany: {
args: Prisma.tokensUpdateManyArgs<ExtArgs>
result: BatchPayload
}
updateManyAndReturn: {
args: Prisma.tokensUpdateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$tokensPayload>[]
}
upsert: {
args: Prisma.tokensUpsertArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$tokensPayload>
}
aggregate: {
args: Prisma.TokensAggregateArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AggregateTokens>
}
groupBy: {
args: Prisma.tokensGroupByArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.TokensGroupByOutputType>[]
}
count: {
args: Prisma.tokensCountArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.TokensCountAggregateOutputType> | number
}
}
}
users: {
payload: Prisma.$usersPayload<ExtArgs>
fields: Prisma.usersFieldRefs
operations: {
findUnique: {
args: Prisma.usersFindUniqueArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$usersPayload> | null
}
findUniqueOrThrow: {
args: Prisma.usersFindUniqueOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$usersPayload>
}
findFirst: {
args: Prisma.usersFindFirstArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$usersPayload> | null
}
findFirstOrThrow: {
args: Prisma.usersFindFirstOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$usersPayload>
}
findMany: {
args: Prisma.usersFindManyArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$usersPayload>[]
}
create: {
args: Prisma.usersCreateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$usersPayload>
}
createMany: {
args: Prisma.usersCreateManyArgs<ExtArgs>
result: BatchPayload
}
createManyAndReturn: {
args: Prisma.usersCreateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$usersPayload>[]
}
delete: {
args: Prisma.usersDeleteArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$usersPayload>
}
update: {
args: Prisma.usersUpdateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$usersPayload>
}
deleteMany: {
args: Prisma.usersDeleteManyArgs<ExtArgs>
result: BatchPayload
}
updateMany: {
args: Prisma.usersUpdateManyArgs<ExtArgs>
result: BatchPayload
}
updateManyAndReturn: {
args: Prisma.usersUpdateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$usersPayload>[]
}
upsert: {
args: Prisma.usersUpsertArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$usersPayload>
}
aggregate: {
args: Prisma.UsersAggregateArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AggregateUsers>
}
groupBy: {
args: Prisma.usersGroupByArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.UsersGroupByOutputType>[]
}
count: {
args: Prisma.usersCountArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.UsersCountAggregateOutputType> | number
}
}
}
}
} & {
other: {
payload: any
operations: {
$executeRaw: {
args: [query: TemplateStringsArray | Sql, ...values: any[]],
result: any
}
$executeRawUnsafe: {
args: [query: string, ...values: any[]],
result: any
}
$queryRaw: {
args: [query: TemplateStringsArray | Sql, ...values: any[]],
result: any
}
$queryRawUnsafe: {
args: [query: string, ...values: any[]],
result: any
}
}
}
}
/**
* Enums
*/
export const TransactionIsolationLevel = runtime.makeStrictEnum({
ReadUncommitted: 'ReadUncommitted',
ReadCommitted: 'ReadCommitted',
RepeatableRead: 'RepeatableRead',
Serializable: 'Serializable'
} as const)
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
export const TokensScalarFieldEnum = {
id: 'id',
token: 'token',
type: 'type',
userId: 'userId',
expiresAt: 'expiresAt',
createdAt: 'createdAt'
} as const
export type TokensScalarFieldEnum = (typeof TokensScalarFieldEnum)[keyof typeof TokensScalarFieldEnum]
export const UsersScalarFieldEnum = {
id: 'id',
name: 'name',
email: 'email',
password: 'password',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type UsersScalarFieldEnum = (typeof UsersScalarFieldEnum)[keyof typeof UsersScalarFieldEnum]
export const SortOrder = {
asc: 'asc',
desc: 'desc'
} as const
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
export const QueryMode = {
default: 'default',
insensitive: 'insensitive'
} as const
export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]
/**
* Field references
*/
/**
* Reference to a field of type 'String'
*/
export type StringFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'String'>
/**
* Reference to a field of type 'String[]'
*/
export type ListStringFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'String[]'>
/**
* Reference to a field of type 'TokenType'
*/
export type EnumTokenTypeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'TokenType'>
/**
* Reference to a field of type 'TokenType[]'
*/
export type ListEnumTokenTypeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'TokenType[]'>
/**
* Reference to a field of type 'DateTime'
*/
export type DateTimeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'DateTime'>
/**
* Reference to a field of type 'DateTime[]'
*/
export type ListDateTimeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'DateTime[]'>
/**
* Reference to a field of type 'Int'
*/
export type IntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Int'>
/**
* Reference to a field of type 'Int[]'
*/
export type ListIntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Int[]'>
/**
* Batch Payload for updateMany & deleteMany & createMany
*/
export type BatchPayload = {
count: number
}
export const defineExtension = runtime.Extensions.defineExtension as unknown as runtime.Types.Extensions.ExtendsHook<"define", TypeMapCb, runtime.Types.Extensions.DefaultArgs>
export type DefaultPrismaClient = PrismaClient
export type ErrorFormat = 'pretty' | 'colorless' | 'minimal'
export type PrismaClientOptions = ({
/**
* Instance of a Driver Adapter, e.g., like one provided by `@prisma/adapter-pg`.
*/
adapter: runtime.SqlDriverAdapterFactory
accelerateUrl?: never
} | {
/**
* Prisma Accelerate URL allowing the client to connect through Accelerate instead of a direct database.
*/
accelerateUrl: string
adapter?: never
}) & {
/**
* @default "colorless"
*/
errorFormat?: ErrorFormat
/**
* @example
* ```
* // Shorthand for `emit: 'stdout'`
* log: ['query', 'info', 'warn', 'error']
*
* // Emit as events only
* log: [
* { emit: 'event', level: 'query' },
* { emit: 'event', level: 'info' },
* { emit: 'event', level: 'warn' }
* { emit: 'event', level: 'error' }
* ]
*
* / Emit as events and log to stdout
* og: [
* { emit: 'stdout', level: 'query' },
* { emit: 'stdout', level: 'info' },
* { emit: 'stdout', level: 'warn' }
* { emit: 'stdout', level: 'error' }
*
* ```
* Read more in our [docs](https://pris.ly/d/logging).
*/
log?: (LogLevel | LogDefinition)[]
/**
* The default values for transactionOptions
* maxWait ?= 2000
* timeout ?= 5000
*/
transactionOptions?: {
maxWait?: number
timeout?: number
isolationLevel?: TransactionIsolationLevel
}
/**
* Global configuration for omitting model fields by default.
*
* @example
* ```
* const prisma = new PrismaClient({
* omit: {
* user: {
* password: true
* }
* }
* })
* ```
*/
omit?: GlobalOmitConfig
/**
* SQL commenter plugins that add metadata to SQL queries as comments.
* Comments follow the sqlcommenter format: https://google.github.io/sqlcommenter/
*
* @example
* ```
* const prisma = new PrismaClient({
* adapter,
* comments: [
* traceContext(),
* queryInsights(),
* ],
* })
* ```
*/
comments?: runtime.SqlCommenterPlugin[]
/**
* Optional maximum size for the query plan cache. If not provided, a default size will be used.
* A value of `0` can be used to disable the cache entirely. A higher cache size can improve
* performance for applications that execute a large number of unique queries, while a smaller
* cache size can reduce memory usage.
*
* @example
* ```
* const prisma = new PrismaClient({
* adapter,
* queryPlanCacheMaxSize: 100,
* })
* ```
*/
queryPlanCacheMaxSize?: number
}
export type GlobalOmitConfig = {
tokens?: Prisma.tokensOmit
users?: Prisma.usersOmit
}
/* Types for Logging */
export type LogLevel = 'info' | 'query' | 'warn' | 'error'
export type LogDefinition = {
level: LogLevel
emit: 'stdout' | 'event'
}
export type CheckIsLogLevel<T> = T extends LogLevel ? T : never;
export type GetLogType<T> = CheckIsLogLevel<
T extends LogDefinition ? T['level'] : T
>;
export type GetEvents<T extends any[]> = T extends Array<LogLevel | LogDefinition>
? GetLogType<T[number]>
: never;
export type QueryEvent = {
timestamp: Date
query: string
params: string
duration: number
target: string
}
export type LogEvent = {
timestamp: Date
message: string
target: string
}
/* End Types for Logging */
export type PrismaAction =
| 'findUnique'
| 'findUniqueOrThrow'
| 'findMany'
| 'findFirst'
| 'findFirstOrThrow'
| 'create'
| 'createMany'
| 'createManyAndReturn'
| 'update'
| 'updateMany'
| 'updateManyAndReturn'
| 'upsert'
| 'delete'
| 'deleteMany'
| 'executeRaw'
| 'queryRaw'
| 'aggregate'
| 'count'
| 'runCommandRaw'
| 'findRaw'
| 'groupBy'
/**
* `PrismaClient` proxy available in interactive transactions.
*/
export type TransactionClient = Omit<DefaultPrismaClient, runtime.ITXClientDenyList>

View file

@ -0,0 +1,112 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* WARNING: This is an internal file that is subject to change!
*
* 🛑 Under no circumstances should you import this file directly! 🛑
*
* All exports from this file are wrapped under a `Prisma` namespace object in the browser.ts file.
* While this enables partial backward compatibility, it is not part of the stable public API.
*
* If you are looking for your Models, Enums, and Input Types, please import them from the respective
* model files in the `model` directory!
*/
import * as runtime from "@prisma/client/runtime/index-browser"
export type * from '../models.js'
export type * from './prismaNamespace.js'
export const Decimal = runtime.Decimal
export const NullTypes = {
DbNull: runtime.NullTypes.DbNull as (new (secret: never) => typeof runtime.DbNull),
JsonNull: runtime.NullTypes.JsonNull as (new (secret: never) => typeof runtime.JsonNull),
AnyNull: runtime.NullTypes.AnyNull as (new (secret: never) => typeof runtime.AnyNull),
}
/**
* Helper for filtering JSON entries that have `null` on the database (empty on the db)
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const DbNull = runtime.DbNull
/**
* Helper for filtering JSON entries that have JSON `null` values (not empty on the db)
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const JsonNull = runtime.JsonNull
/**
* Helper for filtering JSON entries that are `Prisma.DbNull` or `Prisma.JsonNull`
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const AnyNull = runtime.AnyNull
export const ModelName = {
tokens: 'tokens',
users: 'users'
} as const
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
/*
* Enums
*/
export const TransactionIsolationLevel = runtime.makeStrictEnum({
ReadUncommitted: 'ReadUncommitted',
ReadCommitted: 'ReadCommitted',
RepeatableRead: 'RepeatableRead',
Serializable: 'Serializable'
} as const)
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
export const TokensScalarFieldEnum = {
id: 'id',
token: 'token',
type: 'type',
userId: 'userId',
expiresAt: 'expiresAt',
createdAt: 'createdAt'
} as const
export type TokensScalarFieldEnum = (typeof TokensScalarFieldEnum)[keyof typeof TokensScalarFieldEnum]
export const UsersScalarFieldEnum = {
id: 'id',
name: 'name',
email: 'email',
password: 'password',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type UsersScalarFieldEnum = (typeof UsersScalarFieldEnum)[keyof typeof UsersScalarFieldEnum]
export const SortOrder = {
asc: 'asc',
desc: 'desc'
} as const
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
export const QueryMode = {
default: 'default',
insensitive: 'insensitive'
} as const
export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]

View file

@ -0,0 +1,13 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This is a barrel export file for all models and their related types.
*
* 🟢 You can import this file directly.
*/
export type * from './models/tokens.js'
export type * from './models/users.js'
export type * from './commonInputTypes.js'

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
import { PrismaClient } from '../generated/prisma/client.js';
import { PrismaPg } from '@prisma/adapter-pg';
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL! });
const prisma = new PrismaClient({ adapter });
export default prisma;

View file

@ -0,0 +1,23 @@
import { Request, Response, NextFunction } from 'express';
import { verifyAccessToken } from '../services/auth.service.js';
export interface AuthRequest extends Request {
userId?: string;
}
export function authenticate(req: AuthRequest, res: Response, next: NextFunction) {
const header = req.headers.authorization;
if (!header?.startsWith('Bearer ')) {
res.status(401).json({ error: 'Missing token' });
return;
}
try {
const token = header.slice(7);
const payload = verifyAccessToken(token);
req.userId = payload.sub;
next();
} catch {
res.status(401).json({ error: 'Invalid or expired token' });
}
}

View file

@ -0,0 +1,24 @@
import prisma from '../lib/prisma.js';
import { TokenType } from '../generated/prisma/enums.js';
export async function createToken(data: {
id: string;
token: string;
type: TokenType;
userId: string;
expiresAt: Date;
}) {
return prisma.tokens.create({ data });
}
export async function findToken(token: string) {
return prisma.tokens.findUnique({ where: { token } });
}
export async function deleteToken(token: string) {
return prisma.tokens.delete({ where: { token } });
}
export async function deleteTokensByUserId(userId: string) {
return prisma.tokens.deleteMany({ where: { userId } });
}

View file

@ -0,0 +1,30 @@
import prisma from '../lib/prisma.js';
export async function findUserByEmail(email: string) {
return prisma.users.findUnique({ where: { email } });
}
export async function findUserById(id: string) {
return prisma.users.findUnique({ where: { id } });
}
export async function createUser(data: {
id: string;
name: string;
email: string;
password: string;
updatedAt: Date;
}) {
return prisma.users.create({ data });
}
export async function updateUser(
id: string,
data: Partial<{ name: string; email: string; password: string; updatedAt: Date }>
) {
return prisma.users.update({ where: { id }, data });
}
export async function deleteUser(id: string) {
return prisma.users.delete({ where: { id } });
}

View file

@ -0,0 +1,10 @@
import { Router } from 'express';
import * as authController from '../controllers/auth.controller.js';
const router = Router();
router.post('/register', authController.register);
router.post('/login', authController.login);
router.post('/logout', authController.logout);
export default router;

View file

@ -0,0 +1,13 @@
import { Router } from 'express';
import { authenticate } from '../middlewares/auth.middleware.js';
import * as usersController from '../controllers/users.controller.js';
const router = Router();
router.use(authenticate);
router.get('/me', usersController.getMe);
router.put('/me', usersController.updateMe);
router.delete('/me', usersController.deleteMe);
export default router;

View file

@ -1,18 +1,16 @@
import express from 'express';
import cors from 'cors';
import authRoutes from './routes/auth.routes.js';
import usersRoutes from './routes/users.routes.js';
const app = express();
const port = process.env.PORT? Number(process.env.PORT) : 4000;
const port = process.env.PORT ? Number(process.env.PORT) : 4000;
app.use(cors());
app.use(express.json());
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.get('/api/data', (req, res) => {
res.json({ message: 'This is some data from the server!' });
});
app.use('/auth', authRoutes);
app.use('/users', usersRoutes);
app.listen(port, '0.0.0.0', () => {
console.log(`Server is running on port ${port}`);

View file

@ -0,0 +1,72 @@
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import crypto from 'node:crypto';
import { z } from 'zod';
import * as usersRepo from '../repositories/users.repository.js';
import * as tokensRepo from '../repositories/tokens.repository.js';
import { TokenType } from '../generated/prisma/enums.js';
const JWT_SECRET = process.env.JWT_SECRET ?? 'changeme';
const JWT_EXPIRES_IN = '15m';
const REFRESH_EXPIRES_DAYS = 7;
export const registerSchema = z.object({
name: z.string().check(z.minLength(2)),
email: z.email(),
password: z.string().check(z.minLength(6)),
});
export const loginSchema = z.object({
email: z.email(),
password: z.string().check(z.minLength(1)),
});
export async function register(input: z.infer<typeof registerSchema>) {
const existing = await usersRepo.findUserByEmail(input.email);
if (existing) throw new Error('Email already in use');
const hashed = await bcrypt.hash(input.password, 10);
const now = new Date();
const user = await usersRepo.createUser({
id: crypto.randomUUID(),
name: input.name,
email: input.email,
password: hashed,
updatedAt: now,
});
return { id: user.id, name: user.name, email: user.email };
}
export async function login(input: z.infer<typeof loginSchema>) {
const user = await usersRepo.findUserByEmail(input.email);
if (!user) throw new Error('Invalid credentials');
const valid = await bcrypt.compare(input.password, user.password);
if (!valid) throw new Error('Invalid credentials');
const accessToken = jwt.sign({ sub: user.id }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
const refreshToken = crypto.randomUUID();
const expiresAt = new Date();
expiresAt.setDate(expiresAt.getDate() + REFRESH_EXPIRES_DAYS);
await tokensRepo.createToken({
id: crypto.randomUUID(),
token: refreshToken,
type: TokenType.REFRESH,
userId: user.id,
expiresAt,
});
return { accessToken, refreshToken };
}
export async function logout(refreshToken: string) {
await tokensRepo.deleteToken(refreshToken);
}
export function verifyAccessToken(token: string): { sub: string } {
return jwt.verify(token, JWT_SECRET) as { sub: string };
}

View file

@ -0,0 +1,35 @@
import bcrypt from 'bcryptjs';
import { z } from 'zod';
import * as usersRepo from '../repositories/users.repository.js';
export const updateUserSchema = z.object({
name: z.string().check(z.minLength(2)).optional(),
email: z.email().optional(),
password: z.string().check(z.minLength(6)).optional(),
});
export async function getUser(id: string) {
const user = await usersRepo.findUserById(id);
if (!user) throw new Error('User not found');
return { id: user.id, name: user.name, email: user.email, createdAt: user.createdAt };
}
export async function updateUser(id: string, input: z.infer<typeof updateUserSchema>) {
const user = await usersRepo.findUserById(id);
if (!user) throw new Error('User not found');
const data: Parameters<typeof usersRepo.updateUser>[1] = { updatedAt: new Date() };
if (input.name) data.name = input.name;
if (input.email) data.email = input.email;
if (input.password) data.password = await bcrypt.hash(input.password, 10);
const updated = await usersRepo.updateUser(id, data);
return { id: updated.id, name: updated.name, email: updated.email };
}
export async function deleteUser(id: string) {
const user = await usersRepo.findUserById(id);
if (!user) throw new Error('User not found');
await usersRepo.deleteUser(id);
}