wok-server 0.1.2 → 0.1.4

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 CHANGED
@@ -1,4 +1,4 @@
1
- # node 后端框架
1
+ # Wok Server
2
2
 
3
3
  一个简洁易用的 Nodejs 后端框架,使用 Typescript 开发,有完整的类型约束和定义,注释详细,文档齐全,支持国际化。
4
4
 
@@ -20,7 +20,12 @@ function createJsonHandler(opts) {
20
20
  if (opts.validation) {
21
21
  // 切换语言
22
22
  (0, i18n_1.getI18n)().switchByRequest(exchange.request.headers);
23
- (0, validation_1.validate)(body, opts.validation);
23
+ if (typeof opts.validation === 'function') {
24
+ (0, validation_1.validate)(body, opts.validation());
25
+ }
26
+ else {
27
+ (0, validation_1.validate)(body, opts.validation);
28
+ }
24
29
  }
25
30
  const res = await opts.handle(body, exchange);
26
31
  if (!res) {
package/dist/mvc/index.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.stopWebServer = exports.startWebServer = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const fs_1 = require("fs");
6
+ const promises_1 = require("fs/promises");
6
7
  const http_1 = require("http");
7
8
  const os_1 = require("os");
8
9
  const path_1 = require("path");
@@ -73,7 +74,7 @@ async function handleRouter(exchange, routers, staticSettings) {
73
74
  const router = routers[path];
74
75
  if (!router) {
75
76
  // 路由找不不到,尝试静态文件
76
- if (staticSettings.length) {
77
+ if ((exchange.request.method || '').toLowerCase() === 'get' && staticSettings.length) {
77
78
  await handleStatic(exchange, routers, path, staticSettings);
78
79
  }
79
80
  else {
@@ -123,15 +124,15 @@ async function handleStatic(exchange, routers, path, staticSettings) {
123
124
  respond404(exchange, routers, path);
124
125
  return;
125
126
  }
126
- const stat = (0, fs_1.statSync)(fullPath);
127
+ const statRes = await (0, promises_1.stat)(fullPath);
127
128
  // 目录,寻找 index.html
128
- if (stat.isDirectory()) {
129
+ if (statRes.isDirectory()) {
129
130
  const indexPath = (0, path_1.resolve)(fullPath, 'index.html');
130
131
  if (!(0, fs_1.existsSync)(indexPath)) {
131
132
  respond404(exchange, routers, path);
132
133
  return;
133
134
  }
134
- const indexStat = (0, fs_1.statSync)(indexPath);
135
+ const indexStat = await (0, promises_1.stat)(indexPath);
135
136
  if (!indexStat.isFile()) {
136
137
  respond404(exchange, routers, path);
137
138
  return;
@@ -144,7 +145,7 @@ async function handleStatic(exchange, routers, path, staticSettings) {
144
145
  return;
145
146
  }
146
147
  // 文件直接渲染
147
- if (stat.isFile()) {
148
+ if (statRes.isFile()) {
148
149
  // Cache-Control
149
150
  if (matchedSetting.cacheAge >= 0) {
150
151
  exchange.response.setHeader('Cache-Control', matchedSetting.cacheAge === 0 ? 'no-store' : `max-age=${matchedSetting.cacheAge}`);
@@ -210,8 +211,8 @@ async function startWebServer(opts) {
210
211
  if (!(0, fs_1.existsSync)(dir)) {
211
212
  throw new Error(`Static file configuration error,path ${dir} does not exist,config dir:${setting.dir}`);
212
213
  }
213
- const stat = (0, fs_1.statSync)(dir);
214
- if (!stat.isDirectory()) {
214
+ const statRes = await (0, promises_1.stat)(dir);
215
+ if (!statRes.isDirectory()) {
215
216
  throw new Error(`Static file configuration error,path ${dir} is not a directory,config dir:${setting.dir}`);
216
217
  }
217
218
  let finalPath = path.startsWith('/') ? path : '/' + path;
@@ -243,6 +244,7 @@ async function startWebServer(opts) {
243
244
  interceptors.push(...opts.interceptors);
244
245
  }
245
246
  SERVER = (0, http_1.createServer)((req, res) => {
247
+ res.setHeader('Server', 'Wok Server');
246
248
  res.on('error', error => {
247
249
  // 如果响应流发生错误,只能把信息记录下来
248
250
  (0, log_1.getLogger)().error(`Response Error:${req.url}`, error);
@@ -266,7 +268,7 @@ async function startWebServer(opts) {
266
268
  await new Promise((resolve, reject) => {
267
269
  server.on('error', e => {
268
270
  if (e.code === 'EADDRINUSE') {
269
- reject(`端口号 ${config_1.config.port} 已经被占用`);
271
+ reject(`Port ${config_1.config.port} is already in use.`);
270
272
  }
271
273
  else {
272
274
  reject(e);
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderFile = void 0;
4
4
  const fs_1 = require("fs");
5
+ const promises_1 = require("fs/promises");
5
6
  const json_1 = require("./json");
6
7
  const path_1 = require("path");
7
8
  /**
@@ -96,7 +97,7 @@ function decideContentType(fileName) {
96
97
  */
97
98
  async function renderFile(request, response, filePath, download = false) {
98
99
  if (!(0, fs_1.existsSync)(filePath)) {
99
- (0, json_1.renderError)(response, '文件不存在', 404);
100
+ (0, json_1.renderError)(response, 'Cannot find file.', 404);
100
101
  return;
101
102
  }
102
103
  const fileName = (0, path_1.basename)(filePath);
@@ -118,10 +119,30 @@ async function renderFile(request, response, filePath, download = false) {
118
119
  else if (contentType) {
119
120
  response.setHeader('Content-Type', contentType);
120
121
  }
122
+ const statRes = await (0, promises_1.stat)(filePath);
123
+ // 支持 If-Modified-Since
124
+ // 由于只是简单的文件映射,没有 etag,不能支持 If-None-Match
125
+ // 缓存校验只能支持时间的比对,修改时间是文件系统本来就有的
126
+ if (request.headers['if-modified-since']) {
127
+ const modifiedSince = new Date(request.headers['if-modified-since']);
128
+ // 判定日期是否有效
129
+ if (modifiedSince instanceof Date && !isNaN(modifiedSince.getTime())) {
130
+ // 比较更改日期,只精确到秒, UTC 格式只精确到秒,但是 mtime 是包含毫秒的
131
+ const { mtime } = statRes;
132
+ mtime.setMilliseconds(0);
133
+ if (modifiedSince >= mtime) {
134
+ response.statusCode = 304;
135
+ response.setHeader('Last-Modified', statRes.mtime.toUTCString());
136
+ response.end();
137
+ return;
138
+ }
139
+ }
140
+ }
121
141
  // 支持 Range
122
142
  // https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Range
123
143
  const rangeHeader = request.headers['range'];
124
144
  if (!rangeHeader) {
145
+ response.setHeader('Last-Modified', statRes.mtime.toUTCString());
125
146
  return streamFile(filePath, response);
126
147
  }
127
148
  // 解析,range 示例:bytes=200-1000, 2000-6576, 19000-
@@ -141,23 +162,22 @@ async function renderFile(request, response, filePath, download = false) {
141
162
  let start = strs[0] ? parseInt(strs[0], 10) : NaN;
142
163
  let end = strs[1] ? parseInt(strs[1], 10) : NaN;
143
164
  // 解析文件
144
- const stat = (0, fs_1.statSync)(filePath);
145
165
  if (isNaN(start) || start < 0) {
146
166
  // 范围不合法,返回 416
147
167
  (0, json_1.renderError)(response, `Range not satisfiable,start is ${start}`, 416);
148
168
  return;
149
169
  }
150
170
  if (isNaN(end)) {
151
- end = stat.size - 1;
171
+ end = statRes.size - 1;
152
172
  }
153
- else if (end > stat.size - 1) {
173
+ else if (end > statRes.size - 1) {
154
174
  // 范围不合法,返回 416
155
- (0, json_1.renderError)(response, `Range not satisfiable,end must not be greater than ${stat.size - 1}`, 416);
175
+ (0, json_1.renderError)(response, `Range not satisfiable,end must not be greater than ${statRes.size - 1}`, 416);
156
176
  return;
157
177
  }
158
178
  // 注:Range 和 Content-Range 还有 createReadStream 中的字节范围,都是前后全包含的
159
179
  // Content-Range: bytes 42-1233/1234
160
- response.setHeader('Content-Range', `bytes ${start}-${end}/${stat.size}`);
180
+ response.setHeader('Content-Range', `bytes ${start}-${end}/${statRes.size}`);
161
181
  return streamFile(filePath, response, { start, end });
162
182
  }
163
183
  exports.renderFile = renderFile;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wok-server",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "packageManager": "pnpm@8.9.0",
5
5
  "description": "一个基于 NodeJs 和 TypeScript 的后端框架,轻量级、克制、简洁。A lightweight, restrained, and concise backend framework based on Node.js and TypeScript.",
6
6
  "scripts": {
@@ -12,7 +12,7 @@ export declare function createJsonHandler<REQ, RES = void>(opts: {
12
12
  /**
13
13
  * 校验信息,可选,用于检查请求信息.对于一些特殊情况,无法使用校验器的,可以在 handle 中继续处理.
14
14
  */
15
- validation?: ValidationOpts<REQ>;
15
+ validation?: ValidationOpts<REQ> | (() => ValidationOpts<REQ>);
16
16
  /**
17
17
  * 处理请求.
18
18
  * @param body 正文内容