From bee4519ea3c539bfa9b22fabbed843ee6794036f Mon Sep 17 00:00:00 2001 From: Martin Seidel Date: Sat, 14 Mar 2020 15:13:59 +0100 Subject: [PATCH] Static assets Updated readme --- README.md | 45 ++++++++- package-lock.json | 148 ++++++++++++++++++++++++++---- package.json | 10 +- src/KoaAdapter.ts | 10 +- src/NestKoaApplication.ts | 3 + test/StaticAssets.test.ts | 52 +++++++++++ test/sample-data/static/image.png | Bin 0 -> 1045 bytes 7 files changed, 245 insertions(+), 23 deletions(-) create mode 100644 test/StaticAssets.test.ts create mode 100644 test/sample-data/static/image.png diff --git a/README.md b/README.md index 56d6264..f4d44b8 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,8 @@ It consists of `KoaAdapter` which is basically just mapping between Nest server ### Missing parts - - Static assets are not yet implemented - Setting view engine is not implemented - Rendering of static pages is not yet implemented - - CORS settings are not yet implemented - - Not found handler is not yet implemented ## How to use @@ -68,3 +65,45 @@ class TestModule { } } ``` + +#### CORS + +To use CORS you'll have to install [`@koa/cors`](https://github.com/koajs/cors) package. + +``` +npm install @koa/cors +``` + +After installation is done you can set CORS same way as in normal NEST application. + +> The `enableCors` method accepts options same as normal Nest application. The only difference is in `origin` property which should not be function. + +```typescript +const app = NestFactory.create(AppModule, new KoaAdapter()); + +app.enableCors(); + +await app.init(); +``` + +#### Static assets + +To use static assets you have to install [`koa-static`](https://github.com/koajs/static) package. + +``` +npm install koa-static +``` + +Once you have the dependency installed you can set you static assets folder. + +```typescript +const app = NestFactory.create(AppModule, new KoaAdapter()); + +app.useStaticAssets(path.join(__dirname, 'static')); + +app.useStaticAssets(path.join(__dirname, 'static'), options); + +await app.init(); +``` + +> The `useStaticAssets` method also accepts [options](https://github.com/koajs/static#options) which are exactly same as those from `koa-static`. diff --git a/package-lock.json b/package-lock.json index 5388aa2..8540199 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "resolved": "https://registry.npmjs.org/@koa/cors/-/cors-3.0.0.tgz", "integrity": "sha512-hDp+cXj6vTYSwHRJfiSpnf5dTMv3FmqNKh1or9BPJk4SHOviHnK9GoL9dT0ypt/E+hlkRkZ9edHylcosW3Ghrw==", "dev": true, + "optional": true, "requires": { "vary": "^1.1.2" } @@ -301,6 +302,25 @@ "@types/koa": "*" } }, + "@types/koa-send": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@types/koa-send/-/koa-send-4.1.2.tgz", + "integrity": "sha512-rfqKIv9bFds39Jxvsp8o3YJLnEQVPVriYA14AuO2OY65IHh/4UX4U/iMs5L0wATpcRmm1bbe0BNk23TRwx3VQQ==", + "dev": true, + "requires": { + "@types/koa": "*" + } + }, + "@types/koa-static": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/koa-static/-/koa-static-4.0.1.tgz", + "integrity": "sha512-SSpct5fEcAeRkBHa3RiwCIRfDHcD1cZRhwRF///ZfvRt8KhoqRrhK6wpDlYPk/vWHVFE9hPGqh68bhzsHkir4w==", + "dev": true, + "requires": { + "@types/koa": "*", + "@types/koa-send": "*" + } + }, "@types/koa__cors": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/koa__cors/-/koa__cors-3.0.1.tgz", @@ -388,12 +408,12 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.22.0.tgz", - "integrity": "sha512-BvxRLaTDVQ3N+Qq8BivLiE9akQLAOUfxNHIEhedOcg8B2+jY8Rc4/D+iVprvuMX1AdezFYautuGDwr9QxqSxBQ==", + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.23.0.tgz", + "integrity": "sha512-8iA4FvRsz8qTjR0L/nK9RcRUN3QtIHQiOm69FzV7WS3SE+7P7DyGGwh3k4UNR2JBbk+Ej2Io+jLAaqKibNhmtw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "2.22.0", + "@typescript-eslint/experimental-utils": "2.23.0", "eslint-utils": "^1.4.3", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", @@ -409,32 +429,32 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.22.0.tgz", - "integrity": "sha512-sJt1GYBe6yC0dWOQzXlp+tiuGglNhJC9eXZeC8GBVH98Zv9jtatccuhz0OF5kC/DwChqsNfghHx7OlIDQjNYAQ==", + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.23.0.tgz", + "integrity": "sha512-OswxY59RcXH3NNPmq+4Kis2CYZPurRU6mG5xPcn24CjFyfdVli5mySwZz/g/xDbJXgDsYqNGq7enV0IziWGXVQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.22.0", + "@typescript-eslint/typescript-estree": "2.23.0", "eslint-scope": "^5.0.0" } }, "@typescript-eslint/parser": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.22.0.tgz", - "integrity": "sha512-FaZKC1X+nvD7qMPqKFUYHz3H0TAioSVFGvG29f796Nc5tBluoqfHgLbSFKsh7mKjRoeTm8J9WX2Wo9EyZWjG7w==", + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.23.0.tgz", + "integrity": "sha512-k61pn/Nepk43qa1oLMiyqApC6x5eP5ddPz6VUYXCAuXxbmRLqkPYzkFRKl42ltxzB2luvejlVncrEpflgQoSUg==", "dev": true, "requires": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "2.22.0", - "@typescript-eslint/typescript-estree": "2.22.0", + "@typescript-eslint/experimental-utils": "2.23.0", + "@typescript-eslint/typescript-estree": "2.23.0", "eslint-visitor-keys": "^1.1.0" } }, "@typescript-eslint/typescript-estree": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.22.0.tgz", - "integrity": "sha512-2HFZW2FQc4MhIBB8WhDm9lVFaBDy6h9jGrJ4V2Uzxe/ON29HCHBTj3GkgcsgMWfsl2U5as+pTOr30Nibaw7qRQ==", + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.23.0.tgz", + "integrity": "sha512-pmf7IlmvXdlEXvE/JWNNJpEvwBV59wtJqA8MLAxMKLXNKVRC3HZBXR/SlZLPWTCcwOSg9IM7GeRSV3SIerGVqw==", "dev": true, "requires": { "debug": "^4.1.1", @@ -2861,6 +2881,30 @@ } } }, + "koa-send": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.0.tgz", + "integrity": "sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ==", + "dev": true, + "optional": true, + "requires": { + "debug": "^3.1.0", + "http-errors": "^1.6.3", + "mz": "^2.7.0", + "resolve-path": "^1.4.0" + } + }, + "koa-static": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/koa-static/-/koa-static-5.0.0.tgz", + "integrity": "sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==", + "dev": true, + "optional": true, + "requires": { + "debug": "^3.1.0", + "koa-send": "^5.0.0" + } + }, "latest-version": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", @@ -3427,6 +3471,18 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "optional": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4307,6 +4363,46 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "resolve-path": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz", + "integrity": "sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc=", + "dev": true, + "optional": true, + "requires": { + "http-errors": "~1.6.2", + "path-is-absolute": "1.0.1" + }, + "dependencies": { + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "optional": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true, + "optional": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true, + "optional": true + } + } + }, "responselike": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", @@ -4712,6 +4808,26 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "dev": true, + "optional": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "dev": true, + "optional": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", diff --git a/package.json b/package.json index 418743f..56d3bd8 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,10 @@ "@nestjs/common": "^6.11.8", "@nestjs/core": "^6.11.8" }, + "optionalDependencies": { + "@koa/cors": "^3.0.0", + "koa-static": "^5.0.0" + }, "devDependencies": { "@koa/cors": "^3.0.0", "@nestjs/common": "^6.11.11", @@ -42,15 +46,17 @@ "@types/koa": "^2.11.2", "@types/koa-bodyparser": "^4.3.0", "@types/koa-router": "^7.4.0", + "@types/koa-static": "^4.0.1", "@types/koa__cors": "^3.0.1", "@types/mocha": "^7.0.2", "@types/supertest": "^2.0.8", - "@typescript-eslint/eslint-plugin": "^2.22.0", - "@typescript-eslint/parser": "^2.22.0", + "@typescript-eslint/eslint-plugin": "^2.23.0", + "@typescript-eslint/parser": "^2.23.0", "eslint": "^6.8.0", "eslint-config-prettier": "^6.10.0", "eslint-plugin-mocha": "^6.3.0", "eslint-plugin-prettier": "^3.1.2", + "koa-static": "^5.0.0", "mocha": "^7.1.0", "np": "^6.2.0", "prettier": "^1.19.1", diff --git a/src/KoaAdapter.ts b/src/KoaAdapter.ts index 33b46f1..19c9b0d 100644 --- a/src/KoaAdapter.ts +++ b/src/KoaAdapter.ts @@ -14,6 +14,7 @@ import { nestToKoaMiddleware } from './NestKoaMiddleware'; import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface'; import { loadPackage } from '@nestjs/common/utils/load-package.util'; import { KoaCorsOptions } from './KoaCorsOptions'; +import { Options as ServeStaticOptions } from 'koa-static'; type HttpMethods = | 'all' @@ -170,8 +171,13 @@ export class KoaAdapter extends AbstractHttpAdapter< this.httpServer = http.createServer(this.getInstance().callback()); } - public useStaticAssets(...args: any[]): any { - // TODO https://www.npmjs.com/package/koa-static + public useStaticAssets(path: string, options?: ServeStaticOptions): any { + const serveStaticMiddleware = loadPackage( + 'koa-static', + 'KoaAdapter.useStaticAssets()', + ); + + this.getInstance().use(serveStaticMiddleware(path, options)); } public setViewEngine(engine: string): any { diff --git a/src/NestKoaApplication.ts b/src/NestKoaApplication.ts index 078c6ff..5e684e1 100644 --- a/src/NestKoaApplication.ts +++ b/src/NestKoaApplication.ts @@ -1,6 +1,9 @@ import { INestApplication } from '@nestjs/common'; import { Middleware } from 'koa'; +import { Options as ServeStaticOptions } from 'koa-static'; export interface NestKoaApplication extends INestApplication { use(...middleware: Middleware[]): this; + + useStaticAssets(path: string, options?: ServeStaticOptions): this; } diff --git a/test/StaticAssets.test.ts b/test/StaticAssets.test.ts new file mode 100644 index 0000000..3fadddf --- /dev/null +++ b/test/StaticAssets.test.ts @@ -0,0 +1,52 @@ +import { KoaAdapter, NestKoaApplication } from '../src'; +import { Test } from '@nestjs/testing'; +import { HelloWorldController } from './utils/HelloWorldController'; +import { join } from 'path'; +import fs from 'fs'; +import { promisify } from 'util'; +import supertest from 'supertest'; +import assert from 'assert'; + +const readFile = promisify(fs.readFile); + +describe.only('Static assets', () => { + let app: NestKoaApplication; + let file: Buffer; + + before(async () => { + file = await readFile(join(__dirname, 'sample-data/static/image.png')); + }); + + beforeEach(async () => { + const module = await Test.createTestingModule({ + controllers: [HelloWorldController], + }).compile(); + + app = module.createNestApplication(new KoaAdapter()); + app.useStaticAssets(join(__dirname, 'sample-data/static/')); + + await app.init(); + }); + + afterEach(async () => { + await app.close(); + }); + + it('should serve static assets from defined folder', async () => { + return supertest(app.getHttpServer()) + .get('/image.png') + .expect(200) + .expect(res => { + assert(file.equals(res.body)); + }); + }); + + it('should still serve data from endpoints', async () => { + return supertest(app.getHttpServer()) + .get('/hello/world/sync') + .expect(200) + .expect({ + hello: 'world-sync', + }); + }); +}); diff --git a/test/sample-data/static/image.png b/test/sample-data/static/image.png new file mode 100644 index 0000000000000000000000000000000000000000..8991cc475b0b71103b611cbde0eb35b98552fcc8 GIT binary patch literal 1045 zcmV+w1nT>VP)*jSJ z#6&&qIrrPZo!sP`Nis7RG>sq#f*=TjAP9mW2!bF8f*=TjAP9mW2!i-;X}*60Nqe0n zBq0e&UQJ>)w&WKbzo_O$V;G3Xp`+~hICOZq#5~#83>V-B58eU5F(#ZpvIy*3jCCHb z_t+UN+0b-@>2VSrW+qRD-+8tv5o)i*8tyWwTxba0E*I!7IXO}_;`bSuEcb4b(IoJ?JMS7gmKe6xx|~BYTlV_ z6J%r2=W_K4JopR}e+8400=QuR8nCTw$Qws5B5}O?Q4o@LS-#OXHPIMd;L=Iv-ma39 zZx>5D?{=|ViLO2AB>pZ@=Mporm0K|jRvO8Ck;I6ZfypIvl;~nv(V{cXjoA+qOWHV1 zic&JSdy7ubCaL2q+%KzF7;ZYb$smE1@JWFJic5 z-1(XVQO>F&4~skYPQ(mp)dJaPeKvU&8r)td<6W%N*iLZIbS$NmUQ)nvJFL#7yKK@Y_YA@OMP~pD7tJ4<1f@Ne~1<5ClOG1VIo4K@bE%{1f~I%xJ589Wl^Z P00000NkvXXu0mjf@k-ho literal 0 HcmV?d00001