jj.js 0.12.0 → 0.14.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # v0.14.0 / 2024-03-07
2
+ 1. 优化类型文件生成
3
+ 2. 修复优化url生成
4
+ 3. 自动加载功能,不缓存空文件
5
+ 4. 修复优化Resquest类请求方法判断
6
+ 5. 默认开启cookie签名机制
7
+
8
+ # v0.13.0 / 2024-02-27
9
+ 1. 新增request系统类库
10
+ 2. response类isAjax方法迁移到request类
11
+ 3. 优化应用端types类型文件生成逻辑,启动服务时生成一次
12
+
1
13
  # v0.12.0 / 2024-02-26
2
14
  1. 添加依赖`watch`
3
15
 
package/jj.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * jj.js核心库<br/>
3
- * {App, Cache, Context, Controller, Cookie, Ctx, Db, Logger, Middleware, Model, Pagination, Response, Upload, Url, View, utils, config}
3
+ * {App, Cache, Context, Controller, Cookie, Ctx, Db, Logger, Middleware, Model, Pagination, Request, Response, Upload, Url, View, utils, config}
4
4
  * @module core
5
5
  * @type {import('./types').Core}
6
6
  */
package/jsconfig.json CHANGED
@@ -5,5 +5,5 @@
5
5
  "resolveJsonModule": true,
6
6
  "checkJs": true
7
7
  },
8
- "exclude": ["node_modules", "docs", "./lib/ctx.js"]
9
- }
8
+ "exclude": ["node_modules", "docs"]
9
+ }
package/lib/app.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const path = require('path');
2
2
  const Koa = require('koa');
3
- const {app: cfg_app} = require('./config');
3
+ const {app: cfg_app, cookie: cfg_cookie} = require('./config');
4
4
  const Logger = require('./logger');
5
5
  const Response = require('./response');
6
6
  const router = require('./router');
@@ -54,6 +54,9 @@ class App extends Koa
54
54
  // router
55
55
  this.use(router.routes()).use(router.allowedMethods());
56
56
 
57
+ // cookie
58
+ cfg_cookie.keys && (this.keys = cfg_app.app_debug ? ['jj.js'] : cfg_cookie.keys);
59
+
57
60
  // types
