@blocklet/uploader-server 0.1.72 → 0.1.73

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
@@ -5,3 +5,10 @@ export declare function getTrustedDomainsCache({ forceUpdate, ttl, }?: {
5
5
  export declare function checkTrustedReferer(req: any, res: any, next?: Function): Promise<any>;
6
6
  export declare function proxyImageDownload(req: any, res: any, next?: Function): Promise<void>;
7
7
  export declare function setPDFDownloadHeader(req: any, res: any): void;
8
+ export declare const getFileHash: (filePath: string, maxBytes?: number) => Promise<any>;
9
+ export declare function uploadToMediaKit({ filePath, fileName, base64, }: {
10
+ filePath?: string;
11
+ fileName?: string;
12
+ base64?: string;
13
+ }): Promise<import("axios").AxiosResponse<any, any> | undefined>;
14
+ export declare function getMediaKitFileStream(filePath: string): Promise<import("axios").AxiosResponse<import("http").IncomingMessage, any>>;
package/es/utils.js CHANGED
@@ -2,6 +2,12 @@ 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";
5
11
  const DEFAULT_TTL = 5 * 60 * 1e3;
6
12
  const appUrl = process.env.BLOCKLET_APP_URL || "";
7
13
  const trustedDomainsCache = {
@@ -78,3 +84,80 @@ export function setPDFDownloadHeader(req, res) {
78
84
  res.setHeader("Content-Disposition", `attachment; ${filename ? `filename="${filename}"` : ""}`);
79
85
  }
80
86
  }
87
+ export const getFileHash = async (filePath, maxBytes = 5 * 1024 * 1024) => {
88
+ const hash = crypto.createHash("md5");
89
+ const readStream = createReadStream(filePath, {
90
+ start: 0,
91
+ end: maxBytes - 1,
92
+ highWaterMark: 1024 * 1024
93
+ // 1MB chunks
94
+ });
95
+ for await (const chunk of readStream) {
96
+ hash.update(chunk.toString());
97
+ }
98
+ return hash.digest("hex");
99
+ };
100
+ export async function uploadToMediaKit({
101
+ filePath,
102
+ fileName,
103
+ base64
104
+ }) {
105
+ if (!filePath && !base64) {
106
+ throw new Error("filePath or base64 is required");
107
+ }
108
+ if (base64) {
109
+ if (!fileName) {
110
+ throw new Error("fileName is required when base64 is provided");
111
+ }
112
+ const res = await component.call({
113
+ name: ImageBinDid,
114
+ path: "/api/sdk/uploads",
115
+ data: {
116
+ base64,
117
+ filename: fileName
118
+ }
119
+ });
120
+ return res;
121
+ }
122
+ if (filePath) {
123
+ const fileStream = createReadStream(filePath);
124
+ const filename = fileName || path.basename(filePath);
125
+ const form = new FormData();
126
+ const fileHash = await getFileHash(filePath);
127
+ form.append("file", fileStream);
128
+ form.append("filename", filename);
129
+ form.append("hash", fileHash);
130
+ const res = await component.call(
131
+ {
132
+ name: ImageBinDid,
133
+ path: "/api/sdk/uploads",
134
+ data: form,
135
+ headers: {
136
+ "x-component-upload-sig": getSignData({
137
+ data: {
138
+ filename,
139
+ hash: fileHash
140
+ },
141
+ method: "POST",
142
+ url: "/api/sdk/uploads",
143
+ params: {}
144
+ }).sig
145
+ }
146
+ },
147
+ {
148
+ retries: 0
149
+ }
150
+ );
151
+ return res;
152
+ }
153
+ }
154
+ export async function getMediaKitFileStream(filePath) {
155
+ const fileName = path.basename(filePath);
156
+ const res = await component.call({
157
+ name: ImageBinDid,
158
+ path: joinUrl("/uploads", fileName),
159
+ responseType: "stream",
160
+ method: "GET"
161
+ });
162
+ return res;
163
+ }
@@ -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
@@ -5,3 +5,10 @@ export declare function getTrustedDomainsCache({ forceUpdate, ttl, }?: {
5
5
  export declare function checkTrustedReferer(req: any, res: any, next?: Function): Promise<any>;
6
6
  export declare function proxyImageDownload(req: any, res: any, next?: Function): Promise<void>;
7
7
  export declare function setPDFDownloadHeader(req: any, res: any): void;
8
+ export declare const getFileHash: (filePath: string, maxBytes?: number) => Promise<any>;
9
+ export declare function uploadToMediaKit({ filePath, fileName, base64, }: {
10
+ filePath?: string;
11
+ fileName?: string;
12
+ base64?: string;
13
+ }): Promise<import("axios").AxiosResponse<any, any> | undefined>;
14
+ export declare function getMediaKitFileStream(filePath: string): Promise<import("axios").AxiosResponse<import("http").IncomingMessage, any>>;
package/lib/utils.js CHANGED
@@ -4,13 +4,22 @@ 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"));
14
23
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
24
  const DEFAULT_TTL = 5 * 60 * 1e3;
16
25
  const appUrl = process.env.BLOCKLET_APP_URL || "";
@@ -93,4 +102,79 @@ function setPDFDownloadHeader(req, res) {
93
102
  const filename = req.query?.filename ?? req?.path;
94
103
  res.setHeader("Content-Disposition", `attachment; ${filename ? `filename="${filename}"` : ""}`);
95
104
  }
105
+ }
106
+ const getFileHash = async (filePath, maxBytes = 5 * 1024 * 1024) => {
107
+ const hash = _crypto.default.createHash("md5");
108
+ const readStream = (0, _fs.createReadStream)(filePath, {
109
+ start: 0,
110
+ end: maxBytes - 1,
111
+ highWaterMark: 1024 * 1024
112
+ // 1MB chunks
113
+ });
114
+ for await (const chunk of readStream) {
115
+ hash.update(chunk.toString());
116
+ }
117
+ return hash.digest("hex");
118
+ };
119
+ exports.getFileHash = getFileHash;
120
+ async function uploadToMediaKit({
121
+ filePath,
122
+ fileName,
123
+ base64
124
+ }) {
125
+ if (!filePath && !base64) {
126
+ throw new Error("filePath or base64 is required");
127
+ }
128
+ if (base64) {
129
+ if (!fileName) {
130
+ throw new Error("fileName is required when base64 is provided");
131
+ }
132
+ const res = await _component.default.call({
133
+ name: _constants.ImageBinDid,
134
+ path: "/api/sdk/uploads",
135
+ data: {
136
+ base64,
137
+ filename: fileName
138
+ }
139
+ });
140
+ return res;
141
+ }
142
+ if (filePath) {
143
+ const fileStream = (0, _fs.createReadStream)(filePath);
144
+ const filename = fileName || _path.default.basename(filePath);
145
+ const form = new _formData.default();
146
+ const fileHash = await getFileHash(filePath);
147
+ form.append("file", fileStream);
148
+ form.append("filename", filename);
149
+ form.append("hash", fileHash);
150
+ const res = await _component.default.call({
151
+ name: _constants.ImageBinDid,
152
+ path: "/api/sdk/uploads",
153
+ data: form,
154
+ headers: {
155
+ "x-component-upload-sig": (0, _verifySign.getSignData)({
156
+ data: {
157
+ filename,
158
+ hash: fileHash
159
+ },
160
+ method: "POST",
161
+ url: "/api/sdk/uploads",
162
+ params: {}
163
+ }).sig
164
+ }
165
+ }, {
166
+ retries: 0
167
+ });
168
+ return res;
169
+ }
170
+ }
171
+ async function getMediaKitFileStream(filePath) {
172
+ const fileName = _path.default.basename(filePath);
173
+ const res = await _component.default.call({
174
+ name: _constants.ImageBinDid,
175
+ path: (0, _urlJoin.default)("/uploads", fileName),
176
+ responseType: "stream",
177
+ method: "GET"
178
+ });
179
+ return res;
96
180
  }
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.73",
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
  }