@blocklet/uploader-server 0.1.72 → 0.1.74

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.
@@ -0,0 +1 @@
1
+ export declare const ImageBinDid = "z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9";
@@ -0,0 +1 @@
1
+ export const ImageBinDid = "z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9";
@@ -7,5 +7,5 @@ type initStaticResourceMiddlewareOptions = {
7
7
  };
8
8
  export declare const initStaticResourceMiddleware: ({ options, resourceTypes: _resourceTypes, express, skipRunningCheck: _skipRunningCheck, }?: initStaticResourceMiddlewareOptions) => (req: any, res: any, next: Function) => void;
9
9
  export declare const getCanUseResources: () => any;
10
- export declare const initProxyToMediaKitUploadsMiddleware: ({ options, express }?: any) => (req: any, res: any, next: Function) => any;
10
+ export declare const initProxyToMediaKitUploadsMiddleware: ({ options, express }?: any) => (req: any, res: any, next: Function) => Promise<any>;
11
11
  export {};
@@ -2,15 +2,13 @@ const { existsSync } = require("fs");
2
2
  const { join, basename } = require("path");
3
3
  const config = require("@blocklet/sdk/lib/config");
4
4
  const { getResources } = require("@blocklet/sdk/lib/component");
5
- const httpProxy = require("http-proxy");
6
5
  const joinUrl = require("url-join");
6
+ const component = require("@blocklet/sdk/lib/component");
7
7
  const { setPDFDownloadHeader } = require("../utils");
8
- const proxy = httpProxy.createProxyServer();
8
+ const { ImageBinDid } = require("../constants");
9
9
  const logger = console;
10
10
  const ImgResourceType = "imgpack";
11
- const ImageBinDid = "z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9";
12
11
  let skipRunningCheck = false;
