fastify-txstate 3.6.9 → 4.0.1
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/README.md +392 -7
- package/lib/analytics.d.ts +5 -2
- package/lib/analytics.js +28 -33
- package/lib/error.d.ts +1 -10
- package/lib/error.js +7 -28
- package/lib/filestorage.d.ts +1 -1
- package/lib/filestorage.js +27 -31
- package/lib/index.d.ts +9 -194
- package/lib/index.js +9 -422
- package/lib/jwt-auth.d.ts +98 -0
- package/lib/jwt-auth.js +491 -0
- package/lib/oauth.d.ts +49 -0
- package/lib/oauth.js +272 -0
- package/lib/postformdata.js +13 -17
- package/{lib-esm/index.d.ts → lib/server.d.ts} +69 -37
- package/lib/server.js +441 -0
- package/lib/unified-auth.d.ts +13 -7
- package/lib/unified-auth.js +48 -174
- package/package.json +27 -25
- package/lib-esm/index.js +0 -20
- package/lib-esm/package.json +0 -3
package/lib/error.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.ValidationErrors = exports.ValidationError = exports.FailedValidationError = exports.HttpError = void 0;
|
|
4
|
-
exports.fstValidationToMessage = fstValidationToMessage;
|
|
5
|
-
const http_status_codes_1 = require("http-status-codes");
|
|
6
|
-
class HttpError extends Error {
|
|
1
|
+
import { getReasonPhrase } from 'http-status-codes';
|
|
2
|
+
export class HttpError extends Error {
|
|
7
3
|
statusCode;
|
|
8
4
|
constructor(statusCode, message) {
|
|
9
5
|
if (!message) {
|
|
@@ -12,28 +8,13 @@ class HttpError extends Error {
|
|
|
12
8
|
else if (statusCode === 403)
|
|
13
9
|
message = 'You are not authorized for that.';
|
|
14
10
|
else
|
|
15
|
-
message =
|
|
11
|
+
message = getReasonPhrase(statusCode);
|
|
16
12
|
}
|
|
17
13
|
super(message);
|
|
18
14
|
this.statusCode = statusCode;
|
|
19
15
|
}
|
|
20
16
|
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @deprecated This response format is less flexible than the one based on the ValidationMessage
|
|
24
|
-
* interface. Use ValidationError or ValidationErrors instead, and adjust the client to expect
|
|
25
|
-
* the new format.
|
|
26
|
-
*/
|
|
27
|
-
class FailedValidationError extends HttpError {
|
|
28
|
-
errors;
|
|
29
|
-
constructor(errors) {
|
|
30
|
-
super(422, 'Validation failure.');
|
|
31
|
-
this.errors = errors;
|
|
32
|
-
this.errors = errors;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
exports.FailedValidationError = FailedValidationError;
|
|
36
|
-
class ValidationError extends HttpError {
|
|
17
|
+
export class ValidationError extends HttpError {
|
|
37
18
|
path;
|
|
38
19
|
type;
|
|
39
20
|
constructor(message, path, type) {
|
|
@@ -42,16 +23,14 @@ class ValidationError extends HttpError {
|
|
|
42
23
|
this.type = type;
|
|
43
24
|
}
|
|
44
25
|
}
|
|
45
|
-
|
|
46
|
-
class ValidationErrors extends HttpError {
|
|
26
|
+
export class ValidationErrors extends HttpError {
|
|
47
27
|
errors;
|
|
48
28
|
constructor(errors) {
|
|
49
29
|
super(422, errors[0]?.message);
|
|
50
30
|
this.errors = errors;
|
|
51
31
|
}
|
|
52
32
|
}
|
|
53
|
-
|
|
54
|
-
function fstValidationToMessage(v) {
|
|
33
|
+
export function fstValidationToMessage(v) {
|
|
55
34
|
const instancePath = v.keyword === 'required' ? v.instancePath + '/' + v.params.missingProperty : v.instancePath;
|
|
56
|
-
return { message: v.message, path: instancePath.substring(1).replace(/\//
|
|
35
|
+
return { message: v.message, path: instancePath.substring(1).replace(/\//gv, '.'), type: 'error' };
|
|
57
36
|
}
|
package/lib/filestorage.d.ts
CHANGED
package/lib/filestorage.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const promises_2 = require("stream/promises");
|
|
9
|
-
const txstate_utils_1 = require("txstate-utils");
|
|
10
|
-
class FileSystemHandler {
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
3
|
+
import { access, constants, mkdir, rename, unlink, stat } from 'node:fs/promises';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
|
+
import { pipeline } from 'node:stream/promises';
|
|
6
|
+
import { rescue, randomid } from 'txstate-utils';
|
|
7
|
+
export class FileSystemHandler {
|
|
11
8
|
options;
|
|
12
9
|
constructor(options = {}) {
|
|
13
10
|
this.options = {
|
|
@@ -20,50 +17,50 @@ class FileSystemHandler {
|
|
|
20
17
|
this.options.permdir += '/';
|
|
21
18
|
}
|
|
22
19
|
#getTmpLocation() {
|
|
23
|
-
return `${this.options.tmpdir}${
|
|
20
|
+
return `${this.options.tmpdir}${randomid(12)}`;
|
|
24
21
|
}
|
|
25
22
|
#getFileLocation(checksum) {
|
|
26
23
|
return `${this.options.permdir}${checksum.slice(0, 1)}/${checksum.slice(1, 2)}/${checksum.slice(2)}`;
|
|
27
24
|
}
|
|
28
25
|
async #moveToPerm(tmp, checksum) {
|
|
29
26
|
const checksumpath = this.#getFileLocation(checksum);
|
|
30
|
-
await
|
|
31
|
-
await
|
|
27
|
+
await mkdir(dirname(checksumpath), { recursive: true });
|
|
28
|
+
await rename(tmp, checksumpath);
|
|
32
29
|
}
|
|
33
30
|
async init() {
|
|
34
|
-
await
|
|
35
|
-
await
|
|
31
|
+
await mkdir(this.options.tmpdir, { recursive: true });
|
|
32
|
+
await mkdir(this.options.permdir, { recursive: true });
|
|
36
33
|
}
|
|
37
34
|
get(checksum) {
|
|
38
35
|
const filepath = this.#getFileLocation(checksum);
|
|
39
|
-
const stream =
|
|
36
|
+
const stream = createReadStream(filepath);
|
|
40
37
|
return stream;
|
|
41
38
|
}
|
|
42
39
|
async exists(checksum) {
|
|
43
40
|
const filepath = this.#getFileLocation(checksum);
|
|
44
|
-
return (await
|
|
41
|
+
return (await rescue(access(filepath, constants.R_OK), false)) !== false;
|
|
45
42
|
}
|
|
46
43
|
async fileSize(checksum) {
|
|
47
44
|
const filepath = this.#getFileLocation(checksum);
|
|
48
|
-
const info = await
|
|
45
|
+
const info = await stat(filepath);
|
|
49
46
|
return info.size;
|
|
50
47
|
}
|
|
51
48
|
async put(stream) {
|
|
52
49
|
const tmp = this.#getTmpLocation();
|
|
53
|
-
const hash =
|
|
50
|
+
const hash = createHash('sha256');
|
|
54
51
|
let size = 0;
|
|
55
52
|
stream.on('data', (data) => { hash.update(data); size += data.length; });
|
|
56
53
|
try {
|
|
57
|
-
const out =
|
|
54
|
+
const out = createWriteStream(tmp);
|
|
58
55
|
const flushedPromise = new Promise((resolve, reject) => {
|
|
59
56
|
out.on('close', resolve);
|
|
60
57
|
out.on('error', reject);
|
|
61
58
|
});
|
|
62
|
-
await
|
|
59
|
+
await pipeline(stream, out);
|
|
63
60
|
await flushedPromise;
|
|
64
61
|
const checksum = hash.digest('base64url');
|
|
65
|
-
const rereadhash =
|
|
66
|
-
const read =
|
|
62
|
+
const rereadhash = createHash('sha256');
|
|
63
|
+
const read = createReadStream(tmp);
|
|
67
64
|
for await (const chunk of read) {
|
|
68
65
|
rereadhash.update(chunk);
|
|
69
66
|
}
|
|
@@ -74,22 +71,21 @@ class FileSystemHandler {
|
|
|
74
71
|
return { checksum, size };
|
|
75
72
|
}
|
|
76
73
|
catch (e) {
|
|
77
|
-
await
|
|
74
|
+
await rescue(unlink(tmp));
|
|
78
75
|
throw e;
|
|
79
76
|
}
|
|
80
77
|
}
|
|
81
78
|
async remove(checksum) {
|
|
82
79
|
const filepath = this.#getFileLocation(checksum);
|
|
83
80
|
try {
|
|
84
|
-
await
|
|
81
|
+
await unlink(filepath);
|
|
85
82
|
}
|
|
86
83
|
catch (e) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
const err = e;
|
|
85
|
+
if (err.code === 'ENOENT')
|
|
86
|
+
return; // file already gone, nothing to do
|
|
87
|
+
throw e;
|
|
91
88
|
}
|
|
92
89
|
}
|
|
93
90
|
}
|
|
94
|
-
|
|
95
|
-
exports.fileHandler = new FileSystemHandler();
|
|
91
|
+
export const fileHandler = new FileSystemHandler();
|
package/lib/index.d.ts
CHANGED
|
@@ -1,194 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
* The primary identifier for the user that is making the request, after processing
|
|
11
|
-
* their session token / JWT.
|
|
12
|
-
*/
|
|
13
|
-
username: string;
|
|
14
|
-
/**
|
|
15
|
-
* This should be an identifier for the particular session, so that the same user
|
|
16
|
-
* on different devices/browsers/tabs can be distinguished from one another.
|
|
17
|
-
*
|
|
18
|
-
* It should NOT be usable as a cookie or bearer token, as it will appear in logs. If you
|
|
19
|
-
* use JSON Web Tokens, an easy thing is to combine the username with the `iat` issued
|
|
20
|
-
* date to create something unique but not useful to attackers.
|
|
21
|
-
*
|
|
22
|
-
* For lookup tokens, you can do the same `${username}-${createdAt}` after looking up
|
|
23
|
-
* the session in your database.
|
|
24
|
-
*
|
|
25
|
-
* If all else fails, you can sha256 the session token with a salt.
|
|
26
|
-
*/
|
|
27
|
-
sessionId: string;
|
|
28
|
-
/**
|
|
29
|
-
* The date that the session was created, if available. This is useful for considering
|
|
30
|
-
* tokens before a certain date as invalid. For instance, if you want a logout action
|
|
31
|
-
* to invalidate all tokens created until that point, you can compare this field against
|
|
32
|
-
* the last time they logged out.
|
|
33
|
-
*/
|
|
34
|
-
sessionCreatedAt?: Date;
|
|
35
|
-
/**
|
|
36
|
-
* Some authentication systems allow administrators to impersonate regular users, so that
|
|
37
|
-
* they can see what that user sees and troubleshoot issues. We still want to log the administrator
|
|
38
|
-
* with any actions they take while impersonating someone, for auditing purposes, so you should
|
|
39
|
-
* fill this field when applicable.
|
|
40
|
-
*
|
|
41
|
-
* This will also be available at `req.auth.impersonatedBy`, so it is possible for your API
|
|
42
|
-
* to implement complicated authorization rules based on whether a user is being impersonated.
|
|
43
|
-
* It sort of defeats the purpose of impersonation, but used sparingly it could prevent administrators
|
|
44
|
-
* from making mistakes.
|
|
45
|
-
*/
|
|
46
|
-
impersonatedBy?: string;
|
|
47
|
-
/**
|
|
48
|
-
* If your API may be accessed by a different client application, such that the user is actually logged
|
|
49
|
-
* into that application instead of yours, but you accept that application's session tokens, filling
|
|
50
|
-
* this field can help log requests that are authenticated with the other application's token.
|
|
51
|
-
*/
|
|
52
|
-
clientId?: string;
|
|
53
|
-
/**
|
|
54
|
-
* A string that designates the current session as one that has limited authorization. The application will
|
|
55
|
-
* be responsible for checking this field and restricting appropriately.
|
|
56
|
-
*
|
|
57
|
-
* For example, a user who authenticated via non-standard mechanism might be given a scope of 'altlogin' and
|
|
58
|
-
* only a portion of the application's functionality would be available to them.
|
|
59
|
-
*/
|
|
60
|
-
scope?: string;
|
|
61
|
-
/**
|
|
62
|
-
* The token or key that was used to authenticate the request. This is useful for
|
|
63
|
-
* making sub-requests to other APIs that can authenticate with the same token.
|
|
64
|
-
*/
|
|
65
|
-
token: string;
|
|
66
|
-
/**
|
|
67
|
-
* The issuer configuration for the token, if applicable. This helps you generate
|
|
68
|
-
* a proper logout url in multi-issuer environments.
|
|
69
|
-
*/
|
|
70
|
-
issuerConfig?: IssuerConfig;
|
|
71
|
-
}
|
|
72
|
-
export interface IssuerConfig {
|
|
73
|
-
iss: string;
|
|
74
|
-
url?: string;
|
|
75
|
-
publicKey?: string;
|
|
76
|
-
secret?: string;
|
|
77
|
-
validateUrl?: URL;
|
|
78
|
-
logoutUrl?: URL;
|
|
79
|
-
}
|
|
80
|
-
export interface FastifyTxStateOptions extends Partial<FastifyServerOptions> {
|
|
81
|
-
https?: http2.SecureServerOptions;
|
|
82
|
-
validOrigins?: string[];
|
|
83
|
-
validOriginHosts?: string[];
|
|
84
|
-
validOriginSuffixes?: string[];
|
|
85
|
-
skipOriginCheck?: boolean;
|
|
86
|
-
checkOrigin?: (req: FastifyRequest) => boolean;
|
|
87
|
-
/**
|
|
88
|
-
* Run an asynchronous function to check the health of the service.
|
|
89
|
-
*
|
|
90
|
-
* Return a non-empty error message to trigger unhealthy status.
|
|
91
|
-
*
|
|
92
|
-
* Setting a health message with setUnhealthy will override this and prevent it from being executed.
|
|
93
|
-
*/
|
|
94
|
-
checkHealth?: () => Promise<string | {
|
|
95
|
-
status?: number;
|
|
96
|
-
message?: string;
|
|
97
|
-
} | undefined>;
|
|
98
|
-
/**
|
|
99
|
-
* Run an async function to get authentication information out of the request
|
|
100
|
-
* object. Should return an object with at least a username and sessionid (see FastifyTxStateAuthInfo
|
|
101
|
-
* for further detail).
|
|
102
|
-
*
|
|
103
|
-
* The return object will be added to the request object as `req.auth` for later
|
|
104
|
-
* use in your route handlers. It will also be added to the logs in production.
|
|
105
|
-
*
|
|
106
|
-
* IMPORTANT: It is not advisable to return excessive amounts of data here, nor anything
|
|
107
|
-
* particularly sensitive, since it will all be included in every log entry.
|
|
108
|
-
*
|
|
109
|
-
* If this function throws, the client will receive a 401 response.
|
|
110
|
-
*/
|
|
111
|
-
authenticate?: (req: FastifyRequest) => Promise<FastifyTxStateAuthInfo | undefined>;
|
|
112
|
-
}
|
|
113
|
-
declare module 'fastify' {
|
|
114
|
-
interface FastifyRequest {
|
|
115
|
-
auth?: FastifyTxStateAuthInfo;
|
|
116
|
-
/**
|
|
117
|
-
* @deprecated Use `req.auth.token` instead. Just trying to keep everything contained.
|
|
118
|
-
* This will be removed in the next major version.
|
|
119
|
-
*/
|
|
120
|
-
token?: string;
|
|
121
|
-
}
|
|
122
|
-
interface FastifyReply {
|
|
123
|
-
extraLogInfo: any;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
export declare const devLogger: {
|
|
127
|
-
level: string;
|
|
128
|
-
info: (msg: any) => void;
|
|
129
|
-
error: {
|
|
130
|
-
(...data: any[]): void;
|
|
131
|
-
(message?: any, ...optionalParams: any[]): void;
|
|
132
|
-
};
|
|
133
|
-
debug: {
|
|
134
|
-
(...data: any[]): void;
|
|
135
|
-
(message?: any, ...optionalParams: any[]): void;
|
|
136
|
-
};
|
|
137
|
-
fatal: {
|
|
138
|
-
(...data: any[]): void;
|
|
139
|
-
(message?: any, ...optionalParams: any[]): void;
|
|
140
|
-
};
|
|
141
|
-
warn: {
|
|
142
|
-
(...data: any[]): void;
|
|
143
|
-
(message?: any, ...optionalParams: any[]): void;
|
|
144
|
-
};
|
|
145
|
-
trace: {
|
|
146
|
-
(...data: any[]): void;
|
|
147
|
-
(message?: any, ...optionalParams: any[]): void;
|
|
148
|
-
};
|
|
149
|
-
silent: (msg: any) => void;
|
|
150
|
-
child(bindings: any, options?: any): /*elided*/ any;
|
|
151
|
-
};
|
|
152
|
-
export declare const prodLogger: FastifyLoggerOptions;
|
|
153
|
-
export type FastifyInstanceTyped = FastifyInstance<RawServerDefault, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, FastifyBaseLogger, JsonSchemaToTsProvider>;
|
|
154
|
-
export type TxServer = Server;
|
|
155
|
-
export default class Server {
|
|
156
|
-
protected config: FastifyTxStateOptions & {
|
|
157
|
-
http2?: true;
|
|
158
|
-
};
|
|
159
|
-
protected https: boolean;
|
|
160
|
-
protected errorHandlers: ErrorHandler[];
|
|
161
|
-
protected healthMessage?: string;
|
|
162
|
-
protected healthCallback?: () => Promise<string | {
|
|
163
|
-
status?: number;
|
|
164
|
-
message?: string;
|
|
165
|
-
} | undefined>;
|
|
166
|
-
protected shuttingDown: boolean;
|
|
167
|
-
protected sigHandler: (signal: any) => void;
|
|
168
|
-
protected validOrigins: Record<string, boolean>;
|
|
169
|
-
protected validOriginHosts: Record<string, boolean>;
|
|
170
|
-
protected validOriginSuffixes: Set<string>;
|
|
171
|
-
protected swaggerEndpoint: string | undefined;
|
|
172
|
-
app: FastifyInstanceTyped;
|
|
173
|
-
constructor(config?: FastifyTxStateOptions & {
|
|
174
|
-
http2?: true;
|
|
175
|
-
});
|
|
176
|
-
start(port?: number): Promise<void>;
|
|
177
|
-
addErrorHandler(handler: ErrorHandler): void;
|
|
178
|
-
setUnhealthy(message: string): void;
|
|
179
|
-
setHealthy(): void;
|
|
180
|
-
setValidOrigins(origins: string[]): void;
|
|
181
|
-
setValidOriginHosts(hosts: string[]): void;
|
|
182
|
-
setValidOriginSuffixes(suffixes: string[]): void;
|
|
183
|
-
swagger(opts?: {
|
|
184
|
-
path?: string;
|
|
185
|
-
openapi?: FastifyDynamicSwaggerOptions['openapi'];
|
|
186
|
-
ui?: FastifySwaggerUiOptions;
|
|
187
|
-
}): Promise<void>;
|
|
188
|
-
close(softSeconds?: number): Promise<void>;
|
|
189
|
-
}
|
|
190
|
-
export * from './analytics';
|
|
191
|
-
export * from './error';
|
|
192
|
-
export * from './filestorage';
|
|
193
|
-
export * from './unified-auth';
|
|
194
|
-
export * from './postformdata';
|
|
1
|
+
export { default } from './server.ts';
|
|
2
|
+
export * from './server.ts';
|
|
3
|
+
export * from './analytics.ts';
|
|
4
|
+
export * from './error.ts';
|
|
5
|
+
export * from './filestorage.ts';
|
|
6
|
+
export * from './jwt-auth.ts';
|
|
7
|
+
export * from './unified-auth.ts';
|
|
8
|
+
export * from './oauth.ts';
|
|
9
|
+
export * from './postformdata.ts';
|