@blocklet/uploader-server 0.1.90 → 0.1.91

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.
@@ -6,6 +6,5 @@ type initStaticResourceMiddlewareOptions = {
6
6
  skipRunningCheck?: boolean;
7
7
  };
8
8
  export declare const initStaticResourceMiddleware: ({ options, resourceTypes: _resourceTypes, express, skipRunningCheck: _skipRunningCheck, }?: initStaticResourceMiddlewareOptions) => (req: any, res: any, next: Function) => void;
9
- export declare const getCanUseResources: () => any;
10
9
  export declare const initProxyToMediaKitUploadsMiddleware: ({ options, express }?: any) => (req: any, res: any, next: Function) => Promise<any>;
11
10
  export {};
@@ -1,11 +1,13 @@
1
- import { existsSync } from "fs";
1
+ import { existsSync, readdirSync, statSync, createReadStream } from "fs";
2
2
  import { join, basename } from "path";
3
3
  import config from "@blocklet/sdk/lib/config";
4
4
  import { getResources } from "@blocklet/sdk/lib/component";
5
5
  import joinUrl from "url-join";
6
6
  import component from "@blocklet/sdk/lib/component";
7
+ import mime from "mime-types";
7
8
  import { setPDFDownloadHeader, logger } from "../utils.js";
8
9
  import { ImageBinDid } from "../constants.js";
10
+ import ms from "ms";
9
11
  const ImgResourceType = "imgpack";
10
12
  let skipRunningCheck = false;
11
13
  let resourceTypes = [
@@ -16,13 +18,14 @@ let resourceTypes = [
16
18
  // can be string or string[]
17
19
  }
18
20
  ];
19
- let canUseResources = [];
21
+ let resourcesMap = /* @__PURE__ */ new Map();
20
22
  export const mappingResource = async () => {
21
23
  try {
22
24
  const resources = getResources({
23
25
  types: resourceTypes,
24
26
  skipRunningCheck
25
27
  });
28
+ let canUseResources = [];
26
29
  canUseResources = resources.map((resource) => {
27
30
  const originDir = resource.path;
28
31
  const resourceType = resourceTypes.find(({ type }) => originDir.endsWith(type));
@@ -38,11 +41,46 @@ export const mappingResource = async () => {
38
41
  blacklist: resourceType.blacklist
39
42
  }));
40
43
  }).filter(Boolean).flat();
41
- logger.info(
42
- "Mapping can use resources count: ",
43
- canUseResources.length
44
- // canUseResources
45
- );
44
+ resourcesMap.clear();
45
+ for (const resource of canUseResources) {
46
+ const { dir, whitelist, blacklist, originDir, blockletInfo } = resource;
47
+ if (existsSync(dir)) {
48
+ try {
49
+ const files = readdirSync(dir);
50
+ for (const file of files) {
51
+ const filePath = join(dir, file);
52
+ let stat;
53
+ try {
54
+ stat = statSync(filePath);
55
+ if (stat.isDirectory()) continue;
56
+ } catch (e) {
57
+ continue;
58
+ }
59
+ if (whitelist?.length && !whitelist.some((ext) => file.endsWith(ext))) {
60
+ continue;
61
+ }
62
+ if (blacklist?.length && blacklist.some((ext) => file.endsWith(ext))) {
63
+ continue;
64
+ }
65
+ const contentType = mime.lookup(filePath) || "application/octet-stream";
66
+ resourcesMap.set(file, {
67
+ filePath,
68
+ dir,
69
+ originDir,
70
+ blockletInfo,
71
+ whitelist,
72
+ blacklist,
73
+ mtime: stat.mtime,
74
+ size: stat.size,
75
+ contentType
76
+ });
77
+ }
78
+ } catch (err) {
79
+ logger.error(`Error scanning directory ${dir}:`, err);
80
+ }
81
+ }
82
+ }
83
+ logger.info("Mapping resources: files count:", resourcesMap.size, "directories count:", canUseResources.length);
46
84
  return canUseResources;
