@blocklet/uploader-server 0.1.53 → 0.1.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/es/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './middlewares';
2
+ export * from './utils';
package/es/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export * from "./middlewares.js";
2
+ export * from "./utils.js";
@@ -3,4 +3,3 @@ export declare function initCompanion({ path, express, providerOptions, ...restP
3
3
  express: Function;
4
4
  providerOptions?: Object;
5
5
  }): any;
6
- export declare function proxyImageDownload(req: any, res: any, next?: Function): Promise<void>;
@@ -1,8 +1,8 @@
1
1
  const companion = require("@uppy/companion");
2
2
  const bodyParser = require("body-parser");
3
3
  const session = require("express-session");
4
- const axios = require("axios");
5
4
  const crypto = require("crypto");
5
+ const { checkTrustedReferer, proxyImageDownload } = require("../utils");
6
6
  const secret = crypto.randomBytes(32).toString("hex");
7
7
  export function initCompanion({
8
8
  path,
@@ -13,7 +13,7 @@ export function initCompanion({
13
13
  const app = express();
14
14
  app.use(bodyParser.json());
15
15
  app.use(session({ secret }));
16
- app.use("/proxy", proxyImageDownload);
16
+ app.use("/proxy", checkTrustedReferer, proxyImageDownload);
17
17
  let dynamicProviderOptions = providerOptions;
18
18
  const companionOptions = {
19
19
  secret,
@@ -56,37 +56,3 @@ export function initCompanion({
56
56
  };
57
57
  return newCompanion;
58
58
  }
59
- export async function proxyImageDownload(req, res, next) {
60
- let { url, responseType = "stream" } = {
61
- ...req.query,
62
- ...req.body
63
- };
64
- if (url) {
65
- url = encodeURI(url);
66
- try {
67
- const { headers, data, status } = await axios.get(url, {
68
- responseType
69
- });
70
- if (data && status >= 200 && status < 302) {
71
- res.setHeader("Content-Type", headers["content-type"]);
72
- try {
73
- } catch (error) {
74
- }
75
- if (responseType === "stream") {
76
- data.pipe(res);
77
- } else if (responseType === "arraybuffer") {
78
- res.end(data?.toString?.("binary"), "binary");
79
- } else {
80
- res.send(data);
81
- }
82
- } else {
83
- throw new Error("download image error");
84
- }
85
- } catch (err) {
86
- console.error("Proxy url failed: ", err);
87
- res.status(500).send("Proxy url failed");
88
- }
89
- } else {
90
- res.status(500).send('Parameter "url" is required');
91
- }
92
- }
@@ -7,6 +7,7 @@ const crypto = require("crypto");
7
7
  const mime = require("mime-types");
8
8
  const joinUrlLib = require("url-join");
9
9
  const { default: queue } = require("p-queue");
10
+ const { setPDFDownloadHeader } = require("../utils");
10
11
  const validFilePathInDirPath = (dirPath, filePath) => {
11
12
  const fileName = path.basename(filePath);
12
13
  if (!filePath.startsWith(dirPath) || path.join(dirPath, fileName) !== filePath) {
@@ -225,6 +226,7 @@ export function setHeaders(req, res, next) {
225
226
  if (contentType) {
226
227
  res.setHeader("Content-Type", contentType);
227
228
  }
229
+ setPDFDownloadHeader(req, res);
228
230
  }
229
231
  next?.();
230
232
  }
@@ -4,6 +4,7 @@ const config = require("@blocklet/sdk/lib/config");
4
4
  const { getResources } = require("@blocklet/sdk/lib/component");
5
5
  const httpProxy = require("http-proxy");
6
6
  const joinUrl = require("url-join");
7
+ const { setPDFDownloadHeader } = require("../utils");
7
8
  const proxy = httpProxy.createProxyServer();
8
9
  const logger = console;
9
10
  const ImgResourceType = "imgpack";
@@ -101,6 +102,7 @@ export const initProxyToMediaKitUploadsMiddleware = ({ options, express } = {})
101
102
  }
102
103
  const filename = basename(req.url);
103
104
  req.url = joinUrl("/uploads/", filename);
105
+ setPDFDownloadHeader(req, res);
104
106
  proxy.web(
105
107
  req,
106
108
  res,
package/es/utils.d.ts CHANGED
@@ -0,0 +1,7 @@
1
+ export declare function getTrustedDomainsCache({ forceUpdate, ttl, }?: {
2
+ forceUpdate?: boolean;
3
+ ttl?: number;
4
+ }): Promise<string[]>;
5
+ export declare function checkTrustedReferer(req: any, res: any, next?: Function): Promise<any>;
6
+ export declare function proxyImageDownload(req: any, res: any, next?: Function): Promise<void>;
7
+ export declare function setPDFDownloadHeader(req: any, res: any): void;
package/es/utils.js CHANGED
@@ -0,0 +1,79 @@
1
+ import axios from "axios";
2
+ import config from "@blocklet/sdk/lib/config";
3
+ import path from "path";
4
+ import joinUrl from "url-join";
5
+ import { isbot } from "isbot";
6
+ import AuthService from "@blocklet/sdk/service/auth";
7
+ const authClient = new AuthService();
8
+ const DEFAULT_TTL = 5 * 60 * 1e3;
9
+ const trustedDomainsCache = {
10
+ timestamp: 0,
11
+ domains: []
12
+ };
13
+ export async function getTrustedDomainsCache({
14
+ forceUpdate = false,
15
+ ttl = DEFAULT_TTL
16
+ } = {}) {
17
+ const now = Date.now();
18
+ if (!forceUpdate && trustedDomainsCache.domains.length > 0) {
19
+ if (now - trustedDomainsCache.timestamp < ttl) {
20
+ return trustedDomainsCache.domains;
21
+ }
22
+ }
23
+ trustedDomainsCache.domains = await axios.get(joinUrl(config.env.appUrl, "/.well-known/service/api/federated/getTrustedDomains")).then((res) => res.data);
24
+ trustedDomainsCache.timestamp = now;
25
+ return trustedDomainsCache.domains;
26
+ }
27
+ export async function checkTrustedReferer(req, res, next) {
28
+ if (isbot(req.get("user-agent"))) {
29
+ return next?.();
30
+ }
31
+ const referer = req.headers.referer;
32
+ if (!referer) {
33
+ return res.status(403).send("Access denied");
34
+ }
35
+ const allowedDomains = await getTrustedDomainsCache();
36
+ const refererHost = new URL(referer).hostname;
37
+ if (allowedDomains?.length && !allowedDomains.some((domain) => refererHost.includes(domain))) {
38
+ return res.status(403).send("Access denied");
39
+ }
40
+ next?.();
41
+ }
42
+ export async function proxyImageDownload(req, res, next) {
43
+ let { url } = {
44
+ ...req.query,
45
+ ...req.body
46
+ };
47
+ if (url) {
48
+ url = encodeURI(url);
49
+ try {
50
+ const { headers, data, status } = await axios({
51
+ method: "get",
52
+ url,
53
+ responseType: "stream",
54
+ timeout: 30 * 1e3,
55
+ headers: {
56
+ // Add common headers to mimic browser request
57
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
58
+ }
59
+ });
60
+ if (data && status >= 200 && status < 302) {
61
+ res.setHeader("Content-Type", headers["content-type"]);
62
+ data.pipe(res);
63
+ } else {
64
+ throw new Error("download image error");
65
+ }
66
+ } catch (err) {
67
+ console.error("Proxy url failed: ", err);
68
+ res.status(500).send("Proxy url failed");
69
+ }
70
+ } else {
71
+ res.status(500).send('Parameter "url" is required');
72
+ }
73
+ }
74
+ export function setPDFDownloadHeader(req, res) {
75
+ if (path.extname(req.path) === ".pdf") {
76
+ const filename = req.query?.filename ?? req?.path;
77
+ res.setHeader("Content-Disposition", `attachment; ${filename ? `filename="${filename}"` : ""}`);
78
+ }
79
+ }
package/lib/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './middlewares';
2
+ export * from './utils';
package/lib/index.js CHANGED
@@ -13,4 +13,15 @@ Object.keys(_middlewares).forEach(function (key) {
13
13
  return _middlewares[key];
14
14
  }
15
15
  });
16
+ });
17
+ var _utils = require("./utils");
18
+ Object.keys(_utils).forEach(function (key) {
19
+ if (key === "default" || key === "__esModule") return;
20
+ if (key in exports && exports[key] === _utils[key]) return;
21
+ Object.defineProperty(exports, key, {
22
+ enumerable: true,
23
+ get: function () {
24
+ return _utils[key];
25
+ }
26
+ });
16
27
  });
@@ -3,4 +3,3 @@ export declare function initCompanion({ path, express, providerOptions, ...restP
3
3
  express: Function;
4
4
  providerOptions?: Object;
5
5
  }): any;
6
- export declare function proxyImageDownload(req: any, res: any, next?: Function): Promise<void>;
@@ -4,12 +4,14 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.initCompanion = initCompanion;
7
- exports.proxyImageDownload = proxyImageDownload;
8
7
  const companion = require("@uppy/companion");
9
8
  const bodyParser = require("body-parser");
10
9
  const session = require("express-session");
11
- const axios = require("axios");
12
10
  const crypto = require("crypto");
11
+ const {
12
+ checkTrustedReferer,
13
+ proxyImageDownload
14
+ } = require("../utils");
13
15
  const secret = crypto.randomBytes(32).toString("hex");
14
16
  function initCompanion({
15
17
  path,
@@ -22,7 +24,7 @@ function initCompanion({
22
24
  app.use(session({
23
25
  secret
24
26
  }));
25
- app.use("/proxy", proxyImageDownload);
27
+ app.use("/proxy", checkTrustedReferer, proxyImageDownload);
26
28
  let dynamicProviderOptions = providerOptions;
27
29
  const companionOptions = {
28
30
  secret,
@@ -62,43 +64,4 @@ function initCompanion({
62
64
  dynamicProviderOptions = options;
63
65
  };
64
66
  return newCompanion;
65
- }
66
- async function proxyImageDownload(req, res, next) {
67
- let {
68
- url,
69
- responseType = "stream"
70
- } = {
71
- ...req.query,
72
- ...req.body
73
- };
74
- if (url) {
75
- url = encodeURI(url);
76
- try {
77
- const {
78
- headers,
79
- data,
80
- status
81
- } = await axios.get(url, {
82
- responseType
83
- });
84
- if (data && status >= 200 && status < 302) {
85
- res.setHeader("Content-Type", headers["content-type"]);
86
- try {} catch (error) {}
87
- if (responseType === "stream") {
88
- data.pipe(res);
89
- } else if (responseType === "arraybuffer") {
90
- res.end(data?.toString?.("binary"), "binary");
91
- } else {
92
- res.send(data);
93
- }
94
- } else {
95
- throw new Error("download image error");
96
- }
97
- } catch (err) {
98
- console.error("Proxy url failed: ", err);
99
- res.status(500).send("Proxy url failed");
100
- }
101
- } else {
102
- res.status(500).send('Parameter "url" is required');
103
- }
104
67
  }
@@ -27,6 +27,9 @@ const joinUrlLib = require("url-join");
27
27
  const {
28
28
  default: queue
29
29
  } = require("p-queue");
30
+ const {
31
+ setPDFDownloadHeader
32
+ } = require("../utils");
30
33
  const validFilePathInDirPath = (dirPath, filePath) => {
31
34
  const fileName = path.basename(filePath);
32
35
  if (!filePath.startsWith(dirPath) || path.join(dirPath, fileName) !== filePath) {
@@ -267,6 +270,7 @@ function setHeaders(req, res, next) {
267
270
  if (contentType) {
268
271
  res.setHeader("Content-Type", contentType);
269
272
  }
273
+ setPDFDownloadHeader(req, res);
270
274
  }
271
275
  next?.();
272
276
  }
@@ -17,6 +17,9 @@ const {
17
17
  } = require("@blocklet/sdk/lib/component");
18
18
  const httpProxy = require("http-proxy");
19
19
  const joinUrl = require("url-join");
20
+ const {
21
+ setPDFDownloadHeader
22
+ } = require("../utils");
20
23
  const proxy = httpProxy.createProxyServer();
21
24
  const logger = console;
22
25
  const ImgResourceType = "imgpack";
@@ -127,6 +130,7 @@ const initProxyToMediaKitUploadsMiddleware = ({
127
130
  }
128
131
  const filename = basename(req.url);
129
132
  req.url = joinUrl("/uploads/", filename);
133
+ setPDFDownloadHeader(req, res);
130
134
  proxy.web(req, res, {
131
135
  target: mediaKitInfo.webEndpoint,
132
136
  changeOrigin: true,
package/lib/utils.d.ts CHANGED
@@ -0,0 +1,7 @@
1
+ export declare function getTrustedDomainsCache({ forceUpdate, ttl, }?: {
2
+ forceUpdate?: boolean;
3
+ ttl?: number;
4
+ }): Promise<string[]>;
5
+ export declare function checkTrustedReferer(req: any, res: any, next?: Function): Promise<any>;
6
+ export declare function proxyImageDownload(req: any, res: any, next?: Function): Promise<void>;
7
+ export declare function setPDFDownloadHeader(req: any, res: any): void;
package/lib/utils.js CHANGED
@@ -1 +1,95 @@
1
- "use strict";
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.checkTrustedReferer = checkTrustedReferer;
7
+ exports.getTrustedDomainsCache = getTrustedDomainsCache;
8
+ exports.proxyImageDownload = proxyImageDownload;
9
+ exports.setPDFDownloadHeader = setPDFDownloadHeader;
10
+ var _axios = _interopRequireDefault(require("axios"));
11
+ var _config = _interopRequireDefault(require("@blocklet/sdk/lib/config"));
12
+ var _path = _interopRequireDefault(require("path"));
13
+ var _urlJoin = _interopRequireDefault(require("url-join"));
14
+ var _isbot = require("isbot");
15
+ var _auth = _interopRequireDefault(require("@blocklet/sdk/service/auth"));
16
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
+ const authClient = new _auth.default();
18
+ const DEFAULT_TTL = 5 * 60 * 1e3;
19
+ const trustedDomainsCache = {
20
+ timestamp: 0,
21
+ domains: []
22
+ };
23
+ async function getTrustedDomainsCache({
24
+ forceUpdate = false,
25
+ ttl = DEFAULT_TTL
26
+ } = {}) {
27
+ const now = Date.now();
28
+ if (!forceUpdate && trustedDomainsCache.domains.length > 0) {
29
+ if (now - trustedDomainsCache.timestamp < ttl) {
30
+ return trustedDomainsCache.domains;
31
+ }
32
+ }
33
+ trustedDomainsCache.domains = await _axios.default.get((0, _urlJoin.default)(_config.default.env.appUrl, "/.well-known/service/api/federated/getTrustedDomains")).then(res => res.data);
34
+ trustedDomainsCache.timestamp = now;
35
+ return trustedDomainsCache.domains;
36
+ }
37
+ async function checkTrustedReferer(req, res, next) {
38
+ if ((0, _isbot.isbot)(req.get("user-agent"))) {
39
+ return next?.();
40
+ }
41
+ const referer = req.headers.referer;
42
+ if (!referer) {
43
+ return res.status(403).send("Access denied");
44
+ }
45
+ const allowedDomains = await getTrustedDomainsCache();
46
+ const refererHost = new URL(referer).hostname;
47
+ if (allowedDomains?.length && !allowedDomains.some(domain => refererHost.includes(domain))) {
48
+ return res.status(403).send("Access denied");
49
+ }
50
+ next?.();
51
+ }
52
+ async function proxyImageDownload(req, res, next) {
53
+ let {
54
+ url
55
+ } = {
56
+ ...req.query,
57
+ ...req.body
58
+ };
59
+ if (url) {
60
+ url = encodeURI(url);
61
+ try {
62
+ const {
63
+ headers,
64
+ data,
65
+ status
66
+ } = await (0, _axios.default)({
67
+ method: "get",
68
+ url,
69
+ responseType: "stream",
70
+ timeout: 30 * 1e3,
71
+ headers: {
72
+ // Add common headers to mimic browser request
73
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
74
+ }
75
+ });
76
+ if (data && status >= 200 && status < 302) {
77
+ res.setHeader("Content-Type", headers["content-type"]);
78
+ data.pipe(res);
79
+ } else {
80
+ throw new Error("download image error");
81
+ }
82
+ } catch (err) {
83
+ console.error("Proxy url failed: ", err);
84
+ res.status(500).send("Proxy url failed");
85
+ }
86
+ } else {
87
+ res.status(500).send('Parameter "url" is required');
88
+ }
89
+ }
90
+ function setPDFDownloadHeader(req, res) {
91
+ if (_path.default.extname(req.path) === ".pdf") {
92
+ const filename = req.query?.filename ?? req?.path;
93
+ res.setHeader("Content-Disposition", `attachment; ${filename ? `filename="${filename}"` : ""}`);
94
+ }
95
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/uploader-server",
3
- "version": "0.1.53",
3
+ "version": "0.1.55",
4
4
  "description": "blocklet upload server",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -17,8 +17,11 @@
17
17
  "import": "./es/middlewares.js",
18
18
  "types": "./lib/middlewares.d.ts"
19
19
  },
20
- "./lib/": "./lib/",
21
- "./es/": "./es/"
20
+ "./utils": {
21
+ "require": "./lib/utils.js",
22
+ "import": "./es/utils.js",
23
+ "types": "./lib/utils.d.ts"
24
+ }
22
25
  },
23
26
  "files": [
24
27
  "lib",
@@ -38,10 +41,10 @@
38
41
  "@uppy/companion": "4.15.1"
39
42
  },
40
43
  "dependencies": {
41
- "@abtnode/cron": "^1.16.33",
42
- "@blocklet/constant": "^1.16.33",
43
- "@blocklet/logger": "^1.16.33",
44
- "@blocklet/sdk": "^1.16.33",
44
+ "@abtnode/cron": "1.16.34-beta-20241126-104450-d60e25c2",
45
+ "@blocklet/constant": "1.16.34-beta-20241126-104450-d60e25c2",
46
+ "@blocklet/logger": "1.16.34-beta-20241126-104450-d60e25c2",
47
+ "@blocklet/sdk": "1.16.34-beta-20241126-104450-d60e25c2",
45
48
  "@tus/file-store": "1.0.0",
46
49
  "@tus/server": "1.0.0",
47
50
  "@uppy/companion": "4.15.1",
@@ -49,6 +52,7 @@
49
52
  "crypto": "^1.0.1",
50
53
  "express-session": "1.17.3",
51
54
  "http-proxy": "^1.18.1",
55
+ "isbot": "^5.1.17",
52
56
  "mime-types": "^2.1.35",
53
57
  "p-queue": "^6.6.2",
54
58
  "ufo": "^1.5.3",
@@ -57,6 +61,7 @@
57
61
  "devDependencies": {
58
62
  "@arcblock/eslint-config-ts": "^0.2.4",
59
63
  "@types/express": "^4.17.21",
64
+ "@types/http-proxy": "^1.17.15",
60
65
  "@types/mime-types": "^2.1.4",
61
66
  "@types/node": "^20.12.7",
62
67
  "@types/url-join": "^4.0.3",