@devmoods/express-extras 0.13.9 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/migrate.js +10 -0
- package/dist/__tests__/auth.spec.jsx +3 -5
- package/dist/auth.d.ts +10 -9
- package/dist/auth.js +101 -45
- package/dist/context.d.ts +2 -3
- package/dist/context.js +10 -17
- package/dist/email.js +4 -31
- package/dist/errors.js +16 -51
- package/dist/index.d.ts +13 -13
- package/dist/index.js +14 -32
- package/dist/listen.d.ts +2 -4
- package/dist/listen.js +12 -16
- package/dist/logger.d.ts +5 -5
- package/dist/logger.js +21 -26
- package/dist/middleware.d.ts +5 -3
- package/dist/middleware.js +20 -54
- package/dist/migrate.d.ts +1 -1
- package/dist/migrate.js +25 -31
- package/dist/postgres.d.ts +109 -109
- package/dist/postgres.js +7 -16
- package/dist/redis.d.ts +262 -262
- package/dist/redis.js +3 -7
- package/dist/routes.d.ts +1 -1
- package/dist/routes.js +4 -8
- package/dist/sentry.d.ts +6 -3
- package/dist/sentry.js +4 -32
- package/dist/slack.js +7 -9
- package/dist/validate.d.ts +2 -2
- package/dist/validate.js +13 -23
- package/package.json +28 -11
- package/bin/migrate +0 -9
package/bin/migrate.js
ADDED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const auth_1 = require("../auth");
|
|
1
|
+
import { createAuthToken } from '../auth.js';
|
|
4
2
|
test('createAuthToken', async () => {
|
|
5
3
|
process.env.APP_JWT_SECRET = 'test';
|
|
6
|
-
const first =
|
|
4
|
+
const first = createAuthToken({});
|
|
7
5
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
8
|
-
const second =
|
|
6
|
+
const second = createAuthToken({});
|
|
9
7
|
expect(first).not.toEqual(second);
|
|
10
8
|
});
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import { Request, RequestHandler, Response } from 'express';
|
|
2
|
-
import { RedisClient } from './redis';
|
|
1
|
+
import { type Request, type RequestHandler, type Response } from 'express';
|
|
2
|
+
import type { RedisClient } from './redis.js';
|
|
3
3
|
export declare function createAuthToken(payload: any, secret?: string): string;
|
|
4
|
-
interface
|
|
4
|
+
export interface AuthLoginMethods<User = any> {
|
|
5
5
|
emailAndPassword?: (email: string, verifyPassword: (realPassword: string) => Promise<boolean>) => Promise<User>;
|
|
6
6
|
}
|
|
7
|
-
interface AuthOptions<UserModel extends Record<string, any>> {
|
|
8
|
-
getUserByToken
|
|
9
|
-
methods:
|
|
7
|
+
export interface AuthOptions<UserModel extends Record<string, any>> {
|
|
8
|
+
getUserByToken(token: Record<string, any>): Promise<UserModel>;
|
|
9
|
+
methods: AuthLoginMethods<UserModel>;
|
|
10
10
|
redis: RedisClient;
|
|
11
|
-
filterUser
|
|
11
|
+
filterUser(user: UserModel): Partial<UserModel>;
|
|
12
12
|
tokenExpiry?: number;
|
|
13
|
+
onResetRequested?(user: UserModel, temporaryToken: string): void | Promise<void>;
|
|
14
|
+
onPasswordChanged?(user: UserModel, newPassword: string): Promise<void>;
|
|
13
15
|
}
|
|
14
|
-
export declare function createAuth<UserModel extends Record<string, any>>({ getUserByToken, methods: { emailAndPassword }, filterUser, redis, tokenExpiry, }: AuthOptions<UserModel>): {
|
|
16
|
+
export declare function createAuth<UserModel extends Record<string, any>>({ getUserByToken, methods: { emailAndPassword }, filterUser, redis, tokenExpiry, onResetRequested, onPasswordChanged, }: AuthOptions<UserModel>): {
|
|
15
17
|
createRouter: () => import("express-serve-static-core").Router;
|
|
16
18
|
useCurrentUser: () => UserModel | undefined;
|
|
17
19
|
middleware: (authorize?: boolean) => RequestHandler;
|
|
@@ -24,4 +26,3 @@ export declare function parseAuthorizationHeader(req: Request): string | null;
|
|
|
24
26
|
export declare function setAccessTokenCookie(res: Response, token: string | null): void;
|
|
25
27
|
export declare function checkIfPasswordsMatch(realPassword: string, attemptedPassword: string): Promise<boolean>;
|
|
26
28
|
export declare function hashPassword(password: string): Promise<string>;
|
|
27
|
-
export {};
|
package/dist/auth.js
CHANGED
|
@@ -1,40 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
const errors_1 = require("./errors");
|
|
13
|
-
const logger_1 = require("./logger");
|
|
14
|
-
const middleware_1 = require("./middleware");
|
|
15
|
-
const validate_1 = require("./validate");
|
|
1
|
+
import * as bcrypt from 'bcrypt';
|
|
2
|
+
import { Router, } from 'express';
|
|
3
|
+
import jwt from 'jsonwebtoken';
|
|
4
|
+
import * as crypto from 'node:crypto';
|
|
5
|
+
import { v4 } from 'uuid';
|
|
6
|
+
import { setContext, useContext } from './context.js';
|
|
7
|
+
import { BadRequest, NotFound, Unauthorized } from './errors.js';
|
|
8
|
+
import { getLogger } from './logger.js';
|
|
9
|
+
import { route } from './middleware.js';
|
|
10
|
+
import { validateBody, validateParams, } from './validate.js';
|
|
11
|
+
const logger = getLogger('auth');
|
|
16
12
|
const TOKEN_EXPIRES_IN = 3600 * 48;
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
const TEMP_TOKEN_EXIRES_IN = 3600;
|
|
14
|
+
export function createAuthToken(payload, secret = process.env.APP_JWT_SECRET) {
|
|
15
|
+
return jwt.sign(payload, secret, { expiresIn: TOKEN_EXPIRES_IN });
|
|
19
16
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const useCurrentUser = () => (0, context_1.useContext)('currentUser');
|
|
17
|
+
export function createAuth({ getUserByToken, methods: { emailAndPassword }, filterUser = (x) => x, redis, tokenExpiry = TOKEN_EXPIRES_IN, onResetRequested, onPasswordChanged, }) {
|
|
18
|
+
const useCurrentUser = () => useContext('currentUser');
|
|
23
19
|
const createRouter = () => {
|
|
24
|
-
const router =
|
|
25
|
-
router.get('/me', middleware(),
|
|
20
|
+
const router = Router();
|
|
21
|
+
router.get('/me', middleware(), route(() => {
|
|
26
22
|
const user = useCurrentUser();
|
|
27
23
|
return filterUser(user);
|
|
28
24
|
}));
|
|
29
25
|
if (emailAndPassword) {
|
|
30
|
-
router.post('/login',
|
|
26
|
+
router.post('/login', validateBody({
|
|
31
27
|
type: 'object',
|
|
32
28
|
properties: {
|
|
33
29
|
email: { type: 'string' },
|
|
34
30
|
password: { type: 'string' },
|
|
35
31
|
},
|
|
36
32
|
required: ['email', 'password'],
|
|
37
|
-
}),
|
|
33
|
+
}), route(async ({ body: { email, password } }, res) => {
|
|
38
34
|
try {
|
|
39
35
|
const user = await emailAndPassword(email, (realPassword) => checkIfPasswordsMatch(realPassword, password));
|
|
40
36
|
const token = createAuthToken(filterUser(user));
|
|
@@ -43,12 +39,12 @@ function createAuth({ getUserByToken, methods: { emailAndPassword }, filterUser
|
|
|
43
39
|
return [201, { token }];
|
|
44
40
|
}
|
|
45
41
|
catch (error) {
|
|
46
|
-
|
|
47
|
-
throw
|
|
42
|
+
getLogger('core').error(error);
|
|
43
|
+
throw Unauthorized(error.message);
|
|
48
44
|
}
|
|
49
45
|
}));
|
|
50
46
|
}
|
|
51
|
-
router.get('/logout',
|
|
47
|
+
router.get('/logout', route(async (req, res) => {
|
|
52
48
|
const token = extractAccessTokenFromRequest(req);
|
|
53
49
|
if (token) {
|
|
54
50
|
await redis.del(token);
|
|
@@ -56,10 +52,75 @@ function createAuth({ getUserByToken, methods: { emailAndPassword }, filterUser
|
|
|
56
52
|
setAccessTokenCookie(res, null);
|
|
57
53
|
res.redirect('/');
|
|
58
54
|
}));
|
|
59
|
-
|
|
55
|
+
const skipPasswordCheck = async () => true;
|
|
56
|
+
const tokenSchema = {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
token: { type: 'string', minLength: 50, maxLength: 64 },
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
const emailSchema = {
|
|
63
|
+
type: 'string',
|
|
64
|
+
minLength: 4,
|
|
65
|
+
};
|
|
66
|
+
router.post('/reset/:token', validateParams(tokenSchema), validateBody({
|
|
67
|
+
type: 'object',
|
|
68
|
+
properties: {
|
|
69
|
+
email: emailSchema,
|
|
70
|
+
password: { type: 'string' },
|
|
71
|
+
},
|
|
72
|
+
required: ['email', 'password'],
|
|
73
|
+
}), route(async (req) => {
|
|
74
|
+
const { email, password } = req.body;
|
|
75
|
+
const { token } = req.params;
|
|
76
|
+
if (!emailAndPassword) {
|
|
77
|
+
throw new Error('Not applicable for login method');
|
|
78
|
+
}
|
|
79
|
+
const user = await emailAndPassword(email, skipPasswordCheck);
|
|
80
|
+
const tokenEmail = await redis.get(token);
|
|
81
|
+
if (!tokenEmail || email !== tokenEmail) {
|
|
82
|
+
throw BadRequest('invalid email');
|
|
83
|
+
}
|
|
84
|
+
await onPasswordChanged?.(user, await hashPassword(password));
|
|
85
|
+
logger.info('user changed password', { email: tokenEmail });
|
|
86
|
+
return 200;
|
|
87
|
+
}));
|
|
88
|
+
router.get('/reset/:token', validateParams(tokenSchema), route(async (req) => {
|
|
89
|
+
const token = req.params.token;
|
|
90
|
+
const email = await redis.get(token);
|
|
91
|
+
if (!email) {
|
|
92
|
+
throw NotFound();
|
|
93
|
+
}
|
|
94
|
+
return 200;
|
|
95
|
+
}));
|
|
96
|
+
router.post('/reset', middleware(false), validateBody({
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
email: emailSchema,
|
|
100
|
+
},
|
|
101
|
+
required: ['email'],
|
|
102
|
+
}), route(async (req) => {
|
|
103
|
+
const email = req.body.email;
|
|
104
|
+
if (!emailAndPassword) {
|
|
105
|
+
throw new Error('Not applicable for login method');
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const user = await emailAndPassword(email, skipPasswordCheck);
|
|
109
|
+
const temporaryToken = crypto.randomBytes(32).toString('hex');
|
|
110
|
+
await redis.set(temporaryToken, user.email, {
|
|
111
|
+
EX: TEMP_TOKEN_EXIRES_IN,
|
|
112
|
+
});
|
|
113
|
+
await onResetRequested?.(user, temporaryToken);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
logger.error(error, 'auth reset failed');
|
|
117
|
+
}
|
|
118
|
+
return 202;
|
|
119
|
+
}));
|
|
120
|
+
router.get('/token/refresh', middleware(false), route(async (req, res) => {
|
|
60
121
|
const user = useCurrentUser();
|
|
61
122
|
if (!user) {
|
|
62
|
-
throw
|
|
123
|
+
throw Unauthorized();
|
|
63
124
|
}
|
|
64
125
|
const { exp, iat, ...rest } = user;
|
|
65
126
|
const token = createAuthToken(rest);
|
|
@@ -86,17 +147,17 @@ function createAuth({ getUserByToken, methods: { emailAndPassword }, filterUser
|
|
|
86
147
|
}
|
|
87
148
|
throw new InvalidToken('token expired');
|
|
88
149
|
}
|
|
89
|
-
const decoded =
|
|
150
|
+
const decoded = jwt.verify(token.trim(), process.env.APP_JWT_SECRET);
|
|
90
151
|
if (typeof decoded === 'string') {
|
|
91
152
|
throw new Error('Failed to decode token');
|
|
92
153
|
}
|
|
93
154
|
const user = await getUserByToken({ ...decoded });
|
|
94
|
-
|
|
155
|
+
setContext('currentUser', user);
|
|
95
156
|
next();
|
|
96
157
|
}
|
|
97
158
|
catch (error) {
|
|
98
|
-
|
|
99
|
-
next(
|
|
159
|
+
getLogger('core').error(error);
|
|
160
|
+
next(Unauthorized('Invalid token'));
|
|
100
161
|
return;
|
|
101
162
|
}
|
|
102
163
|
};
|
|
@@ -107,12 +168,11 @@ function createAuth({ getUserByToken, methods: { emailAndPassword }, filterUser
|
|
|
107
168
|
middleware,
|
|
108
169
|
};
|
|
109
170
|
}
|
|
110
|
-
exports.createAuth = createAuth;
|
|
111
171
|
/**
|
|
112
172
|
* Extract a Bearer token from the HTTP Authorization header
|
|
113
173
|
* of `req`.
|
|
114
174
|
*/
|
|
115
|
-
function parseAuthorizationHeader(req) {
|
|
175
|
+
export function parseAuthorizationHeader(req) {
|
|
116
176
|
if (!req.headers.authorization) {
|
|
117
177
|
return null;
|
|
118
178
|
}
|
|
@@ -131,7 +191,6 @@ function parseAuthorizationHeader(req) {
|
|
|
131
191
|
}
|
|
132
192
|
return token;
|
|
133
193
|
}
|
|
134
|
-
exports.parseAuthorizationHeader = parseAuthorizationHeader;
|
|
135
194
|
function extractAccessTokenFromRequest(req) {
|
|
136
195
|
const authorizationHeader = parseAuthorizationHeader(req);
|
|
137
196
|
if (authorizationHeader) {
|
|
@@ -142,9 +201,9 @@ function extractAccessTokenFromRequest(req) {
|
|
|
142
201
|
}
|
|
143
202
|
return null;
|
|
144
203
|
}
|
|
145
|
-
function setAccessTokenCookie(res, token) {
|
|
204
|
+
export function setAccessTokenCookie(res, token) {
|
|
146
205
|
if (token == null) {
|
|
147
|
-
res.cookie('access_token',
|
|
206
|
+
res.cookie('access_token', v4(), {
|
|
148
207
|
expires: new Date(1970, 0, 1),
|
|
149
208
|
});
|
|
150
209
|
}
|
|
@@ -155,25 +214,22 @@ function setAccessTokenCookie(res, token) {
|
|
|
155
214
|
});
|
|
156
215
|
}
|
|
157
216
|
}
|
|
158
|
-
exports.setAccessTokenCookie = setAccessTokenCookie;
|
|
159
217
|
class InvalidToken extends Error {
|
|
160
218
|
constructor(message) {
|
|
161
219
|
super(message);
|
|
162
220
|
this.name = 'Invalid Token';
|
|
163
221
|
}
|
|
164
222
|
}
|
|
165
|
-
async function checkIfPasswordsMatch(realPassword, attemptedPassword) {
|
|
223
|
+
export async function checkIfPasswordsMatch(realPassword, attemptedPassword) {
|
|
166
224
|
if (!realPassword) {
|
|
167
225
|
throw new Error('User has no defined password');
|
|
168
226
|
}
|
|
169
|
-
const validPassword = await
|
|
227
|
+
const validPassword = await bcrypt.compare(attemptedPassword, realPassword);
|
|
170
228
|
if (!validPassword) {
|
|
171
229
|
throw new Error(`Passwords does not match`);
|
|
172
230
|
}
|
|
173
231
|
return true;
|
|
174
232
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return bcrypt_1.default.hash(password, 10);
|
|
233
|
+
export function hashPassword(password) {
|
|
234
|
+
return bcrypt.hash(password, 10);
|
|
178
235
|
}
|
|
179
|
-
exports.hashPassword = hashPassword;
|
package/dist/context.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Request, RequestHandler } from 'express';
|
|
2
|
-
interface RequestContextOptions {
|
|
1
|
+
import { type Request, type RequestHandler } from 'express';
|
|
2
|
+
export interface RequestContextOptions {
|
|
3
3
|
getRequestId?: (req: Request) => string | undefined;
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
@@ -23,4 +23,3 @@ export declare function useRequestId(): string | undefined;
|
|
|
23
23
|
* @deprecated
|
|
24
24
|
*/
|
|
25
25
|
export declare const getRequestId: typeof useRequestId;
|
|
26
|
-
export {};
|
package/dist/context.js
CHANGED
|
@@ -1,49 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const cls_hooked_1 = require("cls-hooked");
|
|
5
|
-
const uuid_1 = require("uuid");
|
|
6
|
-
const requestContext = (0, cls_hooked_1.createNamespace)('@requestContext');
|
|
1
|
+
import { createNamespace } from 'cls-hooked';
|
|
2
|
+
import { v4 as uuid } from 'uuid';
|
|
3
|
+
const requestContext = createNamespace('@requestContext');
|
|
7
4
|
/**
|
|
8
5
|
* Middleware for setting a `requestId` on the request context.
|
|
9
6
|
*/
|
|
10
|
-
function withRequestContext({ getRequestId = (req) => req.headers['X-Request-ID'], } = {}) {
|
|
7
|
+
export function withRequestContext({ getRequestId = (req) => req.headers['X-Request-ID'], } = {}) {
|
|
11
8
|
return (req, res, next) => {
|
|
12
|
-
const requestId = getRequestId(req) || (
|
|
9
|
+
const requestId = getRequestId(req) || uuid();
|
|
13
10
|
requestContext.run(() => {
|
|
14
11
|
requestContext.set('requestId', requestId);
|
|
15
12
|
next();
|
|
16
13
|
});
|
|
17
14
|
};
|
|
18
15
|
}
|
|
19
|
-
exports.withRequestContext = withRequestContext;
|
|
20
16
|
/**
|
|
21
17
|
* Return a value from the request context
|
|
22
18
|
*/
|
|
23
|
-
function useContext(key) {
|
|
19
|
+
export function useContext(key) {
|
|
24
20
|
if (requestContext && requestContext.active) {
|
|
25
21
|
return requestContext.get(key);
|
|
26
22
|
}
|
|
27
23
|
}
|
|
28
|
-
exports.useContext = useContext;
|
|
29
24
|
/**
|
|
30
25
|
* @deprecated
|
|
31
26
|
*/
|
|
32
|
-
|
|
27
|
+
export const getContext = useContext;
|
|
33
28
|
/**
|
|
34
29
|
* Set a value on the request context
|
|
35
30
|
*/
|
|
36
|
-
function setContext(key, value) {
|
|
31
|
+
export function setContext(key, value) {
|
|
37
32
|
if (requestContext && requestContext.active) {
|
|
38
33
|
return requestContext.set(key, value);
|
|
39
34
|
}
|
|
40
35
|
}
|
|
41
|
-
|
|
42
|
-
function useRequestId() {
|
|
36
|
+
export function useRequestId() {
|
|
43
37
|
return useContext('requestId');
|
|
44
38
|
}
|
|
45
|
-
exports.useRequestId = useRequestId;
|
|
46
39
|
/**
|
|
47
40
|
* @deprecated
|
|
48
41
|
*/
|
|
49
|
-
|
|
42
|
+
export const getRequestId = useRequestId;
|
package/dist/email.js
CHANGED
|
@@ -1,32 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.createEmail = void 0;
|
|
27
|
-
const nodemailer = __importStar(require("nodemailer"));
|
|
28
|
-
const logger_1 = require("./logger");
|
|
29
|
-
function createEmail({ host, port, user, pass, senderName, senderAddress, }) {
|
|
1
|
+
import * as nodemailer from 'nodemailer';
|
|
2
|
+
import { getLogger } from './logger.js';
|
|
3
|
+
export function createEmail({ host, port, user, pass, senderName, senderAddress, }) {
|
|
30
4
|
const transporter = nodemailer.createTransport({
|
|
31
5
|
host,
|
|
32
6
|
port: parseInt(port || '587', 10),
|
|
@@ -41,8 +15,7 @@ function createEmail({ host, port, user, pass, senderName, senderAddress, }) {
|
|
|
41
15
|
text: message,
|
|
42
16
|
...rest,
|
|
43
17
|
});
|
|
44
|
-
|
|
18
|
+
getLogger('core').info('email-sent', { email: result });
|
|
45
19
|
};
|
|
46
20
|
return { send };
|
|
47
21
|
}
|
|
48
|
-
exports.createEmail = createEmail;
|
package/dist/errors.js
CHANGED
|
@@ -1,58 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.errorHandler = exports.mapToProblem = exports.makeError = exports.Conflict = exports.Unauthorized = exports.NotFound = exports.Forbidden = exports.BadRequest = exports.ApiError = void 0;
|
|
27
|
-
const http_1 = require("http");
|
|
28
|
-
const Sentry = __importStar(require("@sentry/node"));
|
|
29
|
-
const compose_middleware_1 = require("compose-middleware");
|
|
30
|
-
class ApiError extends Error {
|
|
1
|
+
import * as Sentry from '@sentry/node';
|
|
2
|
+
import { errors } from 'compose-middleware';
|
|
3
|
+
import { STATUS_CODES } from 'node:http';
|
|
4
|
+
export class ApiError extends Error {
|
|
31
5
|
}
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
const NotFound = (detail = 'Could not find resource') => makeError(404, detail);
|
|
38
|
-
exports.NotFound = NotFound;
|
|
39
|
-
const Unauthorized = (detail = 'Unauthorized') => makeError(401, detail);
|
|
40
|
-
exports.Unauthorized = Unauthorized;
|
|
41
|
-
const Conflict = (detail = '') => makeError(409, detail);
|
|
42
|
-
exports.Conflict = Conflict;
|
|
6
|
+
export const BadRequest = (reason) => makeError(400, 'Invalid Params', { reason });
|
|
7
|
+
export const Forbidden = (detail = '') => makeError(403, detail);
|
|
8
|
+
export const NotFound = (detail = 'Could not find resource') => makeError(404, detail);
|
|
9
|
+
export const Unauthorized = (detail = 'Unauthorized') => makeError(401, detail);
|
|
10
|
+
export const Conflict = (detail = '') => makeError(409, detail);
|
|
43
11
|
/**
|
|
44
12
|
* Create a new ApiError instance
|
|
45
13
|
*/
|
|
46
|
-
function makeError(status, detail, payload) {
|
|
47
|
-
const error = new ApiError(detail ||
|
|
14
|
+
export function makeError(status, detail, payload) {
|
|
15
|
+
const error = new ApiError(detail || STATUS_CODES[status]);
|
|
48
16
|
error.status = status;
|
|
49
17
|
error.payload = payload;
|
|
50
18
|
return error;
|
|
51
19
|
}
|
|
52
|
-
|
|
53
|
-
function mapToProblem(error) {
|
|
20
|
+
export function mapToProblem(error) {
|
|
54
21
|
if (error instanceof ApiError) {
|
|
55
|
-
const statusMessage =
|
|
22
|
+
const statusMessage = STATUS_CODES[error.status || '500'];
|
|
56
23
|
return {
|
|
57
24
|
type: `/api/problems/${statusMessage
|
|
58
25
|
.toLocaleLowerCase()
|
|
@@ -69,14 +36,13 @@ function mapToProblem(error) {
|
|
|
69
36
|
status: 500,
|
|
70
37
|
};
|
|
71
38
|
}
|
|
72
|
-
exports.mapToProblem = mapToProblem;
|
|
73
39
|
function _errorHandler({ onError }) {
|
|
74
40
|
return (err, req, res, next // eslint-disable-line
|
|
75
41
|
) => {
|
|
76
42
|
if (!err) {
|
|
77
43
|
err = makeError(500);
|
|
78
44
|
}
|
|
79
|
-
onError
|
|
45
|
+
onError?.(err);
|
|
80
46
|
const problem = mapToProblem(err);
|
|
81
47
|
res.status(problem.status).json(problem);
|
|
82
48
|
};
|
|
@@ -86,8 +52,8 @@ function _errorHandler({ onError }) {
|
|
|
86
52
|
*
|
|
87
53
|
* Composed together with the Sentry error handler.
|
|
88
54
|
*/
|
|
89
|
-
function errorHandler({ onError }) {
|
|
90
|
-
return
|
|
55
|
+
export function errorHandler({ onError }) {
|
|
56
|
+
return errors([
|
|
91
57
|
Sentry.Handlers.errorHandler({
|
|
92
58
|
shouldHandleError(error) {
|
|
93
59
|
if (error instanceof ApiError && error.status < 500) {
|
|
@@ -99,4 +65,3 @@ function errorHandler({ onError }) {
|
|
|
99
65
|
_errorHandler({ onError }),
|
|
100
66
|
]);
|
|
101
67
|
}
|
|
102
|
-
exports.errorHandler = errorHandler;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
export * from './auth';
|
|
2
|
-
export * from './context';
|
|
3
|
-
export * from './email';
|
|
4
|
-
export * from './errors';
|
|
5
|
-
export * from './listen';
|
|
6
|
-
export * from './logger';
|
|
7
|
-
export * from './middleware';
|
|
8
|
-
export * from './postgres';
|
|
9
|
-
export * from './redis';
|
|
10
|
-
export * from './routes';
|
|
11
|
-
export * from './sentry';
|
|
12
|
-
export * from './slack';
|
|
13
|
-
export * from './validate';
|
|
1
|
+
export * from './auth.js';
|
|
2
|
+
export * from './context.js';
|
|
3
|
+
export * from './email.js';
|
|
4
|
+
export * from './errors.js';
|
|
5
|
+
export * from './listen.js';
|
|
6
|
+
export * from './logger.js';
|
|
7
|
+
export * from './middleware.js';
|
|
8
|
+
export * from './postgres.js';
|
|
9
|
+
export * from './redis.js';
|
|
10
|
+
export * from './routes.js';
|
|
11
|
+
export * from './sentry.js';
|
|
12
|
+
export * from './slack.js';
|
|
13
|
+
export * from './validate.js';
|
|
14
14
|
export declare const envPort: (defaultPort?: number) => number;
|
package/dist/index.js
CHANGED
|
@@ -1,32 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.envPort = void 0;
|
|
18
|
-
__exportStar(require("./auth"), exports);
|
|
19
|
-
__exportStar(require("./context"), exports);
|
|
20
|
-
__exportStar(require("./email"), exports);
|
|
21
|
-
__exportStar(require("./errors"), exports);
|
|
22
|
-
__exportStar(require("./listen"), exports);
|
|
23
|
-
__exportStar(require("./logger"), exports);
|
|
24
|
-
__exportStar(require("./middleware"), exports);
|
|
25
|
-
__exportStar(require("./postgres"), exports);
|
|
26
|
-
__exportStar(require("./redis"), exports);
|
|
27
|
-
__exportStar(require("./routes"), exports);
|
|
28
|
-
__exportStar(require("./sentry"), exports);
|
|
29
|
-
__exportStar(require("./slack"), exports);
|
|
30
|
-
__exportStar(require("./validate"), exports);
|
|
31
|
-
const envPort = (defaultPort = 3000) => parseInt(process.env.PORT, 10) || defaultPort;
|
|
32
|
-
exports.envPort = envPort;
|
|
1
|
+
export * from './auth.js';
|
|
2
|
+
export * from './context.js';
|
|
3
|
+
export * from './email.js';
|
|
4
|
+
export * from './errors.js';
|
|
5
|
+
export * from './listen.js';
|
|
6
|
+
export * from './logger.js';
|
|
7
|
+
export * from './middleware.js';
|
|
8
|
+
export * from './postgres.js';
|
|
9
|
+
export * from './redis.js';
|
|
10
|
+
export * from './routes.js';
|
|
11
|
+
export * from './sentry.js';
|
|
12
|
+
export * from './slack.js';
|
|
13
|
+
export * from './validate.js';
|
|
14
|
+
export const envPort = (defaultPort = 3000) => parseInt(process.env.PORT, 10) || defaultPort;
|
package/dist/listen.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import http from 'http';
|
|
3
|
-
import { createLogger } from './logger';
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import type * as http from 'node:http';
|
|
4
3
|
/**
|
|
5
4
|
* Listen on the given port and setup proper signal
|
|
6
5
|
* handlers for terminating the process properly.
|
|
@@ -9,7 +8,6 @@ import { createLogger } from './logger';
|
|
|
9
8
|
*/
|
|
10
9
|
export declare function listen(server: http.Server, options: {
|
|
11
10
|
port: number;
|
|
12
|
-
logger: ReturnType<typeof createLogger>;
|
|
13
11
|
onShutdown?: () => void | Promise<void>;
|
|
14
12
|
healthCheck?: () => Promise<void>;
|
|
15
13
|
}): void;
|