47
85
  } catch (error) {
48
86
  logger.error(error);
@@ -56,12 +94,26 @@ events.on(Events.componentStarted, () => mappingResource());
56
94
  events.on(Events.componentStopped, () => mappingResource());
57
95
  events.on(Events.componentUpdated, () => mappingResource());
58
96
  export const initStaticResourceMiddleware = ({
59
- options,
97
+ options = {},
60
98
  resourceTypes: _resourceTypes = resourceTypes,
61
99
  express,
62
100
  skipRunningCheck: _skipRunningCheck
63
101
  } = {}) => {
64
102
  skipRunningCheck = !!_skipRunningCheck;
103
+ let maxAgeInSeconds = 31536e3;
104
+ const maxAge = options.maxAge || "365d";
105
+ if (typeof maxAge === "string") {
106
+ try {
107
+ const milliseconds = ms(maxAge);
108
+ maxAgeInSeconds = typeof milliseconds === "number" ? milliseconds / 1e3 : 31536e3;
109
+ } catch (e) {
110
+ logger.warn(`Invalid maxAge format: ${maxAge}, using default 1 year (31536000 seconds)`);
111
+ }
112
+ } else {
113
+ maxAgeInSeconds = maxAge;
114
+ }
115
+ const cacheControl = `public, max-age=${maxAgeInSeconds}`;
116
+ const cacheControlImmutable = `${cacheControl}, immutable`;
65
117
  if (_resourceTypes?.length > 0) {
66
118
  resourceTypes = _resourceTypes.map((item) => {
67
119
  if (typeof item === "string") {
@@ -80,39 +132,40 @@ export const initStaticResourceMiddleware = ({
80
132
  return (req, res, next) => {
81
133
  const fileName = basename(req.path || req.url?.split("?")[0]);
82
134
  try {
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
- }
91
- const { whitelist, blacklist } = item;
92
- if (whitelist?.length && !whitelist.some((ext) => fileName?.endsWith(ext))) {
93
- return false;
135
+ const resource = resourcesMap.get(fileName);
136
+ if (resource) {
137
+ res.setHeader("Content-Type", resource.contentType);
138
+ res.setHeader("Content-Length", resource.size);
139
+ res.setHeader("Last-Modified", resource.mtime.toUTCString());
140
+ res.setHeader("Cache-Control", options.immutable === false ? cacheControl : cacheControlImmutable);
141
+ if (options.setHeaders && typeof options.setHeaders === "function") {
142
+ const statObj = { mtime: resource.mtime, size: resource.size };
143
+ options.setHeaders(res, resource.filePath, statObj);
94
144
  }
95
- if (blacklist?.length && blacklist.some((ext) => fileName?.endsWith(ext))) {
96
- return false;
145
+ const ifModifiedSince = req.headers["if-modified-since"];
146
+ if (ifModifiedSince) {
147
+ const ifModifiedSinceDate = new Date(ifModifiedSince);
148
+ if (resource.mtime <= ifModifiedSinceDate) {
149
+ res.statusCode = 304;
150
+ res.end();
151
+ return;
152
+ }
97
153
  }
98
- return true;
99
- });
100
- if (matchCanUseResourceItem) {
101
- express.static(matchCanUseResourceItem.dir, {
102
- maxAge: "365d",
103
- immutable: true,
104
- index: false,
105
- ...options
106
- })(req, res, next);
154
+ const fileStream = createReadStream(resource.filePath);
155
+ fileStream.on("error", (error) => {
156
+ logger.error(`Error streaming file ${resource.filePath}:`, error);
157
+ next(error);
158
+ });
159
+ fileStream.pipe(res);
107
160
  } else {
108
161
  next();
109
162
  }
110
163
  } catch (error) {
164
+ logger.error("Error serving static file:", error);
111
165
  next();
112
166
  }
113
167
  };
114
168
  };
115
- export const getCanUseResources = () => canUseResources;
116
169
  export const initProxyToMediaKitUploadsMiddleware = ({ options, express } = {}) => {
117
170
  return async (req, res, next) => {
118
171
  if (!component.getComponentWebEndpoint(ImageBinDid)) {
@@ -6,6 +6,5 @@ type initStaticResourceMiddlewareOptions = {
6
6
  skipRunningCheck?: boolean;
7
7
  };
8
8
  export declare const initStaticResourceMiddleware: ({ options, resourceTypes: _resourceTypes, express, skipRunningCheck: _skipRunningCheck, }?: initStaticResourceMiddlewareOptions) => (req: any, res: any, next: Function) => void;
9
- export declare const getCanUseResources: () => any;
10
9
  export declare const initProxyToMediaKitUploadsMiddleware: ({ options, express }?: any) => (req: any, res: any, next: Function) => Promise<any>;
11
10
  export {};
@@ -3,14 +3,16 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.mappingResource = exports.initStaticResourceMiddleware = exports.initProxyToMediaKitUploadsMiddleware = exports.getCanUseResources = void 0;
6
+ exports.mappingResource = exports.initStaticResourceMiddleware = exports.initProxyToMediaKitUploadsMiddleware = void 0;
7
7
  var _fs = require("fs");
8
8
  var _path = require("path");
9
9
  var _config = _interopRequireDefault(require("@blocklet/sdk/lib/config"));
10
10
  var _component = _interopRequireWildcard(require("@blocklet/sdk/lib/component"));
11
11
  var _urlJoin = _interopRequireDefault(require("url-join"));
12
+ var _mimeTypes = _interopRequireDefault(require("mime-types"));
12
13
  var _utils = require("../utils");
13
14
  var _constants = require("../constants");
15
+ var _ms = _interopRequireDefault(require("ms"));
14
16
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
15
17
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
16
18
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -22,13 +24,14 @@ let resourceTypes = [{
22
24
  folder: ""
23
25
  // can be string or string[]
24
26
  }];
25
- let canUseResources = [];
27
+ let resourcesMap = /* @__PURE__ */new Map();
26
28
  const mappingResource = async () => {
27
29
  try {
28
30
  const resources = (0, _component.getResources)({
29
31
  types: resourceTypes,
30
32
  skipRunningCheck
31
33
  });
34
+ let canUseResources = [];
32
35
  canUseResources = resources.map(resource => {
33
36
  const originDir = resource.path;
34
37
  const resourceType = resourceTypes.find(({
@@ -46,9 +49,52 @@ const mappingResource = async () => {
46
49
  blacklist: resourceType.blacklist
47
50
  }));
48
51
  }).filter(Boolean).flat();
49
- _utils.logger.info("Mapping can use resources count: ", canUseResources.length
50
- // canUseResources
51
- );
52
+ resourcesMap.clear();
53
+ for (const resource of canUseResources) {
54
+ const {
55
+ dir,
56
+ whitelist,
57
+ blacklist,
58
+ originDir,
59
+ blockletInfo
60
+ } = resource;
61
+ if ((0, _fs.existsSync)(dir)) {
62
+ try {
63
+ const files = (0, _fs.readdirSync)(dir);
64
+ for (const file of files) {
65
+ const filePath = (0, _path.join)(dir, file);
66
+ let stat;
67
+ try {
68
+ stat = (0, _fs.statSync)(filePath);
69
+ if (stat.isDirectory()) continue;
70
+ } catch (e) {
71
+ continue;
72
+ }
73
+ if (whitelist?.length && !whitelist.some(ext => file.endsWith(ext))) {
74
+ continue;
75
+ }
76
+ if (blacklist?.length && blacklist.some(ext => file.endsWith(ext))) {
77
+ continue;
78
+ }
79
+ const contentType = _mimeTypes.default.lookup(filePath) || "application/octet-stream";
80
+ resourcesMap.set(file, {
81
+ filePath,
82
+ dir,
83
+ originDir,
84
+ blockletInfo,
85
+ whitelist,
86
+ blacklist,
87
+ mtime: stat.mtime,
88
+ size: stat.size,
89
+ contentType
90
+ });
91
+ }
92
+ } catch (err) {
93
+ _utils.logger.error(`Error scanning directory ${dir}:`, err);
94
+ }
95
+ }
96
+ }
97
+ _utils.logger.info("Mapping resources: files count:", resourcesMap.size, "directories count:", canUseResources.length);
52
98
  return canUseResources;
53
99
  } catch (error) {
54
100
  _utils.logger.error(error);
@@ -66,12 +112,26 @@ events.on(Events.componentStarted, () => mappingResource());
66
112
  events.on(Events.componentStopped, () => mappingResource());
67
113
  events.on(Events.componentUpdated, () => mappingResource());
68
114
  const initStaticResourceMiddleware = ({
69
- options,
115
+ options = {},
70
116
  resourceTypes: _resourceTypes = resourceTypes,
71
117
  express,
72
118
  skipRunningCheck: _skipRunningCheck
73
119
  } = {}) => {
74
120
  skipRunningCheck = !!_skipRunningCheck;
121
+ let maxAgeInSeconds = 31536e3;
122
+ const maxAge = options.maxAge || "365d";
123
+ if (typeof maxAge === "string") {
124
+ try {
125
+ const milliseconds = (0, _ms.default)(maxAge);
126
+ maxAgeInSeconds = typeof milliseconds === "number" ? milliseconds / 1e3 : 31536e3;
127
+ } catch (e) {
128
+ _utils.logger.warn(`Invalid maxAge format: ${maxAge}, using default 1 year (31536000 seconds)`);
129
+ }
130
+ } else {
131
+ maxAgeInSeconds = maxAge;
132
+ }
133
+ const cacheControl = `public, max-age=${maxAgeInSeconds}`;
134
+ const cacheControlImmutable = `${cacheControl}, immutable`;
75
135
  if (_resourceTypes?.length > 0) {
76
136
  resourceTypes = _resourceTypes.map(item => {
77
137
  if (typeof item === "string") {
@@ -90,44 +150,44 @@ const initStaticResourceMiddleware = ({
90
150
  return (req, res, next) => {
91
151
  const fileName = (0, _path.basename)(req.path || req.url?.split("?")[0]);
92
152
  try {
93
- const matchCanUseResourceItem = canUseResources.find(item => {
94
- const normalizedPath = (0, _path.join)(item.dir, fileName);
95
- if (!normalizedPath?.startsWith(item.dir)) {
96
- return false;
97
- }
98
- if (!(0, _fs.existsSync)(normalizedPath)) {
99
- return false;
153
+ const resource = resourcesMap.get(fileName);
154
+ if (resource) {
155
+ res.setHeader("Content-Type", resource.contentType);
156
+ res.setHeader("Content-Length", resource.size);
157
+ res.setHeader("Last-Modified", resource.mtime.toUTCString());
158
+ res.setHeader("Cache-Control", options.immutable === false ? cacheControl : cacheControlImmutable);
159
+ if (options.setHeaders && typeof options.setHeaders === "function") {
160
+ const statObj = {
161
+ mtime: resource.mtime,
162
+ size: resource.size
163
+ };
164
+ options.setHeaders(res, resource.filePath, statObj);
100
165
  }
101
- const {
102
- whitelist,
103
- blacklist
104
- } = item;
105
- if (whitelist?.length && !whitelist.some(ext => fileName?.endsWith(ext))) {
106
- return false;
166
+ const ifModifiedSince = req.headers["if-modified-since"];
167
+ if (ifModifiedSince) {
168
+ const ifModifiedSinceDate = new Date(ifModifiedSince);
169
+ if (resource.mtime <= ifModifiedSinceDate) {
170
+ res.statusCode = 304;
171
+ res.end();
172
+ return;
173
+ }
107
174
  }
108
- if (blacklist?.length && blacklist.some(ext => fileName?.endsWith(ext))) {
109
- return false;
110
- }
111
- return true;
112
- });
113
- if (matchCanUseResourceItem) {
114
- express.static(matchCanUseResourceItem.dir, {
115
- maxAge: "365d",
116
- immutable: true,
117
- index: false,
118
- ...options
119
- })(req, res, next);
175
+ const fileStream = (0, _fs.createReadStream)(resource.filePath);
176
+ fileStream.on("error", error => {
177
+ _utils.logger.error(`Error streaming file ${resource.filePath}:`, error);
178
+ next(error);
179
+ });
180
+ fileStream.pipe(res);
120
181
  } else {
121
182
  next();
122
183
  }
123
184
  } catch (error) {
185
+ _utils.logger.error("Error serving static file:", error);
124
186
  next();
125
187
  }
126
188
  };
127
189
  };
128
190
  exports.initStaticResourceMiddleware = initStaticResourceMiddleware;
129
- const getCanUseResources = () => canUseResources;
130
- exports.getCanUseResources = getCanUseResources;
131
191
  const initProxyToMediaKitUploadsMiddleware = ({
132
192
  options,
133
193
  express
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/uploader-server",
3
- "version": "0.1.90",
3
+ "version": "0.1.91",
4
4
  "description": "blocklet upload server",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -61,6 +61,7 @@
61
61
  "express-session": "1.17.3",
62
62
  "isbot": "^5.1.17",
63
63
  "mime-types": "^2.1.35",
64
+ "ms": "^2.1.3",
64
65
  "p-queue": "6.6.2",
65
66
  "ufo": "^1.5.4",
66
67
  "url-join": "^4.0.1"