chore: better Prettier config for easier reviews
This commit is contained in:
		| @@ -1,23 +1,23 @@ | ||||
| services: | ||||
|   workspace: | ||||
|     build: | ||||
|       context: './' | ||||
|       dockerfile: './Dockerfile' | ||||
|       context: "./" | ||||
|       dockerfile: "./Dockerfile" | ||||
|     volumes: | ||||
|       - '..:/workspace:cached' | ||||
|     command: 'sleep infinity' | ||||
|     network_mode: 'host' | ||||
|       - "..:/workspace:cached" | ||||
|     command: "sleep infinity" | ||||
|     network_mode: "host" | ||||
|  | ||||
|   thream-database: | ||||
|     image: 'postgres:15.4' | ||||
|     image: "postgres:15.4" | ||||
|     environment: | ||||
|       POSTGRES_USER: 'thream_user' | ||||
|       POSTGRES_PASSWORD: 'password' | ||||
|       POSTGRES_DB: 'thream' | ||||
|       POSTGRES_USER: "thream_user" | ||||
|       POSTGRES_PASSWORD: "password" | ||||
|       POSTGRES_DB: "thream" | ||||
|     volumes: | ||||
|       - 'thream-postgres-data:/var/lib/postgresql/data' | ||||
|     restart: 'unless-stopped' | ||||
|     network_mode: 'host' | ||||
|       - "thream-postgres-data:/var/lib/postgresql/data" | ||||
|     restart: "unless-stopped" | ||||
|     network_mode: "host" | ||||
|  | ||||
| volumes: | ||||
|   thream-postgres-data: | ||||
|   | ||||
							
								
								
									
										8
									
								
								.github/ISSUE_TEMPLATE/BUG.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/ISSUE_TEMPLATE/BUG.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +1,8 @@ | ||||
| --- | ||||
| name: '🐛 Bug Report' | ||||
| about: 'Report an unexpected problem or unintended behavior.' | ||||
| title: '[Bug]' | ||||
| labels: 'bug' | ||||
| name: "🐛 Bug Report" | ||||
| about: "Report an unexpected problem or unintended behavior." | ||||
| title: "[Bug]" | ||||
| labels: "bug" | ||||
| --- | ||||
|  | ||||
| <!-- | ||||
|   | ||||
							
								
								
									
										8
									
								
								.github/ISSUE_TEMPLATE/DOCUMENTATION.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/ISSUE_TEMPLATE/DOCUMENTATION.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +1,8 @@ | ||||
| --- | ||||
| name: '📜 Documentation' | ||||
| about: 'Correct spelling errors, improvements or additions to documentation files (README, CONTRIBUTING...).' | ||||
| title: '[Documentation]' | ||||
| labels: 'documentation' | ||||
| name: "📜 Documentation" | ||||
| about: "Correct spelling errors, improvements or additions to documentation files (README, CONTRIBUTING...)." | ||||
| title: "[Documentation]" | ||||
| labels: "documentation" | ||||
| --- | ||||
|  | ||||
| <!-- Please make sure your issue has not already been fixed. --> | ||||
|   | ||||
							
								
								
									
										8
									
								
								.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +1,8 @@ | ||||
| --- | ||||
| name: '✨ Feature Request' | ||||
| about: 'Suggest a new feature idea.' | ||||
| title: '[Feature]' | ||||
| labels: 'feature request' | ||||
| name: "✨ Feature Request" | ||||
| about: "Suggest a new feature idea." | ||||
| title: "[Feature]" | ||||
| labels: "feature request" | ||||
| --- | ||||
|  | ||||
| <!-- Please make sure your issue has not already been fixed. --> | ||||
|   | ||||
							
								
								
									
										8
									
								
								.github/ISSUE_TEMPLATE/IMPROVEMENT.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/ISSUE_TEMPLATE/IMPROVEMENT.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +1,8 @@ | ||||
| --- | ||||
| name: '🔧 Improvement' | ||||
| about: 'Improve structure/format/performance/refactor/tests of the code.' | ||||
| title: '[Improvement]' | ||||
| labels: 'improvement' | ||||
| name: "🔧 Improvement" | ||||
| about: "Improve structure/format/performance/refactor/tests of the code." | ||||
| title: "[Improvement]" | ||||
| labels: "improvement" | ||||
| --- | ||||
|  | ||||
| <!-- Please make sure your issue has not already been fixed. --> | ||||
|   | ||||
							
								
								
									
										8
									
								
								.github/ISSUE_TEMPLATE/QUESTION.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/ISSUE_TEMPLATE/QUESTION.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +1,8 @@ | ||||
| --- | ||||
| name: '🙋 Question' | ||||
| about: 'Further information is requested.' | ||||
| title: '[Question]' | ||||
| labels: 'question' | ||||
| name: "🙋 Question" | ||||
| about: "Further information is requested." | ||||
| title: "[Question]" | ||||
| labels: "question" | ||||
| --- | ||||
|  | ||||
| ### Question | ||||
|   | ||||
							
								
								
									
										16
									
								
								.github/workflows/analyze.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/analyze.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| name: 'Analyze' | ||||
| name: "Analyze" | ||||
|  | ||||
| on: | ||||
|   push: | ||||
| @@ -8,20 +8,20 @@ on: | ||||
|  | ||||
| jobs: | ||||
|   analyze: | ||||
|     runs-on: 'ubuntu-latest' | ||||
|     runs-on: "ubuntu-latest" | ||||
|  | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         language: ['javascript'] | ||||
|         language: ["javascript"] | ||||
|  | ||||
|     steps: | ||||
|       - uses: 'actions/checkout@v4.0.0' | ||||
|       - uses: "actions/checkout@v4.0.0" | ||||
|  | ||||
|       - name: 'Initialize CodeQL' | ||||
|         uses: 'github/codeql-action/init@v2' | ||||
|       - name: "Initialize CodeQL" | ||||
|         uses: "github/codeql-action/init@v2" | ||||
|         with: | ||||
|           languages: ${{ matrix.language }} | ||||
|  | ||||
|       - name: 'Perform CodeQL Analysis' | ||||
|         uses: 'github/codeql-action/analyze@v1' | ||||
|       - name: "Perform CodeQL Analysis" | ||||
|         uses: "github/codeql-action/analyze@v1" | ||||
|   | ||||
							
								
								
									
										24
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| name: 'Build' | ||||
| name: "Build" | ||||
|  | ||||
| on: | ||||
|   push: | ||||
| @@ -8,20 +8,20 @@ on: | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     runs-on: 'ubuntu-latest' | ||||
|     runs-on: "ubuntu-latest" | ||||
|     steps: | ||||
|       - uses: 'actions/checkout@v4.0.0' | ||||
|       - uses: "actions/checkout@v4.0.0" | ||||
|  | ||||
|       - name: 'Setup Node.js' | ||||
|         uses: 'actions/setup-node@v3.8.1' | ||||
|       - name: "Setup Node.js" | ||||
|         uses: "actions/setup-node@v3.8.1" | ||||
|         with: | ||||
|           node-version: '20.x' | ||||
|           cache: 'npm' | ||||
|           node-version: "20.x" | ||||
|           cache: "npm" | ||||
|  | ||||
|       - name: 'Install dependencies' | ||||
|         run: 'npm clean-install' | ||||
|       - name: "Install dependencies" | ||||
|         run: "npm clean-install" | ||||
|  | ||||
|       - name: 'Build' | ||||
|         run: 'npm run build' | ||||
|       - name: "Build" | ||||
|         run: "npm run build" | ||||
|  | ||||
|       - run: 'npm run build:typescript' | ||||
|       - run: "npm run build:typescript" | ||||
|   | ||||
							
								
								
									
										40
									
								
								.github/workflows/lint.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								.github/workflows/lint.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| name: 'Lint' | ||||
| name: "Lint" | ||||
|  | ||||
| on: | ||||
|   push: | ||||
| @@ -8,33 +8,33 @@ on: | ||||
|  | ||||
| jobs: | ||||
|   lint: | ||||
|     runs-on: 'ubuntu-latest' | ||||
|     runs-on: "ubuntu-latest" | ||||
|     steps: | ||||
|       - uses: 'actions/checkout@v4.0.0' | ||||
|       - uses: "actions/checkout@v4.0.0" | ||||
|  | ||||
|       - name: 'Setup Node.js' | ||||
|         uses: 'actions/setup-node@v3.8.1' | ||||
|       - name: "Setup Node.js" | ||||
|         uses: "actions/setup-node@v3.8.1" | ||||
|         with: | ||||
|           node-version: '20.x' | ||||
|           cache: 'npm' | ||||
|           node-version: "20.x" | ||||
|           cache: "npm" | ||||
|  | ||||
|       - name: 'Install dependencies' | ||||
|         run: 'npm clean-install' | ||||
|       - name: "Install dependencies" | ||||
|         run: "npm clean-install" | ||||
|  | ||||
|       - name: 'lint:commit' | ||||
|       - name: "lint:commit" | ||||
|         run: 'npm run lint:commit -- --to "${{ github.sha }}"' | ||||
|  | ||||
|       - name: 'lint:editorconfig' | ||||
|         run: 'npm run lint:editorconfig' | ||||
|       - name: "lint:editorconfig" | ||||
|         run: "npm run lint:editorconfig" | ||||
|  | ||||
|       - name: 'lint:markdown' | ||||
|         run: 'npm run lint:markdown' | ||||
|       - name: "lint:markdown" | ||||
|         run: "npm run lint:markdown" | ||||
|  | ||||
|       - name: 'lint:eslint' | ||||
|         run: 'npm run lint:eslint' | ||||
|       - name: "lint:eslint" | ||||
|         run: "npm run lint:eslint" | ||||
|  | ||||
|       - name: 'lint:prettier' | ||||
|         run: 'npm run lint:prettier' | ||||
|       - name: "lint:prettier" | ||||
|         run: "npm run lint:prettier" | ||||
|  | ||||
|       - name: 'prisma:validate' | ||||
|         run: 'cp .env.example .env && npm run prisma:validate' | ||||
|       - name: "prisma:validate" | ||||
|         run: "cp .env.example .env && npm run prisma:validate" | ||||
|   | ||||
							
								
								
									
										32
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| name: 'Release' | ||||
| name: "Release" | ||||
|  | ||||
| on: | ||||
|   push: | ||||
| @@ -6,36 +6,36 @@ on: | ||||
|  | ||||
| jobs: | ||||
|   release: | ||||
|     runs-on: 'ubuntu-latest' | ||||
|     runs-on: "ubuntu-latest" | ||||
|     steps: | ||||
|       - uses: 'actions/checkout@v4.0.0' | ||||
|       - uses: "actions/checkout@v4.0.0" | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|           persist-credentials: false | ||||
|  | ||||
|       - name: 'Import GPG key' | ||||
|         uses: 'crazy-max/ghaction-import-gpg@v6.0.0' | ||||
|       - name: "Import GPG key" | ||||
|         uses: "crazy-max/ghaction-import-gpg@v6.0.0" | ||||
|         with: | ||||
|           gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} | ||||
|           git_user_signingkey: true | ||||
|           git_commit_gpgsign: true | ||||
|  | ||||
|       - name: 'Setup Node.js' | ||||
|         uses: 'actions/setup-node@v3.8.1' | ||||
|       - name: "Setup Node.js" | ||||
|         uses: "actions/setup-node@v3.8.1" | ||||
|         with: | ||||
|           node-version: '20.x' | ||||
|           cache: 'npm' | ||||
|           node-version: "20.x" | ||||
|           cache: "npm" | ||||
|  | ||||
|       - name: 'Install dependencies' | ||||
|         run: 'npm clean-install' | ||||
|       - name: "Install dependencies" | ||||
|         run: "npm clean-install" | ||||
|  | ||||
|       - name: 'Build' | ||||
|         run: 'npm run build' | ||||
|       - name: "Build" | ||||
|         run: "npm run build" | ||||
|  | ||||
|       - run: 'npm run build:typescript' | ||||
|       - run: "npm run build:typescript" | ||||
|  | ||||
|       - name: 'Release' | ||||
|         run: 'npm run release' | ||||
|       - name: "Release" | ||||
|         run: "npm run release" | ||||
|         env: | ||||
|           GH_TOKEN: ${{ secrets.GH_TOKEN }} | ||||
|           GIT_COMMITTER_NAME: ${{ secrets.GIT_NAME }} | ||||
|   | ||||
							
								
								
									
										28
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| name: 'Test' | ||||
| name: "Test" | ||||
|  | ||||
| on: | ||||
|   push: | ||||
| @@ -8,23 +8,23 @@ on: | ||||
|  | ||||
| jobs: | ||||
|   test: | ||||
|     runs-on: 'ubuntu-latest' | ||||
|     runs-on: "ubuntu-latest" | ||||
|     steps: | ||||
|       - uses: 'actions/checkout@v4.0.0' | ||||
|       - uses: "actions/checkout@v4.0.0" | ||||
|  | ||||
|       - name: 'Setup Node.js' | ||||
|         uses: 'actions/setup-node@v3.8.1' | ||||
|       - name: "Setup Node.js" | ||||
|         uses: "actions/setup-node@v3.8.1" | ||||
|         with: | ||||
|           node-version: '20.x' | ||||
|           cache: 'npm' | ||||
|           node-version: "20.x" | ||||
|           cache: "npm" | ||||
|  | ||||
|       - name: 'Install dependencies' | ||||
|         run: 'npm clean-install' | ||||
|       - name: "Install dependencies" | ||||
|         run: "npm clean-install" | ||||
|  | ||||
|       - name: 'Build' | ||||
|         run: 'npm run build' | ||||
|       - name: "Build" | ||||
|         run: "npm run build" | ||||
|  | ||||
|       - run: 'cp .env.example .env' | ||||
|       - run: "cp .env.example .env" | ||||
|  | ||||
|       - name: 'Test' | ||||
|         run: 'npm run test' | ||||
|       - name: "Test" | ||||
|         run: "npm run test" | ||||
|   | ||||
| @@ -1,6 +1,3 @@ | ||||
| { | ||||
|   "singleQuote": true, | ||||
|   "jsxSingleQuote": true, | ||||
|   "semi": false, | ||||
|   "trailingComma": "none" | ||||
|   "semi": false | ||||
| } | ||||
|   | ||||
| @@ -1,15 +1,15 @@ | ||||
| FROM node:20.6.1 AS dependencies | ||||
| FROM node:20.9.0 AS dependencies | ||||
| WORKDIR /usr/src/app | ||||
| COPY ./package*.json ./ | ||||
| RUN npm clean-install | ||||
|  | ||||
| FROM node:20.6.1 AS builder | ||||
| FROM node:20.9.0 AS builder | ||||
| WORKDIR /usr/src/app | ||||
| COPY --from=dependencies /usr/src/app/node_modules ./node_modules | ||||
| COPY ./ ./ | ||||
| RUN npm run prisma:generate && npm run build | ||||
|  | ||||
| FROM node:20.6.1 AS runner | ||||
| FROM node:20.9.0 AS runner | ||||
| WORKDIR /usr/src/app | ||||
| ENV NODE_ENV=production | ||||
| ENV NODE_OPTIONS=--enable-source-maps | ||||
|   | ||||
							
								
								
									
										26
									
								
								compose.yaml
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								compose.yaml
									
									
									
									
									
								
							| @@ -1,27 +1,27 @@ | ||||
| services: | ||||
|   thream-api: | ||||
|     container_name: 'thream-api' | ||||
|     image: 'thream-api' | ||||
|     restart: 'unless-stopped' | ||||
|     network_mode: 'host' | ||||
|     container_name: "thream-api" | ||||
|     image: "thream-api" | ||||
|     restart: "unless-stopped" | ||||
|     network_mode: "host" | ||||
|     build: | ||||
|       context: './' | ||||
|     env_file: '.env' | ||||
|       context: "./" | ||||
|     env_file: ".env" | ||||
|     depends_on: | ||||
|       - 'thream-database' | ||||
|       - "thream-database" | ||||
|  | ||||
|   thream-database: | ||||
|     container_name: 'thream-database' | ||||
|     image: 'postgres:15.4' | ||||
|     restart: 'unless-stopped' | ||||
|     network_mode: 'host' | ||||
|     env_file: '.env' | ||||
|     container_name: "thream-database" | ||||
|     image: "postgres:15.4" | ||||
|     restart: "unless-stopped" | ||||
|     network_mode: "host" | ||||
|     env_file: ".env" | ||||
|     environment: | ||||
|       POSTGRES_USER: ${DATABASE_USER} | ||||
|       POSTGRES_PASSWORD: ${DATABASE_PASSWORD} | ||||
|       POSTGRES_DB: ${DATABASE_NAME} | ||||
|     volumes: | ||||
|       - 'thream-postgres-data:/var/lib/postgresql/data' | ||||
|       - "thream-postgres-data:/var/lib/postgresql/data" | ||||
|  | ||||
| volumes: | ||||
|   thream-postgres-data: | ||||
|   | ||||
| @@ -1,45 +1,45 @@ | ||||
| /** @type {import('node-plop').PlopGeneratorConfig} */ | ||||
| export const serviceGenerator = { | ||||
|   description: 'REST API endpoint', | ||||
|   description: "REST API endpoint", | ||||
|   prompts: [ | ||||
|     { | ||||
|       type: 'input', | ||||
|       name: 'url', | ||||
|       message: 'url' | ||||
|       type: "input", | ||||
|       name: "url", | ||||
|       message: "url", | ||||
|     }, | ||||
|     { | ||||
|       type: 'list', | ||||
|       name: 'httpMethod', | ||||
|       message: 'httpMethod', | ||||
|       choices: ['GET', 'POST', 'PUT', 'DELETE'] | ||||
|       type: "list", | ||||
|       name: "httpMethod", | ||||
|       message: "httpMethod", | ||||
|       choices: ["GET", "POST", "PUT", "DELETE"], | ||||
|     }, | ||||
|     { | ||||
|       type: 'input', | ||||
|       name: 'description', | ||||
|       message: 'description' | ||||
|       type: "input", | ||||
|       name: "description", | ||||
|       message: "description", | ||||
|     }, | ||||
|     { | ||||
|       type: 'list', | ||||
|       name: 'tag', | ||||
|       message: 'tag', | ||||
|       choices: ['users', 'oauth2', 'guilds', 'channels', 'messages', 'members'] | ||||
|       type: "list", | ||||
|       name: "tag", | ||||
|       message: "tag", | ||||
|       choices: ["users", "oauth2", "guilds", "channels", "messages", "members"], | ||||
|     }, | ||||
|     { | ||||
|       type: 'confirm', | ||||
|       name: 'shouldBeAuthenticated', | ||||
|       message: 'shouldBeAuthenticated' | ||||
|     } | ||||
|       type: "confirm", | ||||
|       name: "shouldBeAuthenticated", | ||||
|       message: "shouldBeAuthenticated", | ||||
|     }, | ||||
|   ], | ||||
|   actions: [ | ||||
|     { | ||||
|       type: 'add', | ||||
|       path: 'src/services/{{url}}/{{lowerCase httpMethod}}.ts', | ||||
|       templateFile: 'generators/service/service.ts.hbs' | ||||
|       type: "add", | ||||
|       path: "src/services/{{url}}/{{lowerCase httpMethod}}.ts", | ||||
|       templateFile: "generators/service/service.ts.hbs", | ||||
|     }, | ||||
|     { | ||||
|       type: 'add', | ||||
|       path: 'src/services/{{url}}/__test__/{{lowerCase httpMethod}}.test.ts', | ||||
|       templateFile: 'generators/service/service.test.ts.hbs' | ||||
|     } | ||||
|   ] | ||||
|       type: "add", | ||||
|       path: "src/services/{{url}}/__test__/{{lowerCase httpMethod}}.test.ts", | ||||
|       templateFile: "generators/service/service.test.ts.hbs", | ||||
|     }, | ||||
|   ], | ||||
| } | ||||
|   | ||||
							
								
								
									
										2051
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2051
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										68
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								package.json
									
									
									
									
									
								
							| @@ -40,71 +40,71 @@ | ||||