13
- let mediaKitInfo = null;
14
12
  let resourceTypes = [
15
13
  {
16
14
  type: ImgResourceType,
@@ -21,10 +19,6 @@ let resourceTypes = [
21
19
  ];
22
20
  let canUseResources = [];
23
21
  export const mappingResource = async () => {
24
- mediaKitInfo = config.components.find((item) => item.did === ImageBinDid);
25
- if (mediaKitInfo) {
26
- mediaKitInfo.uploadsDir = config.env.dataDir.replace(/\/[^/]*$/, "/image-bin/uploads");
27
- }
28
22
  try {
29
23
  const resources = getResources({
30
24
  types: resourceTypes,
@@ -87,6 +81,13 @@ export const initStaticResourceMiddleware = ({
87
81
  return (req, res, next) => {
88
82
  const fileName = basename(req.url);
89
83
  const matchCanUseResourceItem = canUseResources.find((item) => {
84
+ const normalizedPath = join(item.dir, fileName);
85
+ if (!normalizedPath.startsWith(item.dir)) {
86
+ return false;
87
+ }
88
+ if (!existsSync(normalizedPath)) {
89
+ return false;
90
+ }
90
91
  const { whitelist, blacklist } = item;
91
92
  if (whitelist?.length && !whitelist.some((ext) => fileName.endsWith(ext))) {
92
93
  return false;
@@ -94,9 +95,6 @@ export const initStaticResourceMiddleware = ({
94
95
  if (blacklist?.length && blacklist.some((ext) => fileName.endsWith(ext))) {
95
96
  return false;
96
97
  }
97
- if (!existsSync(join(item.dir, fileName))) {
98
- return false;
99
- }
100
98
  return true;
101
99
  });
102
100
  if (matchCanUseResourceItem) {
@@ -113,32 +111,30 @@ export const initStaticResourceMiddleware = ({
113
111
  };
114
112
  export const getCanUseResources = () => canUseResources;
115
113
  export const initProxyToMediaKitUploadsMiddleware = ({ options, express } = {}) => {
116
- return (req, res, next) => {
117
- if (!mediaKitInfo?.webEndpoint) {
114
+ return async (req, res, next) => {
115
+ if (!component.getComponentWebEndpoint(ImageBinDid)) {
118
116
  return next();
119
117
  }
120
118
  setPDFDownloadHeader(req, res);
121
- proxy.once("proxyRes", (proxyRes, req2, res2) => {
122
- if (proxyRes.statusCode >= 200 && proxyRes.statusCode < 400) {
123
- res2.writeHead(proxyRes.statusCode, proxyRes.headers);
124
- proxyRes.pipe(res2);
119
+ try {
120
+ const { data, status, headers } = await component.call({
121
+ name: ImageBinDid,
122
+ path: joinUrl("/uploads", basename(req.url)),
123
+ responseType: "stream",
124
+ method: "GET"
125
+ });
126
+ if (data && status >= 200 && status < 400) {
127
+ res.set("Content-Type", headers["content-type"]);
128
+ data.on("error", (err) => {
129
+ next();
130
+ }).pipe(res).on("error", (err) => {
131
+ next();
132
+ });
125
133
  } else {
126
134
  next();
127
135
  }
128
- });
129
- proxy.once("error", (err, req2, res2) => {
130
- next(err);
131
- });
132
- proxy.web(
133
- req,
134
- res,
135
- {
136
- target: joinUrl(mediaKitInfo.webEndpoint, "/uploads", basename(req.url)),
137
- changeOrigin: true,
138
- selfHandleResponse: true,
139
- ...options
140
- },
141
- next
142
- );
136
+ } catch (error) {
137
+ next();
138
+ }
143
139
  };
144
140
  };
package/es/utils.d.ts CHANGED
@@ -1,3 +1,14 @@
1
+ import type { Method } from 'axios';
2
+ type CallComponentOptions<D = any, P = any> = {
3
+ name?: string;
4
+ method?: Method;
5
+ path: string;
6
+ data?: D;
7
+ params?: P;
8
+ headers?: {
9
+ [key: string]: any;
10
+ };
11
+ };
1
12
  export declare function getTrustedDomainsCache({ forceUpdate, ttl, }?: {
2
13
  forceUpdate?: boolean;
3
14
  ttl?: number;
@@ -5,3 +16,12 @@ export declare function getTrustedDomainsCache({ forceUpdate, ttl, }?: {
5
16
  export declare function checkTrustedReferer(req: any, res: any, next?: Function): Promise<any>;
6
17
  export declare function proxyImageDownload(req: any, res: any, next?: Function): Promise<void>;
7
18
  export declare function setPDFDownloadHeader(req: any, res: any): void;
19
+ export declare const getFileHash: (filePath: string, maxBytes?: number) => Promise<any>;
20
+ export declare function uploadToMediaKit({ filePath, fileName, base64, extraComponentCallOptions, }: {
21
+ filePath?: string;
22
+ fileName?: string;
23
+ base64?: string;
24
+ extraComponentCallOptions?: CallComponentOptions;
25
+ }): Promise<import("axios").AxiosResponse<import("http").IncomingMessage, any> | undefined>;
26
+ export declare function getMediaKitFileStream(filePath: string): Promise<import("axios").AxiosResponse<import("http").IncomingMessage, any>>;
27
+ export {};
package/es/utils.js CHANGED
@@ -2,6 +2,13 @@ import axios from "axios";
2
2
  import path from "path";
3
3
  import joinUrl from "url-join";
4
4
  import { isbot } from "isbot";
5
+ import component from "@blocklet/sdk/lib/component";
6
+ import { ImageBinDid } from "./constants.js";
7
+ import { createReadStream } from "fs";
8
+ import crypto from "crypto";
9
+ import { getSignData } from "@blocklet/sdk/lib/util/verify-sign";
10
+ import FormData from "form-data";
11
+ import omit from "lodash/omit";
5
12
  const DEFAULT_TTL = 5 * 60 * 1e3;
6
13
  const appUrl = process.env.BLOCKLET_APP_URL || "";
7
14
  const trustedDomainsCache = {
@@ -78,3 +85,84 @@ export function setPDFDownloadHeader(req, res) {
78
85
  res.setHeader("Content-Disposition", `attachment; ${filename ? `filename="${filename}"` : ""}`);
79
86
  }
80
87
  }
88
+ export const getFileHash = async (filePath, maxBytes = 5 * 1024 * 1024) => {
89
+ const hash = crypto.createHash("md5");
90
+ const readStream = createReadStream(filePath, {
91
+ start: 0,
92
+ end: maxBytes - 1,
93
+ highWaterMark: 1024 * 1024
94
+ // 1MB chunks
95
+ });
96
+ for await (const chunk of readStream) {
97
+ hash.update(chunk.toString());
98
+ }
99
+ return hash.digest("hex");
100
+ };
101
+ export async function uploadToMediaKit({
102
+ filePath,
103
+ fileName,
104
+ base64,
105
+ extraComponentCallOptions
106
+ }) {
107
+ if (!filePath && !base64) {
108
+ throw new Error("filePath or base64 is required");
109
+ }
110
+ if (base64) {
111
+ if (!fileName) {
112
+ throw new Error("fileName is required when base64 is provided");
113
+ }
114
+ const res = await component.call({
115
+ name: ImageBinDid,
116
+ path: "/api/sdk/uploads",
117
+ data: {
118
+ base64,
119
+ filename: fileName
120
+ },
121
+ ...omit(extraComponentCallOptions, ["name", "path", "data"])
122
+ });
123
+ return res;
124
+ }
125
+ if (filePath) {
126
+ const fileStream = createReadStream(filePath);
127
+ const filename = fileName || path.basename(filePath);
128
+ const form = new FormData();
129
+ const fileHash = await getFileHash(filePath);
130
+ form.append("file", fileStream);
131
+ form.append("filename", filename);
132
+ form.append("hash", fileHash);
133
+ const res = await component.call(
134
+ {
135
+ name: ImageBinDid,
136
+ path: "/api/sdk/uploads",
137
+ data: form,
138
+ headers: {
139
+ "x-component-upload-sig": getSignData({
140
+ data: {
141
+ filename,
142
+ hash: fileHash
143
+ },
144
+ method: "POST",
145
+ url: "/api/sdk/uploads",
146
+ params: extraComponentCallOptions?.params || {}
147
+ }).sig,
148
+ ...extraComponentCallOptions?.headers
149
+ },
150
+ ...omit(extraComponentCallOptions, ["name", "path", "data", "headers"])
151
+ },
152
+ {
153
+ retries: 0
154
+ }
155
+ );
156
+ return res;
157
+ }
158
+ }
159
+ export async function getMediaKitFileStream(filePath) {
160
+ const fileName = path.basename(filePath);
161
+ const res = await component.call({
162
+ name: ImageBinDid,
163
+ path: joinUrl("/uploads", fileName),
164
+ responseType: "stream",
165
+ method: "GET"
166
+ });
167
+ return res;
168
+ }
@@ -0,0 +1 @@
1
+ export declare const ImageBinDid = "z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9";
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ImageBinDid = void 0;
7
+ const ImageBinDid = exports.ImageBinDid = "z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9";
@@ -7,5 +7,5 @@ type initStaticResourceMiddlewareOptions = {
7
7
  };
8
8
  export declare const initStaticResourceMiddleware: ({ options, resourceTypes: _resourceTypes, express, skipRunningCheck: _skipRunningCheck, }?: initStaticResourceMiddlewareOptions) => (req: any, res: any, next: Function) => void;
9
9
  export declare const getCanUseResources: () => any;
10
- export declare const initProxyToMediaKitUploadsMiddleware: ({ options, express }?: any) => (req: any, res: any, next: Function) => any;
10
+ export declare const initProxyToMediaKitUploadsMiddleware: ({ options, express }?: any) => (req: any, res: any, next: Function) => Promise<any>;
11
11
  export {};
@@ -15,17 +15,17 @@ const config = require("@blocklet/sdk/lib/config");
15
15
  const {
16
16
  getResources
17
17
  } = require("@blocklet/sdk/lib/component");
18
- const httpProxy = require("http-proxy");
19
18
  const joinUrl = require("url-join");
19
+ const component = require("@blocklet/sdk/lib/component");
20
20
  const {
21
21
  setPDFDownloadHeader
22
22
  } = require("../utils");
23
- const proxy = httpProxy.createProxyServer();
23
+ const {
24
+ ImageBinDid
25
+ } = require("../constants");
24
26
  const logger = console;
25
27
  const ImgResourceType = "imgpack";
26
- const ImageBinDid = "z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9";
27
28
  let skipRunningCheck = false;
28
- let mediaKitInfo = null;
29
29
  let resourceTypes = [{
30
30
  type: ImgResourceType,
31
31
  did: ImageBinDid,
@@ -34,10 +34,6 @@ let resourceTypes = [{
34
34
  }];
35
35
  let canUseResources = [];
36
36
  const mappingResource = async () => {
37
- mediaKitInfo = config.components.find(item => item.did === ImageBinDid);
38
- if (mediaKitInfo) {
39
- mediaKitInfo.uploadsDir = config.env.dataDir.replace(/\/[^/]*$/, "/image-bin/uploads");
40
- }
41
37
  try {
42
38
  const resources = getResources({
43
39
  types: resourceTypes,
@@ -104,6 +100,13 @@ const initStaticResourceMiddleware = ({
104
100
  return (req, res, next) => {
105
101
  const fileName = basename(req.url);
106
102
  const matchCanUseResourceItem = canUseResources.find(item => {
103
+ const normalizedPath = join(item.dir, fileName);
104
+ if (!normalizedPath.startsWith(item.dir)) {
105
+ return false;
106
+ }
107
+ if (!existsSync(normalizedPath)) {
108
+ return false;
109
+ }
107
110
  const {
108
111
  whitelist,
109
112
  blacklist
@@ -114,9 +117,6 @@ const initStaticResourceMiddleware = ({
114
117
  if (blacklist?.length && blacklist.some(ext => fileName.endsWith(ext))) {
115
118
  return false;
116
119
  }
117
- if (!existsSync(join(item.dir, fileName))) {
118
- return false;
119
- }
120
120
  return true;
121
121
  });
122
122
  if (matchCanUseResourceItem) {
@@ -138,28 +138,35 @@ const initProxyToMediaKitUploadsMiddleware = ({
138
138
  options,
139
139
  express
140
140
  } = {}) => {
141
- return (req, res, next) => {
142
- if (!mediaKitInfo?.webEndpoint) {
141
+ return async (req, res, next) => {
142
+ if (!component.getComponentWebEndpoint(ImageBinDid)) {
143
143
  return next();
144
144
  }
145
145
  setPDFDownloadHeader(req, res);
146
- proxy.once("proxyRes", (proxyRes, req2, res2) => {
147
- if (proxyRes.statusCode >= 200 && proxyRes.statusCode < 400) {
148
- res2.writeHead(proxyRes.statusCode, proxyRes.headers);
149
- proxyRes.pipe(res2);
146
+ try {
147
+ const {
148
+ data,
149
+ status,
150
+ headers
151
+ } = await component.call({
152
+ name: ImageBinDid,
153
+ path: joinUrl("/uploads", basename(req.url)),
154
+ responseType: "stream",
155
+ method: "GET"
156
+ });
157
+ if (data && status >= 200 && status < 400) {
158
+ res.set("Content-Type", headers["content-type"]);
159
+ data.on("error", err => {
160
+ next();
161
+ }).pipe(res).on("error", err => {
162
+ next();
163
+ });
150
164
  } else {
151
165
  next();
152
166
  }
153
- });
154
- proxy.once("error", (err, req2, res2) => {
155
- next(err);
156
- });
157
- proxy.web(req, res, {
158
- target: joinUrl(mediaKitInfo.webEndpoint, "/uploads", basename(req.url)),
159
- changeOrigin: true,
160
- selfHandleResponse: true,
161
- ...options
162
- }, next);
167
+ } catch (error) {
168
+ next();
169
+ }
163
170
  };
164
171
  };
165
172
  exports.initProxyToMediaKitUploadsMiddleware = initProxyToMediaKitUploadsMiddleware;
package/lib/utils.d.ts CHANGED
@@ -1,3 +1,14 @@
1
+ import type { Method } from 'axios';
2
+ type CallComponentOptions<D = any, P = any> = {
3
+ name?: string;
4
+ method?: Method;
5
+ path: string;
6
+ data?: D;
7
+ params?: P;
8
+ headers?: {
9
+ [key: string]: any;
10
+ };
11
+ };
1
12
  export declare function getTrustedDomainsCache({ forceUpdate, ttl, }?: {
2
13
  forceUpdate?: boolean;
3
14
  ttl?: number;
@@ -5,3 +16,12 @@ export declare function getTrustedDomainsCache({ forceUpdate, ttl, }?: {
5
16
  export declare function checkTrustedReferer(req: any, res: any, next?: Function): Promise<any>;
6
17
  export declare function proxyImageDownload(req: any, res: any, next?: Function): Promise<void>;
7
18
  export declare function setPDFDownloadHeader(req: any, res: any): void;
19
+ export declare const getFileHash: (filePath: string, maxBytes?: number) => Promise<any>;
20
+ export declare function uploadToMediaKit({ filePath, fileName, base64, extraComponentCallOptions, }: {
21
+ filePath?: string;
22
+ fileName?: string;
23
+ base64?: string;
24
+ extraComponentCallOptions?: CallComponentOptions;
25
+ }): Promise<import("axios").AxiosResponse<import("http").IncomingMessage, any> | undefined>;
26
+ export declare function getMediaKitFileStream(filePath: string): Promise<import("axios").AxiosResponse<import("http").IncomingMessage, any>>;
27
+ export {};
package/lib/utils.js CHANGED
@@ -4,13 +4,23 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.checkTrustedReferer = checkTrustedReferer;
7
+ exports.getFileHash = void 0;
8
+ exports.getMediaKitFileStream = getMediaKitFileStream;
7
9
  exports.getTrustedDomainsCache = getTrustedDomainsCache;
8
10
  exports.proxyImageDownload = proxyImageDownload;
9
11
  exports.setPDFDownloadHeader = setPDFDownloadHeader;
12
+ exports.uploadToMediaKit = uploadToMediaKit;
10
13
  var _axios = _interopRequireDefault(require("axios"));
11
14
  var _path = _interopRequireDefault(require("path"));
12
15
  var _urlJoin = _interopRequireDefault(require("url-join"));
13
16
  var _isbot = require("isbot");
17
+ var _component = _interopRequireDefault(require("@blocklet/sdk/lib/component"));
18
+ var _constants = require("./constants");
19
+ var _fs = require("fs");
20
+ var _crypto = _interopRequireDefault(require("crypto"));
21
+ var _verifySign = require("@blocklet/sdk/lib/util/verify-sign");
22
+ var _formData = _interopRequireDefault(require("form-data"));
23
+ var _omit = _interopRequireDefault(require("lodash/omit"));
14
24
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
25
  const DEFAULT_TTL = 5 * 60 * 1e3;
16
26
  const appUrl = process.env.BLOCKLET_APP_URL || "";
@@ -93,4 +103,83 @@ function setPDFDownloadHeader(req, res) {
93
103
  const filename = req.query?.filename ?? req?.path;
94
104
  res.setHeader("Content-Disposition", `attachment; ${filename ? `filename="${filename}"` : ""}`);
95
105
  }
106
+ }
107
+ const getFileHash = async (filePath, maxBytes = 5 * 1024 * 1024) => {
108
+ const hash = _crypto.default.createHash("md5");
109
+ const readStream = (0, _fs.createReadStream)(filePath, {
110
+ start: 0,
111
+ end: maxBytes - 1,
112
+ highWaterMark: 1024 * 1024
113
+ // 1MB chunks
114
+ });
115
+ for await (const chunk of readStream) {
116
+ hash.update(chunk.toString());
117
+ }
118
+ return hash.digest("hex");
119
+ };
120
+ exports.getFileHash = getFileHash;
121
+ async function uploadToMediaKit({
122
+ filePath,
123
+ fileName,
124
+ base64,
125
+ extraComponentCallOptions
126
+ }) {
127
+ if (!filePath && !base64) {
128
+ throw new Error("filePath or base64 is required");
129
+ }
130
+ if (base64) {
131
+ if (!fileName) {
132
+ throw new Error("fileName is required when base64 is provided");
133
+ }
134
+ const res = await _component.default.call({
135
+ name: _constants.ImageBinDid,
136
+ path: "/api/sdk/uploads",
137
+ data: {
138
+ base64,
139
+ filename: fileName
140
+ },
141
+ ...(0, _omit.default)(extraComponentCallOptions, ["name", "path", "data"])
142
+ });
143
+ return res;
144
+ }
145
+ if (filePath) {
146
+ const fileStream = (0, _fs.createReadStream)(filePath);
147
+ const filename = fileName || _path.default.basename(filePath);
148
+ const form = new _formData.default();
149
+ const fileHash = await getFileHash(filePath);
150
+ form.append("file", fileStream);
151
+ form.append("filename", filename);
152
+ form.append("hash", fileHash);
153
+ const res = await _component.default.call({
154
+ name: _constants.ImageBinDid,
155
+ path: "/api/sdk/uploads",
156
+ data: form,
157
+ headers: {
158
+ "x-component-upload-sig": (0, _verifySign.getSignData)({
159
+ data: {
160
+ filename,
161
+ hash: fileHash
162
+ },
163
+ method: "POST",
164
+ url: "/api/sdk/uploads",
165
+ params: extraComponentCallOptions?.params || {}
166
+ }).sig,
167
+ ...extraComponentCallOptions?.headers
168
+ },
169
+ ...(0, _omit.default)(extraComponentCallOptions, ["name", "path", "data", "headers"])
170
+ }, {
171
+ retries: 0
172
+ });
173
+ return res;
174
+ }
175
+ }
176
+ async function getMediaKitFileStream(filePath) {
177
+ const fileName = _path.default.basename(filePath);
178
+ const res = await _component.default.call({
179
+ name: _constants.ImageBinDid,
180
+ path: (0, _urlJoin.default)("/uploads", fileName),
181
+ responseType: "stream",
182
+ method: "GET"
183
+ });
184
+ return res;
96
185
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/uploader-server",
3
- "version": "0.1.72",
3
+ "version": "0.1.74",
4
4
  "description": "blocklet upload server",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -47,7 +47,6 @@
47
47
  "body-parser": "^1.20.3",
48
48
  "crypto": "^1.0.1",
49
49
  "express-session": "1.17.3",
50
- "http-proxy": "^1.18.1",
51
50
  "isbot": "^5.1.17",
52
51
  "mime-types": "^2.1.35",
53
52
  "p-queue": "6.6.2",
@@ -57,7 +56,6 @@
57
56
  "devDependencies": {
58
57
  "@arcblock/eslint-config-ts": "^0.2.4",
59
58
  "@types/express": "^4.17.21",
60
- "@types/http-proxy": "^1.17.15",
61
59
  "@types/mime-types": "^2.1.4",
62
60
  "@types/node": "^20.17.9",
63
61
  "@types/url-join": "^4.0.3",
@@ -74,6 +72,7 @@
74
72
  "build:watch": "npx nodemon --ext 'ts,tsx,json,js,jsx' --exec 'pnpm run build' --ignore 'lib/*' --ignore 'es/*' ",
75
73
  "dev": "pnpm run build:watch",
76
74
  "prepublish": "pnpm run build",
77
- "prebuild:dep": "pnpm run build"
75
+ "prebuild:dep": "pnpm run build",
76
+ "dev:yalc": "npx nodemon --ext 'ts,tsx,json,js,jsx' --exec 'pnpm run build && yalc push' --ignore 'lib/*' --ignore 'es/*'"
78
77
  }
79
78
  }