58
61
  if(cfg_app.app_debug) {
59
62
  const {isFileSync} = require('./utils/fs');
package/lib/config.js CHANGED
@@ -49,6 +49,11 @@ const cache = {
49
49
  clear_time: undefined // (undefined: 清理一次, 0: 关闭自动清理, >0: 为自动清理周期)
50
50
  }
51
51
 
52
+ const cookie = {
53
+ keys: [Math.random().toString(36).slice(-8)], // cookie密钥
54
+ signed: true // 默认开启签名
55
+ }
56
+
52
57
  const page = {
53
58
  page_key : 'page', // 默认分页标识
54
59
  key_origin : 'query', // query 或 params
@@ -94,6 +99,6 @@ module.exports = {
94
99
  cache: {...cache, ...base_config.cache},
95
100
  page: {...page, ...base_config.page},
96
101
  routes: base_config.routes,
97
- cookie: base_config.cookie,
102
+ cookie: {...cookie, ...base_config.cookie},
98
103
  tpl: {...tpl, ...base_config.tpl}
99
104
  };
package/lib/cookie.js CHANGED
@@ -30,20 +30,21 @@ class Cookie extends Context
30
30
  if(key === undefined) {
31
31
  return this.all();
32
32
  }
33
- return this.ctx.cookies.get(key, options);
33
+ return this.ctx.cookies.get(key, {signed: cfg_cookie.signed, ...options});
34
34
  }
35
35
 
36
36
  /**
37
37
  * 删除或清理cookie
38
38
  * @public
39
39
  * @param {*} [key] - 为空则清理所有cookie
40
+ * @param {import('cookies').GetOption} [options]
40
41
  * @returns {this}
41
42
  */
42
- delete(key) {
43
+ delete(key, options) {
43
44
  if(key === undefined) {
44
45
  this.clear();
45
46
  } else {
46
- this.ctx.cookies.set(key, '', {maxAge: 0});
47
+ this.ctx.cookies.set(key, '', {...cfg_cookie, ...options, maxAge: 0});
47
48
  }
48
49
  return this;
49
50
  }
package/lib/ctx.js CHANGED
@@ -42,8 +42,8 @@ const Ctx = new Proxy(class {}, {
42
42
  } else {
43
43
  _[APP][$prop] = null;
44
44
  }
45
- // lib调用,防止覆盖
46
- if(['logger', 'pagination', 'response', 'url', 'view'].includes(prop) && _[APP][$prop] && _[APP][$prop].__node && !_[APP][$prop].__node.isClass && $[prop]) {
45
+ // lib内有调用,防止覆盖
46
+ if(['logger', 'pagination', 'request', 'response', 'url', 'view'].includes(prop) && _[APP][$prop] && _[APP][$prop].__node && !_[APP][$prop].__node.isClass && $[prop]) {
47
47
  _[APP][$prop] = $[prop];
48
48
  }
49
49
  }
package/lib/loader.js CHANGED
@@ -43,8 +43,8 @@ function loader(dir='./', ...args) {
43
43
  nodeType[_nodePath] = 'file';
44
44
  } else if(isDirSync(_nodePath)) {
45
45
  nodeType[_nodePath] = 'dir';
46
- } else{
47
- nodeType[_nodePath] = 'none';
46
+ } else {
47
+ // nodeType[_nodePath] = 'none';
48
48
  }
49
49
  }
50
50
  if(nodeType[_nodePath] == 'file') {
package/lib/request.js ADDED
@@ -0,0 +1,204 @@
1
+ const Context = require('./context');
2
+
3
+ class Request extends Context
4
+ {
5
+ /**
6
+ * Initialize a new `Request`
7
+ * @public
8
+ * @param {import('../types').KoaCtx} ctx
9
+ */
10
+ constructor(ctx) {
11
+ super(ctx);
12
+ /**
13
+ * @type {import('querystring').ParsedUrlQuery}
14
+ * @private
15
+ */
16
+ this._get = {...this.ctx.query};
17
+ /**
18
+ * @private
19
+ */
20
+ this._post = {...this.ctx.request.body};
21
+ /**
22
+ * @private
23
+ */
24
+ this._param = {...this.ctx.params};
25
+ }
26
+
27
+ /**
28
+ * 智能获取请求数据
29
+ * @param {string} key
30
+ * @param {*} [default_value='']
31
+ * @returns {*}
32
+ */
33
+ query(key, default_value='') {
34
+ return this.post(key) || this.get(key) || this.param(key) || default_value;
35
+ }
36
+
37
+ /**
38
+ * 获取param请求数据
39
+ * @param {string} key
40
+ * @param {*} default_value
41
+ * @returns {*}
42
+ */
43
+ param(key, default_value='') {
44
+ return this._param[key] || default_value;
45
+ }
46
+
47
+ /**
48
+ * 获取get请求数据
49
+ * @param {string} key
50
+ * @param {*} default_value
51
+ * @returns {*}
52
+ */
53
+ get(key, default_value='') {
54
+ return Array.isArray(this._get[key]) ? this._get[key][0] : this._get[key] || default_value;
55
+ }
56
+
57
+ /**
58
+ * 获取post请求数据
59
+ * @param {string} key
60
+ * @param {*} default_value
61
+ * @returns {*}
62
+ */
63
+ post(key, default_value='') {
64
+ return this._post[key] || default_value;
65
+ }
66
+
67
+
68
+ /**
69
+ * 获取所有请求数据
70
+ * @returns {Object}
71
+ */
72
+ queryAll() {
73
+ return {...this._param, ...this._get, ...this._post};
74
+ }
75
+
76
+ /**
77
+ * 获取所有param数据
78
+ * @returns {Object}
79
+ */
80
+ paramAll() {
81
+ return this._param;
82
+ }
83
+
84
+ /**
85
+ * 获取所有get数据
86
+ * @returns {Object}
87
+ */
88
+ getAll() {
89
+ return this._get;
90
+ }
91
+
92
+ /**
93
+ * 获取所有post数据
94
+ * @returns {Object}
95
+ */
96
+ postAll() {
97
+ return this._post;
98
+ }
99
+
100
+ /**
101
+ * 获取上传file数据
102
+ * @param {string} key
103
+ * @returns {import('formidable').File | import('formidable').File[]}
104
+ */
105
+ file(key) {
106
+ return this.ctx.request.files[key];
107
+ }
108
+
109
+ /**
110
+ * 获取上传file数据
111
+ * @returns {import('formidable').Files}
112
+ */
113
+ fileAll() {
114
+ return this.ctx.request.files;
115
+ }
116
+
117
+ /**
118
+ * 获取请求应用名
119
+ * @returns {string}
120
+ */
121
+ app() {
122
+ return this.ctx.APP;
123
+ }
124
+
125
+ /**
126
+ * 获取请求控制器名
127
+ * @returns {string}
128
+ */
129
+ controller() {
130
+ return this.ctx.CONTROLLER;
131
+ }
132
+
133
+ /**
134
+ * 获取请求方法名
135
+ * @returns {string}
136
+ */
137
+ action() {
138
+ return this.ctx.ACTION;
139
+ }
140
+
141
+ /**
142
+ * 获取请求url
143
+ * @returns {string}
144
+ */
145
+ url() {
146
+ return this.ctx.url;
147
+ }
148
+
149
+ /**
150
+ * 获取请求方式
151
+ * @returns {string}
152
+ */
153
+ method() {
154
+ return this.ctx.method.toLowerCase();
155
+ }
156
+
157
+ /**
158
+ * 判断是否get请求
159
+ * @returns {boolean}
160
+ */
161
+ isGet() {
162
+ return this.method() == 'get';
163
+ }
164
+
165
+ /**
166
+ * 判断是否post请求
167
+ * @returns {boolean}
168
+ */
169
+ isPost() {
170
+ return this.method() == 'post';
171
+ }
172
+
173
+ /**
174
+ * 判断是否ajax请求
175
+ * @public
176
+ * @returns {boolean}
177
+ */
178
+ isAjax() {
179
+ return this.ctx.headers['x-requested-with'] && String(this.ctx.headers['x-requested-with']).toLowerCase() == 'xmlhttprequest'
180
+ || this.ctx.request.type.toLowerCase() == 'application/json'
181
+ || this.query('is_ajax')
182
+ ? true : false;
183
+ }
184
+
185
+ /**
186
+ * 获取header请求数据
187
+ * @param {string} key
188
+ * @param {*} default_value
189
+ * @returns {*}
190
+ */
191
+ header(key, default_value='') {
192
+ return this.ctx.headers[key] || default_value;
193
+ }
194
+
195
+ /**
196
+ * 获取所有header请求数据
197
+ * @returns {Object}
198
+ */
199
+ headerAll() {
200
+ return this.ctx.headers;
201
+ }
202
+ }
203
+
204
+ module.exports = Request;
package/lib/response.js CHANGED
@@ -72,7 +72,7 @@ class Response extends Context
72
72
  */
73
73
  jump(msg, url, state=1) {
74
74
  const tplData = {state, msg, url, wait: this._wait};
75
- this.ctx.body = this.isAjax() ? {state, msg, data: url} : this._tpl_jump.replace(/\{\$(\w+)\}/g, (...args) => {
75
+ this.ctx.body = this.$request.isAjax() ? {state, msg, data: url} : this._tpl_jump.replace(/\{\$(\w+)\}/g, (...args) => {
76
76
  return tplData[args[1]];
77
77
  });
78
78
  }
@@ -99,23 +99,10 @@ class Response extends Context
99
99
  tplData.msg = escapeHtml(tplData.msg);
100
100
  tplData.code = '<span class="line">' + tplData.code.map(str => escapeHtml(str)).join('</span><span class="line">') + '</span>';
101
101
  tplData.stack = tplData.stack.map(str => escapeHtml(str)).join('<br>');
102
- this.ctx.body = this.isAjax() ? {state: 0, msg: tplData.msg} : this._tpl_exception.replace(/\{\$(\w+)\}/g, (...args) => {
102
+ this.ctx.body = this.$request.isAjax() ? {state: 0, msg: tplData.msg} : this._tpl_exception.replace(/\{\$(\w+)\}/g, (...args) => {
103
103
  return tplData[args[1]];
104
104
  });
105
105
  }
106
-
107
- /**
108
- * 判断是否ajax请求
109
- * @public
110
- * @returns {boolean}
111
- */
112
- isAjax() {
113
- return this.ctx.headers['x-requested-with'] && String(this.ctx.headers['x-requested-with']).toLowerCase() == 'xmlhttprequest'
114
- || this.ctx.request.type.toLowerCase() == 'application/json'
115
- || this.ctx.query.is_ajax
116
- || this.ctx.request.body && this.ctx.request.body.is_ajax
117
- ? true : false;
118
- }
119
106
  }
120
107
 
121
108
  module.exports = Response;
package/lib/run.js CHANGED
@@ -10,7 +10,6 @@ const storage = require('./storage');
10
10
 
11
11
  async function run(ctx, next, control_type=cfg_app.controller_folder) {
12
12
  if(cfg_app.app_debug) {
13
- Logger.http('Request:', ctx.request);
14
13
  Logger.http({Router: control_type, APP: ctx.APP, CONTROLLER: ctx.CONTROLLER, ACTION: ctx.ACTION});
15
14
  Object.keys(ctx.params).length && Logger.http('Params:', ctx.params);
16
15
  Object.keys(ctx.query).length && Logger.http('Get:', ctx.query);
package/lib/types.js CHANGED
@@ -21,6 +21,7 @@ function start() {
21
21
  Object.keys(f).forEach(file => {
22
22
  Logger.system("wacth:", file);
23
23
  });
24
+ createFile();
24
25
  } else if(prev === null) {
25
26
  // f is a new file
26
27
  if(prevFile.file != f || prevFile.action != "created") {
@@ -55,18 +56,23 @@ function start() {
55
56
  * createTypesFile
56
57
  */
57
58
  async function createFile(f) {
58
- if(this._f == f) {
59
- return;
60
- }
61
- if(this._f) {
62
- delete require.cache[require.resolve(this._f)];
63
- }
64
- this._f = f;
65
-
66
- const f_info = f.replace(cfg_app.base_dir + path.sep, '').split(path.sep);
67
- if(f_info[0] && f_info[0] != cfg_app.common_app && !~f_info[0].indexOf('.')) {
68
- this._APP = f_info[0];
59
+ if(f) {
60
+ require.cache[f] && delete(require.cache[f])
61
+ if(f == this._f) {
62
+ return;
63
+ }
64
+
65
+ this._f && require.cache[this._f] && delete(require.cache[this._f]);
66
+ this._f = f;
67
+
68
+ const f_info = f.replace(cfg_app.base_dir + path.sep, '').split(path.sep);
69
+ if(cfg_app.app_multi && f_info[0] && f_info[1] && f_info[0] != cfg_app.common_app && f_info[0] != 'config' && !~f_info[0].indexOf('.')) {
70
+ this._APP = f_info[0];
71
+ }
72
+ } else {
73
+ this._APP = cfg_app.default_app;
69
74
  }
75
+
70
76
 
71
77
  const ignore = ['node_modules', 'docker', 'package-lock.json', 'types.js', '.git', '.gitignore'];
72
78
  ignore.push(path.basename(module.parent.parent.parent.filename));
package/lib/url.js CHANGED
@@ -18,19 +18,28 @@ class Url extends Context
18
18
  * 编译生成url
19
19
  * @public
20
20
  * @param {string} [url] - 网址,支持智能解析
21
- * @param {object} [vars] - 网址参数
22
- * @param {string} [ext] - 网址后缀
23
- * @param {string} [domain] - 网址域名
21
+ * @param {object|string|boolean} [vars] - 网址参数
22
+ * @param {string|boolean} [ext] - 网址后缀
23
+ * @param {string|boolean} [domain] - 网址域名
24
24
  * @returns {string}
25
+ * @example - build(url, vars, ext, domain|true)
26
+ * @example - build(url, ext, domain|true)
27
+ * @example - build(url, domain|true)
25
28
  */
26
- build(url='', vars, ext='', domain='') {
27
- if(vars && typeof vars != 'object') {
29
+ build(url='', vars={}, ext='', domain='') {
30
+ if(typeof vars != 'object') {
28
31
  [vars, ext, domain] = [{}, vars, ext];
29
32
  }
33
+ if(ext === true || ext.slice(0, 4) == 'http') {
34
+ [ext, domain] = ['', ext];
35
+ }
36
+ if(domain === true) {
37
+ domain = this.ctx.protocol + '://' + this.ctx.host;
38
+ }
30
39
  const query = {...vars};
31
40
  let urls;
32
41
  if(url.slice(0, 1) == ':') {
33
- url = this.ruleUrl(url.substr(1), query);
42
+ url = this.ruleUrl(url.slice(0, 1), query);
34
43
  } else if(url.slice(0, 1) != '/' && url.slice(0, 4) != 'http') {
35
44
  urls = url.split('/').reverse().map(u => toLine(u));
36
45
  if(urls.length < 3) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jj.js",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "A super simple lightweight NodeJS MVC framework(一个超级简单轻量的NodeJS MVC框架)",
5
5
  "main": "jj.js",
6
6
  "scripts": {},
package/types.js CHANGED
@@ -10,6 +10,7 @@
10
10
  * @typedef {typeof import('./lib/middleware')} Middleware
11
11
  * @typedef {typeof import('./lib/model')} Model
12
12
  * @typedef {typeof import('./lib/pagination')} Pagination
13
+ * @typedef {typeof import('./lib/request')} Request
13
14
  * @typedef {typeof import('./lib/response')} Response
14
15
  * @typedef {typeof import('./lib/storage')} Storage
15
16
  * @typedef {typeof import('./lib/upload')} Upload
@@ -26,6 +27,7 @@
26
27
  * @typedef {typeof import('./lib/middleware').prototype} MiddlewareInstance
27
28
  * @typedef {typeof import('./lib/model').prototype} ModelInstance
28
29
  * @typedef {typeof import('./lib/pagination').prototype} PaginationInstance
30
+ * @typedef {typeof import('./lib/request').prototype} RequestInstance
29
31
  * @typedef {typeof import('./lib/response').prototype} ResponseInstance
30
32
  * @typedef {typeof import('./lib/upload').prototype} UploadInstance
31
33
  * @typedef {typeof import('./lib/url').prototype} UrlInstance
@@ -45,6 +47,7 @@
45
47
  * @property {Middleware} Middleware
46
48
  * @property {Model} Model
47
49
  * @property {Pagination} Pagination
50
+ * @property {Request} Request
48
51
  * @property {Response} Response
49
52
  * @property {Upload} Upload
50
53
  * @property {Url} Url
@@ -67,6 +70,7 @@
67
70
  * @property {MiddlewareInstance} middleware
68
71
  * @property {ModelInstance} model
69
72
  * @property {PaginationInstance} pagination
73
+ * @property {RequestInstance} request
70
74
  * @property {ResponseInstance} response
71
75
  * @property {UploadInstance} upload
72
76
  * @property {UrlInstance} url
@@ -86,7 +90,7 @@
86
90
  * @property {string} [common_app=common] - 公共应用,存放公共模型及逻辑
87
91
  * @property {string} [controller_folder=controller] - 控制器目录名
88
92
  * @property {string} [static_dir=''] - 静态文件目录,相对于应用根目录,为空时,关闭静态访问
89
- * @property {?object} [koa_body=null] - koa-body配置参数,为null或空时,关闭koa-body
93
+ * @property {import('koa-body').IKoaBodyOptions} [koa_body=null] - koa-body配置参数,为null或空时,关闭koa-body
90
94
  * @property {string} [base_dir] - 应用根目录(会自动计算)
91
95
  */
92
96
 
@@ -96,7 +100,7 @@
96
100
  * @property {string} [view_depr='/'] - 模版文件名分割符,'/'代表二级目录
97
101
  * @property {string} [view_ext='.htm'] - 模版文件后缀
98
102
  * @property {string} [view_engine=art-template] - 默认模版引擎,字符串或引擎类
99
- * @property {object} [view_filte={}] - 模版函数
103
+ * @property {Object} [view_filte={}] - 模版函数
100
104
  */
101
105
 
102
106
  /**
@@ -112,7 +116,7 @@
112
116
  */
113
117
 
114
118
  /**
115
- * @typedef {object} DbConfig - 数据库配置
119
+ * @typedef {Object.<string, Object>} DbConfig - 数据库配置
116
120
  * @property {DbConfigItem} default - 数据库参数
117
121
  */
118
122
 
@@ -123,7 +127,7 @@
123
127
  */
124
128
 
125
129
  /**
126
- * @typedef {object} LogConfig - 日志配置
130
+ * @typedef {Object} LogConfig - 日志配置
127
131
  * @property {array} [log_level] - 允许输出的日志级别
128
132
  * @property {LogHandle} [log_handle] - 日志驱动
129
133
  */
@@ -167,6 +171,7 @@
167
171
 
168
172
  /**
169
173
  * @typedef {import('cookies').SetOption} CookieConfig - Cookie配置,一般不用设置
174
+ * @typedef {typeof import('koa').prototype.keys} Keygrip
170
175
  */
171
176
 
172
177
  /**
@@ -184,7 +189,7 @@
184
189
  * @property {CacheConfig} [cache]
185
190
  * @property {PageConfig} [page]
186
191
  * @property {RouteConfig} [routes]
187
- * @property {CookieConfig} [cookie]
192
+ * @property {CookieConfig & {keys?: String[] | Keygrip | undefined}} [cookie]
188
193
  * @property {TplConfig} [tpl]
189
194
  */
190
195
 
@@ -283,6 +288,9 @@ class Ctx {
283
288
  /** @type {(Pagination & PaginationInstance)} */
284
289
  $pagination;
285
290
 
291
+ /** @type {(Request & RequestInstance)} */
292
+ $request;
293
+
286
294
  /** @type {(Response & ResponseInstance)} */
287
295
  $response;
288
296