mm_statics 1.9.1 → 1.9.3
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/README.md +65 -34
- package/index.js +32 -29
- package/lib/manager.js +41 -47
- package/lib/static.js +25 -28
- package/lib/static_handle.js +59 -12
- package/package.json +67 -67
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
- 🔄 自动ES6到AMD格式转换
|
|
10
10
|
- ⚡ Vue单文件组件直接转换(无需AMD包装)
|
|
11
11
|
- 🛡️ 安全文件访问
|
|
12
|
-
-
|
|
12
|
+
- 🧩 服务式调用 `(params, ctx, options)`,不是中间件、不接收 `next`
|
|
13
13
|
- 🔧 Vue 2/Vue 3兼容性支持
|
|
14
14
|
|
|
15
15
|
## 安装
|
|
@@ -23,29 +23,38 @@ npm install mm_statics
|
|
|
23
23
|
### 基本使用
|
|
24
24
|
|
|
25
25
|
```javascript
|
|
26
|
-
const
|
|
26
|
+
const path = require('path');
|
|
27
|
+
const { WebServer } = require('mm_web_server');
|
|
27
28
|
const { statics } = require('mm_statics');
|
|
28
29
|
|
|
29
|
-
const
|
|
30
|
+
const web = new WebServer({ name: 'web', port: 3000 });
|
|
31
|
+
const events = web.getEventManager();
|
|
30
32
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
+
// 创建静态资源处理器:签名统一为 (params, ctx, options)
|
|
34
|
+
const serve = statics({ name: 'static', root: path.join(__dirname, 'static'), path: '/' });
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
// 在 before 阶段挂载:命中文件返回物理路径即短路后续事件,未命中返回空串继续执行
|
|
37
|
+
events.add({ name: 'static', target: '*', stage: 'before', end: true }).main =
|
|
38
|
+
async (params, ctx, options) => serve(params, ctx, options);
|
|
39
|
+
|
|
40
|
+
await web.init();
|
|
41
|
+
await web.start();
|
|
37
42
|
```
|
|
38
43
|
|
|
39
|
-
###
|
|
44
|
+
### 调用约定
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
| 项 | 说明 |
|
|
47
|
+
|---|---|
|
|
48
|
+
| 签名 | `(params, ctx, options)`,与事件管道、适配器保持一致 |
|
|
49
|
+
| 返回值 | 命中返回物理文件路径(字符串),未命中返回空串 |
|
|
50
|
+
| 无 `next` | 未命中也只是返回空串,是否继续由调用方决定,因此它不是中间件 |
|
|
51
|
+
| 响应写出 | 命中时已写出响应(`ctx.res` 直写或 `ctx.set` + `ctx.body`),并标记 `ctx.handled = true` |
|
|
44
52
|
|
|
45
|
-
|
|
53
|
+
### 自定义配置
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
```javascript
|
|
56
|
+
const serve = statics({
|
|
57
|
+
name: 'public', // 驱动名称(必填)
|
|
49
58
|
root: './public', // 静态文件根目录
|
|
50
59
|
path: '/src', // 需要转换的文件路径前缀
|
|
51
60
|
files: ['.js', '.vue'], // 需要转换的文件类型
|
|
@@ -55,15 +64,17 @@ app.use(statics({
|
|
|
55
64
|
immutable: true, // 是否启用不可变缓存
|
|
56
65
|
hidden: false, // 是否支持隐藏文件
|
|
57
66
|
index: 'index.html' // 默认索引文件
|
|
58
|
-
})
|
|
67
|
+
});
|
|
59
68
|
|
|
60
|
-
|
|
69
|
+
// 命中返回文件路径,未命中返回空串
|
|
70
|
+
const file = await serve({ path: '/src/app.js' }, ctx, {});
|
|
61
71
|
```
|
|
62
72
|
|
|
63
73
|
## 配置选项
|
|
64
74
|
|
|
65
75
|
| 配置项 | 类型 | 默认值 | 描述 |
|
|
66
76
|
|--------|------|--------|------|
|
|
77
|
+
| `name` | string | 无(必填) | 驱动名称 |
|
|
67
78
|
| `root` | string | `''` | 静态文件根目录,为空时由 `dir` 推导,处理器兜底为 `./static` |
|
|
68
79
|
| `path` | string | `''` | 路由根路径,为空时自动生成 `/${app}/` |
|
|
69
80
|
| `files` | array | `['.js', '.vue', '.html']` | 需要转换的文件类型 |
|
|
@@ -86,50 +97,54 @@ app.listen(3000);
|
|
|
86
97
|
|
|
87
98
|
```javascript
|
|
88
99
|
// 禁用AMD转换,仅提供静态文件服务
|
|
89
|
-
|
|
100
|
+
const serve = statics({
|
|
101
|
+
name: 'public',
|
|
90
102
|
convert_amd: false,
|
|
91
103
|
compile_vue: false,
|
|
92
104
|
root: './public'
|
|
93
|
-
})
|
|
105
|
+
});
|
|
94
106
|
```
|
|
95
107
|
|
|
96
108
|
### 2. ES6模块转换
|
|
97
109
|
|
|
98
110
|
```javascript
|
|
99
111
|
// 启用AMD转换,自动转换ES6模块
|
|
100
|
-
|
|
112
|
+
const serve = statics({
|
|
113
|
+
name: 'modules',
|
|
101
114
|
convert_amd: true,
|
|
102
115
|
compile_vue: false,
|
|
103
116
|
root: './src',
|
|
104
117
|
path: '/modules',
|
|
105
118
|
files: ['.js', '.ts']
|
|
106
|
-
})
|
|
119
|
+
});
|
|
107
120
|
```
|
|
108
121
|
|
|
109
122
|
### 3. Vue单文件组件支持(AMD转换)
|
|
110
123
|
|
|
111
124
|
```javascript
|
|
112
125
|
// 启用AMD转换,将Vue组件转换为AMD格式
|
|
113
|
-
|
|
126
|
+
const serve = statics({
|
|
127
|
+
name: 'vue',
|
|
114
128
|
convert_amd: true,
|
|
115
129
|
compile_vue: true,
|
|
116
130
|
root: './components',
|
|
117
131
|
path: '/vue',
|
|
118
132
|
files: ['.vue']
|
|
119
|
-
})
|
|
133
|
+
});
|
|
120
134
|
```
|
|
121
135
|
|
|
122
136
|
### 4. Vue单文件组件直接转换(无需AMD包装)
|
|
123
137
|
|
|
124
138
|
```javascript
|
|
125
139
|
// 禁用AMD转换,但启用Vue编译,生成可直接使用的组件
|
|
126
|
-
|
|
140
|
+
const serve = statics({
|
|
141
|
+
name: 'vue_component',
|
|
127
142
|
convert_amd: false,
|
|
128
143
|
compile_vue: true,
|
|
129
144
|
root: './components',
|
|
130
145
|
path: '/vue',
|
|
131
146
|
files: ['.vue']
|
|
132
|
-
})
|
|
147
|
+
});
|
|
133
148
|
```
|
|
134
149
|
|
|
135
150
|
## Vue组件转换特性
|
|
@@ -218,13 +233,20 @@ new Static(config)
|
|
|
218
233
|
|
|
219
234
|
#### 方法
|
|
220
235
|
|
|
221
|
-
- `main(ctx,
|
|
222
|
-
- `
|
|
223
|
-
- `
|
|
224
|
-
- `send(ctx, file_path, config)` - 直接发送指定文件
|
|
236
|
+
- `main(params, ctx, options)` - 主处理逻辑,服务式调用;命中返回物理文件路径,未命中返回空串
|
|
237
|
+
- `run(params, ctx, options)` - 与 `main` 等价,委托给内部 `StaticHandle` 处理
|
|
238
|
+
- `send(ctx, file_path, config)` - 直接发送指定文件(低层 API,不参与请求分发)
|
|
225
239
|
- `_delCache(url)` - 删除指定请求路径的服务端缓存
|
|
226
240
|
- `init()` / `start()` / `stop()` - 生命周期方法(继承自 `mm_mod`)
|
|
227
241
|
|
|
242
|
+
### StaticManager类
|
|
243
|
+
|
|
244
|
+
- `run(params, ctx, options)` - 依次尝试各驱动;已处理返回 `true`,未命中返回 `false`
|
|
245
|
+
- `add(config)` / `del(name)` / `reload(name)` - 驱动注册、注销、重启
|
|
246
|
+
|
|
247
|
+
> 说明:本模块已不再是 Koa/Express 风格的中间件,**不接收 `next` 参数**。
|
|
248
|
+
> 未命中时返回空串或 `false`,由调用方(事件管道、`OS._routeRequest` 等)决定是否继续后续处理。
|
|
249
|
+
|
|
228
250
|
## 缓存策略
|
|
229
251
|
|
|
230
252
|
模块支持以下缓存策略:
|
|
@@ -243,12 +265,12 @@ new Static(config)
|
|
|
243
265
|
|
|
244
266
|
## 错误处理
|
|
245
267
|
|
|
246
|
-
|
|
268
|
+
模块不会自行决定最终状态码,命中失败一律返回空串(管理器返回 `false`),由调用方的响应写出器决定:
|
|
247
269
|
|
|
248
|
-
-
|
|
249
|
-
-
|
|
250
|
-
-
|
|
251
|
-
- Vue
|
|
270
|
+
- 文件不存在 / 路径越界:捕获异常后返回空串,交由后续事件或响应层处理
|
|
271
|
+
- 非 GET/HEAD 请求:直接返回空串,不读取文件
|
|
272
|
+
- 转换失败:记录告警并在可用时回退原内容
|
|
273
|
+
- Vue解析失败:使用回退方案,避免整体请求失败
|
|
252
274
|
|
|
253
275
|
## 开发指南
|
|
254
276
|
|
|
@@ -306,6 +328,15 @@ ISC License
|
|
|
306
328
|
|
|
307
329
|
## 更新日志
|
|
308
330
|
|
|
331
|
+
### 未发布(破坏性变更)
|
|
332
|
+
|
|
333
|
+
- **不再作为中间件**:移除 `next` 参数,`main` / `run` 统一为服务式签名 `(params, ctx, options)`
|
|
334
|
+
- 命中返回物理文件路径,未命中返回空串;`StaticManager.run` 返回布尔值表示是否已处理
|
|
335
|
+
- 仅处理 `GET` / `HEAD`,其它方法直接返回空串(不再读取文件系统)
|
|
336
|
+
- 删除失效的 `_handleRequest`(原实现引用了不存在的 `_handleGetRequest`)
|
|
337
|
+
- `StaticManager.run` 修正为读取注册表驱动列表(此前读 `configs` 恒为空,多驱动分发失效)
|
|
338
|
+
- 同步更新调用方:`mm_os._routeRequest`、`mm_web_server` 示例、模块示例与测试
|
|
339
|
+
|
|
309
340
|
### v1.5.2
|
|
310
341
|
- 新增文件监听功能,支持文件修改时自动更新缓存
|
|
311
342
|
- 集成chokidar模块实现稳定的文件监听
|
package/index.js
CHANGED
|
@@ -1,35 +1,38 @@
|
|
|
1
|
-
const Static = require('./lib/static');
|
|
2
|
-
const StaticHandle = require('./lib/static_handle');
|
|
3
|
-
const Manager = require('./lib/manager');
|
|
4
|
-
const Admin = require('mm_corejs').Admin;
|
|
5
|
-
|
|
6
|
-
const admin = new Admin();
|
|
7
|
-
admin.setClass(Manager);
|
|
8
|
-
|
|
9
|
-
module.exports = {
|
|
10
|
-
Static,
|
|
11
|
-
StaticHandle,
|
|
12
|
-
Manager,
|
|
13
|
-
admin,
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
const Static = require('./lib/static');
|
|
2
|
+
const StaticHandle = require('./lib/static_handle');
|
|
3
|
+
const Manager = require('./lib/manager');
|
|
4
|
+
const Admin = require('mm_corejs').Admin;
|
|
5
|
+
|
|
6
|
+
const admin = new Admin();
|
|
7
|
+
admin.setClass(Manager);
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
Static,
|
|
11
|
+
StaticHandle,
|
|
12
|
+
Manager,
|
|
13
|
+
admin,
|
|
14
|
+
/**
|
|
15
|
+
* 创建静态文件处理器(服务式调用,不是中间件,不接收 next)
|
|
16
|
+
*
|
|
17
|
+
* 返回函数签名 `(params, ctx, options)`:命中文件即写出响应并返回物理路径,
|
|
18
|
+
* 未命中返回空串,由事件管道自行决定是否继续执行后续事件。
|
|
19
|
+
* @param {Object} config 静态文件配置
|
|
20
|
+
* @returns {Function} 处理器函数 (params, ctx, options) => Promise<string>
|
|
21
|
+
*/
|
|
22
|
+
statics(config) {
|
|
23
|
+
const instance = new Static(config);
|
|
24
|
+
return instance.main.bind(instance);
|
|
25
|
+
},
|
|
23
26
|
/**
|
|
24
27
|
* 获取静态文件管理器实例
|
|
25
28
|
* @param {string} scope 作用域名称,为空时使用 os
|
|
26
29
|
* @param {Object} config 管理器配置
|
|
27
30
|
* @returns {Object} 静态文件管理器实例
|
|
28
31
|
*/
|
|
29
|
-
staticAdmin(scope, config) {
|
|
30
|
-
if (!scope) {
|
|
31
|
-
scope = 'os';
|
|
32
|
-
}
|
|
33
|
-
return admin.get(scope, config);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
32
|
+
staticAdmin(scope, config) {
|
|
33
|
+
if (!scope) {
|
|
34
|
+
scope = 'os';
|
|
35
|
+
}
|
|
36
|
+
return admin.get(scope, config);
|
|
37
|
+
}
|
|
38
|
+
};
|
package/lib/manager.js
CHANGED
|
@@ -13,11 +13,6 @@ class StaticManager extends Manager {
|
|
|
13
13
|
* @type {object}
|
|
14
14
|
*/
|
|
15
15
|
static config = {
|
|
16
|
-
/**
|
|
17
|
-
* 名称
|
|
18
|
-
* @type {string}
|
|
19
|
-
*/
|
|
20
|
-
name: "",
|
|
21
16
|
/**
|
|
22
17
|
* 标题
|
|
23
18
|
* @type {string}
|
|
@@ -55,57 +50,51 @@ class StaticManager extends Manager {
|
|
|
55
50
|
}
|
|
56
51
|
|
|
57
52
|
/**
|
|
58
|
-
*
|
|
59
|
-
* @param {object}
|
|
60
|
-
* @param {object}
|
|
53
|
+
* 读取请求路径(优先取事件参数,其次取上下文)
|
|
54
|
+
* @param {object} params 请求参数
|
|
55
|
+
* @param {object} ctx 请求上下文
|
|
56
|
+
* @returns {string} 请求路径
|
|
61
57
|
*/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
var path = ctx.path || (ctx.params && ctx.params.path) || '';
|
|
65
|
-
if (md === "GET" || md === "HEAD") {
|
|
66
|
-
await this._handleGetRequest(ctx, next, path);
|
|
67
|
-
} else {
|
|
68
|
-
await next();
|
|
69
|
-
}
|
|
58
|
+
_requestPath(params, ctx) {
|
|
59
|
+
return (params && params.path) || (ctx && ctx.path) || '';
|
|
70
60
|
}
|
|
71
61
|
|
|
72
62
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* @param {object}
|
|
63
|
+
* 执行请求(服务式调用,不是中间件,不接收 next)
|
|
64
|
+
* 命中静态文件即写出响应并返回 true;未命中返回 false,由上层继续执行后续事件
|
|
65
|
+
* @param {object} params 请求参数(可含 path / url / method)
|
|
66
|
+
* @param {object} ctx 请求上下文
|
|
67
|
+
* @param {object} options 运行选项
|
|
68
|
+
* @returns {Promise<boolean>} 已处理返回 true
|
|
76
69
|
*/
|
|
77
|
-
async run(ctx,
|
|
78
|
-
var
|
|
70
|
+
async run(params, ctx, options) {
|
|
71
|
+
var path = this._requestPath(params, ctx);
|
|
79
72
|
var infos = this._getInfos();
|
|
80
73
|
var len = infos.length;
|
|
81
|
-
let path = ctx.path || (ctx.params && ctx.params.path) || '';
|
|
82
74
|
if (path.indexOf(".") !== -1) {
|
|
83
|
-
|
|
84
|
-
if (!done) {
|
|
85
|
-
await next();
|
|
86
|
-
}
|
|
87
|
-
} else {
|
|
88
|
-
await next();
|
|
89
|
-
await this._handleDirReq(ctx, infos, len, path);
|
|
75
|
+
return await this._tryServeFile(params, ctx, options, infos, len);
|
|
90
76
|
}
|
|
77
|
+
return await this._handleDirReq(params, ctx, options, infos, len, path);
|
|
91
78
|
}
|
|
92
79
|
|
|
93
80
|
/**
|
|
94
81
|
* 尝试提供文件服务
|
|
95
|
-
* @param {object}
|
|
82
|
+
* @param {object} params 请求参数
|
|
83
|
+
* @param {object} ctx 请求上下文
|
|
84
|
+
* @param {object} options 运行选项
|
|
96
85
|
* @param {Array} infos 驱动信息列表
|
|
97
86
|
* @param {number} len 列表长度
|
|
98
|
-
* @
|
|
99
|
-
* @returns {boolean} 是否成功
|
|
87
|
+
* @returns {Promise<boolean>} 是否命中
|
|
100
88
|
*/
|
|
101
|
-
async _tryServeFile(ctx, infos, len
|
|
89
|
+
async _tryServeFile(params, ctx, options, infos, len) {
|
|
102
90
|
for (var i = 0; i < len; i++) {
|
|
103
91
|
var info = infos[i];
|
|
104
|
-
|
|
92
|
+
let is_running = await this.isRunning(info.name);
|
|
93
|
+
if (is_running) {
|
|
105
94
|
let mod = this.get(info.name);
|
|
106
95
|
if (mod) {
|
|
107
96
|
await this._initDrive(mod);
|
|
108
|
-
var done = await mod.run(ctx,
|
|
97
|
+
var done = await mod.run(params, ctx, options);
|
|
109
98
|
if (done) {
|
|
110
99
|
return true;
|
|
111
100
|
}
|
|
@@ -117,32 +106,37 @@ class StaticManager extends Manager {
|
|
|
117
106
|
|
|
118
107
|
/**
|
|
119
108
|
* 处理目录请求
|
|
120
|
-
* @param {object}
|
|
109
|
+
* @param {object} params 请求参数
|
|
110
|
+
* @param {object} ctx 请求上下文
|
|
111
|
+
* @param {object} options 运行选项
|
|
121
112
|
* @param {Array} infos 驱动信息列表
|
|
122
113
|
* @param {number} len 列表长度
|
|
123
114
|
* @param {string} path 请求路径
|
|
115
|
+
* @returns {Promise<boolean>} 驱动已处理目录请求返回 true,仅重定向或未命中返回 false
|
|
124
116
|
*/
|
|
125
|
-
async _handleDirReq(ctx, infos, len, path) {
|
|
117
|
+
async _handleDirReq(params, ctx, options, infos, len, path) {
|
|
126
118
|
var q = this._getQueryString(ctx);
|
|
127
119
|
for (var i = 0; i < len; i++) {
|
|
128
120
|
var info = infos[i];
|
|
129
|
-
|
|
121
|
+
let is_running = await this.isRunning(info.name);
|
|
122
|
+
if (is_running) {
|
|
130
123
|
let mod = this.get(info.name);
|
|
131
124
|
if (mod) {
|
|
132
125
|
var p = mod.config.path;
|
|
133
126
|
if (path === p) {
|
|
134
127
|
this._handleExactMatch(ctx, q, p);
|
|
135
|
-
|
|
128
|
+
return false;
|
|
136
129
|
} else {
|
|
137
130
|
await this._initDrive(mod);
|
|
138
|
-
var done = await mod.run(ctx,
|
|
131
|
+
var done = await mod.run(params, ctx, options);
|
|
139
132
|
if (done) {
|
|
140
|
-
|
|
133
|
+
return true;
|
|
141
134
|
}
|
|
142
135
|
}
|
|
143
136
|
}
|
|
144
137
|
}
|
|
145
138
|
}
|
|
139
|
+
return false;
|
|
146
140
|
}
|
|
147
141
|
|
|
148
142
|
/**
|
|
@@ -171,9 +165,11 @@ class StaticManager extends Manager {
|
|
|
171
165
|
_handleExactMatch(ctx, q, p) {
|
|
172
166
|
// mm_web_server 原生 ctx 无 status 字段,未设置时视为 404 以兼容旧逻辑
|
|
173
167
|
var status = ctx.status === undefined ? 404 : ctx.status;
|
|
174
|
-
|
|
168
|
+
// 驱动路径已带尾部斜杠时(如根路径 /),请求路径与驱动路径相同即已规范,无需重定向,避免自跳死循环
|
|
169
|
+
var need_redirect = status === 404 && typeof ctx.redirect === 'function' && !p.endsWith('/');
|
|
170
|
+
if (need_redirect) {
|
|
175
171
|
// 驱动路径可能已带尾部斜杠,避免拼接出双斜杠
|
|
176
|
-
var target = p
|
|
172
|
+
var target = p + '/';
|
|
177
173
|
if (q) {
|
|
178
174
|
ctx.redirect(target + "?" + q);
|
|
179
175
|
} else {
|
|
@@ -246,15 +242,13 @@ StaticManager.prototype._getInfos = function () {
|
|
|
246
242
|
};
|
|
247
243
|
|
|
248
244
|
/**
|
|
249
|
-
*
|
|
245
|
+
* 初始化驱动实例(重复调用安全由驱动自身保证)
|
|
250
246
|
* @param {object} drive 驱动实例
|
|
251
247
|
* @returns {Promise<void>} 初始化完成
|
|
252
248
|
* @private
|
|
253
249
|
*/
|
|
254
250
|
StaticManager.prototype._initDrive = async function (drive) {
|
|
255
|
-
|
|
256
|
-
await drive.init();
|
|
257
|
-
}
|
|
251
|
+
await drive.init();
|
|
258
252
|
};
|
|
259
253
|
|
|
260
254
|
/**
|
package/lib/static.js
CHANGED
|
@@ -17,8 +17,6 @@ class Static extends Item {
|
|
|
17
17
|
'root': '',
|
|
18
18
|
// 路由根路径
|
|
19
19
|
'path': '',
|
|
20
|
-
// 名称
|
|
21
|
-
'name': '',
|
|
22
20
|
// 首页
|
|
23
21
|
'index': 'index.html',
|
|
24
22
|
// 重定向
|
|
@@ -154,46 +152,45 @@ Static.prototype._getRoot = function (file) {
|
|
|
154
152
|
};
|
|
155
153
|
|
|
156
154
|
/**
|
|
157
|
-
*
|
|
158
|
-
* @param {object}
|
|
159
|
-
* @param {object}
|
|
155
|
+
* 执行前钩子
|
|
156
|
+
* @param {object} params 请求参数
|
|
157
|
+
* @param {object} ctx 请求上下文
|
|
158
|
+
* @param {object} options 运行选项
|
|
160
159
|
*/
|
|
161
|
-
Static.prototype.before = async function (ctx,
|
|
160
|
+
Static.prototype.before = async function (params, ctx, options) { };
|
|
162
161
|
|
|
163
162
|
/**
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* @param {object}
|
|
169
|
-
* @
|
|
170
|
-
* @returns {Promise<string>} 发送成功返回物理文件路径,未命中返回空字符串
|
|
163
|
+
* 主处理函数(服务式调用,不是中间件,不接收 next)
|
|
164
|
+
* 签名与事件管道保持一致:`main(params, ctx, options)`
|
|
165
|
+
* @param {object} params 请求参数(可含 path / url / method)
|
|
166
|
+
* @param {object} ctx 请求上下文
|
|
167
|
+
* @param {object} options 运行选项
|
|
168
|
+
* @returns {Promise<string>} 命中返回物理文件路径,未命中返回空串
|
|
171
169
|
*/
|
|
172
|
-
Static.prototype.main = async function (ctx,
|
|
173
|
-
|
|
174
|
-
return await this.run(ctx, path);
|
|
175
|
-
}
|
|
176
|
-
return await this.send(ctx, path, this.config);
|
|
170
|
+
Static.prototype.main = async function (params, ctx, options) {
|
|
171
|
+
return await this.run(params, ctx, options);
|
|
177
172
|
};
|
|
178
173
|
|
|
179
174
|
/**
|
|
180
|
-
*
|
|
181
|
-
* @param {object}
|
|
182
|
-
* @param {object}
|
|
175
|
+
* 执行后钩子
|
|
176
|
+
* @param {object} params 请求参数
|
|
177
|
+
* @param {object} ctx 请求上下文
|
|
178
|
+
* @param {object} options 运行选项
|
|
183
179
|
*/
|
|
184
|
-
Static.prototype.after = async function (ctx,
|
|
180
|
+
Static.prototype.after = async function (params, ctx, options) { };
|
|
185
181
|
|
|
186
182
|
/**
|
|
187
|
-
*
|
|
188
|
-
* @param {object}
|
|
189
|
-
* @param {object}
|
|
190
|
-
* @
|
|
183
|
+
* 执行静态文件(命中即写出响应,未命中返回空串,由调用方继续后续事件)
|
|
184
|
+
* @param {object} params 请求参数(可含 path / url / method)
|
|
185
|
+
* @param {object} ctx 请求上下文
|
|
186
|
+
* @param {object} options 运行选项
|
|
187
|
+
* @returns {Promise<string>} 命中返回物理文件路径,未命中返回空串
|
|
191
188
|
*/
|
|
192
|
-
Static.prototype.run = async function (ctx,
|
|
189
|
+
Static.prototype.run = async function (params, ctx, options) {
|
|
193
190
|
if (!this.static) {
|
|
194
191
|
this._preset();
|
|
195
192
|
}
|
|
196
|
-
return await this.static.main(ctx,
|
|
193
|
+
return await this.static.main(params, ctx, options);
|
|
197
194
|
};
|
|
198
195
|
|
|
199
196
|
/**
|
package/lib/static_handle.js
CHANGED
|
@@ -356,7 +356,8 @@ StaticHandle.prototype._checkFilePath = function (file_path) {
|
|
|
356
356
|
* @returns {object|null} {start, end} 范围对象,无法解析时返回 null
|
|
357
357
|
*/
|
|
358
358
|
StaticHandle.prototype._parseRange = function (ctx, total) {
|
|
359
|
-
const headers =
|
|
359
|
+
const headers = ctx.headers || (ctx.params && ctx.params.headers)
|
|
360
|
+
|| (ctx.request && ctx.request.headers) || {};
|
|
360
361
|
const range_header = headers['range'];
|
|
361
362
|
if (!range_header) return null;
|
|
362
363
|
const match = /^bytes=(\d*)-(\d*)$/.exec(range_header);
|
|
@@ -772,20 +773,67 @@ StaticHandle.prototype._pathToRoot = function (path) {
|
|
|
772
773
|
}
|
|
773
774
|
|
|
774
775
|
/**
|
|
775
|
-
*
|
|
776
|
-
*
|
|
777
|
-
*
|
|
778
|
-
*
|
|
776
|
+
* 读取请求路径(优先取事件参数,其次取上下文)
|
|
777
|
+
* @param {object} params 请求参数
|
|
778
|
+
* @param {object} ctx 请求上下文
|
|
779
|
+
* @returns {string} 请求路径
|
|
780
|
+
*/
|
|
781
|
+
StaticHandle.prototype._requestPath = function (params, ctx) {
|
|
782
|
+
return (params && params.path) || (ctx && ctx.path) || '';
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* 读取请求方法(兼容参数与上下文两处来源)
|
|
787
|
+
* @param {object} params 请求参数
|
|
788
|
+
* @param {object} ctx 请求上下文
|
|
789
|
+
* @returns {string} 大写的请求方法,缺省为空串
|
|
790
|
+
*/
|
|
791
|
+
StaticHandle.prototype._requestMethod = function (params, ctx) {
|
|
792
|
+
let method = (params && params.method) || (ctx && ctx.method) || '';
|
|
793
|
+
return String(method).toUpperCase();
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* 读取完整请求地址(含查询串,用作缓存键)
|
|
798
|
+
* @param {object} params 请求参数
|
|
779
799
|
* @param {object} ctx 请求上下文
|
|
780
|
-
* @param {
|
|
800
|
+
* @param {string} path 已解析出的请求路径
|
|
801
|
+
* @returns {string} 完整请求地址
|
|
781
802
|
*/
|
|
782
|
-
StaticHandle.prototype.
|
|
783
|
-
|
|
784
|
-
|
|
803
|
+
StaticHandle.prototype._requestUrl = function (params, ctx, path) {
|
|
804
|
+
return (params && params.url) || (ctx && (ctx.url || ctx.path)) || path;
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* 判断请求方法是否允许读取静态文件
|
|
809
|
+
* @param {string} method 请求方法
|
|
810
|
+
* @returns {boolean} GET / HEAD / 未指定方法时返回 true
|
|
811
|
+
*/
|
|
812
|
+
StaticHandle.prototype._isReadable = function (method) {
|
|
813
|
+
return !method || method === 'GET' || method === 'HEAD';
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* 主处理函数(服务式调用,不是中间件,不接收 next)
|
|
818
|
+
*
|
|
819
|
+
* 约定签名 `main(params, ctx, options)`,由事件管道或上层路由直接调用:
|
|
820
|
+
* - 命中文件:写出响应(或返回物理路径由调用方继续处理)并返回物理文件路径;
|
|
821
|
+
* - 未命中:直接返回空串,由调用方决定是否继续执行后续事件,无需回调放行。
|
|
822
|
+
* @param {object} params 请求参数(可含 path / url / method)
|
|
823
|
+
* @param {object} ctx 请求上下文(可含 path / url / res / set)
|
|
824
|
+
* @param {object} options 运行选项(保留,供上层透传)
|
|
825
|
+
* @returns {Promise<string>} 命中返回物理文件路径,未命中返回空串
|
|
826
|
+
*/
|
|
827
|
+
StaticHandle.prototype.main = async function (params, ctx, options) {
|
|
828
|
+
let p = this._requestPath(params, ctx);
|
|
829
|
+
if (!this._isReadable(this._requestMethod(params, ctx))) {
|
|
830
|
+
return '';
|
|
831
|
+
}
|
|
832
|
+
// 无扩展名的路径交给目录分支与后续事件处理
|
|
785
833
|
if (p.indexOf('.') === -1) {
|
|
786
|
-
await next();
|
|
787
834
|
return '';
|
|
788
835
|
}
|
|
836
|
+
let url = this._requestUrl(params, ctx, p);
|
|
789
837
|
let path = this._getRealPath(p);
|
|
790
838
|
|
|
791
839
|
// 检查缓存
|
|
@@ -805,9 +853,8 @@ StaticHandle.prototype.main = async function (ctx, next) {
|
|
|
805
853
|
return await this._sendFile(ctx, path);
|
|
806
854
|
}
|
|
807
855
|
catch {
|
|
808
|
-
|
|
856
|
+
return '';
|
|
809
857
|
}
|
|
810
|
-
return '';
|
|
811
858
|
};
|
|
812
859
|
|
|
813
860
|
/**
|
package/package.json
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "mm_statics",
|
|
3
|
-
"version": "1.9.
|
|
4
|
-
"description": "这是超级美眉statics函数模块,用于web服务端statics缓存",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"test": "mocha tests/index.js",
|
|
8
|
-
"tests": "mocha tests/*.js"
|
|
9
|
-
},
|
|
10
|
-
"repository": {
|
|
11
|
-
"type": "git",
|
|
12
|
-
"url": "git+https://gitee.com/qiuwenwu91/mm_statics.git"
|
|
13
|
-
},
|
|
14
|
-
"keywords": [
|
|
15
|
-
"mm_mod",
|
|
16
|
-
"mm_infra",
|
|
17
|
-
"mm_common",
|
|
18
|
-
"statics",
|
|
19
|
-
"async",
|
|
20
|
-
"await",
|
|
21
|
-
"promise",
|
|
22
|
-
"add",
|
|
23
|
-
"del",
|
|
24
|
-
"set",
|
|
25
|
-
"get",
|
|
26
|
-
"query",
|
|
27
|
-
"run",
|
|
28
|
-
"exec"
|
|
29
|
-
],
|
|
30
|
-
"layer": "infra",
|
|
31
|
-
"group": "web_infra",
|
|
32
|
-
"author": "邱文武",
|
|
33
|
-
"license": "ISC",
|
|
34
|
-
"bugs": {
|
|
35
|
-
"url": "https://gitee.com/qiuwenwu91/mm_statics/issues"
|
|
36
|
-
},
|
|
37
|
-
"homepage": "https://gitee.com/qiuwenwu91/mm_statics#readme",
|
|
38
|
-
"files": [
|
|
39
|
-
"index.js",
|
|
40
|
-
"lib/vue_compiler.js",
|
|
41
|
-
"lib/static.js",
|
|
42
|
-
"lib/static_handle.js",
|
|
43
|
-
"lib/manager.js",
|
|
44
|
-
"eslint.config.js",
|
|
45
|
-
"static/"
|
|
46
|
-
],
|
|
47
|
-
"dependencies": {
|
|
48
|
-
"@vue/compiler-sfc": "^3.5.44",
|
|
49
|
-
"abstract-syntax-tree": "^2.25.0",
|
|
50
|
-
"chokidar": "^5.0.0",
|
|
51
|
-
"marked": "^18.1.1",
|
|
52
|
-
"mm_cache": "^1.5.4",
|
|
53
|
-
"mm_corejs": "^1.1.5",
|
|
54
|
-
"mm_es6_to_amd": "^1.5.9",
|
|
55
|
-
"mm_helper": "^1.0.0",
|
|
56
|
-
"mm_machine": "^3.5.4",
|
|
57
|
-
"prettier": "^3.9.10"
|
|
58
|
-
},
|
|
59
|
-
"devDependencies": {
|
|
60
|
-
"eslint": "^10.12.0",
|
|
61
|
-
"eslint-plugin-jsdoc": "^65.3.4",
|
|
62
|
-
"koa": "^3.2.1",
|
|
63
|
-
"mm_eslint": "^1.7.7",
|
|
64
|
-
"mocha": "^12.0.3",
|
|
65
|
-
"supertest": "^7.3.1"
|
|
66
|
-
}
|
|
67
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "mm_statics",
|
|
3
|
+
"version": "1.9.3",
|
|
4
|
+
"description": "这是超级美眉statics函数模块,用于web服务端statics缓存",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "mocha tests/index.js",
|
|
8
|
+
"tests": "mocha tests/*.js"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://gitee.com/qiuwenwu91/mm_statics.git"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mm_mod",
|
|
16
|
+
"mm_infra",
|
|
17
|
+
"mm_common",
|
|
18
|
+
"statics",
|
|
19
|
+
"async",
|
|
20
|
+
"await",
|
|
21
|
+
"promise",
|
|
22
|
+
"add",
|
|
23
|
+
"del",
|
|
24
|
+
"set",
|
|
25
|
+
"get",
|
|
26
|
+
"query",
|
|
27
|
+
"run",
|
|
28
|
+
"exec"
|
|
29
|
+
],
|
|
30
|
+
"layer": "infra",
|
|
31
|
+
"group": "web_infra",
|
|
32
|
+
"author": "邱文武",
|
|
33
|
+
"license": "ISC",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://gitee.com/qiuwenwu91/mm_statics/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://gitee.com/qiuwenwu91/mm_statics#readme",
|
|
38
|
+
"files": [
|
|
39
|
+
"index.js",
|
|
40
|
+
"lib/vue_compiler.js",
|
|
41
|
+
"lib/static.js",
|
|
42
|
+
"lib/static_handle.js",
|
|
43
|
+
"lib/manager.js",
|
|
44
|
+
"eslint.config.js",
|
|
45
|
+
"static/"
|
|
46
|
+
],
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@vue/compiler-sfc": "^3.5.44",
|
|
49
|
+
"abstract-syntax-tree": "^2.25.0",
|
|
50
|
+
"chokidar": "^5.0.0",
|
|
51
|
+
"marked": "^18.1.1",
|
|
52
|
+
"mm_cache": "^1.5.4",
|
|
53
|
+
"mm_corejs": "^1.1.5",
|
|
54
|
+
"mm_es6_to_amd": "^1.5.9",
|
|
55
|
+
"mm_helper": "^1.0.0",
|
|
56
|
+
"mm_machine": "^3.5.4",
|
|
57
|
+
"prettier": "^3.9.10"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"eslint": "^10.12.0",
|
|
61
|
+
"eslint-plugin-jsdoc": "^65.3.4",
|
|
62
|
+
"koa": "^3.2.1",
|
|
63
|
+
"mm_eslint": "^1.7.7",
|
|
64
|
+
"mocha": "^12.0.3",
|
|
65
|
+
"supertest": "^7.3.1"
|
|
66
|
+
}
|
|
67
|
+
}
|