diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 5480842b..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "kiroAgent.configureMCP": "Disabled" -} \ No newline at end of file diff --git a/backend/src/Implement Role-Based Access Control (RBAC)/app.module.ts b/backend/src/Implement Role-Based Access Control (RBAC)/app.module.ts index f843ba1f..31039000 100644 --- a/backend/src/Implement Role-Based Access Control (RBAC)/app.module.ts +++ b/backend/src/Implement Role-Based Access Control (RBAC)/app.module.ts @@ -1,5 +1,5 @@ import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; -import { DemoController, FakeAuthMiddleware } from './demo/demo.controller'; +import { DemoController, FakeAuthMiddleware } from './demo.controller'; @Module({ controllers: [DemoController], diff --git a/backend/src/Implement Role-Based Access Control (RBAC)/demo.controller.ts b/backend/src/Implement Role-Based Access Control (RBAC)/demo.controller.ts index 2597cd54..1f672732 100644 --- a/backend/src/Implement Role-Based Access Control (RBAC)/demo.controller.ts +++ b/backend/src/Implement Role-Based Access Control (RBAC)/demo.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get, UseGuards, Request } from '@nestjs/common'; -import { Roles } from '../auth/roles.decorator'; -import { RolesGuard } from '../auth/roles.guard'; -import { Role } from '../auth/roles.enum'; +import { Roles } from './roles.decorator'; +import { RolesGuard } from './roles.guard'; +import { Role } from './roles.enum'; /** * Simulates the object that a real auth guard (e.g. JwtAuthGuard) would attach @@ -15,7 +15,11 @@ import { Request as ExpressRequest, Response, NextFunction } from 'express'; @Injectable() export class FakeAuthMiddleware implements NestMiddleware { - use(req: ExpressRequest & { user?: any }, _res: Response, next: NextFunction) { + use( + req: ExpressRequest & { user?: any }, + _res: Response, + next: NextFunction, + ) { const role = (req.query.role as string)?.toUpperCase() ?? Role.USER; req.user = { id: 1, username: 'testuser', role }; next(); @@ -24,10 +28,9 @@ export class FakeAuthMiddleware implements NestMiddleware { // ────────────────────────────────────────────────────────────────────────────── -@UseGuards(RolesGuard) // apply guard to every route in this controller +@UseGuards(RolesGuard) // apply guard to every route in this controller @Controller('demo') export class DemoController { - /** Accessible by any authenticated user (no @Roles restriction) */ @Get('public') publicRoute() { @@ -52,6 +55,8 @@ export class DemoController { @Roles(Role.USER, Role.ADMIN) @Get('shared') shared(@Request() req: any) { - return { message: `Hello, ${req.user.username}! Your role is ${req.user.role}.` }; + return { + message: `Hello, ${req.user.username}! Your role is ${req.user.role}.`, + }; } } diff --git a/backend/src/modules/hospital-integration/hospital-integration.service.ts b/backend/src/modules/hospital-integration/hospital-integration.service.ts index 79b6f476..a2ce2cfb 100644 --- a/backend/src/modules/hospital-integration/hospital-integration.service.ts +++ b/backend/src/modules/hospital-integration/hospital-integration.service.ts @@ -1,202 +1,238 @@ import { Injectable, Logger, HttpException, HttpStatus } from '@nestjs/common'; import { HttpService } from '@nestjs/axios'; import { ConfigService } from '@nestjs/config'; -import { AxiosError, AxiosRequestConfig } from 'axios'; +import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; import { catchError, retry, timeout, map } from 'rxjs/operators'; import { Observable, throwError, timer, firstValueFrom } from 'rxjs'; -import { HospitalClaimDataDto, HospitalVerificationDto } from './dto/hospital-data.dto'; +import { + HospitalClaimDataDto, + HospitalVerificationDto, +} from './dto/hospital-data.dto'; export interface CircuitBreakerState { - failures: number; - lastFailureTime: number; - state: 'CLOSED' | 'OPEN' | 'HALF_OPEN'; + failures: number; + lastFailureTime: number; + state: 'CLOSED' | 'OPEN' | 'HALF_OPEN'; } @Injectable() export class HospitalIntegrationService { - private readonly logger = new Logger(HospitalIntegrationService.name); - private readonly circuitBreakers = new Map(); - - private readonly maxRetries: number; - private readonly retryDelay: number; - private readonly requestTimeout: number; - private readonly circuitBreakerThreshold: number; - private readonly circuitBreakerTimeout: number; - - constructor( - private readonly httpService: HttpService, - private readonly configService: ConfigService, - ) { - this.maxRetries = this.configService.get('hospital.maxRetries', 3); - this.retryDelay = this.configService.get('hospital.retryDelay', 1000); - this.requestTimeout = this.configService.get('hospital.requestTimeout', 10000); - this.circuitBreakerThreshold = this.configService.get('hospital.circuitBreakerThreshold', 5); - this.circuitBreakerTimeout = this.configService.get('hospital.circuitBreakerTimeout', 60000); + private readonly logger = new Logger(HospitalIntegrationService.name); + private readonly circuitBreakers = new Map(); + + private readonly maxRetries: number; + private readonly retryDelay: number; + private readonly requestTimeout: number; + private readonly circuitBreakerThreshold: number; + private readonly circuitBreakerTimeout: number; + + constructor( + private readonly httpService: HttpService, + private readonly configService: ConfigService, + ) { + this.maxRetries = this.configService.get('hospital.maxRetries', 3); + this.retryDelay = this.configService.get( + 'hospital.retryDelay', + 1000, + ); + this.requestTimeout = this.configService.get( + 'hospital.requestTimeout', + 10000, + ); + this.circuitBreakerThreshold = this.configService.get( + 'hospital.circuitBreakerThreshold', + 5, + ); + this.circuitBreakerTimeout = this.configService.get( + 'hospital.circuitBreakerTimeout', + 60000, + ); + } + + private getCircuitBreakerState(hospitalId: string): CircuitBreakerState { + if (!this.circuitBreakers.has(hospitalId)) { + this.circuitBreakers.set(hospitalId, { + failures: 0, + lastFailureTime: 0, + state: 'CLOSED', + }); } + return this.circuitBreakers.get(hospitalId)!; + } - private getCircuitBreakerState(hospitalId: string): CircuitBreakerState { - if (!this.circuitBreakers.has(hospitalId)) { - this.circuitBreakers.set(hospitalId, { - failures: 0, - lastFailureTime: 0, - state: 'CLOSED', - }); - } - return this.circuitBreakers.get(hospitalId)!; - } - - private checkCircuitBreaker(hospitalId: string): void { - const state = this.getCircuitBreakerState(hospitalId); - - if (state.state === 'OPEN') { - const timeSinceLastFailure = Date.now() - state.lastFailureTime; - - if (timeSinceLastFailure > this.circuitBreakerTimeout) { - this.logger.log(`Circuit breaker for ${hospitalId} transitioning to HALF_OPEN`); - state.state = 'HALF_OPEN'; - state.failures = 0; - } else { - throw new HttpException( - `Circuit breaker is OPEN for hospital ${hospitalId}. Service temporarily unavailable.`, - HttpStatus.SERVICE_UNAVAILABLE, - ); - } - } - } + private checkCircuitBreaker(hospitalId: string): void { + const state = this.getCircuitBreakerState(hospitalId); - private recordSuccess(hospitalId: string): void { - const state = this.getCircuitBreakerState(hospitalId); - - if (state.state === 'HALF_OPEN') { - this.logger.log(`Circuit breaker for ${hospitalId} transitioning to CLOSED`); - state.state = 'CLOSED'; - } + if (state.state === 'OPEN') { + const timeSinceLastFailure = Date.now() - state.lastFailureTime; + if (timeSinceLastFailure > this.circuitBreakerTimeout) { + this.logger.log( + `Circuit breaker for ${hospitalId} transitioning to HALF_OPEN`, + ); + state.state = 'HALF_OPEN'; state.failures = 0; - } - - private recordFailure(hospitalId: string): void { - const state = this.getCircuitBreakerState(hospitalId); - state.failures++; - state.lastFailureTime = Date.now(); - - if (state.failures >= this.circuitBreakerThreshold) { - this.logger.warn(`Circuit breaker for ${hospitalId} transitioning to OPEN`); - state.state = 'OPEN'; - } - } - - private makeRequest( - url: string, - hospitalId: string, - config?: AxiosRequestConfig, - ): Observable { - this.checkCircuitBreaker(hospitalId); - - return this.httpService.get(url, config).pipe( - timeout(this.requestTimeout), - retry({ - count: this.maxRetries, - delay: (error, retryCount) => { - this.logger.warn( - `Retry attempt ${retryCount} for ${url} due to: ${error.message}`, - ); - return timer(this.retryDelay * retryCount); - }, - }), - map((response) => { - this.recordSuccess(hospitalId); - return response.data; - }), - catchError((error: AxiosError) => { - this.recordFailure(hospitalId); - this.logger.error( - `Request failed for ${url}: ${error.message}`, - error.stack, - ); - - if (error.code === 'ECONNABORTED') { - return throwError(() => new HttpException( - 'Request timeout', - HttpStatus.REQUEST_TIMEOUT, - )); - } - - return throwError(() => new HttpException( - error.response?.data || 'External service error', - error.response?.status || HttpStatus.BAD_GATEWAY, - )); - }), + } else { + throw new HttpException( + `Circuit breaker is OPEN for hospital ${hospitalId}. Service temporarily unavailable.`, + HttpStatus.SERVICE_UNAVAILABLE, ); + } } + } - async fetchClaimData( - hospitalId: string, - claimId: string, - ): Promise { - const baseUrl = this.configService.get(`hospital.endpoints.${hospitalId}`); - - if (!baseUrl) { - throw new HttpException( - `No endpoint configured for hospital ${hospitalId}`, - HttpStatus.NOT_FOUND, - ); - } - - const url = `${baseUrl}/claims/${claimId}`; - this.logger.log(`Fetching claim data from ${url}`); + private recordSuccess(hospitalId: string): void { + const state = this.getCircuitBreakerState(hospitalId); - return firstValueFrom(this.makeRequest(url, hospitalId)); + if (state.state === 'HALF_OPEN') { + this.logger.log( + `Circuit breaker for ${hospitalId} transitioning to CLOSED`, + ); + state.state = 'CLOSED'; } - async verifyClaimWithHospital( - hospitalId: string, - claimId: string, - ): Promise { - const baseUrl = this.configService.get(`hospital.endpoints.${hospitalId}`); - - if (!baseUrl) { - throw new HttpException( - `No endpoint configured for hospital ${hospitalId}`, - HttpStatus.NOT_FOUND, - ); - } + state.failures = 0; + } - const url = `${baseUrl}/claims/${claimId}/verify`; - this.logger.log(`Verifying claim with hospital at ${url}`); + private recordFailure(hospitalId: string): void { + const state = this.getCircuitBreakerState(hospitalId); + state.failures++; + state.lastFailureTime = Date.now(); - return firstValueFrom(this.makeRequest(url, hospitalId)); + if (state.failures >= this.circuitBreakerThreshold) { + this.logger.warn( + `Circuit breaker for ${hospitalId} transitioning to OPEN`, + ); + state.state = 'OPEN'; } + } + + private makeRequest( + url: string, + hospitalId: string, + config?: AxiosRequestConfig, + ): Observable { + this.checkCircuitBreaker(hospitalId); + + return this.httpService.get(url, config).pipe( + timeout(this.requestTimeout), + retry({ + count: this.maxRetries, + delay: (error, retryCount) => { + this.logger.warn( + `Retry attempt ${retryCount} for ${url} due to: ${error.message}`, + ); + return timer(this.retryDelay * retryCount); + }, + }), + map((response: AxiosResponse) => { + this.recordSuccess(hospitalId); + return response.data; + }), + catchError((error: AxiosError) => { + this.recordFailure(hospitalId); + this.logger.error( + `Request failed for ${url}: ${error.message}`, + error.stack, + ); - async fetchPatientHistory( - hospitalId: string, - patientId: string, - ): Promise { - const baseUrl = this.configService.get(`hospital.endpoints.${hospitalId}`); - - if (!baseUrl) { - throw new HttpException( - `No endpoint configured for hospital ${hospitalId}`, - HttpStatus.NOT_FOUND, - ); + if (error.code === 'ECONNABORTED') { + return throwError( + () => + new HttpException('Request timeout', HttpStatus.REQUEST_TIMEOUT), + ); } - const url = `${baseUrl}/patients/${patientId}/claims`; - this.logger.log(`Fetching patient history from ${url}`); - - return firstValueFrom(this.makeRequest(url, hospitalId)); + return throwError( + () => + new HttpException( + error.response?.data || 'External service error', + error.response?.status || HttpStatus.BAD_GATEWAY, + ), + ); + }), + ); + } + + async fetchClaimData( + hospitalId: string, + claimId: string, + ): Promise { + const baseUrl = this.configService.get( + `hospital.endpoints.${hospitalId}`, + ); + + if (!baseUrl) { + throw new HttpException( + `No endpoint configured for hospital ${hospitalId}`, + HttpStatus.NOT_FOUND, + ); } - getCircuitBreakerStatus(hospitalId: string): CircuitBreakerState { - return this.getCircuitBreakerState(hospitalId); + const url = `${baseUrl}/claims/${claimId}`; + this.logger.log(`Fetching claim data from ${url}`); + + return firstValueFrom( + this.makeRequest(url, hospitalId), + ); + } + + async verifyClaimWithHospital( + hospitalId: string, + claimId: string, + ): Promise { + const baseUrl = this.configService.get( + `hospital.endpoints.${hospitalId}`, + ); + + if (!baseUrl) { + throw new HttpException( + `No endpoint configured for hospital ${hospitalId}`, + HttpStatus.NOT_FOUND, + ); } - resetCircuitBreaker(hospitalId: string): void { - this.logger.log(`Manually resetting circuit breaker for ${hospitalId}`); - this.circuitBreakers.set(hospitalId, { - failures: 0, - lastFailureTime: 0, - state: 'CLOSED', - }); + const url = `${baseUrl}/claims/${claimId}/verify`; + this.logger.log(`Verifying claim with hospital at ${url}`); + + return firstValueFrom( + this.makeRequest(url, hospitalId), + ); + } + + async fetchPatientHistory( + hospitalId: string, + patientId: string, + ): Promise { + const baseUrl = this.configService.get( + `hospital.endpoints.${hospitalId}`, + ); + + if (!baseUrl) { + throw new HttpException( + `No endpoint configured for hospital ${hospitalId}`, + HttpStatus.NOT_FOUND, + ); } + + const url = `${baseUrl}/patients/${patientId}/claims`; + this.logger.log(`Fetching patient history from ${url}`); + + return firstValueFrom( + this.makeRequest(url, hospitalId), + ); + } + + getCircuitBreakerStatus(hospitalId: string): CircuitBreakerState { + return this.getCircuitBreakerState(hospitalId); + } + + resetCircuitBreaker(hospitalId: string): void { + this.logger.log(`Manually resetting circuit breaker for ${hospitalId}`); + this.circuitBreakers.set(hospitalId, { + failures: 0, + lastFailureTime: 0, + state: 'CLOSED', + }); + } } diff --git a/frontend/app/globals.css b/frontend/app/globals.css index b9aba73b..cfcc911e 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -1,6 +1,6 @@ -@import "tailwindcss"; -@source "./**/*.{ts,tsx,js,jsx}"; -@source "../components/**/*.{ts,tsx,js,jsx}"; +@tailwind base; +@tailwind components; +@tailwind utilities; :root { --background: #000000; diff --git a/frontend/package.json b/frontend/package.json index 4a18881b..055e6419 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,7 +19,9 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "autoprefixer": "^10.4.27", "eslint": "^10.0.1", + "postcss": "^8.5.6", "prettier": "^3.8.1", "tailwindcss": "^4", "typescript": "^5" diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 49c68b12..6bda0c9a 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + clsx: + specifier: ^2.1.1 + version: 2.1.1 lucide-react: specifier: ^0.575.0 version: 0.575.0(react@19.2.3) @@ -33,9 +36,15 @@ importers: '@types/react-dom': specifier: ^19 version: 19.2.3(@types/react@19.2.14) + autoprefixer: + specifier: ^10.4.27 + version: 10.4.27(postcss@8.5.6) eslint: specifier: ^10.0.1 version: 10.0.1(jiti@2.6.1) + postcss: + specifier: ^8.5.6 + version: 8.5.6 prettier: specifier: ^3.8.1 version: 3.8.1 @@ -131,89 +140,105 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -274,24 +299,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-musl@16.1.6': resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-x64-gnu@16.1.6': resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-musl@16.1.6': resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-win32-arm64-msvc@16.1.6': resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==} @@ -346,24 +375,28 @@ packages: engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.2.0': resolution: {integrity: sha512-XKcSStleEVnbH6W/9DHzZv1YhjE4eSS6zOu2eRtYAIh7aV4o3vIBs+t/B15xlqoxt6ef/0uiqJVB6hkHjWD/0A==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.2.0': resolution: {integrity: sha512-/hlXCBqn9K6fi7eAM0RsobHwJYa5V/xzWspVTzxnX+Ft9v6n+30Pz8+RxCn7sQL/vRHHLS30iQPrHQunu6/vJA==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.2.0': resolution: {integrity: sha512-lKUaygq4G7sWkhQbfdRRBkaq4LY39IriqBQ+Gk6l5nKq6Ay2M2ZZb1tlIyRNgZKS8cbErTwuYSor0IIULC0SHw==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.2.0': resolution: {integrity: sha512-xuDjhAsFdUuFP5W9Ze4k/o4AskUtI8bcAGU4puTYprr89QaYFmhYOPfP+d1pH+k9ets6RoE23BXZM1X1jJqoyw==} @@ -429,6 +462,13 @@ packages: ajv@6.14.0: resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + autoprefixer@10.4.27: + resolution: {integrity: sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -442,12 +482,24 @@ packages: resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} engines: {node: 18 || 20 || >=22} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + caniuse-lite@1.0.30001770: resolution: {integrity: sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==} + caniuse-lite@1.0.30001774: + resolution: {integrity: sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -471,10 +523,17 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + electron-to-chromium@1.5.302: + resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} + enhanced-resolve@5.19.0: resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} engines: {node: '>=10.13.0'} + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -545,6 +604,9 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -626,24 +688,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.31.1: resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.31.1: resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.31.1: resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.31.1: resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} @@ -709,6 +775,9 @@ packages: sass: optional: true + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -732,6 +801,9 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.4.31: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} @@ -821,6 +893,12 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -1133,6 +1211,15 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + autoprefixer@10.4.27(postcss@8.5.6): + dependencies: + browserslist: 4.28.1 + caniuse-lite: 1.0.30001774 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + balanced-match@4.0.4: {} baseline-browser-mapping@2.10.0: {} @@ -1141,10 +1228,22 @@ snapshots: dependencies: balanced-match: 4.0.4 + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.10.0 + caniuse-lite: 1.0.30001774 + electron-to-chromium: 1.5.302 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + caniuse-lite@1.0.30001770: {} + caniuse-lite@1.0.30001774: {} + client-only@0.0.1: {} + clsx@2.1.1: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -1161,11 +1260,15 @@ snapshots: detect-libc@2.1.2: {} + electron-to-chromium@1.5.302: {} + enhanced-resolve@5.19.0: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 + escalade@3.2.0: {} + escape-string-regexp@4.0.0: {} eslint-scope@9.1.1: @@ -1256,6 +1359,8 @@ snapshots: flatted@3.3.3: {} + fraction.js@5.3.4: {} + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -1386,6 +1491,8 @@ snapshots: - '@babel/core' - babel-plugin-macros + node-releases@2.0.27: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -1409,6 +1516,8 @@ snapshots: picocolors@1.1.1: {} + postcss-value-parser@4.2.0: {} + postcss@8.4.31: dependencies: nanoid: 3.3.11 @@ -1498,6 +1607,12 @@ snapshots: undici-types@6.21.0: {} + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 diff --git a/prisma.config.ts b/prisma.config.ts deleted file mode 100644 index 831a20fa..00000000 --- a/prisma.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -// This file was generated by Prisma, and assumes you have installed the following: -// npm install --save-dev prisma dotenv -import "dotenv/config"; -import { defineConfig } from "prisma/config"; - -export default defineConfig({ - schema: "prisma/schema.prisma", - migrations: { - path: "prisma/migrations", - }, - datasource: { - url: process.env["DATABASE_URL"], - }, -});