diff --git a/.gitignore b/.gitignore index a9bf361..835186c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,8 @@ node_modules dist assets *.tsbuildinfo -morphus.* data + +# Configs +morphus.* +keyfile.json diff --git a/package.json b/package.json index 67de363..be104d9 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,13 @@ "typescript": "^4.4.4" }, "dependencies": { + "@google-cloud/storage": "^5.16.0", "caniuse-db": "^1.0.30001280", "class-validator": "^0.13.1", "convict": "^6.2.1", "convict-format-with-validator": "^6.2.0", "device-detector-js": "^3.0.0", + "fast-crc32c": "^2.0.0", "fastify": "^3.23.1", "fastify-caching": "^6.1.0", "fastify-compress": "^3.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1fb92f2..3f14cd8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,6 +1,7 @@ lockfileVersion: 5.3 specifiers: + '@google-cloud/storage': ^5.16.0 '@types/convict': ^6.1.1 '@types/flat': ^5.0.2 '@types/js-yaml': ^4.0.4 @@ -13,6 +14,7 @@ specifiers: convict: ^6.2.1 convict-format-with-validator: ^6.2.0 device-detector-js: ^3.0.0 + fast-crc32c: ^2.0.0 fastify: ^3.23.1 fastify-caching: ^6.1.0 fastify-compress: ^3.6.1 @@ -28,11 +30,13 @@ specifiers: under-pressure: ^5.8.0 dependencies: + '@google-cloud/storage': 5.16.0 caniuse-db: 1.0.30001280 class-validator: 0.13.1 convict: 6.2.1 convict-format-with-validator: 6.2.0 device-detector-js: 3.0.0 + fast-crc32c: 2.0.0 fastify: 3.23.1 fastify-caching: 6.1.0 fastify-compress: 3.6.1 @@ -64,6 +68,73 @@ packages: ajv: 6.12.6 dev: false + /@google-cloud/common/3.8.1: + resolution: {integrity: sha512-FOs3NFU6bDt5mXE7IFpwIeqzLwRZNu9lJYl+bHVNkwmxX/w4VyDZAiGjQHhpV1Ek+muNKlX8HPchxaIxNTuOhw==} + engines: {node: '>=10'} + dependencies: + '@google-cloud/projectify': 2.1.1 + '@google-cloud/promisify': 2.0.4 + arrify: 2.0.1 + duplexify: 4.1.2 + ent: 2.2.0 + extend: 3.0.2 + google-auth-library: 7.10.2 + retry-request: 4.2.2 + teeny-request: 7.1.3 + transitivePeerDependencies: + - supports-color + dev: false + + /@google-cloud/paginator/3.0.6: + resolution: {integrity: sha512-XCTm/GfQIlc1ZxpNtTSs/mnZxC2cePNhxU3X8EzHXKIJ2JFncmJj2Fcd2IP+gbmZaSZnY0juFxbUCkIeuu/2eQ==} + engines: {node: '>=10'} + dependencies: + arrify: 2.0.1 + extend: 3.0.2 + dev: false + + /@google-cloud/projectify/2.1.1: + resolution: {integrity: sha512-+rssMZHnlh0twl122gXY4/aCrk0G1acBqkHFfYddtsqpYXGxA29nj9V5V9SfC+GyOG00l650f6lG9KL+EpFEWQ==} + engines: {node: '>=10'} + dev: false + + /@google-cloud/promisify/2.0.4: + resolution: {integrity: sha512-j8yRSSqswWi1QqUGKVEKOG03Q7qOoZP6/h2zN2YO+F5h2+DHU0bSrHCK9Y7lo2DI9fBd8qGAw795sf+3Jva4yA==} + engines: {node: '>=10'} + dev: false + + /@google-cloud/storage/5.16.0: + resolution: {integrity: sha512-I/1lA78v9c5EbOM/KfcYsjzA7YlHQmhpzHYdKLKdYC8X5fFaQrw5nK+FU8GbEwdPxmREAF2qPbN7Ccq+A/ndWA==} + engines: {node: '>=10'} + dependencies: + '@google-cloud/common': 3.8.1 + '@google-cloud/paginator': 3.0.6 + '@google-cloud/promisify': 2.0.4 + arrify: 2.0.1 + async-retry: 1.3.3 + compressible: 2.0.18 + date-and-time: 2.0.1 + duplexify: 4.1.2 + extend: 3.0.2 + gcs-resumable-upload: 3.6.0 + get-stream: 6.0.1 + hash-stream-validation: 0.2.4 + mime: 3.0.0 + mime-types: 2.1.34 + p-limit: 3.1.0 + pumpify: 2.0.1 + snakeize: 0.1.0 + stream-events: 1.0.5 + xdg-basedir: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@tootallnate/once/2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: false + /@types/convict/6.1.1: resolution: {integrity: sha512-R+JLaTvhsD06p4jyjUDtbd5xMtZTRE3c0iI+lrFWZogSVEjgTWPYwvJPVf+t92E+yrlbXa4X4Eg9ro6gPdUt4w==} dependencies: @@ -116,6 +187,13 @@ packages: dev: false optional: true + /abort-controller/3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + dependencies: + event-target-shim: 5.0.1 + dev: false + /abstract-cache/1.0.1: resolution: {integrity: sha512-EfUeMhRUbG5bVVbrSY/ogLlFXoyfMAPxMlSP7wrEqH53d+59r2foVy9a5KjmprLKFLOfPQCNKEfpBN/nQ76chw==} dependencies: @@ -128,6 +206,15 @@ packages: resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} dev: false + /agent-base/6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.2 + transitivePeerDependencies: + - supports-color + dev: false + /ajv/6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: @@ -199,6 +286,17 @@ packages: mri: 1.1.4 dev: false + /arrify/2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + dev: false + + /async-retry/1.3.3: + resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + dependencies: + retry: 0.13.1 + dev: false + /async/3.2.2: resolution: {integrity: sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==} dev: false @@ -236,6 +334,10 @@ packages: engines: {node: '>=0.6'} dev: false + /bignumber.js/9.0.1: + resolution: {integrity: sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==} + dev: false + /binary-extensions/2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -248,6 +350,13 @@ packages: chainsaw: 0.1.0 dev: false + /bindings/1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + dependencies: + file-uri-to-path: 1.0.0 + dev: false + optional: true + /bl/4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: @@ -279,6 +388,10 @@ packages: fill-range: 7.0.1 dev: true + /buffer-equal-constant-time/1.0.1: + resolution: {integrity: sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=} + dev: false + /buffer-from/1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -406,9 +519,28 @@ packages: resolution: {integrity: sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==} dev: false + /compressible/2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.51.0 + dev: false + /concat-map/0.0.1: resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + /configstore/5.0.1: + resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} + engines: {node: '>=8'} + dependencies: + dot-prop: 5.3.0 + graceful-fs: 4.2.8 + make-dir: 3.1.0 + unique-string: 2.0.0 + write-file-atomic: 3.0.3 + xdg-basedir: 4.0.0 + dev: false + /console-control-strings/1.1.0: resolution: {integrity: sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=} dev: false @@ -441,6 +573,15 @@ packages: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true + /crypto-random-string/2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + dev: false + + /date-and-time/2.0.1: + resolution: {integrity: sha512-O7Xe5dLaqvY/aF/MFWArsAM1J4j7w1CSZlPCX9uHgmb+6SbkPd8Q4YOvfvH/cZGvFlJFfHOZKxQtmMUOoZhc/w==} + dev: false + /dateformat/4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} dev: false @@ -501,6 +642,13 @@ packages: engines: {node: '>=0.3.1'} dev: true + /dot-prop/5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + dependencies: + is-obj: 2.0.0 + dev: false + /duplexer2/0.1.4: resolution: {integrity: sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=} dependencies: @@ -531,6 +679,12 @@ packages: xtend: 4.0.2 dev: true + /ecdsa-sig-formatter/1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /encoding-negotiator/2.0.1: resolution: {integrity: sha512-GSK7qphNR4iPcejfAlZxKDoz3xMhnspwImK+Af5WhePS9jUpK/Oh7rUdyENWu+9rgDflOCTmAojBsgsvM8neAQ==} engines: {node: '>=10.13.0'} @@ -542,6 +696,10 @@ packages: once: 1.4.0 dev: false + /ent/2.2.0: + resolution: {integrity: sha1-6WQhkyWiHQX0RGai9obtbOX13R0=} + dev: false + /es-abstract/1.19.1: resolution: {integrity: sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==} engines: {node: '>= 0.4'} @@ -586,11 +744,26 @@ packages: engines: {node: '>=0.8.0'} dev: false + /event-target-shim/5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + dev: false + /expand-template/2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} dev: false + /extend/3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + dev: false + + /fast-crc32c/2.0.0: + resolution: {integrity: sha512-LIREwygxtxzHF11oLJ4xIVKu/ZWNgrj/QaGvaSD8ZggIsgCyCtSYevlrpWVqNau57ZwezV8K1HFBSjQ7FcRbTQ==} + optionalDependencies: + sse4_crc32: 6.0.1 + dev: false + /fast-decode-uri-component/1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} dev: false @@ -622,6 +795,10 @@ packages: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} dev: false + /fast-text-encoding/1.0.3: + resolution: {integrity: sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==} + dev: false + /fast-xml-parser/3.19.0: resolution: {integrity: sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg==} hasBin: true @@ -702,6 +879,11 @@ packages: reusify: 1.0.4 dev: false + /file-uri-to-path/1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + dev: false + optional: true + /fill-range/7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -785,6 +967,46 @@ packages: wide-align: 1.1.5 dev: false + /gaxios/4.3.2: + resolution: {integrity: sha512-T+ap6GM6UZ0c4E6yb1y/hy2UB6hTrqhglp3XfmU9qbLCGRYhLVV5aRPpC4EmoG8N8zOnkYCgoBz+ScvGAARY6Q==} + engines: {node: '>=10'} + dependencies: + abort-controller: 3.0.0 + extend: 3.0.2 + https-proxy-agent: 5.0.0 + is-stream: 2.0.1 + node-fetch: 2.6.6 + transitivePeerDependencies: + - supports-color + dev: false + + /gcp-metadata/4.3.1: + resolution: {integrity: sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==} + engines: {node: '>=10'} + dependencies: + gaxios: 4.3.2 + json-bigint: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /gcs-resumable-upload/3.6.0: + resolution: {integrity: sha512-IyaNs4tx3Mp2UKn0CltRUiW/ZXYFlBNuK/V+ixs80chzVD+BJq3+8bfiganATFfCoMluAjokF9EswNJdVuOs8A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + abort-controller: 3.0.0 + async-retry: 1.3.3 + configstore: 5.0.1 + extend: 3.0.2 + gaxios: 4.3.2 + google-auth-library: 7.10.2 + pumpify: 2.0.1 + stream-events: 1.0.5 + transitivePeerDependencies: + - supports-color + dev: false + /get-intrinsic/1.1.1: resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==} dependencies: @@ -793,6 +1015,11 @@ packages: has-symbols: 1.0.2 dev: false + /get-stream/6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: false + /get-symbol-description/1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} engines: {node: '>= 0.4'} @@ -822,10 +1049,46 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 + /google-auth-library/7.10.2: + resolution: {integrity: sha512-M37o9Kxa/TLvOLgF71SXvLeVEP5sbSTmKl1zlIgl72SFy5PtsU3pOdu8G8MIHHpQ3/NZabDI8rQkA9DvQVKkPA==} + engines: {node: '>=10'} + dependencies: + arrify: 2.0.1 + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + fast-text-encoding: 1.0.3 + gaxios: 4.3.2 + gcp-metadata: 4.3.1 + gtoken: 5.3.1 + jws: 4.0.0 + lru-cache: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /google-p12-pem/3.1.2: + resolution: {integrity: sha512-tjf3IQIt7tWCDsa0ofDQ1qqSCNzahXDxdAGJDbruWqu3eCg5CKLYKN+hi0s6lfvzYZ1GDVr+oDF9OOWlDSdf0A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + node-forge: 0.10.0 + dev: false + /graceful-fs/4.2.8: resolution: {integrity: sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==} dev: false + /gtoken/5.3.1: + resolution: {integrity: sha512-yqOREjzLHcbzz1UrQoxhBtpk8KjrVhuqPE7od1K2uhyxG2BHjKZetlbLw/SPZak/QqTIQW+addS+EcjqQsZbwQ==} + engines: {node: '>=10'} + dependencies: + gaxios: 4.3.2 + google-p12-pem: 3.1.2 + jws: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: false + /has-bigints/1.0.1: resolution: {integrity: sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==} dev: false @@ -857,10 +1120,40 @@ packages: dependencies: function-bind: 1.1.1 + /hash-stream-validation/0.2.4: + resolution: {integrity: sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ==} + dev: false + + /http-proxy-agent/5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.2 + transitivePeerDependencies: + - supports-color + dev: false + + /https-proxy-agent/5.0.0: + resolution: {integrity: sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.2 + transitivePeerDependencies: + - supports-color + dev: false + /ieee754/1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} dev: false + /imurmurhash/0.1.4: + resolution: {integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o=} + engines: {node: '>=0.8.19'} + dev: false + /inflight/1.0.6: resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} dependencies: @@ -999,6 +1292,11 @@ packages: engines: {node: '>=0.12.0'} dev: true + /is-obj/2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + dev: false + /is-plain-obj/1.1.0: resolution: {integrity: sha1-caUMhCnfync8kqOQpKA7OfzVHT4=} engines: {node: '>=0.10.0'} @@ -1046,6 +1344,10 @@ packages: has-tostringtag: 1.0.0 dev: false + /is-typedarray/1.0.0: + resolution: {integrity: sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=} + dev: false + /is-weakref/1.0.1: resolution: {integrity: sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==} dependencies: @@ -1073,6 +1375,12 @@ packages: argparse: 2.0.1 dev: false + /json-bigint/1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + dependencies: + bignumber.js: 9.0.1 + dev: false + /json-schema-traverse/0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: false @@ -1085,6 +1393,21 @@ packages: resolution: {integrity: sha1-GjhU4o0rvuqzHMfd9oPS3cVlJwg=} dev: false + /jwa/2.0.0: + resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + dev: false + + /jws/4.0.0: + resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} + dependencies: + jwa: 2.0.0 + safe-buffer: 5.2.1 + dev: false + /leven/2.1.0: resolution: {integrity: sha1-wuep93IJTe6dNCAq6KzORoeHVYA=} engines: {node: '>=0.10.0'} @@ -1126,6 +1449,13 @@ packages: resolution: {integrity: sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=} dev: false + /make-dir/3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + dependencies: + semver: 6.3.0 + dev: false + /make-error/1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true @@ -1149,6 +1479,12 @@ packages: mime-db: 1.51.0 dev: false + /mime/3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + dev: false + /mimic-response/3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -1229,10 +1565,27 @@ packages: semver: 7.3.5 dev: false + /node-addon-api/1.7.2: + resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} + dev: false + optional: true + /node-addon-api/4.2.0: resolution: {integrity: sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q==} dev: false + /node-fetch/2.6.6: + resolution: {integrity: sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==} + engines: {node: 4.x || >=6.0.0} + dependencies: + whatwg-url: 5.0.0 + dev: false + + /node-forge/0.10.0: + resolution: {integrity: sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==} + engines: {node: '>= 6.0.0'} + dev: false + /normalize-path/3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -1286,6 +1639,13 @@ packages: engines: {node: '>=8'} dev: false + /p-limit/3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: false + /path-is-absolute/1.0.1: resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} engines: {node: '>=0.10.0'} @@ -1475,6 +1835,21 @@ packages: engines: {node: '>=4'} dev: false + /retry-request/4.2.2: + resolution: {integrity: sha512-xA93uxUD/rogV7BV59agW/JHPGXeREMWiZc9jhcwY4YdZ7QOtC7qbomYg0n4wyk2lJhggjvKvhNX8wln/Aldhg==} + engines: {node: '>=8.10.0'} + dependencies: + debug: 4.3.2 + extend: 3.0.2 + transitivePeerDependencies: + - supports-color + dev: false + + /retry/0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + dev: false + /reusify/1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -1516,6 +1891,11 @@ packages: resolution: {integrity: sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg==} dev: false + /semver/6.3.0: + resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + hasBin: true + dev: false + /semver/7.3.5: resolution: {integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==} engines: {node: '>=10'} @@ -1581,6 +1961,10 @@ packages: is-arrayish: 0.3.2 dev: false + /snakeize/0.1.0: + resolution: {integrity: sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0=} + dev: false + /sonic-boom/1.4.1: resolution: {integrity: sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==} dependencies: @@ -1611,6 +1995,22 @@ packages: engines: {node: '>= 10.x'} dev: false + /sse4_crc32/6.0.1: + resolution: {integrity: sha512-FUTYXpLroqytNKWIfHzlDWoy9E4tmBB/RklNMy6w3VJs+/XEYAHgbiylg4SS43iOk/9bM0BlJ2EDpFAGT66IoQ==} + engines: {node: '>=4'} + requiresBuild: true + dependencies: + bindings: 1.5.0 + node-addon-api: 1.7.2 + dev: false + optional: true + + /stream-events/1.0.5: + resolution: {integrity: sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==} + dependencies: + stubs: 3.0.0 + dev: false + /stream-shift/1.0.1: resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==} dev: false @@ -1681,6 +2081,10 @@ packages: engines: {node: '>=8'} dev: false + /stubs/3.0.0: + resolution: {integrity: sha1-6NK6H6nJBXAwPAMLaQD31fiavls=} + dev: false + /supports-color/5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -1708,6 +2112,19 @@ packages: readable-stream: 3.6.0 dev: false + /teeny-request/7.1.3: + resolution: {integrity: sha512-Ew3aoFzgQEatLA5OBIjdr1DWJUaC1xardG+qbPPo5k/y/3fMwXLxpjh5UB5dVfElktLaQbbMs80chkz53ByvSg==} + engines: {node: '>=10'} + dependencies: + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.0 + node-fetch: 2.6.6 + stream-events: 1.0.5 + uuid: 8.3.2 + transitivePeerDependencies: + - supports-color + dev: false + /through2/2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} dependencies: @@ -1734,6 +2151,10 @@ packages: is-number: 7.0.0 dev: true + /tr46/0.0.3: + resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=} + dev: false + /traverse/0.3.9: resolution: {integrity: sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=} dev: false @@ -1798,6 +2219,12 @@ packages: safe-buffer: 5.2.1 dev: false + /typedarray-to-buffer/3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + dependencies: + is-typedarray: 1.0.0 + dev: false + /typescript/4.4.4: resolution: {integrity: sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==} engines: {node: '>=4.2.0'} @@ -1828,6 +2255,13 @@ packages: fastify-plugin: 3.0.0 dev: false + /unique-string/2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + dependencies: + crypto-random-string: 2.0.0 + dev: false + /unzipper/0.10.11: resolution: {integrity: sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==} dependencies: @@ -1864,6 +2298,11 @@ packages: which-typed-array: 1.1.7 dev: false + /uuid/8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + /validator/13.7.0: resolution: {integrity: sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==} engines: {node: '>= 0.10'} @@ -1882,6 +2321,17 @@ packages: '@zxing/text-encoding': 0.9.0 dev: false + /webidl-conversions/3.0.1: + resolution: {integrity: sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=} + dev: false + + /whatwg-url/5.0.0: + resolution: {integrity: sha1-lmRU6HZUYuN2RNNib2dCzotwll0=} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false + /which-boxed-primitive/1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} dependencies: @@ -1913,6 +2363,20 @@ packages: /wrappy/1.0.2: resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} + /write-file-atomic/3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + dependencies: + imurmurhash: 0.1.4 + is-typedarray: 1.0.0 + signal-exit: 3.0.5 + typedarray-to-buffer: 3.1.5 + dev: false + + /xdg-basedir/4.0.0: + resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} + engines: {node: '>=8'} + dev: false + /xml/1.0.1: resolution: {integrity: sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=} dev: false @@ -1947,3 +2411,8 @@ packages: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} dev: true + + /yocto-queue/0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: false diff --git a/src/config.ts b/src/config.ts index 328476b..58e44f0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,5 +1,5 @@ -import type { FastifyInstance } from 'fastify' import convict from 'convict' +import type { FastifyInstance } from 'fastify' import yaml from 'js-yaml' convict.addFormat(require('convict-format-with-validator').ipaddress) @@ -8,7 +8,7 @@ export enum StorageType { Local = 'local', Minio = 'minio', S3 = 's3', - // GCS = 'gcs', + GCS = 'gcs', // Azure = 'azure', // B2 = 'b2', } @@ -173,9 +173,25 @@ const config = convict({ sensitive: true, }, }, + + // GCS storage + gcs: { + bucket: { + doc: 'The GCS bucket to use', + format: String, + default: '', + env: 'GCS_BUCKET', + }, + keyFilename: { + doc: 'The GCS key file to use', + format: String, + default: '', + env: 'GCS_KEY_FILENAME', + }, + }, }) -for (const file of ['morphus.yaml', 'morphus.yaml']) { +for (const file of ['morphus.yaml', 'morphus.yml']) { try { config.loadFile(file) break diff --git a/src/controllers/image.ts b/src/controllers/image.ts index 5ccfcc9..f7e87a2 100644 --- a/src/controllers/image.ts +++ b/src/controllers/image.ts @@ -1,25 +1,26 @@ import { IsDefined, + IsIn, IsInt, + IsObject, IsOptional, IsPositive, IsString, IsUrl, - IsIn, - IsObject, ValidateNested, } from 'class-validator' import { RouteHandlerMethod } from 'fastify' -import sharp, { FitEnum, FormatEnum } from 'sharp' import { flatten, unflatten } from 'flat' import ms from 'ms' +import sharp, { FitEnum, FormatEnum } from 'sharp' +import { App } from '..' +import { Config, URLClean } from '../config' import { storage } from '../storage' import { transform } from '../transform' -import { sha3, sortObjectByKeys, testForPrefixOrRegexp, validateSyncOrFail } from '../utils/utils' -import { Config, URLClean } from '../config' import { supportsAvif, supportsWebP } from '../utils/caniuse' import { ForbiddenError } from '../utils/errors' +import { sha3, sortObjectByKeys, testForPrefixOrRegexp, validateSyncOrFail } from '../utils/utils' export enum ImageOperations { resize, @@ -190,7 +191,8 @@ export const image: RouteHandlerMethod = async (request, reply) => { let stream: NodeJS.ReadableStream try { stream = await storage.readStream(q.hash) - } catch (err) { + } catch { + App.log.debug(`Transforming`) stream = await transform(q) } @@ -199,7 +201,6 @@ export const image: RouteHandlerMethod = async (request, reply) => { }) return stream - // .send(stream) } catch (err) { reply.code(400).send(err) return diff --git a/src/index.ts b/src/index.ts index 86731e0..e193c90 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,9 +2,9 @@ import fastify from 'fastify' import { Config, init as initConfig } from './config' import { init as initRoutes } from './controllers' -import { init as initStorage } from './storage' -import { init as initMiddleware } from './fastify/middleware' import { init as initHooks } from './fastify/hooks' +import { init as initMiddleware } from './fastify/middleware' +import { init as initStorage } from './storage' export const App = fastify({ logger: { prettyPrint: true, level: Config.logLevel } }) diff --git a/src/storage/gcs.ts b/src/storage/gcs.ts new file mode 100644 index 0000000..94c743c --- /dev/null +++ b/src/storage/gcs.ts @@ -0,0 +1,40 @@ +import { Bucket, Storage as GCStorage } from '@google-cloud/storage' + +import { Storage } from '.' + +export type GCSConfig = { + bucket: string + keyFilename: string +} + +export class GCS implements Storage { + client: GCStorage + bucket: Bucket + + constructor(private options: GCSConfig) { + this.client = new GCStorage(options) + this.bucket = this.client.bucket(options.bucket) + } + + async init(): Promise { + await this.client.bucket(this.options.bucket).getFiles({ maxResults: 1 }) + } + + async readStream(path: string): Promise { + if (!(await this.exists(path))) throw new Error(`File ${path} does not exist`) + return this.bucket.file(path).createReadStream() + } + + async writeStream(path: string): Promise { + return this.bucket.file(path).createWriteStream() + } + + async exists(path: string): Promise { + const [exists] = await this.bucket.file(path).exists() + return exists + } + + async delete(path: string): Promise { + await this.bucket.file(path).delete() + } +} diff --git a/src/storage/index.ts b/src/storage/index.ts index 128f4e5..c53c106 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -1,14 +1,13 @@ import { FastifyInstance } from 'fastify' + import { Config, StorageType } from '../config' +import { GCS } from './gcs' import { Local } from './local' import { Minio } from './minio' export abstract class Storage { abstract init(): Promise - abstract read(path: string): Promise - abstract write(path: string, data: Buffer): Promise - abstract readStream(path: string): Promise abstract writeStream(path: string): Promise @@ -26,12 +25,6 @@ export async function init(App: FastifyInstance) { storage = new Local(Config.localAssets) break case StorageType.S3: - // storage = new S3({ - // accessKeyId: Config.s3.accessKey, - // secretAccessKey: Config.s3.secretKey, - // bucket: Config.s3.bucket, - // region: Config.s3.region, - // }) storage = new Minio({ accessKey: Config.s3.accessKey, secretKey: Config.s3.secretKey, @@ -49,6 +42,12 @@ export async function init(App: FastifyInstance) { bucket: Config.minio.bucket, }) break + case StorageType.GCS: + storage = new GCS({ + bucket: Config.gcs.bucket, + keyFilename: Config.gcs.keyFilename, + }) + break default: throw new Error(`Unknown storage type: ${Config.storage}`) } @@ -56,6 +55,7 @@ export async function init(App: FastifyInstance) { await storage.init() App.log.debug(`Storage initialized: ${Config.storage}`) } catch (e) { + App.log.error((e as Error).message) App.log.error(`Storage initialization failed: ${Config.storage}`) process.exit(1) } diff --git a/src/storage/local.ts b/src/storage/local.ts index 1658a6b..758bf80 100644 --- a/src/storage/local.ts +++ b/src/storage/local.ts @@ -1,5 +1,5 @@ -import { resolve, join } from 'path' import fs from 'fs' +import { join, resolve } from 'path' import { promisify } from 'util' import { Storage } from './' @@ -13,30 +13,6 @@ export class Local implements Storage { await promisify(fs.mkdir)(this.root, { recursive: true }) } - read(path: string): Promise { - const file = join(this.root, path) - return new Promise((resolve, reject) => { - fs.readFile(file, (err, data) => { - if (err) { - return reject(err) - } - resolve(data) - }) - }) - } - - write(path: string, data: Buffer): Promise { - const file = join(this.root, path) - return new Promise((resolve, reject) => { - fs.writeFile(file, data, (err) => { - if (err) { - return reject(err) - } - resolve() - }) - }) - } - exists(path: string): Promise { const file = join(this.root, path) return new Promise((resolve, reject) => { diff --git a/src/storage/minio.ts b/src/storage/minio.ts index df9330b..4eda94d 100644 --- a/src/storage/minio.ts +++ b/src/storage/minio.ts @@ -2,7 +2,6 @@ import { Client } from 'minio' import { PassThrough } from 'stream' import { Storage } from '.' -import { StreamUtils } from '../utils/utils' export type MinioConfig = { accessKey: string @@ -30,15 +29,6 @@ export class Minio implements Storage { await this.client.bucketExists(this.options.bucket) } - async read(path: string): Promise { - const stream = await this.client.getObject(this.options.bucket, path) - return StreamUtils.toBuffer(stream) - } - async write(path: string, data: Buffer): Promise { - const stream = await StreamUtils.fromBuffer(data) - await this.client.putObject(this.options.bucket, path, stream) - } - async readStream(path: string): Promise { const stream = await this.client.getObject(this.options.bucket, path) return stream diff --git a/src/transform/index.ts b/src/transform/index.ts index a8b08f8..4c0d3dc 100644 --- a/src/transform/index.ts +++ b/src/transform/index.ts @@ -1,8 +1,8 @@ import { get } from 'https' import sharp from 'sharp' import { PassThrough } from 'stream' -import { ComplexParameter, TransformQueryBase } from '../controllers/image' +import { ComplexParameter, TransformQueryBase } from '../controllers/image' import { storage } from '../storage' import { sha3, splitter } from '../utils/utils' diff --git a/src/utils/caniuse.ts b/src/utils/caniuse.ts index e7b2ffc..4b3986b 100644 --- a/src/utils/caniuse.ts +++ b/src/utils/caniuse.ts @@ -1,6 +1,6 @@ -import DeviceDetector from 'device-detector-js' import Avif from 'caniuse-db/features-json/avif.json' import WebP from 'caniuse-db/features-json/webp.json' +import DeviceDetector from 'device-detector-js' const detector = new DeviceDetector() diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 253e3d0..f891c3a 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,5 +1,5 @@ +import { validateSync, ValidationError as VE, ValidatorOptions } from 'class-validator' import { createHash } from 'crypto' -import { validateSync, ValidatorOptions, ValidationError as VE } from 'class-validator' import { PassThrough, Readable } from 'stream' export class ValidationError extends Error {