diff --git a/.github/openapi/python-config.json b/.github/openapi/python-config.json index 709747cf2..cedb23a7e 100644 --- a/.github/openapi/python-config.json +++ b/.github/openapi/python-config.json @@ -1,5 +1,5 @@ { "generatorName": "python", - "packageName": "scicat-sdk-py", - "projectName": "scicat-sdk-py" + "packageName": "scicat_sdk_py", + "projectName": "scicat_sdk_py" } diff --git a/.github/openapi/python-pydantic-v1-config.json b/.github/openapi/python-pydantic-v1-config.json index ef10d66f2..fc2dac9bf 100644 --- a/.github/openapi/python-pydantic-v1-config.json +++ b/.github/openapi/python-pydantic-v1-config.json @@ -1,5 +1,5 @@ { "generatorName": "python-pydantic-v1", - "packageName": "scicat-sdk-pydantic", - "projectName": "scicat-sdk-pydantic" + "packageName": "scicat_sdk_pydantic", + "projectName": "scicat_sdk_pydantic" } diff --git a/README.md b/README.md index b1ffe56a3..1f4c418bb 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,7 @@ Valid environment variables for the .env file. See [.env.example](/.env.example) | `ES_REFRESH` | string | | If set to `wait_for`, Elasticsearch will wait till data is inserted into the specified index before returning a response. | false | | `LOGGERS_CONFIG_FILE` | string | | The file name for loggers configuration, located in the project root directory. | "loggers.json" | | `SWAGGER_PATH` | string | Yes | swaggerPath is the path where the swagger UI will be available| "explorer"| +| `MAX_FILE_UPLOAD_SIZE` | string | Yes | Maximum allowed file upload size | "16mb"| ## Migrating from the old SciCat Backend diff --git a/package-lock.json b/package-lock.json index 2e9c80f8a..bdc009447 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1298,9 +1298,9 @@ } }, "node_modules/@faker-js/faker": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.0.3.tgz", - "integrity": "sha512-lWrrK4QNlFSU+13PL9jMbMKLJYXDFu3tQfayBsMXX7KL/GiQeqfB1CzHkqD5UHBUtPAuPo6XwGbMFNdVMZObRA==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.2.0.tgz", + "integrity": "sha512-ulqQu4KMr1/sTFIYvqSdegHT8NIkt66tFAkugGnHA+1WAfEn6hMzNR+svjXGFRVLnapxvej67Z/LwchFrnLBUg==", "dev": true, "funding": [ { @@ -2139,9 +2139,9 @@ } }, "node_modules/@nestjs/axios": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.1.0.tgz", - "integrity": "sha512-CpeK2ickH//ml+H7kX+QPIpeTwER4yedVcw6GPe6Nv58cmKTa0sb+3A3It7ChKD4deW4UKNvZIpYkUk18q78YQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.1.2.tgz", + "integrity": "sha512-pFlfi4ZQsZtTNNhvgssbxjCHUd1nMpV3sXy/xOOB2uEJhw3M8j8SFR08gjFNil2we2Har7VCsXLfCkwbMHECFQ==", "peerDependencies": { "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", "axios": "^1.3.1", @@ -2288,9 +2288,9 @@ } }, "node_modules/@nestjs/common": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.6.tgz", - "integrity": "sha512-KkezkZvU9poWaNq4L+lNvx+386hpOxPJkfXBBeSMrcqBOx8kVr36TGN2uYkF4Ta4zNu1KbCjmZbc0rhHSg296g==", + "version": "10.4.7", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.7.tgz", + "integrity": "sha512-gIOpjD3Mx8gfYGxYm/RHPcJzqdknNNFCyY+AxzBT3gc5Xvvik1Dn5OxaMGw5EbVfhZgJKVP0n83giUOAlZQe7w==", "dependencies": { "iterare": "1.2.1", "tslib": "2.7.0", @@ -2450,9 +2450,9 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.6.tgz", - "integrity": "sha512-HcyCpAKccAasrLSGRTGWv5BKRs0rwTIFOSsk6laNyqfqvgvYcJQAedarnm4jmaemtmSJ0PFI9PmtEZADd2ahCg==", + "version": "10.4.7", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.7.tgz", + "integrity": "sha512-q6XDOxZPTZ9cxALcVuKUlRBk+cVEv6dW2S8p2yVre22kpEQxq53/OI8EseDvzObGb6hepZ8+yBY04qoYqSlXNQ==", "dependencies": { "body-parser": "1.20.3", "cors": "2.8.5", @@ -3158,9 +3158,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.8.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.1.tgz", - "integrity": "sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dependencies": { "undici-types": "~6.19.8" } diff --git a/src/admin/admin.module.ts b/src/admin/admin.module.ts index e0f55e609..abf43368b 100644 --- a/src/admin/admin.module.ts +++ b/src/admin/admin.module.ts @@ -1,10 +1,11 @@ import { Module } from "@nestjs/common"; import { AdminService } from "./admin.service"; import { AdminController } from "./admin.controller"; +import { ConfigModule } from "@nestjs/config"; @Module({ controllers: [AdminController], - imports: [], + imports: [ConfigModule], providers: [AdminService], exports: [AdminService], }) diff --git a/src/admin/admin.service.spec.ts b/src/admin/admin.service.spec.ts index 5b6c3c628..a138a3a38 100644 --- a/src/admin/admin.service.spec.ts +++ b/src/admin/admin.service.spec.ts @@ -1,27 +1,29 @@ -import { getModelToken } from "@nestjs/mongoose"; import { Test, TestingModule } from "@nestjs/testing"; +import { ConfigService } from "@nestjs/config"; import { AdminService } from "./admin.service"; import config from "../config/frontend.config.json"; +import theme from "../config/frontend.theme.json"; -const mockConfig: Record = config; - -describe("PoliciesService", () => { +describe("AdminService", () => { let service: AdminService; + const mockConfigService = { + get: jest.fn((propertyPath: string) => { + const config = { + maxFileUploadSizeInMb: "12mb", + } as Record; + + return config[propertyPath]; + }), + }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ + AdminService, { - provide: getModelToken("Policy"), - useValue: { - new: jest.fn().mockResolvedValue(mockConfig), - constructor: jest.fn().mockResolvedValue(mockConfig), - find: jest.fn(), - create: jest.fn(), - exec: jest.fn(), - }, + provide: ConfigService, + useValue: mockConfigService, }, - AdminService, ], }).compile(); @@ -31,4 +33,22 @@ describe("PoliciesService", () => { it("should be defined", () => { expect(service).toBeDefined(); }); + + describe("getConfig", () => { + it("should return modified config", async () => { + const result = await service.getConfig(); + + expect(result).toEqual({ + ...config, + maxFileUploadSizeInMb: "12mb", + }); + }); + }); + + describe("getTheme", () => { + it("should return theme config", async () => { + const result = await service.getTheme(); + expect(result).toEqual(theme); + }); + }); }); diff --git a/src/admin/admin.service.ts b/src/admin/admin.service.ts index 970ad4b3d..eccad92ab 100644 --- a/src/admin/admin.service.ts +++ b/src/admin/admin.service.ts @@ -1,14 +1,31 @@ import { Injectable } from "@nestjs/common"; import config from "../config/frontend.config.json"; import theme from "../config/frontend.theme.json"; +import { ConfigService } from "@nestjs/config"; @Injectable() export class AdminService { + constructor(private configService: ConfigService) {} + async getConfig(): Promise | null> { - return config; + const modifiedConfig = this.applyBackendConfigAdjustments(); + + return modifiedConfig; } async getTheme(): Promise | null> { return theme; } + + // NOTE: Adjusts backend config values for frontend use (e.g., file upload limits). + // Add future backend-dependent adjustments here as needed. + private applyBackendConfigAdjustments(): Record { + const postEncodedMaxFileUploadSize = + this.configService.get("maxFileUploadSizeInMb") || "16mb"; + + return { + ...config, + maxFileUploadSizeInMb: postEncodedMaxFileUploadSize, + }; + } } diff --git a/src/config/configuration.ts b/src/config/configuration.ts index 94a190d89..1fc02fbdc 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -58,6 +58,7 @@ const configuration = () => { }); const config = { + maxFileUploadSizeInMb: process.env.MAX_FILE_UPLOAD_SIZE || "16mb", // 16MB by default versions: { api: "3", }, diff --git a/src/main.ts b/src/main.ts index 3af41b802..327248a93 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,4 @@ import session from "express-session"; -import { json } from "body-parser"; import { NestFactory } from "@nestjs/core"; import { DocumentBuilder, @@ -10,9 +9,10 @@ import { AppModule } from "./app.module"; import { Logger, ValidationPipe, VersioningType } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { AllExceptionsFilter, ScicatLogger } from "./loggers/logger.service"; +import { NestExpressApplication } from "@nestjs/platform-express"; async function bootstrap() { - const app = await NestFactory.create(AppModule, { + const app = await NestFactory.create(AppModule, { bufferLogs: true, }); const configService: ConfigService, false> = app.get( @@ -84,7 +84,15 @@ async function bootstrap() { }), ); - app.use(json({ limit: "16mb" })); + const fileUploadLimitInMb = configService.get( + "maxFileUploadSizeInMb", + ); + + app.useBodyParser("json", { limit: fileUploadLimitInMb }); + app.useBodyParser("urlencoded", { + limit: fileUploadLimitInMb, + extended: true, + }); const expressSessionSecret = configService.get( "expressSessionSecret",