├── dev ├── nodemon.json ├── .gitignore ├── .env.example ├── README.md ├── src │ ├── collections │ │ ├── Posts.ts │ │ └── Users.ts │ ├── server.ts │ ├── payload.config.ts │ └── test │ │ └── component.tsx ├── tsconfig.json └── package.json ├── src ├── index.ts ├── mocks │ └── mockFile.js ├── captureException.ts ├── webpack.ts ├── types.ts ├── plugin.ts ├── plugin.spec.ts └── startSentry.ts ├── .prettierrc.js ├── .editorconfig ├── .eslintrc.js ├── README.md ├── tsconfig.json ├── jest.config.js ├── .github └── workflows │ └── main.yml ├── LICENSE.md ├── package.json └── .gitignore /dev/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "ext": "ts", 3 | "exec": "ts-node src/server.ts" 4 | } 5 | -------------------------------------------------------------------------------- /dev/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | dist 4 | build 5 | .DS_Store 6 | package-lock.json 7 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { sentry } from './plugin' 2 | export type { PluginOptions } from './types' 3 | -------------------------------------------------------------------------------- /dev/.env.example: -------------------------------------------------------------------------------- 1 | MONGODB_URI=mongodb://localhost/plugin-sentry 2 | PAYLOAD_SECRET=mwfkfksosksseanllcosjshdncm 3 | -------------------------------------------------------------------------------- /src/mocks/mockFile.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | startSentry: () => {}, 3 | captureException: () => {}, 4 | } 5 | -------------------------------------------------------------------------------- /src/captureException.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from '@sentry/node' 2 | 3 | export const captureException = (err: Error): void => { 4 | Sentry.captureException(err) 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 100, 3 | parser: "typescript", 4 | semi: false, 5 | singleQuote: true, 6 | trailingComma: "all", 7 | arrowParens: "avoid", 8 | }; 9 | -------------------------------------------------------------------------------- /dev/README.md: -------------------------------------------------------------------------------- 1 | # Sentry Plugin Demo 2 | 3 | This project was created using create-payload-app using the blank template. 4 | 5 | ## How to Use 6 | 7 | `yarn dev` will start up your application and reload on any changes. 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | end_of_line = lf 10 | max_line_length = null 11 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@payloadcms'], 3 | overrides: [ 4 | { 5 | files: ['**/*.spec.ts'], 6 | rules: { 7 | '@typescript-eslint/explicit-function-return-type': 'off', 8 | }, 9 | }, 10 | ], 11 | } 12 | -------------------------------------------------------------------------------- /dev/src/collections/Posts.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionConfig } from 'payload/types' 2 | 3 | const Posts: CollectionConfig = { 4 | slug: 'posts', 5 | access: { 6 | create: () => false, 7 | read: () => true, 8 | }, 9 | fields: [ 10 | { 11 | name: 'text', 12 | type: 'text', 13 | }, 14 | ], 15 | } 16 | 17 | export default Posts 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repo has moved! 2 | 3 | This repo has been merged into the [Packages Directory](https://github.com/payloadcms/payload/tree/main/packages) of the [Payload Monorepo](https://github.com/payloadcms/payload). Please refer to the new location of the [Sentry Plugin](https://github.com/payloadcms/payload/tree/main/packages/plugin-sentry) for all future updates, issues, and pull requests. 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "outDir": "./dist", 5 | "allowJs": true, 6 | "module": "commonjs", 7 | "sourceMap": true, 8 | "jsx": "react", 9 | "esModuleInterop": true, 10 | "declaration": true, 11 | "declarationDir": "./dist", 12 | "skipLibCheck": true, 13 | "strict": true, 14 | }, 15 | "include": [ 16 | "src/**/*" 17 | ], 18 | } -------------------------------------------------------------------------------- /dev/src/collections/Users.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionConfig } from 'payload/types' 2 | 3 | const Users: CollectionConfig = { 4 | slug: 'users', 5 | auth: true, 6 | admin: { 7 | useAsTitle: 'email', 8 | }, 9 | access: { 10 | read: () => true, 11 | delete: () => false, 12 | }, 13 | fields: [ 14 | // Email added by default 15 | // Add more fields as needed 16 | ], 17 | } 18 | 19 | export default Users 20 | -------------------------------------------------------------------------------- /dev/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "strict": false, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "outDir": "./dist", 13 | "rootDir": "../", 14 | "jsx": "react", 15 | "sourceMap": true 16 | }, 17 | "ts-node": { 18 | "transpileOnly": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: true, 3 | testEnvironment: 'node', 4 | testMatch: ['**/src/**/*.spec.ts'], 5 | transform: { 6 | '^.+\\.(ts|tsx)?$': 'ts-jest', 7 | }, 8 | testTimeout: 60000, 9 | moduleNameMapper: { 10 | '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 11 | '/src/webpack/mocks/fileMock.js', 12 | '\\.(css|scss)$': '/src/webpack/mocks/emptyModule.js', 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /dev/src/server.ts: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv' 2 | import express from 'express' 3 | import payload from 'payload' 4 | 5 | dotenv.config() 6 | const app = express() 7 | 8 | // Redirect root to Admin panel 9 | app.get('/', (_, res) => { 10 | res.redirect('/admin') 11 | }) 12 | 13 | // Initialize Payload 14 | const start = async (): Promise => { 15 | await payload.init({ 16 | secret: process.env.PAYLOAD_SECRET, 17 | mongoURL: process.env.MONGODB_URI, 18 | express: app, 19 | onInit: () => { 20 | payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`) 21 | }, 22 | }) 23 | 24 | app.listen(3000) 25 | } 26 | 27 | start() 28 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build_and_test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build_and_test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | node-version: [16.x] 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Use Node.js ${{ matrix.node-version }} 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: ${{ matrix.node-version }} 18 | registry-url: https://registry.npmjs.org 19 | 20 | - name: yarn cache 21 | uses: c-hive/gha-yarn-cache@v2 22 | - run: yarn 23 | env: 24 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 25 | - run: yarn build 26 | - run: yarn test 27 | -------------------------------------------------------------------------------- /src/webpack.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import type { Config } from 'payload/config' 3 | import type { Configuration as WebpackConfig } from 'webpack' 4 | 5 | export const extendWebpackConfig = 6 | (config: Config): ((webpackConfig: WebpackConfig) => WebpackConfig) => 7 | webpackConfig => { 8 | const existingWebpackConfig = 9 | typeof config.admin?.webpack === 'function' 10 | ? config.admin.webpack(webpackConfig) 11 | : webpackConfig 12 | 13 | const mockModulePath = path.resolve(__dirname, './mocks/mockFile.js') 14 | 15 | const newWebpack = { 16 | ...existingWebpackConfig, 17 | resolve: { 18 | ...(existingWebpackConfig.resolve || {}), 19 | alias: { 20 | ...(existingWebpackConfig.resolve?.alias ? existingWebpackConfig.resolve.alias : {}), 21 | [path.resolve(__dirname, './startSentry')]: mockModulePath, 22 | [path.resolve(__dirname, './captureException')]: mockModulePath, 23 | }, 24 | }, 25 | } 26 | 27 | return newWebpack 28 | } 29 | -------------------------------------------------------------------------------- /dev/src/payload.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-relative-packages */ 2 | import path from 'path' 3 | import { buildConfig } from 'payload/config' 4 | 5 | import { sentry } from '../../src/index' 6 | import Posts from './collections/Posts' 7 | import Users from './collections/Users' 8 | import { testErrors } from './test/component' 9 | 10 | export default buildConfig({ 11 | serverURL: 'http://localhost:3000', 12 | admin: { 13 | user: Users.slug, 14 | components: { 15 | beforeDashboard: [testErrors], 16 | }, 17 | }, 18 | collections: [Posts, Users], 19 | typescript: { 20 | outputFile: path.resolve(__dirname, 'payload-types.ts'), 21 | }, 22 | graphQL: { 23 | schemaOutputFile: path.resolve(__dirname, 'generated-schema.graphql'), 24 | }, 25 | plugins: [ 26 | sentry({ 27 | dsn: 'https://61edebe5ee6d4d38a9d6459c7323d777@o4505289711681536.ingest.sentry.io/4505357688242176', 28 | options: { 29 | init: { 30 | debug: true, 31 | }, 32 | requestHandler: { 33 | serverName: false, 34 | }, 35 | captureErrors: [400, 403, 404], 36 | }, 37 | }), 38 | ], 39 | }) 40 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHandlerOptions } from '@sentry/node/types/handlers' 2 | import type { ClientOptions } from '@sentry/types' 3 | 4 | export interface PluginOptions { 5 | /** 6 | * Sentry DSN (Data Source Name) 7 | * This is required unless enabled is set to false. 8 | * Sentry automatically assigns a DSN when you create a project. 9 | * If you don't have a DSN yet, you can create a new project here: https://sentry.io 10 | */ 11 | dsn: string | null 12 | /** 13 | * Enable or disable Sentry plugin 14 | * @default false 15 | */ 16 | enabled?: boolean 17 | /** 18 | * Options passed directly to Sentry 19 | * @default false 20 | */ 21 | options?: { 22 | /** 23 | * Passes any valid options to Sentry.init() 24 | */ 25 | init?: Partial 26 | /** 27 | * Passes any valid options to Sentry.Handlers.requestHandler() 28 | */ 29 | requestHandler?: RequestHandlerOptions 30 | /** 31 | * Sentry will only capture 500 errors by default. 32 | * If you want to capture other errors, you can add them as an array here. 33 | */ 34 | captureErrors?: number[] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/plugin.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import type { Config } from 'payload/config' 3 | 4 | import { captureException } from './captureException' 5 | import { startSentry } from './startSentry' 6 | import type { PluginOptions } from './types' 7 | import { extendWebpackConfig } from './webpack' 8 | 9 | export const sentry = 10 | (pluginOptions: PluginOptions) => 11 | (incomingConfig: Config): Config => { 12 | let config = { ...incomingConfig } 13 | const webpack = extendWebpackConfig(incomingConfig) 14 | 15 | config.admin = { 16 | ...(config.admin || {}), 17 | webpack, 18 | } 19 | 20 | if (pluginOptions.enabled === false || !pluginOptions.dsn) { 21 | return config 22 | } 23 | 24 | config.hooks = { 25 | ...(incomingConfig.hooks || {}), 26 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 27 | afterError: (err: any) => { 28 | captureException(err) 29 | }, 30 | } 31 | 32 | config.onInit = async payload => { 33 | if (incomingConfig.onInit) await incomingConfig.onInit(payload) 34 | startSentry(pluginOptions, payload) 35 | } 36 | 37 | return config 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2018-2023 Payload CMS, LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /dev/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "plugin-sentry-demo", 3 | "description": "Payload project to demonstrate how to use the sentry plugin", 4 | "version": "1.0.0", 5 | "main": "dist/server.js", 6 | "license": "MIT", 7 | "scripts": { 8 | "dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon", 9 | "build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build", 10 | "build:server": "tsc", 11 | "build": "yarn copyfiles && yarn build:payload && yarn build:server", 12 | "serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js", 13 | "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png}\" dist/", 14 | "generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types", 15 | "generate:graphQLSchema": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:graphQLSchema" 16 | }, 17 | "dependencies": { 18 | "@sentry/react": "^7.56.0", 19 | "dotenv": "^8.2.0", 20 | "eslint": "^8.42.0", 21 | "express": "^4.17.1", 22 | "payload": "^1.9.2" 23 | }, 24 | "devDependencies": { 25 | "@types/express": "^4.17.9", 26 | "copyfiles": "^2.4.1", 27 | "cross-env": "^7.0.3", 28 | "nodemon": "^2.0.6", 29 | "ts-node": "^9.1.1", 30 | "typescript": "^4.8.4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/plugin.spec.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'payload/config' 2 | import { defaults } from 'payload/dist/config/defaults' 3 | 4 | import { sentry } from './plugin' 5 | 6 | describe('plugin', () => { 7 | it('should run the plugin', () => { 8 | const plugin = sentry({ enabled: true, dsn: 'asdf' }) 9 | const config = plugin(createConfig()) 10 | 11 | assertPluginRan(config) 12 | }) 13 | 14 | it('should default enable: true', () => { 15 | const plugin = sentry({ dsn: 'asdf' }) 16 | const config = plugin(createConfig()) 17 | 18 | assertPluginRan(config) 19 | }) 20 | 21 | it('should not run if dsn is not provided', () => { 22 | const plugin = sentry({ enabled: true, dsn: null }) 23 | const config = plugin(createConfig()) 24 | 25 | assertPluginDidNotRun(config) 26 | }) 27 | 28 | it('should respect enabled: false', () => { 29 | const plugin = sentry({ enabled: false, dsn: null }) 30 | const config = plugin(createConfig()) 31 | 32 | assertPluginDidNotRun(config) 33 | }) 34 | }) 35 | 36 | function assertPluginRan(config: Config) { 37 | expect(config.admin?.webpack).toBeDefined() 38 | expect(config.hooks?.afterError).toBeDefined() 39 | expect(config.onInit).toBeDefined() 40 | } 41 | 42 | function assertPluginDidNotRun(config: Config) { 43 | expect(config.admin?.webpack).toBeDefined() 44 | expect(config.hooks?.afterError).toBeUndefined() 45 | expect(config.onInit).toBeUndefined() 46 | } 47 | 48 | function createConfig(overrides?: Partial): Config { 49 | return { 50 | ...defaults, 51 | ...overrides, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@payloadcms/plugin-sentry", 3 | "version": "0.0.4", 4 | "homepage:": "https://payloadcms.com", 5 | "repository": "git@github.com:payloadcms/plugin-sentry.git", 6 | "description": "Sentry plugin for Payload", 7 | "main": "dist/index.js", 8 | "types": "dist/index.d.ts", 9 | "scripts": { 10 | "build": "tsc", 11 | "test": "jest", 12 | "lint": "eslint src", 13 | "lint:fix": "eslint --fix --ext .ts,.tsx src", 14 | "clean": "rimraf dist && rimraf dev/yarn.lock", 15 | "prepublishOnly": "yarn clean && yarn build && yarn test" 16 | }, 17 | "keywords": [ 18 | "payload", 19 | "cms", 20 | "plugin", 21 | "typescript", 22 | "sentry", 23 | "error handling" 24 | ], 25 | "author": "dev@payloadcms.com", 26 | "license": "MIT", 27 | "files": [ 28 | "dist" 29 | ], 30 | "peerDependencies": { 31 | "payload": "^1.10.1 || ^2.0.0", 32 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0" 33 | }, 34 | "dependencies": { 35 | "@sentry/node": "^7.55.2", 36 | "@sentry/types": "^7.54.0", 37 | "express": "^4.18.2" 38 | }, 39 | "devDependencies": { 40 | "@payloadcms/eslint-config": "^0.0.1", 41 | "@types/express": "^4.17.9", 42 | "@types/jest": "^29.5.2", 43 | "@types/node": "18.11.3", 44 | "@types/react": "18.0.21", 45 | "@typescript-eslint/eslint-plugin": "^5.51.0", 46 | "@typescript-eslint/parser": "^5.51.0", 47 | "copyfiles": "^2.4.1", 48 | "cross-env": "^7.0.3", 49 | "dotenv": "^8.2.0", 50 | "eslint": "^8.19.0", 51 | "eslint-config-prettier": "^8.5.0", 52 | "eslint-plugin-filenames": "^1.3.2", 53 | "eslint-plugin-import": "2.25.4", 54 | "eslint-plugin-prettier": "^4.0.0", 55 | "eslint-plugin-react-hooks": "^4.6.0", 56 | "eslint-plugin-simple-import-sort": "^10.0.0", 57 | "jest": "^29.5.0", 58 | "nodemon": "^2.0.6", 59 | "payload": "^1.10.1", 60 | "prettier": "^2.7.1", 61 | "ts-jest": "^29.1.0", 62 | "ts-node": "^10.9.1", 63 | "typescript": "^4.1.3" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/startSentry.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | /* eslint-disable no-console */ 3 | import * as Sentry from '@sentry/node' 4 | import type { NextFunction, Request, Response } from 'express' 5 | import type express from 'express' 6 | import type { Payload } from 'payload/dist/payload' 7 | 8 | import type { PluginOptions } from './types' 9 | 10 | export const startSentry = (pluginOptions: PluginOptions, payload: Payload): void => { 11 | const { dsn, options } = pluginOptions 12 | const { express: app } = payload 13 | 14 | if (!dsn || !app) return 15 | 16 | try { 17 | Sentry.init({ 18 | ...options?.init, 19 | dsn: dsn, 20 | integrations: [ 21 | ...(options?.init?.integrations || []), 22 | new Sentry.Integrations.Http({ tracing: true }), 23 | new Sentry.Integrations.Express({ app }), 24 | ...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(), 25 | ], 26 | }) 27 | 28 | app.use(Sentry.Handlers.requestHandler(options?.requestHandler || {}) as express.RequestHandler) 29 | app.use(Sentry.Handlers.tracingHandler()) 30 | 31 | app.use( 32 | Sentry.Handlers.errorHandler({ 33 | shouldHandleError(error) { 34 | if (error.status === 500) { 35 | return true 36 | } 37 | if ( 38 | options?.captureErrors && 39 | typeof error.status === 'number' && 40 | options.captureErrors.includes(error.status) 41 | ) { 42 | return true 43 | } 44 | return false 45 | }, 46 | }) as express.ErrorRequestHandler, 47 | ) 48 | 49 | app.use(function onError( 50 | _err: unknown, 51 | _req: Request, 52 | res: Response & { sentry?: string }, 53 | _next: NextFunction, 54 | ) { 55 | res.statusCode = 500 56 | res.end(res.sentry + '\n') 57 | }) 58 | } catch (err: unknown) { 59 | console.log('There was an error initializing Sentry, please ensure you entered a valid DSN') 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /dev/src/test/component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import * as Sentry from '@sentry/react' 3 | export const testErrors = () => { 4 | const notFound = async () => { 5 | const req = await fetch('http://localhost:3000/api/users/notFound', { 6 | method: 'GET', 7 | }) 8 | } 9 | 10 | const cannotCreate = async () => { 11 | const req = await fetch('http://localhost:3000/api/posts', { 12 | method: 'POST', 13 | body: JSON.stringify({ 14 | text: 'New post', 15 | }), 16 | }) 17 | } 18 | 19 | const badLogin = async () => { 20 | const req = await fetch('http://localhost:3000/api/users/login', { 21 | method: 'POST', 22 | body: JSON.stringify({ 23 | email: 'sorry@whoareyou.com', 24 | password: '123456', 25 | }), 26 | }) 27 | } 28 | 29 | const badReq = async () => { 30 | const req = await fetch('http://localhost:3000/api/users/forgot-password', { 31 | method: 'POST', 32 | credentials: 'include', 33 | headers: { 34 | 'Content-Type': 'application/json', 35 | }, 36 | }) 37 | } 38 | 39 | const badReset = async () => { 40 | const req = await fetch('http://localhost:3000/api/users/reset-password', { 41 | method: 'POST', 42 | credentials: 'include', 43 | headers: { 44 | 'Content-Type': 'application/json', 45 | }, 46 | body: JSON.stringify({ 47 | token: '7eac3830ffcfc7f9f66c00315dabeb11575dba91', 48 | password: 'newPassword', 49 | }), 50 | }) 51 | } 52 | 53 | const badVerify = async () => { 54 | const req = await fetch('http://localhost:3000/api/users/unlock', { 55 | method: 'POST', 56 | headers: { 57 | 'Content-Type': 'application/json', 58 | }, 59 | }) 60 | } 61 | 62 | return ( 63 | 64 |

