diff --git a/app/controllers/auth_controller.ts b/app/controllers/auth_controller.ts index 2c76bda..e3f7138 100644 --- a/app/controllers/auth_controller.ts +++ b/app/controllers/auth_controller.ts @@ -9,8 +9,13 @@ dotenv.config() export default class AuthController { /** - * Register a new user using email and password. - * @returns HTTP status code + message + * @register + * @operationId register + * @description Register a new user with email, password, first name and last name + * @requestBody { "email" : "mariorossi@mail.com", "password" : "Password123", "firstName" : "Mario", "lastName" : "Rossi" } + * @responseBody 201 - { "message" : "Utente registrato correttamente" } + * @responseBody 400 - { "message" : "L'utente esiste già" } + * @responseBody 500 - { "message" : "Errore durante la registrazione" } */ public async register({ request, response }: { request: any; response: any }) { const { email, password, firstName, lastName } = request.body() @@ -34,8 +39,14 @@ export default class AuthController { } } /** - * Login a user using email and password. If the user exists and the password is correct, a JWT token is generated. - * @returns HTTP status code + message + JWT token + * @login + * @opetarionId login + * @description Login a user with email and password and set a JWT token in a cookie + * @requestBody { "email" : "mariorossi@mail.com", "password" : "Password123" } + * @responseBody 200 - { "message" : "Login effettuato con successo", "user" : { "_id" : "string", "email" : "string", "firstName" : "string", "lastName" : "string" } } + * @responseBody 400 - { "message" : "Credenziali invalide" } + * @responseBody 500 - { "message" : "Errore durante il login" } + * @cookie token - JWT token */ public async login({ request, response }: { request: any; response: any }) { const { email, password } = request.body() @@ -112,7 +123,13 @@ export default class AuthController { return response.status(500).json({ message: "Errore durante l'aggiornamento del token" }) } } - + /** + * @logout + * @operationId logout + * @description Logout a user and clear the JWT token from the cookie + * @responseBody 200 - { "message" : "Logout effettuato con successo" } + * @responseBody 500 - { "message" : "Errore durante il logout" } + */ public async logout({ response }: { response: any }) { try { await response.clearCookie('token') diff --git a/app/controllers/construction_sites_controller.ts b/app/controllers/construction_sites_controller.ts index 497cfaf..52c952f 100644 --- a/app/controllers/construction_sites_controller.ts +++ b/app/controllers/construction_sites_controller.ts @@ -7,8 +7,10 @@ import { cuid } from '@adonisjs/core/helpers' export default class ConstructionSitesController { /** - * Main method for listing all construction sites. - * @returns list of all construction sites + * @index + * @operationId getAllConstructionSites + * @description Get all construction sites and the user's subscriptions if authenticated + * @responseBody 200 - { "constructionSites" : [ { "_id" : "67aa43801c22906f341e0880", "name" : "Cantiere", "street" : "Via del Brennero", "description" : "Costruzione di un nuovo cantiere", "impacts_road" : "true", "image_path" : "cantiere.png", "is_subscribed" : "true" } ] } */ async index({ request, response }: HttpContext) { const user = request.user @@ -35,9 +37,11 @@ export default class ConstructionSitesController { } /** - * Method for showing a specific construction site. - * @param params: id of the construction site - * @returns list with the construction site with the given id + * @show + * @operationId getConstructionSites + * @description Get construction sites by query and the user's subscriptions if authenticated + * @requestParam query - string - Query to search for in the street and name fields + * @responseBody 200 - { "constructionSites" : [ { "_id" : "67aa43801c22906f341e0880", "name" : "Cantiere", "street" : "Via del Brennero", "description" : "Costruzione di un nuovo cantiere", "impacts_road" : "true", "image_path" : "cantiere.png", "is_subscribed" : "true" } ] } */ async show({ request, params, response }: HttpContext) { const user = request.user @@ -69,9 +73,13 @@ export default class ConstructionSitesController { } /** - * Method for creating a new construction site. - * @param request: construction site data. List can be found in app/models/construction_site_model.ts - * @returns the created construction site + * @store + * @operationId createConstructionSite + * @description Create a new construction site + * @requestBody { "name" : "Cantiere", "street" : "Via del Brennero", "description" : "Costruzione di un nuovo cantiere", "impacts_road" : "true", "image" : "cantiere.png" } + * @responseBody 201 - { "name" : "Cantiere", "street" : "Via del Brennero", "description" : "Costruzione di un nuovo cantiere", "impacts_road" : "true", "image_path" : "cantiere.png" } + * @responseBody 400 - { "message" : "Errore nell'inserimento del cantiere" } + * @responseBody 500 - { "message" : "Errore nell'inserimento del cantiere" } */ async store({ request, response }: HttpContext) { const constructionSite = new ConstructionSite(request.all()) @@ -99,9 +107,13 @@ export default class ConstructionSitesController { } /** - * Method for updating an existing construction site. - * @param params: id of the construction site, request: construction site data to update - * @returns the updated construction site + * @update + * @operationId updateConstructionSite + * @description Update a construction site by ID + * @requestBody { "name" : "Cantiere", "street" : "Via del Brennero", "description" : "Costruzione di un nuovo cantiere", "impacts_road" : "true", "image" : "cantiere.png" } + * @requestParam id - string - ID of the construction site + * @responseBody 200 - { "name" : "Cantiere", "street" : "Via del Brennero", "description" : "Costruzione di un nuovo cantiere", "impacts_road" : "true", "image_path" : "cantiere.png" } + * @responseBody 404 - { "message" : "Cantiere non trovato" } */ async update({ params, request, response }: HttpContext) { try { @@ -123,9 +135,12 @@ export default class ConstructionSitesController { } /** - * Method for deleting a construction site. - * @param params: id of the construction site - * @returns the deleted construction site + * @destroy + * @operationId deleteConstructionSite + * @description Delete a construction site by ID + * @requestParam id - string - ID of the construction site + * @responseBody 204 - { "message" : "Cantiere eliminato con successo" } + * @responseBody 404 - { "message" : "Cantiere non trovato" } */ async destroy({ params, response }: HttpContext) { try { diff --git a/app/controllers/notifications_controller.ts b/app/controllers/notifications_controller.ts index d40fb83..6ce6aa7 100644 --- a/app/controllers/notifications_controller.ts +++ b/app/controllers/notifications_controller.ts @@ -1,6 +1,14 @@ import type { HttpContext } from '@adonisjs/core/http' import Notification from '../models/notification_model.js' +/** + * @getNotifications + * @operationId getNotifications + * @description Get all unread notifications for the authenticated user + * @responseBody 200 - { "notifications" : [ { "_id" : "67aa43801c22906f341e0880", "user_id" : "67aa43801c22906f341e0880", "message" : "Nuova notifica", "read" : "false" } ] } + * @responseBody 400 - { "message" : "Errore durante il recupero delle notifiche" } + * @responseBody 401 - { "message" : "Non autorizzato" } + */ export default class NotificationsController { public async getNotifications({ request, response }: HttpContext) { try { @@ -12,6 +20,14 @@ export default class NotificationsController { } } + /** + * @markAsRead + * @operationId markAsRead + * @description Mark all notifications as read for the authenticated user + * @responseBody 200 - { "message" : "Notifications marked as read" } + * @responseBody 400 - { "message" : "Errore durante la marcatura delle notifiche come lette" } + * @responseBody 401 - { "message" : "Non autorizzato" } + */ public async markAsRead({ request, response }: HttpContext) { try { let user = request.user diff --git a/app/controllers/subscriptions_controller.ts b/app/controllers/subscriptions_controller.ts index 94ca736..771624d 100644 --- a/app/controllers/subscriptions_controller.ts +++ b/app/controllers/subscriptions_controller.ts @@ -2,6 +2,15 @@ import type { HttpContext } from '@adonisjs/core/http' import Subscription from '../models/subscription_model.js' export default class SubscriptionsController { + /** + * @subscribe + * @operationId subscribe + * @description Subscribe the authenticated user to a construction site + * @requestBody { "construction_site_id" : "67aa43801c22906f341e0880" } + * @responseBody 201 - { "user_id" : "67aa43801c22906f341e0880", "construction_site_id" : "67aa438 + * @responseBody 400 - { "message" : "Dati mancanti: user_id o construction_site_id null" } + * @responseBody 409 - { "message" : "L'utente è già iscritto a questo cantiere" } + */ public async subscribe({ request, response }: HttpContext) { const user = request.user const constructionSiteId = request.body().construction_site_id @@ -25,7 +34,14 @@ export default class SubscriptionsController { return response.created(subscription) } - // ROTUE IS DELETE NOT POST + /** + * @unsubscribe + * @operationId unsubscribe + * @description Unsubscribe the authenticated user from a construction site + * @requestParam id - string - Construction site ID + * @response 204 + * @response 404 + */ public async unsubscribe({ params, request, response }: HttpContext) { let user = request.user let subscription = await Subscription.findOne({ diff --git a/config/swagger.ts b/config/swagger.ts new file mode 100644 index 0000000..4541b5d --- /dev/null +++ b/config/swagger.ts @@ -0,0 +1,28 @@ +// for AdonisJS v6 +import path from 'node:path' +import url from 'node:url' +// --- + +export default { + // path: __dirname + '/../', for AdonisJS v5 + path: path.dirname(url.fileURLToPath(import.meta.url)) + '/../', // for AdonisJS v6 + title: 'Citium', + version: '1.0.0', // use info instead + description: 'Citium API documentation', + tagIndex: 2, + info: { + title: 'Citium', + version: '1.0.0', + description: 'Citium API documentation', + }, + snakeCase: true, + + debug: false, // set to true, to get some useful debug output + ignore: ['/swagger', '/docs'], + preferredPutPatch: 'PUT', // if PUT/PATCH are provided for the same route, prefer PUT + common: { + parameters: {}, // OpenAPI conform parameters that are commonly used + headers: {}, // OpenAPI conform headers that are commonly used + }, + showFullPath: false, // the path displayed after endpoint summary +} diff --git a/package-lock.json b/package-lock.json index 71b67fa..5883ac5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@adonisjs/lucid": "^21.3.0", "@adonisjs/session": "^7.5.0", "@vinejs/vine": "^2.1.0", + "adonis-autoswagger": "^3.64.0", "bcrypt": "^5.1.1", "jsonwebtoken": "^9.0.2", "mongodb-memory-server": "^10.1.3", @@ -2339,6 +2340,30 @@ "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", "license": "MIT" }, + "node_modules/abstract-syntax-tree": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/abstract-syntax-tree/-/abstract-syntax-tree-2.22.0.tgz", + "integrity": "sha512-Px1YA1lvdQN/DGqyZ4rIp6LH8mEtRcyFYZw48cYQ/fK0r7QffIPkEV2ob3g804aS9OjUwuKoqhPTKb3kvZVhug==", + "dependencies": { + "ast-types": "0.14.2", + "astring": "^1.8.6", + "esquery": "^1.5.0", + "meriyah": "^4.4.0", + "pure-conditions": "^1.2.1", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=20.11.1" + } + }, + "node_modules/abstract-syntax-tree/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2356,7 +2381,6 @@ "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -2369,7 +2393,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -2388,6 +2411,63 @@ "node": ">=0.4.0" } }, + "node_modules/adonis-autoswagger": { + "version": "3.64.0", + "resolved": "https://registry.npmjs.org/adonis-autoswagger/-/adonis-autoswagger-3.64.0.tgz", + "integrity": "sha512-pq5odOXGqTk5uwirNMjPqqKORLADz0WDwyTy8IhHtJRLI4vRQu5AsO1xwW+SjJQ2yp+bMRlAZqJvB2vgoXaSXw==", + "dependencies": { + "@vinejs/vine": "^2.1.0", + "abstract-syntax-tree": "^2.20.5", + "change-case": "^4.1.2", + "espree": "^9.3.1", + "extract-comments": "^1.1.0", + "http-status-code": "^2.1.0", + "json-to-pretty-yaml": "^1.2.2", + "lodash": "^4.17.21", + "parse-imports": "^1.1.2", + "typescript": "^5.3.3", + "typescript-parser": "^2.6.1" + } + }, + "node_modules/adonis-autoswagger/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/adonis-autoswagger/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/adonis-autoswagger/node_modules/parse-imports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-1.2.0.tgz", + "integrity": "sha512-K5aG9cextqjAlyevwuSMjWPbBr+X8xGgfHS4VopbKC1u3jLndRGl2CoUHMTvuD6LIg4di5TzH/Pw9+XZyTjI/w==", + "dependencies": { + "es-module-lexer": "^1.5.2", + "slashes": "^3.0.12" + }, + "engines": { + "node": ">= 12.17" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -2668,6 +2748,25 @@ "node": ">=12" } }, + "node_modules/ast-types": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", + "integrity": "sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "bin": { + "astring": "bin/astring" + } + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -3002,6 +3101,15 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, "node_modules/camelcase": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", @@ -3035,6 +3143,16 @@ ], "license": "CC-BY-4.0" }, + "node_modules/capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, "node_modules/case-anything": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-3.1.0.tgz", @@ -3091,6 +3209,25 @@ "node": ">=8" } }, + "node_modules/change-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", + "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "dependencies": { + "camel-case": "^4.1.2", + "capital-case": "^1.0.4", + "constant-case": "^3.0.4", + "dot-case": "^3.0.4", + "header-case": "^2.0.4", + "no-case": "^3.0.4", + "param-case": "^3.0.4", + "pascal-case": "^3.1.2", + "path-case": "^3.0.4", + "sentence-case": "^3.0.4", + "snake-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", @@ -3423,6 +3560,16 @@ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "license": "ISC" }, + "node_modules/constant-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case": "^2.0.2" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -3849,6 +3996,15 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "license": "MIT" }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", @@ -4412,7 +4568,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -4422,11 +4577,21 @@ "node": ">=4" } }, + "node_modules/esprima-extract-comments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esprima-extract-comments/-/esprima-extract-comments-1.1.0.tgz", + "integrity": "sha512-sBQUnvJwpeE9QnPrxh7dpI/dp67erYG4WXEAreAMoelPRpMR7NWb4YtwRPn9b+H1uLQKl/qS8WYmyaljTpjIsw==", + "dependencies": { + "esprima": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" @@ -4452,7 +4617,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -4524,6 +4688,18 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/extract-comments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/extract-comments/-/extract-comments-1.1.0.tgz", + "integrity": "sha512-dzbZV2AdSSVW/4E7Ti5hZdHWbA+Z80RJsJhr5uiL10oyjl/gy7/o+HI1HwK4/WSZhlq4SNKU3oUzXlM13Qx02Q==", + "dependencies": { + "esprima-extract-comments": "^1.1.0", + "parse-code-context": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/fast-copy": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", @@ -5348,6 +5524,15 @@ "he": "bin/he" } }, + "node_modules/header-case": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", + "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "dependencies": { + "capital-case": "^1.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/help-me": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", @@ -5408,6 +5593,25 @@ "node": ">= 0.8" } }, + "node_modules/http-status-code": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-status-code/-/http-status-code-2.1.0.tgz", + "integrity": "sha512-xlSRDG0i4j2HDR85FW2VGxFBo9GB1LpBp37/gldjrS5ZlAaHp1h0dbq96crIN/D0ur1jAa75ACrN8WezebLXSw==", + "dependencies": { + "strip-json-comments": "^1.0.2" + } + }, + "node_modules/http-status-code/node_modules/strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha512-AOPG8EBc5wAikaG1/7uFCNFJwnKOuQwFTpYBdTW6OvWHeZBQBrAA/amefHGrEiOnCPcLFZK6FUPtWVKpQVIRgg==", + "bin": { + "strip-json-comments": "cli.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -6355,6 +6559,18 @@ "dev": true, "license": "MIT" }, + "node_modules/json-to-pretty-yaml": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz", + "integrity": "sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A==", + "dependencies": { + "remedial": "^1.0.7", + "remove-trailing-spaces": "^1.0.6" + }, + "engines": { + "node": ">= 0.2.0" + } + }, "node_modules/jsonschema": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz", @@ -6601,7 +6817,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "dev": true, "license": "MIT" }, "node_modules/lodash.flatten": { @@ -6779,6 +6994,14 @@ "dev": true, "license": "MIT" }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -6886,6 +7109,14 @@ "node": ">= 8" } }, + "node_modules/meriyah": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/meriyah/-/meriyah-4.5.0.tgz", + "integrity": "sha512-Rbiu0QPIxTXgOXwiIpRVJfZRQ2FWyfzYrOGBs9SN5RbaXg1CN5ELn/plodwWwluX93yzc4qO/bNIen1ThGFCxw==", + "engines": { + "node": ">=10.4.0" + } + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -7333,6 +7564,15 @@ "node": ">=12.22.0" } }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", @@ -7744,6 +7984,15 @@ "integrity": "sha512-+vYvA/Y31l8Zk8dwxHhL3JfTuHPm6tlxM2A3GeQyl7ovYnSp1+mzAxClxaOr0qO1TtPxbQxetI7v5XqKLJZk7Q==", "license": "MIT" }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7757,6 +8006,14 @@ "node": ">=6" } }, + "node_modules/parse-code-context": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-code-context/-/parse-code-context-1.0.0.tgz", + "integrity": "sha512-OZQaqKaQnR21iqhlnPfVisFjBWjhnMl5J9MgbP8xC+EwoVqbXrq78lp+9Zb3ahmLzrIX5Us/qbvBnaS3hkH6OA==", + "engines": { + "node": ">=6" + } + }, "node_modules/parse-imports": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.2.1.tgz", @@ -7801,6 +8058,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -7808,6 +8074,15 @@ "devOptional": true, "license": "MIT" }, + "node_modules/path-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", + "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -8289,6 +8564,11 @@ "node": ">=6" } }, + "node_modules/pure-conditions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pure-conditions/-/pure-conditions-1.2.1.tgz", + "integrity": "sha512-MKk7sKQiR3Fe3bL/QedUZ1eVoNO0xpOCyiTGdVrK+4ZCDa9TgwNp6D/U1FthjhVGS9a857BS84WncvZvOYECUw==" + }, "node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", @@ -8690,6 +8970,19 @@ "jsesc": "bin/jsesc" } }, + "node_modules/remedial": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz", + "integrity": "sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==", + "engines": { + "node": "*" + } + }, + "node_modules/remove-trailing-spaces": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz", + "integrity": "sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA==" + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -8915,6 +9208,16 @@ "node": ">=10" } }, + "node_modules/sentence-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", + "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, "node_modules/serialize-error": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", @@ -9193,6 +9496,15 @@ "node": ">=8.0.0" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/sonic-boom": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", @@ -10034,7 +10346,6 @@ "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", - "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -10067,6 +10378,34 @@ "typescript": ">=4.8.4 <5.8.0" } }, + "node_modules/typescript-parser": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typescript-parser/-/typescript-parser-2.6.1.tgz", + "integrity": "sha512-p4ZC10pu67KO8+WJALsJWhbAq4pRBIcP+ls8Bhl+V8KvzYQDwxw/P5hJhn3rBdLnfS5aGLflfh7WiZpN6yi+5g==", + "dependencies": { + "lodash": "^4.17.10", + "lodash-es": "^4.17.10", + "tslib": "^1.9.3", + "typescript": "^3.0.3" + } + }, + "node_modules/typescript-parser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/typescript-parser/node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -10183,6 +10522,22 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", + "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index 68b617b..dab7d5e 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "@adonisjs/lucid": "^21.3.0", "@adonisjs/session": "^7.5.0", "@vinejs/vine": "^2.1.0", + "adonis-autoswagger": "^3.64.0", "bcrypt": "^5.1.1", "jsonwebtoken": "^9.0.2", "mongodb-memory-server": "^10.1.3", diff --git a/start/routes.ts b/start/routes.ts index 3ad6356..0bb9f0b 100644 --- a/start/routes.ts +++ b/start/routes.ts @@ -12,6 +12,8 @@ import router from '@adonisjs/core/services/router' import { sep, normalize } from 'node:path' import app from '@adonisjs/core/services/app' import { middleware } from './kernel.js' +import AutoSwagger from 'adonis-autoswagger' +import swagger from '#config/swagger' // Import controllers const AuthController = () => import('#controllers/auth_controller') @@ -32,20 +34,24 @@ router.get('/uploads/*', ({ request, response }) => { return response.download(absolutePath) }) +router.get('/swagger', async () => { + return AutoSwagger.default.docs(router.toJSON(), swagger); +}) + +// Renders Swagger-UI and passes YAML-output of /swagger +router.get('/docs', async () => { + return AutoSwagger.default.ui('/swagger', swagger) + // return AutoSwagger.default.scalar("/swagger"); to use Scalar instead + // return AutoSwagger.default.rapidoc("/swagger", "view"); to use RapiDoc instead (pass "view" default, or "read" to change the render-style) +}) + +router.get('/yaml', async () => { + return AutoSwagger.default.jsonToYaml(router.toJSON()) +}) + // API routes router .group(() => { - // Health check route - router.get('/test', () => { - return { message: 'Hello world' } - }) - - router - .get('/protected', () => { - return { message: 'This is a protected route' } - }) - .use(middleware.jwtAuth()) - // Auth routes router.post('/register', [AuthController, 'register']) router.post('/login', [AuthController, 'login'])