mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
Merge pull request #3362 from logto-io/gao-use-compressed-file-when-possible
refactor(cloud): send compressed spa file when possible
This commit is contained in:
commit
eed6e80e45
6 changed files with 64 additions and 26 deletions
|
@ -29,7 +29,8 @@
|
||||||
"@logto/shared": "workspace:*",
|
"@logto/shared": "workspace:*",
|
||||||
"@silverhand/essentials": "2.4.0",
|
"@silverhand/essentials": "2.4.0",
|
||||||
"@withtyped/postgres": "^0.8.1",
|
"@withtyped/postgres": "^0.8.1",
|
||||||
"@withtyped/server": "^0.8.0",
|
"@withtyped/server": "^0.8.1",
|
||||||
|
"accepts": "^1.3.8",
|
||||||
"chalk": "^5.0.0",
|
"chalk": "^5.0.0",
|
||||||
"decamelize": "^6.0.0",
|
"decamelize": "^6.0.0",
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
|
@ -44,6 +45,7 @@
|
||||||
"@silverhand/eslint-config": "2.0.1",
|
"@silverhand/eslint-config": "2.0.1",
|
||||||
"@silverhand/jest-config": "^2.0.1",
|
"@silverhand/jest-config": "^2.0.1",
|
||||||
"@silverhand/ts-config": "2.0.3",
|
"@silverhand/ts-config": "2.0.3",
|
||||||
|
"@types/accepts": "^1.3.5",
|
||||||
"@types/http-proxy": "^1.17.9",
|
"@types/http-proxy": "^1.17.9",
|
||||||
"@types/jest": "^29.4.0",
|
"@types/jest": "^29.4.0",
|
||||||
"@types/mime-types": "^2.1.1",
|
"@types/mime-types": "^2.1.1",
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { createReadStream } from 'node:fs';
|
import { createReadStream } from 'node:fs';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
|
import type { IncomingMessage } from 'node:http';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
import { assert } from '@silverhand/essentials';
|
import { assert, conditional } from '@silverhand/essentials';
|
||||||
import type { NextFunction, RequestContext } from '@withtyped/server';
|
import type { HttpContext, NextFunction, RequestContext } from '@withtyped/server';
|
||||||
|
import accepts from 'accepts';
|
||||||
import mime from 'mime-types';
|
import mime from 'mime-types';
|
||||||
|
|
||||||
import { matchPathname } from '#src/utils/url.js';
|
import { matchPathname } from '#src/utils/url.js';
|
||||||
|
@ -39,7 +41,11 @@ export default function withSpa<InputContext extends RequestContext>({
|
||||||
}: WithSpaConfig) {
|
}: WithSpaConfig) {
|
||||||
assert(root, new Error('Root directory is required to serve files.'));
|
assert(root, new Error('Root directory is required to serve files.'));
|
||||||
|
|
||||||
return async (context: InputContext, next: NextFunction<InputContext>) => {
|
return async (
|
||||||
|
context: InputContext,
|
||||||
|
next: NextFunction<InputContext>,
|
||||||
|
{ request }: HttpContext
|
||||||
|
) => {
|
||||||
const {
|
const {
|
||||||
headers,
|
headers,
|
||||||
request: { url },
|
request: { url },
|
||||||
|
@ -63,14 +69,15 @@ export default function withSpa<InputContext extends RequestContext>({
|
||||||
return next({ ...context, status: 404 });
|
return next({ ...context, status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const [pathLike, stat] = result;
|
const [pathLike, stat, compression] = (await tryCompressedFile(request, result[0])) ?? result;
|
||||||
|
|
||||||
return next({
|
return next({
|
||||||
...context,
|
...context,
|
||||||
headers: {
|
headers: {
|
||||||
...headers,
|
...headers,
|
||||||
'Content-Length': stat.size,
|
...(compression && { 'Content-Encoding': compression }),
|
||||||
'Content-Type': mime.lookup(pathLike),
|
...(!compression && { 'Content-Length': stat.size }),
|
||||||
|
'Content-Type': mime.lookup(result[0]), // Use the original path to lookup
|
||||||
'Last-Modified': stat.mtime.toUTCString(),
|
'Last-Modified': stat.mtime.toUTCString(),
|
||||||
'Cache-Control': `max-age=${maxAge}`,
|
'Cache-Control': `max-age=${maxAge}`,
|
||||||
ETag: `"${stat.size.toString(16)}-${stat.mtimeMs.toString(16)}"`,
|
ETag: `"${stat.size.toString(16)}-${stat.mtimeMs.toString(16)}"`,
|
||||||
|
@ -81,6 +88,33 @@ export default function withSpa<InputContext extends RequestContext>({
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CompressionEncoding = keyof typeof compressionExtensions;
|
||||||
|
|
||||||
|
const compressionExtensions = {
|
||||||
|
br: 'br',
|
||||||
|
gzip: 'gz',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const compressionEncodings = Object.freeze(Object.keys(compressionExtensions));
|
||||||
|
|
||||||
|
const isValidEncoding = (value?: string): value is CompressionEncoding =>
|
||||||
|
Boolean(value && compressionEncodings.includes(value));
|
||||||
|
|
||||||
|
const tryCompressedFile = async (request: IncomingMessage, pathLike: string) => {
|
||||||
|
// Honor the compression preference
|
||||||
|
const compression = conditional(accepts(request).encodings([...compressionEncodings]));
|
||||||
|
|
||||||
|
if (!isValidEncoding(compression)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await tryStat(pathLike + '.' + compressionExtensions[compression]);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return [...result, compression] as const;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const tryStat = async (pathLike: string) => {
|
const tryStat = async (pathLike: string) => {
|
||||||
try {
|
try {
|
||||||
const stat = await fs.stat(pathLike);
|
const stat = await fs.stat(pathLike);
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
"@logto/shared": "workspace:*",
|
"@logto/shared": "workspace:*",
|
||||||
"@silverhand/essentials": "2.4.0",
|
"@silverhand/essentials": "2.4.0",
|
||||||
"@withtyped/postgres": "^0.8.1",
|
"@withtyped/postgres": "^0.8.1",
|
||||||
"@withtyped/server": "^0.8.0",
|
"@withtyped/server": "^0.8.1",
|
||||||
"aws-sdk": "^2.1329.0",
|
"aws-sdk": "^2.1329.0",
|
||||||
"chalk": "^5.0.0",
|
"chalk": "^5.0.0",
|
||||||
"clean-deep": "^3.4.0",
|
"clean-deep": "^3.4.0",
|
||||||
|
|
|
@ -55,6 +55,6 @@
|
||||||
},
|
},
|
||||||
"prettier": "@silverhand/eslint-config/.prettierrc",
|
"prettier": "@silverhand/eslint-config/.prettierrc",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@withtyped/server": "^0.8.0"
|
"@withtyped/server": "^0.8.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
"@logto/language-kit": "workspace:*",
|
"@logto/language-kit": "workspace:*",
|
||||||
"@logto/phrases": "workspace:*",
|
"@logto/phrases": "workspace:*",
|
||||||
"@logto/phrases-ui": "workspace:*",
|
"@logto/phrases-ui": "workspace:*",
|
||||||
"@withtyped/server": "^0.8.0",
|
"@withtyped/server": "^0.8.1",
|
||||||
"zod": "^3.20.2"
|
"zod": "^3.20.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,12 +116,14 @@ importers:
|
||||||
'@silverhand/essentials': 2.4.0
|
'@silverhand/essentials': 2.4.0
|
||||||
'@silverhand/jest-config': ^2.0.1
|
'@silverhand/jest-config': ^2.0.1
|
||||||
'@silverhand/ts-config': 2.0.3
|
'@silverhand/ts-config': 2.0.3
|
||||||
|
'@types/accepts': ^1.3.5
|
||||||
'@types/http-proxy': ^1.17.9
|
'@types/http-proxy': ^1.17.9
|
||||||
'@types/jest': ^29.4.0
|
'@types/jest': ^29.4.0
|
||||||
'@types/mime-types': ^2.1.1
|
'@types/mime-types': ^2.1.1
|
||||||
'@types/node': ^18.11.18
|
'@types/node': ^18.11.18
|
||||||
'@withtyped/postgres': ^0.8.1
|
'@withtyped/postgres': ^0.8.1
|
||||||
'@withtyped/server': ^0.8.0
|
'@withtyped/server': ^0.8.1
|
||||||
|
accepts: ^1.3.8
|
||||||
chalk: ^5.0.0
|
chalk: ^5.0.0
|
||||||
decamelize: ^6.0.0
|
decamelize: ^6.0.0
|
||||||
dotenv: ^16.0.0
|
dotenv: ^16.0.0
|
||||||
|
@ -143,8 +145,9 @@ importers:
|
||||||
'@logto/schemas': link:../schemas
|
'@logto/schemas': link:../schemas
|
||||||
'@logto/shared': link:../shared
|
'@logto/shared': link:../shared
|
||||||
'@silverhand/essentials': 2.4.0
|
'@silverhand/essentials': 2.4.0
|
||||||
'@withtyped/postgres': 0.8.1_@withtyped+server@0.8.0
|
'@withtyped/postgres': 0.8.1_@withtyped+server@0.8.1
|
||||||
'@withtyped/server': 0.8.0
|
'@withtyped/server': 0.8.1
|
||||||
|
accepts: 1.3.8
|
||||||
chalk: 5.1.2
|
chalk: 5.1.2
|
||||||
decamelize: 6.0.0
|
decamelize: 6.0.0
|
||||||
dotenv: 16.0.0
|
dotenv: 16.0.0
|
||||||
|
@ -158,6 +161,7 @@ importers:
|
||||||
'@silverhand/eslint-config': 2.0.1_kjzxg5porcw5dx54sezsklj5cy
|
'@silverhand/eslint-config': 2.0.1_kjzxg5porcw5dx54sezsklj5cy
|
||||||
'@silverhand/jest-config': 2.0.1_jest@29.5.0
|
'@silverhand/jest-config': 2.0.1_jest@29.5.0
|
||||||
'@silverhand/ts-config': 2.0.3_typescript@4.9.4
|
'@silverhand/ts-config': 2.0.3_typescript@4.9.4
|
||||||
|
'@types/accepts': 1.3.5
|
||||||
'@types/http-proxy': 1.17.9
|
'@types/http-proxy': 1.17.9
|
||||||
'@types/jest': 29.4.0
|
'@types/jest': 29.4.0
|
||||||
'@types/mime-types': 2.1.1
|
'@types/mime-types': 2.1.1
|
||||||
|
@ -358,7 +362,7 @@ importers:
|
||||||
'@types/sinon': ^10.0.13
|
'@types/sinon': ^10.0.13
|
||||||
'@types/supertest': ^2.0.11
|
'@types/supertest': ^2.0.11
|
||||||
'@withtyped/postgres': ^0.8.1
|
'@withtyped/postgres': ^0.8.1
|
||||||
'@withtyped/server': ^0.8.0
|
'@withtyped/server': ^0.8.1
|
||||||
aws-sdk: ^2.1329.0
|
aws-sdk: ^2.1329.0
|
||||||
chalk: ^5.0.0
|
chalk: ^5.0.0
|
||||||
clean-deep: ^3.4.0
|
clean-deep: ^3.4.0
|
||||||
|
@ -421,8 +425,8 @@ importers:
|
||||||
'@logto/schemas': link:../schemas
|
'@logto/schemas': link:../schemas
|
||||||
'@logto/shared': link:../shared
|
'@logto/shared': link:../shared
|
||||||
'@silverhand/essentials': 2.4.0
|
'@silverhand/essentials': 2.4.0
|
||||||
'@withtyped/postgres': 0.8.1_@withtyped+server@0.8.0
|
'@withtyped/postgres': 0.8.1_@withtyped+server@0.8.1
|
||||||
'@withtyped/server': 0.8.0
|
'@withtyped/server': 0.8.1
|
||||||
aws-sdk: 2.1329.0
|
aws-sdk: 2.1329.0
|
||||||
chalk: 5.1.2
|
chalk: 5.1.2
|
||||||
clean-deep: 3.4.0
|
clean-deep: 3.4.0
|
||||||
|
@ -575,7 +579,7 @@ importers:
|
||||||
'@types/jest': ^29.4.0
|
'@types/jest': ^29.4.0
|
||||||
'@types/jest-environment-puppeteer': ^5.0.3
|
'@types/jest-environment-puppeteer': ^5.0.3
|
||||||
'@types/node': ^18.11.18
|
'@types/node': ^18.11.18
|
||||||
'@withtyped/server': ^0.8.0
|
'@withtyped/server': ^0.8.1
|
||||||
dotenv: ^16.0.0
|
dotenv: ^16.0.0
|
||||||
eslint: ^8.34.0
|
eslint: ^8.34.0
|
||||||
got: ^12.5.3
|
got: ^12.5.3
|
||||||
|
@ -589,7 +593,7 @@ importers:
|
||||||
text-encoder: ^0.0.4
|
text-encoder: ^0.0.4
|
||||||
typescript: ^4.9.4
|
typescript: ^4.9.4
|
||||||
dependencies:
|
dependencies:
|
||||||
'@withtyped/server': 0.8.0
|
'@withtyped/server': 0.8.1
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@jest/types': 29.1.2
|
'@jest/types': 29.1.2
|
||||||
'@logto/connector-kit': link:../toolkit/connector-kit
|
'@logto/connector-kit': link:../toolkit/connector-kit
|
||||||
|
@ -683,7 +687,7 @@ importers:
|
||||||
'@types/jest': ^29.4.0
|
'@types/jest': ^29.4.0
|
||||||
'@types/node': ^18.11.18
|
'@types/node': ^18.11.18
|
||||||
'@types/pluralize': ^0.0.29
|
'@types/pluralize': ^0.0.29
|
||||||
'@withtyped/server': ^0.8.0
|
'@withtyped/server': ^0.8.1
|
||||||
camelcase: ^7.0.0
|
camelcase: ^7.0.0
|
||||||
chalk: ^5.0.0
|
chalk: ^5.0.0
|
||||||
eslint: ^8.34.0
|
eslint: ^8.34.0
|
||||||
|
@ -702,7 +706,7 @@ importers:
|
||||||
'@logto/language-kit': link:../toolkit/language-kit
|
'@logto/language-kit': link:../toolkit/language-kit
|
||||||
'@logto/phrases': link:../phrases
|
'@logto/phrases': link:../phrases
|
||||||
'@logto/phrases-ui': link:../phrases-ui
|
'@logto/phrases-ui': link:../phrases-ui
|
||||||
'@withtyped/server': 0.8.0
|
'@withtyped/server': 0.8.1
|
||||||
zod: 3.20.2
|
zod: 3.20.2
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@silverhand/eslint-config': 2.0.1_kjzxg5porcw5dx54sezsklj5cy
|
'@silverhand/eslint-config': 2.0.1_kjzxg5porcw5dx54sezsklj5cy
|
||||||
|
@ -4634,21 +4638,21 @@ packages:
|
||||||
eslint-visitor-keys: 3.3.0
|
eslint-visitor-keys: 3.3.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@withtyped/postgres/0.8.1_@withtyped+server@0.8.0:
|
/@withtyped/postgres/0.8.1_@withtyped+server@0.8.1:
|
||||||
resolution: {integrity: sha512-BkX1SPDV8bZFn1LEI6jzTes+y/4BGuPEBOi8p6jH9ZBySTuaW0/JqgEb1aKk5GtJ0aFGXhgq4Fk+JkLOc6zehA==}
|
resolution: {integrity: sha512-BkX1SPDV8bZFn1LEI6jzTes+y/4BGuPEBOi8p6jH9ZBySTuaW0/JqgEb1aKk5GtJ0aFGXhgq4Fk+JkLOc6zehA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@withtyped/server': ^0.8.0
|
'@withtyped/server': ^0.8.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/pg': 8.6.6
|
'@types/pg': 8.6.6
|
||||||
'@withtyped/server': 0.8.0
|
'@withtyped/server': 0.8.1
|
||||||
'@withtyped/shared': 0.2.0
|
'@withtyped/shared': 0.2.0
|
||||||
pg: 8.8.0
|
pg: 8.8.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- pg-native
|
- pg-native
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@withtyped/server/0.8.0:
|
/@withtyped/server/0.8.1:
|
||||||
resolution: {integrity: sha512-p9gRdEvUNBJ0X15jB4xZutMkzjF19EoBrGjvmaokbuO4+Ub5CSpfV/SOl+7vob8cAgIdWVriZfDGD73ZH0YWJQ==}
|
resolution: {integrity: sha512-gNJ0lmwYiAScb7oQWu7BHyy+PT2/j34kGWIjElyfrYZP7JWpZSrEyxaVwDFRo/oA9vzru3AOnPFTMcjmPq4AKg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@withtyped/shared': 0.2.0
|
'@withtyped/shared': 0.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -4691,7 +4695,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
negotiator: 0.6.3
|
negotiator: 0.6.3
|
||||||
dev: true
|
|
||||||
|
|
||||||
/acorn-globals/7.0.1:
|
/acorn-globals/7.0.1:
|
||||||
resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==}
|
resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==}
|
||||||
|
@ -10776,7 +10779,6 @@ packages:
|
||||||
/negotiator/0.6.3:
|
/negotiator/0.6.3:
|
||||||
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/nise/5.1.3:
|
/nise/5.1.3:
|
||||||
resolution: {integrity: sha512-U597iWTTBBYIV72986jyU382/MMZ70ApWcRmkoF1AZ75bpqOtI3Gugv/6+0jLgoDOabmcSwYBkSSAWIp1eA5cg==}
|
resolution: {integrity: sha512-U597iWTTBBYIV72986jyU382/MMZ70ApWcRmkoF1AZ75bpqOtI3Gugv/6+0jLgoDOabmcSwYBkSSAWIp1eA5cg==}
|
||||||
|
|
Loading…
Reference in a new issue