Adding Swagger

This commit is contained in:
2026-03-29 23:56:11 +02:00
parent fce91fa465
commit fe1bcc0ff7
11 changed files with 1883 additions and 16 deletions
+210
View File
@@ -0,0 +1,210 @@
# Guide Swagger - Documentation OpenAPI pour Adastra API
## Accès à la documentation
Une fois le serveur lancé, accédez à : **http://localhost:3200/api-docs**
## Exemple de documentation pour une route
Voici un exemple de comment documenter une GET route avec des paramètres de query :
```javascript
/**
* @swagger
* /api/v1/articles:
* get:
* summary: Récupère la liste des articles
* description: Retourne une liste paginée de tous les articles
* tags:
* - Articles
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* default: 1
* description: Numéro de page
* - in: query
* name: limit
* schema:
* type: integer
* default: 10
* description: Nombre d'articles par page
* responses:
* 200:
* description: Liste des articles récupérée avec succès
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* type: array
* items:
* type: object
* properties:
* id:
* type: integer
* title:
* type: string
* content:
* type: string
* total:
* type: integer
* 400:
* description: Erreur de validation
* 401:
* description: Non authentifié
* 500:
* description: Erreur serveur
*/
router.get('/', async (req, res) => {
// ... logique du contrôleur
});
```
## Exemple avec POST et authentification
```javascript
/**
* @swagger
* /api/v1/articles:
* post:
* summary: Crée un nouvel article
* description: Crée un nouvel article avec les données fournies
* tags:
* - Articles
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - title
* - content
* properties:
* title:
* type: string
* example: "Mon Article"
* content:
* type: string
* example: "Contenu de l'article"
* tags:
* type: array
* items:
* type: string
* example: ["tag1", "tag2"]
* responses:
* 201:
* description: Article créé avec succès
* content:
* application/json:
* schema:
* type: object
* properties:
* id:
* type: integer
* title:
* type: string
* content:
* type: string
* 400:
* description: Données invalides
* 401:
* description: Non authentifié
*/
router.post('/', authenticate, async (req, res) => {
// ... logique du contrôleur
});
```
## Exemple avec paramètres URL
```javascript
/**
* @swagger
* /api/v1/articles/{id}:
* get:
* summary: Récupère un article par ID
* tags:
* - Articles
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID de l'article
* responses:
* 200:
* description: Article récupéré avec succès
* 404:
* description: Article non trouvé
* 500:
* description: Erreur serveur
*/
router.get('/:id', async (req, res) => {
// ... logique du contrôleur
});
```
## Types de schémas courants
### Objets simples
```javascript
schema: {
type: object
properties:
id: { type: integer }
name: { type: string }
email: { type: string, format: email }
age: { type: integer, minimum: 0 }
isActive: { type: boolean }
}
```
### Arrays
```javascript
schema: {
type: array
items:
type: object
properties:
id: { type: integer }
name: { type: string }
}
```
### Enums
```javascript
schema: {
type: string
enum: ["pending", "approved", "rejected"]
}
```
## Tags disponibles
Les tags disponibles dans la config Swagger sont :
- **Articles** - Gestion des articles
- **Users** - Gestion des utilisateurs
- **Products** - Gestion des produits
- **Clans** - Gestion des clans
- **Hero Wars** - Données Hero Wars
## Instructions générales
1. **Placez les commentaires JSDoc directement au-dessus de la définition de la route**
2. **Utilisez le tag `@swagger`** pour indiquer que c'est de la documentation OpenAPI
3. **Commencez par le chemin d'accès** : `/api/v1/articles`, `/api/v1/articles/{id}`, etc.
4. **Spécifiez la méthode HTTP** : `get`, `post`, `put`, `delete`, `patch`
5. **Incluez toujours** : `summary`, `description`, `tags`, `responses`
6. **Pour les routes protégées**, utilisez `security: [{ bearerAuth: [] }]` ou `security: [{ apiKeyAuth: [] }]`
## Pour plus de détails
Consultez la documentation officielle : https://swagger.io/specification/
---
**Note** : Une fois que vous aurez ajouté les commentaires Swagger à vos routes, redémarrez le serveur et accédez à `/api-docs` pour voir la documentation mise à jour.
+10 -1
View File
@@ -15,7 +15,9 @@ var express = require('express'),
const DateUtilities = require('./src/utils/dateUtilities'),
exceptionHandler = require('./src/middlewares/exceptions.handler'),
connectDb = require('./src/database/connectDb'),
config = require('./src/config');
config = require('./src/config'),
swaggerUi = require('swagger-ui-express'),
swaggerSpec = require('./src/config/swagger');
// Create global app object
var app = express();
@@ -57,6 +59,13 @@ require('./src/database/models/mongo');
config.initAuth();
//config.initApiKey();
// Setup Swagger documentation
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
swaggerOptions: {
persistAuthorization: true,
},
}));
// Register routes AFTER connections are established
const setupRoutes = () => {
app.use(require('./src/routes'));
+1 -8
View File
@@ -1,12 +1,5 @@
# Environment files - keep .example files only
.env
.env.local
.env.development
.env.production
.env.test
.env*
# Keep example files for documentation
!.env.example
!.env.local.example
!.env.development.example
!.env.production.example
+212 -7
View File
@@ -19,6 +19,7 @@
"express": "^4.18.2",
"express-jwt": "^8.4.1",
"express-session": "^1.18.0",
"js-yaml": "^4.1.1",
"jsonwebtoken": "^9.0.2",
"method-override": "3.0.0",
"methods": "1.1.2",
@@ -34,6 +35,8 @@
"sequelize": "^6.37.3",
"sequelize-auto": "^0.8.8",
"slug": "^8.2.3",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
"underscore": "^1.13.6",
"uuid": "^9.0.1"
},
@@ -58,6 +61,46 @@
"node": ">=0.10.0"
}
},
"node_modules/@apidevtools/json-schema-ref-parser": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz",
"integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==",
"dependencies": {
"@jsdevtools/ono": "^7.1.3",
"@types/json-schema": "^7.0.6",
"call-me-maybe": "^1.0.1",
"js-yaml": "^4.1.0"
}
},
"node_modules/@apidevtools/openapi-schemas": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
"integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==",
"engines": {
"node": ">=10"
}
},
"node_modules/@apidevtools/swagger-methods": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
"integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg=="
},
"node_modules/@apidevtools/swagger-parser": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz",
"integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==",
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^9.0.6",
"@apidevtools/openapi-schemas": "^2.0.4",
"@apidevtools/swagger-methods": "^3.0.2",
"@jsdevtools/ono": "^7.1.3",
"call-me-maybe": "^1.0.1",
"z-schema": "^5.0.1"
},
"peerDependencies": {
"openapi-types": ">=7"
}
},
"node_modules/@aws-crypto/crc32": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz",
@@ -990,6 +1033,11 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/@jsdevtools/ono": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="
},
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
@@ -1133,6 +1181,12 @@
"node": "*"
}
},
"node_modules/@scarf/scarf": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
"integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==",
"hasInstallScript": true
},
"node_modules/@sendgrid/client": {
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-7.7.0.tgz",
@@ -1747,6 +1801,11 @@
"@types/ms": "*"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
},
"node_modules/@types/jsonwebtoken": {
"version": "9.0.6",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz",
@@ -1927,8 +1986,7 @@
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/array-flatten": {
"version": "1.1.1",
@@ -2199,6 +2257,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/call-me-maybe": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
"integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ=="
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -2557,6 +2620,17 @@
"node": ">=8"
}
},
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"dependencies": {
"esutils": "^2.0.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/dotenv": {
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
@@ -2908,7 +2982,6 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -4118,10 +4191,9 @@
"dev": true
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dependencies": {
"argparse": "^2.0.1"
},
@@ -4312,6 +4384,12 @@
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead."
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
@@ -4337,6 +4415,11 @@
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"node_modules/lodash.mergewith": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ=="
},
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
@@ -5037,6 +5120,12 @@
"wrappy": "1"
}
},
"node_modules/openapi-types": {
"version": "12.1.3",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
"peer": true
},
"node_modules/optionator": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
@@ -6428,6 +6517,86 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/swagger-jsdoc": {
"version": "6.2.8",
"resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz",
"integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==",
"dependencies": {
"commander": "6.2.0",
"doctrine": "3.0.0",
"glob": "7.1.6",
"lodash.mergewith": "^4.6.2",
"swagger-parser": "^10.0.3",
"yaml": "2.0.0-1"
},
"bin": {
"swagger-jsdoc": "bin/swagger-jsdoc.js"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/swagger-jsdoc/node_modules/commander": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz",
"integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
"engines": {
"node": ">= 6"
}
},
"node_modules/swagger-jsdoc/node_modules/glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/swagger-parser": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz",
"integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==",
"dependencies": {
"@apidevtools/swagger-parser": "10.0.3"
},
"engines": {
"node": ">=10"
}
},
"node_modules/swagger-ui-dist": {
"version": "5.32.1",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.32.1.tgz",
"integrity": "sha512-6HQoo7+j8PA2QqP5kgAb9dl1uxUjvR0SAoL/WUp1sTEvm0F6D5npgU2OGCLwl++bIInqGlEUQ2mpuZRZYtyCzQ==",
"dependencies": {
"@scarf/scarf": "=1.4.0"
}
},
"node_modules/swagger-ui-express": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz",
"integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==",
"dependencies": {
"swagger-ui-dist": ">=5.0.0"
},
"engines": {
"node": ">= v0.10.32"
},
"peerDependencies": {
"express": ">=4.0.0 || >=5.0.0-beta"
}
},
"node_modules/tar": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
@@ -6863,6 +7032,14 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/yaml": {
"version": "2.0.0-1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz",
"integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==",
"engines": {
"node": ">= 6"
}
},
"node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
@@ -6899,6 +7076,34 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/z-schema": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz",
"integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==",
"dependencies": {
"lodash.get": "^4.4.2",
"lodash.isequal": "^4.5.0",
"validator": "^13.7.0"
},
"bin": {
"z-schema": "bin/z-schema"
},
"engines": {
"node": ">=8.0.0"
},
"optionalDependencies": {
"commander": "^9.4.1"
}
},
"node_modules/z-schema/node_modules/commander": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
"optional": true,
"engines": {
"node": "^12.20.0 || >=14"
}
}
}
}
+3
View File
@@ -34,6 +34,7 @@
"express": "^4.18.2",
"express-jwt": "^8.4.1",
"express-session": "^1.18.0",
"js-yaml": "^4.1.1",
"jsonwebtoken": "^9.0.2",
"method-override": "3.0.0",
"methods": "1.1.2",
@@ -49,6 +50,8 @@
"sequelize": "^6.37.3",
"sequelize-auto": "^0.8.8",
"slug": "^8.2.3",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
"underscore": "^1.13.6",
"uuid": "^9.0.1"
},
+130
View File
@@ -0,0 +1,130 @@
const swaggerJsdoc = require('swagger-jsdoc');
const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');
// Fonction pour charger tous les fichiers YAML du dossier swagger
const loadSwaggerFiles = () => {
const swaggerDir = path.join(__dirname, '../swagger');
const files = fs.readdirSync(swaggerDir).filter(file => file.endsWith('.yaml'));
const merged = { paths: {}, components: { schemas: {} } };
files.forEach(file => {
try {
const content = fs.readFileSync(path.join(swaggerDir, file), 'utf8');
const doc = yaml.load(content);
// Fusionner les paths
if (doc.paths) {
Object.assign(merged.paths, doc.paths);
}
// Fusionner les schemas
if (doc.components && doc.components.schemas) {
Object.assign(merged.components.schemas, doc.components.schemas);
}
} catch (error) {
console.error(`Erreur lors du chargement de ${file}:`, error);
}
});
return merged;
};
const swaggerDefinitions = loadSwaggerFiles();
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'Adastra API',
version: '1.0.0',
description: 'Adastra API Documentation - Express.js API with Sequelize (MySQL) and Mongoose (MongoDB)',
contact: {
name: 'Julien Gautier',
email: 'jgautier.webdev@gmail.com',
},
},
servers: [
{
url: 'http://localhost:3201',
description: 'Development server',
},
{
url: 'http://localhost:3200',
description: 'Local development server',
},
{
url: 'https://api.example.com',
description: 'Production server',
},
],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
description: 'JWT Bearer token authentication',
},
apiKeyAuth: {
type: 'apiKey',
in: 'header',
name: 'x-api-key',
description: 'API Key authentication',
},
},
schemas: {
Error: {
type: 'object',
properties: {
error: {
type: 'string',
},
message: {
type: 'string',
},
statusCode: {
type: 'integer',
},
},
},
...swaggerDefinitions.components.schemas,
},
},
paths: swaggerDefinitions.paths,
tags: [
{
name: 'Articles',
description: 'Article management endpoints',
},
{
name: 'Clans',
description: 'Clan management endpoints',
},
{
name: 'Members',
description: 'Member management endpoints',
},
{
name: 'Users',
description: 'User management endpoints',
},
{
name: 'Products',
description: 'Product management endpoints',
},
{
name: 'Hero Wars',
description: 'Hero Wars game data endpoints',
},
],
},
// Les définitions sont chargées depuis les fichiers YAML
apis: [],
};
const swaggerSpec = swaggerJsdoc(options);
module.exports = swaggerSpec;
+366
View File
@@ -0,0 +1,366 @@
paths:
/api/v2/articles:
get:
summary: Récupère la liste paginée des articles
description: Retourne une liste paginée de tous les articles. L'authentification est optionnelle.
tags:
- Articles
parameters:
- in: query
name: page
schema:
type: integer
default: 1
description: Numéro de page
- in: query
name: limit
schema:
type: integer
default: 20
description: Nombre d'articles par page
- in: query
name: search
schema:
type: string
description: Terme de recherche pour les articles
responses:
'200':
description: Liste des articles récupérée avec succès
content:
application/json:
schema:
type: object
properties:
articles:
type: array
items:
$ref: '#/components/schemas/Article'
articlesCount:
type: integer
'400':
description: Erreur de validation
'500':
description: Erreur serveur
post:
summary: Crée un nouvel article
description: Crée un nouvel article avec les données fournies. Authentification requise.
tags:
- Articles
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- article
properties:
article:
type: object
required:
- title
- description
- body
properties:
title:
type: string
example: "Titre de l'article"
description:
type: string
example: "Description courte"
body:
type: string
example: "Contenu complet de l'article"
tagList:
type: array
items:
type: string
example: ["tag1", "tag2"]
responses:
'201':
description: Article créé avec succès
content:
application/json:
schema:
type: object
properties:
article:
$ref: '#/components/schemas/Article'
'400':
description: Données invalides
'401':
description: Non authentifié
'422':
description: Erreur de validation des données
/api/v2/articles/feed:
get:
summary: Récupère le fil d'actualité des articles
description: Retourne les articles des utilisateurs suivis par l'utilisateur authentifié
tags:
- Articles
security:
- bearerAuth: []
parameters:
- in: query
name: page
schema:
type: integer
default: 1
description: Numéro de page
- in: query
name: limit
schema:
type: integer
default: 20
description: Nombre d'articles par page
responses:
'200':
description: Fil d'actualité récupéré avec succès
content:
application/json:
schema:
type: object
properties:
articles:
type: array
items:
$ref: '#/components/schemas/Article'
articlesCount:
type: integer
'401':
description: Non authentifié
'500':
description: Erreur serveur
/api/v2/articles/{slug}:
get:
summary: Récupère un article par son slug
description: Retourne les détails complets d'un article spécifique
tags:
- Articles
parameters:
- in: path
name: slug
required: true
schema:
type: string
description: Le slug unique de l'article
responses:
'200':
description: Article récupéré avec succès
content:
application/json:
schema:
type: object
properties:
article:
$ref: '#/components/schemas/Article'
'404':
description: Article non trouvé
'500':
description: Erreur serveur
put:
summary: Met à jour un article
description: Met à jour un article existant. Seul l'auteur peut modifier son article. Authentification requise.
tags:
- Articles
security:
- bearerAuth: []
parameters:
- in: path
name: slug
required: true
schema:
type: string
description: Le slug unique de l'article
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- article
properties:
article:
type: object
properties:
title:
type: string
description:
type: string
body:
type: string
tagList:
type: array
items:
type: string
responses:
'200':
description: Article mis à jour avec succès
content:
application/json:
schema:
type: object
properties:
article:
$ref: '#/components/schemas/Article'
'401':
description: Non authentifié
'403':
description: Vous n'êtes pas autorisé à modifier cet article
'404':
description: Article non trouvé
'422':
description: Erreur de validation des données
delete:
summary: Supprime un article
description: Supprime un article existant. Seul l'auteur peut supprimer son article. Authentification requise.
tags:
- Articles
security:
- bearerAuth: []
parameters:
- in: path
name: slug
required: true
schema:
type: string
description: Le slug unique de l'article
responses:
'200':
description: Article supprimé avec succès
'401':
description: Non authentifié
'403':
description: Vous n'êtes pas autorisé à supprimer cet article
'404':
description: Article non trouvé
'500':
description: Erreur serveur
/api/v2/articles/{slug}/favorite:
post:
summary: Ajoute un article aux favoris
description: Ajoute un article à la liste des favoris de l'utilisateur authentifié
tags:
- Articles
security:
- bearerAuth: []
parameters:
- in: path
name: slug
required: true
schema:
type: string
description: Le slug unique de l'article
responses:
'200':
description: Article ajouté aux favoris avec succès
content:
application/json:
schema:
type: object
properties:
article:
$ref: '#/components/schemas/Article'
'401':
description: Non authentifié
'404':
description: Article non trouvé
'500':
description: Erreur serveur
delete:
summary: Retire un article des favoris
description: Retire un article de la liste des favoris de l'utilisateur authentifié
tags:
- Articles
security:
- bearerAuth: []
parameters:
- in: path
name: slug
required: true
schema:
type: string
description: Le slug unique de l'article
responses:
'200':
description: Article retiré des favoris avec succès
content:
application/json:
schema:
type: object
properties:
article:
$ref: '#/components/schemas/Article'
'401':
description: Non authentifié
'404':
description: Article non trouvé
'500':
description: Erreur serveur
components:
schemas:
Article:
type: object
properties:
slug:
type: string
description: Identifiant unique de l'article (généré à partir du titre)
example: "mon-premier-article"
title:
type: string
description: Titre de l'article
example: "Mon Premier Article"
description:
type: string
description: Description courte de l'article
example: "Description courte pour le résumé"
body:
type: string
description: Contenu complet de l'article
example: "Contenu complet de l'article..."
tagList:
type: array
items:
type: string
description: Liste des tags associés à l'article
example: ["tag1", "tag2"]
author:
type: object
properties:
username:
type: string
image:
type: string
bio:
type: string
following:
type: boolean
description: Informations de l'auteur de l'article
createdAt:
type: string
format: date-time
description: Date/heure de création de l'article
example: "2024-01-15T10:30:00Z"
updatedAt:
type: string
format: date-time
description: Date/heure de la dernière mise à jour
example: "2024-01-15T10:30:00Z"
favorited:
type: boolean
description: Indique si l'article est mis en favori par l'utilisateur authentifié
favoritesCount:
type: integer
description: Nombre de fois que cet article a été ajouté aux favoris
example: 5
+242
View File
@@ -0,0 +1,242 @@
paths:
/api/v3/clans:
get:
summary: Récupère la liste de tous les clans
description: Retourne une liste de tous les clans. L'authentification est optionnelle.
tags:
- Clans
parameters:
- in: query
name: page
schema:
type: integer
default: 1
description: Numéro de page
- in: query
name: limit
schema:
type: integer
default: 20
description: Nombre de clans par page
- in: query
name: search
schema:
type: string
description: Terme de recherche pour les clans
responses:
'200':
description: Liste des clans récupérée avec succès
content:
application/json:
schema:
type: object
properties:
clans:
type: array
items:
$ref: '#/components/schemas/Clan'
clansCount:
type: integer
'400':
description: Erreur de validation
'500':
description: Erreur serveur
post:
summary: Crée un nouveau clan
description: Crée un nouveau clan avec les données fournies
tags:
- Clans
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- clan
properties:
clan:
type: object
required:
- name
properties:
name:
type: string
example: "Mon Clan"
description:
type: string
example: "Description du clan"
level:
type: integer
example: 5
server:
type: string
example: "EU"
responses:
'201':
description: Clan créé avec succès
content:
application/json:
schema:
type: object
properties:
clan:
$ref: '#/components/schemas/Clan'
'400':
description: Données invalides
'422':
description: Erreur de validation des données
'500':
description: Erreur serveur
/api/v3/clans/{clanId}:
get:
summary: Récupère les détails d'un clan
description: Retourne les détails complets d'un clan spécifique. Authentification requise.
tags:
- Clans
security:
- bearerAuth: []
parameters:
- in: path
name: clanId
required: true
schema:
type: integer
description: L'ID unique du clan
responses:
'200':
description: Clan récupéré avec succès
content:
application/json:
schema:
type: object
properties:
clan:
$ref: '#/components/schemas/Clan'
'401':
description: Non authentifié
'404':
description: Clan non trouvé
'500':
description: Erreur serveur
put:
summary: Met à jour un clan
description: Met à jour les informations d'un clan. Authentification requise.
tags:
- Clans
security:
- bearerAuth: []
parameters:
- in: path
name: clanId
required: true
schema:
type: integer
description: L'ID unique du clan
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- clan
properties:
clan:
type: object
properties:
name:
type: string
description:
type: string
level:
type: integer
server:
type: string
responses:
'200':
description: Clan mis à jour avec succès
content:
application/json:
schema:
type: object
properties:
clan:
$ref: '#/components/schemas/Clan'
'401':
description: Non authentifié
'404':
description: Clan non trouvé
'422':
description: Erreur de validation des données
'500':
description: Erreur serveur
delete:
summary: Supprime un clan
description: Supprime un clan. Authentification requise.
tags:
- Clans
security:
- bearerAuth: []
parameters:
- in: path
name: clanId
required: true
schema:
type: integer
description: L'ID unique du clan
responses:
'200':
description: Clan supprimé avec succès
'401':
description: Non authentifié
'403':
description: Vous n'êtes pas autorisé à supprimer ce clan
'404':
description: Clan non trouvé
'500':
description: Erreur serveur
components:
schemas:
Clan:
type: object
properties:
id:
type: integer
description: Identifiant unique du clan
name:
type: string
description: Nom du clan
example: "Mon Clan"
description:
type: string
description: Description du clan
level:
type: integer
description: Niveau du clan
example: 5
server:
type: string
description: Serveur du clan
example: "EU"
membersCount:
type: integer
description: Nombre de membres du clan
example: 30
foundedAt:
type: string
format: date-time
description: Date de création du clan
createdAt:
type: string
format: date-time
description: Date/heure de création du clan
updatedAt:
type: string
format: date-time
description: Date/heure de la dernière mise à jour
+270
View File
@@ -0,0 +1,270 @@
paths:
/api/v3/members:
get:
summary: Récupère la liste de tous les membres
description: Retourne une liste paginée de tous les membres. Authentification requise.
tags:
- Members
security:
- bearerAuth: []
parameters:
- in: query
name: page
schema:
type: integer
default: 1
description: Numéro de page
- in: query
name: limit
schema:
type: integer
default: 20
description: Nombre de membres par page
- in: query
name: clanId
schema:
type: integer
description: Filtrer les membres par clan
- in: query
name: search
schema:
type: string
description: Terme de recherche pour les membres
responses:
'200':
description: Liste des membres récupérée avec succès
content:
application/json:
schema:
type: object
properties:
members:
type: array
items:
$ref: '#/components/schemas/Member'
membersCount:
type: integer
'401':
description: Non authentifié
'400':
description: Erreur de validation
'500':
description: Erreur serveur
post:
summary: Crée un nouveau membre
description: Crée un nouveau membre du clan. Authentification requise.
tags:
- Members
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- member
properties:
member:
type: object
required:
- userId
- clanId
properties:
userId:
type: integer
example: 1
clanId:
type: integer
example: 1
role:
type: string
enum: ["member", "officer", "leader"]
default: "member"
example: "member"
joinedAt:
type: string
format: date-time
responses:
'201':
description: Membre créé avec succès
content:
application/json:
schema:
type: object
properties:
member:
$ref: '#/components/schemas/Member'
'400':
description: Données invalides
'401':
description: Non authentifié
'422':
description: Erreur de validation des données
'500':
description: Erreur serveur
/api/v3/members/{memberId}:
get:
summary: Récupère les détails d'un membre
description: Retourne les détails complets d'un membre spécifique. Authentification requise.
tags:
- Members
security:
- bearerAuth: []
parameters:
- in: path
name: memberId
required: true
schema:
type: integer
description: L'ID unique du membre
responses:
'200':
description: Membre récupéré avec succès
content:
application/json:
schema:
type: object
properties:
member:
$ref: '#/components/schemas/Member'
'401':
description: Non authentifié
'404':
description: Membre non trouvé
'500':
description: Erreur serveur
put:
summary: Met à jour un membre
description: Met à jour les informations d'un membre (rôle, date d'adhésion, etc.). Authentification requise.
tags:
- Members
security:
- bearerAuth: []
parameters:
- in: path
name: memberId
required: true
schema:
type: integer
description: L'ID unique du membre
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- member
properties:
member:
type: object
properties:
role:
type: string
enum: ["member", "officer", "leader"]
joinedAt:
type: string
format: date-time
responses:
'200':
description: Membre mis à jour avec succès
content:
application/json:
schema:
type: object
properties:
member:
$ref: '#/components/schemas/Member'
'401':
description: Non authentifié
'404':
description: Membre non trouvé
'422':
description: Erreur de validation des données
'500':
description: Erreur serveur
delete:
summary: Supprime un membre
description: Supprime un membre du clan. Authentification requise.
tags:
- Members
security:
- bearerAuth: []
parameters:
- in: path
name: memberId
required: true
schema:
type: integer
description: L'ID unique du membre
responses:
'200':
description: Membre supprimé avec succès
'401':
description: Non authentifié
'403':
description: Vous n'êtes pas autorisé à supprimer ce membre
'404':
description: Membre non trouvé
'500':
description: Erreur serveur
components:
schemas:
Member:
type: object
properties:
id:
type: integer
description: Identifiant unique du membre
userId:
type: integer
description: ID de l'utilisateur associé
user:
type: object
description: Informations de l'utilisateur
properties:
id:
type: integer
username:
type: string
email:
type: string
image:
type: string
clanId:
type: integer
description: ID du clan
clan:
type: object
description: Informations du clan
properties:
id:
type: integer
name:
type: string
level:
type: integer
role:
type: string
description: Rôle du membre dans le clan
enum: ["member", "officer", "leader"]
example: "member"
joinedAt:
type: string
format: date-time
description: Date d'adhésion au clan
createdAt:
type: string
format: date-time
description: Date/heure de création du membre
updatedAt:
type: string
format: date-time
description: Date/heure de la dernière mise à jour
+209
View File
@@ -0,0 +1,209 @@
# Template pour ajouter une nouvelle ressource Swagger
Utilisez ce fichier comme base pour documenter les routes d'une nouvelle ressource.
```yaml
paths:
/api/v1/articles:
get:
summary: Récupère la liste des articles
description: Retourne une liste paginée de tous les articles
tags:
- Articles
parameters:
- in: query
name: page
schema:
type: integer
default: 1
description: Numéro de page
- in: query
name: limit
schema:
type: integer
default: 10
description: Nombre d'éléments par page
responses:
'200':
description: Articles récupérés avec succès
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Article'
total:
type: integer
'400':
description: Erreur de validation
'500':
description: Erreur serveur
post:
summary: Crée un nouvel article
description: Crée un nouvel article avec les données fournies
tags:
- Articles
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- title
- content
properties:
title:
type: string
example: "Titre de l'article"
content:
type: string
example: "Contenu de l'article"
tags:
type: array
items:
type: string
responses:
'201':
description: Article créé avec succès
content:
application/json:
schema:
$ref: '#/components/schemas/Article'
'400':
description: Données invalides
'401':
description: Non authentifié
/api/v1/articles/{id}:
get:
summary: Récupère un article par ID
tags:
- Articles
parameters:
- in: path
name: id
required: true
schema:
type: integer
description: ID de l'article
responses:
'200':
description: Article récupéré avec succès
content:
application/json:
schema:
$ref: '#/components/schemas/Article'
'404':
description: Article non trouvé
put:
summary: Met à jour un article
tags:
- Articles
security:
- bearerAuth: []
parameters:
- in: path
name: id
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
title:
type: string
content:
type: string
tags:
type: array
items:
type: string
responses:
'200':
description: Article mis à jour avec succès
content:
application/json:
schema:
$ref: '#/components/schemas/Article'
'404':
description: Article non trouvé
delete:
summary: Supprime un article
tags:
- Articles
security:
- bearerAuth: []
parameters:
- in: path
name: id
required: true
schema:
type: integer
responses:
'200':
description: Article supprimé avec succès
'404':
description: Article non trouvé
components:
schemas:
Article:
type: object
properties:
id:
type: integer
title:
type: string
content:
type: string
author:
type: string
tags:
type: array
items:
type: string
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
```
## Instructions
1. **Créer un nouveau fichier YAML** dans `src/swagger/` :
```bash
touch src/swagger/articles.yaml
cp src/swagger/template.yaml src/swagger/articles.yaml
```
2. **Modifier le fichier** pour adapter les routes à votre ressource
3. **Redémarrer le serveur** :
```bash
npm run local
```
4. **Consulter les docs** : http://localhost:3200/api-docs
## Conventions
- **Un fichier YAML par ressource** (users.yaml, articles.yaml, products.yaml, etc.)
- **Réutiliser les schémas** : utilisez `$ref: '#/components/schemas/User'` au lieu de redéfinir
- **Utiliser les tags** pour regrouper les routes (Articles, Users, Products, etc.)
- **Utiliser les chemins corrects** : `/api/v1/...`, `/api/v2/...`, etc.
- **Documenter les réponses d'erreur** : 400, 401, 404, 500
+230
View File
@@ -0,0 +1,230 @@
paths:
/api/v1/user:
get:
summary: Récupère le profil de l'utilisateur connecté
description: Retourne les informations du profil de l'utilisateur authentifié
tags:
- Users
security:
- bearerAuth: []
responses:
'200':
description: Profil utilisateur récupéré avec succès
content:
application/json:
schema:
type: object
properties:
user:
$ref: '#/components/schemas/User'
'401':
description: Authentification requise
'500':
description: Erreur serveur
put:
summary: Met à jour le profil de l'utilisateur connecté
description: Permet à l'utilisateur authentifié de mettre à jour les informations de son profil
tags:
- Users
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
user:
type: object
properties:
username:
type: string
email:
type: string
firstname:
type: string
lastname:
type: string
phone:
type: string
licence:
type: string
poids:
type: number
image:
type: string
bg_image:
type: string
password:
type: string
role:
type: string
responses:
'200':
description: Profil utilisateur mis à jour avec succès
content:
application/json:
schema:
type: object
properties:
user:
$ref: '#/components/schemas/User'
'401':
description: Authentification requise
'500':
description: Erreur serveur
/api/v1/users/login:
post:
summary: Authentifie un utilisateur
description: Authentifie un utilisateur avec email et mot de passe, retourne un JWT
tags:
- Users
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- user
properties:
user:
type: object
required:
- email
- password
properties:
email:
type: string
format: email
password:
type: string
responses:
'200':
description: Authentification réussie, JWT retourné
content:
application/json:
schema:
type: object
properties:
user:
$ref: '#/components/schemas/User'
'422':
description: Email ou mot de passe manquant/incorrect
'500':
description: Erreur serveur
/api/v1/authenticate:
get:
summary: Authentification par clé API
description: Authentifie un utilisateur/application avec une clé API dans les headers
tags:
- Users
security:
- apiKeyAuth: []
responses:
'200':
description: Authentification réussie
content:
application/json:
schema:
type: object
properties:
user:
$ref: '#/components/schemas/User'
application:
type: object
'401':
description: Clé API invalide ou non autorisée
'500':
description: Erreur serveur
/api/v1/users:
post:
summary: Crée un nouvel utilisateur
description: Crée un nouvel utilisateur avec les données fournies
tags:
- Users
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- user
properties:
user:
type: object
required:
- email
- firstname
- lastname
- password
properties:
username:
type: string
email:
type: string
format: email
firstname:
type: string
lastname:
type: string
phone:
type: string
licence:
type: string
poids:
type: number
password:
type: string
responses:
'200':
description: Utilisateur créé avec succès
content:
application/json:
schema:
type: object
properties:
user:
$ref: '#/components/schemas/User'
'422':
description: Données invalides
'500':
description: Erreur serveur
components:
schemas:
User:
type: object
properties:
id:
type: string
username:
type: string
email:
type: string
format: email
firstname:
type: string
lastname:
type: string
phone:
type: string
licence:
type: string
poids:
type: number
image:
type: string
bg_image:
type: string
role:
type: string
token:
type: string
description: JWT Bearer token pour les requêtes authentifiées