Test Errors

65 |
66 | 69 | 70 | 73 | 74 | 77 | 78 | 81 | 82 | 85 | 86 | 89 |
90 |
91 | ) 92 | } 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dev/yarn.lock 2 | 3 | # Created by https://www.gitignore.io/api/node,macos,windows,webstorm,sublimetext,visualstudiocode 4 | 5 | ### macOS ### 6 | *.DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear in the root of a volume 14 | .DocumentRevisions-V100 15 | .fseventsd 16 | .Spotlight-V100 17 | .TemporaryItems 18 | .Trashes 19 | .VolumeIcon.icns 20 | .com.apple.timemachine.donotpresent 21 | 22 | # Directories potentially created on remote AFP share 23 | .AppleDB 24 | .AppleDesktop 25 | Network Trash Folder 26 | Temporary Items 27 | .apdisk 28 | 29 | ### Node ### 30 | # Logs 31 | logs 32 | *.log 33 | npm-debug.log* 34 | yarn-debug.log* 35 | yarn-error.log* 36 | 37 | # Runtime data 38 | pids 39 | *.pid 40 | *.seed 41 | *.pid.lock 42 | 43 | # Directory for instrumented libs generated by jscoverage/JSCover 44 | lib-cov 45 | 46 | # Coverage directory used by tools like istanbul 47 | coverage 48 | 49 | # nyc test coverage 50 | .nyc_output 51 | 52 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 53 | .grunt 54 | 55 | # Bower dependency directory (https://bower.io/) 56 | bower_components 57 | 58 | # node-waf configuration 59 | .lock-wscript 60 | 61 | # Compiled binary addons (http://nodejs.org/api/addons.html) 62 | build/Release 63 | 64 | # Dependency directories 65 | node_modules/ 66 | jspm_packages/ 67 | 68 | # Typescript v1 declaration files 69 | typings/ 70 | 71 | # Optional npm cache directory 72 | .npm 73 | 74 | # Optional eslint cache 75 | .eslintcache 76 | 77 | # Optional REPL history 78 | .node_repl_history 79 | 80 | # Output of 'npm pack' 81 | *.tgz 82 | 83 | # Yarn Integrity file 84 | .yarn-integrity 85 | 86 | # Yarn Berry 87 | .yarn/* 88 | !.yarn/patches 89 | !.yarn/plugins 90 | !.yarn/releases 91 | !.yarn/sdks 92 | !.yarn/versions 93 | .pnp.* 94 | 95 | # dotenv environment variables file 96 | .env 97 | 98 | 99 | ### SublimeText ### 100 | # cache files for sublime text 101 | *.tmlanguage.cache 102 | *.tmPreferences.cache 103 | *.stTheme.cache 104 | 105 | # workspace files are user-specific 106 | *.sublime-workspace 107 | 108 | # project files should be checked into the repository, unless a significant 109 | # proportion of contributors will probably not be using SublimeText 110 | # *.sublime-project 111 | 112 | # sftp configuration file 113 | sftp-config.json 114 | 115 | # Package control specific files 116 | Package Control.last-run 117 | Package Control.ca-list 118 | Package Control.ca-bundle 119 | Package Control.system-ca-bundle 120 | Package Control.cache/ 121 | Package Control.ca-certs/ 122 | Package Control.merged-ca-bundle 123 | Package Control.user-ca-bundle 124 | oscrypto-ca-bundle.crt 125 | bh_unicode_properties.cache 126 | 127 | # Sublime-github package stores a github token in this file 128 | # https://packagecontrol.io/packages/sublime-github 129 | GitHub.sublime-settings 130 | 131 | ### VisualStudioCode ### 132 | .vscode/* 133 | !.vscode/tasks.json 134 | !.vscode/launch.json 135 | !.vscode/extensions.json 136 | .history 137 | 138 | ### WebStorm ### 139 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 140 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 141 | 142 | .idea/* 143 | # User-specific stuff: 144 | .idea/**/workspace.xml 145 | .idea/**/tasks.xml 146 | .idea/dictionaries 147 | 148 | # Sensitive or high-churn files: 149 | .idea/**/dataSources/ 150 | .idea/**/dataSources.ids 151 | .idea/**/dataSources.xml 152 | .idea/**/dataSources.local.xml 153 | .idea/**/sqlDataSources.xml 154 | .idea/**/dynamic.xml 155 | .idea/**/uiDesigner.xml 156 | 157 | # Gradle: 158 | .idea/**/gradle.xml 159 | .idea/**/libraries 160 | 161 | # CMake 162 | cmake-build-debug/ 163 | 164 | # Mongo Explorer plugin: 165 | .idea/**/mongoSettings.xml 166 | 167 | ## File-based project format: 168 | *.iws 169 | 170 | ## Plugin-specific files: 171 | 172 | # IntelliJ 173 | /out/ 174 | 175 | # mpeltonen/sbt-idea plugin 176 | .idea_modules/ 177 | 178 | # JIRA plugin 179 | atlassian-ide-plugin.xml 180 | 181 | # Cursive Clojure plugin 182 | .idea/replstate.xml 183 | 184 | # Ruby plugin and RubyMine 185 | /.rakeTasks 186 | 187 | # Crashlytics plugin (for Android Studio and IntelliJ) 188 | com_crashlytics_export_strings.xml 189 | crashlytics.properties 190 | crashlytics-build.properties 191 | fabric.properties 192 | 193 | ### WebStorm Patch ### 194 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 195 | 196 | # *.iml 197 | # modules.xml 198 | # .idea/misc.xml 199 | # *.ipr 200 | 201 | # Sonarlint plugin 202 | .idea/sonarlint 203 | 204 | ### Windows ### 205 | # Windows thumbnail cache files 206 | Thumbs.db 207 | ehthumbs.db 208 | ehthumbs_vista.db 209 | 210 | # Folder config file 211 | Desktop.ini 212 | 213 | # Recycle Bin used on file shares 214 | $RECYCLE.BIN/ 215 | 216 | # Windows Installer files 217 | *.cab 218 | *.msi 219 | *.msm 220 | *.msp 221 | 222 | # Windows shortcuts 223 | *.lnk 224 | 225 | # End of https://www.gitignore.io/api/node,macos,windows,webstorm,sublimetext,visualstudiocode 226 | 227 | # Ignore all uploads 228 | demo/upload 229 | demo/media 230 | demo/files 231 | 232 | # Ignore build folder 233 | build 234 | 235 | # Ignore built components 236 | components/index.js 237 | components/styles.css 238 | 239 | # Ignore generated 240 | demo/generated-types.ts 241 | demo/generated-schema.graphql 242 | 243 | # Ignore dist, no need for git 244 | dist 245 | 246 | # Ignore emulator volumes 247 | src/adapters/s3/emulator/.localstack/ 248 | --------------------------------------------------------------------------------