@blocklet/uploader-server 0.1.71 → 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,
@@ -85,16 +79,20 @@ export const initStaticResourceMiddleware = ({
85
79
  }
86
80
  mappingResource();
87
81
  return (req, res, next) => {
88
- const urlPath = new URL(`http://localhost${req.url}`).pathname;
82
+ const fileName = basename(req.url);
89
83
  const matchCanUseResourceItem = canUseResources.find((item) => {
90
- if (!existsSync(join(item.dir, urlPath))) {
84
+ const normalizedPath = join(item.dir, fileName);
85
+ if (!normalizedPath.startsWith(item.dir)) {
86
+ return false;
87
+ }
88
+ if (!existsSync(normalizedPath)) {
91
89
  return false;
92
90
  }
93
91
  const { whitelist, blacklist } = item;
94
- if (whitelist?.length && !whitelist.some((ext) => urlPath.endsWith(ext))) {
92
+ if (whitelist?.length && !whitelist.some((ext) => fileName.endsWith(ext))) {
95
93
  return false;
96
94
  }
97
- if (blacklist?.length && blacklist.some((ext) => urlPath.endsWith(ext))) {
95
+ if (blacklist?.length && blacklist.some((ext) => fileName.endsWith(ext))) {
98
96
  return false;
99
97
  }
100
98
  return true;
@@ -113,34 +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
- const filename = basename(req.url);
121
- req.url = joinUrl("/uploads/", filename);
122
118
  setPDFDownloadHeader(req, res);
123
- proxy.once("proxyRes", (proxyRes, req2, res2) => {
124
- if (proxyRes.statusCode >= 200 && proxyRes.statusCode < 400) {
125
- res2.writeHead(proxyRes.statusCode, proxyRes.headers);
126
- 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
+ });
127
133
  } else {
128
134
  next();
129
135
  }
130
- });
131
- proxy.once("error", (err, req2, res2) => {
132
- next(err);
133
- });
134
- proxy.web(
135
- req,
136
- res,
137
- {
138
- target: mediaKitInfo.webEndpoint,
139
- changeOrigin: true,
140
- selfHandleResponse: true,
141
- ...options
142
- },
143
- next
144
- );
136
+ } catch (error) {
137
+ next();
138
+ }
145
139
  };
146
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,
@@ -102,19 +98,23 @@ const initStaticResourceMiddleware = ({
102
98
  }
103
99
  mappingResource();
104
100
  return (req, res, next) => {
105
- const urlPath = new URL(`http://localhost${req.url}`).pathname;
101
+ const fileName = basename(req.url);
106
102
  const matchCanUseResourceItem = canUseResources.find(item => {
107
- if (!existsSync(join(item.dir, urlPath))) {
103
+ const normalizedPath = join(item.dir, fileName);
104
+ if (!normalizedPath.startsWith(item.dir)) {
105
+ return false;
106
+ }
107
+ if (!existsSync(normalizedPath)) {
108
108
  return false;
109
109
  }
110
110
  const {
111
111
  whitelist,
112
112
  blacklist
113
113
  } = item;
114
- if (whitelist?.length && !whitelist.some(ext => urlPath.endsWith(ext))) {
114
+ if (whitelist?.length && !whitelist.some(ext => fileName.endsWith(ext))) {
115
115
  return false;
116
116
  }
117
- if (blacklist?.length && blacklist.some(ext => urlPath.endsWith(ext))) {
117
+ if (blacklist?.length && blacklist.some(ext => fileName.endsWith(ext))) {
118
118
  return false;
119
119
  }
120
120
  return true;
@@ -138,30 +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
- const filename = basename(req.url);
146
- req.url = joinUrl("/uploads/", filename);
147
145
  setPDFDownloadHeader(req, res);
148
- proxy.once("proxyRes", (proxyRes, req2, res2) => {
149
- if (proxyRes.statusCode >= 200 && proxyRes.statusCode < 400) {
150
- res2.writeHead(proxyRes.statusCode, proxyRes.headers);
151
- 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
+ });
152
164
  } else {
153
165
  next();
154
166
  }
155
- });
156
- proxy.once("error", (err, req2, res2) => {
157
- next(err);
158
- });
159
- proxy.web(req, res, {
160
- target: mediaKitInfo.webEndpoint,
161
- changeOrigin: true,
162
- selfHandleResponse: true,
163
- ...options
164
- }, next);
167
+ } catch (error) {
168
+ next();
169
+ }
165
170
  };
166
171
  };
167
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.71",
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
  }