refactor(validation): restucture api input and output validation
This commit is contained in:
parent
485a95f99a
commit
eef17c5360
34 changed files with 1891 additions and 1802 deletions
62
exportSwagger.ts
Normal file
62
exportSwagger.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import { registry } from '@/lib/swagger';
|
||||||
|
import { OpenApiGeneratorV3 } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
function recursiveFileSearch(dir: string, fileList: string[] = []): string[] {
|
||||||
|
const files = fs.readdirSync(dir);
|
||||||
|
files.forEach((file) => {
|
||||||
|
const filePath = path.join(dir, file);
|
||||||
|
if (fs.statSync(filePath).isDirectory()) {
|
||||||
|
recursiveFileSearch(filePath, fileList);
|
||||||
|
} else if (file.match(/swagger\.ts$/)) {
|
||||||
|
fileList.push(filePath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return fileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exportSwagger() {
|
||||||
|
const filesToImport = recursiveFileSearch(
|
||||||
|
path.join(process.cwd(), 'src', 'app', 'api'),
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
filesToImport.map((file) => {
|
||||||
|
return import(file)
|
||||||
|
.then((module) => {
|
||||||
|
if (module.default) {
|
||||||
|
module.default(registry);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(`Error importing ${file}:`, error);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await import('./src/app/api/validation');
|
||||||
|
|
||||||
|
const generator = new OpenApiGeneratorV3(registry.definitions);
|
||||||
|
const spec = generator.generateDocument({
|
||||||
|
openapi: '3.0.0',
|
||||||
|
info: {
|
||||||
|
version: '1.0.0',
|
||||||
|
title: 'MeetUP',
|
||||||
|
description: 'API documentation for MeetUP application',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const outputPath = path.join(
|
||||||
|
process.cwd(),
|
||||||
|
'src',
|
||||||
|
'generated',
|
||||||
|
'swagger.json',
|
||||||
|
);
|
||||||
|
fs.writeFileSync(outputPath, JSON.stringify(spec, null, 2), 'utf8');
|
||||||
|
console.log(`Swagger JSON generated at ${outputPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
exportSwagger().catch((error) => {
|
||||||
|
console.error('Error exporting Swagger:', error);
|
||||||
|
});
|
|
@ -1,121 +0,0 @@
|
||||||
{
|
|
||||||
"apiFolder": "src/app/api",
|
|
||||||
"definition": {
|
|
||||||
"openapi": "3.0.0",
|
|
||||||
"info": {
|
|
||||||
"title": "MeetUP API",
|
|
||||||
"version": "1.0"
|
|
||||||
},
|
|
||||||
"components": {
|
|
||||||
"schemas": {
|
|
||||||
"ErrorResponse": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"success": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Error message"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ZodErrorResponse": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"success": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Error message"
|
|
||||||
},
|
|
||||||
"errors": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"path": { "type": "string" },
|
|
||||||
"message": { "type": "string" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"User": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": { "type": "string" },
|
|
||||||
"name": { "type": "string" },
|
|
||||||
"first_name": { "type": "string" },
|
|
||||||
"last_name": { "type": "string" },
|
|
||||||
"email": { "type": "string", "format": "email" },
|
|
||||||
"image": { "type": "string", "format": "uri" },
|
|
||||||
"timezone": { "type": "string", "description": "User timezone" },
|
|
||||||
"created_at": { "type": "string", "format": "date-time" },
|
|
||||||
"updated_at": { "type": "string", "format": "date-time" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PublicUser": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": { "type": "string" },
|
|
||||||
"name": { "type": "string" },
|
|
||||||
"first_name": { "type": "string" },
|
|
||||||
"last_name": { "type": "string" },
|
|
||||||
"image": { "type": "string", "format": "uri" },
|
|
||||||
"timezone": { "type": "string", "description": "User timezone" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"SimpleUser": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": { "type": "string" },
|
|
||||||
"name": { "type": "string" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Participant": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"user": {
|
|
||||||
"$ref": "#/components/schemas/SimpleUser"
|
|
||||||
},
|
|
||||||
"status": { "type": "string" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Event": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": { "type": "string" },
|
|
||||||
"title": { "type": "string" },
|
|
||||||
"description": { "type": "string" },
|
|
||||||
"start_time": { "type": "string", "format": "date-time" },
|
|
||||||
"end_time": { "type": "string", "format": "date-time" },
|
|
||||||
"status": { "type": "string" },
|
|
||||||
"location": { "type": "string" },
|
|
||||||
"organizer": {
|
|
||||||
"$ref": "#/components/schemas/SimpleUser"
|
|
||||||
},
|
|
||||||
"participants": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"user": {
|
|
||||||
"$ref": "#/components/schemas/SimpleUser"
|
|
||||||
},
|
|
||||||
"status": { "type": "string" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"created_at": { "type": "string", "format": "date-time" },
|
|
||||||
"updated_at": { "type": "string", "format": "date-time" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": []
|
|
||||||
}
|
|
||||||
}
|
|
11
package.json
11
package.json
|
@ -14,10 +14,11 @@
|
||||||
"prisma:db:push": "dotenv -e .env.local -- prisma db push",
|
"prisma:db:push": "dotenv -e .env.local -- prisma db push",
|
||||||
"prisma:migrate:reset": "dotenv -e .env.local -- prisma migrate reset",
|
"prisma:migrate:reset": "dotenv -e .env.local -- prisma migrate reset",
|
||||||
"dev_container": "docker compose -f docker-compose.dev.yml up --watch --build",
|
"dev_container": "docker compose -f docker-compose.dev.yml up --watch --build",
|
||||||
"swagger:generate": "next-swagger-doc-cli next-swagger-doc.json --output src/generated/swagger.json",
|
"swagger:generate": "ts-node -r tsconfig-paths/register exportSwagger.ts",
|
||||||
"orval:generate": "orval"
|
"orval:generate": "orval"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@asteasolutions/zod-to-openapi": "^8.0.0-beta.4",
|
||||||
"@auth/prisma-adapter": "^2.9.1",
|
"@auth/prisma-adapter": "^2.9.1",
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
||||||
|
@ -40,9 +41,8 @@
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.511.0",
|
"lucide-react": "^0.511.0",
|
||||||
"next": "15.3.4",
|
"next": "15.4.0-canary.85",
|
||||||
"next-auth": "^5.0.0-beta.25",
|
"next-auth": "^5.0.0-beta.25",
|
||||||
"next-swagger-doc": "^0.4.1",
|
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
|
@ -58,6 +58,7 @@
|
||||||
"@types/react": "19.1.8",
|
"@types/react": "19.1.8",
|
||||||
"@types/react-dom": "19.1.6",
|
"@types/react-dom": "19.1.6",
|
||||||
"@types/swagger-ui-react": "^5",
|
"@types/swagger-ui-react": "^5",
|
||||||
|
"@types/webpack-env": "^1.18.8",
|
||||||
"dotenv-cli": "8.0.0",
|
"dotenv-cli": "8.0.0",
|
||||||
"eslint": "9.29.0",
|
"eslint": "9.29.0",
|
||||||
"eslint-config-next": "15.3.4",
|
"eslint-config-next": "15.3.4",
|
||||||
|
@ -67,8 +68,10 @@
|
||||||
"prettier": "3.5.3",
|
"prettier": "3.5.3",
|
||||||
"prisma": "6.9.0",
|
"prisma": "6.9.0",
|
||||||
"tailwindcss": "4.1.10",
|
"tailwindcss": "4.1.10",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"tsconfig-paths": "^4.2.0",
|
||||||
"tw-animate-css": "1.3.4",
|
"tw-animate-css": "1.3.4",
|
||||||
"typescript": "5.8.3"
|
"typescript": "^5.8.3"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@4.9.2"
|
"packageManager": "yarn@4.9.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,97 +1,40 @@
|
||||||
import { prisma } from '@/prisma';
|
import { prisma } from '@/prisma';
|
||||||
import { auth } from '@/auth';
|
import { auth } from '@/auth';
|
||||||
import { NextResponse } from 'next/server';
|
import {
|
||||||
import { z } from 'zod/v4';
|
returnZodTypeCheckedResponse,
|
||||||
|
userAuthenticated,
|
||||||
|
} from '@/lib/apiHelpers';
|
||||||
|
import {
|
||||||
|
ErrorResponseSchema,
|
||||||
|
SuccessResponseSchema,
|
||||||
|
ZodErrorResponseSchema,
|
||||||
|
} from '@/app/api/validation';
|
||||||
|
import {
|
||||||
|
ParticipantResponseSchema,
|
||||||
|
updateParticipantSchema,
|
||||||
|
} from '../validation';
|
||||||
|
|
||||||
const patchParticipantSchema = z.object({
|
|
||||||
status: z.enum(['ACCEPTED', 'DECLINED', 'TENTATIVE', 'PENDING']),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/event/{eventID}/participant/{user}:
|
|
||||||
* get:
|
|
||||||
* summary: Get a specific participant's details in an event
|
|
||||||
* description: Returns the details of a specific participant in an event.
|
|
||||||
* tags:
|
|
||||||
* - Event_Participant
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: eventID
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The ID of the event.
|
|
||||||
* - in: path
|
|
||||||
* name: user
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The ID or name of the user.
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Details of the participant.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* participant:
|
|
||||||
* $ref: "#/components/schemas/Participant"
|
|
||||||
* 401:
|
|
||||||
* description: Not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Not authenticated
|
|
||||||
* 404:
|
|
||||||
* description: User not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: User not found
|
|
||||||
* 403:
|
|
||||||
* description: User is not a participant or organizer of this event.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: User is not a participant or organizer of this event
|
|
||||||
*/
|
|
||||||
export const GET = auth(async (req, { params }) => {
|
export const GET = auth(async (req, { params }) => {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dbUser = await prisma.user.findUnique({
|
const dbUser = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: req.auth.user.id,
|
id: authCheck.user.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dbUser) {
|
if (!dbUser)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'User not found' },
|
{ success: false, message: 'User not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const eventID = (await params).eventID;
|
const eventID = (await params).eventID;
|
||||||
const user = (await params).user;
|
const user = (await params).user;
|
||||||
|
@ -110,15 +53,15 @@ export const GET = auth(async (req, { params }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isParticipant && !isOrganizer) {
|
if (!isParticipant && !isOrganizer)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
message: 'User is not a participant or organizer of this event',
|
message: 'User is not a participant or organizer of this event',
|
||||||
},
|
},
|
||||||
{ status: 403 },
|
{ status: 403 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const participant = await prisma.meetingParticipant.findUnique({
|
const participant = await prisma.meetingParticipant.findUnique({
|
||||||
where: {
|
where: {
|
||||||
|
@ -132,110 +75,50 @@ export const GET = auth(async (req, { params }) => {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
status: true,
|
status: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!participant) {
|
if (!participant)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'Participant not found' },
|
{ success: false, message: 'Participant not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json({
|
return returnZodTypeCheckedResponse(ParticipantResponseSchema, {
|
||||||
success: true,
|
success: true,
|
||||||
participant,
|
participant,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/event/{eventID}/participant/{user}:
|
|
||||||
* delete:
|
|
||||||
* summary: Remove a participant from an event
|
|
||||||
* description: Removes a participant from an event. Only the organizer can remove participants.
|
|
||||||
* tags:
|
|
||||||
* - Event_Participant
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: eventID
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The ID of the event.
|
|
||||||
* - in: path
|
|
||||||
* name: user
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The ID or name of the user to be removed.
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Participant removed successfully.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* message:
|
|
||||||
* type: string
|
|
||||||
* 401:
|
|
||||||
* description: Not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Not authenticated
|
|
||||||
* 404:
|
|
||||||
* description: User not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: User not found
|
|
||||||
* 403:
|
|
||||||
* description: Only organizer can remove participants.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Only organizer can remove participants
|
|
||||||
*/
|
|
||||||
export const DELETE = auth(async (req, { params }) => {
|
export const DELETE = auth(async (req, { params }) => {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dbUser = await prisma.user.findUnique({
|
const dbUser = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: req.auth.user.id,
|
id: authCheck.user.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dbUser) {
|
if (!dbUser)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'User not found' },
|
{ success: false, message: 'User not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const eventID = (await params).eventID;
|
const eventID = (await params).eventID;
|
||||||
const user = (await params).user;
|
const user = (await params).user;
|
||||||
|
@ -247,12 +130,12 @@ export const DELETE = auth(async (req, { params }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isOrganizer) {
|
if (!isOrganizer)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'Only organizer can remove participants' },
|
{ success: false, message: 'Only organizer can remove participants' },
|
||||||
{ status: 403 },
|
{ status: 403 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const participant = await prisma.meetingParticipant.findUnique({
|
const participant = await prisma.meetingParticipant.findUnique({
|
||||||
where: {
|
where: {
|
||||||
|
@ -263,12 +146,12 @@ export const DELETE = auth(async (req, { params }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!participant) {
|
if (!participant)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'Participant not found' },
|
{ success: false, message: 'Participant not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.meetingParticipant.delete({
|
await prisma.meetingParticipant.delete({
|
||||||
where: {
|
where: {
|
||||||
|
@ -279,124 +162,44 @@ export const DELETE = auth(async (req, { params }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json({
|
return returnZodTypeCheckedResponse(
|
||||||
success: true,
|
SuccessResponseSchema,
|
||||||
message: 'Participant removed successfully',
|
{ success: true, message: 'Participant removed successfully' },
|
||||||
});
|
{ status: 200 },
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/event/{eventID}/participant/{user}:
|
|
||||||
* patch:
|
|
||||||
* summary: Update a participant's status in an event
|
|
||||||
* description: Updates the status of a participant in an event. Only the participant can update their own status.
|
|
||||||
* tags:
|
|
||||||
* - Event_Participant
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: eventID
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The ID of the event.
|
|
||||||
* - in: path
|
|
||||||
* name: user
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The ID or name of the user whose status is being updated.
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* status:
|
|
||||||
* type: string
|
|
||||||
* enum: [accepted, declined, tentative]
|
|
||||||
* description: The new status of the participant.
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Participant status updated successfully.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* participant:
|
|
||||||
* $ref: "#/components/schemas/Participant"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid request data.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ZodErrorResponse"
|
|
||||||
* 401:
|
|
||||||
* description: Not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Not authenticated
|
|
||||||
* 404:
|
|
||||||
* description: User not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: User not found
|
|
||||||
* 403:
|
|
||||||
* description: Only participant can update their status.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Only participant can update their status
|
|
||||||
*/
|
|
||||||
export const PATCH = auth(async (req, { params }) => {
|
export const PATCH = auth(async (req, { params }) => {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dbUser = await prisma.user.findUnique({
|
const dbUser = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: req.auth.user.id,
|
id: authCheck.user.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dbUser) {
|
if (!dbUser)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'User not found' },
|
{ success: false, message: 'User not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const eventID = (await params).eventID;
|
const eventID = (await params).eventID;
|
||||||
const user = (await params).user;
|
const user = (await params).user;
|
||||||
|
|
||||||
if (dbUser.id !== user && dbUser.name !== user) {
|
if (dbUser.id !== user && dbUser.name !== user)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'You can only update your own participation' },
|
{ success: false, message: 'You can only update your own participation' },
|
||||||
{ status: 403 },
|
{ status: 403 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const participant = await prisma.meetingParticipant.findUnique({
|
const participant = await prisma.meetingParticipant.findUnique({
|
||||||
where: {
|
where: {
|
||||||
|
@ -410,23 +213,28 @@ export const PATCH = auth(async (req, { params }) => {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
status: true,
|
status: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!participant) {
|
if (!participant)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'Participant not found' },
|
{ success: false, message: 'Participant not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const body = await req.json();
|
const body = await req.json();
|
||||||
const parsedBody = patchParticipantSchema.safeParse(body);
|
const parsedBody = await updateParticipantSchema.safeParseAsync(body);
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ZodErrorResponseSchema,
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Invalid request body',
|
message: 'Invalid request body',
|
||||||
|
@ -434,10 +242,9 @@ export const PATCH = auth(async (req, { params }) => {
|
||||||
},
|
},
|
||||||
{ status: 400 },
|
{ status: 400 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
const { status } = parsedBody.data;
|
const { status } = parsedBody.data;
|
||||||
|
|
||||||
await prisma.meetingParticipant.update({
|
const updatedParticipant = await prisma.meetingParticipant.update({
|
||||||
where: {
|
where: {
|
||||||
meeting_id_user_id: {
|
meeting_id_user_id: {
|
||||||
meeting_id: eventID,
|
meeting_id: eventID,
|
||||||
|
@ -447,10 +254,23 @@ export const PATCH = auth(async (req, { params }) => {
|
||||||
data: {
|
data: {
|
||||||
status,
|
status,
|
||||||
},
|
},
|
||||||
|
select: {
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
status: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json({
|
return returnZodTypeCheckedResponse(ParticipantResponseSchema, {
|
||||||
success: true,
|
success: true,
|
||||||
participant,
|
participant: updatedParticipant,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
102
src/app/api/event/[eventID]/participant/[user]/swagger.ts
Normal file
102
src/app/api/event/[eventID]/participant/[user]/swagger.ts
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
import {
|
||||||
|
ParticipantResponseSchema,
|
||||||
|
updateParticipantSchema,
|
||||||
|
} from '../validation';
|
||||||
|
import {
|
||||||
|
invalidRequestDataResponse,
|
||||||
|
notAuthenticatedResponse,
|
||||||
|
serverReturnedDataValidationErrorResponse,
|
||||||
|
userNotFoundResponse,
|
||||||
|
} from '@/lib/defaultApiResponses';
|
||||||
|
import {
|
||||||
|
EventIdParamSchema,
|
||||||
|
UserIdParamSchema,
|
||||||
|
SuccessResponseSchema,
|
||||||
|
} from '@/app/api/validation';
|
||||||
|
|
||||||
|
export default function registerSwaggerPaths(registry: OpenAPIRegistry) {
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'get',
|
||||||
|
path: '/event/{eventID}/participant/{user}',
|
||||||
|
request: {
|
||||||
|
params: zod.object({
|
||||||
|
eventID: EventIdParamSchema,
|
||||||
|
user: UserIdParamSchema,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'Get a participant for the event',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: ParticipantResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event Participant'],
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'delete',
|
||||||
|
path: '/event/{eventID}/participant/{user}',
|
||||||
|
request: {
|
||||||
|
params: zod.object({
|
||||||
|
eventID: EventIdParamSchema,
|
||||||
|
user: UserIdParamSchema,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'Participant removed successfully',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: SuccessResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event Participant'],
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'patch',
|
||||||
|
path: '/event/{eventID}/participant/{user}',
|
||||||
|
request: {
|
||||||
|
params: zod.object({
|
||||||
|
eventID: EventIdParamSchema,
|
||||||
|
user: UserIdParamSchema,
|
||||||
|
}),
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: updateParticipantSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'Participant updated successfully',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: ParticipantResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
...invalidRequestDataResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event Participant'],
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,94 +1,40 @@
|
||||||
import { prisma } from '@/prisma';
|
import { prisma } from '@/prisma';
|
||||||
import { auth } from '@/auth';
|
import { auth } from '@/auth';
|
||||||
import { NextResponse } from 'next/server';
|
import {
|
||||||
import { z } from 'zod/v4';
|
returnZodTypeCheckedResponse,
|
||||||
import { userIdSchema } from '@/lib/validation/user';
|
userAuthenticated,
|
||||||
|
} from '@/lib/apiHelpers';
|
||||||
|
import {
|
||||||
|
ErrorResponseSchema,
|
||||||
|
ZodErrorResponseSchema,
|
||||||
|
} from '@/app/api/validation';
|
||||||
|
import {
|
||||||
|
inviteParticipantSchema,
|
||||||
|
ParticipantResponseSchema,
|
||||||
|
ParticipantsResponseSchema,
|
||||||
|
} from './validation';
|
||||||
|
|
||||||
const postParticipantSchema = z.object({
|
|
||||||
userId: userIdSchema,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/event/{eventID}/participant:
|
|
||||||
* get:
|
|
||||||
* summary: Get participants of an event
|
|
||||||
* description: Returns all participants of a specific event.
|
|
||||||
* tags:
|
|
||||||
* - Event_Participant
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: eventID
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The ID of the event.
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: List of participants for the event.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* participants:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Participant"
|
|
||||||
* 401:
|
|
||||||
* description: Not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Not authenticated
|
|
||||||
* 404:
|
|
||||||
* description: User not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: User not found
|
|
||||||
* 403:
|
|
||||||
* description: User is not a participant or organizer of this event.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: User is not a participant or organizer of this event
|
|
||||||
*/
|
|
||||||
export const GET = auth(async (req, { params }) => {
|
export const GET = auth(async (req, { params }) => {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dbUser = await prisma.user.findUnique({
|
const dbUser = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: req.auth.user.id,
|
id: authCheck.user.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dbUser) {
|
if (!dbUser)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'User not found' },
|
{ success: false, message: 'User not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const eventID = (await params).eventID;
|
const eventID = (await params).eventID;
|
||||||
|
|
||||||
|
@ -97,6 +43,9 @@ export const GET = auth(async (req, { params }) => {
|
||||||
meeting_id: eventID,
|
meeting_id: eventID,
|
||||||
user_id: dbUser.id,
|
user_id: dbUser.id,
|
||||||
},
|
},
|
||||||
|
select: {
|
||||||
|
status: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const isOrganizer = await prisma.meeting.findFirst({
|
const isOrganizer = await prisma.meeting.findFirst({
|
||||||
|
@ -104,17 +53,20 @@ export const GET = auth(async (req, { params }) => {
|
||||||
id: eventID,
|
id: eventID,
|
||||||
organizer_id: dbUser.id,
|
organizer_id: dbUser.id,
|
||||||
},
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isParticipant && !isOrganizer) {
|
if (!isParticipant && !isOrganizer)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
message: 'User is not a participant or organizer of this event',
|
message: 'User is not a participant or organizer of this event',
|
||||||
},
|
},
|
||||||
{ status: 403 },
|
{ status: 403 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const participants = await prisma.meetingParticipant.findMany({
|
const participants = await prisma.meetingParticipant.findMany({
|
||||||
where: {
|
where: {
|
||||||
|
@ -125,122 +77,43 @@ export const GET = auth(async (req, { params }) => {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
status: true,
|
status: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json({
|
return returnZodTypeCheckedResponse(ParticipantsResponseSchema, {
|
||||||
success: true,
|
success: true,
|
||||||
participants,
|
participants,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/event/{eventID}/participant:
|
|
||||||
* post:
|
|
||||||
* summary: Add a participant to an event
|
|
||||||
* description: Adds a user as a participant to a specific event.
|
|
||||||
* tags:
|
|
||||||
* - Event_Participant
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: eventID
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The ID of the event.
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* userId:
|
|
||||||
* type: string
|
|
||||||
* description: The ID of the user to add as a participant.
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Participant added successfully.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* participant:
|
|
||||||
* $ref: "#/components/schemas/Participant"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid request data.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ZodErrorResponse"
|
|
||||||
* 401:
|
|
||||||
* description: Not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Not authenticated
|
|
||||||
* 404:
|
|
||||||
* description: User not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: User not found
|
|
||||||
* 403:
|
|
||||||
* description: Only organizers can add participants.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Only organizers can add participants
|
|
||||||
* 409:
|
|
||||||
* description: User is already a participant of this event.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: User is already a participant of this event
|
|
||||||
*/
|
|
||||||
export const POST = auth(async (req, { params }) => {
|
export const POST = auth(async (req, { params }) => {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dbUser = await prisma.user.findUnique({
|
const dbUser = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: req.auth.user.id,
|
id: authCheck.user.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dbUser) {
|
if (!dbUser)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'User not found' },
|
{ success: false, message: 'User not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const eventID = (await params).eventID;
|
const eventID = (await params).eventID;
|
||||||
|
|
||||||
|
@ -251,17 +124,18 @@ export const POST = auth(async (req, { params }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isOrganizer) {
|
if (!isOrganizer)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'Only organizers can add participants' },
|
{ success: false, message: 'Only organizers can add participants' },
|
||||||
{ status: 403 },
|
{ status: 403 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const dataRaw = await req.json();
|
const dataRaw = await req.json();
|
||||||
const data = await postParticipantSchema.safeParseAsync(dataRaw);
|
const data = await inviteParticipantSchema.safeParseAsync(dataRaw);
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ZodErrorResponseSchema,
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Invalid request data',
|
message: 'Invalid request data',
|
||||||
|
@ -270,41 +144,55 @@ export const POST = auth(async (req, { params }) => {
|
||||||
{ status: 400 },
|
{ status: 400 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const { userId } = data.data;
|
const { user_id } = data.data;
|
||||||
|
|
||||||
const participantExists = await prisma.meetingParticipant.findFirst({
|
const participantExists = await prisma.meetingParticipant.findFirst({
|
||||||
where: {
|
where: {
|
||||||
meeting_id: eventID,
|
meeting_id: eventID,
|
||||||
user_id: userId,
|
user_id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (participantExists) {
|
if (participantExists)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
message: 'User is already a participant of this event',
|
message: 'User is already a participant of this event',
|
||||||
},
|
},
|
||||||
{ status: 409 },
|
{ status: 409 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const newParticipant = await prisma.meetingParticipant.create({
|
const newParticipant = await prisma.meetingParticipant.create({
|
||||||
data: {
|
data: {
|
||||||
meeting_id: eventID,
|
meeting_id: eventID,
|
||||||
user_id: userId,
|
user_id,
|
||||||
},
|
},
|
||||||
include: {
|
select: {
|
||||||
user: true,
|
user: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
status: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json({
|
return returnZodTypeCheckedResponse(ParticipantResponseSchema, {
|
||||||
success: true,
|
success: true,
|
||||||
participant: {
|
participant: {
|
||||||
user: {
|
user: {
|
||||||
id: newParticipant.user.id,
|
id: newParticipant.user.id,
|
||||||
name: newParticipant.user.name,
|
name: newParticipant.user.name,
|
||||||
|
first_name: newParticipant.user.first_name,
|
||||||
|
last_name: newParticipant.user.last_name,
|
||||||
|
image: newParticipant.user.image,
|
||||||
|
timezone: newParticipant.user.timezone,
|
||||||
},
|
},
|
||||||
status: newParticipant.status,
|
status: newParticipant.status,
|
||||||
},
|
},
|
||||||
|
|
72
src/app/api/event/[eventID]/participant/swagger.ts
Normal file
72
src/app/api/event/[eventID]/participant/swagger.ts
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
import {
|
||||||
|
ParticipantsResponseSchema,
|
||||||
|
ParticipantResponseSchema,
|
||||||
|
inviteParticipantSchema,
|
||||||
|
} from './validation';
|
||||||
|
import {
|
||||||
|
invalidRequestDataResponse,
|
||||||
|
notAuthenticatedResponse,
|
||||||
|
serverReturnedDataValidationErrorResponse,
|
||||||
|
userNotFoundResponse,
|
||||||
|
} from '@/lib/defaultApiResponses';
|
||||||
|
import { EventIdParamSchema } from '@/app/api/validation';
|
||||||
|
|
||||||
|
export default function registerSwaggerPaths(registry: OpenAPIRegistry) {
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'get',
|
||||||
|
path: '/event/{eventID}/participant',
|
||||||
|
request: {
|
||||||
|
params: zod.object({
|
||||||
|
eventID: EventIdParamSchema,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'List participants for the event',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: ParticipantsResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event Participant'],
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'post',
|
||||||
|
path: '/event/{eventID}/participant',
|
||||||
|
request: {
|
||||||
|
params: zod.object({
|
||||||
|
eventID: EventIdParamSchema,
|
||||||
|
}),
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: inviteParticipantSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'Participant invited successfully',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: ParticipantResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...invalidRequestDataResponse,
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event Participant'],
|
||||||
|
});
|
||||||
|
}
|
50
src/app/api/event/[eventID]/participant/validation.ts
Normal file
50
src/app/api/event/[eventID]/participant/validation.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
import {
|
||||||
|
existingUserIdServerSchema,
|
||||||
|
PublicUserSchema,
|
||||||
|
} from '@/app/api/user/validation';
|
||||||
|
|
||||||
|
extendZodWithOpenApi(zod);
|
||||||
|
|
||||||
|
export const participantStatusSchema = zod.enum([
|
||||||
|
'ACCEPTED',
|
||||||
|
'DECLINED',
|
||||||
|
'TENTATIVE',
|
||||||
|
'PENDING',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const inviteParticipantSchema = zod
|
||||||
|
.object({
|
||||||
|
user_id: existingUserIdServerSchema,
|
||||||
|
})
|
||||||
|
.openapi('InviteParticipant', {
|
||||||
|
description: 'Schema for inviting a participant to an event',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateParticipantSchema = zod
|
||||||
|
.object({
|
||||||
|
status: participantStatusSchema,
|
||||||
|
})
|
||||||
|
.openapi('UpdateParticipant', {
|
||||||
|
description: 'Schema for updating participant status in an event',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ParticipantSchema = zod
|
||||||
|
.object({
|
||||||
|
user: PublicUserSchema,
|
||||||
|
status: participantStatusSchema,
|
||||||
|
})
|
||||||
|
.openapi('Participant', {
|
||||||
|
description: 'Participant information including user and status',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ParticipantResponseSchema = zod.object({
|
||||||
|
success: zod.boolean(),
|
||||||
|
participant: ParticipantSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ParticipantsResponseSchema = zod.object({
|
||||||
|
success: zod.boolean(),
|
||||||
|
participants: zod.array(ParticipantSchema),
|
||||||
|
});
|
|
@ -1,90 +1,48 @@
|
||||||
import { prisma } from '@/prisma';
|
import { prisma } from '@/prisma';
|
||||||
import { auth } from '@/auth';
|
import { auth } from '@/auth';
|
||||||
import { NextResponse } from 'next/server';
|
import {
|
||||||
import { z } from 'zod/v4';
|
returnZodTypeCheckedResponse,
|
||||||
|
userAuthenticated,
|
||||||
|
} from '@/lib/apiHelpers';
|
||||||
|
import {
|
||||||
|
ErrorResponseSchema,
|
||||||
|
SuccessResponseSchema,
|
||||||
|
ZodErrorResponseSchema,
|
||||||
|
} from '../../validation';
|
||||||
|
import { EventResponseSchema } from '../validation';
|
||||||
|
import { updateEventSchema } from '../validation';
|
||||||
|
|
||||||
const patchEventSchema = z.object({
|
|
||||||
title: z.string().optional(),
|
|
||||||
description: z.string().optional(),
|
|
||||||
start_time: z.iso.datetime().optional(),
|
|
||||||
end_time: z.iso.datetime().optional(),
|
|
||||||
location: z.string().optional(),
|
|
||||||
status: z.enum(['TENTATIVE', 'CONFIRMED', 'CANCELLED']).optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/event/{eventID}:
|
|
||||||
* get:
|
|
||||||
* summary: Get details of a specific event
|
|
||||||
* description: Returns the details of an event by its ID.
|
|
||||||
* tags:
|
|
||||||
* - Event
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: eventID
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The ID of the event to retrieve.
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Event details retrieved successfully.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* event:
|
|
||||||
* $ref: "#/components/schemas/Event"
|
|
||||||
* 401:
|
|
||||||
* description: Not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Not authenticated
|
|
||||||
* 404:
|
|
||||||
* description: User or event not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
*/
|
|
||||||
export const GET = auth(async (req, { params }) => {
|
export const GET = auth(async (req, { params }) => {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dbUser = await prisma.user.findUnique({
|
const dbUser = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: req.auth.user.id,
|
id: authCheck.user.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dbUser) {
|
if (!dbUser)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'User not found' },
|
{ success: false, message: 'User not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const eventID = (await params).eventID;
|
const eventID = (await params).eventID;
|
||||||
|
|
||||||
const event = await prisma.meeting.findUnique({
|
const event = await prisma.meeting.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: eventID,
|
id: eventID,
|
||||||
|
OR: [
|
||||||
|
{ organizer_id: dbUser.id },
|
||||||
|
{ participants: { some: { user_id: dbUser.id } } },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
|
@ -101,6 +59,10 @@ export const GET = auth(async (req, { params }) => {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
participants: {
|
participants: {
|
||||||
|
@ -109,6 +71,10 @@ export const GET = auth(async (req, { params }) => {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
status: true,
|
status: true,
|
||||||
|
@ -117,120 +83,67 @@ export const GET = auth(async (req, { params }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!event) {
|
if (!event)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'Event not found' },
|
{ success: false, message: 'Event not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
{
|
EventResponseSchema,
|
||||||
success: true,
|
{ success: true, event },
|
||||||
event: event,
|
|
||||||
},
|
|
||||||
{ status: 200 },
|
{ status: 200 },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/event/{eventID}:
|
|
||||||
* delete:
|
|
||||||
* summary: Delete a specific event
|
|
||||||
* description: Deletes an event by its ID if the user is the organizer.
|
|
||||||
* tags:
|
|
||||||
* - Event
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: eventID
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The ID of the event to delete.
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Event deleted successfully.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* message:
|
|
||||||
* type: string
|
|
||||||
* 401:
|
|
||||||
* description: Not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Not authenticated
|
|
||||||
* 403:
|
|
||||||
* description: User is not the organizer.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: You are not the organizer of this event
|
|
||||||
* 404:
|
|
||||||
* description: User or event not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
*/
|
|
||||||
export const DELETE = auth(async (req, { params }) => {
|
export const DELETE = auth(async (req, { params }) => {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dbUser = await prisma.user.findUnique({
|
const dbUser = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: req.auth.user.id,
|
id: authCheck.user.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dbUser) {
|
if (!dbUser)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'User not found' },
|
{ success: false, message: 'User not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const eventID = (await params).eventID;
|
const eventID = (await params).eventID;
|
||||||
|
|
||||||
const event = await prisma.meeting.findUnique({
|
const event = await prisma.meeting.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: eventID,
|
id: eventID,
|
||||||
|
OR: [
|
||||||
|
{ organizer_id: dbUser.id },
|
||||||
|
{ participants: { some: { user_id: dbUser.id } } },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!event) {
|
if (!event)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'Event not found' },
|
{ success: false, message: 'Event not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (event.organizer_id !== dbUser.id) {
|
if (event.organizer_id !== dbUser.id)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'You are not the organizer of this event' },
|
{ success: false, message: 'You are not the organizer of this event' },
|
||||||
{ status: 403 },
|
{ status: 403 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.meeting.delete({
|
await prisma.meeting.delete({
|
||||||
where: {
|
where: {
|
||||||
|
@ -238,146 +151,66 @@ export const DELETE = auth(async (req, { params }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
SuccessResponseSchema,
|
||||||
{ success: true, message: 'Event deleted successfully' },
|
{ success: true, message: 'Event deleted successfully' },
|
||||||
{ status: 200 },
|
{ status: 200 },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/event/{eventID}:
|
|
||||||
* patch:
|
|
||||||
* summary: Update a specific event
|
|
||||||
* description: Updates an event by its ID if the user is the organizer.
|
|
||||||
* tags:
|
|
||||||
* - Event
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: eventID
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The ID of the event to update.
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* title:
|
|
||||||
* type: string
|
|
||||||
* description:
|
|
||||||
* type: string
|
|
||||||
* start_time:
|
|
||||||
* type: string
|
|
||||||
* format: date-time
|
|
||||||
* end_time:
|
|
||||||
* type: string
|
|
||||||
* format: date-time
|
|
||||||
* location:
|
|
||||||
* type: string
|
|
||||||
* status:
|
|
||||||
* type: string
|
|
||||||
* enum: [TENTATIVE, CONFIRMED, CANCELLED]
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Event updated successfully.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* event:
|
|
||||||
* $ref: "#/components/schemas/Event"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid request data.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ZodErrorResponse"
|
|
||||||
* 401:
|
|
||||||
* description: Not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Not authenticated
|
|
||||||
* 403:
|
|
||||||
* description: User is not the organizer.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: You are not the organizer of this event
|
|
||||||
* 404:
|
|
||||||
* description: User or event not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: User or event not found
|
|
||||||
*/
|
|
||||||
export const PATCH = auth(async (req, { params }) => {
|
export const PATCH = auth(async (req, { params }) => {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dbUser = await prisma.user.findUnique({
|
const dbUser = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: req.auth.user.id,
|
id: authCheck.user.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dbUser) {
|
if (!dbUser)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'User not found' },
|
{ success: false, message: 'User not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const eventID = (await params).eventID;
|
const eventID = (await params).eventID;
|
||||||
|
|
||||||
const event = await prisma.meeting.findUnique({
|
const event = await prisma.meeting.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: eventID,
|
id: eventID,
|
||||||
|
OR: [
|
||||||
|
{ organizer_id: dbUser.id },
|
||||||
|
{ participants: { some: { user_id: dbUser.id } } },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!event) {
|
if (!event)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'Event not found' },
|
{ success: false, message: 'Event not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (event.organizer_id !== dbUser.id) {
|
if (event.organizer_id !== dbUser.id)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'You are not the organizer of this event' },
|
{ success: false, message: 'You are not the organizer of this event' },
|
||||||
{ status: 403 },
|
{ status: 403 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const dataRaw = await req.json();
|
const dataRaw = await req.json();
|
||||||
const data = await patchEventSchema.safeParseAsync(dataRaw);
|
const data = await updateEventSchema.safeParseAsync(dataRaw);
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ZodErrorResponseSchema,
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Invalid input data',
|
message: 'Invalid input data',
|
||||||
|
@ -416,6 +249,10 @@ export const PATCH = auth(async (req, { params }) => {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
participants: {
|
participants: {
|
||||||
|
@ -424,6 +261,10 @@ export const PATCH = auth(async (req, { params }) => {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
status: true,
|
status: true,
|
||||||
|
@ -432,7 +273,8 @@ export const PATCH = auth(async (req, { params }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
EventResponseSchema,
|
||||||
{
|
{
|
||||||
success: true,
|
success: true,
|
||||||
event: updatedEvent,
|
event: updatedEvent,
|
||||||
|
|
94
src/app/api/event/[eventID]/swagger.ts
Normal file
94
src/app/api/event/[eventID]/swagger.ts
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import { EventResponseSchema, updateEventSchema } from '../validation';
|
||||||
|
import {
|
||||||
|
invalidRequestDataResponse,
|
||||||
|
notAuthenticatedResponse,
|
||||||
|
serverReturnedDataValidationErrorResponse,
|
||||||
|
userNotFoundResponse,
|
||||||
|
} from '@/lib/defaultApiResponses';
|
||||||
|
import {
|
||||||
|
EventIdParamSchema,
|
||||||
|
SuccessResponseSchema,
|
||||||
|
} from '@/app/api/validation';
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
|
||||||
|
export default function registerSwaggerPaths(registry: OpenAPIRegistry) {
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'get',
|
||||||
|
path: '/event/{eventID}',
|
||||||
|
request: {
|
||||||
|
params: zod.object({
|
||||||
|
eventID: EventIdParamSchema,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'Event retrieved successfully',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: EventResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event'],
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'delete',
|
||||||
|
path: '/event/{eventID}',
|
||||||
|
request: {
|
||||||
|
params: zod.object({
|
||||||
|
eventID: EventIdParamSchema,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'Event deleted successfully',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: SuccessResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event'],
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'patch',
|
||||||
|
path: '/event/{eventID}',
|
||||||
|
request: {
|
||||||
|
params: zod.object({
|
||||||
|
eventID: EventIdParamSchema,
|
||||||
|
}),
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: updateEventSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'Event updated successfully',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: EventResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...invalidRequestDataResponse,
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event'],
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,95 +1,34 @@
|
||||||
import { prisma } from '@/prisma';
|
import { prisma } from '@/prisma';
|
||||||
import { auth } from '@/auth';
|
import { auth } from '@/auth';
|
||||||
import { NextResponse } from 'next/server';
|
import {
|
||||||
import { z } from 'zod/v4';
|
returnZodTypeCheckedResponse,
|
||||||
|
userAuthenticated,
|
||||||
|
} from '@/lib/apiHelpers';
|
||||||
|
import { ErrorResponseSchema, ZodErrorResponseSchema } from '../validation';
|
||||||
|
import {
|
||||||
|
createEventSchema,
|
||||||
|
EventResponseSchema,
|
||||||
|
EventsResponseSchema,
|
||||||
|
} from './validation';
|
||||||
|
|
||||||
const postEventSchema = z
|
|
||||||
.object({
|
|
||||||
title: z.string().min(1, 'Title is required'),
|
|
||||||
description: z.string().optional(),
|
|
||||||
start_time: z.iso.datetime(),
|
|
||||||
end_time: z.iso.datetime(),
|
|
||||||
location: z.string().optional().default(''),
|
|
||||||
participants: z.array(z.string()).optional(),
|
|
||||||
})
|
|
||||||
.refine((data) => new Date(data.start_time) < new Date(data.end_time), {
|
|
||||||
error: 'Start time must be before end time',
|
|
||||||
})
|
|
||||||
.refine(
|
|
||||||
async (data) =>
|
|
||||||
!data.participants ||
|
|
||||||
(await Promise.all(
|
|
||||||
data.participants.map(async (userId) => {
|
|
||||||
const user = await prisma.user.findUnique({ where: { id: userId } });
|
|
||||||
return !!user;
|
|
||||||
}),
|
|
||||||
).then((results) => results.every(Boolean))),
|
|
||||||
{
|
|
||||||
error: 'One or more participant user IDs are invalid',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/event:
|
|
||||||
* get:
|
|
||||||
* summary: Get all events for the authenticated user
|
|
||||||
* description: Returns all events where the user is an organizer or a participant.
|
|
||||||
* tags:
|
|
||||||
* - Event
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: List of events for the user.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* events:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Event"
|
|
||||||
* 401:
|
|
||||||
* description: Not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Not authenticated
|
|
||||||
* 404:
|
|
||||||
* description: User not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: User not found
|
|
||||||
*/
|
|
||||||
export const GET = auth(async (req) => {
|
export const GET = auth(async (req) => {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dbUser = await prisma.user.findUnique({
|
const dbUser = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: req.auth.user.id,
|
id: authCheck.user.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dbUser)
|
if (!dbUser)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
{ success: false, message: 'User not found' },
|
{ success: false, message: 'User not found' },
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
|
@ -115,6 +54,10 @@ export const GET = auth(async (req) => {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
participants: {
|
participants: {
|
||||||
|
@ -123,6 +66,10 @@ export const GET = auth(async (req) => {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
status: true,
|
status: true,
|
||||||
|
@ -131,101 +78,30 @@ export const GET = auth(async (req) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json({
|
return returnZodTypeCheckedResponse(
|
||||||
success: true,
|
EventsResponseSchema,
|
||||||
events: userEvents,
|
{
|
||||||
});
|
success: true,
|
||||||
|
events: userEvents,
|
||||||
|
},
|
||||||
|
{ status: 200 },
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/event:
|
|
||||||
* post:
|
|
||||||
* summary: Create a new event
|
|
||||||
* description: Creates a new event as the authenticated user (organizer).
|
|
||||||
* tags:
|
|
||||||
* - Event
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* required:
|
|
||||||
* - title
|
|
||||||
* - start_time
|
|
||||||
* - end_time
|
|
||||||
* properties:
|
|
||||||
* title:
|
|
||||||
* type: string
|
|
||||||
* description:
|
|
||||||
* type: string
|
|
||||||
* start_time:
|
|
||||||
* type: string
|
|
||||||
* format: date-time
|
|
||||||
* end_time:
|
|
||||||
* type: string
|
|
||||||
* format: date-time
|
|
||||||
* location:
|
|
||||||
* type: string
|
|
||||||
* participants:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* type: string
|
|
||||||
* description: User ID of a participant
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Event created successfully.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* event:
|
|
||||||
* $ref: "#/components/schemas/Event"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid request data.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ZodErrorResponse"
|
|
||||||
* 401:
|
|
||||||
* description: Not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: Not authenticated
|
|
||||||
* 404:
|
|
||||||
* description: User not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: User not found
|
|
||||||
*/
|
|
||||||
export const POST = auth(async (req) => {
|
export const POST = auth(async (req) => {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dataRaw = await req.json();
|
const dataRaw = await req.json();
|
||||||
const data = await postEventSchema.safeParseAsync(dataRaw);
|
const data = await createEventSchema.safeParseAsync(dataRaw);
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ZodErrorResponseSchema,
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Invalid request data',
|
message: 'Invalid request data',
|
||||||
|
@ -244,7 +120,7 @@ export const POST = auth(async (req) => {
|
||||||
start_time,
|
start_time,
|
||||||
end_time,
|
end_time,
|
||||||
location,
|
location,
|
||||||
organizer_id: req.auth.user.id,
|
organizer_id: authCheck.user.id!,
|
||||||
participants: participants
|
participants: participants
|
||||||
? {
|
? {
|
||||||
create: participants.map((userId) => ({
|
create: participants.map((userId) => ({
|
||||||
|
@ -267,6 +143,10 @@ export const POST = auth(async (req) => {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
participants: {
|
participants: {
|
||||||
|
@ -275,6 +155,10 @@ export const POST = auth(async (req) => {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
status: true,
|
status: true,
|
||||||
|
@ -283,8 +167,12 @@ export const POST = auth(async (req) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json({
|
return returnZodTypeCheckedResponse(
|
||||||
success: true,
|
EventResponseSchema,
|
||||||
event: newEvent,
|
{
|
||||||
});
|
success: true,
|
||||||
|
event: newEvent,
|
||||||
|
},
|
||||||
|
{ status: 201 },
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
62
src/app/api/event/swagger.ts
Normal file
62
src/app/api/event/swagger.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import {
|
||||||
|
EventResponseSchema,
|
||||||
|
EventsResponseSchema,
|
||||||
|
createEventSchema,
|
||||||
|
} from './validation';
|
||||||
|
import {
|
||||||
|
invalidRequestDataResponse,
|
||||||
|
notAuthenticatedResponse,
|
||||||
|
serverReturnedDataValidationErrorResponse,
|
||||||
|
userNotFoundResponse,
|
||||||
|
} from '@/lib/defaultApiResponses';
|
||||||
|
|
||||||
|
export default function registerSwaggerPaths(registry: OpenAPIRegistry) {
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'get',
|
||||||
|
path: '/api/event',
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'List of events for the authenticated user',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: EventsResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event'],
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'post',
|
||||||
|
path: '/api/event',
|
||||||
|
request: {
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: createEventSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
201: {
|
||||||
|
description: 'Event created successfully.',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: EventResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...invalidRequestDataResponse,
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event'],
|
||||||
|
});
|
||||||
|
}
|
168
src/app/api/event/validation.ts
Normal file
168
src/app/api/event/validation.ts
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
import {
|
||||||
|
existingUserIdServerSchema,
|
||||||
|
PublicUserSchema,
|
||||||
|
} from '../user/validation';
|
||||||
|
import { ParticipantSchema } from './[eventID]/participant/validation';
|
||||||
|
|
||||||
|
extendZodWithOpenApi(zod);
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Event ID Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const eventIdSchema = zod.string().min(1, 'Event ID is required');
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Event Title Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const eventTitleSchema = zod
|
||||||
|
.string()
|
||||||
|
.min(1, 'Title is required')
|
||||||
|
.max(100, 'Title must be at most 100 characters long');
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Event Description Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const eventDescriptionSchema = zod
|
||||||
|
.string()
|
||||||
|
.max(500, 'Description must be at most 500 characters long');
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Event start time Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const eventStartTimeSchema = zod.iso
|
||||||
|
.datetime()
|
||||||
|
.or(zod.date().transform((date) => date.toISOString()))
|
||||||
|
.refine((date) => !isNaN(new Date(date).getTime()), {
|
||||||
|
message: 'Invalid start time',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Event end time Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const eventEndTimeSchema = zod.iso.datetime().or(
|
||||||
|
zod
|
||||||
|
.date()
|
||||||
|
.transform((date) => date.toISOString())
|
||||||
|
.refine((date) => !isNaN(new Date(date).getTime()), {
|
||||||
|
message: 'Invalid end time',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Event Location Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const eventLocationSchema = zod
|
||||||
|
.string()
|
||||||
|
.max(200, 'Location must be at most 200 characters long');
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Event Participants Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const eventParticipantsSchema = zod.array(existingUserIdServerSchema);
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Event Status Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const eventStatusSchema = zod.enum([
|
||||||
|
'TENTATIVE',
|
||||||
|
'CONFIRMED',
|
||||||
|
'CANCELLED',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Create Event Schema
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const createEventSchema = zod
|
||||||
|
.object({
|
||||||
|
title: eventTitleSchema,
|
||||||
|
description: eventDescriptionSchema.optional(),
|
||||||
|
start_time: eventStartTimeSchema,
|
||||||
|
end_time: eventEndTimeSchema,
|
||||||
|
location: eventLocationSchema.optional().default(''),
|
||||||
|
participants: eventParticipantsSchema.optional(),
|
||||||
|
status: eventStatusSchema.optional().default('TENTATIVE'),
|
||||||
|
})
|
||||||
|
.refine((data) => new Date(data.start_time) < new Date(data.end_time), {
|
||||||
|
message: 'Start time must be before end time',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Update Event Schema
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const updateEventSchema = zod
|
||||||
|
.object({
|
||||||
|
id: eventIdSchema,
|
||||||
|
title: eventTitleSchema.optional(),
|
||||||
|
description: eventDescriptionSchema.optional(),
|
||||||
|
start_time: eventStartTimeSchema.optional(),
|
||||||
|
end_time: eventEndTimeSchema.optional(),
|
||||||
|
location: eventLocationSchema.optional().default(''),
|
||||||
|
participants: eventParticipantsSchema.optional(),
|
||||||
|
status: eventStatusSchema.optional(),
|
||||||
|
})
|
||||||
|
.refine(
|
||||||
|
(data) => {
|
||||||
|
if (data.start_time && data.end_time) {
|
||||||
|
return new Date(data.start_time) < new Date(data.end_time);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: 'Start time must be before end time',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Event Schema Validation (for API responses)
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const EventSchema = zod
|
||||||
|
.object({
|
||||||
|
id: eventIdSchema,
|
||||||
|
title: eventTitleSchema,
|
||||||
|
description: eventDescriptionSchema.nullish(),
|
||||||
|
start_time: eventStartTimeSchema,
|
||||||
|
end_time: eventEndTimeSchema,
|
||||||
|
location: eventLocationSchema.nullish(),
|
||||||
|
status: eventStatusSchema,
|
||||||
|
created_at: zod.date(),
|
||||||
|
updated_at: zod.date(),
|
||||||
|
organizer: PublicUserSchema,
|
||||||
|
participants: zod.array(ParticipantSchema).nullish(),
|
||||||
|
})
|
||||||
|
.openapi('Event', {
|
||||||
|
description: 'Event information including all fields',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const EventResponseSchema = zod.object({
|
||||||
|
success: zod.boolean(),
|
||||||
|
event: EventSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const EventsResponseSchema = zod.object({
|
||||||
|
success: zod.boolean(),
|
||||||
|
events: zod.array(EventSchema),
|
||||||
|
});
|
|
@ -1,118 +1,29 @@
|
||||||
import { auth } from '@/auth';
|
import { auth } from '@/auth';
|
||||||
import { NextResponse } from 'next/server';
|
|
||||||
import { prisma } from '@/prisma';
|
import { prisma } from '@/prisma';
|
||||||
import { z } from 'zod/v4';
|
import { searchUserSchema, searchUserResponseSchema } from './validation';
|
||||||
|
import {
|
||||||
|
returnZodTypeCheckedResponse,
|
||||||
|
userAuthenticated,
|
||||||
|
} from '@/lib/apiHelpers';
|
||||||
|
import {
|
||||||
|
ErrorResponseSchema,
|
||||||
|
ZodErrorResponseSchema,
|
||||||
|
} from '@/app/api/validation';
|
||||||
|
|
||||||
const getSearchUserSchema = z.object({
|
|
||||||
query: z.string().optional().default(''),
|
|
||||||
count: z.coerce.number().min(1).max(100).default(10),
|
|
||||||
page: z.coerce.number().min(1).default(1),
|
|
||||||
sort_by: z
|
|
||||||
.enum(['created_at', 'name', 'first_name', 'last_name', 'id'])
|
|
||||||
.optional()
|
|
||||||
.default('created_at'),
|
|
||||||
sort_order: z.enum(['asc', 'desc']).optional().default('desc'),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/search/user:
|
|
||||||
* get:
|
|
||||||
* summary: Search for users
|
|
||||||
* description: Search for users by name, first name, or last name with pagination and sorting.
|
|
||||||
* tags:
|
|
||||||
* - Search
|
|
||||||
* parameters:
|
|
||||||
* - in: query
|
|
||||||
* name: query
|
|
||||||
* required: false
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The search query to filter users by name, first name, or last name.
|
|
||||||
* - in: query
|
|
||||||
* name: count
|
|
||||||
* required: false
|
|
||||||
* schema:
|
|
||||||
* type: integer
|
|
||||||
* description: The number of users to return per page (default is 10).
|
|
||||||
* - in: query
|
|
||||||
* name: page
|
|
||||||
* required: false
|
|
||||||
* schema:
|
|
||||||
* type: integer
|
|
||||||
* description: The page number to return (default is 1).
|
|
||||||
* - in: query
|
|
||||||
* name: sort_by
|
|
||||||
* required: false
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* enum: ["created_at", "name", "first_name", "last_name", "id"]
|
|
||||||
* description: The field to sort by (default is 'created_at').
|
|
||||||
* - in: query
|
|
||||||
* name: sort_order
|
|
||||||
* required: false
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* enum: ["asc", "desc"]
|
|
||||||
* description: The order to sort by, either 'asc' or 'desc' (default is 'desc').
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Users found successfully.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* users:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/PublicUser"
|
|
||||||
* count:
|
|
||||||
* type: integer
|
|
||||||
* description: The number of users returned.
|
|
||||||
* 400:
|
|
||||||
* description: Invalid request data.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ZodErrorResponse"
|
|
||||||
* 401:
|
|
||||||
* description: User is not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: "Not authenticated"
|
|
||||||
* 404:
|
|
||||||
* description: User not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: "User not found"
|
|
||||||
*/
|
|
||||||
export const GET = auth(async function GET(req) {
|
export const GET = auth(async function GET(req) {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dataRaw = Object.fromEntries(new URL(req.url).searchParams);
|
const dataRaw = Object.fromEntries(new URL(req.url).searchParams);
|
||||||
const data = await getSearchUserSchema.safeParseAsync(dataRaw);
|
const data = await searchUserSchema.safeParseAsync(dataRaw);
|
||||||
if (!data.success) {
|
if (!data.success)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ZodErrorResponseSchema,
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Invalid request data',
|
message: 'Invalid request data',
|
||||||
|
@ -120,7 +31,6 @@ export const GET = auth(async function GET(req) {
|
||||||
},
|
},
|
||||||
{ status: 400 },
|
{ status: 400 },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
const { query, count, page, sort_by, sort_order } = data.data;
|
const { query, count, page, sort_by, sort_order } = data.data;
|
||||||
|
|
||||||
const dbUsers = await prisma.user.findMany({
|
const dbUsers = await prisma.user.findMany({
|
||||||
|
@ -156,9 +66,14 @@ export const GET = auth(async function GET(req) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json({
|
return returnZodTypeCheckedResponse(
|
||||||
success: true,
|
searchUserResponseSchema,
|
||||||
users: dbUsers,
|
{
|
||||||
count: userCount,
|
success: true,
|
||||||
});
|
users: dbUsers,
|
||||||
|
total_count: userCount,
|
||||||
|
total_pages: Math.ceil(userCount / count),
|
||||||
|
},
|
||||||
|
{ status: 200 },
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
33
src/app/api/search/user/swagger.ts
Normal file
33
src/app/api/search/user/swagger.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import { searchUserResponseSchema, searchUserSchema } from './validation';
|
||||||
|
import {
|
||||||
|
invalidRequestDataResponse,
|
||||||
|
notAuthenticatedResponse,
|
||||||
|
serverReturnedDataValidationErrorResponse,
|
||||||
|
userNotFoundResponse,
|
||||||
|
} from '@/lib/defaultApiResponses';
|
||||||
|
|
||||||
|
export default function registerSwaggerPaths(registry: OpenAPIRegistry) {
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'get',
|
||||||
|
path: '/api/search/user',
|
||||||
|
request: {
|
||||||
|
query: searchUserSchema,
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'User search results',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: searchUserResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...invalidRequestDataResponse,
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Search'],
|
||||||
|
});
|
||||||
|
}
|
20
src/app/api/search/user/validation.ts
Normal file
20
src/app/api/search/user/validation.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
import { PublicUserSchema } from '../../user/validation';
|
||||||
|
|
||||||
|
export const searchUserSchema = zod.object({
|
||||||
|
query: zod.string().optional().default(''),
|
||||||
|
count: zod.coerce.number().min(1).max(100).default(10),
|
||||||
|
page: zod.coerce.number().min(1).default(1),
|
||||||
|
sort_by: zod
|
||||||
|
.enum(['created_at', 'name', 'first_name', 'last_name', 'id'])
|
||||||
|
.optional()
|
||||||
|
.default('created_at'),
|
||||||
|
sort_order: zod.enum(['asc', 'desc']).optional().default('desc'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const searchUserResponseSchema = zod.object({
|
||||||
|
success: zod.boolean(),
|
||||||
|
users: zod.array(PublicUserSchema),
|
||||||
|
total_count: zod.number(),
|
||||||
|
total_pages: zod.number(),
|
||||||
|
});
|
|
@ -1,64 +1,19 @@
|
||||||
import { auth } from '@/auth';
|
import { auth } from '@/auth';
|
||||||
import { NextResponse } from 'next/server';
|
|
||||||
import { prisma } from '@/prisma';
|
import { prisma } from '@/prisma';
|
||||||
|
import {
|
||||||
|
returnZodTypeCheckedResponse,
|
||||||
|
userAuthenticated,
|
||||||
|
} from '@/lib/apiHelpers';
|
||||||
|
import { PublicUserResponseSchema } from '../validation';
|
||||||
|
import { ErrorResponseSchema } from '@/app/api/validation';
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/user/{user}:
|
|
||||||
* get:
|
|
||||||
* summary: Get user information by ID or name
|
|
||||||
* description: Retrieve the information of a specific user by ID or name.
|
|
||||||
* tags:
|
|
||||||
* - User
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: user
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* description: The ID or name of the user to retrieve.
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: User information retrieved successfully.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: "object"
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: "boolean"
|
|
||||||
* default: true
|
|
||||||
* user:
|
|
||||||
* $ref: "#/components/schemas/PublicUser"
|
|
||||||
* 401:
|
|
||||||
* description: User is not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: "Not authenticated"
|
|
||||||
* 404:
|
|
||||||
* description: User not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: "User not found"
|
|
||||||
*/
|
|
||||||
export const GET = auth(async function GET(req, { params }) {
|
export const GET = auth(async function GET(req, { params }) {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const requestedUser = (await params).user;
|
const requestedUser = (await params).user;
|
||||||
|
@ -75,17 +30,26 @@ export const GET = auth(async function GET(req, { params }) {
|
||||||
created_at: true,
|
created_at: true,
|
||||||
updated_at: true,
|
updated_at: true,
|
||||||
image: true,
|
image: true,
|
||||||
|
timezone: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dbUser)
|
if (!dbUser)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
{ success: false, message: 'User not found' },
|
ErrorResponseSchema,
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: 'User not found',
|
||||||
|
},
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
|
|
||||||
return NextResponse.json({
|
return returnZodTypeCheckedResponse(
|
||||||
success: true,
|
PublicUserResponseSchema,
|
||||||
user: dbUser,
|
{
|
||||||
});
|
success: true,
|
||||||
|
user: dbUser,
|
||||||
|
},
|
||||||
|
{ status: 200 },
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
33
src/app/api/user/[user]/swagger.ts
Normal file
33
src/app/api/user/[user]/swagger.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { PublicUserResponseSchema } from '../validation';
|
||||||
|
import {
|
||||||
|
notAuthenticatedResponse,
|
||||||
|
userNotFoundResponse,
|
||||||
|
} from '@/lib/defaultApiResponses';
|
||||||
|
import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
import { UserIdParamSchema } from '../../validation';
|
||||||
|
|
||||||
|
export default function registerSwaggerPaths(registry: OpenAPIRegistry) {
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'get',
|
||||||
|
path: '/api/user/{user}',
|
||||||
|
request: {
|
||||||
|
params: zod.object({
|
||||||
|
user: UserIdParamSchema,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'User information retrieved successfully.',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: PublicUserResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
},
|
||||||
|
tags: ['User'],
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,78 +1,28 @@
|
||||||
import { auth } from '@/auth';
|
import { auth } from '@/auth';
|
||||||
import { NextResponse } from 'next/server';
|
|
||||||
import { prisma } from '@/prisma';
|
import { prisma } from '@/prisma';
|
||||||
|
import { updateUserServerSchema } from './validation';
|
||||||
import {
|
import {
|
||||||
userEmailSchema,
|
returnZodTypeCheckedResponse,
|
||||||
userFirstNameSchema,
|
userAuthenticated,
|
||||||
userNameSchema,
|
} from '@/lib/apiHelpers';
|
||||||
userLastNameSchema,
|
import { FullUserResponseSchema } from '../validation';
|
||||||
} from '@/lib/validation/user';
|
import {
|
||||||
import { z } from 'zod/v4';
|
ErrorResponseSchema,
|
||||||
|
ZodErrorResponseSchema,
|
||||||
|
} from '@/app/api/validation';
|
||||||
|
|
||||||
const patchUserMeSchema = z.object({
|
|
||||||
name: userNameSchema.optional(),
|
|
||||||
first_name: userFirstNameSchema.optional(),
|
|
||||||
last_name: userLastNameSchema.optional(),
|
|
||||||
email: userEmailSchema.optional(),
|
|
||||||
image: z.string().optional(),
|
|
||||||
timezone: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/user/me:
|
|
||||||
* get:
|
|
||||||
* summary: Get the currently authenticated user's information
|
|
||||||
* description: Retrieve the information of the currently authenticated user.
|
|
||||||
* tags:
|
|
||||||
* - User
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: User information retrieved successfully.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* default: true
|
|
||||||
* user:
|
|
||||||
* $ref: "#/components/schemas/User"
|
|
||||||
* 401:
|
|
||||||
* description: User is not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: "Not authenticated"
|
|
||||||
* 404:
|
|
||||||
* description: User not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: "User not found"
|
|
||||||
*/
|
|
||||||
export const GET = auth(async function GET(req) {
|
export const GET = auth(async function GET(req) {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user || !req.auth.user.id)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dbUser = await prisma.user.findUnique({
|
const dbUser = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: req.auth.user.id,
|
id: authCheck.user.id,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
|
@ -87,109 +37,35 @@ export const GET = auth(async function GET(req) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!dbUser)
|
if (!dbUser)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
{ success: false, message: 'User not found' },
|
ErrorResponseSchema,
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: 'User not found',
|
||||||
|
},
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
|
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(FullUserResponseSchema, {
|
||||||
{
|
success: true,
|
||||||
success: true,
|
user: dbUser,
|
||||||
user: {
|
});
|
||||||
...dbUser,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ status: 200 },
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/user/me:
|
|
||||||
* patch:
|
|
||||||
* summary: Update the currently authenticated user's information
|
|
||||||
* description: Update the information of the currently authenticated user.
|
|
||||||
* tags:
|
|
||||||
* - User
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* name:
|
|
||||||
* type: string
|
|
||||||
* description: Username of the user.
|
|
||||||
* first_name:
|
|
||||||
* type: string
|
|
||||||
* description: First name of the user.
|
|
||||||
* last_name:
|
|
||||||
* type: string
|
|
||||||
* description: Last name of the user.
|
|
||||||
* email:
|
|
||||||
* type: string
|
|
||||||
* description: Email address of the user.
|
|
||||||
* image:
|
|
||||||
* type: string
|
|
||||||
* description: URL of the user's profile image.
|
|
||||||
* timezone:
|
|
||||||
* type: string
|
|
||||||
* description: Timezone of the user.
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: User information updated successfully.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* default: true
|
|
||||||
* user:
|
|
||||||
* $ref: "#/components/schemas/User"
|
|
||||||
* 401:
|
|
||||||
* description: User is not authenticated.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: "Not authenticated"
|
|
||||||
* 404:
|
|
||||||
* description: User not found.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ErrorResponse"
|
|
||||||
* example:
|
|
||||||
* success: false
|
|
||||||
* message: "User not found"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid request data.
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/ZodErrorResponse"
|
|
||||||
*/
|
|
||||||
export const PATCH = auth(async function PATCH(req) {
|
export const PATCH = auth(async function PATCH(req) {
|
||||||
if (!req.auth)
|
const authCheck = userAuthenticated(req);
|
||||||
return NextResponse.json(
|
if (!authCheck.continue)
|
||||||
{ success: false, message: 'Not authenticated' },
|
return returnZodTypeCheckedResponse(
|
||||||
{ status: 401 },
|
ErrorResponseSchema,
|
||||||
);
|
authCheck.response,
|
||||||
if (!req.auth.user)
|
authCheck.metadata,
|
||||||
return NextResponse.json(
|
|
||||||
{ success: false, message: 'User not found' },
|
|
||||||
{ status: 404 },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dataRaw = await req.json();
|
const dataRaw = await req.json();
|
||||||
const data = await patchUserMeSchema.safeParseAsync(dataRaw);
|
const data = await updateUserServerSchema.safeParseAsync(dataRaw);
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
ZodErrorResponseSchema,
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Invalid request data',
|
message: 'Invalid request data',
|
||||||
|
@ -198,19 +74,19 @@ export const PATCH = auth(async function PATCH(req) {
|
||||||
{ status: 400 },
|
{ status: 400 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const { name, first_name, last_name, email, image, timezone } = data.data;
|
if (Object.keys(data.data).length === 0) {
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
|
{ success: false, message: 'No data to update' },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const updatedUser = await prisma.user.update({
|
const updatedUser = await prisma.user.update({
|
||||||
where: {
|
where: {
|
||||||
id: req.auth.user.id,
|
id: authCheck.user.id,
|
||||||
},
|
|
||||||
data: {
|
|
||||||
name,
|
|
||||||
first_name,
|
|
||||||
last_name,
|
|
||||||
email,
|
|
||||||
image,
|
|
||||||
timezone,
|
|
||||||
},
|
},
|
||||||
|
data: data.data,
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
@ -224,11 +100,16 @@ export const PATCH = auth(async function PATCH(req) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!updatedUser)
|
if (!updatedUser)
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
{ success: false, message: 'User not found' },
|
ErrorResponseSchema,
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: 'User not found',
|
||||||
|
},
|
||||||
{ status: 404 },
|
{ status: 404 },
|
||||||
);
|
);
|
||||||
return NextResponse.json(
|
return returnZodTypeCheckedResponse(
|
||||||
|
FullUserResponseSchema,
|
||||||
{
|
{
|
||||||
success: true,
|
success: true,
|
||||||
user: updatedUser,
|
user: updatedUser,
|
||||||
|
|
63
src/app/api/user/me/swagger.ts
Normal file
63
src/app/api/user/me/swagger.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import { FullUserResponseSchema } from '../validation';
|
||||||
|
import { updateUserServerSchema } from './validation';
|
||||||
|
import {
|
||||||
|
invalidRequestDataResponse,
|
||||||
|
notAuthenticatedResponse,
|
||||||
|
serverReturnedDataValidationErrorResponse,
|
||||||
|
userNotFoundResponse,
|
||||||
|
} from '@/lib/defaultApiResponses';
|
||||||
|
|
||||||
|
export default function registerSwaggerPaths(registry: OpenAPIRegistry) {
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'get',
|
||||||
|
path: '/api/user/me',
|
||||||
|
description: 'Get the currently authenticated user',
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'User information retrieved successfully',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: FullUserResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['User'],
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'patch',
|
||||||
|
path: '/api/user/me',
|
||||||
|
description: 'Update the currently authenticated user',
|
||||||
|
request: {
|
||||||
|
body: {
|
||||||
|
description: 'User information to update',
|
||||||
|
required: true,
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: updateUserServerSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'User information updated successfully',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: FullUserResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...invalidRequestDataResponse,
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['User'],
|
||||||
|
});
|
||||||
|
}
|
21
src/app/api/user/me/validation.ts
Normal file
21
src/app/api/user/me/validation.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
import {
|
||||||
|
firstNameSchema,
|
||||||
|
lastNameSchema,
|
||||||
|
newUserEmailServerSchema,
|
||||||
|
newUserNameServerSchema,
|
||||||
|
} from '@/app/api/user/validation';
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Update User Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const updateUserServerSchema = zod.object({
|
||||||
|
name: newUserNameServerSchema.optional(),
|
||||||
|
first_name: firstNameSchema.optional(),
|
||||||
|
last_name: lastNameSchema.optional(),
|
||||||
|
email: newUserEmailServerSchema.optional(),
|
||||||
|
image: zod.string().optional(),
|
||||||
|
timezone: zod.string().optional(),
|
||||||
|
});
|
149
src/app/api/user/validation.ts
Normal file
149
src/app/api/user/validation.ts
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import { prisma } from '@/prisma';
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
|
||||||
|
extendZodWithOpenApi(zod);
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Email Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const emailSchema = zod
|
||||||
|
.email('Invalid email address')
|
||||||
|
.min(3, 'Email is required');
|
||||||
|
|
||||||
|
export const newUserEmailServerSchema = emailSchema.refine(async (val) => {
|
||||||
|
const existingUser = await prisma.user.findUnique({
|
||||||
|
where: { email: val },
|
||||||
|
});
|
||||||
|
return !existingUser;
|
||||||
|
}, 'Email in use by another account');
|
||||||
|
|
||||||
|
export const existingUserEmailServerSchema = emailSchema.refine(async (val) => {
|
||||||
|
const existingUser = await prisma.user.findUnique({
|
||||||
|
where: { email: val },
|
||||||
|
});
|
||||||
|
return !!existingUser;
|
||||||
|
}, 'Email not found');
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// First Name Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const firstNameSchema = zod
|
||||||
|
.string()
|
||||||
|
.min(1, 'First name is required')
|
||||||
|
.max(32, 'First name must be at most 32 characters long');
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Last Name Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const lastNameSchema = zod
|
||||||
|
.string()
|
||||||
|
.min(1, 'Last name is required')
|
||||||
|
.max(32, 'Last name must be at most 32 characters long');
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Username Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const userNameSchema = zod
|
||||||
|
.string()
|
||||||
|
.min(3, 'Username is required')
|
||||||
|
.max(32, 'Username must be at most 32 characters long')
|
||||||
|
.regex(
|
||||||
|
/^[a-zA-Z0-9_]+$/,
|
||||||
|
'Username can only contain letters, numbers, and underscores',
|
||||||
|
);
|
||||||
|
|
||||||
|
export const newUserNameServerSchema = userNameSchema.refine(async (val) => {
|
||||||
|
const existingUser = await prisma.user.findUnique({
|
||||||
|
where: { name: val },
|
||||||
|
});
|
||||||
|
return !existingUser;
|
||||||
|
}, 'Username in use by another account');
|
||||||
|
|
||||||
|
export const existingUserNameServerSchema = userNameSchema.refine(
|
||||||
|
async (val) => {
|
||||||
|
const existingUser = await prisma.user.findUnique({
|
||||||
|
where: { name: val },
|
||||||
|
});
|
||||||
|
return !!existingUser;
|
||||||
|
},
|
||||||
|
'Username not found',
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// User ID Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const existingUserIdServerSchema = zod
|
||||||
|
.string()
|
||||||
|
.min(1, 'User ID is required')
|
||||||
|
.refine(async (val) => {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id: val },
|
||||||
|
});
|
||||||
|
return !!user;
|
||||||
|
}, 'User not found');
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Password Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const passwordSchema = zod
|
||||||
|
.string()
|
||||||
|
.min(8, 'Password must be at least 8 characters long')
|
||||||
|
.max(128, 'Password must be at most 128 characters long')
|
||||||
|
.regex(
|
||||||
|
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+={}\[\]:;"'<>,.?\/\\-]).{8,}$/,
|
||||||
|
'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character',
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// User Schema Validation (for API responses)
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const FullUserSchema = zod
|
||||||
|
.object({
|
||||||
|
id: zod.string(),
|
||||||
|
name: zod.string(),
|
||||||
|
first_name: zod.string().nullish(),
|
||||||
|
last_name: zod.string().nullish(),
|
||||||
|
email: zod.email(),
|
||||||
|
image: zod.string().nullish(),
|
||||||
|
timezone: zod.string(),
|
||||||
|
created_at: zod.date(),
|
||||||
|
updated_at: zod.date(),
|
||||||
|
})
|
||||||
|
.openapi('FullUser', {
|
||||||
|
description: 'Full user information including all fields',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PublicUserSchema = FullUserSchema.pick({
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
|
}).openapi('PublicUser', {
|
||||||
|
description: 'Public user information excluding sensitive data',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const FullUserResponseSchema = zod.object({
|
||||||
|
success: zod.boolean(),
|
||||||
|
user: FullUserSchema,
|
||||||
|
});
|
||||||
|
export const PublicUserResponseSchema = zod.object({
|
||||||
|
success: zod.boolean(),
|
||||||
|
user: PublicUserSchema,
|
||||||
|
});
|
87
src/app/api/validation.ts
Normal file
87
src/app/api/validation.ts
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import { registry } from '@/lib/swagger';
|
||||||
|
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
|
||||||
|
extendZodWithOpenApi(zod);
|
||||||
|
|
||||||
|
export const ErrorResponseSchema = zod
|
||||||
|
.object({
|
||||||
|
success: zod.boolean(),
|
||||||
|
message: zod.string(),
|
||||||
|
})
|
||||||
|
.openapi('ErrorResponseSchema', {
|
||||||
|
description: 'Error response schema',
|
||||||
|
example: {
|
||||||
|
success: false,
|
||||||
|
message: 'An error occurred',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ZodErrorResponseSchema = ErrorResponseSchema.extend({
|
||||||
|
errors: zod.array(
|
||||||
|
zod.object({
|
||||||
|
expected: zod.string().optional(),
|
||||||
|
code: zod.string(),
|
||||||
|
path: zod.array(
|
||||||
|
zod
|
||||||
|
.string()
|
||||||
|
.or(zod.number())
|
||||||
|
.or(
|
||||||
|
zod.symbol().openapi({
|
||||||
|
type: 'string',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
message: zod.string(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}).openapi('ZodErrorResponseSchema', {
|
||||||
|
description: 'Zod error response schema',
|
||||||
|
example: {
|
||||||
|
success: false,
|
||||||
|
message: 'Invalid request data',
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
expected: 'string',
|
||||||
|
code: 'invalid_type',
|
||||||
|
path: ['first_name'],
|
||||||
|
message: 'Invalid input: expected string, received number',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SuccessResponseSchema = zod
|
||||||
|
.object({
|
||||||
|
success: zod.boolean(),
|
||||||
|
message: zod.string().optional(),
|
||||||
|
})
|
||||||
|
.openapi('SuccessResponseSchema', {
|
||||||
|
description: 'Success response schema',
|
||||||
|
example: {
|
||||||
|
success: true,
|
||||||
|
message: 'Operation completed successfully',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UserIdParamSchema = registry.registerParameter(
|
||||||
|
'UserIdOrNameParam',
|
||||||
|
zod.string().openapi({
|
||||||
|
param: {
|
||||||
|
name: 'user',
|
||||||
|
in: 'path',
|
||||||
|
},
|
||||||
|
example: '12345',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const EventIdParamSchema = registry.registerParameter(
|
||||||
|
'EventIdParam',
|
||||||
|
zod.string().openapi({
|
||||||
|
param: {
|
||||||
|
name: 'eventID',
|
||||||
|
in: 'path',
|
||||||
|
},
|
||||||
|
example: '67890',
|
||||||
|
}),
|
||||||
|
);
|
|
@ -8,7 +8,7 @@ import Authentik from 'next-auth/providers/authentik';
|
||||||
import { PrismaAdapter } from '@auth/prisma-adapter';
|
import { PrismaAdapter } from '@auth/prisma-adapter';
|
||||||
import { prisma } from '@/prisma';
|
import { prisma } from '@/prisma';
|
||||||
|
|
||||||
import { loginClientSchema } from './lib/validation/user';
|
import { loginSchema } from '@/lib/auth/validation';
|
||||||
import { ZodError } from 'zod/v4';
|
import { ZodError } from 'zod/v4';
|
||||||
|
|
||||||
class InvalidLoginError extends CredentialsSignin {
|
class InvalidLoginError extends CredentialsSignin {
|
||||||
|
@ -37,7 +37,7 @@ const providers: Provider[] = [
|
||||||
if (process.env.DISABLE_PASSWORD_LOGIN) return null;
|
if (process.env.DISABLE_PASSWORD_LOGIN) return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { email, password } = await loginClientSchema.parseAsync(c);
|
const { email, password } = await loginSchema.parseAsync(c);
|
||||||
|
|
||||||
const user = await prisma.user.findFirst({
|
const user = await prisma.user.findFirst({
|
||||||
where: { OR: [{ email }, { name: email }] },
|
where: { OR: [{ email }, { name: email }] },
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useRouter } from 'next/navigation';
|
||||||
import LabeledInput from '@/components/custom-ui/labeled-input';
|
import LabeledInput from '@/components/custom-ui/labeled-input';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import useZodForm from '@/lib/hooks/useZodForm';
|
import useZodForm from '@/lib/hooks/useZodForm';
|
||||||
import { loginClientSchema, registerClientSchema } from '@/lib/validation/user';
|
import { loginSchema, registerSchema } from '@/lib/auth/validation';
|
||||||
import { loginAction } from '@/lib/auth/login';
|
import { loginAction } from '@/lib/auth/login';
|
||||||
import { registerAction } from '@/lib/auth/register';
|
import { registerAction } from '@/lib/auth/register';
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ function LoginFormElement({
|
||||||
formRef?: React.RefObject<HTMLFormElement | null>;
|
formRef?: React.RefObject<HTMLFormElement | null>;
|
||||||
}) {
|
}) {
|
||||||
const { handleSubmit, formState, register, setError } =
|
const { handleSubmit, formState, register, setError } =
|
||||||
useZodForm(loginClientSchema);
|
useZodForm(loginSchema);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const onSubmit = handleSubmit(async (data) => {
|
const onSubmit = handleSubmit(async (data) => {
|
||||||
|
@ -95,7 +95,7 @@ function RegisterFormElement({
|
||||||
formRef?: React.RefObject<HTMLFormElement | null>;
|
formRef?: React.RefObject<HTMLFormElement | null>;
|
||||||
}) {
|
}) {
|
||||||
const { handleSubmit, formState, register, setError } =
|
const { handleSubmit, formState, register, setError } =
|
||||||
useZodForm(registerClientSchema);
|
useZodForm(registerSchema);
|
||||||
|
|
||||||
const onSubmit = handleSubmit(async (data) => {
|
const onSubmit = handleSubmit(async (data) => {
|
||||||
try {
|
try {
|
||||||
|
|
38
src/lib/apiHelpers.ts
Normal file
38
src/lib/apiHelpers.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { NextAuthRequest } from 'next-auth';
|
||||||
|
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
extendZodWithOpenApi(zod);
|
||||||
|
|
||||||
|
export function userAuthenticated(req: NextAuthRequest) {
|
||||||
|
if (!req.auth || !req.auth.user || !req.auth.user.id)
|
||||||
|
return {
|
||||||
|
continue: false,
|
||||||
|
response: { success: false, message: 'Not authenticated' },
|
||||||
|
metadata: { status: 401 },
|
||||||
|
} as const;
|
||||||
|
return { continue: true, user: req.auth.user } as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function returnZodTypeCheckedResponse<
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
Schema extends zod.ZodType<any, any, any>,
|
||||||
|
>(
|
||||||
|
expectedType: Schema,
|
||||||
|
response: zod.input<Schema>,
|
||||||
|
metadata?: { status: number },
|
||||||
|
): NextResponse {
|
||||||
|
const result = expectedType.safeParse(response);
|
||||||
|
if (!result.success) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: 'Invalid response format',
|
||||||
|
errors: result.error.issues,
|
||||||
|
},
|
||||||
|
{ status: 500 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return NextResponse.json(result.data, { status: metadata?.status || 200 });
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import { z } from 'zod/v4';
|
import { z } from 'zod/v4';
|
||||||
import { loginClientSchema } from '@/lib/validation/user';
|
import { loginSchema } from './validation';
|
||||||
import { signIn } from '@/auth';
|
import { signIn } from '@/auth';
|
||||||
|
|
||||||
export async function loginAction(data: z.infer<typeof loginClientSchema>) {
|
export async function loginAction(data: z.infer<typeof loginSchema>) {
|
||||||
try {
|
try {
|
||||||
await signIn('credentials', {
|
await signIn('credentials', {
|
||||||
...data,
|
...data,
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
|
|
||||||
import type { z } from 'zod/v4';
|
import type { z } from 'zod/v4';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { registerSchema } from '@/lib/validation/user';
|
import { registerServerSchema } from './validation';
|
||||||
import { prisma } from '@/prisma';
|
import { prisma } from '@/prisma';
|
||||||
|
|
||||||
export async function registerAction(data: z.infer<typeof registerSchema>) {
|
export async function registerAction(
|
||||||
|
data: z.infer<typeof registerServerSchema>,
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const result = await registerSchema.safeParseAsync(data);
|
const result = await registerServerSchema.safeParseAsync(data);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
return {
|
return {
|
||||||
|
|
53
src/lib/auth/validation.ts
Normal file
53
src/lib/auth/validation.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
import {
|
||||||
|
emailSchema,
|
||||||
|
firstNameSchema,
|
||||||
|
lastNameSchema,
|
||||||
|
newUserEmailServerSchema,
|
||||||
|
newUserNameServerSchema,
|
||||||
|
passwordSchema,
|
||||||
|
userNameSchema,
|
||||||
|
} from '@/app/api/user/validation';
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Login Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const loginSchema = zod.object({
|
||||||
|
email: emailSchema.or(userNameSchema),
|
||||||
|
password: zod.string().min(1, 'Password is required'),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// Register Validation
|
||||||
|
//
|
||||||
|
// ----------------------------------------
|
||||||
|
export const registerServerSchema = zod
|
||||||
|
.object({
|
||||||
|
firstName: firstNameSchema,
|
||||||
|
lastName: lastNameSchema,
|
||||||
|
email: newUserEmailServerSchema,
|
||||||
|
password: passwordSchema,
|
||||||
|
confirmPassword: passwordSchema,
|
||||||
|
username: newUserNameServerSchema,
|
||||||
|
})
|
||||||
|
.refine((data) => data.password === data.confirmPassword, {
|
||||||
|
message: 'Passwords do not match',
|
||||||
|
path: ['confirmPassword'],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const registerSchema = zod
|
||||||
|
.object({
|
||||||
|
firstName: firstNameSchema,
|
||||||
|
lastName: lastNameSchema,
|
||||||
|
email: emailSchema,
|
||||||
|
password: passwordSchema,
|
||||||
|
confirmPassword: passwordSchema,
|
||||||
|
username: userNameSchema,
|
||||||
|
})
|
||||||
|
.refine((data) => data.password === data.confirmPassword, {
|
||||||
|
message: 'Passwords do not match',
|
||||||
|
path: ['confirmPassword'],
|
||||||
|
});
|
60
src/lib/defaultApiResponses.ts
Normal file
60
src/lib/defaultApiResponses.ts
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import {
|
||||||
|
ErrorResponseSchema,
|
||||||
|
ZodErrorResponseSchema,
|
||||||
|
} from '@/app/api/validation';
|
||||||
|
|
||||||
|
export const invalidRequestDataResponse = {
|
||||||
|
400: {
|
||||||
|
description: 'Invalid request data',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: ZodErrorResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const notAuthenticatedResponse = {
|
||||||
|
401: {
|
||||||
|
description: 'Not authenticated',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: ErrorResponseSchema,
|
||||||
|
example: {
|
||||||
|
success: false,
|
||||||
|
message: 'Not authenticated',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const userNotFoundResponse = {
|
||||||
|
404: {
|
||||||
|
description: 'User not found',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: ErrorResponseSchema,
|
||||||
|
example: {
|
||||||
|
success: false,
|
||||||
|
message: 'User not found',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const serverReturnedDataValidationErrorResponse = {
|
||||||
|
500: {
|
||||||
|
description: 'Server returned data validation error',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: ZodErrorResponseSchema,
|
||||||
|
example: {
|
||||||
|
success: false,
|
||||||
|
message: 'Server returned data validation error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,8 +1,36 @@
|
||||||
import { createSwaggerSpec } from 'next-swagger-doc';
|
import {
|
||||||
|
OpenAPIRegistry,
|
||||||
|
OpenApiGeneratorV3,
|
||||||
|
} from '@asteasolutions/zod-to-openapi';
|
||||||
|
|
||||||
import swaggerConfig from '../../next-swagger-doc.json';
|
export const registry = new OpenAPIRegistry();
|
||||||
|
|
||||||
export const getApiDocs = async () => {
|
export const getApiDocs = async () => {
|
||||||
const spec = createSwaggerSpec(swaggerConfig);
|
const swaggerFiles = require.context('../app', true, /swagger\.ts$/);
|
||||||
return spec;
|
|
||||||
|
swaggerFiles
|
||||||
|
.keys()
|
||||||
|
.sort((a, b) => b.length - a.length)
|
||||||
|
.forEach((file) => {
|
||||||
|
console.log(`Registering Swagger file: ${file}`);
|
||||||
|
swaggerFiles(file).default?.(registry);
|
||||||
|
});
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
|
require('@/app/api/validation');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const generator = new OpenApiGeneratorV3(registry.definitions);
|
||||||
|
const spec = generator.generateDocument({
|
||||||
|
openapi: '3.0.0',
|
||||||
|
info: {
|
||||||
|
version: '1.0.0',
|
||||||
|
title: 'MeetUP',
|
||||||
|
description: 'API documentation for MeetUP application',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return spec;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating API docs:', error);
|
||||||
|
throw new Error('Failed to generate API documentation');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
import { prisma } from '@/prisma';
|
|
||||||
import zod from 'zod/v4';
|
|
||||||
|
|
||||||
export const userEmailClientSchema = zod
|
|
||||||
.email('Invalid email address')
|
|
||||||
.min(3, 'Email is required');
|
|
||||||
|
|
||||||
export const userEmailSchema = userEmailClientSchema.refine(async (val) => {
|
|
||||||
const existingUser = await prisma.user.findUnique({
|
|
||||||
where: { email: val },
|
|
||||||
});
|
|
||||||
return !existingUser;
|
|
||||||
}, 'Email in use by another account');
|
|
||||||
|
|
||||||
export const userFirstNameSchema = zod
|
|
||||||
.string()
|
|
||||||
.min(1, 'First name is required')
|
|
||||||
.max(32, 'First name must be at most 32 characters long');
|
|
||||||
|
|
||||||
export const userLastNameSchema = zod
|
|
||||||
.string()
|
|
||||||
.min(1, 'Last name is required')
|
|
||||||
.max(32, 'Last name must be at most 32 characters long');
|
|
||||||
|
|
||||||
export const userNameClientSchema = zod
|
|
||||||
.string()
|
|
||||||
.min(3, 'Username is required')
|
|
||||||
.max(32, 'Username must be at most 32 characters long')
|
|
||||||
.regex(
|
|
||||||
/^[a-zA-Z0-9_]+$/,
|
|
||||||
'Username can only contain letters, numbers, and underscores',
|
|
||||||
)
|
|
||||||
.refine((val) => !disallowedUsernames.includes(val?.toLowerCase() || ''), {
|
|
||||||
error: 'Username is not allowed',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const userNameSchema = userNameClientSchema.refine(async (val) => {
|
|
||||||
const existingUser = await prisma.user.findUnique({
|
|
||||||
where: { name: val },
|
|
||||||
});
|
|
||||||
return !existingUser;
|
|
||||||
}, 'Username in use by another account');
|
|
||||||
|
|
||||||
export const loginClientSchema = zod.object({
|
|
||||||
email: userEmailClientSchema.or(userNameClientSchema),
|
|
||||||
password: zod.string().min(1, 'Password is required'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const userIdSchema = zod
|
|
||||||
.string()
|
|
||||||
.min(1, 'User ID is required')
|
|
||||||
.refine(async (val) => {
|
|
||||||
const user = await prisma.user.findUnique({
|
|
||||||
where: { id: val },
|
|
||||||
});
|
|
||||||
return !!user;
|
|
||||||
}, 'User not found');
|
|
||||||
|
|
||||||
export const registerSchema = zod
|
|
||||||
.object({
|
|
||||||
firstName: userFirstNameSchema,
|
|
||||||
lastName: userLastNameSchema,
|
|
||||||
email: userEmailSchema,
|
|
||||||
password: zod
|
|
||||||
.string()
|
|
||||||
.min(8, 'Password must be at least 8 characters long')
|
|
||||||
.max(128, 'Password must be at most 128 characters long'),
|
|
||||||
confirmPassword: zod
|
|
||||||
.string()
|
|
||||||
.min(8, 'Password must be at least 8 characters long')
|
|
||||||
.max(128, 'Password must be at most 128 characters long'),
|
|
||||||
username: userNameSchema,
|
|
||||||
})
|
|
||||||
.refine((data) => data.password === data.confirmPassword, {
|
|
||||||
error: 'Passwords do not match',
|
|
||||||
path: ['confirmPassword'],
|
|
||||||
})
|
|
||||||
.refine(
|
|
||||||
(data) =>
|
|
||||||
!data.password.includes(data.firstName) &&
|
|
||||||
!data.password.includes(data.lastName) &&
|
|
||||||
!data.password.includes(data.email) &&
|
|
||||||
!data.password.includes(data.username),
|
|
||||||
{
|
|
||||||
error:
|
|
||||||
'Password cannot contain your first name, last name, email, or username',
|
|
||||||
path: ['password'],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const registerClientSchema = zod
|
|
||||||
.object({
|
|
||||||
firstName: userFirstNameSchema,
|
|
||||||
lastName: userLastNameSchema,
|
|
||||||
email: userEmailClientSchema,
|
|
||||||
password: zod
|
|
||||||
.string()
|
|
||||||
.min(8, 'Password must be at least 8 characters long')
|
|
||||||
.max(128, 'Password must be at most 128 characters long'),
|
|
||||||
confirmPassword: zod
|
|
||||||
.string()
|
|
||||||
.min(8, 'Password must be at least 8 characters long')
|
|
||||||
.max(128, 'Password must be at most 128 characters long'),
|
|
||||||
username: userNameClientSchema,
|
|
||||||
})
|
|
||||||
.refine((data) => data.password === data.confirmPassword, {
|
|
||||||
error: 'Passwords do not match',
|
|
||||||
path: ['confirmPassword'],
|
|
||||||
})
|
|
||||||
.refine(
|
|
||||||
(data) =>
|
|
||||||
!data.password.includes(data.firstName) &&
|
|
||||||
!data.password.includes(data.lastName) &&
|
|
||||||
!data.password.includes(data.email) &&
|
|
||||||
!data.password.includes(data.username),
|
|
||||||
{
|
|
||||||
error:
|
|
||||||
'Password cannot contain your first name, last name, email, or username',
|
|
||||||
path: ['password'],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const disallowedUsernames = ['me', 'admin', 'search'];
|
|
|
@ -22,6 +22,11 @@
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ts-node": {
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|
536
yarn.lock
536
yarn.lock
|
@ -33,19 +33,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@apidevtools/json-schema-ref-parser@npm:^9.0.6":
|
"@apidevtools/openapi-schemas@npm:^2.1.0":
|
||||||
version: 9.1.2
|
|
||||||
resolution: "@apidevtools/json-schema-ref-parser@npm:9.1.2"
|
|
||||||
dependencies:
|
|
||||||
"@jsdevtools/ono": "npm:^7.1.3"
|
|
||||||
"@types/json-schema": "npm:^7.0.6"
|
|
||||||
call-me-maybe: "npm:^1.0.1"
|
|
||||||
js-yaml: "npm:^4.1.0"
|
|
||||||
checksum: 10c0/ebf952eb2e00bf0919f024e72897e047fd5012f0a9e47ac361873f6de0a733b9334513cdbc73205a6b43ac4a652b8c87f55e489c39b2d60bd0bc1cb2b411e218
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@apidevtools/openapi-schemas@npm:^2.0.4, @apidevtools/openapi-schemas@npm:^2.1.0":
|
|
||||||
version: 2.1.0
|
version: 2.1.0
|
||||||
resolution: "@apidevtools/openapi-schemas@npm:2.1.0"
|
resolution: "@apidevtools/openapi-schemas@npm:2.1.0"
|
||||||
checksum: 10c0/f4aa0f9df32e474d166c84ef91bceb18fa1c4f44b5593879529154ef340846811ea57dc2921560f157f692262827d28d988dd6e19fb21f00320e9961964176b4
|
checksum: 10c0/f4aa0f9df32e474d166c84ef91bceb18fa1c4f44b5593879529154ef340846811ea57dc2921560f157f692262827d28d988dd6e19fb21f00320e9961964176b4
|
||||||
|
@ -59,22 +47,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@apidevtools/swagger-parser@npm:10.0.3":
|
|
||||||
version: 10.0.3
|
|
||||||
resolution: "@apidevtools/swagger-parser@npm:10.0.3"
|
|
||||||
dependencies:
|
|
||||||
"@apidevtools/json-schema-ref-parser": "npm:^9.0.6"
|
|
||||||
"@apidevtools/openapi-schemas": "npm:^2.0.4"
|
|
||||||
"@apidevtools/swagger-methods": "npm:^3.0.2"
|
|
||||||
"@jsdevtools/ono": "npm:^7.1.3"
|
|
||||||
call-me-maybe: "npm:^1.0.1"
|
|
||||||
z-schema: "npm:^5.0.1"
|
|
||||||
peerDependencies:
|
|
||||||
openapi-types: ">=7"
|
|
||||||
checksum: 10c0/3b43f719c2d647ac8dcf30f132834d413ce21cbf7a8d9c3b35ec91149dd25d608c8fd892358fcd61a8edd8c5140a7fb13676f948e2d87067d081a47b8c7107e9
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@apidevtools/swagger-parser@npm:^10.1.1":
|
"@apidevtools/swagger-parser@npm:^10.1.1":
|
||||||
version: 10.1.1
|
version: 10.1.1
|
||||||
resolution: "@apidevtools/swagger-parser@npm:10.1.1"
|
resolution: "@apidevtools/swagger-parser@npm:10.1.1"
|
||||||
|
@ -92,6 +64,17 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@asteasolutions/zod-to-openapi@npm:^8.0.0-beta.4":
|
||||||
|
version: 8.0.0-beta.4
|
||||||
|
resolution: "@asteasolutions/zod-to-openapi@npm:8.0.0-beta.4"
|
||||||
|
dependencies:
|
||||||
|
openapi3-ts: "npm:^4.1.2"
|
||||||
|
peerDependencies:
|
||||||
|
zod: ~3.25.1
|
||||||
|
checksum: 10c0/596a149bc1c132f640befc1a9c58ca31d651146ea83ee98861cf65bddca3be6dbb324d82179ef45717f49c4e73114e3f86213711671c9065eaf01d594b408ff2
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@asyncapi/specs@npm:^6.8.0":
|
"@asyncapi/specs@npm:^6.8.0":
|
||||||
version: 6.8.1
|
version: 6.8.1
|
||||||
resolution: "@asyncapi/specs@npm:6.8.1"
|
resolution: "@asyncapi/specs@npm:6.8.1"
|
||||||
|
@ -152,6 +135,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@cspotcode/source-map-support@npm:^0.8.0":
|
||||||
|
version: 0.8.1
|
||||||
|
resolution: "@cspotcode/source-map-support@npm:0.8.1"
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/trace-mapping": "npm:0.3.9"
|
||||||
|
checksum: 10c0/05c5368c13b662ee4c122c7bfbe5dc0b613416672a829f3e78bc49a357a197e0218d6e74e7c66cfcd04e15a179acab080bd3c69658c9fbefd0e1ccd950a07fc6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@emnapi/core@npm:^1.4.3":
|
"@emnapi/core@npm:^1.4.3":
|
||||||
version: 1.4.3
|
version: 1.4.3
|
||||||
resolution: "@emnapi/core@npm:1.4.3"
|
resolution: "@emnapi/core@npm:1.4.3"
|
||||||
|
@ -861,7 +853,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@jridgewell/resolve-uri@npm:^3.1.0":
|
"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0":
|
||||||
version: 3.1.2
|
version: 3.1.2
|
||||||
resolution: "@jridgewell/resolve-uri@npm:3.1.2"
|
resolution: "@jridgewell/resolve-uri@npm:3.1.2"
|
||||||
checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e
|
checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e
|
||||||
|
@ -882,6 +874,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping@npm:0.3.9":
|
||||||
|
version: 0.3.9
|
||||||
|
resolution: "@jridgewell/trace-mapping@npm:0.3.9"
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/resolve-uri": "npm:^3.0.3"
|
||||||
|
"@jridgewell/sourcemap-codec": "npm:^1.4.10"
|
||||||
|
checksum: 10c0/fa425b606d7c7ee5bfa6a31a7b050dd5814b4082f318e0e4190f991902181b4330f43f4805db1dd4f2433fd0ed9cc7a7b9c2683f1deeab1df1b0a98b1e24055b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@jridgewell/trace-mapping@npm:^0.3.24":
|
"@jridgewell/trace-mapping@npm:^0.3.24":
|
||||||
version: 0.3.25
|
version: 0.3.25
|
||||||
resolution: "@jridgewell/trace-mapping@npm:0.3.25"
|
resolution: "@jridgewell/trace-mapping@npm:0.3.25"
|
||||||
|
@ -937,10 +939,10 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@next/env@npm:15.3.4":
|
"@next/env@npm:15.4.0-canary.85":
|
||||||
version: 15.3.4
|
version: 15.4.0-canary.85
|
||||||
resolution: "@next/env@npm:15.3.4"
|
resolution: "@next/env@npm:15.4.0-canary.85"
|
||||||
checksum: 10c0/43d37896e1422c9c353d9ded1d1b01545aa30b2bb125bcc40ffd4474dbc6e0ba603a77fc2a598616964a925379bb5a39eb1a242f0c49fc933e39e099fb2f7d75
|
checksum: 10c0/2a11c5530bd87edf993413044af5175f7e230d6db41145e3c56059a0cd1fc675b6150e1644b1e8731ff1c590927b4d6c27550ca1bafae3318fffd6483e75d1da
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -953,58 +955,58 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@next/swc-darwin-arm64@npm:15.3.4":
|
"@next/swc-darwin-arm64@npm:15.4.0-canary.85":
|
||||||
version: 15.3.4
|
version: 15.4.0-canary.85
|
||||||
resolution: "@next/swc-darwin-arm64@npm:15.3.4"
|
resolution: "@next/swc-darwin-arm64@npm:15.4.0-canary.85"
|
||||||
conditions: os=darwin & cpu=arm64
|
conditions: os=darwin & cpu=arm64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@next/swc-darwin-x64@npm:15.3.4":
|
"@next/swc-darwin-x64@npm:15.4.0-canary.85":
|
||||||
version: 15.3.4
|
version: 15.4.0-canary.85
|
||||||
resolution: "@next/swc-darwin-x64@npm:15.3.4"
|
resolution: "@next/swc-darwin-x64@npm:15.4.0-canary.85"
|
||||||
conditions: os=darwin & cpu=x64
|
conditions: os=darwin & cpu=x64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@next/swc-linux-arm64-gnu@npm:15.3.4":
|
"@next/swc-linux-arm64-gnu@npm:15.4.0-canary.85":
|
||||||
version: 15.3.4
|
version: 15.4.0-canary.85
|
||||||
resolution: "@next/swc-linux-arm64-gnu@npm:15.3.4"
|
resolution: "@next/swc-linux-arm64-gnu@npm:15.4.0-canary.85"
|
||||||
conditions: os=linux & cpu=arm64 & libc=glibc
|
conditions: os=linux & cpu=arm64 & libc=glibc
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@next/swc-linux-arm64-musl@npm:15.3.4":
|
"@next/swc-linux-arm64-musl@npm:15.4.0-canary.85":
|
||||||
version: 15.3.4
|
version: 15.4.0-canary.85
|
||||||
resolution: "@next/swc-linux-arm64-musl@npm:15.3.4"
|
resolution: "@next/swc-linux-arm64-musl@npm:15.4.0-canary.85"
|
||||||
conditions: os=linux & cpu=arm64 & libc=musl
|
conditions: os=linux & cpu=arm64 & libc=musl
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@next/swc-linux-x64-gnu@npm:15.3.4":
|
"@next/swc-linux-x64-gnu@npm:15.4.0-canary.85":
|
||||||
version: 15.3.4
|
version: 15.4.0-canary.85
|
||||||
resolution: "@next/swc-linux-x64-gnu@npm:15.3.4"
|
resolution: "@next/swc-linux-x64-gnu@npm:15.4.0-canary.85"
|
||||||
conditions: os=linux & cpu=x64 & libc=glibc
|
conditions: os=linux & cpu=x64 & libc=glibc
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@next/swc-linux-x64-musl@npm:15.3.4":
|
"@next/swc-linux-x64-musl@npm:15.4.0-canary.85":
|
||||||
version: 15.3.4
|
version: 15.4.0-canary.85
|
||||||
resolution: "@next/swc-linux-x64-musl@npm:15.3.4"
|
resolution: "@next/swc-linux-x64-musl@npm:15.4.0-canary.85"
|
||||||
conditions: os=linux & cpu=x64 & libc=musl
|
conditions: os=linux & cpu=x64 & libc=musl
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@next/swc-win32-arm64-msvc@npm:15.3.4":
|
"@next/swc-win32-arm64-msvc@npm:15.4.0-canary.85":
|
||||||
version: 15.3.4
|
version: 15.4.0-canary.85
|
||||||
resolution: "@next/swc-win32-arm64-msvc@npm:15.3.4"
|
resolution: "@next/swc-win32-arm64-msvc@npm:15.4.0-canary.85"
|
||||||
conditions: os=win32 & cpu=arm64
|
conditions: os=win32 & cpu=arm64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@next/swc-win32-x64-msvc@npm:15.3.4":
|
"@next/swc-win32-x64-msvc@npm:15.4.0-canary.85":
|
||||||
version: 15.3.4
|
version: 15.4.0-canary.85
|
||||||
resolution: "@next/swc-win32-x64-msvc@npm:15.3.4"
|
resolution: "@next/swc-win32-x64-msvc@npm:15.4.0-canary.85"
|
||||||
conditions: os=win32 & cpu=x64
|
conditions: os=win32 & cpu=x64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
@ -2797,13 +2799,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@swc/counter@npm:0.1.3":
|
|
||||||
version: 0.1.3
|
|
||||||
resolution: "@swc/counter@npm:0.1.3"
|
|
||||||
checksum: 10c0/8424f60f6bf8694cfd2a9bca45845bce29f26105cda8cf19cdb9fd3e78dc6338699e4db77a89ae449260bafa1cc6bec307e81e7fb96dbf7dcfce0eea55151356
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@swc/helpers@npm:0.5.15":
|
"@swc/helpers@npm:0.5.15":
|
||||||
version: 0.5.15
|
version: 0.5.15
|
||||||
resolution: "@swc/helpers@npm:0.5.15"
|
resolution: "@swc/helpers@npm:0.5.15"
|
||||||
|
@ -3013,6 +3008,34 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@tsconfig/node10@npm:^1.0.7":
|
||||||
|
version: 1.0.11
|
||||||
|
resolution: "@tsconfig/node10@npm:1.0.11"
|
||||||
|
checksum: 10c0/28a0710e5d039e0de484bdf85fee883bfd3f6a8980601f4d44066b0a6bcd821d31c4e231d1117731c4e24268bd4cf2a788a6787c12fc7f8d11014c07d582783c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@tsconfig/node12@npm:^1.0.7":
|
||||||
|
version: 1.0.11
|
||||||
|
resolution: "@tsconfig/node12@npm:1.0.11"
|
||||||
|
checksum: 10c0/dddca2b553e2bee1308a056705103fc8304e42bb2d2cbd797b84403a223b25c78f2c683ec3e24a095e82cd435387c877239bffcb15a590ba817cd3f6b9a99fd9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@tsconfig/node14@npm:^1.0.0":
|
||||||
|
version: 1.0.3
|
||||||
|
resolution: "@tsconfig/node14@npm:1.0.3"
|
||||||
|
checksum: 10c0/67c1316d065fdaa32525bc9449ff82c197c4c19092b9663b23213c8cbbf8d88b6ed6a17898e0cbc2711950fbfaf40388938c1c748a2ee89f7234fc9e7fe2bf44
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@tsconfig/node16@npm:^1.0.2":
|
||||||
|
version: 1.0.4
|
||||||
|
resolution: "@tsconfig/node16@npm:1.0.4"
|
||||||
|
checksum: 10c0/05f8f2734e266fb1839eb1d57290df1664fe2aa3b0fdd685a9035806daa635f7519bf6d5d9b33f6e69dd545b8c46bd6e2b5c79acb2b1f146e885f7f11a42a5bb
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@tybys/wasm-util@npm:^0.9.0":
|
"@tybys/wasm-util@npm:^0.9.0":
|
||||||
version: 0.9.0
|
version: 0.9.0
|
||||||
resolution: "@tybys/wasm-util@npm:0.9.0"
|
resolution: "@tybys/wasm-util@npm:0.9.0"
|
||||||
|
@ -3056,7 +3079,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/json-schema@npm:^7.0.11, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.4, @types/json-schema@npm:^7.0.6, @types/json-schema@npm:^7.0.7":
|
"@types/json-schema@npm:^7.0.11, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.4, @types/json-schema@npm:^7.0.7":
|
||||||
version: 7.0.15
|
version: 7.0.15
|
||||||
resolution: "@types/json-schema@npm:7.0.15"
|
resolution: "@types/json-schema@npm:7.0.15"
|
||||||
checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db
|
checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db
|
||||||
|
@ -3115,13 +3138,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/swagger-jsdoc@npm:6.0.4":
|
|
||||||
version: 6.0.4
|
|
||||||
resolution: "@types/swagger-jsdoc@npm:6.0.4"
|
|
||||||
checksum: 10c0/fbe17d91a12e1e60a255b02e6def6877c81b356c75ffcd0e5167fbaf1476e2d6600cd7eea79e6b3e0ff7929dec33ade345147509ed3b98026f63c782b74514f6
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@types/swagger-ui-react@npm:^5":
|
"@types/swagger-ui-react@npm:^5":
|
||||||
version: 5.18.0
|
version: 5.18.0
|
||||||
resolution: "@types/swagger-ui-react@npm:5.18.0"
|
resolution: "@types/swagger-ui-react@npm:5.18.0"
|
||||||
|
@ -3166,6 +3182,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/webpack-env@npm:^1.18.8":
|
||||||
|
version: 1.18.8
|
||||||
|
resolution: "@types/webpack-env@npm:1.18.8"
|
||||||
|
checksum: 10c0/527a5d1eb75c5243e4f3665d956c7c340f899955dd25d16c9fd9750406f32e95a3a17d207640295038e8235c0c2a2daf084f420e088e58b965d82fc74f6012d7
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0":
|
"@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0":
|
||||||
version: 8.34.1
|
version: 8.34.1
|
||||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.34.1"
|
resolution: "@typescript-eslint/eslint-plugin@npm:8.34.1"
|
||||||
|
@ -3462,7 +3485,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"acorn@npm:^8.14.1, acorn@npm:^8.15.0":
|
"acorn-walk@npm:^8.1.1":
|
||||||
|
version: 8.3.4
|
||||||
|
resolution: "acorn-walk@npm:8.3.4"
|
||||||
|
dependencies:
|
||||||
|
acorn: "npm:^8.11.0"
|
||||||
|
checksum: 10c0/76537ac5fb2c37a64560feaf3342023dadc086c46da57da363e64c6148dc21b57d49ace26f949e225063acb6fb441eabffd89f7a3066de5ad37ab3e328927c62
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"acorn@npm:^8.11.0, acorn@npm:^8.14.1, acorn@npm:^8.15.0, acorn@npm:^8.4.1":
|
||||||
version: 8.15.0
|
version: 8.15.0
|
||||||
resolution: "acorn@npm:8.15.0"
|
resolution: "acorn@npm:8.15.0"
|
||||||
bin:
|
bin:
|
||||||
|
@ -3581,6 +3613,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"arg@npm:^4.1.0":
|
||||||
|
version: 4.1.3
|
||||||
|
resolution: "arg@npm:4.1.3"
|
||||||
|
checksum: 10c0/070ff801a9d236a6caa647507bdcc7034530604844d64408149a26b9e87c2f97650055c0f049abd1efc024b334635c01f29e0b632b371ac3f26130f4cf65997a
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"argparse@npm:^1.0.10":
|
"argparse@npm:^1.0.10":
|
||||||
version: 1.0.10
|
version: 1.0.10
|
||||||
resolution: "argparse@npm:1.0.10"
|
resolution: "argparse@npm:1.0.10"
|
||||||
|
@ -3851,15 +3890,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"busboy@npm:1.6.0":
|
|
||||||
version: 1.6.0
|
|
||||||
resolution: "busboy@npm:1.6.0"
|
|
||||||
dependencies:
|
|
||||||
streamsearch: "npm:^1.1.0"
|
|
||||||
checksum: 10c0/fa7e836a2b82699b6e074393428b91ae579d4f9e21f5ac468e1b459a244341d722d2d22d10920cdd849743dbece6dca11d72de939fb75a7448825cf2babfba1f
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"cac@npm:^6.7.14":
|
"cac@npm:^6.7.14":
|
||||||
version: 6.7.14
|
version: 6.7.14
|
||||||
resolution: "cac@npm:6.7.14"
|
resolution: "cac@npm:6.7.14"
|
||||||
|
@ -4003,16 +4033,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"cleye@npm:1.3.2":
|
|
||||||
version: 1.3.2
|
|
||||||
resolution: "cleye@npm:1.3.2"
|
|
||||||
dependencies:
|
|
||||||
terminal-columns: "npm:^1.4.1"
|
|
||||||
type-flag: "npm:^3.0.0"
|
|
||||||
checksum: 10c0/2cd63c194d8476230cb9730dae87ae106995ff36b5e43436965caf14ebc6386b7a33e61cc12f90f27e42f4490383fb6032f1f1956bbbc12a3bb13e5cc74cbd78
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"client-only@npm:0.0.1":
|
"client-only@npm:0.0.1":
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
resolution: "client-only@npm:0.0.1"
|
resolution: "client-only@npm:0.0.1"
|
||||||
|
@ -4090,20 +4110,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"commander@npm:6.2.0":
|
|
||||||
version: 6.2.0
|
|
||||||
resolution: "commander@npm:6.2.0"
|
|
||||||
checksum: 10c0/1b701c6726fc2b6c6a7d9ab017be9465153546a05767cdd0e15e9f9a11c07f88f64d47684b90b07e5fb103d173efb6afdf4a21f6d6c4c25f7376bd027d21062c
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"commander@npm:^9.4.1":
|
|
||||||
version: 9.5.0
|
|
||||||
resolution: "commander@npm:9.5.0"
|
|
||||||
checksum: 10c0/5f7784fbda2aaec39e89eb46f06a999e00224b3763dc65976e05929ec486e174fe9aac2655f03ba6a5e83875bd173be5283dc19309b7c65954701c02025b3c1d
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"compare-versions@npm:^6.1.1":
|
"compare-versions@npm:^6.1.1":
|
||||||
version: 6.1.1
|
version: 6.1.1
|
||||||
resolution: "compare-versions@npm:6.1.1"
|
resolution: "compare-versions@npm:6.1.1"
|
||||||
|
@ -4134,6 +4140,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"create-require@npm:^1.1.0":
|
||||||
|
version: 1.1.1
|
||||||
|
resolution: "create-require@npm:1.1.1"
|
||||||
|
checksum: 10c0/157cbc59b2430ae9a90034a5f3a1b398b6738bf510f713edc4d4e45e169bc514d3d99dd34d8d01ca7ae7830b5b8b537e46ae8f3c8f932371b0875c0151d7ec91
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6":
|
"cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6":
|
||||||
version: 7.0.6
|
version: 7.0.6
|
||||||
resolution: "cross-spawn@npm:7.0.6"
|
resolution: "cross-spawn@npm:7.0.6"
|
||||||
|
@ -4291,6 +4304,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"diff@npm:^4.0.1":
|
||||||
|
version: 4.0.2
|
||||||
|
resolution: "diff@npm:4.0.2"
|
||||||
|
checksum: 10c0/81b91f9d39c4eaca068eb0c1eb0e4afbdc5bb2941d197f513dd596b820b956fef43485876226d65d497bebc15666aa2aa82c679e84f65d5f2bfbf14ee46e32c1
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"dir-glob@npm:^3.0.1":
|
"dir-glob@npm:^3.0.1":
|
||||||
version: 3.0.1
|
version: 3.0.1
|
||||||
resolution: "dir-glob@npm:3.0.1"
|
resolution: "dir-glob@npm:3.0.1"
|
||||||
|
@ -4300,15 +4320,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"doctrine@npm:3.0.0":
|
|
||||||
version: 3.0.0
|
|
||||||
resolution: "doctrine@npm:3.0.0"
|
|
||||||
dependencies:
|
|
||||||
esutils: "npm:^2.0.2"
|
|
||||||
checksum: 10c0/c96bdccabe9d62ab6fea9399fdff04a66e6563c1d6fb3a3a063e8d53c3bb136ba63e84250bbf63d00086a769ad53aef92d2bd483f03f837fc97b71cbee6b2520
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"doctrine@npm:^2.1.0":
|
"doctrine@npm:^2.1.0":
|
||||||
version: 2.1.0
|
version: 2.1.0
|
||||||
resolution: "doctrine@npm:2.1.0"
|
resolution: "doctrine@npm:2.1.0"
|
||||||
|
@ -5251,13 +5262,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"fs.realpath@npm:^1.0.0":
|
|
||||||
version: 1.0.0
|
|
||||||
resolution: "fs.realpath@npm:1.0.0"
|
|
||||||
checksum: 10c0/444cf1291d997165dfd4c0d58b69f0e4782bfd9149fd72faa4fe299e68e0e93d6db941660b37dd29153bf7186672ececa3b50b7e7249477b03fdf850f287c948
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"function-bind@npm:^1.1.2":
|
"function-bind@npm:^1.1.2":
|
||||||
version: 1.1.2
|
version: 1.1.2
|
||||||
resolution: "function-bind@npm:1.1.2"
|
resolution: "function-bind@npm:1.1.2"
|
||||||
|
@ -5373,20 +5377,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"glob@npm:7.1.6":
|
|
||||||
version: 7.1.6
|
|
||||||
resolution: "glob@npm:7.1.6"
|
|
||||||
dependencies:
|
|
||||||
fs.realpath: "npm:^1.0.0"
|
|
||||||
inflight: "npm:^1.0.4"
|
|
||||||
inherits: "npm:2"
|
|
||||||
minimatch: "npm:^3.0.4"
|
|
||||||
once: "npm:^1.3.0"
|
|
||||||
path-is-absolute: "npm:^1.0.0"
|
|
||||||
checksum: 10c0/2575cce9306ac534388db751f0aa3e78afedb6af8f3b529ac6b2354f66765545145dba8530abf7bff49fb399a047d3f9b6901c38ee4c9503f592960d9af67763
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"glob@npm:^10.2.2":
|
"glob@npm:^10.2.2":
|
||||||
version: 10.4.5
|
version: 10.4.5
|
||||||
resolution: "glob@npm:10.4.5"
|
resolution: "glob@npm:10.4.5"
|
||||||
|
@ -5648,17 +5638,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"inflight@npm:^1.0.4":
|
"inherits@npm:^2.0.1":
|
||||||
version: 1.0.6
|
|
||||||
resolution: "inflight@npm:1.0.6"
|
|
||||||
dependencies:
|
|
||||||
once: "npm:^1.3.0"
|
|
||||||
wrappy: "npm:1"
|
|
||||||
checksum: 10c0/7faca22584600a9dc5b9fca2cd5feb7135ac8c935449837b315676b4c90aa4f391ec4f42240178244b5a34e8bede1948627fda392ca3191522fc46b34e985ab2
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"inherits@npm:2, inherits@npm:^2.0.1":
|
|
||||||
version: 2.0.4
|
version: 2.0.4
|
||||||
resolution: "inherits@npm:2.0.4"
|
resolution: "inherits@npm:2.0.4"
|
||||||
checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2
|
checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2
|
||||||
|
@ -5988,7 +5968,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"isarray@npm:2.0.5, isarray@npm:^2.0.5":
|
"isarray@npm:^2.0.5":
|
||||||
version: 2.0.5
|
version: 2.0.5
|
||||||
resolution: "isarray@npm:2.0.5"
|
resolution: "isarray@npm:2.0.5"
|
||||||
checksum: 10c0/4199f14a7a13da2177c66c31080008b7124331956f47bca57dd0b6ea9f11687aa25e565a2c7a2b519bc86988d10398e3049a1f5df13c9f6b7664154690ae79fd
|
checksum: 10c0/4199f14a7a13da2177c66c31080008b7124331956f47bca57dd0b6ea9f11687aa25e565a2c7a2b519bc86988d10398e3049a1f5df13c9f6b7664154690ae79fd
|
||||||
|
@ -6130,6 +6110,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"json5@npm:^2.2.2":
|
||||||
|
version: 2.2.3
|
||||||
|
resolution: "json5@npm:2.2.3"
|
||||||
|
bin:
|
||||||
|
json5: lib/cli.js
|
||||||
|
checksum: 10c0/5a04eed94810fa55c5ea138b2f7a5c12b97c3750bc63d11e511dcecbfef758003861522a070c2272764ee0f4e3e323862f386945aeb5b85b87ee43f084ba586c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"jsonc-parser@npm:~2.2.1":
|
"jsonc-parser@npm:~2.2.1":
|
||||||
version: 2.2.1
|
version: 2.2.1
|
||||||
resolution: "jsonc-parser@npm:2.2.1"
|
resolution: "jsonc-parser@npm:2.2.1"
|
||||||
|
@ -6367,13 +6356,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"lodash.get@npm:^4.4.2":
|
|
||||||
version: 4.4.2
|
|
||||||
resolution: "lodash.get@npm:4.4.2"
|
|
||||||
checksum: 10c0/48f40d471a1654397ed41685495acb31498d5ed696185ac8973daef424a749ca0c7871bf7b665d5c14f5cc479394479e0307e781f61d5573831769593411be6e
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"lodash.isempty@npm:^4.4.0":
|
"lodash.isempty@npm:^4.4.0":
|
||||||
version: 4.4.0
|
version: 4.4.0
|
||||||
resolution: "lodash.isempty@npm:4.4.0"
|
resolution: "lodash.isempty@npm:4.4.0"
|
||||||
|
@ -6381,13 +6363,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"lodash.isequal@npm:^4.5.0":
|
|
||||||
version: 4.5.0
|
|
||||||
resolution: "lodash.isequal@npm:4.5.0"
|
|
||||||
checksum: 10c0/dfdb2356db19631a4b445d5f37868a095e2402292d59539a987f134a8778c62a2810c2452d11ae9e6dcac71fc9de40a6fedcb20e2952a15b431ad8b29e50e28f
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"lodash.merge@npm:^4.6.2":
|
"lodash.merge@npm:^4.6.2":
|
||||||
version: 4.6.2
|
version: 4.6.2
|
||||||
resolution: "lodash.merge@npm:4.6.2"
|
resolution: "lodash.merge@npm:4.6.2"
|
||||||
|
@ -6395,13 +6370,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"lodash.mergewith@npm:^4.6.2":
|
|
||||||
version: 4.6.2
|
|
||||||
resolution: "lodash.mergewith@npm:4.6.2"
|
|
||||||
checksum: 10c0/4adbed65ff96fd65b0b3861f6899f98304f90fd71e7f1eb36c1270e05d500ee7f5ec44c02ef979b5ddbf75c0a0b9b99c35f0ad58f4011934c4d4e99e5200b3b5
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"lodash.omitby@npm:^4.6.0":
|
"lodash.omitby@npm:^4.6.0":
|
||||||
version: 4.6.0
|
version: 4.6.0
|
||||||
resolution: "lodash.omitby@npm:4.6.0"
|
resolution: "lodash.omitby@npm:4.6.0"
|
||||||
|
@ -6511,6 +6479,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"make-error@npm:^1.1.1":
|
||||||
|
version: 1.3.6
|
||||||
|
resolution: "make-error@npm:1.3.6"
|
||||||
|
checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"make-fetch-happen@npm:^14.0.3":
|
"make-fetch-happen@npm:^14.0.3":
|
||||||
version: 14.0.3
|
version: 14.0.3
|
||||||
resolution: "make-fetch-happen@npm:14.0.3"
|
resolution: "make-fetch-happen@npm:14.0.3"
|
||||||
|
@ -6564,6 +6539,7 @@ __metadata:
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "meetup@workspace:."
|
resolution: "meetup@workspace:."
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@asteasolutions/zod-to-openapi": "npm:^8.0.0-beta.4"
|
||||||
"@auth/prisma-adapter": "npm:^2.9.1"
|
"@auth/prisma-adapter": "npm:^2.9.1"
|
||||||
"@eslint/eslintrc": "npm:3.3.1"
|
"@eslint/eslintrc": "npm:3.3.1"
|
||||||
"@fortawesome/fontawesome-svg-core": "npm:^6.7.2"
|
"@fortawesome/fontawesome-svg-core": "npm:^6.7.2"
|
||||||
|
@ -6588,6 +6564,7 @@ __metadata:
|
||||||
"@types/react": "npm:19.1.8"
|
"@types/react": "npm:19.1.8"
|
||||||
"@types/react-dom": "npm:19.1.6"
|
"@types/react-dom": "npm:19.1.6"
|
||||||
"@types/swagger-ui-react": "npm:^5"
|
"@types/swagger-ui-react": "npm:^5"
|
||||||
|
"@types/webpack-env": "npm:^1.18.8"
|
||||||
bcryptjs: "npm:^3.0.2"
|
bcryptjs: "npm:^3.0.2"
|
||||||
class-variance-authority: "npm:^0.7.1"
|
class-variance-authority: "npm:^0.7.1"
|
||||||
clsx: "npm:^2.1.1"
|
clsx: "npm:^2.1.1"
|
||||||
|
@ -6596,9 +6573,8 @@ __metadata:
|
||||||
eslint-config-next: "npm:15.3.4"
|
eslint-config-next: "npm:15.3.4"
|
||||||
eslint-config-prettier: "npm:10.1.5"
|
eslint-config-prettier: "npm:10.1.5"
|
||||||
lucide-react: "npm:^0.511.0"
|
lucide-react: "npm:^0.511.0"
|
||||||
next: "npm:15.3.4"
|
next: "npm:15.4.0-canary.85"
|
||||||
next-auth: "npm:^5.0.0-beta.25"
|
next-auth: "npm:^5.0.0-beta.25"
|
||||||
next-swagger-doc: "npm:^0.4.1"
|
|
||||||
next-themes: "npm:^0.4.6"
|
next-themes: "npm:^0.4.6"
|
||||||
orval: "npm:^7.10.0"
|
orval: "npm:^7.10.0"
|
||||||
postcss: "npm:8.5.6"
|
postcss: "npm:8.5.6"
|
||||||
|
@ -6610,8 +6586,10 @@ __metadata:
|
||||||
swagger-ui-react: "npm:^5.24.1"
|
swagger-ui-react: "npm:^5.24.1"
|
||||||
tailwind-merge: "npm:^3.2.0"
|
tailwind-merge: "npm:^3.2.0"
|
||||||
tailwindcss: "npm:4.1.10"
|
tailwindcss: "npm:4.1.10"
|
||||||
|
ts-node: "npm:^10.9.2"
|
||||||
|
tsconfig-paths: "npm:^4.2.0"
|
||||||
tw-animate-css: "npm:1.3.4"
|
tw-animate-css: "npm:1.3.4"
|
||||||
typescript: "npm:5.8.3"
|
typescript: "npm:^5.8.3"
|
||||||
zod: "npm:^3.25.60"
|
zod: "npm:^3.25.60"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
@ -6672,7 +6650,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"minimatch@npm:3.1.2, minimatch@npm:^3.0.4, minimatch@npm:^3.1.2":
|
"minimatch@npm:3.1.2, minimatch@npm:^3.1.2":
|
||||||
version: 3.1.2
|
version: 3.1.2
|
||||||
resolution: "minimatch@npm:3.1.2"
|
resolution: "minimatch@npm:3.1.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -6868,22 +6846,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"next-swagger-doc@npm:^0.4.1":
|
|
||||||
version: 0.4.1
|
|
||||||
resolution: "next-swagger-doc@npm:0.4.1"
|
|
||||||
dependencies:
|
|
||||||
"@types/swagger-jsdoc": "npm:6.0.4"
|
|
||||||
cleye: "npm:1.3.2"
|
|
||||||
isarray: "npm:2.0.5"
|
|
||||||
swagger-jsdoc: "npm:6.2.8"
|
|
||||||
peerDependencies:
|
|
||||||
next: ">=9"
|
|
||||||
bin:
|
|
||||||
next-swagger-doc-cli: dist/cli.js
|
|
||||||
checksum: 10c0/8ff0d33aad41296eb88f03243a627f9f0db528ea6889e528bbdbf66e38499069bfb9b7c29d2787c49f90f0b2a86a9c3e3475218d5af7a13dd4b0a37bec959703
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"next-themes@npm:^0.4.6":
|
"next-themes@npm:^0.4.6":
|
||||||
version: 0.4.6
|
version: 0.4.6
|
||||||
resolution: "next-themes@npm:0.4.6"
|
resolution: "next-themes@npm:0.4.6"
|
||||||
|
@ -6894,29 +6856,27 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"next@npm:15.3.4":
|
"next@npm:15.4.0-canary.85":
|
||||||
version: 15.3.4
|
version: 15.4.0-canary.85
|
||||||
resolution: "next@npm:15.3.4"
|
resolution: "next@npm:15.4.0-canary.85"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@next/env": "npm:15.3.4"
|
"@next/env": "npm:15.4.0-canary.85"
|
||||||
"@next/swc-darwin-arm64": "npm:15.3.4"
|
"@next/swc-darwin-arm64": "npm:15.4.0-canary.85"
|
||||||
"@next/swc-darwin-x64": "npm:15.3.4"
|
"@next/swc-darwin-x64": "npm:15.4.0-canary.85"
|
||||||
"@next/swc-linux-arm64-gnu": "npm:15.3.4"
|
"@next/swc-linux-arm64-gnu": "npm:15.4.0-canary.85"
|
||||||
"@next/swc-linux-arm64-musl": "npm:15.3.4"
|
"@next/swc-linux-arm64-musl": "npm:15.4.0-canary.85"
|
||||||
"@next/swc-linux-x64-gnu": "npm:15.3.4"
|
"@next/swc-linux-x64-gnu": "npm:15.4.0-canary.85"
|
||||||
"@next/swc-linux-x64-musl": "npm:15.3.4"
|
"@next/swc-linux-x64-musl": "npm:15.4.0-canary.85"
|
||||||
"@next/swc-win32-arm64-msvc": "npm:15.3.4"
|
"@next/swc-win32-arm64-msvc": "npm:15.4.0-canary.85"
|
||||||
"@next/swc-win32-x64-msvc": "npm:15.3.4"
|
"@next/swc-win32-x64-msvc": "npm:15.4.0-canary.85"
|
||||||
"@swc/counter": "npm:0.1.3"
|
|
||||||
"@swc/helpers": "npm:0.5.15"
|
"@swc/helpers": "npm:0.5.15"
|
||||||
busboy: "npm:1.6.0"
|
|
||||||
caniuse-lite: "npm:^1.0.30001579"
|
caniuse-lite: "npm:^1.0.30001579"
|
||||||
postcss: "npm:8.4.31"
|
postcss: "npm:8.4.31"
|
||||||
sharp: "npm:^0.34.1"
|
sharp: "npm:^0.34.1"
|
||||||
styled-jsx: "npm:5.1.6"
|
styled-jsx: "npm:5.1.6"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@opentelemetry/api": ^1.1.0
|
"@opentelemetry/api": ^1.1.0
|
||||||
"@playwright/test": ^1.41.2
|
"@playwright/test": ^1.51.1
|
||||||
babel-plugin-react-compiler: "*"
|
babel-plugin-react-compiler: "*"
|
||||||
react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
|
react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
|
||||||
react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
|
react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
|
||||||
|
@ -6951,7 +6911,7 @@ __metadata:
|
||||||
optional: true
|
optional: true
|
||||||
bin:
|
bin:
|
||||||
next: dist/bin/next
|
next: dist/bin/next
|
||||||
checksum: 10c0/52d3fba6f53d5d2a339cbde433ab360301e9a0a0d9b95a656bf29ce1af43f02e9cc32571d5d4095bcb8ab7a795207d6e75c64b33fc1f90d21f2f9b157cc9a503
|
checksum: 10c0/756f84e1412b690b7c4687f3da4984ed3134c571a5d1568dcf7e764e4328ed7789f1898b43490682021a3b27f38975804bc9770495ae495925aeb50cd706dfdb
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -7237,15 +7197,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"once@npm:^1.3.0":
|
|
||||||
version: 1.4.0
|
|
||||||
resolution: "once@npm:1.4.0"
|
|
||||||
dependencies:
|
|
||||||
wrappy: "npm:1"
|
|
||||||
checksum: 10c0/5d48aca287dfefabd756621c5dfce5c91a549a93e9fdb7b8246bc4c4790aa2ec17b34a260530474635147aeb631a2dcc8b32c613df0675f96041cbb8244517d0
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"onetime@npm:^5.1.2":
|
"onetime@npm:^5.1.2":
|
||||||
version: 5.1.2
|
version: 5.1.2
|
||||||
resolution: "onetime@npm:5.1.2"
|
resolution: "onetime@npm:5.1.2"
|
||||||
|
@ -7282,7 +7233,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"openapi3-ts@npm:4.4.0, openapi3-ts@npm:^4.2.2":
|
"openapi3-ts@npm:4.4.0, openapi3-ts@npm:^4.1.2, openapi3-ts@npm:^4.2.2":
|
||||||
version: 4.4.0
|
version: 4.4.0
|
||||||
resolution: "openapi3-ts@npm:4.4.0"
|
resolution: "openapi3-ts@npm:4.4.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -7414,13 +7365,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"path-is-absolute@npm:^1.0.0":
|
|
||||||
version: 1.0.1
|
|
||||||
resolution: "path-is-absolute@npm:1.0.1"
|
|
||||||
checksum: 10c0/127da03c82172a2a50099cddbf02510c1791fc2cc5f7713ddb613a56838db1e8168b121a920079d052e0936c23005562059756d653b7c544c53185efe53be078
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"path-key@npm:^3.0.0, path-key@npm:^3.1.0":
|
"path-key@npm:^3.0.0, path-key@npm:^3.1.0":
|
||||||
version: 3.1.1
|
version: 3.1.1
|
||||||
resolution: "path-key@npm:3.1.1"
|
resolution: "path-key@npm:3.1.1"
|
||||||
|
@ -8539,13 +8483,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"streamsearch@npm:^1.1.0":
|
|
||||||
version: 1.1.0
|
|
||||||
resolution: "streamsearch@npm:1.1.0"
|
|
||||||
checksum: 10c0/fbd9aecc2621364384d157f7e59426f4bfd385e8b424b5aaa79c83a6f5a1c8fd2e4e3289e95de1eb3511cb96bb333d6281a9919fafce760e4edb35b2cd2facab
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"string-argv@npm:^0.3.2":
|
"string-argv@npm:^0.3.2":
|
||||||
version: 0.3.2
|
version: 0.3.2
|
||||||
resolution: "string-argv@npm:0.3.2"
|
resolution: "string-argv@npm:0.3.2"
|
||||||
|
@ -8752,31 +8689,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"swagger-jsdoc@npm:6.2.8":
|
|
||||||
version: 6.2.8
|
|
||||||
resolution: "swagger-jsdoc@npm:6.2.8"
|
|
||||||
dependencies:
|
|
||||||
commander: "npm:6.2.0"
|
|
||||||
doctrine: "npm:3.0.0"
|
|
||||||
glob: "npm:7.1.6"
|
|
||||||
lodash.mergewith: "npm:^4.6.2"
|
|
||||||
swagger-parser: "npm:^10.0.3"
|
|
||||||
yaml: "npm:2.0.0-1"
|
|
||||||
bin:
|
|
||||||
swagger-jsdoc: bin/swagger-jsdoc.js
|
|
||||||
checksum: 10c0/7e20f08e8d90cc1e787cd82c096291cf12533359f89c70fbe4295a01f7c4734f2e82a03ba94027127bcd3da04b817abfe979f00d00ef0cd8283e449250a66215
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"swagger-parser@npm:^10.0.3":
|
|
||||||
version: 10.0.3
|
|
||||||
resolution: "swagger-parser@npm:10.0.3"
|
|
||||||
dependencies:
|
|
||||||
"@apidevtools/swagger-parser": "npm:10.0.3"
|
|
||||||
checksum: 10c0/d1a5c05f651f21a23508a36416071630b83e91dfffd52a6d44b06ca2cd1b86304c0dd2f4c04526c999b70062fa89bde3f5d54a1436626f4350590b6c6265a098
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"swagger-ui-react@npm:^5.24.1":
|
"swagger-ui-react@npm:^5.24.1":
|
||||||
version: 5.24.2
|
version: 5.24.2
|
||||||
resolution: "swagger-ui-react@npm:5.24.2"
|
resolution: "swagger-ui-react@npm:5.24.2"
|
||||||
|
@ -8879,13 +8791,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"terminal-columns@npm:^1.4.1":
|
|
||||||
version: 1.4.1
|
|
||||||
resolution: "terminal-columns@npm:1.4.1"
|
|
||||||
checksum: 10c0/e79135b0e9605d247ac26addf2afc0a4313ca4b70e6ff5b8571ab95c93703ab60db5b7b8a61a715e0ea5b6067434cfe1ae58f4392e01ad86c514b347a2a34e34
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.13":
|
"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.13":
|
||||||
version: 0.2.14
|
version: 0.2.14
|
||||||
resolution: "tinyglobby@npm:0.2.14"
|
resolution: "tinyglobby@npm:0.2.14"
|
||||||
|
@ -8973,6 +8878,44 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ts-node@npm:^10.9.2":
|
||||||
|
version: 10.9.2
|
||||||
|
resolution: "ts-node@npm:10.9.2"
|
||||||
|
dependencies:
|
||||||
|
"@cspotcode/source-map-support": "npm:^0.8.0"
|
||||||
|
"@tsconfig/node10": "npm:^1.0.7"
|
||||||
|
"@tsconfig/node12": "npm:^1.0.7"
|
||||||
|
"@tsconfig/node14": "npm:^1.0.0"
|
||||||
|
"@tsconfig/node16": "npm:^1.0.2"
|
||||||
|
acorn: "npm:^8.4.1"
|
||||||
|
acorn-walk: "npm:^8.1.1"
|
||||||
|
arg: "npm:^4.1.0"
|
||||||
|
create-require: "npm:^1.1.0"
|
||||||
|
diff: "npm:^4.0.1"
|
||||||
|
make-error: "npm:^1.1.1"
|
||||||
|
v8-compile-cache-lib: "npm:^3.0.1"
|
||||||
|
yn: "npm:3.1.1"
|
||||||
|
peerDependencies:
|
||||||
|
"@swc/core": ">=1.2.50"
|
||||||
|
"@swc/wasm": ">=1.2.50"
|
||||||
|
"@types/node": "*"
|
||||||
|
typescript: ">=2.7"
|
||||||
|
peerDependenciesMeta:
|
||||||
|
"@swc/core":
|
||||||
|
optional: true
|
||||||
|
"@swc/wasm":
|
||||||
|
optional: true
|
||||||
|
bin:
|
||||||
|
ts-node: dist/bin.js
|
||||||
|
ts-node-cwd: dist/bin-cwd.js
|
||||||
|
ts-node-esm: dist/bin-esm.js
|
||||||
|
ts-node-script: dist/bin-script.js
|
||||||
|
ts-node-transpile-only: dist/bin-transpile.js
|
||||||
|
ts-script: dist/bin-script-deprecated.js
|
||||||
|
checksum: 10c0/5f29938489f96982a25ba650b64218e83a3357d76f7bede80195c65ab44ad279c8357264639b7abdd5d7e75fc269a83daa0e9c62fd8637a3def67254ecc9ddc2
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"ts-toolbelt@npm:^9.6.0":
|
"ts-toolbelt@npm:^9.6.0":
|
||||||
version: 9.6.0
|
version: 9.6.0
|
||||||
resolution: "ts-toolbelt@npm:9.6.0"
|
resolution: "ts-toolbelt@npm:9.6.0"
|
||||||
|
@ -9006,6 +8949,17 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"tsconfig-paths@npm:^4.2.0":
|
||||||
|
version: 4.2.0
|
||||||
|
resolution: "tsconfig-paths@npm:4.2.0"
|
||||||
|
dependencies:
|
||||||
|
json5: "npm:^2.2.2"
|
||||||
|
minimist: "npm:^1.2.6"
|
||||||
|
strip-bom: "npm:^3.0.0"
|
||||||
|
checksum: 10c0/09a5877402d082bb1134930c10249edeebc0211f36150c35e1c542e5b91f1047b1ccf7da1e59babca1ef1f014c525510f4f870de7c9bda470c73bb4e2721b3ea
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"tslib@npm:^1.14.1":
|
"tslib@npm:^1.14.1":
|
||||||
version: 1.14.1
|
version: 1.14.1
|
||||||
resolution: "tslib@npm:1.14.1"
|
resolution: "tslib@npm:1.14.1"
|
||||||
|
@ -9043,13 +8997,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"type-flag@npm:^3.0.0":
|
|
||||||
version: 3.0.0
|
|
||||||
resolution: "type-flag@npm:3.0.0"
|
|
||||||
checksum: 10c0/b1015d4eb18cd85432fa3bcd0228149dd6893a9c1360a2ad619f79b72e19acfb648a932e2a9ccf2e2db0f0c4b86385bd886a3364e221afb116424e0ae6d1b1ba
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"typed-array-buffer@npm:^1.0.3":
|
"typed-array-buffer@npm:^1.0.3":
|
||||||
version: 1.0.3
|
version: 1.0.3
|
||||||
resolution: "typed-array-buffer@npm:1.0.3"
|
resolution: "typed-array-buffer@npm:1.0.3"
|
||||||
|
@ -9138,7 +9085,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"typescript@npm:5.8.3, typescript@npm:^5.6.3":
|
"typescript@npm:^5.6.3, typescript@npm:^5.8.3":
|
||||||
version: 5.8.3
|
version: 5.8.3
|
||||||
resolution: "typescript@npm:5.8.3"
|
resolution: "typescript@npm:5.8.3"
|
||||||
bin:
|
bin:
|
||||||
|
@ -9148,7 +9095,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"typescript@patch:typescript@npm%3A5.8.3#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.6.3#optional!builtin<compat/typescript>":
|
"typescript@patch:typescript@npm%3A^5.6.3#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.8.3#optional!builtin<compat/typescript>":
|
||||||
version: 5.8.3
|
version: 5.8.3
|
||||||
resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin<compat/typescript>::version=5.8.3&hash=5786d5"
|
resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin<compat/typescript>::version=5.8.3&hash=5786d5"
|
||||||
bin:
|
bin:
|
||||||
|
@ -9363,7 +9310,14 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"validator@npm:^13.11.0, validator@npm:^13.7.0":
|
"v8-compile-cache-lib@npm:^3.0.1":
|
||||||
|
version: 3.0.1
|
||||||
|
resolution: "v8-compile-cache-lib@npm:3.0.1"
|
||||||
|
checksum: 10c0/bdc36fb8095d3b41df197f5fb6f11e3a26adf4059df3213e3baa93810d8f0cc76f9a74aaefc18b73e91fe7e19154ed6f134eda6fded2e0f1c8d2272ed2d2d391
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"validator@npm:^13.11.0":
|
||||||
version: 13.15.15
|
version: 13.15.15
|
||||||
resolution: "validator@npm:13.15.15"
|
resolution: "validator@npm:13.15.15"
|
||||||
checksum: 10c0/f5349d1fbb9cc36f9f6c5dab1880764ddad1d0d2b084e2a71e5964f7de1635d20e406611559df9a3db24828ce775cbee5e3b6dd52f0d555a61939ed7ea5990bd
|
checksum: 10c0/f5349d1fbb9cc36f9f6c5dab1880764ddad1d0d2b084e2a71e5964f7de1635d20e406611559df9a3db24828ce775cbee5e3b6dd52f0d555a61939ed7ea5990bd
|
||||||
|
@ -9513,13 +9467,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"wrappy@npm:1":
|
|
||||||
version: 1.0.2
|
|
||||||
resolution: "wrappy@npm:1.0.2"
|
|
||||||
checksum: 10c0/56fece1a4018c6a6c8e28fbc88c87e0fbf4ea8fd64fc6c63b18f4acc4bd13e0ad2515189786dd2c30d3eec9663d70f4ecf699330002f8ccb547e4a18231fc9f0
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"xml-but-prettier@npm:^1.0.1":
|
"xml-but-prettier@npm:^1.0.1":
|
||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
resolution: "xml-but-prettier@npm:1.0.1"
|
resolution: "xml-but-prettier@npm:1.0.1"
|
||||||
|
@ -9564,13 +9511,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"yaml@npm:2.0.0-1":
|
|
||||||
version: 2.0.0-1
|
|
||||||
resolution: "yaml@npm:2.0.0-1"
|
|
||||||
checksum: 10c0/e76eba2fbae37cd3e5bff057184be7cdca849895149d2f5660386871a501d76d2e1ec5906c48269a9fe798f214df31d342675b37bcd9d09af7c12eb6fb46a740
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"yaml@npm:^1.10.0":
|
"yaml@npm:^1.10.0":
|
||||||
version: 1.10.2
|
version: 1.10.2
|
||||||
resolution: "yaml@npm:1.10.2"
|
resolution: "yaml@npm:1.10.2"
|
||||||
|
@ -9609,6 +9549,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"yn@npm:3.1.1":
|
||||||
|
version: 3.1.1
|
||||||
|
resolution: "yn@npm:3.1.1"
|
||||||
|
checksum: 10c0/0732468dd7622ed8a274f640f191f3eaf1f39d5349a1b72836df484998d7d9807fbea094e2f5486d6b0cd2414aad5775972df0e68f8604db89a239f0f4bf7443
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"yocto-queue@npm:^0.1.0":
|
"yocto-queue@npm:^0.1.0":
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
resolution: "yocto-queue@npm:0.1.0"
|
resolution: "yocto-queue@npm:0.1.0"
|
||||||
|
@ -9616,23 +9563,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"z-schema@npm:^5.0.1":
|
|
||||||
version: 5.0.5
|
|
||||||
resolution: "z-schema@npm:5.0.5"
|
|
||||||
dependencies:
|
|
||||||
commander: "npm:^9.4.1"
|
|
||||||
lodash.get: "npm:^4.4.2"
|
|
||||||
lodash.isequal: "npm:^4.5.0"
|
|
||||||
validator: "npm:^13.7.0"
|
|
||||||
dependenciesMeta:
|
|
||||||
commander:
|
|
||||||
optional: true
|
|
||||||
bin:
|
|
||||||
z-schema: bin/z-schema
|
|
||||||
checksum: 10c0/e4c812cfe6468c19b2a21d07d4ff8fb70359062d33400b45f89017eaa3efe9d51e85963f2b115eaaa99a16b451782249bf9b1fa8b31d35cc473e7becb3e44264
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"zenscroll@npm:^4.0.2":
|
"zenscroll@npm:^4.0.2":
|
||||||
version: 4.0.2
|
version: 4.0.2
|
||||||
resolution: "zenscroll@npm:4.0.2"
|
resolution: "zenscroll@npm:4.0.2"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue