32 Commits

Author SHA1 Message Date
23d2a9da71 chore(release): 1.2.7 [skip ci] 2023-07-02 16:58:58 +00:00
8e238f80c5 docs: use ssh to clone the repository 2023-07-02 18:54:02 +02:00
ca7de85e4b chore: enable source maps for easier debugging 2023-07-02 18:52:01 +02:00
ab19598edd fix: update author - Théo LUDWIG 2023-07-02 18:46:58 +02:00
1c1644f243 fix: update dependencies to latest 2023-07-02 18:45:54 +02:00
10110d1a36 chore(release): 1.2.6 [skip ci] 2023-05-13 18:12:41 +00:00
73d2da66b2 fix: update dependencies to latest 2023-05-13 20:09:02 +02:00
b07a62de8b chore(release): 1.2.5 [skip ci] 2023-04-02 21:49:01 +00:00
8a327eb7c7 fix: update dependencies to latest 2023-04-02 23:45:47 +02:00
78d7dbdb3f chore(release): 1.2.4 [skip ci] 2023-01-11 17:04:21 +00:00
e30a66eeb6 fix: update dependencies to latest 2023-01-11 18:02:38 +01:00
12dcabccb3 chore(release): 1.2.3 [skip ci] 2022-12-13 10:40:30 +00:00
224d3b3764 fix: dependencies security vulnerabilities (fastify) 2022-12-13 10:38:59 +00:00
fdecf5ce1a chore(release): 1.2.2 [skip ci] 2022-11-08 11:22:53 +00:00
6926132a1b fix: dependencies security vulnerabilities 2022-11-08 11:16:24 +00:00
e1543becc5 chore(release): 1.2.1 [skip ci] 2022-10-04 13:06:50 +00:00
b985172cd0 fix: schemaValidationMessages errors in Swagger docs 2022-10-04 14:59:14 +02:00
8ac1696ca0 docs: add information for development 2022-10-04 14:58:12 +02:00
de34618a7c chore: simplify Docker setup 2022-08-30 17:57:29 +02:00
400dc7ec2a chore: fix Dockerfile 2022-08-30 17:53:57 +02:00
49ac4f6ca4 chore(release): 1.2.0 [skip ci] 2022-08-29 17:47:08 +00:00
8e69511e3e docs: add oauth2 tag 2022-08-29 17:37:00 +00:00
7e305429b4 feat: make JWT refreshTokens more secure
Don't store the token itself in the database, store a UUID, and when refreshing the accessToken, verify the token and verify that in the payload there is a corresponding UUID stored in the database
2022-08-29 17:26:43 +00:00
b71da7dcc9 fix: on password reset, delete all refresh tokens 2022-08-29 16:32:24 +00:00
a6dd112e4a refactor: minor changes 2022-08-29 16:10:17 +00:00
ab94d1e656 ci: fix prisma:validate error in CI 2022-08-23 23:58:38 +02:00
8483cd4772 ci: usage of ubuntu-latest 2022-08-23 23:57:52 +02:00
46745e1b7e build(deps): update latest 2022-08-23 21:53:07 +00:00
50dbab7dfe chore(release): 1.1.0 [skip ci] 2022-06-29 04:34:56 +00:00
2f78604116 fix: sort public guilds with descending members count 2022-06-29 04:16:48 +00:00
4d565e4f1f build(deps): update latest 2022-06-29 03:59:30 +00:00
183377afc3 feat: update file-uploads-api to v1.1.0 2022-06-29 03:58:26 +00:00
113 changed files with 8879 additions and 16718 deletions

View File

@ -1 +1,3 @@
{ "extends": ["@commitlint/config-conventional"] } {
"extends": ["@commitlint/config-conventional"]
}

View File

@ -1,2 +1 @@
ARG VARIANT="16" FROM mcr.microsoft.com/devcontainers/javascript-node:18
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}

View File

@ -3,6 +3,8 @@
"dockerComposeFile": "./docker-compose.yml", "dockerComposeFile": "./docker-compose.yml",
"service": "workspace", "service": "workspace",
"workspaceFolder": "/workspace", "workspaceFolder": "/workspace",
"customizations": {
"vscode": {
"settings": { "settings": {
"remote.autoForwardPorts": false "remote.autoForwardPorts": false
}, },
@ -14,7 +16,9 @@
"prisma.prisma", "prisma.prisma",
"mikestead.dotenv", "mikestead.dotenv",
"ms-azuretools.vscode-docker" "ms-azuretools.vscode-docker"
], ]
}
},
"forwardPorts": [8080, 5555, 5432, 1080], "forwardPorts": [8080, 5555, 5432, 1080],
"postAttachCommand": ["npm", "install"], "postAttachCommand": ["npm", "install"],
"remoteUser": "node" "remoteUser": "node"

View File

@ -12,7 +12,7 @@ services:
- 'host.docker.internal:host-gateway' - 'host.docker.internal:host-gateway'
thream-database: thream-database:
image: 'postgres:14.2' image: 'postgres:15.3'
environment: environment:
POSTGRES_USER: 'user' POSTGRES_USER: 'user'
POSTGRES_PASSWORD: 'password' POSTGRES_PASSWORD: 'password'
@ -21,10 +21,5 @@ services:
- 'postgres-data:/var/lib/postgresql/data' - 'postgres-data:/var/lib/postgresql/data'
restart: 'unless-stopped' restart: 'unless-stopped'
thream-maildev:
image: 'maildev/maildev:1.1.0'
ports:
- '1080:80'
volumes: volumes:
postgres-data: postgres-data:

View File

@ -1,9 +1,6 @@
.vscode .*
.git !.npmrc
.env !.swcrc
build build
coverage coverage
.nyc_output
node_modules node_modules
tmp
temp

View File

@ -15,6 +15,7 @@ GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET= GOOGLE_CLIENT_SECRET=
HOST=0.0.0.0 HOST=0.0.0.0
JWT_ACCESS_EXPIRES_IN=15 minutes JWT_ACCESS_EXPIRES_IN=15 minutes
# You can generate JWT secrets with the `npm run generate:jwt-secret` command.
JWT_ACCESS_SECRET=accessTokenSecret JWT_ACCESS_SECRET=accessTokenSecret
JWT_REFRESH_SECRET=refreshTokenSecret JWT_REFRESH_SECRET=refreshTokenSecret
NODE_ENV=development NODE_ENV=development

View File

@ -4,13 +4,9 @@
"parserOptions": { "parserOptions": {
"project": "./tsconfig.json" "project": "./tsconfig.json"
}, },
"env": {
"node": true
},
"rules": { "rules": {
"prettier/prettier": "error", "prettier/prettier": "error",
"import/extensions": ["error", "always"], "import/extensions": ["error", "always"],
"unicorn/prevent-abbreviations": "error", "unicorn/prevent-abbreviations": "error"
"unicorn/prefer-node-protocol": "error"
} }
} }

View File

@ -1,6 +1,6 @@
<!-- Please first discuss the change you wish to make via issue before making a change. It might avoid a waste of your time. --> <!-- Please first discuss the change you wish to make via issue before making a change. It might avoid a waste of your time. -->
## What changes this PR introduce? # What changes this PR introduce?
## List any relevant issue numbers ## List any relevant issue numbers

View File

@ -16,10 +16,10 @@ jobs:
language: ['javascript'] language: ['javascript']
steps: steps:
- uses: 'actions/checkout@v3.0.0' - uses: 'actions/checkout@v3.5.3'
- name: 'Initialize CodeQL' - name: 'Initialize CodeQL'
uses: 'github/codeql-action/init@v1' uses: 'github/codeql-action/init@v2'
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}

View File

@ -10,16 +10,18 @@ jobs:
build: build:
runs-on: 'ubuntu-latest' runs-on: 'ubuntu-latest'
steps: steps:
- uses: 'actions/checkout@v3.0.0' - uses: 'actions/checkout@v3.5.3'
- name: 'Use Node.js' - name: 'Setup Node.js'
uses: 'actions/setup-node@v3.0.0' uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: '16.x' node-version: '18.x'
cache: 'npm' cache: 'npm'
- name: 'Install' - name: 'Install dependencies'
run: 'npm install' run: 'npm clean-install'
- name: 'Build' - name: 'Build'
run: 'npm run build' run: 'npm run build'
- run: 'npm run build:typescript'

View File

@ -10,16 +10,16 @@ jobs:
lint: lint:
runs-on: 'ubuntu-latest' runs-on: 'ubuntu-latest'
steps: steps:
- uses: 'actions/checkout@v3.0.0' - uses: 'actions/checkout@v3.5.3'
- name: 'Use Node.js' - name: 'Setup Node.js'
uses: 'actions/setup-node@v3.0.0' uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: '16.x' node-version: '18.x'
cache: 'npm' cache: 'npm'
- name: 'Install' - name: 'Install dependencies'
run: 'npm install' run: 'npm clean-install'
- name: 'lint:commit' - name: 'lint:commit'
run: 'npm run lint:commit -- --to "${{ github.sha }}"' run: 'npm run lint:commit -- --to "${{ github.sha }}"'
@ -30,8 +30,8 @@ jobs:
- name: 'lint:markdown' - name: 'lint:markdown'
run: 'npm run lint:markdown' run: 'npm run lint:markdown'
- name: 'lint:typescript' - name: 'lint:eslint'
run: 'npm run lint:typescript' run: 'npm run lint:eslint'
- name: 'lint:prettier' - name: 'lint:prettier'
run: 'npm run lint:prettier' run: 'npm run lint:prettier'
@ -41,7 +41,5 @@ jobs:
with: with:
github_token: ${{ secrets.github_token }} github_token: ${{ secrets.github_token }}
- name: 'lint:docker' - name: 'prisma:validate'
uses: 'hadolint/hadolint-action@v1.6.0' run: 'cp .env.example .env && npm run prisma:validate'
with:
dockerfile: './Dockerfile'

View File

@ -8,30 +8,32 @@ jobs:
release: release:
runs-on: 'ubuntu-latest' runs-on: 'ubuntu-latest'
steps: steps:
- uses: 'actions/checkout@v3.0.0' - uses: 'actions/checkout@v3.5.3'
with: with:
fetch-depth: 0 fetch-depth: 0
persist-credentials: false persist-credentials: false
- name: 'Import GPG key' - name: 'Import GPG key'
uses: 'crazy-max/ghaction-import-gpg@v3.2.0' uses: 'crazy-max/ghaction-import-gpg@v5.3.0'
with: with:
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
git-user-signingkey: true git_user_signingkey: true
git-commit-gpgsign: true git_commit_gpgsign: true
- name: 'Use Node.js' - name: 'Setup Node.js'
uses: 'actions/setup-node@v3.0.0' uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: '16.x' node-version: '18.x'
cache: 'npm' cache: 'npm'
- name: 'Install' - name: 'Install dependencies'
run: 'npm install' run: 'npm clean-install'
- name: 'Build' - name: 'Build'
run: 'npm run build' run: 'npm run build'
- run: 'npm run build:typescript'
- name: 'Release' - name: 'Release'
run: 'npm run release' run: 'npm run release'
env: env:

View File

@ -8,18 +8,18 @@ on:
jobs: jobs:
test: test:
runs-on: 'macos-latest' runs-on: 'ubuntu-latest'
steps: steps:
- uses: 'actions/checkout@v3.0.0' - uses: 'actions/checkout@v3.5.3'
- name: 'Use Node.js' - name: 'Setup Node.js'
uses: 'actions/setup-node@v3.0.0' uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: '16.x' node-version: '18.x'
cache: 'npm' cache: 'npm'
- name: 'Install' - name: 'Install dependencies'
run: 'npm install' run: 'npm clean-install'
- name: 'Build' - name: 'Build'
run: 'npm run build' run: 'npm run build'

1
.gitignore vendored
View File

@ -35,3 +35,4 @@ npm-debug.log*
# misc # misc
.DS_Store .DS_Store
*.hbs

View File

@ -3,3 +3,4 @@
npm run lint:staged npm run lint:staged
npm run build npm run build
npm run build:typescript

View File

@ -2,5 +2,6 @@
"*": ["editorconfig-checker"], "*": ["editorconfig-checker"],
"*.{js,jsx,ts,tsx}": ["prettier --write", "eslint --fix"], "*.{js,jsx,ts,tsx}": ["prettier --write", "eslint --fix"],
"*.{json,jsonc,yml,yaml}": ["prettier --write"], "*.{json,jsonc,yml,yaml}": ["prettier --write"],
"*.md": ["prettier --write", "markdownlint --dot --fix"] "*.{md,mdx}": ["prettier --write", "markdownlint-cli2 --fix"],
"prisma/schema.prisma": ["prisma validate"]
} }

11
.markdownlint-cli2.jsonc Normal file
View File

@ -0,0 +1,11 @@
{
"config": {
"extends": "markdownlint/style/prettier",
"relative-links": true,
"default": true,
"MD033": false
},
"globs": ["**/*.{md,mdx}"],
"ignores": ["**/node_modules"],
"customRules": ["markdownlint-rule-relative-links"]
}

View File

@ -1,6 +0,0 @@
{
"default": true,
"MD013": false,
"MD033": false,
"MD041": false
}

View File

@ -1,6 +0,0 @@
build
node_modules
coverage
package.json
package-lock.json
*.hbs

15
.swcrc
View File

@ -1,22 +1,13 @@
{ {
"sourceMaps": true,
"jsc": { "jsc": {
"parser": { "parser": {
"syntax": "typescript", "syntax": "typescript",
"decorators": true,
"dynamicImport": true "dynamicImport": true
}, },
"transform": { "target": "es2022"
"legacyDecorator": true,
"decoratorMetadata": true
},
"target": "es2022",
"loose": true
}, },
"module": { "module": {
"type": "es6", "type": "es6"
"strict": false,
"strictMode": true,
"lazy": false,
"noInterop": false
} }
} }

View File

@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at reported to the community leaders responsible for enforcement at
contact@divlo.fr. <contact@theoludwig.fr>.
All complaints will be reviewed and investigated promptly and fairly. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the All community leaders are obligated to respect the privacy and security of the

View File

@ -29,38 +29,14 @@ If you're adding new features to **Thream/api**, please include tests.
## Commits ## Commits
The commit message guidelines respect The commit message guidelines adheres to [Conventional Commits](https://www.conventionalcommits.org/) and [Semantic Versioning](https://semver.org/) for releases.
[@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional)
and [Semantic Versioning](https://semver.org/) for releases.
### Types
Types define which kind of changes you made to the project.
| Types | Description |
| -------- | ------------------------------------------------------------------------------------------------------------ |
| feat | A new feature. |
| fix | A bug fix. |
| docs | Documentation only changes. |
| style | Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc). |
| refactor | A code change that neither fixes a bug nor adds a feature. |
| perf | A code change that improves performance. |
| test | Adding missing tests or correcting existing tests. |
| build | Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm). |
| ci | Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs). |
| chore | Other changes that don't modify src or test files. |
| revert | Reverts a previous commit. |
### Scopes
Scopes define what part of the code changed.
### Examples ### Examples
```sh ```sh
git commit -m "feat(services): add POST /users/signup" git commit -m "feat: add POST /users/signup"
git commit -m "docs(readme): update installation process" git commit -m "docs(readme): update installation process"
git commit -m "fix(services): should emit events to connected users" git commit -m "fix: should emit events to connected users"
``` ```
## Directory Structure ## Directory Structure
@ -86,7 +62,6 @@ git commit -m "fix(services): should emit events to connected users"
- `services` : all REST API endpoints - `services` : all REST API endpoints
- `tools` : configs and utilities - `tools` : configs and utilities
- `typings` : types gloablly used in the project - `typings` : types gloablly used in the project
- `uploads` : uploaded files by users
### Services folder explained with an example ### Services folder explained with an example
@ -121,3 +96,5 @@ The folders after `src/services` : is the real path of the routes in the API exc
folders starting and ending with `__` like `__test__` or `__utils__`. folders starting and ending with `__` like `__test__` or `__utils__`.
The filenames correspond to the HTTP methods used (`get`, `post`, `put`, `delete`). The filenames correspond to the HTTP methods used (`get`, `post`, `put`, `delete`).
You can generate the boilerplate code for a new service with the `npm run generate` command.

View File

@ -1,23 +1,21 @@
FROM node:16.14.2 AS dependencies FROM node:18.16.1 AS dependencies
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY ./package*.json ./ COPY ./package*.json ./
RUN npm install RUN npm install
FROM node:16.14.2 AS builder FROM node:18.16.1 AS builder
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY --from=dependencies /usr/src/app/node_modules ./node_modules COPY --from=dependencies /usr/src/app/node_modules ./node_modules
COPY ./ ./ COPY ./ ./
RUN npm run prisma:generate && npm run build RUN npm run prisma:generate && npm run build
FROM node:16.14.2 AS runner FROM node:18.16.1 AS runner
WORKDIR /usr/src/app WORKDIR /usr/src/app
ENV NODE_ENV=production ENV NODE_ENV=production
COPY --from=builder /usr/src/app/node_modules ./node_modules COPY --from=builder /usr/src/app/node_modules ./node_modules
COPY --from=builder /usr/src/app/start.sh ./docker-start.sh
COPY --from=builder /usr/src/app/package.json ./package.json COPY --from=builder /usr/src/app/package.json ./package.json
COPY --from=builder /usr/src/app/email ./email COPY --from=builder /usr/src/app/email ./email
COPY --from=builder /usr/src/app/build ./build COPY --from=builder /usr/src/app/build ./build
COPY --from=builder /usr/src/app/prisma ./prisma COPY --from=builder /usr/src/app/prisma ./prisma
COPY --from=builder /usr/src/app/uploads ./uploads
USER node USER node
CMD ["./docker-start.sh"] CMD ["node", "build/index.js"]

View File

@ -18,7 +18,7 @@
Thream's Application Programming Interface (API) to stay close with your friends and communities. Thream's Application Programming Interface (API) to stay close with your friends and communities.
It uses [Thream/file-uploads-api](https://github.com/Thream/file-uploads-api) [v1.0.0](https://github.com/Thream/file-uploads-api/releases/tag/v1.0.0). It uses [Thream/file-uploads-api](https://github.com/Thream/file-uploads-api) [v1.1.5](https://github.com/Thream/file-uploads-api/releases/tag/v1.1.5).
## ⚙️ Getting Started ## ⚙️ Getting Started
@ -32,7 +32,7 @@ It uses [Thream/file-uploads-api](https://github.com/Thream/file-uploads-api) [v
```sh ```sh
# Clone the repository # Clone the repository
git clone https://github.com/Thream/api.git git clone git@github.com:Thream/api.git
# Go to the project root # Go to the project root
cd api cd api
@ -42,6 +42,7 @@ cp .env.example .env
# Install # Install
npm install npm install
npm run prisma:generate
``` ```
You will need to configure the environment variables by creating an `.env` file at You will need to configure the environment variables by creating an `.env` file at
@ -49,6 +50,8 @@ the root of the project (see `.env.example`).
### Local Development environment ### Local Development environment
Recommended to use [VSCode: Remote development in Containers](https://code.visualstudio.com/docs/remote/containers-tutorial).
#### Setup the database #### Setup the database
```sh ```sh
@ -76,18 +79,6 @@ npm run dev
npm run prisma:studio npm run prisma:studio
``` ```
### Production environment with [Docker](https://www.docker.com/)
```sh
# Setup and run all the services for you
docker-compose up --build
```
#### Services started
- API : `http://localhost:8080`
- [PostgreSQL database](https://www.postgresql.org/)
## 💡 Contributing ## 💡 Contributing
Anyone can help to improve the project, submit a Feature Request, a bug report or Anyone can help to improve the project, submit a Feature Request, a bug report or

View File

@ -1,29 +0,0 @@
version: '3.0'
services:
thream-api:
container_name: ${COMPOSE_PROJECT_NAME}
build:
context: './'
env_file:
- '.env'
ports:
- '${PORT}:${PORT}'
depends_on:
- 'thream-database'
volumes:
- './uploads:/usr/src/app/uploads'
restart: 'unless-stopped'
thream-database:
container_name: 'thream-database'
image: 'postgres:14.2'
environment:
POSTGRES_USER: 'user'
POSTGRES_PASSWORD: 'password'
POSTGRES_DB: 'thream'
volumes:
- 'database-volume:/var/lib/postgresql/data'
restart: 'unless-stopped'
volumes:
database-volume:

View File

@ -1,4 +0,0 @@
#!/bin/bash
npm run prisma:migrate:deploy
node build/index.js

View File

@ -22,7 +22,7 @@ export const serviceGenerator = {
type: 'list', type: 'list',
name: 'tag', name: 'tag',
message: 'tag', message: 'tag',
choices: ['users', 'guilds', 'channels', 'messages', 'members', 'uploads'] choices: ['users', 'oauth2', 'guilds', 'channels', 'messages', 'members']
}, },
{ {
type: 'confirm', type: 'confirm',

24438
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,101 +1,112 @@
{ {
"name": "@thream/api", "name": "@thream/api",
"version": "1.0.1", "version": "1.2.7",
"description": "Thream's application programming interface to stay close with your friends and communities.", "description": "Thream's application programming interface to stay close with your friends and communities.",
"private": true, "private": true,
"type": "module", "type": "module",
"repository": { "imports": {
"type": "git", "#src/*": "./build/*"
"url": "https://github.com/Thream/api"
}, },
"engines": { "engines": {
"node": ">=16.0.0", "node": ">=16.0.0",
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"repository": {
"type": "git",
"url": "https://github.com/Thream/api"
},
"scripts": { "scripts": {
"build": "rimraf ./build && swc ./src --out-dir ./build && tsc", "build": "rimraf ./build && swc ./src --out-dir ./build",
"build:dev": "swc ./src --out-dir ./build --watch", "build:typescript": "tsc",
"start": "node build/index.js", "start": "node --enable-source-maps build/index.js",
"dev": "concurrently -k -n \"TypeScript,Node\" -p \"[{name}]\" -c \"blue,green\" \"npm run build:dev\" \"cross-env NODE_ENV=development nodemon build/index.js\"", "dev:build": "swc ./src --out-dir ./build --watch",
"dev": "concurrently --kill-others --names \"TypeScript,Node,Maildev,Prisma Studio\" \"npm run dev:build\" \"cross-env NODE_ENV=development node --watch --enable-source-maps build/index.js\" \"npm run maildev\" \"npm run prisma:studio\"",
"maildev": "maildev",
"generate": "plop", "generate": "plop",
"generate:jwt-secret": "node --enable-source-maps ./build/scripts/generate-jwt-secret.js",
"lint:commit": "commitlint", "lint:commit": "commitlint",
"lint:editorconfig": "editorconfig-checker", "lint:editorconfig": "editorconfig-checker",
"lint:markdown": "markdownlint \"**/*.md\" --dot --ignore-path \".gitignore\"", "lint:markdown": "markdownlint-cli2",
"lint:typescript": "eslint \"**/*.{js,jsx,ts,tsx}\" --ignore-path \".gitignore\"", "lint:eslint": "eslint . --ignore-path .gitignore",
"lint:prettier": "prettier \".\" --check", "lint:prettier": "prettier . --check --ignore-path .gitignore",
"lint:staged": "lint-staged", "lint:staged": "lint-staged",
"test": "cross-env NODE_ENV=test c8 tap", "test": "cross-env NODE_ENV=test c8 tap",
"prisma:validate": "prisma validate",
"prisma:generate": "prisma generate", "prisma:generate": "prisma generate",
"prisma:studio": "prisma studio", "prisma:studio": "prisma studio --browser=none",
"prisma:migrate:dev": "prisma migrate dev", "prisma:migrate:dev": "prisma migrate dev",
"prisma:migrate:deploy": "prisma migrate deploy", "prisma:migrate:deploy": "prisma migrate deploy",
"release": "semantic-release", "release": "semantic-release",
"postinstall": "husky install" "postinstall": "husky install"
}, },
"dependencies": { "dependencies": {
"@prisma/client": "3.12.0", "@fastify/cors": "8.3.0",
"@sinclair/typebox": "0.23.4", "@fastify/helmet": "11.0.0",
"@thream/socketio-jwt": "3.0.0", "@fastify/multipart": "7.7.0",
"axios": "0.26.1", "@fastify/rate-limit": "8.0.1",
"@fastify/sensible": "5.2.0",
"@fastify/swagger": "8.6.0",
"@fastify/swagger-ui": "1.9.2",
"@prisma/client": "4.16.2",
"@sinclair/typebox": "0.29.0",
"@thream/socketio-jwt": "3.1.1",
"axios": "1.4.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"dotenv": "16.0.0", "dotenv": "16.3.1",
"ejs": "3.1.6", "ejs": "3.1.9",
"fastify": "3.28.0", "fastify": "4.19.1",
"fastify-cors": "6.0.3", "fastify-plugin": "4.5.0",
"fastify-helmet": "7.0.1",
"fastify-multipart": "5.3.1",
"fastify-plugin": "3.0.1",
"fastify-rate-limit": "5.8.0",
"fastify-sensible": "3.1.2",
"fastify-swagger": "5.1.0",
"form-data": "4.0.0", "form-data": "4.0.0",
"http-errors": "2.0.0", "http-errors": "2.0.0",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "9.0.0",
"ms": "2.1.3", "ms": "2.1.3",
"nodemailer": "6.7.3", "nodemailer": "6.9.3",
"read-pkg": "7.1.0", "read-pkg": "8.0.0",
"socket.io": "4.4.1" "socket.io": "4.7.1"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "16.2.3", "@commitlint/cli": "17.6.6",
"@commitlint/config-conventional": "16.2.1", "@commitlint/config-conventional": "17.6.6",
"@saithodev/semantic-release-backmerge": "2.1.2", "@saithodev/semantic-release-backmerge": "3.2.0",
"@semantic-release/git": "10.0.1", "@semantic-release/git": "10.0.1",
"@swc/cli": "0.1.57", "@swc/cli": "0.1.62",
"@swc/core": "1.2.164", "@swc/core": "1.3.67",
"@tsconfig/strictest": "2.0.1",
"@types/bcryptjs": "2.4.2", "@types/bcryptjs": "2.4.2",
"@types/busboy": "1.5.0", "@types/busboy": "1.5.0",
"@types/ejs": "3.1.0", "@types/ejs": "3.1.2",
"@types/http-errors": "1.8.2", "@types/http-errors": "2.0.1",
"@types/jsonwebtoken": "8.5.8", "@types/jsonwebtoken": "9.0.2",
"@types/ms": "0.7.31", "@types/ms": "0.7.31",
"@types/node": "17.0.23", "@types/node": "20.3.3",
"@types/nodemailer": "6.4.4", "@types/nodemailer": "6.4.8",
"@types/sinon": "10.0.11", "@types/sinon": "10.0.15",
"@types/tap": "15.0.6", "@types/tap": "15.0.8",
"@typescript-eslint/eslint-plugin": "5.18.0", "@typescript-eslint/eslint-plugin": "5.60.1",
"c8": "7.11.0", "@typescript-eslint/parser": "5.60.1",
"concurrently": "7.1.0", "c8": "8.0.0",
"concurrently": "8.2.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"editorconfig-checker": "4.0.2", "editorconfig-checker": "5.1.1",
"eslint": "8.12.0", "eslint": "8.44.0",
"eslint-config-conventions": "2.0.0", "eslint-config-conventions": "10.0.0",
"eslint-config-prettier": "8.5.0", "eslint-config-prettier": "8.8.0",
"eslint-plugin-import": "2.26.0", "eslint-plugin-import": "2.27.5",
"eslint-plugin-prettier": "4.0.0", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-promise": "6.0.0", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-unicorn": "42.0.0", "eslint-plugin-unicorn": "47.0.0",
"husky": "7.0.4", "husky": "8.0.3",
"lint-staged": "12.3.7", "lint-staged": "13.2.3",
"markdownlint-cli": "0.31.1", "maildev": "2.0.5",
"nodemon": "2.0.15", "markdownlint-cli2": "0.8.1",
"plop": "3.0.5", "markdownlint-rule-relative-links": "2.1.0",
"prettier": "2.6.2", "plop": "3.1.2",
"prisma": "3.12.0", "prettier": "2.8.8",
"rimraf": "3.0.2", "prisma": "4.16.2",
"semantic-release": "19.0.2", "rimraf": "5.0.1",
"sinon": "13.0.1", "semantic-release": "21.0.6",
"tap": "16.0.1", "sinon": "15.2.0",
"typescript": "4.6.3" "tap": "16.3.7",
"typescript": "5.0.4"
} }
} }

View File

@ -1,8 +1,9 @@
import { User } from '@prisma/client' import type { User } from '@prisma/client'
import sinon from 'sinon' import sinon from 'sinon'
import { refreshTokenExample } from '../../models/RefreshToken.js' import { refreshTokenExample } from '../../models/RefreshToken.js'
import { userExample, UserJWT } from '../../models/User.js' import type { UserJWT } from '../../models/User.js'
import { userExample } from '../../models/User.js'
import { userSettingsExample } from '../../models/UserSettings.js' import { userSettingsExample } from '../../models/UserSettings.js'
import { import {
generateAccessToken, generateAccessToken,

View File

@ -1,21 +1,27 @@
import dotenv from 'dotenv' import dotenv from 'dotenv'
import fastify from 'fastify' import fastify from 'fastify'
import fastifyCors from 'fastify-cors' import fastifyCors from '@fastify/cors'
import fastifySwagger from 'fastify-swagger' import fastifySwagger from '@fastify/swagger'
import fastifyHelmet from 'fastify-helmet' import fastifySwaggerUI from '@fastify/swagger-ui'
import fastifyRateLimit from 'fastify-rate-limit' import fastifyHelmet from '@fastify/helmet'
import fastifySensible from 'fastify-sensible' import fastifyRateLimit from '@fastify/rate-limit'
import fastifySensible from '@fastify/sensible'
import { readPackage } from 'read-pkg'
import { services } from './services/index.js' import { services } from './services/index.js'
import { swaggerOptions } from './tools/configurations/swaggerOptions.js'
import fastifySocketIo from './tools/plugins/socket-io.js' import fastifySocketIo from './tools/plugins/socket-io.js'
dotenv.config() dotenv.config()
const packageJSON = await readPackage()
export const application = fastify({ export const application = fastify({
logger: process.env.NODE_ENV === 'development', logger: process.env['NODE_ENV'] === 'development',
ajv: { ajv: {
customOptions: { customOptions: {
format: 'full' strict: 'log',
keywords: ['kind', 'modifier'],
formats: {
full: true
}
} }
} }
}) })
@ -35,5 +41,35 @@ await application.register(fastifyRateLimit, {
max: 200, max: 200,
timeWindow: '1 minute' timeWindow: '1 minute'
}) })
await application.register(fastifySwagger, swaggerOptions) await application.register(fastifySwagger, {
openapi: {
info: {
title: packageJSON.name,
description: packageJSON.description,
version: packageJSON.version
},
tags: [
{ name: 'users' },
{ name: 'oauth2' },
{ name: 'guilds' },
{ name: 'channels' },
{ name: 'messages' },
{ name: 'members' }
],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT'
}
}
}
},
hideUntagged: true
})
await application.register(fastifySwaggerUI, {
routePrefix: '/documentation',
staticCSP: true
})
await application.register(services) await application.register(services)

View File

@ -1,5 +1,8 @@
import { application } from './application.js' import { application } from './application.js'
import { HOST, PORT } from './tools/configurations/index.js' import { HOST, PORT } from '#src/tools/configurations.js'
const address = await application.listen(PORT, HOST) const address = await application.listen({
port: PORT,
host: HOST
})
console.log('\u001B[36m%s\u001B[0m', `🚀 Server listening at ${address}`) console.log('\u001B[36m%s\u001B[0m', `🚀 Server listening at ${address}`)

View File

@ -1,5 +1,5 @@
import { Type } from '@sinclair/typebox' import { Type } from '@sinclair/typebox'
import { Channel } from '@prisma/client' import type { Channel } from '@prisma/client'
import { date, id } from './utils.js' import { date, id } from './utils.js'
import { guildExample } from './Guild.js' import { guildExample } from './Guild.js'

View File

@ -1,4 +1,4 @@
import { Guild } from '@prisma/client' import type { Guild } from '@prisma/client'
import { Type } from '@sinclair/typebox' import { Type } from '@sinclair/typebox'
import { date, id } from './utils.js' import { date, id } from './utils.js'

View File

@ -1,5 +1,5 @@
import { Type } from '@sinclair/typebox' import { Type } from '@sinclair/typebox'
import { Member } from '@prisma/client' import type { Member } from '@prisma/client'
import { date, id } from './utils.js' import { date, id } from './utils.js'
import { guildExample } from './Guild.js' import { guildExample } from './Guild.js'

View File

@ -1,4 +1,4 @@
import { Message } from '@prisma/client' import type { Message } from '@prisma/client'
import { Type } from '@sinclair/typebox' import { Type } from '@sinclair/typebox'
import { date, id } from './utils.js' import { date, id } from './utils.js'
@ -14,8 +14,7 @@ export const messageSchema = {
type: Type.Union(types, { default: 'text' }), type: Type.Union(types, { default: 'text' }),
mimetype: Type.String({ mimetype: Type.String({
maxLength: 127, maxLength: 127,
default: 'text/plain', default: 'text/plain'
format: 'mimetype'
}), }),
createdAt: date.createdAt, createdAt: date.createdAt,
updatedAt: date.updatedAt, updatedAt: date.updatedAt,

View File

@ -5,15 +5,15 @@ import { date, id } from './utils.js'
export const providers = ['Google', 'GitHub', 'Discord'] as const export const providers = ['Google', 'GitHub', 'Discord'] as const
export const strategies = [...providers, 'Local'] as const export const strategies = [...providers, 'Local'] as const
export const strategiesTypebox = strategies.map((strategy) => export const strategiesTypebox = strategies.map((strategy) => {
Type.Literal(strategy) return Type.Literal(strategy)
) })
export const providersTypebox = providers.map((provider) => export const providersTypebox = providers.map((provider) => {
Type.Literal(provider) return Type.Literal(provider)
) })
export type ProviderOAuth = typeof providers[number] export type ProviderOAuth = (typeof providers)[number]
export type AuthenticationStrategy = typeof strategies[number] export type AuthenticationStrategy = (typeof strategies)[number]
export const oauthSchema = { export const oauthSchema = {
id, id,

View File

@ -1,4 +1,4 @@
import { RefreshToken } from '@prisma/client' import type { RefreshToken } from '@prisma/client'
import { Type } from '@sinclair/typebox' import { Type } from '@sinclair/typebox'
import { userExample } from './User.js' import { userExample } from './User.js'
@ -6,7 +6,7 @@ import { date, id } from './utils.js'
export const refreshTokensSchema = { export const refreshTokensSchema = {
id, id,
token: Type.String(), token: Type.String({ format: 'uuid' }),
createdAt: date.createdAt, createdAt: date.createdAt,
updatedAt: date.updatedAt, updatedAt: date.updatedAt,
userId: id userId: id
@ -15,7 +15,7 @@ export const refreshTokensSchema = {
export const refreshTokenExample: RefreshToken = { export const refreshTokenExample: RefreshToken = {
id: 1, id: 1,
userId: userExample.id, userId: userExample.id,
token: 'sometoken', token: 'sometokenUUID',
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date() updatedAt: new Date()
} }

View File

@ -1,7 +1,9 @@
import { User } from '@prisma/client' import type { User } from '@prisma/client'
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { Type } from '@sinclair/typebox'
import { AuthenticationStrategy, strategiesTypebox } from './OAuth.js' import type { AuthenticationStrategy } from './OAuth.js'
import { strategiesTypebox } from './OAuth.js'
import { userSettingsSchema } from './UserSettings.js' import { userSettingsSchema } from './UserSettings.js'
import { date, id } from './utils.js' import { date, id } from './utils.js'
@ -10,6 +12,10 @@ export interface UserJWT {
currentStrategy: AuthenticationStrategy currentStrategy: AuthenticationStrategy
} }
export interface UserRefreshJWT extends UserJWT {
tokenUUID: string
}
export interface UserRequest { export interface UserRequest {
current: User current: User
currentStrategy: AuthenticationStrategy currentStrategy: AuthenticationStrategy

View File

@ -1,5 +1,6 @@
import { UserSetting } from '@prisma/client' import type { UserSetting } from '@prisma/client'
import { Type, Static } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { Type } from '@sinclair/typebox'
import { date, id } from './utils.js' import { date, id } from './utils.js'

View File

@ -34,7 +34,7 @@ export const fastifyErrorsSchema = {
404: { 404: {
statusCode: Type.Literal(404), statusCode: Type.Literal(404),
error: Type.Literal('Not Found'), error: Type.Literal('Not Found'),
message: Type.Literal('Not Found') message: Type.String()
}, },
431: { 431: {
statusCode: Type.Literal(431), statusCode: Type.Literal(431),

View File

@ -0,0 +1,3 @@
import crypto from 'node:crypto'
console.log(crypto.randomBytes(256).toString('base64'))

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../../tools/database/prisma.js' import prisma from '../../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../../models/utils.js' import { fastifyErrors } from '../../../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../../tools/database/prisma.js' import prisma from '../../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../../models/utils.js' import { fastifyErrors } from '../../../../models/utils.js'

View File

@ -1,6 +1,7 @@
import { Type, Static } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import fastifyMultipart from 'fastify-multipart' import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import fastifyMultipart from '@fastify/multipart'
import prisma from '../../../../../tools/database/prisma.js' import prisma from '../../../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'

View File

@ -1,4 +1,4 @@
import { FastifyPluginAsync } from 'fastify' import type { FastifyPluginAsync } from 'fastify'
import { deleteChannelService } from './[channelId]/delete.js' import { deleteChannelService } from './[channelId]/delete.js'
import { getChannelByIdService } from './[channelId]/get.js' import { getChannelByIdService } from './[channelId]/get.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../../tools/database/prisma.js' import prisma from '../../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../../models/utils.js' import { fastifyErrors } from '../../../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../../tools/database/prisma.js' import prisma from '../../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../../models/utils.js' import { fastifyErrors } from '../../../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'

View File

@ -1,6 +1,7 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import fastifyMultipart from 'fastify-multipart' import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import fastifyMultipart from '@fastify/multipart'
import authenticateUser from '../../../../tools/plugins/authenticateUser.js' import authenticateUser from '../../../../tools/plugins/authenticateUser.js'
import { fastifyErrors } from '../../../../models/utils.js' import { fastifyErrors } from '../../../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../../tools/database/prisma.js' import prisma from '../../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../../models/utils.js' import { fastifyErrors } from '../../../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../../../tools/database/prisma.js' import prisma from '../../../../../tools/database/prisma.js'
import { import {

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../../../tools/database/prisma.js' import prisma from '../../../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Type, Static } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../tools/database/prisma.js' import prisma from '../../tools/database/prisma.js'
import { fastifyErrors, id } from '../../models/utils.js' import { fastifyErrors, id } from '../../models/utils.js'

View File

@ -1,4 +1,4 @@
import { FastifyPluginAsync } from 'fastify' import type { FastifyPluginAsync } from 'fastify'
import { getGuilds } from './get.js' import { getGuilds } from './get.js'
import { postGuilds } from './post.js' import { postGuilds } from './post.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../tools/database/prisma.js' import prisma from '../../tools/database/prisma.js'
import { fastifyErrors } from '../../models/utils.js' import { fastifyErrors } from '../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'
@ -18,7 +19,8 @@ const querySchema = Type.Object({
export type QuerySchemaType = Static<typeof querySchema> export type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'GET all the public guilds.', description:
'GET all the public guilds (ordered by descending members count).',
tags: ['guilds'] as string[], tags: ['guilds'] as string[],
security: [ security: [
{ {
@ -55,7 +57,11 @@ export const getGuildsPublic: FastifyPluginAsync = async (fastify) => {
} }
const guildsRequest = await prisma.guild.findMany({ const guildsRequest = await prisma.guild.findMany({
...getPaginationOptions(request.query), ...getPaginationOptions(request.query),
orderBy: { createdAt: 'desc' }, orderBy: {
members: {
_count: 'desc'
}
},
...(request.query.search != null && { ...(request.query.search != null && {
where: { where: {
name: { contains: request.query.search } name: { contains: request.query.search }

View File

@ -1,4 +1,4 @@
import { FastifyPluginAsync } from 'fastify' import type { FastifyPluginAsync } from 'fastify'
import { usersService } from './users/index.js' import { usersService } from './users/index.js'
import { guildsService } from './guilds/index.js' import { guildsService } from './guilds/index.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'

View File

@ -1,4 +1,4 @@
import { FastifyPluginAsync } from 'fastify' import type { FastifyPluginAsync } from 'fastify'
import { deleteMessageService } from './[messageId]/delete.js' import { deleteMessageService } from './[messageId]/delete.js'
import { putMessageService } from './[messageId]/put.js' import { putMessageService } from './[messageId]/put.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'
@ -50,6 +51,7 @@ export const getUserById: FastifyPluginAsync = async (fastify) => {
id: true, id: true,
name: true, name: true,
email: settings.isPublicEmail, email: settings.isPublicEmail,
isConfirmed: true,
logo: true, logo: true,
status: true, status: true,
biography: true, biography: true,

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'
@ -53,7 +54,7 @@ export const getConfirmEmail: FastifyPluginAsync = async (fastify) => {
reply.statusCode = 200 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!'
} }
await reply.redirect(redirectURI) return await reply.redirect(redirectURI)
} }
}) })
} }

View File

@ -1,4 +1,4 @@
import { FastifyPluginAsync, FastifySchema } from 'fastify' import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'

View File

@ -1,6 +1,6 @@
import { Type } from '@sinclair/typebox' import { Type } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import fastifyMultipart from 'fastify-multipart' import fastifyMultipart from '@fastify/multipart'
import authenticateUser from '../../../../tools/plugins/authenticateUser.js' import authenticateUser from '../../../../tools/plugins/authenticateUser.js'
import { fastifyErrors } from '../../../../models/utils.js' import { fastifyErrors } from '../../../../models/utils.js'

View File

@ -1,15 +1,16 @@
import { randomUUID } from 'node:crypto' import { randomUUID } from 'node:crypto'
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'
import authenticateUser from '../../../tools/plugins/authenticateUser.js' import authenticateUser from '../../../tools/plugins/authenticateUser.js'
import { userCurrentSchema, userSchema } from '../../../models/User.js' import { userCurrentSchema, userSchema } from '../../../models/User.js'
import { sendEmail } from '../../../tools/email/sendEmail.js' import { sendEmail } from '../../../tools/email/sendEmail.js'
import { API_URL } from '../../../tools/configurations/index.js' import { API_URL } from '../../../tools/configurations.js'
import { Language, Theme } from '../../../models/UserSettings.js' import type { Language, Theme } from '../../../models/UserSettings.js'
import { parseStringNullish } from '../../../tools/utils/parseStringNullish.js' import { parseStringNullish } from '../../../tools/utils/parseStringNullish.js'
const bodyPutServiceSchema = Type.Object({ const bodyPutServiceSchema = Type.Object({
@ -136,7 +137,9 @@ export const putCurrentUser: FastifyPluginAsync = async (fastify) => {
}) })
await fastify.io.emitToAuthorizedUsers({ await fastify.io.emitToAuthorizedUsers({
event: 'users', event: 'users',
isAuthorizedCallback: () => true, isAuthorizedCallback: () => {
return true
},
payload: { payload: {
action: 'update', action: 'update',
item: user item: user

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../../tools/database/prisma.js' import prisma from '../../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../../models/utils.js' import { fastifyErrors } from '../../../../models/utils.js'

View File

@ -1,4 +1,4 @@
import { FastifyPluginAsync } from 'fastify' import type { FastifyPluginAsync } from 'fastify'
import { postSignupUser } from './signup/post.js' import { postSignupUser } from './signup/post.js'
import { getConfirmEmail } from './confirm-email/get.js' import { getConfirmEmail } from './confirm-email/get.js'

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../../tools/database/prisma.js' import prisma from '../../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../../models/utils.js' import { fastifyErrors } from '../../../../models/utils.js'
@ -14,7 +15,7 @@ type Parameters = Static<typeof parametersSchema>
const deleteServiceSchema: FastifySchema = { const deleteServiceSchema: FastifySchema = {
description: 'DELETE a provider to authenticate with for a user.', description: 'DELETE a provider to authenticate with for a user.',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
security: [ security: [
{ {
bearerAuth: [] bearerAuth: []
@ -55,7 +56,9 @@ export const deleteProviderService: FastifyPluginAsync = async (fastify) => {
if (user.current.password != null) { if (user.current.password != null) {
strategies.push('Local') strategies.push('Local')
} }
const oauthProvider = OAuths.find((oauth) => oauth.provider === provider) const oauthProvider = OAuths.find((oauth) => {
return oauth.provider === provider
})
if (oauthProvider == null) { if (oauthProvider == null) {
throw fastify.httpErrors.notFound('You are not using this provider') throw fastify.httpErrors.notFound('You are not using this provider')
} }

View File

@ -7,9 +7,9 @@ import { OAuthStrategy } from '../../../../../tools/utils/OAuthStrategy.js'
export const DISCORD_PROVIDER = 'Discord' export const DISCORD_PROVIDER = 'Discord'
export const DISCORD_BASE_URL = 'https://discord.com/api/v10' export const DISCORD_BASE_URL = 'https://discord.com/api/v10'
export const DISCORD_CLIENT_ID = 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 = 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 const discordStrategy = new OAuthStrategy(DISCORD_PROVIDER)
export interface DiscordUser { export interface DiscordUser {

View File

@ -1,7 +1,8 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js' import { API_URL } from '../../../../../tools/configurations.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'
import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from '../__utils__/utils.js' import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from '../__utils__/utils.js'
import authenticateUser from '../../../../../tools/plugins/authenticateUser.js' import authenticateUser from '../../../../../tools/plugins/authenticateUser.js'
@ -14,7 +15,7 @@ type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'Discord OAuth2 - add-strategy', description: 'Discord OAuth2 - add-strategy',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
security: [ security: [
{ {
bearerAuth: [] bearerAuth: []

View File

@ -1,7 +1,8 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js' import { API_URL } from '../../../../../tools/configurations.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'
import { discordStrategy, getDiscordUserData } from '../__utils__/utils.js' import { discordStrategy, getDiscordUserData } from '../__utils__/utils.js'
import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js' import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js'
@ -17,7 +18,7 @@ type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'Discord OAuth2 - callback-add-strategy', description: 'Discord OAuth2 - callback-add-strategy',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
querystring: querySchema, querystring: querySchema,
response: { response: {
200: Type.String(), 200: Type.String(),

View File

@ -1,7 +1,8 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js' import { API_URL } from '../../../../../tools/configurations.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'
import { discordStrategy, getDiscordUserData } from '../__utils__/utils.js' import { discordStrategy, getDiscordUserData } from '../__utils__/utils.js'
import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js' import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js'
@ -15,7 +16,7 @@ type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'Discord OAuth2 - callback', description: 'Discord OAuth2 - callback',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
querystring: querySchema, querystring: querySchema,
response: { response: {
200: Type.String(), 200: Type.String(),

View File

@ -1,7 +1,8 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js' import { API_URL } from '../../../../../tools/configurations.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'
import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from '../__utils__/utils.js' import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from '../__utils__/utils.js'
@ -13,7 +14,7 @@ type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'Discord OAuth2 - signin', description: 'Discord OAuth2 - signin',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
querystring: querySchema, querystring: querySchema,
response: { response: {
200: Type.String(), 200: Type.String(),

View File

@ -8,9 +8,9 @@ export const GITHUB_PROVIDER = 'GitHub'
export const GITHUB_BASE_URL = 'https://github.com' export const GITHUB_BASE_URL = 'https://github.com'
export const GITHUB_API_BASE_URL = 'https://api.github.com' export const GITHUB_API_BASE_URL = 'https://api.github.com'
export const GITHUB_CLIENT_ID = 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 = 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 const githubStrategy = new OAuthStrategy(GITHUB_PROVIDER)
export interface GitHubUser { export interface GitHubUser {

View File

@ -1,7 +1,8 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js' import { API_URL } from '../../../../../tools/configurations.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'
import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from '../__utils__/utils.js' import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from '../__utils__/utils.js'
import authenticateUser from '../../../../../tools/plugins/authenticateUser.js' import authenticateUser from '../../../../../tools/plugins/authenticateUser.js'
@ -14,7 +15,7 @@ type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'GitHub OAuth2 - add-strategy', description: 'GitHub OAuth2 - add-strategy',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
security: [ security: [
{ {
bearerAuth: [] bearerAuth: []

View File

@ -1,7 +1,8 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js' import { API_URL } from '../../../../../tools/configurations.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'
import { githubStrategy, getGitHubUserData } from '../__utils__/utils.js' import { githubStrategy, getGitHubUserData } from '../__utils__/utils.js'
import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js' import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js'
@ -17,7 +18,7 @@ type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'GitHub OAuth2 - callback-add-strategy', description: 'GitHub OAuth2 - callback-add-strategy',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
querystring: querySchema, querystring: querySchema,
response: { response: {
200: Type.String(), 200: Type.String(),

View File

@ -1,7 +1,8 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js' import { API_URL } from '../../../../../tools/configurations.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'
import { githubStrategy, getGitHubUserData } from '../__utils__/utils.js' import { githubStrategy, getGitHubUserData } from '../__utils__/utils.js'
import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js' import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js'
@ -15,7 +16,7 @@ type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'GitHub OAuth2 - callback', description: 'GitHub OAuth2 - callback',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
querystring: querySchema, querystring: querySchema,
response: { response: {
200: Type.String(), 200: Type.String(),

View File

@ -1,7 +1,8 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js' import { API_URL } from '../../../../../tools/configurations.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'
import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from '../__utils__/utils.js' import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from '../__utils__/utils.js'
@ -13,7 +14,7 @@ type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'GitHub OAuth2 - signin', description: 'GitHub OAuth2 - signin',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
querystring: querySchema, querystring: querySchema,
response: { response: {
200: Type.String(), 200: Type.String(),

View File

@ -10,9 +10,9 @@ export const GOOGLE_OAUTH2_TOKEN = 'https://oauth2.googleapis.com/token'
export const GOOGLE_USERINFO = 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 = 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 = 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 const googleStrategy = new OAuthStrategy(GOOGLE_PROVIDER)
export interface GoogleUser { export interface GoogleUser {

View File

@ -1,7 +1,8 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js' import { API_URL } from '../../../../../tools/configurations.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'
import { GOOGLE_BASE_URL, GOOGLE_CLIENT_ID } from '../__utils__/utils.js' import { GOOGLE_BASE_URL, GOOGLE_CLIENT_ID } from '../__utils__/utils.js'
import authenticateUser from '../../../../../tools/plugins/authenticateUser.js' import authenticateUser from '../../../../../tools/plugins/authenticateUser.js'
@ -14,7 +15,7 @@ type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'Google OAuth2 - add-strategy', description: 'Google OAuth2 - add-strategy',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
security: [ security: [
{ {
bearerAuth: [] bearerAuth: []

View File

@ -1,7 +1,8 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js' import { API_URL } from '../../../../../tools/configurations.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'
import { googleStrategy, getGoogleUserData } from '../__utils__/utils.js' import { googleStrategy, getGoogleUserData } from '../__utils__/utils.js'
import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js' import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js'
@ -17,7 +18,7 @@ type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'Google OAuth2 - callback-add-strategy', description: 'Google OAuth2 - callback-add-strategy',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
querystring: querySchema, querystring: querySchema,
response: { response: {
200: Type.String(), 200: Type.String(),

View File

@ -1,7 +1,8 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js' import { API_URL } from '../../../../../tools/configurations.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'
import { googleStrategy, getGoogleUserData } from '../__utils__/utils.js' import { googleStrategy, getGoogleUserData } from '../__utils__/utils.js'
import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js' import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js'
@ -15,7 +16,7 @@ type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'Google OAuth2 - callback', description: 'Google OAuth2 - callback',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
querystring: querySchema, querystring: querySchema,
response: { response: {
200: Type.String(), 200: Type.String(),

View File

@ -1,7 +1,8 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js' import { API_URL } from '../../../../../tools/configurations.js'
import { fastifyErrors } from '../../../../../models/utils.js' import { fastifyErrors } from '../../../../../models/utils.js'
import { GOOGLE_BASE_URL, GOOGLE_CLIENT_ID } from '../__utils__/utils.js' import { GOOGLE_BASE_URL, GOOGLE_CLIENT_ID } from '../__utils__/utils.js'
@ -13,7 +14,7 @@ type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = { const getServiceSchema: FastifySchema = {
description: 'Google OAuth2 - signin', description: 'Google OAuth2 - signin',
tags: ['users'] as string[], tags: ['oauth2'] as string[],
querystring: querySchema, querystring: querySchema,
response: { response: {
200: Type.String(), 200: Type.String(),

View File

@ -1,5 +1,6 @@
import tap from 'tap' import tap from 'tap'
import sinon from 'sinon' import sinon from 'sinon'
import jwt from 'jsonwebtoken'
import { application } from '../../../../application.js' import { application } from '../../../../application.js'
import { authenticateUserTest } from '../../../../__test__/utils/authenticateUserTest.js' import { authenticateUserTest } from '../../../../__test__/utils/authenticateUserTest.js'
@ -13,8 +14,7 @@ await tap.test('POST /users/refresh-token', async (t) => {
}) })
await t.test('succeeds', async (t) => { await t.test('succeeds', async (t) => {
const { accessToken, refreshToken, refreshTokenStubValue } = const { refreshToken, refreshTokenStubValue } = await authenticateUserTest()
await authenticateUserTest()
sinon.stub(prisma, 'refreshToken').value({ sinon.stub(prisma, 'refreshToken').value({
...refreshTokenStubValue, ...refreshTokenStubValue,
findFirst: async () => { findFirst: async () => {
@ -28,9 +28,6 @@ await tap.test('POST /users/refresh-token', async (t) => {
const response = await application.inject({ const response = await application.inject({
method: 'POST', method: 'POST',
url: '/users/refresh-token', url: '/users/refresh-token',
headers: {
authorization: `Bearer ${accessToken}`
},
payload: { refreshToken } payload: { refreshToken }
}) })
const responseJson = response.json() const responseJson = response.json()
@ -62,6 +59,9 @@ await tap.test('POST /users/refresh-token', async (t) => {
return refreshTokenExample return refreshTokenExample
} }
}) })
sinon.stub(jwt, 'verify').value(() => {
throw new Error('Invalid token')
})
const response = await application.inject({ const response = await application.inject({
method: 'POST', method: 'POST',
url: '/users/refresh-token', url: '/users/refresh-token',

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import jwt from 'jsonwebtoken' import jwt from 'jsonwebtoken'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
@ -9,8 +10,8 @@ import {
jwtSchema, jwtSchema,
expiresIn expiresIn
} from '../../../tools/utils/jwtToken.js' } from '../../../tools/utils/jwtToken.js'
import { UserJWT } from '../../../models/User.js' import type { UserRefreshJWT } from '../../../models/User.js'
import { JWT_REFRESH_SECRET } from '../../../tools/configurations/index.js' import { JWT_REFRESH_SECRET } from '../../../tools/configurations.js'
const bodyPostRefreshTokenSchema = Type.Object({ const bodyPostRefreshTokenSchema = Type.Object({
refreshToken: jwtSchema.refreshToken refreshToken: jwtSchema.refreshToken
@ -43,20 +44,20 @@ export const postRefreshTokenUser: FastifyPluginAsync = async (fastify) => {
schema: postRefreshTokenSchema, schema: postRefreshTokenSchema,
handler: async (request, reply) => { handler: async (request, reply) => {
const { refreshToken } = request.body const { refreshToken } = request.body
try {
const userRefreshJWT = jwt.verify(
refreshToken,
JWT_REFRESH_SECRET
) as UserRefreshJWT
const foundRefreshToken = await prisma.refreshToken.findFirst({ const foundRefreshToken = await prisma.refreshToken.findFirst({
where: { token: refreshToken } where: { token: userRefreshJWT.tokenUUID }
}) })
if (foundRefreshToken == null) { if (foundRefreshToken == null) {
throw fastify.httpErrors.forbidden() throw fastify.httpErrors.forbidden()
} }
try {
const userJWT = jwt.verify(
foundRefreshToken.token,
JWT_REFRESH_SECRET
) as UserJWT
const accessToken = generateAccessToken({ const accessToken = generateAccessToken({
id: userJWT.id, id: userRefreshJWT.id,
currentStrategy: userJWT.currentStrategy currentStrategy: userRefreshJWT.currentStrategy
}) })
reply.statusCode = 200 reply.statusCode = 200
return { return {

View File

@ -25,6 +25,11 @@ await tap.test('PUT /users/reset-password', async (t) => {
return userExample return userExample
} }
}) })
sinon.stub(prisma, 'refreshToken').value({
deleteMany: async () => {
return { count: 1 }
}
})
const response = await application.inject({ const response = await application.inject({
method: 'PUT', method: 'PUT',
url: '/users/reset-password', url: '/users/reset-password',

View File

@ -1,14 +1,15 @@
import { randomUUID } from 'node:crypto' import { randomUUID } from 'node:crypto'
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import ms from 'ms' import ms from 'ms'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'
import { userSchema } from '../../../models/User.js' import { userSchema } from '../../../models/User.js'
import { sendEmail } from '../../../tools/email/sendEmail.js' import { sendEmail } from '../../../tools/email/sendEmail.js'
import { Language, Theme } from '../../../models/UserSettings.js' import type { Language, Theme } from '../../../models/UserSettings.js'
const queryPostResetPasswordSchema = Type.Object({ const queryPostResetPasswordSchema = Type.Object({
redirectURI: Type.String({ format: 'uri-reference' }) redirectURI: Type.String({ format: 'uri-reference' })

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import bcrypt from 'bcryptjs' import bcrypt from 'bcryptjs'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
@ -39,7 +40,7 @@ export const putResetPasswordUser: FastifyPluginAsync = async (fastify) => {
user?.temporaryExpirationToken != null && user?.temporaryExpirationToken != null &&
user.temporaryExpirationToken.getTime() > Date.now() user.temporaryExpirationToken.getTime() > Date.now()
if (user == null || !isValidTemporaryToken) { if (user == null || !isValidTemporaryToken) {
throw fastify.httpErrors.badRequest('"tempToken" is invalid') throw fastify.httpErrors.badRequest('`temporaryToken` is invalid.')
} }
const hashedPassword = await bcrypt.hash(password, 12) const hashedPassword = await bcrypt.hash(password, 12)
await prisma.user.update({ await prisma.user.update({
@ -52,6 +53,11 @@ export const putResetPasswordUser: FastifyPluginAsync = async (fastify) => {
temporaryExpirationToken: null temporaryExpirationToken: null
} }
}) })
await prisma.refreshToken.deleteMany({
where: {
userId: user.id
}
})
reply.statusCode = 200 reply.statusCode = 200
return 'The new password has been saved!' return 'The new password has been saved!'
} }

View File

@ -1,5 +1,6 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import bcrypt from 'bcryptjs' import bcrypt from 'bcryptjs'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'

View File

@ -1,9 +1,11 @@
import tap from 'tap' import tap from 'tap'
import sinon from 'sinon' import sinon from 'sinon'
import jwt from 'jsonwebtoken'
import { application } from '../../../../application.js' import { application } from '../../../../application.js'
import prisma from '../../../../tools/database/prisma.js' import prisma from '../../../../tools/database/prisma.js'
import { refreshTokenExample } from '../../../../models/RefreshToken.js' import { refreshTokenExample } from '../../../../models/RefreshToken.js'
import type { UserRefreshJWT } from '../../../../models/User.js'
await tap.test('POST /users/signout', async (t) => { await tap.test('POST /users/signout', async (t) => {
t.afterEach(() => { t.afterEach(() => {
@ -17,10 +19,18 @@ await tap.test('POST /users/signout', async (t) => {
}, },
delete: async () => {} delete: async () => {}
}) })
sinon.stub(jwt, 'verify').value(() => {
const value: UserRefreshJWT = {
id: 1,
tokenUUID: refreshTokenExample.token,
currentStrategy: 'Local'
}
return value
})
const response = await application.inject({ const response = await application.inject({
method: 'POST', method: 'POST',
url: '/users/signout', url: '/users/signout',
payload: { refreshToken: refreshTokenExample.token } payload: { refreshToken: 'jwt token' }
}) })
t.equal(response.statusCode, 200) t.equal(response.statusCode, 200)
}) })

View File

@ -1,5 +1,5 @@
import { Type } from '@sinclair/typebox' import { Type } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'

View File

@ -1,12 +1,16 @@
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import jwt from 'jsonwebtoken'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'
import { refreshTokensSchema } from '../../../models/RefreshToken.js' import { JWT_REFRESH_SECRET } from '../../../tools/configurations.js'
import type { UserRefreshJWT } from '../../../models/User.js'
import { jwtSchema } from '../../../tools/utils/jwtToken.js'
const bodyPostSignoutSchema = Type.Object({ const bodyPostSignoutSchema = Type.Object({
refreshToken: refreshTokensSchema.token refreshToken: jwtSchema.refreshToken
}) })
type BodyPostSignoutSchemaType = Static<typeof bodyPostSignoutSchema> type BodyPostSignoutSchemaType = Static<typeof bodyPostSignoutSchema>
@ -32,21 +36,27 @@ export const postSignoutUser: FastifyPluginAsync = async (fastify) => {
schema: postSignoutSchema, schema: postSignoutSchema,
handler: async (request, reply) => { handler: async (request, reply) => {
const { refreshToken } = request.body const { refreshToken } = request.body
const token = await prisma.refreshToken.findFirst({ try {
where: { const userRefreshJWT = jwt.verify(
token: refreshToken refreshToken,
} JWT_REFRESH_SECRET
) as UserRefreshJWT
const foundRefreshToken = await prisma.refreshToken.findFirst({
where: { token: userRefreshJWT.tokenUUID }
}) })
if (token == null) { if (foundRefreshToken == null) {
throw fastify.httpErrors.notFound() throw fastify.httpErrors.notFound()
} }
await prisma.refreshToken.delete({ await prisma.refreshToken.delete({
where: { where: {
id: token.id id: foundRefreshToken.id
} }
}) })
reply.statusCode = 200 reply.statusCode = 200
return {} return {}
} catch {
throw fastify.httpErrors.notFound()
}
} }
}) })
} }

View File

@ -1,18 +1,16 @@
import { randomUUID } from 'node:crypto' import { randomUUID } from 'node:crypto'
import { Static, Type } from '@sinclair/typebox' import type { Static } from '@sinclair/typebox'
import { Type } from '@sinclair/typebox'
import bcrypt from 'bcryptjs' import bcrypt from 'bcryptjs'
import { FastifyPluginAsync, FastifySchema } from 'fastify' import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import prisma from '../../../tools/database/prisma.js' import prisma from '../../../tools/database/prisma.js'
import { fastifyErrors } from '../../../models/utils.js' import { fastifyErrors } from '../../../models/utils.js'
import { import type { BodyUserSchemaType } from '../../../models/User.js'
bodyUserSchema, import { bodyUserSchema, userPublicSchema } from '../../../models/User.js'
BodyUserSchemaType,
userPublicSchema
} from '../../../models/User.js'
import { sendEmail } from '../../../tools/email/sendEmail.js' import { sendEmail } from '../../../tools/email/sendEmail.js'
import { API_URL } from '../../../tools/configurations/index.js' import { API_URL } from '../../../tools/configurations.js'
const queryPostSignupSchema = Type.Object({ const queryPostSignupSchema = Type.Object({
redirectURI: Type.Optional(Type.String({ format: 'uri-reference' })) redirectURI: Type.Optional(Type.String({ format: 'uri-reference' }))

View File

@ -0,0 +1,25 @@
import { URL } from 'node:url'
import dotenv from 'dotenv'
dotenv.config()
export const PORT = parseInt(process.env['PORT'] ?? '8080', 10)
export const HOST = process.env['HOST'] ?? '0.0.0.0'
export const API_URL = process.env['API_URL'] ?? `http://${HOST}:${PORT}`
export const FILE_UPLOADS_API_URL =
process.env['FILE_UPLOADS_API_URL'] ?? 'http://localhost:8000'
export const FILE_UPLOADS_API_KEY =
process.env['FILE_UPLOADS_API_KEY'] ?? 'apiKeySecret'
export const JWT_ACCESS_SECRET =
process.env['JWT_ACCESS_SECRET'] ?? 'accessTokenSecret'
export const JWT_REFRESH_SECRET =
process.env['JWT_REFRESH_SECRET'] ?? 'refreshTokenSecret'
export const JWT_ACCESS_EXPIRES_IN =
process.env['JWT_ACCESS_EXPIRES_IN'] ?? '15 minutes'
export const SRC_URL = new URL('../', import.meta.url)
export const ROOT_URL = new URL('../', SRC_URL)
export const EMAIL_URL = new URL('./email/', ROOT_URL)
export const EMAIL_TEMPLATE_URL = new URL('./email-template.ejs', EMAIL_URL)
export const EMAIL_LOCALES_URL = new URL('./locales/', EMAIL_URL)

Some files were not shown because too many files have changed in this diff Show More