|     "postinstall": "husky install" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@fastify/cors": "8.3.0", | ||||
|     "@fastify/cors": "8.4.0", | ||||
|     "@fastify/helmet": "11.1.1", | ||||
|     "@fastify/multipart": "7.7.3", | ||||
|     "@fastify/multipart": "8.0.0", | ||||
|     "@fastify/rate-limit": "8.0.3", | ||||
|     "@fastify/sensible": "5.3.0", | ||||
|     "@fastify/swagger": "8.10.0", | ||||
|     "@fastify/swagger-ui": "1.9.3", | ||||
|     "@prisma/client": "5.3.1", | ||||
|     "@sinclair/typebox": "0.31.15", | ||||
|     "@fastify/sensible": "5.5.0", | ||||
|     "@fastify/swagger": "8.12.0", | ||||
|     "@fastify/swagger-ui": "1.10.1", | ||||
|     "@prisma/client": "5.4.2", | ||||
|     "@sinclair/typebox": "0.31.18", | ||||
|     "@thream/socketio-jwt": "3.1.3", | ||||
|     "axios": "1.5.0", | ||||
|     "axios": "1.5.1", | ||||
|     "bcryptjs": "2.4.3", | ||||
|     "dotenv": "16.3.1", | ||||
|     "ejs": "3.1.9", | ||||
|     "fastify": "4.23.2", | ||||
|     "fastify": "4.24.3", | ||||
|     "fastify-plugin": "4.5.1", | ||||
|     "form-data": "4.0.0", | ||||
|     "http-errors": "2.0.0", | ||||
|     "jsonwebtoken": "9.0.2", | ||||
|     "ms": "2.1.3", | ||||
|     "nodemailer": "6.9.5", | ||||
|     "nodemailer": "6.9.7", | ||||
|     "read-pkg": "8.1.0", | ||||
|     "socket.io": "4.7.2" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@commitlint/cli": "17.7.1", | ||||
|     "@commitlint/config-conventional": "17.7.0", | ||||
|     "@saithodev/semantic-release-backmerge": "3.2.0", | ||||
|     "@commitlint/cli": "18.0.0", | ||||
|     "@commitlint/config-conventional": "18.0.0", | ||||
|     "@saithodev/semantic-release-backmerge": "3.2.1", | ||||
|     "@semantic-release/git": "10.0.1", | ||||
|     "@swc/cli": "0.1.62", | ||||
|     "@swc/core": "1.3.85", | ||||
|     "@swc/core": "1.3.94", | ||||
|     "@tsconfig/strictest": "2.0.2", | ||||
|     "@types/bcryptjs": "2.4.4", | ||||
|     "@types/busboy": "1.5.1", | ||||
|     "@types/ejs": "3.1.2", | ||||
|     "@types/http-errors": "2.0.2", | ||||
|     "@types/jsonwebtoken": "9.0.3", | ||||
|     "@types/ms": "0.7.31", | ||||
|     "@types/node": "20.6.2", | ||||
|     "@types/nodemailer": "6.4.10", | ||||
|     "@types/sinon": "10.0.16", | ||||
|     "@typescript-eslint/eslint-plugin": "6.7.2", | ||||
|     "@typescript-eslint/parser": "6.7.2", | ||||
|     "@types/bcryptjs": "2.4.5", | ||||
|     "@types/busboy": "1.5.2", | ||||
|     "@types/ejs": "3.1.4", | ||||
|     "@types/http-errors": "2.0.3", | ||||
|     "@types/jsonwebtoken": "9.0.4", | ||||
|     "@types/ms": "0.7.33", | ||||
|     "@types/node": "20.8.7", | ||||
|     "@types/nodemailer": "6.4.13", | ||||
|     "@types/sinon": "10.0.20", | ||||
|     "@typescript-eslint/eslint-plugin": "6.9.0", | ||||
|     "@typescript-eslint/parser": "6.9.0", | ||||
|     "chokidar": "3.5.3", | ||||
|     "concurrently": "8.2.1", | ||||
|     "concurrently": "8.2.2", | ||||
|     "cross-env": "7.0.3", | ||||
|     "editorconfig-checker": "5.1.1", | ||||
|     "eslint": "8.49.0", | ||||
|     "eslint-config-conventions": "11.0.1", | ||||
|     "eslint": "8.52.0", | ||||
|     "eslint-config-conventions": "12.0.0", | ||||
|     "eslint-config-prettier": "9.0.0", | ||||
|     "eslint-plugin-import": "2.28.1", | ||||
|     "eslint-plugin-prettier": "5.0.0", | ||||
|     "eslint-plugin-import": "2.29.0", | ||||
|     "eslint-plugin-prettier": "5.0.1", | ||||
|     "eslint-plugin-promise": "6.1.1", | ||||
|     "eslint-plugin-unicorn": "48.0.1", | ||||
|     "husky": "8.0.3", | ||||
|     "lint-staged": "14.0.1", | ||||
|     "lint-staged": "15.0.2", | ||||
|     "maildev": "2.1.0", | ||||
|     "markdownlint-cli2": "0.10.0", | ||||
|     "markdownlint-rule-relative-links": "2.1.0", | ||||
|     "plop": "4.0.0", | ||||
|     "prettier": "3.0.3", | ||||
|     "prisma": "5.3.1", | ||||
|     "rimraf": "5.0.1", | ||||
|     "prisma": "5.4.2", | ||||
|     "rimraf": "5.0.5", | ||||
|     "semantic-release": "21.1.1", | ||||
|     "sinon": "16.0.0", | ||||
|     "sinon": "17.0.0", | ||||
|     "typescript": "5.2.2" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { serviceGenerator } from './generators/service/index.js' | ||||
| import { serviceGenerator } from "./generators/service/index.js" | ||||
|  | ||||
| export default ( | ||||
|   /** @type {import('plop').NodePlopAPI} */ | ||||
|   plop | ||||
|   plop, | ||||
| ) => { | ||||
|   plop.setGenerator('service', serviceGenerator) | ||||
|   plop.setGenerator("service", serviceGenerator) | ||||
| } | ||||
|   | ||||
| @@ -1,35 +1,35 @@ | ||||
| import type { User } from '@prisma/client' | ||||
| import sinon from 'sinon' | ||||
| import type { User } from "@prisma/client" | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { refreshTokenExample } from '#src/models/RefreshToken.js' | ||||
| import type { UserJWT } from '#src/models/User.js' | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { userSettingsExample } from '#src/models/UserSettings.js' | ||||
| import { refreshTokenExample } from "#src/models/RefreshToken.js" | ||||
| import type { UserJWT } from "#src/models/User.js" | ||||
| import { userExample } from "#src/models/User.js" | ||||
| import { userSettingsExample } from "#src/models/UserSettings.js" | ||||
| import { | ||||
|   generateAccessToken, | ||||
|   generateRefreshToken | ||||
| } from '#src/tools/utils/jwtToken.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
|   generateRefreshToken, | ||||
| } from "#src/tools/utils/jwtToken.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
|  | ||||
| const userStubValue = { | ||||
|   findUnique: async () => { | ||||
|     return userExample | ||||
|   } | ||||
|   }, | ||||
| } | ||||
| const userSettingStubValue = { | ||||
|   findFirst: async () => { | ||||
|     return userSettingsExample | ||||
|   } | ||||
|   }, | ||||
| } | ||||
| const oAuthStubValue = { | ||||
|   findMany: async () => { | ||||
|     return [] | ||||
|   } | ||||
|   }, | ||||
| } | ||||
| const refreshTokenStubValue = { | ||||
|   create: async () => { | ||||
|     return refreshTokenExample | ||||
|   } | ||||
|   }, | ||||
| } | ||||
|  | ||||
| export const authenticateUserTest = async (): Promise<{ | ||||
| @@ -41,13 +41,13 @@ export const authenticateUserTest = async (): Promise<{ | ||||
|   oAuthStubValue: typeof oAuthStubValue | ||||
|   refreshTokenStubValue: typeof refreshTokenStubValue | ||||
| }> => { | ||||
|   sinon.stub(prisma, 'user').value(userStubValue) | ||||
|   sinon.stub(prisma, 'userSetting').value(userSettingStubValue) | ||||
|   sinon.stub(prisma, 'oAuth').value(oAuthStubValue) | ||||
|   sinon.stub(prisma, 'refreshToken').value(refreshTokenStubValue) | ||||
|   sinon.stub(prisma, "user").value(userStubValue) | ||||
|   sinon.stub(prisma, "userSetting").value(userSettingStubValue) | ||||
|   sinon.stub(prisma, "oAuth").value(oAuthStubValue) | ||||
|   sinon.stub(prisma, "refreshToken").value(refreshTokenStubValue) | ||||
|   const userJWT: UserJWT = { | ||||
|     currentStrategy: 'Local', | ||||
|     id: 1 | ||||
|     currentStrategy: "Local", | ||||
|     id: 1, | ||||
|   } | ||||
|   const accessToken = generateAccessToken(userJWT) | ||||
|   const refreshToken = await generateRefreshToken(userJWT) | ||||
| @@ -58,6 +58,6 @@ export const authenticateUserTest = async (): Promise<{ | ||||
|     userStubValue, | ||||
|     userSettingStubValue, | ||||
|     oAuthStubValue, | ||||
|     refreshTokenStubValue | ||||
|     refreshTokenStubValue, | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,75 +1,75 @@ | ||||
| import dotenv from 'dotenv' | ||||
| import fastify from 'fastify' | ||||
| import fastifyCors from '@fastify/cors' | ||||
| import fastifySwagger from '@fastify/swagger' | ||||
| import fastifySwaggerUI from '@fastify/swagger-ui' | ||||
| import fastifyHelmet from '@fastify/helmet' | ||||
| import fastifyRateLimit from '@fastify/rate-limit' | ||||
| import fastifySensible from '@fastify/sensible' | ||||
| import { readPackage } from 'read-pkg' | ||||
| import dotenv from "dotenv" | ||||
| import fastify from "fastify" | ||||
| import fastifyCors from "@fastify/cors" | ||||
| import fastifySwagger from "@fastify/swagger" | ||||
| import fastifySwaggerUI from "@fastify/swagger-ui" | ||||
| import fastifyHelmet from "@fastify/helmet" | ||||
| import fastifyRateLimit from "@fastify/rate-limit" | ||||
| import fastifySensible from "@fastify/sensible" | ||||
| import { readPackage } from "read-pkg" | ||||
|  | ||||
| import { services } from '#src/services/index.js' | ||||
| import fastifySocketIo from '#src/tools/plugins/socket-io.js' | ||||
| import { services } from "#src/services/index.js" | ||||
| import fastifySocketIo from "#src/tools/plugins/socket-io.js" | ||||
|  | ||||
| dotenv.config() | ||||
| const packageJSON = await readPackage() | ||||
| export const application = fastify({ | ||||
|   logger: process.env['NODE_ENV'] === 'development', | ||||
|   logger: process.env["NODE_ENV"] === "development", | ||||
|   ajv: { | ||||
|     customOptions: { | ||||
|       strict: 'log', | ||||
|       keywords: ['kind', 'modifier'], | ||||
|       strict: "log", | ||||
|       keywords: ["kind", "modifier"], | ||||
|       formats: { | ||||
|         full: true | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|         full: true, | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
| }) | ||||
|  | ||||
| await application.register(fastifyCors) | ||||
| await application.register(fastifySensible) | ||||
| await application.register(fastifySocketIo, { | ||||
|   cors: { | ||||
|     origin: '*', | ||||
|     methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', | ||||
|     origin: "*", | ||||
|     methods: "GET,HEAD,PUT,PATCH,POST,DELETE", | ||||
|     preflightContinue: false, | ||||
|     optionsSuccessStatus: 204 | ||||
|   } | ||||
|     optionsSuccessStatus: 204, | ||||
|   }, | ||||
| }) | ||||
| await application.register(fastifyHelmet) | ||||
| await application.register(fastifyRateLimit, { | ||||
|   max: 200, | ||||
|   timeWindow: '1 minute' | ||||
|   timeWindow: "1 minute", | ||||
| }) | ||||
| await application.register(fastifySwagger, { | ||||
|   openapi: { | ||||
|     info: { | ||||
|       title: packageJSON.name, | ||||
|       description: packageJSON.description, | ||||
|       version: packageJSON.version | ||||
|       version: packageJSON.version, | ||||
|     }, | ||||
|     tags: [ | ||||
|       { name: 'users' }, | ||||
|       { name: 'oauth2' }, | ||||
|       { name: 'guilds' }, | ||||
|       { name: 'channels' }, | ||||
|       { name: 'messages' }, | ||||
|       { name: 'members' } | ||||
|       { name: "users" }, | ||||
|       { name: "oauth2" }, | ||||
|       { name: "guilds" }, | ||||
|       { name: "channels" }, | ||||
|       { name: "messages" }, | ||||
|       { name: "members" }, | ||||
|     ], | ||||
|     components: { | ||||
|       securitySchemes: { | ||||
|         bearerAuth: { | ||||
|           type: 'http', | ||||
|           scheme: 'bearer', | ||||
|           bearerFormat: 'JWT' | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|           type: "http", | ||||
|           scheme: "bearer", | ||||
|           bearerFormat: "JWT", | ||||
|         }, | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
|   hideUntagged: true | ||||
|   hideUntagged: true, | ||||
| }) | ||||
| await application.register(fastifySwaggerUI, { | ||||
|   routePrefix: '/documentation', | ||||
|   staticCSP: true | ||||
|   routePrefix: "/documentation", | ||||
|   staticCSP: true, | ||||
| }) | ||||
| await application.register(services) | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { application } from '#src/application.js' | ||||
| import { HOST, PORT } from '#src/tools/configurations.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { HOST, PORT } from "#src/tools/configurations.js" | ||||
|  | ||||
| const address = await application.listen({ | ||||
|   port: PORT, | ||||
|   host: HOST | ||||
|   host: HOST, | ||||
| }) | ||||
| console.log(`Server listening at ${address}`) | ||||
|   | ||||
| @@ -1,23 +1,23 @@ | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { Channel } from '@prisma/client' | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { Channel } from "@prisma/client" | ||||
|  | ||||
| import { date, id } from '#src/models/utils.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { date, id } from "#src/models/utils.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
|  | ||||
| export const types = [Type.Literal('text')] | ||||
| export const types = [Type.Literal("text")] | ||||
|  | ||||
| export const channelSchema = { | ||||
|   id, | ||||
|   name: Type.String({ minLength: 1, maxLength: 20 }), | ||||
|   createdAt: date.createdAt, | ||||
|   updatedAt: date.updatedAt, | ||||
|   guildId: id | ||||
|   guildId: id, | ||||
| } | ||||
|  | ||||
| export const channelExample: Channel = { | ||||
|   id: 1, | ||||
|   name: 'general', | ||||
|   name: "general", | ||||
|   guildId: guildExample.id, | ||||
|   createdAt: new Date(), | ||||
|   updatedAt: new Date() | ||||
|   updatedAt: new Date(), | ||||
| } | ||||
|   | ||||
| @@ -1,28 +1,28 @@ | ||||
| import type { Guild } from '@prisma/client' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { Guild } from "@prisma/client" | ||||
| import { Type } from "@sinclair/typebox" | ||||
|  | ||||
| import { date, id } from '#src/models/utils.js' | ||||
| import { date, id } from "#src/models/utils.js" | ||||
|  | ||||
| export const guildSchema = { | ||||
|   id, | ||||
|   name: Type.String({ minLength: 1, maxLength: 30 }), | ||||
|   icon: Type.Union([ | ||||
|     Type.String({ format: 'uri-reference', minLength: 1 }), | ||||
|     Type.Null() | ||||
|     Type.String({ format: "uri-reference", minLength: 1 }), | ||||
|     Type.Null(), | ||||
|   ]), | ||||
|   description: Type.Union([ | ||||
|     Type.String({ minLength: 1, maxLength: 160 }), | ||||
|     Type.Null() | ||||
|     Type.Null(), | ||||
|   ]), | ||||
|   createdAt: date.createdAt, | ||||
|   updatedAt: date.updatedAt | ||||
|   updatedAt: date.updatedAt, | ||||
| } | ||||
|  | ||||
| export const guildExample: Guild = { | ||||
|   id: 1, | ||||
|   name: 'GuildExample', | ||||
|   description: 'guild example.', | ||||
|   name: "GuildExample", | ||||
|   description: "guild example.", | ||||
|   icon: null, | ||||
|   createdAt: new Date(), | ||||
|   updatedAt: new Date() | ||||
|   updatedAt: new Date(), | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { Member } from '@prisma/client' | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { Member } from "@prisma/client" | ||||
|  | ||||
| import { date, id } from '#src/models/utils.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { date, id } from "#src/models/utils.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
| import { userExample } from "#src/models/User.js" | ||||
|  | ||||
| export const memberSchema = { | ||||
|   id, | ||||
| @@ -11,7 +11,7 @@ export const memberSchema = { | ||||
|   createdAt: date.createdAt, | ||||
|   updatedAt: date.updatedAt, | ||||
|   userId: id, | ||||
|   guildId: id | ||||
|   guildId: id, | ||||
| } | ||||
|  | ||||
| export const memberExample: Member = { | ||||
| @@ -20,5 +20,5 @@ export const memberExample: Member = { | ||||
|   userId: userExample.id, | ||||
|   guildId: guildExample.id, | ||||
|   createdAt: new Date(), | ||||
|   updatedAt: new Date() | ||||
|   updatedAt: new Date(), | ||||
| } | ||||
|   | ||||
| @@ -1,34 +1,34 @@ | ||||
| import type { Message } from '@prisma/client' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { Message } from "@prisma/client" | ||||
| import { Type } from "@sinclair/typebox" | ||||
|  | ||||
| import { date, id } from '#src/models/utils.js' | ||||
| import { date, id } from "#src/models/utils.js" | ||||
|  | ||||
| export const types = [Type.Literal('text'), Type.Literal('file')] | ||||
| export const types = [Type.Literal("text"), Type.Literal("file")] | ||||
|  | ||||
| export const messageSchema = { | ||||
|   id, | ||||
|   value: Type.String({ | ||||
|     minLength: 1, | ||||
|     maxLength: 20_000 | ||||
|     maxLength: 20_000, | ||||
|   }), | ||||
|   type: Type.Union(types, { default: 'text' }), | ||||
|   type: Type.Union(types, { default: "text" }), | ||||
|   mimetype: Type.String({ | ||||
|     maxLength: 127, | ||||
|     default: 'text/plain' | ||||
|     default: "text/plain", | ||||
|   }), | ||||
|   createdAt: date.createdAt, | ||||
|   updatedAt: date.updatedAt, | ||||
|   memberId: id, | ||||
|   channelId: id | ||||
|   channelId: id, | ||||
| } | ||||
|  | ||||
| export const messageExample: Message = { | ||||
|   id: 1, | ||||
|   value: 'Hello, world!', | ||||
|   type: 'text', | ||||
|   mimetype: 'text/plain', | ||||
|   value: "Hello, world!", | ||||
|   type: "text", | ||||
|   mimetype: "text/plain", | ||||
|   createdAt: new Date(), | ||||
|   updatedAt: new Date(), | ||||
|   memberId: 1, | ||||
|   channelId: 1 | ||||
|   channelId: 1, | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import { Type } from "@sinclair/typebox" | ||||
|  | ||||
| import { date, id } from '#src/models/utils.js' | ||||
| import { date, id } from "#src/models/utils.js" | ||||
|  | ||||
| export const providers = ['Google', 'GitHub', 'Discord'] as const | ||||
| export const strategies = [...providers, 'Local'] as const | ||||
| export const providers = ["Google", "GitHub", "Discord"] as const | ||||
| export const strategies = [...providers, "Local"] as const | ||||
|  | ||||
| export const strategiesTypebox = strategies.map((strategy) => { | ||||
|   return Type.Literal(strategy) | ||||
| @@ -21,5 +21,5 @@ export const oauthSchema = { | ||||
|   provider: Type.Union([...providersTypebox]), | ||||
|   createdAt: date.createdAt, | ||||
|   updatedAt: date.updatedAt, | ||||
|   userId: id | ||||
|   userId: id, | ||||
| } | ||||
|   | ||||
| @@ -1,21 +1,21 @@ | ||||
| import type { RefreshToken } from '@prisma/client' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { RefreshToken } from "@prisma/client" | ||||
| import { Type } from "@sinclair/typebox" | ||||
|  | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { date, id } from '#src/models/utils.js' | ||||
| import { userExample } from "#src/models/User.js" | ||||
| import { date, id } from "#src/models/utils.js" | ||||
|  | ||||
| export const refreshTokensSchema = { | ||||
|   id, | ||||
|   token: Type.String({ format: 'uuid' }), | ||||
|   token: Type.String({ format: "uuid" }), | ||||
|   createdAt: date.createdAt, | ||||
|   updatedAt: date.updatedAt, | ||||
|   userId: id | ||||
|   userId: id, | ||||
| } | ||||
|  | ||||
| export const refreshTokenExample: RefreshToken = { | ||||
|   id: 1, | ||||
|   userId: userExample.id, | ||||
|   token: 'sometokenUUID', | ||||
|   token: "sometokenUUID", | ||||
|   createdAt: new Date(), | ||||
|   updatedAt: new Date() | ||||
|   updatedAt: new Date(), | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| import type { User } from '@prisma/client' | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { User } from "@prisma/client" | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
|  | ||||
| import type { AuthenticationStrategy } from '#src/models/OAuth.js' | ||||
| import { strategiesTypebox } from '#src/models/OAuth.js' | ||||
| import { userSettingsSchema } from '#src/models/UserSettings.js' | ||||
| import { date, id } from '#src/models/utils.js' | ||||
| import type { AuthenticationStrategy } from "#src/models/OAuth.js" | ||||
| import { strategiesTypebox } from "#src/models/OAuth.js" | ||||
| import { userSettingsSchema } from "#src/models/UserSettings.js" | ||||
| import { date, id } from "#src/models/utils.js" | ||||
|  | ||||
| export interface UserJWT { | ||||
|   id: number | ||||
| @@ -25,21 +25,21 @@ export interface UserRequest { | ||||
| export const userSchema = { | ||||
|   id, | ||||
|   name: Type.String({ minLength: 1, maxLength: 30 }), | ||||
|   email: Type.String({ minLength: 1, maxLength: 254, format: 'email' }), | ||||
|   email: Type.String({ minLength: 1, maxLength: 254, format: "email" }), | ||||
|   password: Type.String({ minLength: 1 }), | ||||
|   logo: Type.String({ minLength: 1, format: 'uri-reference' }), | ||||
|   logo: Type.String({ minLength: 1, format: "uri-reference" }), | ||||
|   status: Type.String({ minLength: 1, maxLength: 50 }), | ||||
|   biography: Type.String({ minLength: 1, maxLength: 160 }), | ||||
|   website: Type.String({ | ||||
|     minLength: 1, | ||||
|     maxLength: 255, | ||||
|     format: 'uri' | ||||
|     format: "uri", | ||||
|   }), | ||||
|   isConfirmed: Type.Boolean({ default: false }), | ||||
|   temporaryToken: Type.String(), | ||||
|   temporaryExpirationToken: Type.String({ format: 'date-time' }), | ||||
|   temporaryExpirationToken: Type.String({ format: "date-time" }), | ||||
|   createdAt: date.createdAt, | ||||
|   updatedAt: date.updatedAt | ||||
|   updatedAt: date.updatedAt, | ||||
| } | ||||
|  | ||||
| export const userPublicWithoutSettingsSchema = { | ||||
| @@ -52,20 +52,20 @@ export const userPublicWithoutSettingsSchema = { | ||||
|   website: Type.Union([userSchema.website, Type.Null()]), | ||||
|   isConfirmed: userSchema.isConfirmed, | ||||
|   createdAt: date.createdAt, | ||||
|   updatedAt: date.updatedAt | ||||
|   updatedAt: date.updatedAt, | ||||
| } | ||||
|  | ||||
| export const userPublicSchema = { | ||||
|   ...userPublicWithoutSettingsSchema, | ||||
|   settings: Type.Object(userSettingsSchema) | ||||
|   settings: Type.Object(userSettingsSchema), | ||||
| } | ||||
|  | ||||
| export const userCurrentSchema = Type.Object({ | ||||
|   user: Type.Object({ | ||||
|     ...userPublicSchema, | ||||
|     currentStrategy: Type.Union([...strategiesTypebox]), | ||||
|     strategies: Type.Array(Type.Union([...strategiesTypebox])) | ||||
|   }) | ||||
|     strategies: Type.Array(Type.Union([...strategiesTypebox])), | ||||
|   }), | ||||
| }) | ||||
|  | ||||
| export const bodyUserSchema = Type.Object({ | ||||
| @@ -73,23 +73,23 @@ export const bodyUserSchema = Type.Object({ | ||||
|   name: userSchema.name, | ||||
|   password: userSchema.password, | ||||
|   theme: userSettingsSchema.theme, | ||||
|   language: userSettingsSchema.language | ||||
|   language: userSettingsSchema.language, | ||||
| }) | ||||
|  | ||||
| export type BodyUserSchemaType = Static<typeof bodyUserSchema> | ||||
|  | ||||
| export const userExample: User = { | ||||
|   id: 1, | ||||
|   name: 'Divlo', | ||||
|   email: 'contact@divlo.fr', | ||||
|   password: 'somepassword', | ||||
|   name: "Divlo", | ||||
|   email: "contact@divlo.fr", | ||||
|   password: "somepassword", | ||||
|   logo: null, | ||||
|   status: null, | ||||
|   biography: null, | ||||
|   website: null, | ||||
|   isConfirmed: true, | ||||
|   temporaryToken: 'temporaryUUIDtoken', | ||||
|   temporaryToken: "temporaryUUIDtoken", | ||||
|   temporaryExpirationToken: new Date(), | ||||
|   createdAt: new Date(), | ||||
|   updatedAt: new Date() | ||||
|   updatedAt: new Date(), | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| import type { UserSetting } from '@prisma/client' | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { UserSetting } from "@prisma/client" | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
|  | ||||
| import { date, id } from '#src/models/utils.js' | ||||
| import { date, id } from "#src/models/utils.js" | ||||
|  | ||||
| export const languages = [Type.Literal('fr'), Type.Literal('en')] | ||||
| export const themes = [Type.Literal('light'), Type.Literal('dark')] | ||||
| export const languages = [Type.Literal("fr"), Type.Literal("en")] | ||||
| export const themes = [Type.Literal("light"), Type.Literal("dark")] | ||||
|  | ||||
| export const userSettingsSchema = { | ||||
|   id, | ||||
| @@ -15,7 +15,7 @@ export const userSettingsSchema = { | ||||
|   isPublicGuilds: Type.Boolean(), | ||||
|   createdAt: date.createdAt, | ||||
|   updatedAt: date.updatedAt, | ||||
|   userId: id | ||||
|   userId: id, | ||||
| } | ||||
|  | ||||
| export type Theme = Static<typeof userSettingsSchema.theme> | ||||
| @@ -23,11 +23,11 @@ export type Language = Static<typeof userSettingsSchema.language> | ||||
|  | ||||
| export const userSettingsExample: UserSetting = { | ||||
|   id: 1, | ||||
|   theme: 'dark', | ||||
|   language: 'en', | ||||
|   theme: "dark", | ||||
|   language: "en", | ||||
|   isPublicEmail: false, | ||||
|   isPublicGuilds: false, | ||||
|   userId: 1, | ||||
|   createdAt: new Date(), | ||||
|   updatedAt: new Date() | ||||
|   updatedAt: new Date(), | ||||
| } | ||||
|   | ||||
| @@ -1,51 +1,51 @@ | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import { Type } from "@sinclair/typebox" | ||||
|  | ||||
| export const date = { | ||||
|   createdAt: Type.String({ | ||||
|     format: 'date-time', | ||||
|     description: 'Created date time' | ||||
|     format: "date-time", | ||||
|     description: "Created date time", | ||||
|   }), | ||||
|   updatedAt: Type.String({ | ||||
|     format: 'date-time', | ||||
|     description: 'Last updated date time' | ||||
|   }) | ||||
|     format: "date-time", | ||||
|     description: "Last updated date time", | ||||
|   }), | ||||
| } | ||||
|  | ||||
| export const id = Type.Integer({ minimum: 1, description: 'Unique identifier' }) | ||||
| export const id = Type.Integer({ minimum: 1, description: "Unique identifier" }) | ||||
|  | ||||
| export const redirectURI = Type.String({ format: 'uri-reference' }) | ||||
| export const redirectURI = Type.String({ format: "uri-reference" }) | ||||
|  | ||||
| export const fastifyErrorsSchema = { | ||||
|   400: { | ||||
|     statusCode: Type.Literal(400), | ||||
|     error: Type.Literal('Bad Request'), | ||||
|     message: Type.String() | ||||
|     error: Type.Literal("Bad Request"), | ||||
|     message: Type.String(), | ||||
|   }, | ||||
|   401: { | ||||
|     statusCode: Type.Literal(401), | ||||
|     error: Type.Literal('Unauthorized'), | ||||
|     message: Type.Literal('Unauthorized') | ||||
|     error: Type.Literal("Unauthorized"), | ||||
|     message: Type.Literal("Unauthorized"), | ||||
|   }, | ||||
|   403: { | ||||
|     statusCode: Type.Literal(403), | ||||
|     error: Type.Literal('Forbidden'), | ||||
|     message: Type.Literal('Forbidden') | ||||
|     error: Type.Literal("Forbidden"), | ||||
|     message: Type.Literal("Forbidden"), | ||||
|   }, | ||||
|   404: { | ||||
|     statusCode: Type.Literal(404), | ||||
|     error: Type.Literal('Not Found'), | ||||
|     message: Type.String() | ||||
|     error: Type.Literal("Not Found"), | ||||
|     message: Type.String(), | ||||
|   }, | ||||
|   431: { | ||||
|     statusCode: Type.Literal(431), | ||||
|     error: Type.Literal('Request Header Fields Too Large'), | ||||
|     message: Type.String() | ||||
|     error: Type.Literal("Request Header Fields Too Large"), | ||||
|     message: Type.String(), | ||||
|   }, | ||||
|   500: { | ||||
|     statusCode: Type.Literal(500), | ||||
|     error: Type.Literal('Internal Server Error'), | ||||
|     message: Type.Literal('Something went wrong') | ||||
|   } | ||||
|     error: Type.Literal("Internal Server Error"), | ||||
|     message: Type.Literal("Something went wrong"), | ||||
|   }, | ||||
| } | ||||
|  | ||||
| export const fastifyErrors = { | ||||
| @@ -54,5 +54,5 @@ export const fastifyErrors = { | ||||
|   403: Type.Object(fastifyErrorsSchema[403]), | ||||
|   404: Type.Object(fastifyErrorsSchema[404]), | ||||
|   431: Type.Object(fastifyErrorsSchema[431]), | ||||
|   500: Type.Object(fastifyErrorsSchema[500]) | ||||
|   500: Type.Object(fastifyErrorsSchema[500]), | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| import crypto from 'node:crypto' | ||||
| import crypto from "node:crypto" | ||||
|  | ||||
| console.log(crypto.randomBytes(256).toString('base64')) | ||||
| console.log(crypto.randomBytes(256).toString("base64")) | ||||
|   | ||||
| @@ -1,30 +1,30 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
|  | ||||
| await test('DELETE /channels/[channelId]', async (t) => { | ||||
| await test("DELETE /channels/[channelId]", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const defaultChannelId = 5 | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       }, | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...channelExample, | ||||
|           id: defaultChannelId | ||||
|           id: defaultChannelId, | ||||
|         } | ||||
|       }, | ||||
|       count: async () => { | ||||
| @@ -32,19 +32,19 @@ await test('DELETE /channels/[channelId]', async (t) => { | ||||
|       }, | ||||
|       delete: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return memberExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/channels/${channelExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -54,91 +54,91 @@ await test('DELETE /channels/[channelId]', async (t) => { | ||||
|     assert.strictEqual(responseJson.defaultChannelId, defaultChannelId) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if there is only one channel', async () => { | ||||
|   await t.test("fails if there is only one channel", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       }, | ||||
|       count: async () => { | ||||
|         return 1 | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return memberExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/channels/${channelExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the channel is not found', async () => { | ||||
|   await t.test("fails if the channel is not found", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/channels/${channelExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the member is not found', async () => { | ||||
|   await t.test("fails if the member is not found", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/channels/${channelExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the member is not owner', async () => { | ||||
|   await t.test("fails if the member is not owner", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           isOwner: false | ||||
|           isOwner: false, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/channels/${channelExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|   }) | ||||
|   | ||||
| @@ -1,37 +1,37 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
|  | ||||
| await test('GET /channels/[channelId]', async (t) => { | ||||
| await test("GET /channels/[channelId]", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return memberExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       method: "GET", | ||||
|       url: `/channels/${channelExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -40,58 +40,58 @@ await test('GET /channels/[channelId]', async (t) => { | ||||
|     assert.strictEqual(responseJson.channel.guildId, channelExample.guildId) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with not found member', async () => { | ||||
|   await t.test("fails with not found member", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       method: "GET", | ||||
|       url: `/channels/${channelExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|     assert.strictEqual(responseJson.message, 'Channel not found') | ||||
|     assert.strictEqual(responseJson.message, "Channel not found") | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with not found channel', async () => { | ||||
|   await t.test("fails with not found channel", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return memberExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       method: "GET", | ||||
|       url: `/channels/${channelExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|     assert.strictEqual(responseJson.message, 'Channel not found') | ||||
|     assert.strictEqual(responseJson.message, "Channel not found") | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with unauthenticated user', async () => { | ||||
|   await t.test("fails with unauthenticated user", async () => { | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/channels/1' | ||||
|       method: "GET", | ||||
|       url: "/channels/1", | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 401) | ||||
|   }) | ||||
|   | ||||
| @@ -1,53 +1,53 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
|  | ||||
| const newName = 'new channel name' | ||||
| const newName = "new channel name" | ||||
|  | ||||
| await test('PUT /channels/[channelId]', async (t) => { | ||||
| await test("PUT /channels/[channelId]", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const defaultChannelId = 5 | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       }, | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...channelExample, | ||||
|           id: defaultChannelId | ||||
|           id: defaultChannelId, | ||||
|         } | ||||
|       }, | ||||
|       update: async () => { | ||||
|         return { | ||||
|           ...channelExample, | ||||
|           name: newName | ||||
|           name: newName, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return memberExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       method: "PUT", | ||||
|       url: `/channels/${channelExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { name: newName } | ||||
|       payload: { name: newName }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -57,74 +57,74 @@ await test('PUT /channels/[channelId]', async (t) => { | ||||
|     assert.strictEqual(responseJson.defaultChannelId, defaultChannelId) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the channel is not found', async () => { | ||||
|   await t.test("fails if the channel is not found", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return memberExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       method: "PUT", | ||||
|       url: `/channels/${channelExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { name: newName } | ||||
|       payload: { name: newName }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the member is not found', async () => { | ||||
|   await t.test("fails if the member is not found", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       method: "PUT", | ||||
|       url: `/channels/${channelExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { name: newName } | ||||
|       payload: { name: newName }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the member is not owner', async () => { | ||||
|   await t.test("fails if the member is not owner", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           isOwner: false | ||||
|           isOwner: false, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       method: "PUT", | ||||
|       url: `/channels/${channelExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { name: newName } | ||||
|       payload: { name: newName }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|   }) | ||||
|   | ||||
| @@ -1,38 +1,38 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   channelId: channelSchema.id | ||||
|   channelId: channelSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const deleteServiceSchema: FastifySchema = { | ||||
|   description: 'DELETE a channel with its id.', | ||||
|   tags: ['channels'] as string[], | ||||
|   description: "DELETE a channel with its id.", | ||||
|   tags: ["channels"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   response: { | ||||
|     200: Type.Object({ | ||||
|       ...channelSchema, | ||||
|       defaultChannelId: channelSchema.id | ||||
|       defaultChannelId: channelSchema.id, | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const deleteChannelService: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -41,8 +41,8 @@ export const deleteChannelService: FastifyPluginAsync = async (fastify) => { | ||||
|   fastify.route<{ | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'DELETE', | ||||
|     url: '/channels/:channelId', | ||||
|     method: "DELETE", | ||||
|     url: "/channels/:channelId", | ||||
|     schema: deleteServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -51,51 +51,51 @@ export const deleteChannelService: FastifyPluginAsync = async (fastify) => { | ||||
|       const { user, params } = request | ||||
|       const { channelId } = params | ||||
|       const channelCheck = await prisma.channel.findUnique({ | ||||
|         where: { id: channelId } | ||||
|         where: { id: channelId }, | ||||
|       }) | ||||
|       if (channelCheck == null) { | ||||
|         throw fastify.httpErrors.notFound('Channel not found') | ||||
|         throw fastify.httpErrors.notFound("Channel not found") | ||||
|       } | ||||
|       const member = await prisma.member.findFirst({ | ||||
|         where: { guildId: channelCheck.guildId, userId: user.current.id } | ||||
|         where: { guildId: channelCheck.guildId, userId: user.current.id }, | ||||
|       }) | ||||
|       if (member == null) { | ||||
|         throw fastify.httpErrors.notFound('Member not found') | ||||
|         throw fastify.httpErrors.notFound("Member not found") | ||||
|       } | ||||
|       if (!member.isOwner) { | ||||
|         throw fastify.httpErrors.badRequest('You should be a member owner') | ||||
|         throw fastify.httpErrors.badRequest("You should be a member owner") | ||||
|       } | ||||
|       const channelCount = await prisma.channel.count({ | ||||
|         where: { guildId: channelCheck.guildId } | ||||
|         where: { guildId: channelCheck.guildId }, | ||||
|       }) | ||||
|       if (channelCount <= 1) { | ||||
|         throw fastify.httpErrors.badRequest( | ||||
|           'The guild should have at least one channel' | ||||
|           "The guild should have at least one channel", | ||||
|         ) | ||||
|       } | ||||
|       const channel = await prisma.channel.delete({ | ||||
|         where: { id: channelId } | ||||
|         where: { id: channelId }, | ||||
|       }) | ||||
|       const defaultChannel = await prisma.channel.findFirst({ | ||||
|         where: { guildId: member.guildId } | ||||
|         where: { guildId: member.guildId }, | ||||
|       }) | ||||
|       if (defaultChannel == null) { | ||||
|         throw fastify.httpErrors.internalServerError() | ||||
|       } | ||||
|       const item = { | ||||
|         ...channel, | ||||
|         defaultChannelId: defaultChannel.id | ||||
|         defaultChannelId: defaultChannel.id, | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'channels', | ||||
|         event: "channels", | ||||
|         guildId: member.guildId, | ||||
|         payload: { | ||||
|           action: 'delete', | ||||
|           item | ||||
|         } | ||||
|           action: "delete", | ||||
|           item, | ||||
|         }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return item | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,37 +1,37 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   channelId: channelSchema.id | ||||
|   channelId: channelSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'GET a channel with its id.', | ||||
|   tags: ['channels'] as string[], | ||||
|   description: "GET a channel with its id.", | ||||
|   tags: ["channels"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   response: { | ||||
|     200: Type.Object({ | ||||
|       channel: Type.Object(channelSchema) | ||||
|       channel: Type.Object(channelSchema), | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getChannelByIdService: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -40,8 +40,8 @@ export const getChannelByIdService: FastifyPluginAsync = async (fastify) => { | ||||
|   fastify.route<{ | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/channels/:channelId', | ||||
|     method: "GET", | ||||
|     url: "/channels/:channelId", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -49,19 +49,19 @@ export const getChannelByIdService: FastifyPluginAsync = async (fastify) => { | ||||
|       } | ||||
|       const { channelId } = request.params | ||||
|       const channel = await prisma.channel.findUnique({ | ||||
|         where: { id: channelId } | ||||
|         where: { id: channelId }, | ||||
|       }) | ||||
|       if (channel == null) { | ||||
|         throw fastify.httpErrors.notFound('Channel not found') | ||||
|         throw fastify.httpErrors.notFound("Channel not found") | ||||
|       } | ||||
|       const member = await prisma.member.findFirst({ | ||||
|         where: { guildId: channel.guildId, userId: request.user.current.id } | ||||
|         where: { guildId: channel.guildId, userId: request.user.current.id }, | ||||
|       }) | ||||
|       if (member == null) { | ||||
|         throw fastify.httpErrors.notFound('Channel not found') | ||||
|         throw fastify.httpErrors.notFound("Channel not found") | ||||
|       } | ||||
|       reply.statusCode = 200 | ||||
|       return { channel } | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,47 +1,47 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { messageExample } from '#src/models/Message.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { userExample } from "#src/models/User.js" | ||||
| import { messageExample } from "#src/models/Message.js" | ||||
|  | ||||
| await test('GET /channels/[channelId]/messages', async (t) => { | ||||
| await test("GET /channels/[channelId]/messages", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           user: userExample | ||||
|           user: userExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'message').value({ | ||||
|     sinon.stub(prisma, "message").value({ | ||||
|       findMany: async () => { | ||||
|         return [messageExample] | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       method: "GET", | ||||
|       url: `/channels/${channelExample.id}/messages`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -56,61 +56,61 @@ await test('GET /channels/[channelId]/messages', async (t) => { | ||||
|     assert.strictEqual(responseJson[0].member.user.name, userExample.name) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with not found channel', async () => { | ||||
|   await t.test("fails with not found channel", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           user: userExample | ||||
|           user: userExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       method: "GET", | ||||
|       url: `/channels/${channelExample.id}/messages`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|     assert.strictEqual(responseJson.message, 'Channel not found') | ||||
|     assert.strictEqual(responseJson.message, "Channel not found") | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with not found member', async () => { | ||||
|   await t.test("fails with not found member", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       method: "GET", | ||||
|       url: `/channels/${channelExample.id}/messages`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|     assert.strictEqual(responseJson.message, 'Channel not found') | ||||
|     assert.strictEqual(responseJson.message, "Channel not found") | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with unauthenticated user', async () => { | ||||
|   await t.test("fails with unauthenticated user", async () => { | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: `/channels/1/messages` | ||||
|       method: "GET", | ||||
|       url: `/channels/1/messages`, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 401) | ||||
|   }) | ||||
|   | ||||
| @@ -1,48 +1,48 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { messageExample } from '#src/models/Message.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { userExample } from "#src/models/User.js" | ||||
| import { messageExample } from "#src/models/Message.js" | ||||
|  | ||||
| await test('POST /channels/[channelId]/messages', async (t) => { | ||||
| await test("POST /channels/[channelId]/messages", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           user: userExample | ||||
|           user: userExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'message').value({ | ||||
|     sinon.stub(prisma, "message").value({ | ||||
|       create: async () => { | ||||
|         return messageExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'POST', | ||||
|       method: "POST", | ||||
|       url: `/channels/${channelExample.id}/messages`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { value: messageExample.value } | ||||
|       payload: { value: messageExample.value }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 201) | ||||
| @@ -56,82 +56,82 @@ await test('POST /channels/[channelId]/messages', async (t) => { | ||||
|     assert.strictEqual(responseJson.member.user.name, userExample.name) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with no message value', async () => { | ||||
|   await t.test("fails with no message value", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           user: userExample | ||||
|           user: userExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'POST', | ||||
|       method: "POST", | ||||
|       url: `/channels/${channelExample.id}/messages`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: {} | ||||
|       payload: {}, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with not found channel', async () => { | ||||
|   await t.test("fails with not found channel", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           user: userExample | ||||
|           user: userExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'POST', | ||||
|       url: '/channels/5/messages', | ||||
|       method: "POST", | ||||
|       url: "/channels/5/messages", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { value: messageExample.value } | ||||
|       payload: { value: messageExample.value }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|     assert.strictEqual(responseJson.message, 'Channel not found') | ||||
|     assert.strictEqual(responseJson.message, "Channel not found") | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with not found member', async () => { | ||||
|   await t.test("fails with not found member", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findUnique: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'POST', | ||||
|       method: "POST", | ||||
|       url: `/channels/${channelExample.id}/messages`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { value: messageExample.value } | ||||
|       payload: { value: messageExample.value }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|     assert.strictEqual(responseJson.message, 'Channel not found') | ||||
|     assert.strictEqual(responseJson.message, "Channel not found") | ||||
|   }) | ||||
| }) | ||||
|   | ||||
| @@ -1,34 +1,34 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { messageSchema } from '#src/models/Message.js' | ||||
| import { memberSchema } from '#src/models/Member.js' | ||||
| import { userPublicWithoutSettingsSchema } from '#src/models/User.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { messageSchema } from "#src/models/Message.js" | ||||
| import { memberSchema } from "#src/models/Member.js" | ||||
| import { userPublicWithoutSettingsSchema } from "#src/models/User.js" | ||||
| import { | ||||
|   getPaginationOptions, | ||||
|   queryPaginationObjectSchema | ||||
| } from '#src/tools/database/pagination.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
|   queryPaginationObjectSchema, | ||||
| } from "#src/tools/database/pagination.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
|  | ||||
| type QuerySchemaType = Static<typeof queryPaginationObjectSchema> | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   channelId: channelSchema.id | ||||
|   channelId: channelSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'GET all the messages of a channel by its id.', | ||||
|   tags: ['messages'] as string[], | ||||
|   description: "GET all the messages of a channel by its id.", | ||||
|   tags: ["messages"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   querystring: queryPaginationObjectSchema, | ||||
| @@ -38,20 +38,20 @@ const getServiceSchema: FastifySchema = { | ||||
|         ...messageSchema, | ||||
|         member: Type.Object({ | ||||
|           ...memberSchema, | ||||
|           user: Type.Object(userPublicWithoutSettingsSchema) | ||||
|         }) | ||||
|       }) | ||||
|           user: Type.Object(userPublicWithoutSettingsSchema), | ||||
|         }), | ||||
|       }), | ||||
|     ), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getMessagesByChannelIdService: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.register(authenticateUser) | ||||
|  | ||||
| @@ -59,8 +59,8 @@ export const getMessagesByChannelIdService: FastifyPluginAsync = async ( | ||||
|     Params: Parameters | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/channels/:channelId/messages', | ||||
|     method: "GET", | ||||
|     url: "/channels/:channelId/messages", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -68,21 +68,21 @@ export const getMessagesByChannelIdService: FastifyPluginAsync = async ( | ||||
|       } | ||||
|       const { channelId } = request.params | ||||
|       const channel = await prisma.channel.findUnique({ | ||||
|         where: { id: channelId } | ||||
|         where: { id: channelId }, | ||||
|       }) | ||||
|       if (channel == null) { | ||||
|         throw fastify.httpErrors.notFound('Channel not found') | ||||
|         throw fastify.httpErrors.notFound("Channel not found") | ||||
|       } | ||||
|       const memberCheck = await prisma.member.findFirst({ | ||||
|         where: { guildId: channel.guildId, userId: request.user.current.id } | ||||
|         where: { guildId: channel.guildId, userId: request.user.current.id }, | ||||
|       }) | ||||
|       if (memberCheck == null) { | ||||
|         throw fastify.httpErrors.notFound('Channel not found') | ||||
|         throw fastify.httpErrors.notFound("Channel not found") | ||||
|       } | ||||
|       const messagesRequest = await prisma.message.findMany({ | ||||
|         ...getPaginationOptions(request.query), | ||||
|         orderBy: { createdAt: 'desc' }, | ||||
|         where: { channelId } | ||||
|         orderBy: { createdAt: "desc" }, | ||||
|         where: { channelId }, | ||||
|       }) | ||||
|       const messages = await Promise.all( | ||||
|         messagesRequest.reverse().map(async (message) => { | ||||
| @@ -98,10 +98,10 @@ export const getMessagesByChannelIdService: FastifyPluginAsync = async ( | ||||
|                   biography: true, | ||||
|                   website: true, | ||||
|                   createdAt: true, | ||||
|                   updatedAt: true | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|                   updatedAt: true, | ||||
|                 }, | ||||
|               }, | ||||
|             }, | ||||
|           }) | ||||
|           return { | ||||
|             ...message, | ||||
| @@ -109,14 +109,14 @@ export const getMessagesByChannelIdService: FastifyPluginAsync = async ( | ||||
|               ...member, | ||||
|               user: { | ||||
|                 ...member?.user, | ||||
|                 email: null | ||||
|               } | ||||
|             } | ||||
|                 email: null, | ||||
|               }, | ||||
|             }, | ||||
|           } | ||||
|         }) | ||||
|         }), | ||||
|       ) | ||||
|       reply.statusCode = 200 | ||||
|       return messages | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,35 +1,35 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { messageSchema } from '#src/models/Message.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
| import { memberSchema } from '#src/models/Member.js' | ||||
| import { userPublicWithoutSettingsSchema } from '#src/models/User.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { messageSchema } from "#src/models/Message.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
| import { memberSchema } from "#src/models/Member.js" | ||||
| import { userPublicWithoutSettingsSchema } from "#src/models/User.js" | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   channelId: channelSchema.id | ||||
|   channelId: channelSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const bodyPostServiceSchema = Type.Object({ | ||||
|   value: messageSchema.value | ||||
|   value: messageSchema.value, | ||||
| }) | ||||
|  | ||||
| type BodyPostServiceSchemaType = Static<typeof bodyPostServiceSchema> | ||||
|  | ||||
| const postServiceSchema: FastifySchema = { | ||||
|   description: | ||||
|     'POST a new message (text) in a specific channel using its channelId.', | ||||
|   tags: ['messages'] as string[], | ||||
|     "POST a new message (text) in a specific channel using its channelId.", | ||||
|   tags: ["messages"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   body: bodyPostServiceSchema, | ||||
|   params: parametersSchema, | ||||
| @@ -38,19 +38,19 @@ const postServiceSchema: FastifySchema = { | ||||
|       ...messageSchema, | ||||
|       member: Type.Object({ | ||||
|         ...memberSchema, | ||||
|         user: Type.Object(userPublicWithoutSettingsSchema) | ||||
|       }) | ||||
|         user: Type.Object(userPublicWithoutSettingsSchema), | ||||
|       }), | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const postMessageByChannelIdService: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.register(authenticateUser) | ||||
|  | ||||
| @@ -58,8 +58,8 @@ export const postMessageByChannelIdService: FastifyPluginAsync = async ( | ||||
|     Body: BodyPostServiceSchemaType | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'POST', | ||||
|     url: '/channels/:channelId/messages', | ||||
|     method: "POST", | ||||
|     url: "/channels/:channelId/messages", | ||||
|     schema: postServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -67,10 +67,10 @@ export const postMessageByChannelIdService: FastifyPluginAsync = async ( | ||||
|       } | ||||
|       const { channelId } = request.params | ||||
|       const channel = await prisma.channel.findUnique({ | ||||
|         where: { id: channelId } | ||||
|         where: { id: channelId }, | ||||
|       }) | ||||
|       if (channel == null) { | ||||
|         throw fastify.httpErrors.notFound('Channel not found') | ||||
|         throw fastify.httpErrors.notFound("Channel not found") | ||||
|       } | ||||
|       const memberCheck = await prisma.member.findFirst({ | ||||
|         where: { guildId: channel.guildId, userId: request.user.current.id }, | ||||
| @@ -84,23 +84,23 @@ export const postMessageByChannelIdService: FastifyPluginAsync = async ( | ||||
|               biography: true, | ||||
|               website: true, | ||||
|               createdAt: true, | ||||
|               updatedAt: true | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|               updatedAt: true, | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|       }) | ||||
|       if (memberCheck == null) { | ||||
|         throw fastify.httpErrors.notFound('Channel not found') | ||||
|         throw fastify.httpErrors.notFound("Channel not found") | ||||
|       } | ||||
|       const { value } = request.body | ||||
|       const message = await prisma.message.create({ | ||||
|         data: { | ||||
|           value, | ||||
|           type: 'text', | ||||
|           mimetype: 'text/plain', | ||||
|           type: "text", | ||||
|           mimetype: "text/plain", | ||||
|           channelId, | ||||
|           memberId: memberCheck.id | ||||
|         } | ||||
|           memberId: memberCheck.id, | ||||
|         }, | ||||
|       }) | ||||
|       const item = { | ||||
|         ...message, | ||||
| @@ -108,17 +108,17 @@ export const postMessageByChannelIdService: FastifyPluginAsync = async ( | ||||
|           ...memberCheck, | ||||
|           user: { | ||||
|             ...memberCheck.user, | ||||
|             email: null | ||||
|           } | ||||
|         } | ||||
|             email: null, | ||||
|           }, | ||||
|         }, | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'messages', | ||||
|         event: "messages", | ||||
|         guildId: item.member.guildId, | ||||
|         payload: { action: 'create', item } | ||||
|         payload: { action: "create", item }, | ||||
|       }) | ||||
|       reply.statusCode = 201 | ||||
|       return item | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,33 +1,33 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import fastifyMultipart from '@fastify/multipart' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
| import fastifyMultipart from "@fastify/multipart" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { messageSchema } from '#src/models/Message.js' | ||||
| import { memberSchema } from '#src/models/Member.js' | ||||
| import { userPublicWithoutSettingsSchema } from '#src/models/User.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
| import { uploadFile } from '#src/tools/utils/uploadFile.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { messageSchema } from "#src/models/Message.js" | ||||
| import { memberSchema } from "#src/models/Member.js" | ||||
| import { userPublicWithoutSettingsSchema } from "#src/models/User.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
| import { uploadFile } from "#src/tools/utils/uploadFile.js" | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   channelId: channelSchema.id | ||||
|   channelId: channelSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const postServiceSchema: FastifySchema = { | ||||
|   description: | ||||
|     'POST a new message (file) in a specific channel using its channelId.', | ||||
|   tags: ['messages'] as string[], | ||||
|   consumes: ['multipart/form-data'] as string[], | ||||
|   produces: ['application/json'] as string[], | ||||
|     "POST a new message (file) in a specific channel using its channelId.", | ||||
|   tags: ["messages"] as string[], | ||||
|   consumes: ["multipart/form-data"] as string[], | ||||
|   produces: ["application/json"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   response: { | ||||
| @@ -35,20 +35,20 @@ const postServiceSchema: FastifySchema = { | ||||
|       ...messageSchema, | ||||
|       member: Type.Object({ | ||||
|         ...memberSchema, | ||||
|         user: Type.Object(userPublicWithoutSettingsSchema) | ||||
|       }) | ||||
|         user: Type.Object(userPublicWithoutSettingsSchema), | ||||
|       }), | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     431: fastifyErrors[431], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.register(authenticateUser) | ||||
|  | ||||
| @@ -57,8 +57,8 @@ export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async ( | ||||
|   fastify.route<{ | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'POST', | ||||
|     url: '/channels/:channelId/messages/uploads', | ||||
|     method: "POST", | ||||
|     url: "/channels/:channelId/messages/uploads", | ||||
|     schema: postServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -66,10 +66,10 @@ export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async ( | ||||
|       } | ||||
|       const { channelId } = request.params | ||||
|       const channel = await prisma.channel.findUnique({ | ||||
|         where: { id: channelId } | ||||
|         where: { id: channelId }, | ||||
|       }) | ||||
|       if (channel == null) { | ||||
|         throw fastify.httpErrors.notFound('Channel not found') | ||||
|         throw fastify.httpErrors.notFound("Channel not found") | ||||
|       } | ||||
|       const memberCheck = await prisma.member.findFirst({ | ||||
|         where: { guildId: channel.guildId, userId: request.user.current.id }, | ||||
| @@ -83,27 +83,27 @@ export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async ( | ||||
|               biography: true, | ||||
|               website: true, | ||||
|               createdAt: true, | ||||
|               updatedAt: true | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|               updatedAt: true, | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|       }) | ||||
|       if (memberCheck == null) { | ||||
|         throw fastify.httpErrors.notFound('Channel not found') | ||||
|         throw fastify.httpErrors.notFound("Channel not found") | ||||
|       } | ||||
|       const file = await uploadFile({ | ||||
|         fastify, | ||||
|         request, | ||||
|         folderInUploadsFolder: 'messages' | ||||
|         folderInUploadsFolder: "messages", | ||||
|       }) | ||||
|       const message = await prisma.message.create({ | ||||
|         data: { | ||||
|           value: file.pathToStoreInDatabase, | ||||
|           type: 'file', | ||||
|           type: "file", | ||||
|           mimetype: file.mimetype, | ||||
|           channelId, | ||||
|           memberId: memberCheck.id | ||||
|         } | ||||
|           memberId: memberCheck.id, | ||||
|         }, | ||||
|       }) | ||||
|       reply.statusCode = 201 | ||||
|       const item = { | ||||
| @@ -112,16 +112,16 @@ export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async ( | ||||
|           ...memberCheck, | ||||
|           user: { | ||||
|             ...memberCheck.user, | ||||
|             email: null | ||||
|           } | ||||
|         } | ||||
|             email: null, | ||||
|           }, | ||||
|         }, | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'messages', | ||||
|         event: "messages", | ||||
|         guildId: item.member.guildId, | ||||
|         payload: { action: 'create', item } | ||||
|         payload: { action: "create", item }, | ||||
|       }) | ||||
|       return item | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,45 +1,45 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
|  | ||||
| const bodyPutServiceSchema = Type.Object({ | ||||
|   name: channelSchema.name | ||||
|   name: channelSchema.name, | ||||
| }) | ||||
|  | ||||
| type BodyPutServiceSchemaType = Static<typeof bodyPutServiceSchema> | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   channelId: channelSchema.id | ||||
|   channelId: channelSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const putServiceSchema: FastifySchema = { | ||||
|   description: 'UPDATE a channel with its id.', | ||||
|   tags: ['channels'] as string[], | ||||
|   description: "UPDATE a channel with its id.", | ||||
|   tags: ["channels"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   body: bodyPutServiceSchema, | ||||
|   response: { | ||||
|     200: Type.Object({ | ||||
|       ...channelSchema, | ||||
|       defaultChannelId: channelSchema.id | ||||
|       defaultChannelId: channelSchema.id, | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const putChannelService: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -49,8 +49,8 @@ export const putChannelService: FastifyPluginAsync = async (fastify) => { | ||||
|     Body: BodyPutServiceSchemaType | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'PUT', | ||||
|     url: '/channels/:channelId', | ||||
|     method: "PUT", | ||||
|     url: "/channels/:channelId", | ||||
|     schema: putServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -60,44 +60,44 @@ export const putChannelService: FastifyPluginAsync = async (fastify) => { | ||||
|       const { channelId } = params | ||||
|       const { name } = body | ||||
|       const channelCheck = await prisma.channel.findUnique({ | ||||
|         where: { id: channelId } | ||||
|         where: { id: channelId }, | ||||
|       }) | ||||
|       if (channelCheck == null) { | ||||
|         throw fastify.httpErrors.notFound('Channel not found') | ||||
|         throw fastify.httpErrors.notFound("Channel not found") | ||||
|       } | ||||
|       const member = await prisma.member.findFirst({ | ||||
|         where: { guildId: channelCheck.guildId, userId: user.current.id } | ||||
|         where: { guildId: channelCheck.guildId, userId: user.current.id }, | ||||
|       }) | ||||
|       if (member == null) { | ||||
|         throw fastify.httpErrors.notFound('Member not found') | ||||
|         throw fastify.httpErrors.notFound("Member not found") | ||||
|       } | ||||
|       if (!member.isOwner) { | ||||
|         throw fastify.httpErrors.badRequest('You should be a member owner') | ||||
|         throw fastify.httpErrors.badRequest("You should be a member owner") | ||||
|       } | ||||
|       const channel = await prisma.channel.update({ | ||||
|         where: { id: channelId }, | ||||
|         data: { name } | ||||
|         data: { name }, | ||||
|       }) | ||||
|       const defaultChannel = await prisma.channel.findFirst({ | ||||
|         where: { guildId: member.guildId } | ||||
|         where: { guildId: member.guildId }, | ||||
|       }) | ||||
|       if (defaultChannel == null) { | ||||
|         throw fastify.httpErrors.internalServerError() | ||||
|       } | ||||
|       const item = { | ||||
|         ...channel, | ||||
|         defaultChannelId: defaultChannel.id | ||||
|         defaultChannelId: defaultChannel.id, | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'channels', | ||||
|         event: "channels", | ||||
|         guildId: member.guildId, | ||||
|         payload: { | ||||
|           action: 'update', | ||||
|           item | ||||
|         } | ||||
|           action: "update", | ||||
|           item, | ||||
|         }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return item | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| import type { FastifyPluginAsync } from 'fastify' | ||||
| import type { FastifyPluginAsync } from "fastify" | ||||
|  | ||||
| import { deleteChannelService } from './[channelId]/delete.js' | ||||
| import { getChannelByIdService } from './[channelId]/get.js' | ||||
| import { getMessagesByChannelIdService } from './[channelId]/messages/get.js' | ||||
| import { postMessageByChannelIdService } from './[channelId]/messages/post.js' | ||||
| import { postMessageUploadsByChannelIdService } from './[channelId]/messages/uploads/post.js' | ||||
| import { putChannelService } from './[channelId]/put.js' | ||||
| import { deleteChannelService } from "./[channelId]/delete.js" | ||||
| import { getChannelByIdService } from "./[channelId]/get.js" | ||||
| import { getMessagesByChannelIdService } from "./[channelId]/messages/get.js" | ||||
| import { postMessageByChannelIdService } from "./[channelId]/messages/post.js" | ||||
| import { postMessageUploadsByChannelIdService } from "./[channelId]/messages/uploads/post.js" | ||||
| import { putChannelService } from "./[channelId]/put.js" | ||||
|  | ||||
| export const channelsService: FastifyPluginAsync = async (fastify) => { | ||||
|   await fastify.register(getChannelByIdService) | ||||
|   | ||||
| @@ -1,41 +1,41 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
|  | ||||
| await test('DELETE /guilds/[guildId]', async (t) => { | ||||
| await test("DELETE /guilds/[guildId]", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds and delete the guild', async () => { | ||||
|   await t.test("succeeds and delete the guild", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           isOwner: true, | ||||
|           guild: guildExample | ||||
|           guild: guildExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'guild').value({ | ||||
|     sinon.stub(prisma, "guild").value({ | ||||
|       delete: async () => { | ||||
|         return guildExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/guilds/${guildExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -46,44 +46,44 @@ await test('DELETE /guilds/[guildId]', async (t) => { | ||||
|  | ||||
|   await t.test("fails if the guild doesn't exist", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/guilds/${guildExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the user is not the owner', async () => { | ||||
|   await t.test("fails if the user is not the owner", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           isOwner: false, | ||||
|           guild: guildExample | ||||
|           guild: guildExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/guilds/${guildExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|     assert.strictEqual( | ||||
|       responseJson.message, | ||||
|       'You should be an owner of the guild' | ||||
|       "You should be an owner of the guild", | ||||
|     ) | ||||
|   }) | ||||
| }) | ||||
|   | ||||
| @@ -1,48 +1,48 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
| import { userExample } from "#src/models/User.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
|  | ||||
| const defaultChannelId = 5 | ||||
|  | ||||
| await test('GET /guilds/[guildId]', async (t) => { | ||||
| await test("GET /guilds/[guildId]", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken, user } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           guild: guildExample, | ||||
|           user: userExample | ||||
|           user: userExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...channelExample, | ||||
|           id: defaultChannelId | ||||
|           id: defaultChannelId, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       method: "GET", | ||||
|       url: `/guilds/${guildExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -55,29 +55,29 @@ await test('GET /guilds/[guildId]', async (t) => { | ||||
|     assert.strictEqual(responseJson.guild.defaultChannelId, defaultChannelId) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with not found member/guild', async () => { | ||||
|   await t.test("fails with not found member/guild", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/guilds/1', | ||||
|       method: "GET", | ||||
|       url: "/guilds/1", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|     assert.strictEqual(responseJson.message, 'Member not found') | ||||
|     assert.strictEqual(responseJson.message, "Member not found") | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with unauthenticated user', async () => { | ||||
|   await t.test("fails with unauthenticated user", async () => { | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/guilds/1' | ||||
|       method: "GET", | ||||
|       url: "/guilds/1", | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 401) | ||||
|   }) | ||||
|   | ||||
| @@ -1,62 +1,62 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
|  | ||||
| const defaultChannelId = 5 | ||||
| const newName = 'New guild name' | ||||
| const newDescription = 'New guild description' | ||||
| const newName = "New guild name" | ||||
| const newDescription = "New guild description" | ||||
|  | ||||
| await test('PUT /guilds/[guildId]', async (t) => { | ||||
| await test("PUT /guilds/[guildId]", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds and edit the guild', async () => { | ||||
|   await t.test("succeeds and edit the guild", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           isOwner: true, | ||||
|           guild: guildExample | ||||
|           guild: guildExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...channelExample, | ||||
|           id: defaultChannelId | ||||
|           id: defaultChannelId, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'guild').value({ | ||||
|     sinon.stub(prisma, "guild").value({ | ||||
|       update: async () => { | ||||
|         return { | ||||
|           ...guildExample, | ||||
|           name: newName, | ||||
|           description: newDescription | ||||
|           description: newDescription, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       method: "PUT", | ||||
|       url: `/guilds/${guildExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { | ||||
|         name: newName, | ||||
|         description: newDescription | ||||
|       } | ||||
|         description: newDescription, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -67,52 +67,52 @@ await test('PUT /guilds/[guildId]', async (t) => { | ||||
|  | ||||
|   await t.test("fails if the guild doesn't exist", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       method: "PUT", | ||||
|       url: `/guilds/${guildExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { | ||||
|         name: newName, | ||||
|         description: newDescription | ||||
|       } | ||||
|         description: newDescription, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the user is not the owner', async () => { | ||||
|   await t.test("fails if the user is not the owner", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           isOwner: false, | ||||
|           guild: guildExample | ||||
|           guild: guildExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       method: "PUT", | ||||
|       url: `/guilds/${guildExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { | ||||
|         name: newName, | ||||
|         description: newDescription | ||||
|       } | ||||
|         description: newDescription, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|     assert.strictEqual( | ||||
|       responseJson.message, | ||||
|       'You should be an owner of the guild' | ||||
|       "You should be an owner of the guild", | ||||
|     ) | ||||
|   }) | ||||
| }) | ||||
|   | ||||
| @@ -1,38 +1,38 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
|  | ||||
| await test('GET /guilds/[guildId]/channels', async (t) => { | ||||
| await test("GET /guilds/[guildId]/channels", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return memberExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findMany: async () => { | ||||
|         return [channelExample] | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       method: "GET", | ||||
|       url: `/guilds/${guildExample.id}/channels`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -42,29 +42,29 @@ await test('GET /guilds/[guildId]/channels', async (t) => { | ||||
|     assert.strictEqual(responseJson[0].guildId, channelExample.guildId) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with not found member/guild', async () => { | ||||
|   await t.test("fails with not found member/guild", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/guilds/1/channels', | ||||
|       method: "GET", | ||||
|       url: "/guilds/1/channels", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|     assert.strictEqual(responseJson.message, 'Member not found') | ||||
|     assert.strictEqual(responseJson.message, "Member not found") | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with unauthenticated user', async () => { | ||||
|   await t.test("fails with unauthenticated user", async () => { | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/guilds/1/channels' | ||||
|       method: "GET", | ||||
|       url: "/guilds/1/channels", | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 401) | ||||
|   }) | ||||
|   | ||||
| @@ -1,47 +1,47 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
|  | ||||
| const defaultChannelId = 5 | ||||
|  | ||||
| await test('POST /guilds/[guildId]/channels', async (t) => { | ||||
| await test("POST /guilds/[guildId]/channels", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return memberExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...channelExample, | ||||
|           id: defaultChannelId | ||||
|           id: defaultChannelId, | ||||
|         } | ||||
|       }, | ||||
|       create: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'POST', | ||||
|       method: "POST", | ||||
|       url: `/guilds/${guildExample.id}/channels`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { name: channelExample.name } | ||||
|       payload: { name: channelExample.name }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 201) | ||||
| @@ -51,41 +51,41 @@ await test('POST /guilds/[guildId]/channels', async (t) => { | ||||
|     assert.strictEqual(responseJson.defaultChannelId, defaultChannelId) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the member is not found', async () => { | ||||
|   await t.test("fails if the member is not found", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'POST', | ||||
|       method: "POST", | ||||
|       url: `/guilds/${guildExample.id}/channels`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { name: channelExample.name } | ||||
|       payload: { name: channelExample.name }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the member is not owner', async () => { | ||||
|   await t.test("fails if the member is not owner", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           isOwner: false | ||||
|           isOwner: false, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'POST', | ||||
|       method: "POST", | ||||
|       url: `/guilds/${guildExample.id}/channels`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { name: channelExample.name } | ||||
|       payload: { name: channelExample.name }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|   }) | ||||
|   | ||||
| @@ -1,32 +1,32 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
| import { | ||||
|   getPaginationOptions, | ||||
|   queryPaginationObjectSchema | ||||
| } from '#src/tools/database/pagination.js' | ||||
|   queryPaginationObjectSchema, | ||||
| } from "#src/tools/database/pagination.js" | ||||
|  | ||||
| type QuerySchemaType = Static<typeof queryPaginationObjectSchema> | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   guildId: guildSchema.id | ||||
|   guildId: guildSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'GET all the channels of a guild with its id.', | ||||
|   tags: ['channels'] as string[], | ||||
|   description: "GET all the channels of a guild with its id.", | ||||
|   tags: ["channels"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   querystring: queryPaginationObjectSchema, | ||||
| @@ -36,12 +36,12 @@ const getServiceSchema: FastifySchema = { | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getChannelsByGuildIdService: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.register(authenticateUser) | ||||
|  | ||||
| @@ -49,8 +49,8 @@ export const getChannelsByGuildIdService: FastifyPluginAsync = async ( | ||||
|     Params: Parameters | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/guilds/:guildId/channels', | ||||
|     method: "GET", | ||||
|     url: "/guilds/:guildId/channels", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -58,19 +58,19 @@ export const getChannelsByGuildIdService: FastifyPluginAsync = async ( | ||||
|       } | ||||
|       const { guildId } = request.params | ||||
|       const member = await prisma.member.findFirst({ | ||||
|         where: { guildId, userId: request.user.current.id } | ||||
|         where: { guildId, userId: request.user.current.id }, | ||||
|       }) | ||||
|       if (member == null) { | ||||
|         throw fastify.httpErrors.notFound('Member not found') | ||||
|         throw fastify.httpErrors.notFound("Member not found") | ||||
|       } | ||||
|       const channels = await prisma.channel.findMany({ | ||||
|         ...getPaginationOptions(request.query), | ||||
|         where: { | ||||
|           guildId | ||||
|         } | ||||
|           guildId, | ||||
|         }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return channels | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,46 +1,46 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
|  | ||||
| const bodyPostServiceSchema = Type.Object({ | ||||
|   name: channelSchema.name | ||||
|   name: channelSchema.name, | ||||
| }) | ||||
|  | ||||
| type BodyPostServiceSchemaType = Static<typeof bodyPostServiceSchema> | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   guildId: guildSchema.id | ||||
|   guildId: guildSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const postChannelServiceSchema: FastifySchema = { | ||||
|   description: 'Create a channel.', | ||||
|   tags: ['channels'] as string[], | ||||
|   description: "Create a channel.", | ||||
|   tags: ["channels"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   body: bodyPostServiceSchema, | ||||
|   params: parametersSchema, | ||||
|   response: { | ||||
|     201: Type.Object({ | ||||
|       ...channelSchema, | ||||
|       defaultChannelId: channelSchema.id | ||||
|       defaultChannelId: channelSchema.id, | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const postChannelService: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -50,8 +50,8 @@ export const postChannelService: FastifyPluginAsync = async (fastify) => { | ||||
|     Body: BodyPostServiceSchemaType | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'POST', | ||||
|     url: '/guilds/:guildId/channels', | ||||
|     method: "POST", | ||||
|     url: "/guilds/:guildId/channels", | ||||
|     schema: postChannelServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -61,40 +61,40 @@ export const postChannelService: FastifyPluginAsync = async (fastify) => { | ||||
|       const { guildId } = params | ||||
|       const { name } = body | ||||
|       const member = await prisma.member.findFirst({ | ||||
|         where: { guildId, userId: user.current.id } | ||||
|         where: { guildId, userId: user.current.id }, | ||||
|       }) | ||||
|       if (member == null) { | ||||
|         throw fastify.httpErrors.notFound('Member not found') | ||||
|         throw fastify.httpErrors.notFound("Member not found") | ||||
|       } | ||||
|       if (!member.isOwner) { | ||||
|         throw fastify.httpErrors.badRequest('You should be a member owner') | ||||
|         throw fastify.httpErrors.badRequest("You should be a member owner") | ||||
|       } | ||||
|       const channel = await prisma.channel.create({ | ||||
|         data: { | ||||
|           name, | ||||
|           guildId | ||||
|         } | ||||
|           guildId, | ||||
|         }, | ||||
|       }) | ||||
|       const defaultChannel = await prisma.channel.findFirst({ | ||||
|         where: { guildId: member.guildId } | ||||
|         where: { guildId: member.guildId }, | ||||
|       }) | ||||
|       if (defaultChannel == null) { | ||||
|         throw fastify.httpErrors.internalServerError() | ||||
|       } | ||||
|       const item = { | ||||
|         ...channel, | ||||
|         defaultChannelId: defaultChannel.id | ||||
|         defaultChannelId: defaultChannel.id, | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'channels', | ||||
|         event: "channels", | ||||
|         guildId, | ||||
|         payload: { | ||||
|           action: 'create', | ||||
|           item | ||||
|         } | ||||
|           action: "create", | ||||
|           item, | ||||
|         }, | ||||
|       }) | ||||
|       reply.statusCode = 201 | ||||
|       return item | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,25 +1,25 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   guildId: guildSchema.id | ||||
|   guildId: guildSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const deleteServiceSchema: FastifySchema = { | ||||
|   description: 'DELETE a guild with the guildId.', | ||||
|   tags: ['guilds'] as string[], | ||||
|   description: "DELETE a guild with the guildId.", | ||||
|   tags: ["guilds"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   response: { | ||||
| @@ -28,8 +28,8 @@ const deleteServiceSchema: FastifySchema = { | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const deleteGuildByIdService: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -38,8 +38,8 @@ export const deleteGuildByIdService: FastifyPluginAsync = async (fastify) => { | ||||
|   fastify.route<{ | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'DELETE', | ||||
|     url: '/guilds/:guildId', | ||||
|     method: "DELETE", | ||||
|     url: "/guilds/:guildId", | ||||
|     schema: deleteServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -49,30 +49,30 @@ export const deleteGuildByIdService: FastifyPluginAsync = async (fastify) => { | ||||
|       const member = await prisma.member.findFirst({ | ||||
|         where: { guildId, userId: request.user.current.id }, | ||||
|         include: { | ||||
|           guild: true | ||||
|         } | ||||
|           guild: true, | ||||
|         }, | ||||
|       }) | ||||
|       if (member == null || member.guild == null) { | ||||
|         throw fastify.httpErrors.notFound('Member not found') | ||||
|         throw fastify.httpErrors.notFound("Member not found") | ||||
|       } | ||||
|       if (!member.isOwner) { | ||||
|         throw fastify.httpErrors.badRequest( | ||||
|           'You should be an owner of the guild' | ||||
|           "You should be an owner of the guild", | ||||
|         ) | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'guilds', | ||||
|         event: "guilds", | ||||
|         guildId: member.guildId, | ||||
|         payload: { | ||||
|           action: 'delete', | ||||
|           item: member.guild | ||||
|         } | ||||
|           action: "delete", | ||||
|           item: member.guild, | ||||
|         }, | ||||
|       }) | ||||
|       const guild = await prisma.guild.delete({ | ||||
|         where: { id: member.guildId } | ||||
|         where: { id: member.guildId }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return guild | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,59 +1,59 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import { memberSchema } from '#src/models/Member.js' | ||||
| import { userPublicWithoutSettingsSchema } from '#src/models/User.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
| import { memberSchema } from "#src/models/Member.js" | ||||
| import { userPublicWithoutSettingsSchema } from "#src/models/User.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   guildId: guildSchema.id | ||||
|   guildId: guildSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'GET a guild member with the guildId.', | ||||
|   tags: ['guilds'] as string[], | ||||
|   description: "GET a guild member with the guildId.", | ||||
|   tags: ["guilds"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   response: { | ||||
|     200: Type.Object({ | ||||
|       guild: Type.Object({ | ||||
|         ...guildSchema, | ||||
|         defaultChannelId: channelSchema.id | ||||
|         defaultChannelId: channelSchema.id, | ||||
|       }), | ||||
|       member: Type.Object({ | ||||
|         ...memberSchema, | ||||
|         user: Type.Object(userPublicWithoutSettingsSchema) | ||||
|       }) | ||||
|         user: Type.Object(userPublicWithoutSettingsSchema), | ||||
|       }), | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getGuildMemberByIdService: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.register(authenticateUser) | ||||
|  | ||||
|   fastify.route<{ | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/guilds/:guildId', | ||||
|     method: "GET", | ||||
|     url: "/guilds/:guildId", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -72,17 +72,17 @@ export const getGuildMemberByIdService: FastifyPluginAsync = async ( | ||||
|               biography: true, | ||||
|               website: true, | ||||
|               createdAt: true, | ||||
|               updatedAt: true | ||||
|             } | ||||
|               updatedAt: true, | ||||
|             }, | ||||
|           }, | ||||
|           guild: true | ||||
|         } | ||||
|           guild: true, | ||||
|         }, | ||||
|       }) | ||||
|       if (member == null) { | ||||
|         throw fastify.httpErrors.notFound('Member not found') | ||||
|         throw fastify.httpErrors.notFound("Member not found") | ||||
|       } | ||||
|       const defaultChannel = await prisma.channel.findFirst({ | ||||
|         where: { guildId: member.guildId } | ||||
|         where: { guildId: member.guildId }, | ||||
|       }) | ||||
|       if (defaultChannel == null) { | ||||
|         throw fastify.httpErrors.internalServerError() | ||||
| @@ -90,18 +90,18 @@ export const getGuildMemberByIdService: FastifyPluginAsync = async ( | ||||
|       const item = { | ||||
|         guild: { | ||||
|           ...member.guild, | ||||
|           defaultChannelId: defaultChannel.id | ||||
|           defaultChannelId: defaultChannel.id, | ||||
|         }, | ||||
|         member: { | ||||
|           ...member, | ||||
|           user: { | ||||
|             ...member.user, | ||||
|             email: null | ||||
|           } | ||||
|         } | ||||
|             email: null, | ||||
|           }, | ||||
|         }, | ||||
|       } | ||||
|       reply.statusCode = 200 | ||||
|       return item | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,44 +1,44 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import fastifyMultipart from '@fastify/multipart' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
| import fastifyMultipart from "@fastify/multipart" | ||||
|  | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { uploadFile } from '#src/tools/utils/uploadFile.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { uploadFile } from "#src/tools/utils/uploadFile.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   guildId: guildSchema.id | ||||
|   guildId: guildSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const putServiceSchema: FastifySchema = { | ||||
|   description: 'Edit the icon of the guild with its id', | ||||
|   tags: ['guilds'] as string[], | ||||
|   consumes: ['multipart/form-data'] as string[], | ||||
|   produces: ['application/json'] as string[], | ||||
|   description: "Edit the icon of the guild with its id", | ||||
|   tags: ["guilds"] as string[], | ||||
|   consumes: ["multipart/form-data"] as string[], | ||||
|   produces: ["application/json"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   response: { | ||||
|     200: Type.Object({ | ||||
|       ...guildSchema, | ||||
|       defaultChannelId: channelSchema.id | ||||
|       defaultChannelId: channelSchema.id, | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     431: fastifyErrors[431], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const putGuildIconById: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -49,8 +49,8 @@ export const putGuildIconById: FastifyPluginAsync = async (fastify) => { | ||||
|   fastify.route<{ | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'PUT', | ||||
|     url: '/guilds/:guildId/icon', | ||||
|     method: "PUT", | ||||
|     url: "/guilds/:guildId/icon", | ||||
|     schema: putServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -64,14 +64,14 @@ export const putGuildIconById: FastifyPluginAsync = async (fastify) => { | ||||
|       const file = await uploadFile({ | ||||
|         fastify, | ||||
|         request, | ||||
|         folderInUploadsFolder: 'guilds' | ||||
|         folderInUploadsFolder: "guilds", | ||||
|       }) | ||||
|       await prisma.guild.update({ | ||||
|         where: { id: guildId }, | ||||
|         data: { icon: file.pathToStoreInDatabase } | ||||
|         data: { icon: file.pathToStoreInDatabase }, | ||||
|       }) | ||||
|       const defaultChannel = await prisma.channel.findFirst({ | ||||
|         where: { guildId: guild.id } | ||||
|         where: { guildId: guild.id }, | ||||
|       }) | ||||
|       if (defaultChannel == null) { | ||||
|         throw fastify.httpErrors.internalServerError() | ||||
| @@ -79,15 +79,15 @@ export const putGuildIconById: FastifyPluginAsync = async (fastify) => { | ||||
|       const item = { | ||||
|         ...guild, | ||||
|         icon: file.pathToStoreInDatabase, | ||||
|         defaultChannelId: defaultChannel.id | ||||
|         defaultChannelId: defaultChannel.id, | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'guilds', | ||||
|         event: "guilds", | ||||
|         guildId: guild.id, | ||||
|         payload: { action: 'update', item } | ||||
|         payload: { action: "update", item }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return item | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,36 +1,36 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
| import { userExample } from "#src/models/User.js" | ||||
|  | ||||
| await test('GET /guilds/[guildId]/members', async (t) => { | ||||
| await test("GET /guilds/[guildId]/members", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return memberExample | ||||
|       }, | ||||
|       findMany: async () => { | ||||
|         return [{ ...memberExample, user: userExample }] | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       method: "GET", | ||||
|       url: `/guilds/${guildExample.id}/members`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -42,29 +42,29 @@ await test('GET /guilds/[guildId]/members', async (t) => { | ||||
|     assert.strictEqual(responseJson[0].user.email, null) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with not found member/guild', async () => { | ||||
|   await t.test("fails with not found member/guild", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/guilds/1/members', | ||||
|       method: "GET", | ||||
|       url: "/guilds/1/members", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|     assert.strictEqual(responseJson.message, 'Member not found') | ||||
|     assert.strictEqual(responseJson.message, "Member not found") | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with unauthenticated user', async () => { | ||||
|   await t.test("fails with unauthenticated user", async () => { | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/guilds/1/members' | ||||
|       method: "GET", | ||||
|       url: "/guilds/1/members", | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 401) | ||||
|   }) | ||||
|   | ||||
| @@ -1,33 +1,33 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
| import { | ||||
|   getPaginationOptions, | ||||
|   queryPaginationObjectSchema | ||||
| } from '#src/tools/database/pagination.js' | ||||
| import { memberSchema } from '#src/models/Member.js' | ||||
| import { userPublicWithoutSettingsSchema } from '#src/models/User.js' | ||||
|   queryPaginationObjectSchema, | ||||
| } from "#src/tools/database/pagination.js" | ||||
| import { memberSchema } from "#src/models/Member.js" | ||||
| import { userPublicWithoutSettingsSchema } from "#src/models/User.js" | ||||
|  | ||||
| type QuerySchemaType = Static<typeof queryPaginationObjectSchema> | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   guildId: guildSchema.id | ||||
|   guildId: guildSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'GET all the members of a guild with its id.', | ||||
|   tags: ['members'] as string[], | ||||
|   description: "GET all the members of a guild with its id.", | ||||
|   tags: ["members"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   querystring: queryPaginationObjectSchema, | ||||
| @@ -35,19 +35,19 @@ const getServiceSchema: FastifySchema = { | ||||
|     200: Type.Array( | ||||
|       Type.Object({ | ||||
|         ...memberSchema, | ||||
|         user: Type.Object(userPublicWithoutSettingsSchema) | ||||
|       }) | ||||
|         user: Type.Object(userPublicWithoutSettingsSchema), | ||||
|       }), | ||||
|     ), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getMembersByGuildIdService: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.register(authenticateUser) | ||||
|  | ||||
| @@ -55,8 +55,8 @@ export const getMembersByGuildIdService: FastifyPluginAsync = async ( | ||||
|     Params: Parameters | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/guilds/:guildId/members', | ||||
|     method: "GET", | ||||
|     url: "/guilds/:guildId/members", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -64,14 +64,14 @@ export const getMembersByGuildIdService: FastifyPluginAsync = async ( | ||||
|       } | ||||
|       const { guildId } = request.params | ||||
|       const memberCheck = await prisma.member.findFirst({ | ||||
|         where: { guildId, userId: request.user.current.id } | ||||
|         where: { guildId, userId: request.user.current.id }, | ||||
|       }) | ||||
|       if (memberCheck == null) { | ||||
|         throw fastify.httpErrors.notFound('Member not found') | ||||
|         throw fastify.httpErrors.notFound("Member not found") | ||||
|       } | ||||
|       const membersRequest = await prisma.member.findMany({ | ||||
|         ...getPaginationOptions(request.query), | ||||
|         orderBy: { createdAt: 'asc' }, | ||||
|         orderBy: { createdAt: "asc" }, | ||||
|         where: { guildId }, | ||||
|         include: { | ||||
|           user: { | ||||
| @@ -83,22 +83,22 @@ export const getMembersByGuildIdService: FastifyPluginAsync = async ( | ||||
|               biography: true, | ||||
|               website: true, | ||||
|               createdAt: true, | ||||
|               updatedAt: true | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|               updatedAt: true, | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|       }) | ||||
|       const members = membersRequest.map((member) => { | ||||
|         return { | ||||
|           ...member, | ||||
|           user: { | ||||
|             ...member.user, | ||||
|             email: null | ||||
|           } | ||||
|             email: null, | ||||
|           }, | ||||
|         } | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return members | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,49 +1,49 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
| import { userExample } from "#src/models/User.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
|  | ||||
| const defaultChannelId = 5 | ||||
|  | ||||
| await test('POST /guilds/[guildId]/members/join', async (t) => { | ||||
| await test("POST /guilds/[guildId]/members/join", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       }, | ||||
|       create: async () => { | ||||
|         return { ...memberExample, user: userExample } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findFirst: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'guild').value({ | ||||
|     sinon.stub(prisma, "guild").value({ | ||||
|       findUnique: async () => { | ||||
|         return guildExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'POST', | ||||
|       method: "POST", | ||||
|       url: `/guilds/${guildExample.id}/members/join`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 201) | ||||
| @@ -56,59 +56,59 @@ await test('POST /guilds/[guildId]/members/join', async (t) => { | ||||
|     assert.strictEqual(responseJson.guild.defaultChannelId, channelExample.id) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the guild is not found', async () => { | ||||
|   await t.test("fails if the guild is not found", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'guild').value({ | ||||
|     sinon.stub(prisma, "guild").value({ | ||||
|       findUnique: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'POST', | ||||
|       method: "POST", | ||||
|       url: `/guilds/${guildExample.id}/members/join`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the user is already in the guild', async () => { | ||||
|   await t.test("fails if the user is already in the guild", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return memberExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...channelExample, | ||||
|           id: defaultChannelId | ||||
|           id: defaultChannelId, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'guild').value({ | ||||
|     sinon.stub(prisma, "guild").value({ | ||||
|       findUnique: async () => { | ||||
|         return guildExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'POST', | ||||
|       method: "POST", | ||||
|       url: `/guilds/${guildExample.id}/members/join`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|   | ||||
| @@ -1,28 +1,28 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors, fastifyErrorsSchema, id } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import { memberSchema } from '#src/models/Member.js' | ||||
| import { userPublicWithoutSettingsSchema } from '#src/models/User.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors, fastifyErrorsSchema, id } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
| import { memberSchema } from "#src/models/Member.js" | ||||
| import { userPublicWithoutSettingsSchema } from "#src/models/User.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   guildId: guildSchema.id | ||||
|   guildId: guildSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const postServiceSchema: FastifySchema = { | ||||
|   description: 'Join a guild (create a member).', | ||||
|   tags: ['members'] as string[], | ||||
|   description: "Join a guild (create a member).", | ||||
|   tags: ["members"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   response: { | ||||
| @@ -30,19 +30,19 @@ const postServiceSchema: FastifySchema = { | ||||
|       ...memberSchema, | ||||
|       guild: Type.Object({ | ||||
|         ...guildSchema, | ||||
|         defaultChannelId: id | ||||
|         defaultChannelId: id, | ||||
|       }), | ||||
|       user: Type.Object(userPublicWithoutSettingsSchema) | ||||
|       user: Type.Object(userPublicWithoutSettingsSchema), | ||||
|     }), | ||||
|     400: Type.Object({ | ||||
|       ...fastifyErrorsSchema[400], | ||||
|       defaultChannelId: channelSchema.id | ||||
|       defaultChannelId: channelSchema.id, | ||||
|     }), | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const postMemberService: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -51,8 +51,8 @@ export const postMemberService: FastifyPluginAsync = async (fastify) => { | ||||
|   fastify.route<{ | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'POST', | ||||
|     url: '/guilds/:guildId/members/join', | ||||
|     method: "POST", | ||||
|     url: "/guilds/:guildId/members/join", | ||||
|     schema: postServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -62,16 +62,16 @@ export const postMemberService: FastifyPluginAsync = async (fastify) => { | ||||
|       const { guildId } = params | ||||
|       const guild = await prisma.guild.findUnique({ | ||||
|         where: { | ||||
|           id: guildId | ||||
|         } | ||||
|           id: guildId, | ||||
|         }, | ||||
|       }) | ||||
|       if (guild == null) { | ||||
|         throw fastify.httpErrors.notFound('Guild not found') | ||||
|         throw fastify.httpErrors.notFound("Guild not found") | ||||
|       } | ||||
|       const defaultChannel = await prisma.channel.findFirst({ | ||||
|         where: { | ||||
|           guildId | ||||
|         } | ||||
|           guildId, | ||||
|         }, | ||||
|       }) | ||||
|       if (defaultChannel == null) { | ||||
|         throw fastify.httpErrors.internalServerError() | ||||
| @@ -79,22 +79,22 @@ export const postMemberService: FastifyPluginAsync = async (fastify) => { | ||||
|       const memberCheck = await prisma.member.findFirst({ | ||||
|         where: { | ||||
|           userId: user.current.id, | ||||
|           guildId: guild.id | ||||
|         } | ||||
|           guildId: guild.id, | ||||
|         }, | ||||
|       }) | ||||
|       if (memberCheck != null) { | ||||
|         throw fastify.httpErrors.createError( | ||||
|           400, | ||||
|           'You are already in the guild', | ||||
|           "You are already in the guild", | ||||
|           { | ||||
|             defaultChannelId: defaultChannel.id | ||||
|           } | ||||
|             defaultChannelId: defaultChannel.id, | ||||
|           }, | ||||
|         ) | ||||
|       } | ||||
|       const member = await prisma.member.create({ | ||||
|         data: { | ||||
|           guildId, | ||||
|           userId: user.current.id | ||||
|           userId: user.current.id, | ||||
|         }, | ||||
|         include: { | ||||
|           user: { | ||||
| @@ -106,33 +106,33 @@ export const postMemberService: FastifyPluginAsync = async (fastify) => { | ||||
|               biography: true, | ||||
|               website: true, | ||||
|               createdAt: true, | ||||
|               updatedAt: true | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|               updatedAt: true, | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|       }) | ||||
|  | ||||
|       const item = { | ||||
|         ...member, | ||||
|         user: { | ||||
|           ...member.user, | ||||
|           email: null | ||||
|           email: null, | ||||
|         }, | ||||
|         guild: { | ||||
|           ...guild, | ||||
|           defaultChannelId: defaultChannel.id | ||||
|         } | ||||
|           defaultChannelId: defaultChannel.id, | ||||
|         }, | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'members', | ||||
|         event: "members", | ||||
|         guildId, | ||||
|         payload: { | ||||
|           action: 'create', | ||||
|           item | ||||
|         } | ||||
|           action: "create", | ||||
|           item, | ||||
|         }, | ||||
|       }) | ||||
|       reply.statusCode = 201 | ||||
|       return item | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,39 +1,39 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
|  | ||||
| await test('DELETE /guilds/[guildId]/members/leave', async (t) => { | ||||
| await test("DELETE /guilds/[guildId]/members/leave", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     const member = { | ||||
|       ...memberExample, | ||||
|       isOwner: false | ||||
|       isOwner: false, | ||||
|     } | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return member | ||||
|       }, | ||||
|       delete: async () => { | ||||
|         return member | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/guilds/${guildExample.id}/members/leave`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -42,40 +42,40 @@ await test('DELETE /guilds/[guildId]/members/leave', async (t) => { | ||||
|     assert.strictEqual(responseJson.userId, member.userId) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the member is not found', async () => { | ||||
|   await t.test("fails if the member is not found", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/guilds/${guildExample.id}/members/leave`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the member is owner', async () => { | ||||
|   await t.test("fails if the member is owner", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     const member = { | ||||
|       ...memberExample, | ||||
|       isOwner: true | ||||
|       isOwner: true, | ||||
|     } | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return member | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/guilds/${guildExample.id}/members/leave`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|   }) | ||||
|   | ||||
| @@ -1,26 +1,26 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import { memberSchema } from '#src/models/Member.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
| import { memberSchema } from "#src/models/Member.js" | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   guildId: guildSchema.id | ||||
|   guildId: guildSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const deleteServiceSchema: FastifySchema = { | ||||
|   description: 'Leave a guild (delete a member).', | ||||
|   tags: ['members'] as string[], | ||||
|   description: "Leave a guild (delete a member).", | ||||
|   tags: ["members"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   response: { | ||||
| @@ -29,8 +29,8 @@ const deleteServiceSchema: FastifySchema = { | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const deleteMemberService: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -39,8 +39,8 @@ export const deleteMemberService: FastifyPluginAsync = async (fastify) => { | ||||
|   fastify.route<{ | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'DELETE', | ||||
|     url: '/guilds/:guildId/members/leave', | ||||
|     method: "DELETE", | ||||
|     url: "/guilds/:guildId/members/leave", | ||||
|     schema: deleteServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -49,27 +49,27 @@ export const deleteMemberService: FastifyPluginAsync = async (fastify) => { | ||||
|       const { user, params } = request | ||||
|       const { guildId } = params | ||||
|       const member = await prisma.member.findFirst({ | ||||
|         where: { guildId, userId: user.current.id } | ||||
|         where: { guildId, userId: user.current.id }, | ||||
|       }) | ||||
|       if (member == null) { | ||||
|         throw fastify.httpErrors.notFound('Member not found') | ||||
|         throw fastify.httpErrors.notFound("Member not found") | ||||
|       } | ||||
|       if (member.isOwner) { | ||||
|         throw fastify.httpErrors.badRequest( | ||||
|           "The member owner can't leave the guild (you can delete it instead)" | ||||
|           "The member owner can't leave the guild (you can delete it instead)", | ||||
|         ) | ||||
|       } | ||||
|       await prisma.member.delete({ where: { id: member.id } }) | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'members', | ||||
|         event: "members", | ||||
|         guildId, | ||||
|         payload: { | ||||
|           action: 'delete', | ||||
|           item: member | ||||
|         } | ||||
|           action: "delete", | ||||
|           item: member, | ||||
|         }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return member | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,48 +1,48 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import { parseStringNullish } from '#src/tools/utils/parseStringNullish.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
| import { parseStringNullish } from "#src/tools/utils/parseStringNullish.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   guildId: guildSchema.id | ||||
|   guildId: guildSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const bodyPutServiceSchema = Type.Object({ | ||||
|   name: Type.Optional(guildSchema.name), | ||||
|   description: Type.Optional(guildSchema.description) | ||||
|   description: Type.Optional(guildSchema.description), | ||||
| }) | ||||
|  | ||||
| type BodyPutServiceSchemaType = Static<typeof bodyPutServiceSchema> | ||||
|  | ||||
| const putServiceSchema: FastifySchema = { | ||||
|   description: 'Update a guild with the guildId.', | ||||
|   tags: ['guilds'] as string[], | ||||
|   description: "Update a guild with the guildId.", | ||||
|   tags: ["guilds"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   body: bodyPutServiceSchema, | ||||
|   params: parametersSchema, | ||||
|   response: { | ||||
|     200: Type.Object({ | ||||
|       ...guildSchema, | ||||
|       defaultChannelId: channelSchema.id | ||||
|       defaultChannelId: channelSchema.id, | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const putGuildByIdService: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -52,8 +52,8 @@ export const putGuildByIdService: FastifyPluginAsync = async (fastify) => { | ||||
|     Body: BodyPutServiceSchemaType | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'PUT', | ||||
|     url: '/guilds/:guildId', | ||||
|     method: "PUT", | ||||
|     url: "/guilds/:guildId", | ||||
|     schema: putServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -64,41 +64,44 @@ export const putGuildByIdService: FastifyPluginAsync = async (fastify) => { | ||||
|       const member = await prisma.member.findFirst({ | ||||
|         where: { guildId, userId: request.user.current.id }, | ||||
|         include: { | ||||
|           guild: true | ||||
|         } | ||||
|           guild: true, | ||||
|         }, | ||||
|       }) | ||||
|       if (member == null || member.guild == null) { | ||||
|         throw fastify.httpErrors.notFound('Member not found') | ||||
|         throw fastify.httpErrors.notFound("Member not found") | ||||
|       } | ||||
|       if (!member.isOwner) { | ||||
|         throw fastify.httpErrors.badRequest( | ||||
|           'You should be an owner of the guild' | ||||
|           "You should be an owner of the guild", | ||||
|         ) | ||||
|       } | ||||
|       const guild = await prisma.guild.update({ | ||||
|         where: { id: guildId }, | ||||
|         data: { | ||||
|           name: name ?? member.guild.name, | ||||
|           description: parseStringNullish(member.guild.description, description) | ||||
|         } | ||||
|           description: parseStringNullish( | ||||
|             member.guild.description, | ||||
|             description, | ||||
|           ), | ||||
|         }, | ||||
|       }) | ||||
|       const defaultChannel = await prisma.channel.findFirst({ | ||||
|         where: { guildId: guild.id } | ||||
|         where: { guildId: guild.id }, | ||||
|       }) | ||||
|       if (defaultChannel == null) { | ||||
|         throw fastify.httpErrors.internalServerError() | ||||
|       } | ||||
|       const item = { | ||||
|         ...guild, | ||||
|         defaultChannelId: defaultChannel.id | ||||
|         defaultChannelId: defaultChannel.id, | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'guilds', | ||||
|         event: "guilds", | ||||
|         guildId: guild.id, | ||||
|         payload: { action: 'update', item } | ||||
|         payload: { action: "update", item }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return item | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,43 +1,43 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
|  | ||||
| await test('GET /guilds', async (t) => { | ||||
| await test("GET /guilds", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'guild').value({ | ||||
|     sinon.stub(prisma, "guild").value({ | ||||
|       findUnique: async () => { | ||||
|         return guildExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findMany: async () => { | ||||
|         return [memberExample] | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       findFirst: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/guilds', | ||||
|       method: "GET", | ||||
|       url: "/guilds", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
|   | ||||
| @@ -1,54 +1,54 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
| import { userExample } from "#src/models/User.js" | ||||
|  | ||||
| await test('POST /guilds', async (t) => { | ||||
| await test("POST /guilds", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken, user } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'guild').value({ | ||||
|     sinon.stub(prisma, "guild").value({ | ||||
|       create: async () => { | ||||
|         return guildExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       create: async () => { | ||||
|         return memberExample | ||||
|       }, | ||||
|       findUnique: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           ...userExample | ||||
|           ...userExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'channel').value({ | ||||
|     sinon.stub(prisma, "channel").value({ | ||||
|       create: async () => { | ||||
|         return channelExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'POST', | ||||
|       url: '/guilds', | ||||
|       method: "POST", | ||||
|       url: "/guilds", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { | ||||
|         name: guildExample.name, | ||||
|         description: guildExample.description | ||||
|       } | ||||
|         description: guildExample.description, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 201) | ||||
| @@ -61,21 +61,21 @@ await test('POST /guilds', async (t) => { | ||||
|     assert.strictEqual(responseJson.guild.members[0].guildId, guildExample.id) | ||||
|     assert.strictEqual( | ||||
|       responseJson.guild.members[0].isOwner, | ||||
|       memberExample.isOwner | ||||
|       memberExample.isOwner, | ||||
|     ) | ||||
|     assert.strictEqual(responseJson.guild.channels.length, 1) | ||||
|     assert.strictEqual(responseJson.guild.channels[0].id, channelExample.id) | ||||
|     assert.strictEqual(responseJson.guild.channels[0].guildId, guildExample.id) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with empty name and description', async () => { | ||||
|   await t.test("fails with empty name and description", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     const response = await application.inject({ | ||||
|       method: 'POST', | ||||
|       url: '/guilds', | ||||
|       method: "POST", | ||||
|       url: "/guilds", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|   }) | ||||
|   | ||||
| @@ -1,39 +1,39 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors, id } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors, id } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
| import { | ||||
|   getPaginationOptions, | ||||
|   queryPaginationObjectSchema | ||||
| } from '#src/tools/database/pagination.js' | ||||
|   queryPaginationObjectSchema, | ||||
| } from "#src/tools/database/pagination.js" | ||||
|  | ||||
| type QuerySchemaType = Static<typeof queryPaginationObjectSchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'GET all the guilds of an user.', | ||||
|   tags: ['guilds'] as string[], | ||||
|   description: "GET all the guilds of an user.", | ||||
|   tags: ["guilds"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   querystring: queryPaginationObjectSchema, | ||||
|   response: { | ||||
|     200: Type.Array( | ||||
|       Type.Object({ | ||||
|         ...guildSchema, | ||||
|         defaultChannelId: id | ||||
|       }) | ||||
|         defaultChannelId: id, | ||||
|       }), | ||||
|     ), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getGuilds: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -42,8 +42,8 @@ export const getGuilds: FastifyPluginAsync = async (fastify) => { | ||||
|   fastify.route<{ | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/guilds', | ||||
|     method: "GET", | ||||
|     url: "/guilds", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -52,29 +52,29 @@ export const getGuilds: FastifyPluginAsync = async (fastify) => { | ||||
|       const membersRequest = await prisma.member.findMany({ | ||||
|         ...getPaginationOptions(request.query), | ||||
|         where: { | ||||
|           userId: request.user.current.id | ||||
|         } | ||||
|           userId: request.user.current.id, | ||||
|         }, | ||||
|       }) | ||||
|       const guilds = await Promise.all( | ||||
|         membersRequest.map(async (member) => { | ||||
|           const channel = await prisma.channel.findFirst({ | ||||
|             where: { | ||||
|               guildId: member.guildId | ||||
|             } | ||||
|               guildId: member.guildId, | ||||
|             }, | ||||
|           }) | ||||
|           const guild = await prisma.guild.findUnique({ | ||||
|             where: { | ||||
|               id: member.guildId | ||||
|             } | ||||
|               id: member.guildId, | ||||
|             }, | ||||
|           }) | ||||
|           return { | ||||
|             ...guild, | ||||
|             defaultChannelId: channel?.id | ||||
|             defaultChannelId: channel?.id, | ||||
|           } | ||||
|         }) | ||||
|         }), | ||||
|       ) | ||||
|       reply.statusCode = 200 | ||||
|       return guilds | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,17 +1,17 @@ | ||||
| import type { FastifyPluginAsync } from 'fastify' | ||||
| import type { FastifyPluginAsync } from "fastify" | ||||
|  | ||||
| import { getGuilds } from './get.js' | ||||
| import { postGuilds } from './post.js' | ||||
| import { getGuildsPublic } from './public/get.js' | ||||
| import { getChannelsByGuildIdService } from './[guildId]/channels/get.js' | ||||
| import { postChannelService } from './[guildId]/channels/post.js' | ||||
| import { deleteGuildByIdService } from './[guildId]/delete.js' | ||||
| import { getGuildMemberByIdService } from './[guildId]/get.js' | ||||
| import { putGuildIconById } from './[guildId]/icon/put.js' | ||||
| import { getMembersByGuildIdService } from './[guildId]/members/get.js' | ||||
| import { postMemberService } from './[guildId]/members/join/post.js' | ||||
| import { deleteMemberService } from './[guildId]/members/leave/delete.js' | ||||
| import { putGuildByIdService } from './[guildId]/put.js' | ||||
| import { getGuilds } from "./get.js" | ||||
| import { postGuilds } from "./post.js" | ||||
| import { getGuildsPublic } from "./public/get.js" | ||||
| import { getChannelsByGuildIdService } from "./[guildId]/channels/get.js" | ||||
| import { postChannelService } from "./[guildId]/channels/post.js" | ||||
| import { deleteGuildByIdService } from "./[guildId]/delete.js" | ||||
| import { getGuildMemberByIdService } from "./[guildId]/get.js" | ||||
| import { putGuildIconById } from "./[guildId]/icon/put.js" | ||||
| import { getMembersByGuildIdService } from "./[guildId]/members/get.js" | ||||
| import { postMemberService } from "./[guildId]/members/join/post.js" | ||||
| import { deleteMemberService } from "./[guildId]/members/leave/delete.js" | ||||
| import { putGuildByIdService } from "./[guildId]/put.js" | ||||
|  | ||||
| export const guildsService: FastifyPluginAsync = async (fastify) => { | ||||
|   await fastify.register(postGuilds) | ||||
|   | ||||
| @@ -1,30 +1,30 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import { channelSchema } from '#src/models/Channel.js' | ||||
| import { memberSchema } from '#src/models/Member.js' | ||||
| import { userPublicWithoutSettingsSchema } from '#src/models/User.js' | ||||
| import { parseStringNullish } from '#src/tools/utils/parseStringNullish.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
| import { channelSchema } from "#src/models/Channel.js" | ||||
| import { memberSchema } from "#src/models/Member.js" | ||||
| import { userPublicWithoutSettingsSchema } from "#src/models/User.js" | ||||
| import { parseStringNullish } from "#src/tools/utils/parseStringNullish.js" | ||||
|  | ||||
| const bodyPostServiceSchema = Type.Object({ | ||||
|   name: guildSchema.name, | ||||
|   description: guildSchema.description | ||||
|   description: guildSchema.description, | ||||
| }) | ||||
|  | ||||
| type BodyPostServiceSchemaType = Static<typeof bodyPostServiceSchema> | ||||
|  | ||||
| const postServiceSchema: FastifySchema = { | ||||
|   description: 'Create a guild.', | ||||
|   tags: ['guilds'] as string[], | ||||
|   description: "Create a guild.", | ||||
|   tags: ["guilds"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   body: bodyPostServiceSchema, | ||||
|   response: { | ||||
| @@ -35,16 +35,16 @@ const postServiceSchema: FastifySchema = { | ||||
|         members: Type.Array( | ||||
|           Type.Object({ | ||||
|             ...memberSchema, | ||||
|             user: Type.Object(userPublicWithoutSettingsSchema) | ||||
|           }) | ||||
|         ) | ||||
|       }) | ||||
|             user: Type.Object(userPublicWithoutSettingsSchema), | ||||
|           }), | ||||
|         ), | ||||
|       }), | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const postGuilds: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -53,8 +53,8 @@ export const postGuilds: FastifyPluginAsync = async (fastify) => { | ||||
|   fastify.route<{ | ||||
|     Body: BodyPostServiceSchemaType | ||||
|   }>({ | ||||
|     method: 'POST', | ||||
|     url: '/guilds', | ||||
|     method: "POST", | ||||
|     url: "/guilds", | ||||
|     schema: postServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -62,37 +62,37 @@ export const postGuilds: FastifyPluginAsync = async (fastify) => { | ||||
|       } | ||||
|       const { name, description } = request.body | ||||
|       const guild = await prisma.guild.create({ | ||||
|         data: { name, description: parseStringNullish(description) } | ||||
|         data: { name, description: parseStringNullish(description) }, | ||||
|       }) | ||||
|       const channel = await prisma.channel.create({ | ||||
|         data: { name: 'general', guildId: guild.id } | ||||
|         data: { name: "general", guildId: guild.id }, | ||||
|       }) | ||||
|       const memberCreated = await prisma.member.create({ | ||||
|         data: { | ||||
|           userId: request.user.current.id, | ||||
|           isOwner: true, | ||||
|           guildId: guild.id | ||||
|         } | ||||
|           guildId: guild.id, | ||||
|         }, | ||||
|       }) | ||||
|       const members = await Promise.all( | ||||
|         [memberCreated].map(async (member) => { | ||||
|           const user = await prisma.user.findUnique({ | ||||
|             where: { id: member?.userId } | ||||
|             where: { id: member?.userId }, | ||||
|           }) | ||||
|           return { | ||||
|             ...member, | ||||
|             user | ||||
|             user, | ||||
|           } | ||||
|         }) | ||||
|         }), | ||||
|       ) | ||||
|       reply.statusCode = 201 | ||||
|       return { | ||||
|         guild: { | ||||
|           ...guild, | ||||
|           channels: [channel], | ||||
|           members | ||||
|         } | ||||
|           members, | ||||
|         }, | ||||
|       } | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,36 +1,36 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { guildExample } from '#src/models/Guild.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { guildExample } from "#src/models/Guild.js" | ||||
|  | ||||
| await test('GET /guilds/public', async (t) => { | ||||
| await test("GET /guilds/public", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'guild').value({ | ||||
|     sinon.stub(prisma, "guild").value({ | ||||
|       findMany: async () => { | ||||
|         return [guildExample] | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       count: async () => { | ||||
|         return 2 | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/guilds/public', | ||||
|       method: "GET", | ||||
|       url: "/guilds/public", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
|   | ||||
| @@ -1,45 +1,45 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
| import { | ||||
|   getPaginationOptions, | ||||
|   queryPaginationSchema | ||||
| } from '#src/tools/database/pagination.js' | ||||
|   queryPaginationSchema, | ||||
| } from "#src/tools/database/pagination.js" | ||||
|  | ||||
| const querySchema = Type.Object({ | ||||
|   search: Type.Optional(Type.String()), | ||||
|   ...queryPaginationSchema | ||||
|   ...queryPaginationSchema, | ||||
| }) | ||||
|  | ||||
| export type QuerySchemaType = Static<typeof querySchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: | ||||
|     'GET all the public guilds (ordered by descending members count).', | ||||
|   tags: ['guilds'] as string[], | ||||
|     "GET all the public guilds (ordered by descending members count).", | ||||
|   tags: ["guilds"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   querystring: querySchema, | ||||
|   response: { | ||||
|     200: Type.Array( | ||||
|       Type.Object({ | ||||
|         ...guildSchema, | ||||
|         membersCount: Type.Integer() | ||||
|       }) | ||||
|         membersCount: Type.Integer(), | ||||
|       }), | ||||
|     ), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getGuildsPublic: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -48,8 +48,8 @@ export const getGuildsPublic: FastifyPluginAsync = async (fastify) => { | ||||
|   fastify.route<{ | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/guilds/public', | ||||
|     method: "GET", | ||||
|     url: "/guilds/public", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -59,28 +59,28 @@ export const getGuildsPublic: FastifyPluginAsync = async (fastify) => { | ||||
|         ...getPaginationOptions(request.query), | ||||
|         orderBy: { | ||||
|           members: { | ||||
|             _count: 'desc' | ||||
|           } | ||||
|             _count: "desc", | ||||
|           }, | ||||
|         }, | ||||
|         ...(request.query.search != null && { | ||||
|           where: { | ||||
|             name: { contains: request.query.search } | ||||
|           } | ||||
|         }) | ||||
|             name: { contains: request.query.search }, | ||||
|           }, | ||||
|         }), | ||||
|       }) | ||||
|       const guilds = await Promise.all( | ||||
|         guildsRequest.map(async (guild) => { | ||||
|           const membersCount = await prisma.member.count({ | ||||
|             where: { guildId: guild.id } | ||||
|             where: { guildId: guild.id }, | ||||
|           }) | ||||
|           return { | ||||
|             ...guild, | ||||
|             membersCount | ||||
|             membersCount, | ||||
|           } | ||||
|         }) | ||||
|         }), | ||||
|       ) | ||||
|       reply.statusCode = 200 | ||||
|       return guilds | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import type { FastifyPluginAsync } from 'fastify' | ||||
| import type { FastifyPluginAsync } from "fastify" | ||||
|  | ||||
| import { usersService } from './users/index.js' | ||||
| import { guildsService } from './guilds/index.js' | ||||
| import { channelsService } from './channels/index.js' | ||||
| import { messagesService } from './messages/index.js' | ||||
| import { usersService } from "./users/index.js" | ||||
| import { guildsService } from "./guilds/index.js" | ||||
| import { channelsService } from "./channels/index.js" | ||||
| import { messagesService } from "./messages/index.js" | ||||
|  | ||||
| export const services: FastifyPluginAsync = async (fastify) => { | ||||
|   await fastify.register(channelsService) | ||||
|   | ||||
| @@ -1,48 +1,48 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { messageExample } from '#src/models/Message.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { messageExample } from "#src/models/Message.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { userExample } from "#src/models/User.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
|  | ||||
| await test('DELETE /messsages/[messageId]', async (t) => { | ||||
| await test("DELETE /messsages/[messageId]", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'message').value({ | ||||
|     sinon.stub(prisma, "message").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...messageExample, | ||||
|           channel: channelExample | ||||
|           channel: channelExample, | ||||
|         } | ||||
|       }, | ||||
|       delete: async () => { | ||||
|         return messageExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           user: userExample | ||||
|           user: userExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/messages/${messageExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -56,73 +56,73 @@ await test('DELETE /messsages/[messageId]', async (t) => { | ||||
|     assert.strictEqual(responseJson.member.user.name, userExample.name) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the message is not found', async () => { | ||||
|   await t.test("fails if the message is not found", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'message').value({ | ||||
|     sinon.stub(prisma, "message").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/messages/${messageExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the member is not found', async () => { | ||||
|   await t.test("fails if the member is not found", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'message').value({ | ||||
|     sinon.stub(prisma, "message").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...messageExample, | ||||
|           channel: channelExample | ||||
|           channel: channelExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/messages/${messageExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the member is not owner of the message', async () => { | ||||
|   await t.test("fails if the member is not owner of the message", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     const randomUserIdOwnerOfMessage = 14 | ||||
|     sinon.stub(prisma, 'message').value({ | ||||
|     sinon.stub(prisma, "message").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...messageExample, | ||||
|           channel: channelExample | ||||
|           channel: channelExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           userId: randomUserIdOwnerOfMessage | ||||
|           userId: randomUserIdOwnerOfMessage, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'DELETE', | ||||
|       method: "DELETE", | ||||
|       url: `/messages/${messageExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|   }) | ||||
|   | ||||
| @@ -1,53 +1,53 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { messageExample } from '#src/models/Message.js' | ||||
| import { memberExample } from '#src/models/Member.js' | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { channelExample } from '#src/models/Channel.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { messageExample } from "#src/models/Message.js" | ||||
| import { memberExample } from "#src/models/Member.js" | ||||
| import { userExample } from "#src/models/User.js" | ||||
| import { channelExample } from "#src/models/Channel.js" | ||||
|  | ||||
| await test('PUT /messsages/[messageId]', async (t) => { | ||||
| await test("PUT /messsages/[messageId]", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     const newValue = 'some message' | ||||
|     sinon.stub(prisma, 'message').value({ | ||||
|     const newValue = "some message" | ||||
|     sinon.stub(prisma, "message").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...messageExample, | ||||
|           channel: channelExample | ||||
|           channel: channelExample, | ||||
|         } | ||||
|       }, | ||||
|       update: async () => { | ||||
|         return { | ||||
|           ...messageExample, | ||||
|           value: newValue | ||||
|           value: newValue, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...memberExample, | ||||
|           user: userExample | ||||
|           user: userExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       method: "PUT", | ||||
|       url: `/messages/${messageExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { value: newValue } | ||||
|       payload: { value: newValue }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -61,83 +61,83 @@ await test('PUT /messsages/[messageId]', async (t) => { | ||||
|     assert.strictEqual(responseJson.member.user.name, userExample.name) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the message is not found', async () => { | ||||
|   await t.test("fails if the message is not found", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     const newValue = 'some message' | ||||
|     sinon.stub(prisma, 'message').value({ | ||||
|     const newValue = "some message" | ||||
|     sinon.stub(prisma, "message").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       method: "PUT", | ||||
|       url: `/messages/${messageExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { value: newValue } | ||||
|       payload: { value: newValue }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails if the member is not found', async () => { | ||||
|   await t.test("fails if the member is not found", async () => { | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     const newValue = 'some message' | ||||
|     sinon.stub(prisma, 'message').value({ | ||||
|     const newValue = "some message" | ||||
|     sinon.stub(prisma, "message").value({ | ||||
|       findFirst: async () => { | ||||
|         return { | ||||
|           ...messageExample, | ||||
|           channel: channelExample | ||||
|           channel: channelExample, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'member').value({ | ||||
|     sinon.stub(prisma, "member").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       method: "PUT", | ||||
|       url: `/messages/${messageExample.id}`, | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { value: newValue } | ||||
|       payload: { value: newValue }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|   }) | ||||
|  | ||||
|   await t.test( | ||||
|     'fails if the member is not the owner of the message', | ||||
|     "fails if the member is not the owner of the message", | ||||
|     async () => { | ||||
|       const { accessToken } = await authenticateUserTest() | ||||
|       const newValue = 'some message' | ||||
|       const newValue = "some message" | ||||
|       const randomUserIdOwnerOfMessage = 14 | ||||
|       sinon.stub(prisma, 'message').value({ | ||||
|       sinon.stub(prisma, "message").value({ | ||||
|         findFirst: async () => { | ||||
|           return { | ||||
|             ...messageExample, | ||||
|             channel: channelExample | ||||
|             channel: channelExample, | ||||
|           } | ||||
|         } | ||||
|         }, | ||||
|       }) | ||||
|       sinon.stub(prisma, 'member').value({ | ||||
|       sinon.stub(prisma, "member").value({ | ||||
|         findFirst: async () => { | ||||
|           return { | ||||
|             ...memberExample, | ||||
|             userId: randomUserIdOwnerOfMessage | ||||
|             userId: randomUserIdOwnerOfMessage, | ||||
|           } | ||||
|         } | ||||
|         }, | ||||
|       }) | ||||
|       const response = await application.inject({ | ||||
|         method: 'PUT', | ||||
|         method: "PUT", | ||||
|         url: `/messages/${messageExample.id}`, | ||||
|         headers: { | ||||
|           authorization: `Bearer ${accessToken}` | ||||
|           authorization: `Bearer ${accessToken}`, | ||||
|         }, | ||||
|         payload: { value: newValue } | ||||
|         payload: { value: newValue }, | ||||
|       }) | ||||
|       assert.strictEqual(response.statusCode, 400) | ||||
|     } | ||||
|     }, | ||||
|   ) | ||||
| }) | ||||
|   | ||||
| @@ -1,27 +1,27 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { messageSchema } from '#src/models/Message.js' | ||||
| import { memberSchema } from '#src/models/Member.js' | ||||
| import { userPublicWithoutSettingsSchema } from '#src/models/User.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { messageSchema } from "#src/models/Message.js" | ||||
| import { memberSchema } from "#src/models/Member.js" | ||||
| import { userPublicWithoutSettingsSchema } from "#src/models/User.js" | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   messageId: messageSchema.id | ||||
|   messageId: messageSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const putServiceSchema: FastifySchema = { | ||||
|   description: 'UPDATE a message with its id.', | ||||
|   tags: ['messages'] as string[], | ||||
|   description: "UPDATE a message with its id.", | ||||
|   tags: ["messages"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   response: { | ||||
| @@ -29,15 +29,15 @@ const putServiceSchema: FastifySchema = { | ||||
|       ...messageSchema, | ||||
|       member: Type.Object({ | ||||
|         ...memberSchema, | ||||
|         user: Type.Object(userPublicWithoutSettingsSchema) | ||||
|       }) | ||||
|         user: Type.Object(userPublicWithoutSettingsSchema), | ||||
|       }), | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const deleteMessageService: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -46,8 +46,8 @@ export const deleteMessageService: FastifyPluginAsync = async (fastify) => { | ||||
|   fastify.route<{ | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'DELETE', | ||||
|     url: '/messages/:messageId', | ||||
|     method: "DELETE", | ||||
|     url: "/messages/:messageId", | ||||
|     schema: putServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -58,16 +58,16 @@ export const deleteMessageService: FastifyPluginAsync = async (fastify) => { | ||||
|       const messageCheck = await prisma.message.findFirst({ | ||||
|         where: { id: messageId }, | ||||
|         include: { | ||||
|           channel: true | ||||
|         } | ||||
|           channel: true, | ||||
|         }, | ||||
|       }) | ||||
|       if (messageCheck == null || messageCheck.channel == null) { | ||||
|         throw fastify.httpErrors.notFound('Message not found') | ||||
|         throw fastify.httpErrors.notFound("Message not found") | ||||
|       } | ||||
|       const member = await prisma.member.findFirst({ | ||||
|         where: { | ||||
|           guildId: messageCheck.channel.guildId, | ||||
|           userId: user.current.id | ||||
|           userId: user.current.id, | ||||
|         }, | ||||
|         include: { | ||||
|           user: { | ||||
| @@ -79,23 +79,23 @@ export const deleteMessageService: FastifyPluginAsync = async (fastify) => { | ||||
|               biography: true, | ||||
|               website: true, | ||||
|               createdAt: true, | ||||
|               updatedAt: true | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|               updatedAt: true, | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|       }) | ||||
|       if (member == null) { | ||||
|         throw fastify.httpErrors.notFound('Member not found') | ||||
|         throw fastify.httpErrors.notFound("Member not found") | ||||
|       } | ||||
|       if (member.userId !== user.current.id) { | ||||
|         throw fastify.httpErrors.badRequest( | ||||
|           'You should be the owner of the message' | ||||
|           "You should be the owner of the message", | ||||
|         ) | ||||
|       } | ||||
|       const message = await prisma.message.delete({ | ||||
|         where: { | ||||
|           id: messageCheck.id | ||||
|         } | ||||
|           id: messageCheck.id, | ||||
|         }, | ||||
|       }) | ||||
|       const item = { | ||||
|         ...message, | ||||
| @@ -103,17 +103,17 @@ export const deleteMessageService: FastifyPluginAsync = async (fastify) => { | ||||
|           ...member, | ||||
|           user: { | ||||
|             ...member.user, | ||||
|             email: null | ||||
|           } | ||||
|         } | ||||
|             email: null, | ||||
|           }, | ||||
|         }, | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'messages', | ||||
|         event: "messages", | ||||
|         guildId: item.member.guildId, | ||||
|         payload: { action: 'delete', item } | ||||
|         payload: { action: "delete", item }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return item | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,33 +1,33 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { messageSchema } from '#src/models/Message.js' | ||||
| import { memberSchema } from '#src/models/Member.js' | ||||
| import { userPublicWithoutSettingsSchema } from '#src/models/User.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { messageSchema } from "#src/models/Message.js" | ||||
| import { memberSchema } from "#src/models/Member.js" | ||||
| import { userPublicWithoutSettingsSchema } from "#src/models/User.js" | ||||
|  | ||||
| const bodyPutServiceSchema = Type.Object({ | ||||
|   value: messageSchema.value | ||||
|   value: messageSchema.value, | ||||
| }) | ||||
|  | ||||
| type BodyPutServiceSchemaType = Static<typeof bodyPutServiceSchema> | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   messageId: messageSchema.id | ||||
|   messageId: messageSchema.id, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const putServiceSchema: FastifySchema = { | ||||
|   description: 'UPDATE a message with its id.', | ||||
|   tags: ['messages'] as string[], | ||||
|   description: "UPDATE a message with its id.", | ||||
|   tags: ["messages"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   body: bodyPutServiceSchema, | ||||
|   params: parametersSchema, | ||||
| @@ -36,15 +36,15 @@ const putServiceSchema: FastifySchema = { | ||||
|       ...messageSchema, | ||||
|       member: Type.Object({ | ||||
|         ...memberSchema, | ||||
|         user: Type.Object(userPublicWithoutSettingsSchema) | ||||
|       }) | ||||
|         user: Type.Object(userPublicWithoutSettingsSchema), | ||||
|       }), | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const putMessageService: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -54,8 +54,8 @@ export const putMessageService: FastifyPluginAsync = async (fastify) => { | ||||
|     Body: BodyPutServiceSchemaType | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'PUT', | ||||
|     url: '/messages/:messageId', | ||||
|     method: "PUT", | ||||
|     url: "/messages/:messageId", | ||||
|     schema: putServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -65,18 +65,18 @@ export const putMessageService: FastifyPluginAsync = async (fastify) => { | ||||
|       const { messageId } = params | ||||
|       const { value } = body | ||||
|       const messageCheck = await prisma.message.findFirst({ | ||||
|         where: { id: messageId, type: 'text' }, | ||||
|         where: { id: messageId, type: "text" }, | ||||
|         include: { | ||||
|           channel: true | ||||
|         } | ||||
|           channel: true, | ||||
|         }, | ||||
|       }) | ||||
|       if (messageCheck == null || messageCheck.channel == null) { | ||||
|         throw fastify.httpErrors.notFound('Message not found') | ||||
|         throw fastify.httpErrors.notFound("Message not found") | ||||
|       } | ||||
|       const member = await prisma.member.findFirst({ | ||||
|         where: { | ||||
|           guildId: messageCheck.channel.guildId, | ||||
|           userId: user.current.id | ||||
|           userId: user.current.id, | ||||
|         }, | ||||
|         include: { | ||||
|           user: { | ||||
| @@ -88,26 +88,26 @@ export const putMessageService: FastifyPluginAsync = async (fastify) => { | ||||
|               biography: true, | ||||
|               website: true, | ||||
|               createdAt: true, | ||||
|               updatedAt: true | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|               updatedAt: true, | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|       }) | ||||
|       if (member == null) { | ||||
|         throw fastify.httpErrors.notFound('Member not found') | ||||
|         throw fastify.httpErrors.notFound("Member not found") | ||||
|       } | ||||
|       if (member.userId !== user.current.id) { | ||||
|         throw fastify.httpErrors.badRequest( | ||||
|           'You should be the owner of the message' | ||||
|           "You should be the owner of the message", | ||||
|         ) | ||||
|       } | ||||
|       const message = await prisma.message.update({ | ||||
|         where: { | ||||
|           id: messageCheck.id | ||||
|           id: messageCheck.id, | ||||
|         }, | ||||
|         data: { | ||||
|           value | ||||
|         } | ||||
|           value, | ||||
|         }, | ||||
|       }) | ||||
|       const item = { | ||||
|         ...message, | ||||
| @@ -115,17 +115,17 @@ export const putMessageService: FastifyPluginAsync = async (fastify) => { | ||||
|           ...member, | ||||
|           user: { | ||||
|             ...member.user, | ||||
|             email: null | ||||
|           } | ||||
|         } | ||||
|             email: null, | ||||
|           }, | ||||
|         }, | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'messages', | ||||
|         event: "messages", | ||||
|         guildId: item.member.guildId, | ||||
|         payload: { action: 'update', item } | ||||
|         payload: { action: "update", item }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return item | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import type { FastifyPluginAsync } from 'fastify' | ||||
| import type { FastifyPluginAsync } from "fastify" | ||||
|  | ||||
| import { deleteMessageService } from './[messageId]/delete.js' | ||||
| import { putMessageService } from './[messageId]/put.js' | ||||
| import { deleteMessageService } from "./[messageId]/delete.js" | ||||
| import { putMessageService } from "./[messageId]/put.js" | ||||
|  | ||||
| export const messagesService: FastifyPluginAsync = async (fastify) => { | ||||
|   await fastify.register(putMessageService) | ||||
|   | ||||
| @@ -1,37 +1,37 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { userSettingsExample } from '#src/models/UserSettings.js' | ||||
| import { application } from "#src/application.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { userExample } from "#src/models/User.js" | ||||
| import { userSettingsExample } from "#src/models/UserSettings.js" | ||||
|  | ||||
| await test('GET /users/[userId]', async (t) => { | ||||
| await test("GET /users/[userId]", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|     sinon.stub(prisma, 'guild').value({ | ||||
|   await t.test("succeeds", async () => { | ||||
|     sinon.stub(prisma, "guild").value({ | ||||
|       findMany: async () => { | ||||
|         return [] | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'user').value({ | ||||
|     sinon.stub(prisma, "user").value({ | ||||
|       findUnique: async () => { | ||||
|         return userExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     sinon.stub(prisma, 'userSetting').value({ | ||||
|     sinon.stub(prisma, "userSetting").value({ | ||||
|       findFirst: async () => { | ||||
|         return userSettingsExample | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: `/users/${userExample.id}` | ||||
|       method: "GET", | ||||
|       url: `/users/${userExample.id}`, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -39,18 +39,18 @@ await test('GET /users/[userId]', async (t) => { | ||||
|     assert.strictEqual(responseJson.user.name, userExample.name) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with not found user', async () => { | ||||
|     sinon.stub(prisma, 'userSetting').value({ | ||||
|   await t.test("fails with not found user", async () => { | ||||
|     sinon.stub(prisma, "userSetting").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: `/users/1` | ||||
|       method: "GET", | ||||
|       url: `/users/1`, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 404) | ||||
|     assert.strictEqual(responseJson.message, 'User not found') | ||||
|     assert.strictEqual(responseJson.message, "User not found") | ||||
|   }) | ||||
| }) | ||||
|   | ||||
| @@ -1,51 +1,51 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { userPublicSchema } from '#src/models/User.js' | ||||
| import { guildSchema } from '#src/models/Guild.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { userPublicSchema } from "#src/models/User.js" | ||||
| import { guildSchema } from "#src/models/Guild.js" | ||||
|  | ||||
| const parametersGetUserSchema = Type.Object({ | ||||
|   userId: userPublicSchema.id | ||||
|   userId: userPublicSchema.id, | ||||
| }) | ||||
|  | ||||
| type ParametersGetUser = Static<typeof parametersGetUserSchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'GET the public user informations with its id', | ||||
|   tags: ['users'] as string[], | ||||
|   description: "GET the public user informations with its id", | ||||
|   tags: ["users"] as string[], | ||||
|   params: parametersGetUserSchema, | ||||
|   response: { | ||||
|     200: Type.Object({ | ||||
|       user: Type.Object(userPublicSchema), | ||||
|       guilds: Type.Array(Type.Object(guildSchema)) | ||||
|       guilds: Type.Array(Type.Object(guildSchema)), | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getUserById: FastifyPluginAsync = async (fastify) => { | ||||
|   await fastify.route<{ | ||||
|     Params: ParametersGetUser | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/users/:userId', | ||||
|     method: "GET", | ||||
|     url: "/users/:userId", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       const { userId } = request.params | ||||
|       const settings = await prisma.userSetting.findFirst({ | ||||
|         where: { userId } | ||||
|         where: { userId }, | ||||
|       }) | ||||
|       if (settings == null) { | ||||
|         throw fastify.httpErrors.notFound('User not found') | ||||
|         throw fastify.httpErrors.notFound("User not found") | ||||
|       } | ||||
|       const user = await prisma.user.findUnique({ | ||||
|         where: { | ||||
|           id: userId | ||||
|           id: userId, | ||||
|         }, | ||||
|         select: { | ||||
|           id: true, | ||||
| @@ -57,18 +57,18 @@ export const getUserById: FastifyPluginAsync = async (fastify) => { | ||||
|           biography: true, | ||||
|           website: true, | ||||
|           createdAt: true, | ||||
|           updatedAt: true | ||||
|         } | ||||
|           updatedAt: true, | ||||
|         }, | ||||
|       }) | ||||
|       if (user == null) { | ||||
|         throw fastify.httpErrors.notFound('User not found') | ||||
|         throw fastify.httpErrors.notFound("User not found") | ||||
|       } | ||||
|       reply.statusCode = 200 | ||||
|       return { | ||||
|         user: { | ||||
|           ...user, | ||||
|           email: user.email ?? null, | ||||
|           settings | ||||
|           settings, | ||||
|         }, | ||||
|         guilds: !settings.isPublicGuilds | ||||
|           ? [] | ||||
| @@ -77,12 +77,12 @@ export const getUserById: FastifyPluginAsync = async (fastify) => { | ||||
|               where: { | ||||
|                 members: { | ||||
|                   some: { | ||||
|                     userId | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             }) | ||||
|                     userId, | ||||
|                   }, | ||||
|                 }, | ||||
|               }, | ||||
|             }), | ||||
|       } | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,51 +1,51 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { userExample } from '#src/models/User.js' | ||||
| import { application } from "#src/application.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { userExample } from "#src/models/User.js" | ||||
|  | ||||
| await test('GET /users/confirm-email', async (t) => { | ||||
| await test("GET /users/confirm-email", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|     sinon.stub(prisma, 'user').value({ | ||||
|   await t.test("succeeds", async () => { | ||||
|     sinon.stub(prisma, "user").value({ | ||||
|       findFirst: async () => { | ||||
|         return userExample | ||||
|       }, | ||||
|       update: async () => { | ||||
|         return { ...userExample, isConfirmed: true, temporaryToken: null } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/users/confirm-email', | ||||
|       method: "GET", | ||||
|       url: "/users/confirm-email", | ||||
|       query: { | ||||
|         temporaryToken: userExample.temporaryToken ?? '' | ||||
|       } | ||||
|         temporaryToken: userExample.temporaryToken ?? "", | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
|   }) | ||||
|  | ||||
|   await t.test('should fails with invalid `temporaryToken`', async () => { | ||||
|     sinon.stub(prisma, 'user').value({ | ||||
|   await t.test("should fails with invalid `temporaryToken`", async () => { | ||||
|     sinon.stub(prisma, "user").value({ | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
|       }, | ||||
|       update: async () => { | ||||
|         return { ...userExample, isConfirmed: true, temporaryToken: null } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/users/confirm-email', | ||||
|       method: "GET", | ||||
|       url: "/users/confirm-email", | ||||
|       query: { | ||||
|         temporaryToken: userExample.temporaryToken ?? '' | ||||
|       } | ||||
|         temporaryToken: userExample.temporaryToken ?? "", | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 403) | ||||
|   }) | ||||
|   | ||||
| @@ -1,44 +1,44 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { userSchema } from '#src/models/User.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { userSchema } from "#src/models/User.js" | ||||
|  | ||||
| const queryGetConfirmEmailSchema = Type.Object({ | ||||
|   redirectURI: Type.Optional(Type.String({ format: 'uri-reference' })), | ||||
|   temporaryToken: userSchema.temporaryToken | ||||
|   redirectURI: Type.Optional(Type.String({ format: "uri-reference" })), | ||||
|   temporaryToken: userSchema.temporaryToken, | ||||
| }) | ||||
|  | ||||
| type QueryGetConfirmEmailSchemaType = Static<typeof queryGetConfirmEmailSchema> | ||||
|  | ||||
| const getConfirmEmailSchema: FastifySchema = { | ||||
|   description: 'Confirm the account of the user.', | ||||
|   tags: ['users'] as string[], | ||||
|   description: "Confirm the account of the user.", | ||||
|   tags: ["users"] as string[], | ||||
|   querystring: queryGetConfirmEmailSchema, | ||||
|   response: { | ||||
|     200: Type.String(), | ||||
|     400: fastifyErrors[400], | ||||
|     403: fastifyErrors[403], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getConfirmEmail: FastifyPluginAsync = async (fastify) => { | ||||
|   await fastify.route<{ | ||||
|     Querystring: QueryGetConfirmEmailSchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/users/confirm-email', | ||||
|     method: "GET", | ||||
|     url: "/users/confirm-email", | ||||
|     schema: getConfirmEmailSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       const { redirectURI, temporaryToken } = request.query | ||||
|       const user = await prisma.user.findFirst({ | ||||
|         where: { | ||||
|           temporaryToken, | ||||
|           isConfirmed: false | ||||
|         } | ||||
|           isConfirmed: false, | ||||
|         }, | ||||
|       }) | ||||
|       if (user == null) { | ||||
|         throw fastify.httpErrors.forbidden() | ||||
| @@ -47,14 +47,14 @@ export const getConfirmEmail: FastifyPluginAsync = async (fastify) => { | ||||
|         where: { id: user.id }, | ||||
|         data: { | ||||
|           temporaryToken: null, | ||||
|           isConfirmed: true | ||||
|         } | ||||
|           isConfirmed: true, | ||||
|         }, | ||||
|       }) | ||||
|       if (redirectURI == null) { | ||||
|         reply.statusCode = 200 | ||||
|         return 'Success, your email has been confirmed, you can now signin!' | ||||
|         return "Success, your email has been confirmed, you can now signin!" | ||||
|       } | ||||
|       return await reply.redirect(redirectURI) | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,35 +1,35 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
|  | ||||
| await test('GET /users/current', async (t) => { | ||||
| await test("GET /users/current", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds', async () => { | ||||
|   await t.test("succeeds", async () => { | ||||
|     const { accessToken, user } = await authenticateUserTest() | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/users/current', | ||||
|       method: "GET", | ||||
|       url: "/users/current", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|       } | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
|     assert.strictEqual(responseJson.user.name, user.name) | ||||
|     assert.deepStrictEqual(responseJson.user.strategies, ['Local']) | ||||
|     assert.deepStrictEqual(responseJson.user.strategies, ["Local"]) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with unauthenticated user', async () => { | ||||
|   await t.test("fails with unauthenticated user", async () => { | ||||
|     const response = await application.inject({ | ||||
|       method: 'GET', | ||||
|       url: '/users/current' | ||||
|       method: "GET", | ||||
|       url: "/users/current", | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 401) | ||||
|   }) | ||||
|   | ||||
| @@ -1,21 +1,21 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import { application } from "#src/application.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
|  | ||||
| await test('PUT /users/current', async (t) => { | ||||
| await test("PUT /users/current", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds with valid accessToken and valid name', async () => { | ||||
|     const newName = 'John Doe' | ||||
|   await t.test("succeeds with valid accessToken and valid name", async () => { | ||||
|     const newName = "John Doe" | ||||
|     const { accessToken, user, userStubValue } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'user').value({ | ||||
|     sinon.stub(prisma, "user").value({ | ||||
|       ...userStubValue, | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
| @@ -23,29 +23,29 @@ await test('PUT /users/current', async (t) => { | ||||
|       update: async () => { | ||||
|         return { | ||||
|           ...user, | ||||
|           name: newName | ||||
|           name: newName, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       url: '/users/current', | ||||
|       method: "PUT", | ||||
|       url: "/users/current", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { | ||||
|         name: newName | ||||
|       } | ||||
|         name: newName, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
|     assert.strictEqual(responseJson.user.name, newName) | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds and only update the status', async () => { | ||||
|     const newStatus = '👀 Working on secret projects...' | ||||
|   await t.test("succeeds and only update the status", async () => { | ||||
|     const newStatus = "👀 Working on secret projects..." | ||||
|     const { accessToken, user, userStubValue } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'user').value({ | ||||
|     sinon.stub(prisma, "user").value({ | ||||
|       ...userStubValue, | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
| @@ -53,19 +53,19 @@ await test('PUT /users/current', async (t) => { | ||||
|       update: async () => { | ||||
|         return { | ||||
|           ...user, | ||||
|           status: newStatus | ||||
|           status: newStatus, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       url: '/users/current', | ||||
|       method: "PUT", | ||||
|       url: "/users/current", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { | ||||
|         status: newStatus | ||||
|       } | ||||
|         status: newStatus, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
| @@ -73,48 +73,48 @@ await test('PUT /users/current', async (t) => { | ||||
|     assert.strictEqual(responseJson.user.status, newStatus) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with name already used', async () => { | ||||
|     const newName = 'John Doe' | ||||
|   await t.test("fails with name already used", async () => { | ||||
|     const newName = "John Doe" | ||||
|     const { accessToken, user, userStubValue } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'user').value({ | ||||
|     sinon.stub(prisma, "user").value({ | ||||
|       ...userStubValue, | ||||
|       findFirst: async () => { | ||||
|         return user | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       url: '/users/current', | ||||
|       method: "PUT", | ||||
|       url: "/users/current", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { | ||||
|         name: newName | ||||
|       } | ||||
|         name: newName, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|   }) | ||||
|  | ||||
|   await t.test('fails with invalid website url', async () => { | ||||
|     const newWebsite = 'invalid website url' | ||||
|   await t.test("fails with invalid website url", async () => { | ||||
|     const newWebsite = "invalid website url" | ||||
|     const { accessToken } = await authenticateUserTest() | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       url: '/users/current', | ||||
|       method: "PUT", | ||||
|       url: "/users/current", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { | ||||
|         website: newWebsite | ||||
|       } | ||||
|         website: newWebsite, | ||||
|       }, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|   }) | ||||
|  | ||||
|   await t.test('succeeds with valid website url', async () => { | ||||
|     const newWebsite = 'https://somerandomwebsite.com' | ||||
|   await t.test("succeeds with valid website url", async () => { | ||||
|     const newWebsite = "https://somerandomwebsite.com" | ||||
|     const { accessToken, user, userStubValue } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'user').value({ | ||||
|     sinon.stub(prisma, "user").value({ | ||||
|       ...userStubValue, | ||||
|       findFirst: async () => { | ||||
|         return null | ||||
| @@ -122,19 +122,19 @@ await test('PUT /users/current', async (t) => { | ||||
|       update: async () => { | ||||
|         return { | ||||
|           ...user, | ||||
|           website: newWebsite | ||||
|           website: newWebsite, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       url: '/users/current', | ||||
|       method: "PUT", | ||||
|       url: "/users/current", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: { | ||||
|         website: newWebsite | ||||
|       } | ||||
|         website: newWebsite, | ||||
|       }, | ||||
|     }) | ||||
|     const responseJson = response.json() | ||||
|     assert.strictEqual(response.statusCode, 200) | ||||
|   | ||||
| @@ -1,33 +1,33 @@ | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { userCurrentSchema } from '#src/models/User.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { userCurrentSchema } from "#src/models/User.js" | ||||
|  | ||||
| const getCurrentUserSchema: FastifySchema = { | ||||
|   description: 'GET the current connected user', | ||||
|   tags: ['users'] as string[], | ||||
|   description: "GET the current connected user", | ||||
|   tags: ["users"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   response: { | ||||
|     200: userCurrentSchema, | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getCurrentUser: FastifyPluginAsync = async (fastify) => { | ||||
|   await fastify.register(authenticateUser) | ||||
|  | ||||
|   fastify.route({ | ||||
|     method: 'GET', | ||||
|     url: '/users/current', | ||||
|     method: "GET", | ||||
|     url: "/users/current", | ||||
|     schema: getCurrentUserSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -35,16 +35,16 @@ export const getCurrentUser: FastifyPluginAsync = async (fastify) => { | ||||
|       } | ||||
|       const { user } = request | ||||
|       const settings = await prisma.userSetting.findFirst({ | ||||
|         where: { userId: user.current.id } | ||||
|         where: { userId: user.current.id }, | ||||
|       }) | ||||
|       const OAuths = await prisma.oAuth.findMany({ | ||||
|         where: { userId: user.current.id } | ||||
|         where: { userId: user.current.id }, | ||||
|       }) | ||||
|       const strategies = OAuths.map((oauth) => { | ||||
|         return oauth.provider | ||||
|       }) | ||||
|       if (user.current.password != null) { | ||||
|         strategies.push('Local') | ||||
|         strategies.push("Local") | ||||
|       } | ||||
|       reply.statusCode = 200 | ||||
|       return { | ||||
| @@ -52,9 +52,9 @@ export const getCurrentUser: FastifyPluginAsync = async (fastify) => { | ||||
|           ...user.current, | ||||
|           settings, | ||||
|           currentStrategy: user.currentStrategy, | ||||
|           strategies | ||||
|         } | ||||
|           strategies, | ||||
|         }, | ||||
|       } | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,34 +1,34 @@ | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import fastifyMultipart from '@fastify/multipart' | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
| import fastifyMultipart from "@fastify/multipart" | ||||
|  | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { uploadFile } from '#src/tools/utils/uploadFile.js' | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { uploadFile } from "#src/tools/utils/uploadFile.js" | ||||
|  | ||||
| const putServiceSchema: FastifySchema = { | ||||
|   description: 'Edit the current connected user logo', | ||||
|   tags: ['users'] as string[], | ||||
|   consumes: ['multipart/form-data'] as string[], | ||||
|   produces: ['application/json'] as string[], | ||||
|   description: "Edit the current connected user logo", | ||||
|   tags: ["users"] as string[], | ||||
|   consumes: ["multipart/form-data"] as string[], | ||||
|   produces: ["application/json"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   response: { | ||||
|     200: Type.Object({ | ||||
|       user: Type.Object({ | ||||
|         logo: Type.String() | ||||
|       }) | ||||
|         logo: Type.String(), | ||||
|       }), | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     431: fastifyErrors[431], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const putCurrentUserLogo: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -37,8 +37,8 @@ export const putCurrentUserLogo: FastifyPluginAsync = async (fastify) => { | ||||
|   await fastify.register(fastifyMultipart) | ||||
|  | ||||
|   fastify.route({ | ||||
|     method: 'PUT', | ||||
|     url: '/users/current/logo', | ||||
|     method: "PUT", | ||||
|     url: "/users/current/logo", | ||||
|     schema: putServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -47,18 +47,18 @@ export const putCurrentUserLogo: FastifyPluginAsync = async (fastify) => { | ||||
|       const file = await uploadFile({ | ||||
|         fastify, | ||||
|         request, | ||||
|         folderInUploadsFolder: 'users' | ||||
|         folderInUploadsFolder: "users", | ||||
|       }) | ||||
|       await prisma.user.update({ | ||||
|         where: { id: request.user.current.id }, | ||||
|         data: { logo: file.pathToStoreInDatabase } | ||||
|         data: { logo: file.pathToStoreInDatabase }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return { | ||||
|         user: { | ||||
|           logo: file.pathToStoreInDatabase | ||||
|         } | ||||
|           logo: file.pathToStoreInDatabase, | ||||
|         }, | ||||
|       } | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,41 +1,41 @@ | ||||
| import { randomUUID } from 'node:crypto' | ||||
| import { randomUUID } from "node:crypto" | ||||
|  | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { userCurrentSchema, userSchema } from '#src/models/User.js' | ||||
| import { sendEmail } from '#src/tools/email/sendEmail.js' | ||||
| import { API_URL } from '#src/tools/configurations.js' | ||||
| import type { Language, Theme } from '#src/models/UserSettings.js' | ||||
| import { parseStringNullish } from '#src/tools/utils/parseStringNullish.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { userCurrentSchema, userSchema } from "#src/models/User.js" | ||||
| import { sendEmail } from "#src/tools/email/sendEmail.js" | ||||
| import { API_URL } from "#src/tools/configurations.js" | ||||
| import type { Language, Theme } from "#src/models/UserSettings.js" | ||||
| import { parseStringNullish } from "#src/tools/utils/parseStringNullish.js" | ||||
|  | ||||
| const bodyPutServiceSchema = Type.Object({ | ||||
|   name: Type.Optional(userSchema.name), | ||||
|   email: Type.Optional(Type.Union([userSchema.email, Type.Null()])), | ||||
|   status: Type.Optional(Type.Union([userSchema.status, Type.Null()])), | ||||
|   biography: Type.Optional(Type.Union([userSchema.biography, Type.Null()])), | ||||
|   website: Type.Optional(Type.Union([userSchema.website, Type.Null()])) | ||||
|   website: Type.Optional(Type.Union([userSchema.website, Type.Null()])), | ||||
| }) | ||||
|  | ||||
| type BodyPutServiceSchemaType = Static<typeof bodyPutServiceSchema> | ||||
|  | ||||
| const queryPutCurrentUserSchema = Type.Object({ | ||||
|   redirectURI: Type.Optional(Type.String({ format: 'uri-reference' })) | ||||
|   redirectURI: Type.Optional(Type.String({ format: "uri-reference" })), | ||||
| }) | ||||
|  | ||||
| type QueryPutCurrentUserSchemaType = Static<typeof queryPutCurrentUserSchema> | ||||
|  | ||||
| const putServiceSchema: FastifySchema = { | ||||
|   description: 'Edit the current connected user information', | ||||
|   tags: ['users'] as string[], | ||||
|   description: "Edit the current connected user information", | ||||
|   tags: ["users"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   body: bodyPutServiceSchema, | ||||
|   querystring: queryPutCurrentUserSchema, | ||||
| @@ -44,8 +44,8 @@ const putServiceSchema: FastifySchema = { | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const putCurrentUser: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -55,8 +55,8 @@ export const putCurrentUser: FastifyPluginAsync = async (fastify) => { | ||||
|     Body: BodyPutServiceSchemaType | ||||
|     Querystring: QueryPutCurrentUserSchemaType | ||||
|   }>({ | ||||
|     method: 'PUT', | ||||
|     url: '/users/current', | ||||
|     method: "PUT", | ||||
|     url: "/users/current", | ||||
|     schema: putServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -68,59 +68,59 @@ export const putCurrentUser: FastifyPluginAsync = async (fastify) => { | ||||
|         where: { | ||||
|           OR: [ | ||||
|             ...(email != null ? [{ email }] : [{}]), | ||||
|             ...(name != null ? [{ name }] : [{}]) | ||||
|             ...(name != null ? [{ name }] : [{}]), | ||||
|           ], | ||||
|           AND: [{ id: { not: request.user.current.id } }] | ||||
|         } | ||||
|           AND: [{ id: { not: request.user.current.id } }], | ||||
|         }, | ||||
|       }) | ||||
|       if (userValidation != null) { | ||||
|         throw fastify.httpErrors.badRequest( | ||||
|           'body.email or body.name already taken.' | ||||
|           "body.email or body.name already taken.", | ||||
|         ) | ||||
|       } | ||||
|       const settings = await prisma.userSetting.findFirst({ | ||||
|         where: { userId: request.user.current.id } | ||||
|         where: { userId: request.user.current.id }, | ||||
|       }) | ||||
|       if (settings == null) { | ||||
|         throw fastify.httpErrors.internalServerError() | ||||
|       } | ||||
|       const OAuths = await prisma.oAuth.findMany({ | ||||
|         where: { userId: request.user.current.id } | ||||
|         where: { userId: request.user.current.id }, | ||||
|       }) | ||||
|       const strategies = OAuths.map((oauth) => { | ||||
|         return oauth.provider | ||||
|       }) | ||||
|       if (request.user.current.password != null) { | ||||
|         strategies.push('Local') | ||||
|         strategies.push("Local") | ||||
|       } | ||||
|       if (email === null && strategies.includes('Local')) { | ||||
|       if (email === null && strategies.includes("Local")) { | ||||
|         throw fastify.httpErrors.badRequest( | ||||
|           'You must have an email to sign in.' | ||||
|           "You must have an email to sign in.", | ||||
|         ) | ||||
|       } | ||||
|       if (email != null && email !== request.user.current.email) { | ||||
|         await prisma.refreshToken.deleteMany({ | ||||
|           where: { | ||||
|             userId: request.user.current.id | ||||
|           } | ||||
|             userId: request.user.current.id, | ||||
|           }, | ||||
|         }) | ||||
|         const temporaryToken = randomUUID() | ||||
|         const redirectQuery = | ||||
|           redirectURI != null ? `&redirectURI=${redirectURI}` : '' | ||||
|           redirectURI != null ? `&redirectURI=${redirectURI}` : "" | ||||
|         await sendEmail({ | ||||
|           type: 'confirm-email', | ||||
|           type: "confirm-email", | ||||
|           email, | ||||
|           url: `${API_URL}/users/confirm-email?temporaryToken=${temporaryToken}${redirectQuery}`, | ||||
|           language: settings.language as Language, | ||||
|           theme: settings.theme as Theme | ||||
|           theme: settings.theme as Theme, | ||||
|         }) | ||||
|         await prisma.user.update({ | ||||
|           where: { id: request.user.current.id }, | ||||
|           data: { | ||||
|             email, | ||||
|             temporaryToken, | ||||
|             isConfirmed: false | ||||
|           } | ||||
|             isConfirmed: false, | ||||
|           }, | ||||
|         }) | ||||
|       } | ||||
|       const user = await prisma.user.update({ | ||||
| @@ -130,20 +130,20 @@ export const putCurrentUser: FastifyPluginAsync = async (fastify) => { | ||||
|           status: parseStringNullish(request.user.current.status, status), | ||||
|           biography: parseStringNullish( | ||||
|             request.user.current.biography, | ||||
|             biography | ||||
|             biography, | ||||
|           ), | ||||
|           website: parseStringNullish(request.user.current.website, website) | ||||
|         } | ||||
|           website: parseStringNullish(request.user.current.website, website), | ||||
|         }, | ||||
|       }) | ||||
|       await fastify.io.emitToAuthorizedUsers({ | ||||
|         event: 'users', | ||||
|         event: "users", | ||||
|         isAuthorizedCallback: () => { | ||||
|           return true | ||||
|         }, | ||||
|         payload: { | ||||
|           action: 'update', | ||||
|           item: user | ||||
|         } | ||||
|           action: "update", | ||||
|           item: user, | ||||
|         }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return { | ||||
| @@ -151,9 +151,9 @@ export const putCurrentUser: FastifyPluginAsync = async (fastify) => { | ||||
|           ...user, | ||||
|           settings, | ||||
|           currentStrategy: request.user.currentStrategy, | ||||
|           strategies | ||||
|         } | ||||
|           strategies, | ||||
|         }, | ||||
|       } | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,29 +1,29 @@ | ||||
| import test from 'node:test' | ||||
| import assert from 'node:assert/strict' | ||||
| import test from "node:test" | ||||
| import assert from "node:assert/strict" | ||||
|  | ||||
| import sinon from 'sinon' | ||||
| import sinon from "sinon" | ||||
|  | ||||
| import { application } from '#src/application.js' | ||||
| import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { userSettingsExample } from '#src/models/UserSettings.js' | ||||
| import { application } from "#src/application.js" | ||||
| import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { userSettingsExample } from "#src/models/UserSettings.js" | ||||
|  | ||||
| await test('PUT /users/current/settings', async (t) => { | ||||
| await test("PUT /users/current/settings", async (t) => { | ||||
|   t.afterEach(() => { | ||||
|     sinon.restore() | ||||
|   }) | ||||
|  | ||||
|   await t.test( | ||||
|     'succeeds and edit the theme, language, isPublicEmail and isPublicGuilds', | ||||
|     "succeeds and edit the theme, language, isPublicEmail and isPublicGuilds", | ||||
|     async () => { | ||||
|       const newSettings = { | ||||
|         theme: 'light', | ||||
|         language: 'fr', | ||||
|         theme: "light", | ||||
|         language: "fr", | ||||
|         isPublicEmail: true, | ||||
|         isPublicGuilds: true | ||||
|         isPublicGuilds: true, | ||||
|       } | ||||
|       const { accessToken, userSettingStubValue } = await authenticateUserTest() | ||||
|       sinon.stub(prisma, 'userSetting').value({ | ||||
|       sinon.stub(prisma, "userSetting").value({ | ||||
|         ...userSettingStubValue, | ||||
|         findFirst: async () => { | ||||
|           return userSettingsExample | ||||
| @@ -31,17 +31,17 @@ await test('PUT /users/current/settings', async (t) => { | ||||
|         update: async () => { | ||||
|           return { | ||||
|             ...userSettingsExample, | ||||
|             ...newSettings | ||||
|             ...newSettings, | ||||
|           } | ||||
|         } | ||||
|         }, | ||||
|       }) | ||||
|       const response = await application.inject({ | ||||
|         method: 'PUT', | ||||
|         url: '/users/current/settings', | ||||
|         method: "PUT", | ||||
|         url: "/users/current/settings", | ||||
|         headers: { | ||||
|           authorization: `Bearer ${accessToken}` | ||||
|           authorization: `Bearer ${accessToken}`, | ||||
|         }, | ||||
|         payload: newSettings | ||||
|         payload: newSettings, | ||||
|       }) | ||||
|       const responseJson = response.json() | ||||
|       assert.strictEqual(response.statusCode, 200) | ||||
| @@ -49,21 +49,21 @@ await test('PUT /users/current/settings', async (t) => { | ||||
|       assert.strictEqual(responseJson.settings.language, newSettings.language) | ||||
|       assert.strictEqual( | ||||
|         responseJson.settings.isPublicEmail, | ||||
|         newSettings.isPublicEmail | ||||
|         newSettings.isPublicEmail, | ||||
|       ) | ||||
|       assert.strictEqual( | ||||
|         responseJson.settings.isPublicGuilds, | ||||
|         newSettings.isPublicGuilds | ||||
|         newSettings.isPublicGuilds, | ||||
|       ) | ||||
|     } | ||||
|     }, | ||||
|   ) | ||||
|  | ||||
|   await t.test('fails with invalid language', async () => { | ||||
|   await t.test("fails with invalid language", async () => { | ||||
|     const newSettings = { | ||||
|       language: 'somerandomlanguage' | ||||
|       language: "somerandomlanguage", | ||||
|     } | ||||
|     const { accessToken, userSettingStubValue } = await authenticateUserTest() | ||||
|     sinon.stub(prisma, 'userSetting').value({ | ||||
|     sinon.stub(prisma, "userSetting").value({ | ||||
|       ...userSettingStubValue, | ||||
|       findFirst: async () => { | ||||
|         return userSettingsExample | ||||
| @@ -71,17 +71,17 @@ await test('PUT /users/current/settings', async (t) => { | ||||
|       update: async () => { | ||||
|         return { | ||||
|           ...userSettingsExample, | ||||
|           ...newSettings | ||||
|           ...newSettings, | ||||
|         } | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|     const response = await application.inject({ | ||||
|       method: 'PUT', | ||||
|       url: '/users/current/settings', | ||||
|       method: "PUT", | ||||
|       url: "/users/current/settings", | ||||
|       headers: { | ||||
|         authorization: `Bearer ${accessToken}` | ||||
|         authorization: `Bearer ${accessToken}`, | ||||
|       }, | ||||
|       payload: newSettings | ||||
|       payload: newSettings, | ||||
|     }) | ||||
|     assert.strictEqual(response.statusCode, 400) | ||||
|   }) | ||||
|   | ||||
| @@ -1,39 +1,39 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { userSettingsSchema } from '#src/models/UserSettings.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { userSettingsSchema } from "#src/models/UserSettings.js" | ||||
|  | ||||
| const bodyPutServiceSchema = Type.Object({ | ||||
|   theme: Type.Optional(userSettingsSchema.theme), | ||||
|   language: Type.Optional(userSettingsSchema.language), | ||||
|   isPublicEmail: Type.Optional(userSettingsSchema.isPublicEmail), | ||||
|   isPublicGuilds: Type.Optional(userSettingsSchema.isPublicGuilds) | ||||
|   isPublicGuilds: Type.Optional(userSettingsSchema.isPublicGuilds), | ||||
| }) | ||||
|  | ||||
| type BodyPutServiceSchemaType = Static<typeof bodyPutServiceSchema> | ||||
|  | ||||
| const putServiceSchema: FastifySchema = { | ||||
|   description: 'Edit the current connected user settings', | ||||
|   tags: ['users'] as string[], | ||||
|   description: "Edit the current connected user settings", | ||||
|   tags: ["users"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   body: bodyPutServiceSchema, | ||||
|   response: { | ||||
|     200: Type.Object({ | ||||
|       settings: Type.Object(userSettingsSchema) | ||||
|       settings: Type.Object(userSettingsSchema), | ||||
|     }), | ||||
|     400: fastifyErrors[400], | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const putCurrentUserSettings: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -42,8 +42,8 @@ export const putCurrentUserSettings: FastifyPluginAsync = async (fastify) => { | ||||
|   fastify.route<{ | ||||
|     Body: BodyPutServiceSchemaType | ||||
|   }>({ | ||||
|     method: 'PUT', | ||||
|     url: '/users/current/settings', | ||||
|     method: "PUT", | ||||
|     url: "/users/current/settings", | ||||
|     schema: putServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -51,7 +51,7 @@ export const putCurrentUserSettings: FastifyPluginAsync = async (fastify) => { | ||||
|       } | ||||
|       const { theme, language, isPublicEmail, isPublicGuilds } = request.body | ||||
|       const settings = await prisma.userSetting.findFirst({ | ||||
|         where: { userId: request.user.current.id } | ||||
|         where: { userId: request.user.current.id }, | ||||
|       }) | ||||
|       if (settings == null) { | ||||
|         throw fastify.httpErrors.internalServerError() | ||||
| @@ -62,11 +62,11 @@ export const putCurrentUserSettings: FastifyPluginAsync = async (fastify) => { | ||||
|           theme: theme ?? settings.theme, | ||||
|           language: language ?? settings.language, | ||||
|           isPublicEmail: isPublicEmail ?? settings.isPublicEmail, | ||||
|           isPublicGuilds: isPublicGuilds ?? settings.isPublicGuilds | ||||
|         } | ||||
|           isPublicGuilds: isPublicGuilds ?? settings.isPublicGuilds, | ||||
|         }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return { settings: newSettings } | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,31 +1,31 @@ | ||||
| import type { FastifyPluginAsync } from 'fastify' | ||||
| import type { FastifyPluginAsync } from "fastify" | ||||
|  | ||||
| import { postSignupUser } from './signup/post.js' | ||||
| import { getConfirmEmail } from './confirm-email/get.js' | ||||
| import { postSigninUser } from './signin/post.js' | ||||
| import { postSignoutUser } from './signout/post.js' | ||||
| import { deleteSignoutUser } from './signout/delete.js' | ||||
| import { postRefreshTokenUser } from './refresh-token/post.js' | ||||
| import { putResetPasswordUser } from './reset-password/put.js' | ||||
| import { postResetPasswordUser } from './reset-password/post.js' | ||||
| import { getCurrentUser } from './current/get.js' | ||||
| import { putCurrentUser } from './current/put.js' | ||||
| import { putCurrentUserSettings } from './current/settings/put.js' | ||||
| import { getUserById } from './[userId]/get.js' | ||||
| import { putCurrentUserLogo } from './current/logo/put.js' | ||||
| import { getSigninDiscordOAuth2Service } from './oauth2/discord/signin/get.js' | ||||
| import { getCallbackDiscordOAuth2Service } from './oauth2/discord/callback/get.js' | ||||
| import { getSigninGoogleOAuth2Service } from './oauth2/google/signin/get.js' | ||||
| import { getCallbackGoogleOAuth2Service } from './oauth2/google/callback/get.js' | ||||
| import { getSigninGitHubOAuth2Service } from './oauth2/github/signin/get.js' | ||||
| import { getCallbackGitHubOAuth2Service } from './oauth2/github/callback/get.js' | ||||
| import { deleteProviderService } from './oauth2/[provider]/delete.js' | ||||
| import { getCallbackAddStrategyDiscordOAuth2Service } from './oauth2/discord/callback-add-strategy/get.js' | ||||
| import { getAddStrategyDiscordOAuth2Service } from './oauth2/discord/add-strategy/get.js' | ||||
| import { getAddStrategyGitHubOAuth2Service } from './oauth2/github/add-strategy/get.js' | ||||
| import { getCallbackAddStrategyGitHubOAuth2Service } from './oauth2/github/callback-add-strategy/get.js' | ||||
| import { getCallbackAddStrategyGoogleOAuth2Service } from './oauth2/google/callback-add-strategy/get.js' | ||||
| import { getAddStrategyGoogleOAuth2Service } from './oauth2/google/add-strategy/get.js' | ||||
| import { postSignupUser } from "./signup/post.js" | ||||
| import { getConfirmEmail } from "./confirm-email/get.js" | ||||
| import { postSigninUser } from "./signin/post.js" | ||||
| import { postSignoutUser } from "./signout/post.js" | ||||
| import { deleteSignoutUser } from "./signout/delete.js" | ||||
| import { postRefreshTokenUser } from "./refresh-token/post.js" | ||||
| import { putResetPasswordUser } from "./reset-password/put.js" | ||||
| import { postResetPasswordUser } from "./reset-password/post.js" | ||||
| import { getCurrentUser } from "./current/get.js" | ||||
| import { putCurrentUser } from "./current/put.js" | ||||
| import { putCurrentUserSettings } from "./current/settings/put.js" | ||||
| import { getUserById } from "./[userId]/get.js" | ||||
| import { putCurrentUserLogo } from "./current/logo/put.js" | ||||
| import { getSigninDiscordOAuth2Service } from "./oauth2/discord/signin/get.js" | ||||
| import { getCallbackDiscordOAuth2Service } from "./oauth2/discord/callback/get.js" | ||||
| import { getSigninGoogleOAuth2Service } from "./oauth2/google/signin/get.js" | ||||
| import { getCallbackGoogleOAuth2Service } from "./oauth2/google/callback/get.js" | ||||
| import { getSigninGitHubOAuth2Service } from "./oauth2/github/signin/get.js" | ||||
| import { getCallbackGitHubOAuth2Service } from "./oauth2/github/callback/get.js" | ||||
| import { deleteProviderService } from "./oauth2/[provider]/delete.js" | ||||
| import { getCallbackAddStrategyDiscordOAuth2Service } from "./oauth2/discord/callback-add-strategy/get.js" | ||||
| import { getAddStrategyDiscordOAuth2Service } from "./oauth2/discord/add-strategy/get.js" | ||||
| import { getAddStrategyGitHubOAuth2Service } from "./oauth2/github/add-strategy/get.js" | ||||
| import { getCallbackAddStrategyGitHubOAuth2Service } from "./oauth2/github/callback-add-strategy/get.js" | ||||
| import { getCallbackAddStrategyGoogleOAuth2Service } from "./oauth2/google/callback-add-strategy/get.js" | ||||
| import { getAddStrategyGoogleOAuth2Service } from "./oauth2/google/add-strategy/get.js" | ||||
|  | ||||
| export const usersService: FastifyPluginAsync = async (fastify) => { | ||||
|   await fastify.register(postSignupUser) | ||||
|   | ||||
| @@ -1,25 +1,25 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import prisma from '#src/tools/database/prisma.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { oauthSchema } from '#src/models/OAuth.js' | ||||
| import prisma from "#src/tools/database/prisma.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
| import { oauthSchema } from "#src/models/OAuth.js" | ||||
|  | ||||
| const parametersSchema = Type.Object({ | ||||
|   provider: oauthSchema.provider | ||||
|   provider: oauthSchema.provider, | ||||
| }) | ||||
|  | ||||
| type Parameters = Static<typeof parametersSchema> | ||||
|  | ||||
| const deleteServiceSchema: FastifySchema = { | ||||
|   description: 'DELETE a provider to authenticate with for a user.', | ||||
|   tags: ['oauth2'] as string[], | ||||
|   description: "DELETE a provider to authenticate with for a user.", | ||||
|   tags: ["oauth2"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   params: parametersSchema, | ||||
|   response: { | ||||
| @@ -28,8 +28,8 @@ const deleteServiceSchema: FastifySchema = { | ||||
|     401: fastifyErrors[401], | ||||
|     403: fastifyErrors[403], | ||||
|     404: fastifyErrors[404], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const deleteProviderService: FastifyPluginAsync = async (fastify) => { | ||||
| @@ -38,8 +38,8 @@ export const deleteProviderService: FastifyPluginAsync = async (fastify) => { | ||||
|   fastify.route<{ | ||||
|     Params: Parameters | ||||
|   }>({ | ||||
|     method: 'DELETE', | ||||
|     url: '/users/oauth2/:provider', | ||||
|     method: "DELETE", | ||||
|     url: "/users/oauth2/:provider", | ||||
|     schema: deleteServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -48,31 +48,31 @@ export const deleteProviderService: FastifyPluginAsync = async (fastify) => { | ||||
|       const { user, params } = request | ||||
|       const { provider } = params | ||||
|       const OAuths = await prisma.oAuth.findMany({ | ||||
|         where: { userId: user.current.id } | ||||
|         where: { userId: user.current.id }, | ||||
|       }) | ||||
|       const strategies = OAuths.map((oauth) => { | ||||
|         return oauth.provider | ||||
|       }) | ||||
|       if (user.current.password != null) { | ||||
|         strategies.push('Local') | ||||
|         strategies.push("Local") | ||||
|       } | ||||
|       const oauthProvider = OAuths.find((oauth) => { | ||||
|         return oauth.provider === provider | ||||
|       }) | ||||
|       if (oauthProvider == null) { | ||||
|         throw fastify.httpErrors.notFound('You are not using this provider') | ||||
|         throw fastify.httpErrors.notFound("You are not using this provider") | ||||
|       } | ||||
|       const hasOthersWayToAuthenticate = strategies.length >= 2 | ||||
|       if (!hasOthersWayToAuthenticate) { | ||||
|         throw fastify.httpErrors.badRequest( | ||||
|           "You can't delete your only way to authenticate" | ||||
|           "You can't delete your only way to authenticate", | ||||
|         ) | ||||
|       } | ||||
|       const oauthProviderDelete = await prisma.oAuth.delete({ | ||||
|         where: { id: oauthProvider.id } | ||||
|         where: { id: oauthProvider.id }, | ||||
|       }) | ||||
|       reply.statusCode = 200 | ||||
|       return oauthProviderDelete | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,15 +1,15 @@ | ||||
| import querystring from 'node:querystring' | ||||
| import querystring from "node:querystring" | ||||
|  | ||||
| import axios from 'axios' | ||||
| import axios from "axios" | ||||
|  | ||||
| import { OAuthStrategy } from '#src/tools/utils/OAuthStrategy.js' | ||||
| import { OAuthStrategy } from "#src/tools/utils/OAuthStrategy.js" | ||||
|  | ||||
| export const DISCORD_PROVIDER = 'Discord' | ||||
| export const DISCORD_BASE_URL = 'https://discord.com/api/v10' | ||||
| export const DISCORD_PROVIDER = "Discord" | ||||
| export const DISCORD_BASE_URL = "https://discord.com/api/v10" | ||||
| export const DISCORD_CLIENT_ID = | ||||
|   process.env['DISCORD_CLIENT_ID'] ?? 'DISCORD_CLIENT_ID' | ||||
|   process.env["DISCORD_CLIENT_ID"] ?? "DISCORD_CLIENT_ID" | ||||
| export const DISCORD_CLIENT_SECRET = | ||||
|   process.env['DISCORD_CLIENT_SECRET'] ?? 'DISCORD_CLIENT_SECRET' | ||||
|   process.env["DISCORD_CLIENT_SECRET"] ?? "DISCORD_CLIENT_SECRET" | ||||
| export const discordStrategy = new OAuthStrategy(DISCORD_PROVIDER) | ||||
|  | ||||
| export interface DiscordUser { | ||||
| @@ -30,31 +30,31 @@ export interface DiscordTokens { | ||||
|  | ||||
| export const getDiscordUserData = async ( | ||||
|   code: string, | ||||
|   redirectURI: string | ||||
|   redirectURI: string, | ||||
| ): Promise<DiscordUser> => { | ||||
|   const { data: tokens } = await axios.post<DiscordTokens>( | ||||
|     `${DISCORD_BASE_URL}/oauth2/token`, | ||||
|     querystring.stringify({ | ||||
|       client_id: DISCORD_CLIENT_ID, | ||||
|       client_secret: DISCORD_CLIENT_SECRET, | ||||
|       grant_type: 'authorization_code', | ||||
|       grant_type: "authorization_code", | ||||
|       code, | ||||
|       redirect_uri: redirectURI, | ||||
|       scope: 'identify' | ||||
|       scope: "identify", | ||||
|     }), | ||||
|     { | ||||
|       headers: { | ||||
|         'Content-Type': 'application/x-www-form-urlencoded' | ||||
|       } | ||||
|     } | ||||
|         "Content-Type": "application/x-www-form-urlencoded", | ||||
|       }, | ||||
|     }, | ||||
|   ) | ||||
|   const { data: discordUser } = await axios.get<DiscordUser>( | ||||
|     `${DISCORD_BASE_URL}/users/@me`, | ||||
|     { | ||||
|       headers: { | ||||
|         Authorization: `${tokens.token_type} ${tokens.access_token}` | ||||
|       } | ||||
|     } | ||||
|         Authorization: `${tokens.token_type} ${tokens.access_token}`, | ||||
|       }, | ||||
|     }, | ||||
|   ) | ||||
|   return discordUser | ||||
| } | ||||
|   | ||||
| @@ -1,44 +1,44 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import { API_URL } from '#src/tools/configurations.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from '../__utils__/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { API_URL } from "#src/tools/configurations.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from "../__utils__/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
|  | ||||
| const querySchema = Type.Object({ | ||||
|   redirectURI: Type.String({ format: 'uri-reference' }) | ||||
|   redirectURI: Type.String({ format: "uri-reference" }), | ||||
| }) | ||||
|  | ||||
| type QuerySchemaType = Static<typeof querySchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'Discord OAuth2 - add-strategy', | ||||
|   tags: ['oauth2'] as string[], | ||||
|   description: "Discord OAuth2 - add-strategy", | ||||
|   tags: ["oauth2"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   querystring: querySchema, | ||||
|   response: { | ||||
|     200: Type.String(), | ||||
|     400: fastifyErrors[400], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getAddStrategyDiscordOAuth2Service: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.register(authenticateUser) | ||||
|  | ||||
|   await fastify.route<{ | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/users/oauth2/discord/add-strategy', | ||||
|     method: "GET", | ||||
|     url: "/users/oauth2/discord/add-strategy", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -49,6 +49,6 @@ export const getAddStrategyDiscordOAuth2Service: FastifyPluginAsync = async ( | ||||
|       const url = `${DISCORD_BASE_URL}/oauth2/authorize?client_id=${DISCORD_CLIENT_ID}&scope=identify&response_type=code&state=${request.user.accessToken}&redirect_uri=${redirectCallback}` | ||||
|       reply.statusCode = 200 | ||||
|       return url | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,30 +1,30 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import { API_URL } from '#src/tools/configurations.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { discordStrategy, getDiscordUserData } from '../__utils__/utils.js' | ||||
| import { buildQueryURL } from '#src/tools/utils/buildQueryURL.js' | ||||
| import { getUserWithBearerToken } from '#src/tools/plugins/authenticateUser.js' | ||||
| import { API_URL } from "#src/tools/configurations.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { discordStrategy, getDiscordUserData } from "../__utils__/utils.js" | ||||
| import { buildQueryURL } from "#src/tools/utils/buildQueryURL.js" | ||||
| import { getUserWithBearerToken } from "#src/tools/plugins/authenticateUser.js" | ||||
|  | ||||
| const querySchema = Type.Object({ | ||||
|   code: Type.String(), | ||||
|   state: Type.String(), | ||||
|   redirectURI: Type.String({ format: 'uri-reference' }) | ||||
|   redirectURI: Type.String({ format: "uri-reference" }), | ||||
| }) | ||||
|  | ||||
| type QuerySchemaType = Static<typeof querySchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'Discord OAuth2 - callback-add-strategy', | ||||
|   tags: ['oauth2'] as string[], | ||||
|   description: "Discord OAuth2 - callback-add-strategy", | ||||
|   tags: ["oauth2"] as string[], | ||||
|   querystring: querySchema, | ||||
|   response: { | ||||
|     200: Type.String(), | ||||
|     400: fastifyErrors[400], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getCallbackAddStrategyDiscordOAuth2Service: FastifyPluginAsync = | ||||
| @@ -32,26 +32,26 @@ export const getCallbackAddStrategyDiscordOAuth2Service: FastifyPluginAsync = | ||||
|     await fastify.route<{ | ||||
|       Querystring: QuerySchemaType | ||||
|     }>({ | ||||
|       method: 'GET', | ||||
|       url: '/users/oauth2/discord/callback-add-strategy', | ||||
|       method: "GET", | ||||
|       url: "/users/oauth2/discord/callback-add-strategy", | ||||
|       schema: getServiceSchema, | ||||
|       handler: async (request, reply) => { | ||||
|         const { redirectURI, code, state: accessToken } = request.query | ||||
|         const userRequest = await getUserWithBearerToken( | ||||
|           `Bearer ${accessToken}` | ||||
|           `Bearer ${accessToken}`, | ||||
|         ) | ||||
|         const discordUser = await getDiscordUserData( | ||||
|           code, | ||||
|           `${API_URL}/users/oauth2/discord/callback-add-strategy?redirectURI=${redirectURI}` | ||||
|           `${API_URL}/users/oauth2/discord/callback-add-strategy?redirectURI=${redirectURI}`, | ||||
|         ) | ||||
|         const message = await discordStrategy.callbackAddStrategy( | ||||
|           { | ||||
|             name: discordUser.username, | ||||
|             id: discordUser.id | ||||
|             id: discordUser.id, | ||||
|           }, | ||||
|           userRequest | ||||
|           userRequest, | ||||
|         ) | ||||
|         return await reply.redirect(buildQueryURL(redirectURI, { message })) | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|   } | ||||
|   | ||||
| @@ -1,50 +1,50 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import { API_URL } from '#src/tools/configurations.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { discordStrategy, getDiscordUserData } from '../__utils__/utils.js' | ||||
| import { buildQueryURL } from '#src/tools/utils/buildQueryURL.js' | ||||
| import { API_URL } from "#src/tools/configurations.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { discordStrategy, getDiscordUserData } from "../__utils__/utils.js" | ||||
| import { buildQueryURL } from "#src/tools/utils/buildQueryURL.js" | ||||
|  | ||||
| const querySchema = Type.Object({ | ||||
|   code: Type.String(), | ||||
|   redirectURI: Type.String({ format: 'uri-reference' }) | ||||
|   redirectURI: Type.String({ format: "uri-reference" }), | ||||
| }) | ||||
|  | ||||
| type QuerySchemaType = Static<typeof querySchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'Discord OAuth2 - callback', | ||||
|   tags: ['oauth2'] as string[], | ||||
|   description: "Discord OAuth2 - callback", | ||||
|   tags: ["oauth2"] as string[], | ||||
|   querystring: querySchema, | ||||
|   response: { | ||||
|     200: Type.String(), | ||||
|     400: fastifyErrors[400], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getCallbackDiscordOAuth2Service: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.route<{ | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/users/oauth2/discord/callback', | ||||
|     method: "GET", | ||||
|     url: "/users/oauth2/discord/callback", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       const { redirectURI, code } = request.query | ||||
|       const discordUser = await getDiscordUserData( | ||||
|         code, | ||||
|         `${API_URL}/users/oauth2/discord/callback?redirectURI=${redirectURI}` | ||||
|         `${API_URL}/users/oauth2/discord/callback?redirectURI=${redirectURI}`, | ||||
|       ) | ||||
|       const responseJWT = await discordStrategy.callbackSignin({ | ||||
|         name: discordUser.username, | ||||
|         id: discordUser.id | ||||
|         id: discordUser.id, | ||||
|       }) | ||||
|       return await reply.redirect(buildQueryURL(redirectURI, responseJWT)) | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,36 +1,36 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import { API_URL } from '#src/tools/configurations.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from '../__utils__/utils.js' | ||||
| import { API_URL } from "#src/tools/configurations.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from "../__utils__/utils.js" | ||||
|  | ||||
| const querySchema = Type.Object({ | ||||
|   redirectURI: Type.String({ format: 'uri-reference' }) | ||||
|   redirectURI: Type.String({ format: "uri-reference" }), | ||||
| }) | ||||
|  | ||||
| type QuerySchemaType = Static<typeof querySchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'Discord OAuth2 - signin', | ||||
|   tags: ['oauth2'] as string[], | ||||
|   description: "Discord OAuth2 - signin", | ||||
|   tags: ["oauth2"] as string[], | ||||
|   querystring: querySchema, | ||||
|   response: { | ||||
|     200: Type.String(), | ||||
|     400: fastifyErrors[400], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getSigninDiscordOAuth2Service: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.route<{ | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/users/oauth2/discord/signin', | ||||
|     method: "GET", | ||||
|     url: "/users/oauth2/discord/signin", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       const { redirectURI } = request.query | ||||
| @@ -38,6 +38,6 @@ export const getSigninDiscordOAuth2Service: FastifyPluginAsync = async ( | ||||
|       const url = `${DISCORD_BASE_URL}/oauth2/authorize?client_id=${DISCORD_CLIENT_ID}&scope=identify&response_type=code&redirect_uri=${redirectCallback}` | ||||
|       reply.statusCode = 200 | ||||
|       return url | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,16 +1,16 @@ | ||||
| import querystring from 'node:querystring' | ||||
| import querystring from "node:querystring" | ||||
|  | ||||
| import axios from 'axios' | ||||
| import axios from "axios" | ||||
|  | ||||
| import { OAuthStrategy } from '#src/tools/utils/OAuthStrategy.js' | ||||
| import { OAuthStrategy } from "#src/tools/utils/OAuthStrategy.js" | ||||
|  | ||||
| export const GITHUB_PROVIDER = 'GitHub' | ||||
| export const GITHUB_BASE_URL = 'https://github.com' | ||||
| export const GITHUB_API_BASE_URL = 'https://api.github.com' | ||||
| export const GITHUB_PROVIDER = "GitHub" | ||||
| export const GITHUB_BASE_URL = "https://github.com" | ||||
| export const GITHUB_API_BASE_URL = "https://api.github.com" | ||||
| export const GITHUB_CLIENT_ID = | ||||
|   process.env['GITHUB_CLIENT_ID'] ?? 'GITHUB_CLIENT_ID' | ||||
|   process.env["GITHUB_CLIENT_ID"] ?? "GITHUB_CLIENT_ID" | ||||
| export const GITHUB_CLIENT_SECRET = | ||||
|   process.env['GITHUB_CLIENT_SECRET'] ?? 'GITHUB_CLIENT_SECRET' | ||||
|   process.env["GITHUB_CLIENT_SECRET"] ?? "GITHUB_CLIENT_SECRET" | ||||
| export const githubStrategy = new OAuthStrategy(GITHUB_PROVIDER) | ||||
|  | ||||
| export interface GitHubUser { | ||||
| @@ -28,7 +28,7 @@ export interface GitHubTokens { | ||||
|  | ||||
| export const getGitHubUserData = async ( | ||||
|   code: string, | ||||
|   redirectURI: string | ||||
|   redirectURI: string, | ||||
| ): Promise<GitHubUser> => { | ||||
|   const { data: token } = await axios.post<GitHubTokens>( | ||||
|     `${GITHUB_BASE_URL}/login/oauth/access_token`, | ||||
| @@ -36,22 +36,22 @@ export const getGitHubUserData = async ( | ||||
|       client_id: GITHUB_CLIENT_ID, | ||||
|       client_secret: GITHUB_CLIENT_SECRET, | ||||
|       code, | ||||
|       redirect_uri: redirectURI | ||||
|       redirect_uri: redirectURI, | ||||
|     }), | ||||
|     { | ||||
|       headers: { | ||||
|         'Content-Type': 'application/x-www-form-urlencoded', | ||||
|         Accept: 'application/json' | ||||
|       } | ||||
|     } | ||||
|         "Content-Type": "application/x-www-form-urlencoded", | ||||
|         Accept: "application/json", | ||||
|       }, | ||||
|     }, | ||||
|   ) | ||||
|   const { data: githubUser } = await axios.get<GitHubUser>( | ||||
|     `${GITHUB_API_BASE_URL}/user`, | ||||
|     { | ||||
|       headers: { | ||||
|         Authorization: `token ${token.access_token}` | ||||
|       } | ||||
|     } | ||||
|         Authorization: `token ${token.access_token}`, | ||||
|       }, | ||||
|     }, | ||||
|   ) | ||||
|   return githubUser | ||||
| } | ||||
|   | ||||
| @@ -1,44 +1,44 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import { API_URL } from '#src/tools/configurations.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from '../__utils__/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { API_URL } from "#src/tools/configurations.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from "../__utils__/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
|  | ||||
| const querySchema = Type.Object({ | ||||
|   redirectURI: Type.String({ format: 'uri-reference' }) | ||||
|   redirectURI: Type.String({ format: "uri-reference" }), | ||||
| }) | ||||
|  | ||||
| type QuerySchemaType = Static<typeof querySchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'GitHub OAuth2 - add-strategy', | ||||
|   tags: ['oauth2'] as string[], | ||||
|   description: "GitHub OAuth2 - add-strategy", | ||||
|   tags: ["oauth2"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   querystring: querySchema, | ||||
|   response: { | ||||
|     200: Type.String(), | ||||
|     400: fastifyErrors[400], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getAddStrategyGitHubOAuth2Service: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.register(authenticateUser) | ||||
|  | ||||
|   await fastify.route<{ | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/users/oauth2/github/add-strategy', | ||||
|     method: "GET", | ||||
|     url: "/users/oauth2/github/add-strategy", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -49,6 +49,6 @@ export const getAddStrategyGitHubOAuth2Service: FastifyPluginAsync = async ( | ||||
|       const url = `${GITHUB_BASE_URL}/login/oauth/authorize?client_id=${GITHUB_CLIENT_ID}&state=${request.user.accessToken}&redirect_uri=${redirectCallback}` | ||||
|       reply.statusCode = 200 | ||||
|       return url | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,30 +1,30 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import { API_URL } from '#src/tools/configurations.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { githubStrategy, getGitHubUserData } from '../__utils__/utils.js' | ||||
| import { buildQueryURL } from '#src/tools/utils/buildQueryURL.js' | ||||
| import { getUserWithBearerToken } from '#src/tools/plugins/authenticateUser.js' | ||||
| import { API_URL } from "#src/tools/configurations.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { githubStrategy, getGitHubUserData } from "../__utils__/utils.js" | ||||
| import { buildQueryURL } from "#src/tools/utils/buildQueryURL.js" | ||||
| import { getUserWithBearerToken } from "#src/tools/plugins/authenticateUser.js" | ||||
|  | ||||
| const querySchema = Type.Object({ | ||||
|   code: Type.String(), | ||||
|   state: Type.String(), | ||||
|   redirectURI: Type.String({ format: 'uri-reference' }) | ||||
|   redirectURI: Type.String({ format: "uri-reference" }), | ||||
| }) | ||||
|  | ||||
| type QuerySchemaType = Static<typeof querySchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'GitHub OAuth2 - callback-add-strategy', | ||||
|   tags: ['oauth2'] as string[], | ||||
|   description: "GitHub OAuth2 - callback-add-strategy", | ||||
|   tags: ["oauth2"] as string[], | ||||
|   querystring: querySchema, | ||||
|   response: { | ||||
|     200: Type.String(), | ||||
|     400: fastifyErrors[400], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getCallbackAddStrategyGitHubOAuth2Service: FastifyPluginAsync = | ||||
| @@ -32,26 +32,26 @@ export const getCallbackAddStrategyGitHubOAuth2Service: FastifyPluginAsync = | ||||
|     await fastify.route<{ | ||||
|       Querystring: QuerySchemaType | ||||
|     }>({ | ||||
|       method: 'GET', | ||||
|       url: '/users/oauth2/github/callback-add-strategy', | ||||
|       method: "GET", | ||||
|       url: "/users/oauth2/github/callback-add-strategy", | ||||
|       schema: getServiceSchema, | ||||
|       handler: async (request, reply) => { | ||||
|         const { redirectURI, code, state: accessToken } = request.query | ||||
|         const userRequest = await getUserWithBearerToken( | ||||
|           `Bearer ${accessToken}` | ||||
|           `Bearer ${accessToken}`, | ||||
|         ) | ||||
|         const githubUser = await getGitHubUserData( | ||||
|           code, | ||||
|           `${API_URL}/users/oauth2/github/callback-add-strategy?redirectURI=${redirectURI}` | ||||
|           `${API_URL}/users/oauth2/github/callback-add-strategy?redirectURI=${redirectURI}`, | ||||
|         ) | ||||
|         const message = await githubStrategy.callbackAddStrategy( | ||||
|           { | ||||
|             name: githubUser.name, | ||||
|             id: githubUser.id | ||||
|             id: githubUser.id, | ||||
|           }, | ||||
|           userRequest | ||||
|           userRequest, | ||||
|         ) | ||||
|         return await reply.redirect(buildQueryURL(redirectURI, { message })) | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|   } | ||||
|   | ||||
| @@ -1,50 +1,50 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import { API_URL } from '#src/tools/configurations.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { githubStrategy, getGitHubUserData } from '../__utils__/utils.js' | ||||
| import { buildQueryURL } from '#src/tools/utils/buildQueryURL.js' | ||||
| import { API_URL } from "#src/tools/configurations.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { githubStrategy, getGitHubUserData } from "../__utils__/utils.js" | ||||
| import { buildQueryURL } from "#src/tools/utils/buildQueryURL.js" | ||||
|  | ||||
| const querySchema = Type.Object({ | ||||
|   code: Type.String(), | ||||
|   redirectURI: Type.String({ format: 'uri-reference' }) | ||||
|   redirectURI: Type.String({ format: "uri-reference" }), | ||||
| }) | ||||
|  | ||||
| type QuerySchemaType = Static<typeof querySchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'GitHub OAuth2 - callback', | ||||
|   tags: ['oauth2'] as string[], | ||||
|   description: "GitHub OAuth2 - callback", | ||||
|   tags: ["oauth2"] as string[], | ||||
|   querystring: querySchema, | ||||
|   response: { | ||||
|     200: Type.String(), | ||||
|     400: fastifyErrors[400], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getCallbackGitHubOAuth2Service: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.route<{ | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/users/oauth2/github/callback', | ||||
|     method: "GET", | ||||
|     url: "/users/oauth2/github/callback", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       const { redirectURI, code } = request.query | ||||
|       const githubUser = await getGitHubUserData( | ||||
|         code, | ||||
|         `${API_URL}/users/oauth2/github/callback?redirectURI=${redirectURI}` | ||||
|         `${API_URL}/users/oauth2/github/callback?redirectURI=${redirectURI}`, | ||||
|       ) | ||||
|       const responseJWT = await githubStrategy.callbackSignin({ | ||||
|         name: githubUser.name, | ||||
|         id: githubUser.id | ||||
|         id: githubUser.id, | ||||
|       }) | ||||
|       return await reply.redirect(buildQueryURL(redirectURI, responseJWT)) | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,36 +1,36 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import { API_URL } from '#src/tools/configurations.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from '../__utils__/utils.js' | ||||
| import { API_URL } from "#src/tools/configurations.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from "../__utils__/utils.js" | ||||
|  | ||||
| const querySchema = Type.Object({ | ||||
|   redirectURI: Type.String({ format: 'uri-reference' }) | ||||
|   redirectURI: Type.String({ format: "uri-reference" }), | ||||
| }) | ||||
|  | ||||
| type QuerySchemaType = Static<typeof querySchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'GitHub OAuth2 - signin', | ||||
|   tags: ['oauth2'] as string[], | ||||
|   description: "GitHub OAuth2 - signin", | ||||
|   tags: ["oauth2"] as string[], | ||||
|   querystring: querySchema, | ||||
|   response: { | ||||
|     200: Type.String(), | ||||
|     400: fastifyErrors[400], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getSigninGitHubOAuth2Service: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.route<{ | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/users/oauth2/github/signin', | ||||
|     method: "GET", | ||||
|     url: "/users/oauth2/github/signin", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       const { redirectURI } = request.query | ||||
| @@ -38,6 +38,6 @@ export const getSigninGitHubOAuth2Service: FastifyPluginAsync = async ( | ||||
|       const url = `${GITHUB_BASE_URL}/login/oauth/authorize?client_id=${GITHUB_CLIENT_ID}&redirect_uri=${redirectCallback}` | ||||
|       reply.statusCode = 200 | ||||
|       return url | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,18 +1,18 @@ | ||||
| import querystring from 'node:querystring' | ||||
| import querystring from "node:querystring" | ||||
|  | ||||
| import axios from 'axios' | ||||
| import axios from "axios" | ||||
|  | ||||
| import { OAuthStrategy } from '#src/tools/utils/OAuthStrategy.js' | ||||
| import { OAuthStrategy } from "#src/tools/utils/OAuthStrategy.js" | ||||
|  | ||||
| export const GOOGLE_PROVIDER = 'Google' | ||||
| export const GOOGLE_BASE_URL = 'https://accounts.google.com/o/oauth2/v2/auth' | ||||
| export const GOOGLE_OAUTH2_TOKEN = 'https://oauth2.googleapis.com/token' | ||||
| export const GOOGLE_PROVIDER = "Google" | ||||
| export const GOOGLE_BASE_URL = "https://accounts.google.com/o/oauth2/v2/auth" | ||||
| export const GOOGLE_OAUTH2_TOKEN = "https://oauth2.googleapis.com/token" | ||||
| export const GOOGLE_USERINFO = | ||||
|   'https://www.googleapis.com/oauth2/v1/userinfo?alt=json' | ||||
|   "https://www.googleapis.com/oauth2/v1/userinfo?alt=json" | ||||
| export const GOOGLE_CLIENT_ID = | ||||
|   process.env['GOOGLE_CLIENT_ID'] ?? 'GOOGLE_CLIENT_ID' | ||||
|   process.env["GOOGLE_CLIENT_ID"] ?? "GOOGLE_CLIENT_ID" | ||||
| export const GOOGLE_CLIENT_SECRET = | ||||
|   process.env['GOOGLE_CLIENT_SECRET'] ?? 'GOOGLE_CLIENT_SECRET' | ||||
|   process.env["GOOGLE_CLIENT_SECRET"] ?? "GOOGLE_CLIENT_SECRET" | ||||
| export const googleStrategy = new OAuthStrategy(GOOGLE_PROVIDER) | ||||
|  | ||||
| export interface GoogleUser { | ||||
| @@ -34,7 +34,7 @@ export interface GoogleTokens { | ||||
|  | ||||
| export const getGoogleUserData = async ( | ||||
|   code: string, | ||||
|   redirectURI: string | ||||
|   redirectURI: string, | ||||
| ): Promise<GoogleUser> => { | ||||
|   const { data: token } = await axios.post<GoogleTokens>( | ||||
|     GOOGLE_OAUTH2_TOKEN, | ||||
| @@ -43,17 +43,17 @@ export const getGoogleUserData = async ( | ||||
|       client_secret: GOOGLE_CLIENT_SECRET, | ||||
|       code, | ||||
|       redirect_uri: redirectURI, | ||||
|       grant_type: 'authorization_code' | ||||
|       grant_type: "authorization_code", | ||||
|     }), | ||||
|     { | ||||
|       headers: { | ||||
|         'Content-Type': 'application/x-www-form-urlencoded', | ||||
|         Accept: 'application/json' | ||||
|       } | ||||
|     } | ||||
|         "Content-Type": "application/x-www-form-urlencoded", | ||||
|         Accept: "application/json", | ||||
|       }, | ||||
|     }, | ||||
|   ) | ||||
|   const { data: googleUser } = await axios.get<GoogleUser>( | ||||
|     `${GOOGLE_USERINFO}&access_token=${token.access_token}` | ||||
|     `${GOOGLE_USERINFO}&access_token=${token.access_token}`, | ||||
|   ) | ||||
|   return googleUser | ||||
| } | ||||
|   | ||||
| @@ -1,44 +1,44 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import { API_URL } from '#src/tools/configurations.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { GOOGLE_BASE_URL, GOOGLE_CLIENT_ID } from '../__utils__/utils.js' | ||||
| import authenticateUser from '#src/tools/plugins/authenticateUser.js' | ||||
| import { API_URL } from "#src/tools/configurations.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { GOOGLE_BASE_URL, GOOGLE_CLIENT_ID } from "../__utils__/utils.js" | ||||
| import authenticateUser from "#src/tools/plugins/authenticateUser.js" | ||||
|  | ||||
| const querySchema = Type.Object({ | ||||
|   redirectURI: Type.String({ format: 'uri-reference' }) | ||||
|   redirectURI: Type.String({ format: "uri-reference" }), | ||||
| }) | ||||
|  | ||||
| type QuerySchemaType = Static<typeof querySchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'Google OAuth2 - add-strategy', | ||||
|   tags: ['oauth2'] as string[], | ||||
|   description: "Google OAuth2 - add-strategy", | ||||
|   tags: ["oauth2"] as string[], | ||||
|   security: [ | ||||
|     { | ||||
|       bearerAuth: [] | ||||
|     } | ||||
|       bearerAuth: [], | ||||
|     }, | ||||
|   ] as Array<{ [key: string]: [] }>, | ||||
|   querystring: querySchema, | ||||
|   response: { | ||||
|     200: Type.String(), | ||||
|     400: fastifyErrors[400], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getAddStrategyGoogleOAuth2Service: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.register(authenticateUser) | ||||
|  | ||||
|   await fastify.route<{ | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/users/oauth2/google/add-strategy', | ||||
|     method: "GET", | ||||
|     url: "/users/oauth2/google/add-strategy", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       if (request.user == null) { | ||||
| @@ -49,6 +49,6 @@ export const getAddStrategyGoogleOAuth2Service: FastifyPluginAsync = async ( | ||||
|       const url = `${GOOGLE_BASE_URL}?client_id=${GOOGLE_CLIENT_ID}&state=${request.user.accessToken}&redirect_uri=${redirectCallback}&response_type=code&scope=profile&access_type=online` | ||||
|       reply.statusCode = 200 | ||||
|       return url | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,30 +1,30 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import { API_URL } from '#src/tools/configurations.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { googleStrategy, getGoogleUserData } from '../__utils__/utils.js' | ||||
| import { buildQueryURL } from '#src/tools/utils/buildQueryURL.js' | ||||
| import { getUserWithBearerToken } from '#src/tools/plugins/authenticateUser.js' | ||||
| import { API_URL } from "#src/tools/configurations.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { googleStrategy, getGoogleUserData } from "../__utils__/utils.js" | ||||
| import { buildQueryURL } from "#src/tools/utils/buildQueryURL.js" | ||||
| import { getUserWithBearerToken } from "#src/tools/plugins/authenticateUser.js" | ||||
|  | ||||
| const querySchema = Type.Object({ | ||||
|   code: Type.String(), | ||||
|   state: Type.String(), | ||||
|   redirectURI: Type.String({ format: 'uri-reference' }) | ||||
|   redirectURI: Type.String({ format: "uri-reference" }), | ||||
| }) | ||||
|  | ||||
| type QuerySchemaType = Static<typeof querySchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'Google OAuth2 - callback-add-strategy', | ||||
|   tags: ['oauth2'] as string[], | ||||
|   description: "Google OAuth2 - callback-add-strategy", | ||||
|   tags: ["oauth2"] as string[], | ||||
|   querystring: querySchema, | ||||
|   response: { | ||||
|     200: Type.String(), | ||||
|     400: fastifyErrors[400], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getCallbackAddStrategyGoogleOAuth2Service: FastifyPluginAsync = | ||||
| @@ -32,26 +32,26 @@ export const getCallbackAddStrategyGoogleOAuth2Service: FastifyPluginAsync = | ||||
|     await fastify.route<{ | ||||
|       Querystring: QuerySchemaType | ||||
|     }>({ | ||||
|       method: 'GET', | ||||
|       url: '/users/oauth2/google/callback-add-strategy', | ||||
|       method: "GET", | ||||
|       url: "/users/oauth2/google/callback-add-strategy", | ||||
|       schema: getServiceSchema, | ||||
|       handler: async (request, reply) => { | ||||
|         const { redirectURI, code, state: accessToken } = request.query | ||||
|         const userRequest = await getUserWithBearerToken( | ||||
|           `Bearer ${accessToken}` | ||||
|           `Bearer ${accessToken}`, | ||||
|         ) | ||||
|         const googleUser = await getGoogleUserData( | ||||
|           code, | ||||
|           `${API_URL}/users/oauth2/google/callback-add-strategy?redirectURI=${redirectURI}` | ||||
|           `${API_URL}/users/oauth2/google/callback-add-strategy?redirectURI=${redirectURI}`, | ||||
|         ) | ||||
|         const message = await googleStrategy.callbackAddStrategy( | ||||
|           { | ||||
|             name: googleUser.name, | ||||
|             id: googleUser.id | ||||
|             id: googleUser.id, | ||||
|           }, | ||||
|           userRequest | ||||
|           userRequest, | ||||
|         ) | ||||
|         return await reply.redirect(buildQueryURL(redirectURI, { message })) | ||||
|       } | ||||
|       }, | ||||
|     }) | ||||
|   } | ||||
|   | ||||
| @@ -1,50 +1,50 @@ | ||||
| import type { Static } from '@sinclair/typebox' | ||||
| import { Type } from '@sinclair/typebox' | ||||
| import type { FastifyPluginAsync, FastifySchema } from 'fastify' | ||||
| import type { Static } from "@sinclair/typebox" | ||||
| import { Type } from "@sinclair/typebox" | ||||
| import type { FastifyPluginAsync, FastifySchema } from "fastify" | ||||
|  | ||||
| import { API_URL } from '#src/tools/configurations.js' | ||||
| import { fastifyErrors } from '#src/models/utils.js' | ||||
| import { googleStrategy, getGoogleUserData } from '../__utils__/utils.js' | ||||
| import { buildQueryURL } from '#src/tools/utils/buildQueryURL.js' | ||||
| import { API_URL } from "#src/tools/configurations.js" | ||||
| import { fastifyErrors } from "#src/models/utils.js" | ||||
| import { googleStrategy, getGoogleUserData } from "../__utils__/utils.js" | ||||
| import { buildQueryURL } from "#src/tools/utils/buildQueryURL.js" | ||||
|  | ||||
| const querySchema = Type.Object({ | ||||
|   code: Type.String(), | ||||
|   redirectURI: Type.String({ format: 'uri-reference' }) | ||||
|   redirectURI: Type.String({ format: "uri-reference" }), | ||||
| }) | ||||
|  | ||||
| type QuerySchemaType = Static<typeof querySchema> | ||||
|  | ||||
| const getServiceSchema: FastifySchema = { | ||||
|   description: 'Google OAuth2 - callback', | ||||
|   tags: ['oauth2'] as string[], | ||||
|   description: "Google OAuth2 - callback", | ||||
|   tags: ["oauth2"] as string[], | ||||
|   querystring: querySchema, | ||||
|   response: { | ||||
|     200: Type.String(), | ||||
|     400: fastifyErrors[400], | ||||
|     500: fastifyErrors[500] | ||||
|   } | ||||
|     500: fastifyErrors[500], | ||||
|   }, | ||||
| } as const | ||||
|  | ||||
| export const getCallbackGoogleOAuth2Service: FastifyPluginAsync = async ( | ||||
|   fastify | ||||
|   fastify, | ||||
| ) => { | ||||
|   await fastify.route<{ | ||||
|     Querystring: QuerySchemaType | ||||
|   }>({ | ||||
|     method: 'GET', | ||||
|     url: '/users/oauth2/google/callback', | ||||
|     method: "GET", | ||||
|     url: "/users/oauth2/google/callback", | ||||
|     schema: getServiceSchema, | ||||
|     handler: async (request, reply) => { | ||||
|       const { redirectURI, code } = request.query | ||||
|       const googleUser = await getGoogleUserData( | ||||
|         code, | ||||
|         `${API_URL}/users/oauth2/google/callback?redirectURI=${redirectURI}` | ||||
|         `${API_URL}/users/oauth2/google/callback?redirectURI=${redirectURI}`, | ||||
|       ) | ||||
|       const responseJWT = await googleStrategy.callbackSignin({ | ||||
|         name: googleUser.name, | ||||
|         id: googleUser.id | ||||
|         id: googleUser.id, | ||||
|       }) | ||||
|       return await reply.redirect(buildQueryURL(redirectURI, responseJWT)) | ||||
|     } | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user