0
Fork 0
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:
Gao Sun 2023-03-12 21:31:57 +08:00 committed by GitHub
commit eed6e80e45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 64 additions and 26 deletions

View file

@ -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",

View file

@ -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);

View file

@ -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",

View file

@ -55,6 +55,6 @@
},
"prettier": "@silverhand/eslint-config/.prettierrc",
"dependencies": {
"@withtyped/server": "^0.8.0"
"@withtyped/server": "^0.8.1"
}
}

View file

@ -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"
}
}

View file

@ -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==}