mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -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:*",
|
||||
"@silverhand/essentials": "2.4.0",
|
||||
"@withtyped/postgres": "^0.8.1",
|
||||
"@withtyped/server": "^0.8.0",
|
||||
"@withtyped/server": "^0.8.1",
|
||||
"accepts": "^1.3.8",
|
||||
"chalk": "^5.0.0",
|
||||
"decamelize": "^6.0.0",
|
||||
"dotenv": "^16.0.0",
|
||||
|
@ -44,6 +45,7 @@
|
|||
"@silverhand/eslint-config": "2.0.1",
|
||||
"@silverhand/jest-config": "^2.0.1",
|
||||
"@silverhand/ts-config": "2.0.3",
|
||||
"@types/accepts": "^1.3.5",
|
||||
"@types/http-proxy": "^1.17.9",
|
||||
"@types/jest": "^29.4.0",
|
||||
"@types/mime-types": "^2.1.1",
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { createReadStream } from 'node:fs';
|
||||
import fs from 'node:fs/promises';
|
||||
import type { IncomingMessage } from 'node:http';
|
||||
import path from 'node:path';
|
||||
|
||||
import { assert } from '@silverhand/essentials';
|
||||
import type { NextFunction, RequestContext } from '@withtyped/server';
|
||||
import { assert, conditional } from '@silverhand/essentials';
|
||||
import type { HttpContext, NextFunction, RequestContext } from '@withtyped/server';
|
||||
import accepts from 'accepts';
|
||||
import mime from 'mime-types';
|
||||
|
||||
import { matchPathname } from '#src/utils/url.js';
|
||||
|
@ -39,7 +41,11 @@ export default function withSpa<InputContext extends RequestContext>({
|
|||
}: WithSpaConfig) {
|
||||
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 {
|
||||
headers,
|
||||
request: { url },
|
||||
|
@ -63,14 +69,15 @@ export default function withSpa<InputContext extends RequestContext>({
|
|||
return next({ ...context, status: 404 });
|
||||
}
|
||||
|
||||
const [pathLike, stat] = result;
|
||||
const [pathLike, stat, compression] = (await tryCompressedFile(request, result[0])) ?? result;
|
||||
|
||||
return next({
|
||||
...context,
|
||||
headers: {
|
||||
...headers,
|
||||
'Content-Length': stat.size,
|
||||
'Content-Type': mime.lookup(pathLike),
|
||||
...(compression && { 'Content-Encoding': compression }),
|
||||
...(!compression && { 'Content-Length': stat.size }),
|
||||
'Content-Type': mime.lookup(result[0]), // Use the original path to lookup
|
||||
'Last-Modified': stat.mtime.toUTCString(),
|
||||
'Cache-Control': `max-age=${maxAge}`,
|
||||
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) => {
|
||||
try {
|
||||
const stat = await fs.stat(pathLike);
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
"@logto/shared": "workspace:*",
|
||||
"@silverhand/essentials": "2.4.0",
|
||||
"@withtyped/postgres": "^0.8.1",
|
||||
"@withtyped/server": "^0.8.0",
|
||||
"@withtyped/server": "^0.8.1",
|
||||
"aws-sdk": "^2.1329.0",
|
||||
"chalk": "^5.0.0",
|
||||
"clean-deep": "^3.4.0",
|
||||
|
|
|
@ -55,6 +55,6 @@
|
|||
},
|
||||
"prettier": "@silverhand/eslint-config/.prettierrc",
|
||||
"dependencies": {
|
||||
"@withtyped/server": "^0.8.0"
|
||||
"@withtyped/server": "^0.8.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
"@logto/language-kit": "workspace:*",
|
||||
"@logto/phrases": "workspace:*",
|
||||
"@logto/phrases-ui": "workspace:*",
|
||||
"@withtyped/server": "^0.8.0",
|
||||
"@withtyped/server": "^0.8.1",
|
||||
"zod": "^3.20.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,12 +116,14 @@ importers:
|
|||
'@silverhand/essentials': 2.4.0
|
||||
'@silverhand/jest-config': ^2.0.1
|
||||
'@silverhand/ts-config': 2.0.3
|
||||
'@types/accepts': ^1.3.5
|
||||
'@types/http-proxy': ^1.17.9
|
||||
'@types/jest': ^29.4.0
|
||||
'@types/mime-types': ^2.1.1
|
||||
'@types/node': ^18.11.18
|
||||
'@withtyped/postgres': ^0.8.1
|
||||
'@withtyped/server': ^0.8.0
|
||||
'@withtyped/server': ^0.8.1
|
||||
accepts: ^1.3.8
|
||||
chalk: ^5.0.0
|
||||
decamelize: ^6.0.0
|
||||
dotenv: ^16.0.0
|
||||
|
@ -143,8 +145,9 @@ importers:
|
|||
'@logto/schemas': link:../schemas
|
||||
'@logto/shared': link:../shared
|
||||
'@silverhand/essentials': 2.4.0
|
||||
'@withtyped/postgres': 0.8.1_@withtyped+server@0.8.0
|
||||
'@withtyped/server': 0.8.0
|
||||
'@withtyped/postgres': 0.8.1_@withtyped+server@0.8.1
|
||||
'@withtyped/server': 0.8.1
|
||||
accepts: 1.3.8
|
||||
chalk: 5.1.2
|
||||
decamelize: 6.0.0
|
||||
dotenv: 16.0.0
|
||||
|
@ -158,6 +161,7 @@ importers:
|
|||
'@silverhand/eslint-config': 2.0.1_kjzxg5porcw5dx54sezsklj5cy
|
||||
'@silverhand/jest-config': 2.0.1_jest@29.5.0
|
||||
'@silverhand/ts-config': 2.0.3_typescript@4.9.4
|
||||
'@types/accepts': 1.3.5
|
||||
'@types/http-proxy': 1.17.9
|
||||
'@types/jest': 29.4.0
|
||||
'@types/mime-types': 2.1.1
|
||||
|
@ -358,7 +362,7 @@ importers:
|
|||
'@types/sinon': ^10.0.13
|
||||
'@types/supertest': ^2.0.11
|
||||
'@withtyped/postgres': ^0.8.1
|
||||
'@withtyped/server': ^0.8.0
|
||||
'@withtyped/server': ^0.8.1
|
||||
aws-sdk: ^2.1329.0
|
||||
chalk: ^5.0.0
|
||||
clean-deep: ^3.4.0
|
||||
|
@ -421,8 +425,8 @@ importers:
|
|||
'@logto/schemas': link:../schemas
|
||||
'@logto/shared': link:../shared
|
||||
'@silverhand/essentials': 2.4.0
|
||||
'@withtyped/postgres': 0.8.1_@withtyped+server@0.8.0
|
||||
'@withtyped/server': 0.8.0
|
||||
'@withtyped/postgres': 0.8.1_@withtyped+server@0.8.1
|
||||
'@withtyped/server': 0.8.1
|
||||
aws-sdk: 2.1329.0
|
||||
chalk: 5.1.2
|
||||
clean-deep: 3.4.0
|
||||
|
@ -575,7 +579,7 @@ importers:
|
|||
'@types/jest': ^29.4.0
|
||||
'@types/jest-environment-puppeteer': ^5.0.3
|
||||
'@types/node': ^18.11.18
|
||||
'@withtyped/server': ^0.8.0
|
||||
'@withtyped/server': ^0.8.1
|
||||
dotenv: ^16.0.0
|
||||
eslint: ^8.34.0
|
||||
got: ^12.5.3
|
||||
|
@ -589,7 +593,7 @@ importers:
|
|||
text-encoder: ^0.0.4
|
||||
typescript: ^4.9.4
|
||||
dependencies:
|
||||
'@withtyped/server': 0.8.0
|
||||
'@withtyped/server': 0.8.1
|
||||
devDependencies:
|
||||
'@jest/types': 29.1.2
|
||||
'@logto/connector-kit': link:../toolkit/connector-kit
|
||||
|
@ -683,7 +687,7 @@ importers:
|
|||
'@types/jest': ^29.4.0
|
||||
'@types/node': ^18.11.18
|
||||
'@types/pluralize': ^0.0.29
|
||||
'@withtyped/server': ^0.8.0
|
||||
'@withtyped/server': ^0.8.1
|
||||
camelcase: ^7.0.0
|
||||
chalk: ^5.0.0
|
||||
eslint: ^8.34.0
|
||||
|
@ -702,7 +706,7 @@ importers:
|
|||
'@logto/language-kit': link:../toolkit/language-kit
|
||||
'@logto/phrases': link:../phrases
|
||||
'@logto/phrases-ui': link:../phrases-ui
|
||||
'@withtyped/server': 0.8.0
|
||||
'@withtyped/server': 0.8.1
|
||||
zod: 3.20.2
|
||||
devDependencies:
|
||||
'@silverhand/eslint-config': 2.0.1_kjzxg5porcw5dx54sezsklj5cy
|
||||
|
@ -4634,21 +4638,21 @@ packages:
|
|||
eslint-visitor-keys: 3.3.0
|
||||
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==}
|
||||
peerDependencies:
|
||||
'@withtyped/server': ^0.8.0
|
||||
dependencies:
|
||||
'@types/pg': 8.6.6
|
||||
'@withtyped/server': 0.8.0
|
||||
'@withtyped/server': 0.8.1
|
||||
'@withtyped/shared': 0.2.0
|
||||
pg: 8.8.0
|
||||
transitivePeerDependencies:
|
||||
- pg-native
|
||||
dev: false
|
||||
|
||||
/@withtyped/server/0.8.0:
|
||||
resolution: {integrity: sha512-p9gRdEvUNBJ0X15jB4xZutMkzjF19EoBrGjvmaokbuO4+Ub5CSpfV/SOl+7vob8cAgIdWVriZfDGD73ZH0YWJQ==}
|
||||
/@withtyped/server/0.8.1:
|
||||
resolution: {integrity: sha512-gNJ0lmwYiAScb7oQWu7BHyy+PT2/j34kGWIjElyfrYZP7JWpZSrEyxaVwDFRo/oA9vzru3AOnPFTMcjmPq4AKg==}
|
||||
dependencies:
|
||||
'@withtyped/shared': 0.2.0
|
||||
dev: false
|
||||
|
@ -4691,7 +4695,6 @@ packages:
|
|||
dependencies:
|
||||
mime-types: 2.1.35
|
||||
negotiator: 0.6.3
|
||||
dev: true
|
||||
|
||||
/acorn-globals/7.0.1:
|
||||
resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==}
|
||||
|
@ -10776,7 +10779,6 @@ packages:
|
|||
/negotiator/0.6.3:
|
||||
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: true
|
||||
|
||||
/nise/5.1.3:
|
||||
resolution: {integrity: sha512-U597iWTTBBYIV72986jyU382/MMZ70ApWcRmkoF1AZ75bpqOtI3Gugv/6+0jLgoDOabmcSwYBkSSAWIp1eA5cg==}
|
||||
|
|
Loading…
Reference in a new issue