diff --git a/Dockerfile b/Dockerfile index c2d5d19..dfd58b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,8 @@ USER node FROM base AS build RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile RUN pnpm run build +# Remove devDependencies +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile ################### @@ -30,15 +32,15 @@ RUN pnpm run build FROM alpine:3.19 As production ENV PORT=3333 -ENV NODE_VERSION 20.11.0-r0 -ENV NPM_VERSION 10.2.5-r0 +ENV NODE_VERSION 20.11.1-r0 +# ENV NPM_VERSION 10.2.5-r0 # Make sure `"type": "module"` is set in package.json COPY --from=build /app/package.json /app/package.json # Production dependencies -# COPY --from=build /app/node_modules /app/node_modules +COPY --from=build /app/node_modules /app/node_modules # Application -COPY --from=build /app/dist /app +COPY --from=build /app/dist/src /app # COPY --from=prod-deps /app/node_modules /app/node_modules # COPY --from=build /app/dist /app @@ -76,4 +78,4 @@ RUN chown node .srp USER node -CMD [ "node", "app/main.cjs" ] \ No newline at end of file +CMD [ "node", "app/main.js" ] \ No newline at end of file diff --git a/package.json b/package.json index 370e55b..2cfcfaf 100644 --- a/package.json +++ b/package.json @@ -13,13 +13,16 @@ "default": "./dist/main.cjs" } }, + "imports": { + "@/*": "./src/*" + }, "scripts": { "start": "tsx src/main.ts", "dev": "tsx watch src/main.ts", "test:coverage": "vitest run --coverage", "test": "vitest", "test:ci": "vitest run", - "build": "esbuild src/main.ts --bundle --platform=node --target=node16 --format=cjs --outfile=dist/main.cjs --minify", + "build": "tsc && tsc-alias -p tsconfig.json", "format": "prettier -w src", "lint": "tsc --noEmit && eslint ./src && prettier -c src", "lint:fix": "eslint --fix ./src && prettier -w src", @@ -38,6 +41,7 @@ "eslint": "^8.56.0", "node-mocks-http": "^1.14.1", "prettier": "^3.1.1", + "tsc-alias": "^1.8.8", "tsx": "^4.7.0", "typescript": "^5.3.3", "vitest": "^1.1.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d1dbe4..d2a2c43 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -79,6 +79,9 @@ devDependencies: prettier: specifier: ^3.1.1 version: 3.2.4 + tsc-alias: + specifier: ^1.8.8 + version: 1.8.8 tsx: specifier: ^4.7.0 version: 4.7.0 @@ -1271,6 +1274,14 @@ packages: engines: {node: '>=10'} dev: true + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true @@ -1309,6 +1320,11 @@ packages: engines: {node: ^4.5.0 || >= 5.9} dev: false + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + /body-parser@1.20.1: resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -1417,6 +1433,21 @@ packages: get-func-name: 2.0.2 dev: true + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1430,6 +1461,11 @@ packages: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} dev: false + /commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + dev: true + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2227,6 +2263,13 @@ packages: engines: {node: '>= 0.10'} dev: false + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2500,6 +2543,11 @@ packages: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: false + /mylas@2.1.13: + resolution: {integrity: sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==} + engines: {node: '>=12.0.0'} + dev: true + /nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2532,6 +2580,11 @@ packages: type-is: 1.6.18 dev: true + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + /npm-run-path@5.2.0: resolution: {integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2726,6 +2779,13 @@ packages: pathe: 1.1.2 dev: true + /plimit-lit@1.6.1: + resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==} + engines: {node: '>=12'} + dependencies: + queue-lit: 1.5.2 + dev: true + /postcss@8.4.33: resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==} engines: {node: ^10 || ^12 || >=14} @@ -2791,6 +2851,11 @@ packages: side-channel: 1.0.4 dev: false + /queue-lit@1.5.2: + resolution: {integrity: sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==} + engines: {node: '>=12'} + dev: true + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true @@ -2833,6 +2898,13 @@ packages: string_decoder: 1.3.0 dev: false + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + /real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} @@ -3172,6 +3244,18 @@ packages: resolution: {integrity: sha512-H+vROPaW9pvAtraUdrPVFN28VjMzn6dox9Qy3hHKRQg4l+S0KP6V+D295F8gqZSBEkeTTIXZRvYy0xkcMjGBsg==} dev: false + /tsc-alias@1.8.8: + resolution: {integrity: sha512-OYUOd2wl0H858NvABWr/BoSKNERw3N9GTi3rHPK8Iv4O1UyUXIrTTOAZNHsjlVpXFOhpJBVARI1s+rzwLivN3Q==} + hasBin: true + dependencies: + chokidar: 3.6.0 + commander: 9.5.0 + globby: 11.1.0 + mylas: 2.1.13 + normalize-path: 3.0.0 + plimit-lit: 1.6.1 + dev: true + /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} diff --git a/src/cache/index.ts b/src/cache/index.ts index 77e55d4..0d13c97 100644 --- a/src/cache/index.ts +++ b/src/cache/index.ts @@ -1 +1 @@ -export * from './cache'; +export * from './cache.js'; diff --git a/src/database/config-rule.service.ts b/src/database/config-rule.service.ts index 5704dcd..b1a9cec 100644 --- a/src/database/config-rule.service.ts +++ b/src/database/config-rule.service.ts @@ -1,11 +1,11 @@ -import { logger } from '@/logger'; -import type { SpyConfigRuleEntityAzureTable } from '../entities/spy-config-rule.entity'; -import { AzureTable } from './azure-table'; +import { logger } from '@/logger/index.js'; +import type { SpyConfigRuleEntityAzureTable } from '../entities/spy-config-rule.entity.js'; +import { AzureTable } from './azure-table.js'; import invariant from 'tiny-invariant'; import { TableClient } from '@azure/data-tables'; -import { env } from '@/environment'; +import { env } from '@/environment/index.js'; import { ODataExpression } from 'ts-odata-client'; -import { trimStartAndEndSlash } from '@/utils'; +import { trimStartAndEndSlash } from '@/utils/index.js'; export class SpyConfigRuleService { private oDataExpresion = ODataExpression.forV4(); @@ -15,17 +15,18 @@ export class SpyConfigRuleService { async listAllMatchUpstreamUrlRules(upstreamUrl: string) { const url = trimStartAndEndSlash(upstreamUrl); await this.tableClient.createTable(); - return this.tableClient.list( - this.oDataExpresion - .filter(p => - p.upstreamUrl - .$equals('') - .or(p.upstreamUrl.$equals(null)) - .or(p.upstreamUrl.$equals(url)) - .or(p.upstreamUrl.$equals(`${url}/`)) - ) - .build() - ); + const filterQuery = this.oDataExpresion + .filter(p => + p.upstreamUrl + .$equals('') + .or(p.upstreamUrl.$equals(url)) + .or(p.upstreamUrl.$equals(`${url}/`)) + ) + .build(); + logger.debug(`Filter query: ${JSON.stringify(filterQuery, null, 2)}`); + return this.tableClient.list({ + filter: filterQuery.filter, + }); } async insertRule(rule: Omit) { diff --git a/src/database/index.ts b/src/database/index.ts index 5fd0104..3ddabfb 100644 --- a/src/database/index.ts +++ b/src/database/index.ts @@ -1,2 +1,2 @@ -export * from './azure-table'; -export * from './config-rule.service'; +export * from './azure-table.js'; +export * from './config-rule.service.js'; diff --git a/src/entities/index.ts b/src/entities/index.ts index 787e0bd..516b7d9 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -1 +1 @@ -export * from './spy-config-rule.entity'; +export * from './spy-config-rule.entity.js'; diff --git a/src/entities/spy-config-rule.entity.ts b/src/entities/spy-config-rule.entity.ts index be710ab..1e321b2 100644 --- a/src/entities/spy-config-rule.entity.ts +++ b/src/entities/spy-config-rule.entity.ts @@ -1,4 +1,4 @@ -import type { AzureTableEntityBase } from '../database/azure-table'; +import type { AzureTableEntityBase } from '../database/azure-table.js'; export interface SpyConfigRuleEntity { /** diff --git a/src/environment/env.ts b/src/environment/env.ts index 59b2f0e..a2ed987 100644 --- a/src/environment/env.ts +++ b/src/environment/env.ts @@ -1,6 +1,6 @@ import 'dotenv/config'; -import { environmentSchema } from '../environment/env.schema'; -import { extractErorMessage } from '@/utils'; +import { environmentSchema } from '../environment/env.schema.js'; +import { extractErorMessage } from '@/utils/index.js'; import { zodParser } from '@thaitype/record-parser/zod'; /** * Get environment variables diff --git a/src/environment/index.ts b/src/environment/index.ts index c1532d6..7915515 100644 --- a/src/environment/index.ts +++ b/src/environment/index.ts @@ -1 +1 @@ -export * from './env'; +export * from './env.js'; diff --git a/src/logger/index.ts b/src/logger/index.ts index 1ff09ef..3bb73ef 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -1 +1 @@ -export * from './logger'; +export * from './logger.js'; diff --git a/src/logger/logger.ts b/src/logger/logger.ts index 097bdbc..23b0794 100644 --- a/src/logger/logger.ts +++ b/src/logger/logger.ts @@ -1,6 +1,6 @@ -import { env } from '@/environment'; -import pino from 'pino'; -import pinoHttp from 'pino-http'; +import { env } from '@/environment/index.js'; +import { pino } from 'pino'; +import { pinoHttp } from 'pino-http'; import fs from 'fs'; const logDir = '.srp/logs'; diff --git a/src/main.ts b/src/main.ts index 66906bf..07ca9f4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,5 @@ -import { server } from './server'; -import { extractErorMessage } from './utils/utils'; +import { server } from './server.js'; +import { extractErorMessage } from './utils/utils.js'; server().catch(error => { console.error(extractErorMessage(error)); diff --git a/src/plugins/bootstrap.ts b/src/plugins/bootstrap.ts index 663f091..1e5d3b4 100644 --- a/src/plugins/bootstrap.ts +++ b/src/plugins/bootstrap.ts @@ -1,14 +1,16 @@ -import { initSampleRule, spyConfigRuleService } from '../database'; -import { env } from '../environment'; -import { httpLogger, logger, stringLogger } from '../logger'; +import { initSampleRule, spyConfigRuleService } from '../database/index.js'; +import { env } from '../environment/index.js'; +import { httpLogger, logger, stringLogger } from '../logger/index.js'; import type express from 'express'; -import { spyMiddleware } from './spy-middleware'; -import type { SpyConfigRuleEntityAzureTable } from '@/entities'; -import { SpyRule } from './spy-rule'; -import { cache } from '@/cache'; -import type { RuleConfig } from './rule.schema'; -import { ruleConfigSchema } from './rule.schema'; +import { spyMiddleware } from './spy-middleware.js'; +import type { SpyConfigRuleEntityAzureTable } from '@/entities/index.js'; +import { SpyRule } from './spy-rule.js'; +import { cache } from '@/cache/index.js'; +import type { RuleConfig } from './rule.schema.js'; +import { ruleConfigSchema } from './rule.schema.js'; import { DataViewer, DataContainer } from '@thaitype/data-viewer-server'; +import { mergeRuleConfig } from '@/utils/utils.js'; +import { RestError } from '@azure/data-tables'; export async function initRulePlugin() { logger.info('Connecting to Azure Table'); @@ -57,7 +59,7 @@ export function defineRuleReport(rules: RuleConfig): DataContainer { } export async function registerSpyPlugin(app: express.Express) { - await initRulePlugin(); + const initRuleResult = await initRulePlugin(); const dataViewer = new DataViewer({ path: env.srpAdminRootPath, logger: stringLogger, @@ -71,7 +73,7 @@ export async function registerSpyPlugin(app: express.Express) { forceReset: true, }); // Set dataViewer to show the new rule - dataViewer.set(defineRuleReport(result)); + dataViewer.set(defineRuleReport(mergeRuleConfig(initRuleResult, result))); logger.info('Revalidate spyConfig'); next(); }); @@ -82,19 +84,34 @@ export async function registerSpyPlugin(app: express.Express) { app.use(spyMiddleware); } +async function getSpyConfigRule(upstreamUrl: string) { + const rules: SpyConfigRuleEntityAzureTable[] = []; + try { + const rawRules = await spyConfigRuleService.listAllMatchUpstreamUrlRules(upstreamUrl); + // TODO: Fix later, this might be slow if there are many rules + for await (const rawRule of rawRules) { + rules.push(rawRule); + } + } catch (e) { + if(e instanceof RestError) { + logger.error(`Error getting spyConfig rule: ${e.message} \n ${e.stack}`); + } else if(e instanceof Error) { + logger.error(`Error getting spyConfig rule: ${e.message} \n ${e.stack}`); + } else { + logger.error('Unknown error, ' + String(e)); + } + throw e; + } + return rules; +} + /** * Responds to the root admin path * - Revalidate rule from data store and cache it. */ export async function parseSpyConfig(upstreamUrl: string): Promise { - const rawRules = await spyConfigRuleService.listAllMatchUpstreamUrlRules(upstreamUrl); - const rules: SpyConfigRuleEntityAzureTable[] = []; - // TODO: Fix later, this might be slow if there are many rules - for await (const rawRule of rawRules) { - rules.push(rawRule); - } - + const rules = await getSpyConfigRule(upstreamUrl); try { return new SpyRule(rules).parse(); } catch (e) { diff --git a/src/plugins/index.ts b/src/plugins/index.ts index afd4fca..7226190 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1,4 +1,4 @@ -export * from './response-transformer'; -export * from './spy-rule-plugin'; -export * from './rule-matcher'; -export * from './bootstrap'; +export * from './response-transformer/index.js'; +export * from './spy-rule-plugin.js'; +export * from './rule-matcher.js'; +export * from './bootstrap.js'; diff --git a/src/plugins/response-transformer/action-expressions/base-action-expression.ts b/src/plugins/response-transformer/action-expressions/base-action-expression.ts index 8b15069..0a69b8f 100644 --- a/src/plugins/response-transformer/action-expressions/base-action-expression.ts +++ b/src/plugins/response-transformer/action-expressions/base-action-expression.ts @@ -1,6 +1,6 @@ -import type { HandleResponseParams } from '@/plugins'; -import type { ExpressionOptions, ExpressionValidateResult } from '../response-transformer-expression'; -import { logger } from '@/logger'; +import type { HandleResponseParams } from '@/plugins/index.js'; +import type { ExpressionOptions, ExpressionValidateResult } from '../response-transformer-expression.js'; +import { logger } from '@/logger/index.js'; export abstract class BaseActionExpression { constructor( diff --git a/src/plugins/response-transformer/action-expressions/index.ts b/src/plugins/response-transformer/action-expressions/index.ts index 8361132..ce0b7df 100644 --- a/src/plugins/response-transformer/action-expressions/index.ts +++ b/src/plugins/response-transformer/action-expressions/index.ts @@ -1 +1 @@ -export * from './replace-status-code'; +export * from './replace-status-code.js'; diff --git a/src/plugins/response-transformer/action-expressions/replace-status-code.ts b/src/plugins/response-transformer/action-expressions/replace-status-code.ts index 90646a4..837bdf4 100644 --- a/src/plugins/response-transformer/action-expressions/replace-status-code.ts +++ b/src/plugins/response-transformer/action-expressions/replace-status-code.ts @@ -1,5 +1,5 @@ -import type { ExpressionValidateResult } from '../response-transformer-expression'; -import { BaseActionExpression } from './base-action-expression'; +import type { ExpressionValidateResult } from '../response-transformer-expression.js'; +import { BaseActionExpression } from './base-action-expression.js'; export class ReplaceStatusCodeActionExpression extends BaseActionExpression { validate(): ExpressionValidateResult { diff --git a/src/plugins/response-transformer/index.ts b/src/plugins/response-transformer/index.ts index 009222f..8b1214f 100644 --- a/src/plugins/response-transformer/index.ts +++ b/src/plugins/response-transformer/index.ts @@ -1 +1 @@ -export * from './response-transformer'; +export * from './response-transformer.js'; diff --git a/src/plugins/response-transformer/response-transformer-expression.ts b/src/plugins/response-transformer/response-transformer-expression.ts index cb90e81..95f9b1c 100644 --- a/src/plugins/response-transformer/response-transformer-expression.ts +++ b/src/plugins/response-transformer/response-transformer-expression.ts @@ -1,6 +1,6 @@ -import type { Rule } from '../rule.schema'; -import type { HandleResponseParams } from '../spy-rule-plugin'; -import { ReplaceStatusCodeActionExpression } from './action-expressions'; +import type { Rule } from '../rule.schema.js'; +import type { HandleResponseParams } from '../spy-rule-plugin.js'; +import { ReplaceStatusCodeActionExpression } from './action-expressions/index.js'; export type ExpressionValidateResult = | { diff --git a/src/plugins/response-transformer/response-transformer.ts b/src/plugins/response-transformer/response-transformer.ts index f8f892a..7ffbae5 100644 --- a/src/plugins/response-transformer/response-transformer.ts +++ b/src/plugins/response-transformer/response-transformer.ts @@ -1,7 +1,7 @@ -import { logger } from '@/logger'; -import type { HandleResponseParams } from '../spy-rule-plugin'; -import { ResponseTransformerExpression } from './response-transformer-expression'; -import type { Rule } from '../rule.schema'; +import { logger } from '@/logger/index.js'; +import type { HandleResponseParams } from '../spy-rule-plugin.js'; +import { ResponseTransformerExpression } from './response-transformer-expression.js'; +import type { Rule } from '../rule.schema.js'; export class ResponseTransformerPlugin { static name = 'response-transformer'; diff --git a/src/plugins/rule-matcher.test.ts b/src/plugins/rule-matcher.test.ts index 7fbca9c..a539ddc 100644 --- a/src/plugins/rule-matcher.test.ts +++ b/src/plugins/rule-matcher.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from 'vitest'; -import { isMatchedRule } from './rule-matcher'; +import { isMatchedRule } from './rule-matcher.js'; import httpMocks from 'node-mocks-http'; const sharedRule = { diff --git a/src/plugins/rule-matcher.ts b/src/plugins/rule-matcher.ts index 3fb9178..2f6a2e7 100644 --- a/src/plugins/rule-matcher.ts +++ b/src/plugins/rule-matcher.ts @@ -1,5 +1,5 @@ import type { IncomingMessage } from 'node:http'; -import { trimStartAndEndSlash } from '@/utils'; +import { trimStartAndEndSlash } from '@/utils/index.js'; export function isMatchedRule( rule: { diff --git a/src/plugins/spy-middleware.ts b/src/plugins/spy-middleware.ts index e9baae3..790309f 100644 --- a/src/plugins/spy-middleware.ts +++ b/src/plugins/spy-middleware.ts @@ -1,8 +1,8 @@ import type { Options as HttpProxyMiddlewareOptions } from 'http-proxy-middleware'; import { createProxyMiddleware, responseInterceptor } from 'http-proxy-middleware'; -import { env } from '@/environment'; -import { stringLogger } from '@/logger'; -import { SpyRulePlugin } from './spy-rule-plugin'; +import { env } from '@/environment/env.js'; +import { stringLogger } from '@/logger/logger.js'; +import { SpyRulePlugin } from './spy-rule-plugin.js'; /** * Configure proxy middleware */ diff --git a/src/plugins/spy-rule-plugin.ts b/src/plugins/spy-rule-plugin.ts index d9aaeec..5860344 100644 --- a/src/plugins/spy-rule-plugin.ts +++ b/src/plugins/spy-rule-plugin.ts @@ -1,8 +1,9 @@ -import { logger } from '@/logger'; +import { logger } from '@/logger/logger.js'; import type { IncomingMessage, ServerResponse } from 'node:http'; -import { ResponseTransformerPlugin } from './response-transformer'; -import { getSpyConfig } from './bootstrap'; -import { isMatchedRule } from './rule-matcher'; +import { ResponseTransformerPlugin } from './response-transformer/response-transformer.js'; +import { getSpyConfig } from './bootstrap.js'; +import { isMatchedRule } from './rule-matcher.js'; +import type { RuleConfig } from './rule.schema.js'; export interface HandleResponseParams { responseBuffer: Buffer; @@ -20,7 +21,7 @@ export class SpyRulePlugin { res.setHeader('Powered-by', 'thaitype/spy-reverse-proxy'); const rule = await getSpyConfig(); - for (const value of Object.values(rule.rules)) { + for (const value of Object.values(rule.rules)) { if (isMatchedRule(value, req)) { if (value.plugin === ResponseTransformerPlugin.name) { const plugin = new ResponseTransformerPlugin(value); diff --git a/src/plugins/spy-rule.test.ts b/src/plugins/spy-rule.test.ts index e16ec8d..1d32ffc 100644 --- a/src/plugins/spy-rule.test.ts +++ b/src/plugins/spy-rule.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from 'vitest'; -import { SpyRule } from './spy-rule'; -import { RuleConfig } from './rule.schema'; +import { SpyRule } from './spy-rule.js'; +import { RuleConfig } from './rule.schema.js'; const sharedRule = { plugin: 'plugin', diff --git a/src/plugins/spy-rule.ts b/src/plugins/spy-rule.ts index 3b56df2..9667657 100644 --- a/src/plugins/spy-rule.ts +++ b/src/plugins/spy-rule.ts @@ -1,8 +1,8 @@ -import type { SpyConfigRuleEntity } from '@/entities'; -import type { Rule, RuleConfig } from './rule.schema'; -import { trimStartAndEndSlash } from '@/utils'; -import { ResponseTransformerPlugin } from '.'; -import { ResponseTransformerExpression } from './response-transformer/response-transformer-expression'; +import type { SpyConfigRuleEntity } from '@/entities/spy-config-rule.entity.js'; +import type { Rule, RuleConfig } from './rule.schema.js'; +import { trimStartAndEndSlash } from '@/utils/index.js'; +import { ResponseTransformerExpression } from './response-transformer/response-transformer-expression.js'; +import { ResponseTransformerPlugin } from './response-transformer/response-transformer.js'; /** * TODO: Hard coded allowed plugins diff --git a/src/server.ts b/src/server.ts index c8629ff..850f30d 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,7 +1,7 @@ import express from 'express'; -import { env } from '@/environment'; -import { logger } from '@/logger'; -import { registerSpyPlugin } from './plugins'; +import { env } from '@/environment/env.js'; +import { logger } from '@/logger/logger.js'; +import { registerSpyPlugin } from './plugins/index.js'; export async function server() { const app = express(); diff --git a/src/utils/index.ts b/src/utils/index.ts index 04bca77..9be8099 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1 +1 @@ -export * from './utils'; +export * from './utils.js'; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 8c155f3..960d4ec 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,3 +1,4 @@ +import type { RuleConfig } from '@/plugins/rule.schema.js'; import { ZodError } from 'zod'; import { fromZodError } from 'zod-validation-error'; @@ -14,3 +15,12 @@ export function extractErorMessage(error: unknown): string { return String(error); } + +export function mergeRuleConfig(ruleConfig: RuleConfig, newRuleConfig: RuleConfig): RuleConfig { + const mergedRuleConfig: RuleConfig = { + rules: { ...ruleConfig.rules, ...newRuleConfig.rules }, + errorMessages: [...ruleConfig.errorMessages, ...newRuleConfig.errorMessages], + }; + + return mergedRuleConfig; +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 47d7457..fcb5303 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,9 +12,9 @@ "strict": true, "noUncheckedIndexedAccess": true, // /* If transpiling with TypeScript: */ - // "moduleResolution": "NodeNext", - // "module": "NodeNext", - // "outDir": "dist", + "moduleResolution": "NodeNext", + "module": "NodeNext", + "outDir": "dist", // "sourceMap": true, // /* AND if you're building for a library: */ // "declaration": true, @@ -22,9 +22,9 @@ // "composite": true, // "declarationMap": true, /* If NOT transpiling with TypeScript: */ - "moduleResolution": "Bundler", - "module": "ESNext", - "noEmit": true, + // "moduleResolution": "Bundler", + // "module": "ESNext", + // "noEmit": true, // /* If your code runs in the DOM: */ "lib": ["es2022", "dom", "dom.iterable"], /* If your code doesn't run in the DOM: */