commit ecce207db238936ebc626893e74dde379ea18008 Author: 宇天 Date: Tue Sep 15 18:35:00 2020 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ef7335 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +.DS_Store +.AppleDouble +.LSOverride +.vscode +.idea + +node_modules/ + + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ab60297 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..ac7cf08 --- /dev/null +++ b/Readme.md @@ -0,0 +1,130 @@ +# Five.js(node-five) + +![give me five](http://attach.cdn.doui.cc/apps/five.jpg) + +一个轻量级的,易学的,拓展性灵活的 nodejs MVC 框架, 5 分钟即可上手。取自"Give me five"之意, 一切就是这么简单 + +该分支要求 nodejs 版本在 7.0 或以上,默认使用 mongoDB/MySQL,其他的数据库可以自行拓展 + +## 启用方法(步骤) + +**注** +`本框架和用法 都是在 Linux 或者 Mac 下面测试通过。至于使用 Windows 并坚持玩新技术的同学,我坚信他们一定有着过人的、` +`甚至是不可告人的兼容性 bug 处理能力,所以这部分同学麻烦在安装过程无法继续时,自行兼容一下` + +1. 下载安装 Five.js 框架。 + + * 为了方便下载安装及管理, 推荐使用 five-cli(这是一款专门为框架开发的脚本工具) 进行操作。 + +```bash +# 全局安装 five-cli +sudo npm i five-cli -g + +# 进入项目目录 +cd /project/demo +# 初始化一个项目,初始化完成会自动安装所需要的依赖 +five-cli init +# 初始化完成之后, 执行以下命令即可启动了,如果需要修改配置,可以先修改好再启动 +five-cli start +``` + + * 也可以自行通过 npm 安装, 自己构建启动配置 + +```bash +# 进入项目目录 +cd /project/demo + +npm i node-five --save +mkdir apps public data views + +touch app.js +# 自行编辑app.js, 然后通过node, pm2启动项目即可 + +``` + + +2. 配置框架 + +建立启动文件, 如 app.js + +```javascript +'use strict' + +const Five = require('five') +const app = new Five() + +app.set({ website: 'www.your_domain.com' }) +app.set({ domain: 'your_domain.com' }) // 设置域,cookie用到,不设置则同步website + +app.set({ VIEWS: './views/' }) // [可选], 但是要用到模板渲染页面时, 必须指定 +app.preload('./apps/') // [必须], 预加载应用目录 + +app.listen(3001) // 默认是3000 +``` + +其他的配置, 请参考 `文档(全局配置)` 一节 + + +3. 启动应用。在项目根目录打开终端, 输入以下命令 `five-cli start`, 然后根据提示操作, 即可 + +```bash +# 初始化完成之后, 执行以下命令即可启动了,如果需要修改配置,可以先修改好再启动 +five-cli start + +不是使用five-cli创建的项目, 可使用node/pm2等启动项目 +node app.js +# or +pm2 start app.js +``` + + +4. 添加 nginx 配置(使用其他 web 服务,如 apache 的童鞋,请自行根据所使用的 web 服务器语法改写**强烈推荐 nginx**), 路径啥的自行根据自己的机器修改 + +```nginx +upstream five_upstream { + server 127.0.0.1:3000; + #server 127.0.0.1:3005; + keepalive 64; +} + +server { + + listen 80; + server_name doui.cc; + index index.html index.htm; + root /www/doui.cc/public; + + location ~ ^/(images/|js/|css/|cache/|favicon.ico|robots.txt) { + expires 1d; + access_log off; + } + + location / { + try_files $uri + @proxy; + } + + location @proxy { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; + proxy_max_temp_file_size 0; + proxy_pass http://five_upstream; + proxy_redirect off; + proxy_read_timeout 240s; + } +} +``` + + + + +5. Enjoy you web + +## 版权说明 + +> 本框架使用 MIT 开源协议, 一切的使用,请遵循 MIT 协议。 diff --git a/index.js b/index.js new file mode 100644 index 0000000..2f37efd --- /dev/null +++ b/index.js @@ -0,0 +1,202 @@ +/** + * 框架核心 + * @authors yutent (yutent@doui.cc) + * @date 2015-11-25 18:06:14 + * + */ +'use strict' + +require('es.shim') // 加载拓展方法 +var init = require('./lib/reg-init') + +var log = console.log +var http = require('http') +var path = require('path') +var Request = require('http.request') +var Response = require('http.response') +var routerWare = require('./lib/middleware/router') +var cookieWare = require('./lib/middleware/cookie') +var sessionWare = require('./lib/middleware/session') +var credentialsWare = require('./lib/middleware/credentials') + +function hideProperty(host, name, value) { + Object.defineProperty(host, name, { + value: value, + writable: true, + enumerable: false, + configurable: true + }) +} + +class Five { + constructor() { + hideProperty(this, '__FIVE__', Object.assign({}, init)) + hideProperty(this, '__MODULES__', { __error__: null }) + hideProperty(this, '__MIDDLEWARE__', []) + hideProperty(this, '__INSTANCE__', {}) + + global.libs = { + Smarty: require('smartyx'), //模板引擎 + Log: require('./lib/module/log'), //基础日志记录工具 + Email: require('./lib/module/sendmail'), //加载email发送类 + Mysql: require('mysqli'), //加载mysql操作类 + Ioredis: require('ioredis') + } + global.Util = { + sec: require('crypto.js'), + path: require('path'), + url: require('url'), + fs: require('iofs'), + child: require('child_process') + } + global.Controller = require('./lib/controller') + } + + __init__() { + var { domain, website, session } = this.__FIVE__ + domain = domain || website + session.domain = session.domain || domain + this.set({ domain, session }) + + // 这里只创建session的存储器, 而初始化操作在中间件中进行 + if (session.type === 'redis') { + hideProperty( + this, + '__SESSION_STORE__', + new libs.Ioredis({ + host: session.db.host || '127.0.0.1', + port: session.db.port || 6379, + db: session.db.db || 0 + }) + ) + } else { + hideProperty(this, '__SESSION_STORE__', {}) + } + + // 将session和cookie的中间件提到最前 + // 以便用户自定义的中间件可以直接操作session和cookie + this.__MIDDLEWARE__.unshift(sessionWare) + this.__MIDDLEWARE__.unshift(cookieWare) + this.__MIDDLEWARE__.unshift(credentialsWare) + + this.use(routerWare) + } + + /*------------------------------------------------------------------------*/ + + // 注册属性到全局Five对象 + set(obj) { + for (let i in obj) { + if (typeof obj[i] === 'object' && !Array.isArray(obj[i])) { + if (!this.__FIVE__[i]) { + this.__FIVE__[i] = obj[i] + } else { + try { + Object.assign(this.__FIVE__[i], obj[i]) + } catch (err) { + log(err) + } + } + } else { + this.__FIVE__[i] = obj[i] + } + } + return this + } + + // 获取全局配置 + get(key) { + try { + return new Function('o', `return o.${key}`)(this.__FIVE__) + } catch (err) { + return + } + } + + // 加载中间件/缓存模块 + // 与别的中间件用法有些不一样, 回调的传入参数中的req和res, + // 并非原生的request对象和response对象, + // 而是框架内部封装过的,可通过origin属性访问原生的对象 + use(key, fn) { + if (arguments.length === 1) { + if (typeof key !== 'function') { + throw TypeError('argument 1 must be a callback') + } + this.__MIDDLEWARE__.push(key) + } else { + if (typeof key !== 'string') { + return + } + libs[key] = fn + } + } + // 预加载应用 + preload(dir) { + var list = Util.fs.ls(dir) + + if (list) { + list.forEach(file => { + var { name } = path.parse(file) + if (name.startsWith('.')) { + return + } + try { + this.__MODULES__[name] = require(file) + } catch (err) { + this.__MODULES__.__error__ = err + } + }) + } + + return this + } + + // 注册实例化对象到实例池中 + // 与use方法不同的是, 这个会在server创建之前就已经执行 + ins(name, fn) { + var _this = this + if (arguments.length === 1) { + return this.__INSTANCE__[name] + } + if (typeof fn === 'function') { + fn.call(this, this.__FIVE__, function next(instance) { + if (instance) { + _this.__INSTANCE__[name] = instance + } + }) + } + } + + // 启动http服务 + listen(port) { + var _this = this + + this.__init__() + + var server = http.createServer(function(req, res) { + var response = new Response(req, res) + var request = new Request(req, res) + + response.set('X-Powered-By', 'Five.js') + + var middleware = _this.__MIDDLEWARE__.concat() + var fn = middleware.shift() + if (fn) { + ;(async function next() { + await fn.call(_this, request, response, function() { + fn = middleware.shift() + if (fn) { + next() + } + }) + })() + } + }) + + server.listen(port || this.get('port')) + + return server + } +} + +module.exports = Five diff --git a/lib/controller.js b/lib/controller.js new file mode 100644 index 0000000..5544fad --- /dev/null +++ b/lib/controller.js @@ -0,0 +1,79 @@ +/** + * 控制器基类 + * @authors yutent (yutent@doui.cc) + * @date 2016-01-02 23:19:16 + * + */ + +'use strict' + +const smarty = new libs.Smarty() +const jwt = require('./module/jwt') + +class Controller { + constructor({ ctx, req, res }) { + this.ctx = ctx + this.name = req.app + this.request = req + this.response = res + + this.jwt = { + sign: jwt.sign.bind(this), + result: jwt.verify.call(this) + } + + smarty.config('path', this.ctx.get('VIEWS')) + smarty.config('ext', this.ctx.get('temp_ext')) + smarty.config('cache', !!this.ctx.get('temp_cache')) + + this.cookie = this.ctx.ins('cookie') + this.session = this.ctx.ins('session') + } + + //定义一个变量,类似于smarty,把该 + assign(key, val) { + key += '' + if (!key) { + return + } + + if (val === undefined || val === null) { + val = '' + } + + smarty.assign(key, val) + } + + //模板渲染, 参数是模板名, 可不带后缀, 默认是 .tpl + render(file, noParse = false) { + smarty + .render(file, noParse) + .then(html => { + this.response.render(html) + }) + .catch(err => { + this.response.error(err) + }) + } + + // RESFULL-API规范的纯API返回 + send(status = 200, msg = 'success', data = {}) { + if (typeof msg === 'object') { + data = msg + msg = 'success' + } + this.response.send(status, msg, data) + } + + //针对框架定制的debug信息输出 + xdebug(err) { + let msg = err + if (this.ctx.get('debug')) { + msg = err.stack || err + } + + this.response.append('X-debug', msg + '') + } +} + +module.exports = Controller diff --git a/lib/middleware/cookie.js b/lib/middleware/cookie.js new file mode 100644 index 0000000..b2339c2 --- /dev/null +++ b/lib/middleware/cookie.js @@ -0,0 +1,38 @@ +/** + * + * @authors yutent (yutent@doui.cc) + * @date 2018-05-26 00:01:00 + * @version $Id$ + */ + +const Cookie = require('http.cookie') + +module.exports = function(req, res, next) { + var cookie = new Cookie(req.origin.req, req.origin.res) + var domain = this.get('domain') + this.__INSTANCE__.cookie = function(key, val, opt) { + if (typeof key !== 'string') { + throw new Error( + `argument key must be a string in cookie() ${typeof key} given` + ) + } + + if (arguments.length === 1) { + return cookie.get(key) + } + + if (!opt) { + opt = {} + } + opt.domain = opt.domain || domain + + val += '' + + if (!val) { + opt.expires = opt.maxAge = -1 + } + + cookie.set(key, val, opt) + } + next() +} diff --git a/lib/middleware/credentials.js b/lib/middleware/credentials.js new file mode 100644 index 0000000..06eb369 --- /dev/null +++ b/lib/middleware/credentials.js @@ -0,0 +1,37 @@ +/** + * + * @authors yutent (yutent@doui.cc) + * @date 2018-09-03 22:26:51 + */ + +'use strict' + +module.exports = function(req, res, next) { + var supportCredentials = this.get('supportCredentials') + var credentialsRule = this.get('credentialsRule') + var credentialsMaxAge = this.get('credentialsMaxAge') + + if (supportCredentials) { + var origin = req.header('origin') || req.header('referer') || '' + var headers = req.header('access-control-request-headers') + origin = Util.url.parse(origin) + + if (credentialsRule && origin.hostname) { + if (!credentialsRule.test(origin.hostname)) { + return res.end('') + } + } + res.set('Access-Control-Allow-Credentials', 'true') + res.set('Access-Control-Allow-Origin', `${origin.protocol}//${origin.host}`) + if (headers) { + res.set('Access-Control-Allow-Headers', headers) + } + if (credentialsMaxAge) { + res.set('Access-Control-Max-Age', credentialsMaxAge) + } + if (req.method === 'OPTIONS') { + return res.end('') + } + } + next() +} diff --git a/lib/middleware/router.js b/lib/middleware/router.js new file mode 100644 index 0000000..e09e083 --- /dev/null +++ b/lib/middleware/router.js @@ -0,0 +1,53 @@ +/** + * 路由 + * @authors yutent (yutent@doui.cc) + * @date 2015-10-01 19:11:19 + * + */ +'use strict' + +module.exports = function(req, res, next) { + if (!this.__MODULES__[req.app]) { + if (!this.__MODULES__.__error__) { + res.error(`The app [${req.app}] not found`, 404) + } else { + res.error( + this.get('debug') + ? this.__MODULES__.__error__.stack + : this.__MODULES__.__error__, + 500 + ) + } + return + } + + try { + if (req.path.length < 1) { + req.path.push('index') + } + + var app = new this.__MODULES__[req.app]({ ctx: this, req, res }) + + if (this.get('routeMode') === 1) { + var act = req.path.shift() + + if (app[act + 'Action']) { + app[act + 'Action'].apply(app, req.path).catch(err => { + res.error(this.get('debug') ? err.stack || err : err, 500) + }) + } else { + res.error(`Action[${act}] not found`, 404) + } + } else { + if (app.indexAction) { + app.indexAction.apply(app, req.path).catch(err => { + res.error(this.get('debug') ? err.stack || err : err, 500) + }) + } else { + res.error(`Default Action not found`, 404) + } + } + } catch (err) { + res.error(this.get('debug') ? err.stack || err : err, 500) + } +} diff --git a/lib/middleware/session.js b/lib/middleware/session.js new file mode 100644 index 0000000..e25467b --- /dev/null +++ b/lib/middleware/session.js @@ -0,0 +1,65 @@ +/** + * + * @authors yutent (yutent@doui.cc) + * @date 2018-07-26 15:50:25 + * @version $Id$ + */ +const redisStore = require('../module/redis-store') +const nativeStore = require('../module/native-store') + +module.exports = function(req, res, next) { + var opt = this.get('session') + var jwt = this.get('jwt') + var cookie = this.ins('cookie') + var session = null + var uuid = Util.sec.uuid() + var ssid = '' + + opt.jwt = jwt + + if (req.method === 'OPTIONS') { + return next() + } + + if (jwt) { + var auth = req.header('authorization') + if (auth) { + ssid = auth.split('.').pop() + uuid = auth + } + } else { + ssid = cookie('NODESSID') + // 校验级别为1, 则混入ua + if (opt.level > 0) { + uuid += req.header('user-agent') + } + // 校验级别为2, 则混入ip + if (opt.level > 1) { + uuid += req.ip() + } + } + uuid = Util.sec.sha1(uuid) + + if (opt.type === 'redis') { + session = new redisStore(this.__SESSION_STORE__, opt, uuid) + } else { + session = new nativeStore(this.__SESSION_STORE__, opt, uuid) + } + + // 启用SESSION + // ssid非法或过期时,需要重写 + if (!ssid || ssid !== session.start(ssid)) { + ssid = session.start(ssid) + if (!jwt) { + cookie('NODESSID', ssid, { + httpOnly: true, + expires: opt.ttl, + domain: opt.domain + }) + } + } + + this.__INSTANCE__.session = session + + next() +} diff --git a/lib/module/jwt.js b/lib/module/jwt.js new file mode 100644 index 0000000..64bc0c5 --- /dev/null +++ b/lib/module/jwt.js @@ -0,0 +1,63 @@ +/** + * + * @authors yutent (yutent@doui.cc) + * @date 2018-08-24 15:24:56 + */ + +'use strict' + +const sha256 = (str, secret) => { + return Util.sec + .hmac('sha256', str, secret, 'base64') + .replace(/[+\/]/g, m => { + return m === '+' ? '-' : '_' + }) + .replace(/=/g, '') +} + +const sign = function(token) { + // "{"typ":"JWT","alg":"HS256"}" + // 这里固定使用sha256 + var header = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' + var opt = this.ctx.get('session') + var secret = this.ctx.get('jwt') + // 加入过期时间, 同session.ttl + var payload = { token, expires: Date.now() + opt.ttl * 1000 } + payload = JSON.stringify(payload) + + payload = Util.sec.base64encode(payload, true) + var auth = sha256(`${header}.${payload}`, secret) + this.ctx.ins('session').start(auth) + return `${header}.${payload}.${auth}` +} + +const verify = function() { + var jwt = this.request.header('authorization') || '' + var secret = this.ctx.get('jwt') + var auth, token, payload + + jwt = jwt.split('.') + + if (!secret || jwt.length !== 3) { + return false + } + + auth = jwt.pop() + token = JSON.parse(Util.sec.base64decode(jwt[1], true)) + + // 如果已经过期, 则不再校验hash + if (Date.now() > token.expires) { + return 'expired' + } + payload = jwt.join('.') + + if (sha256(payload, secret) === auth) { + return token.token + } + return false +} + +module.exports = { + verify, + sign +} diff --git a/lib/module/log.js b/lib/module/log.js new file mode 100644 index 0000000..baa92f0 --- /dev/null +++ b/lib/module/log.js @@ -0,0 +1,48 @@ +/** + * + * @authors yutent (yutent@doui.cc) + * @date 2015-11-25 17:48:17 + * + */ +'use strict' + +class Log { + constructor(file = 'run_time.log', dir) { + if (!dir) { + throw new Error(`agument dir must be a string, but ${typeof dir} given.`) + } + + if (!Util.fs.exists(dir)) { + Util.fs.mkdir(dir) + } + + this.file = Util.path.resolve(dir, file) + } + + error(str) { + this.save(str, 'error') + } + + info(str) { + this.save(str, 'info') + } + + warn(str) { + this.save(str, 'warning') + } + + debug(str) { + this.save(str, 'debug') + } + + //写入日志文件 + save(str, type) { + type = type || 'debug' + Util.fs.origin.appendFile( + `[${type}] ${new Date().format('Y-m-d_H:i:s')} ${str} \n`, + this.file + ) + } +} + +module.exports = Log diff --git a/lib/module/native-store.js b/lib/module/native-store.js new file mode 100644 index 0000000..09bd956 --- /dev/null +++ b/lib/module/native-store.js @@ -0,0 +1,83 @@ +/** + * + * @authors yutent (yutent@doui.cc) + * @date 2016-03-15 16:01:38 + * + */ +'use strict' + +function hideProperty(host, name, value) { + Object.defineProperty(host, name, { + value: value, + writable: true, + enumerable: false, + configurable: true + }) +} + +class Session { + constructor(store, opt, uuid) { + this.opt = opt + this.uuid = uuid + this.store = store + } + + createSsid(ssid) { + if (ssid) { + if (!this.opt.jwt && this.opt.level > 0 && ssid !== this.uuid) { + ssid = this.uuid + } + } else { + ssid = this.uuid + } + this.ssid = ssid + + if ( + !this.store.hasOwnProperty(ssid) || + this.store[ssid].__EXPIRES__ < Date.now() + ) { + this.store[ssid] = {} + } + //设置session有效期 + hideProperty( + this.store[ssid], + '__EXPIRES__', + Date.now() + this.opt.ttl * 1000 + ) + } + + start(ssid) { + this.createSsid(ssid) + return this.ssid + } + + // 获取session字段值 + get(key) { + return key ? this.store[this.ssid][key] || null : this.store[this.ssid] + } + + // 设置session字段值 + set(key, val) { + if (typeof key === 'object') { + for (let i in key) { + this.store[this.ssid][i] = key[i] + } + } else { + this.store[this.ssid][key] = val + } + } + + // 删除单个字段 + unset(key) { + if (this.store[this.ssid].hasOwnProperty(key)) { + delete this.store[this.ssid][key] + } + } + + // 清除个人session + clear() { + this.store[this.ssid] = {} + } +} + +module.exports = Session diff --git a/lib/module/redis-store.js b/lib/module/redis-store.js new file mode 100644 index 0000000..1578697 --- /dev/null +++ b/lib/module/redis-store.js @@ -0,0 +1,83 @@ +/** + * Session类, 存入store + * @authors yutent (yutent@doui.cc) + * @date 2016-03-14 16:08:57 + * + */ +'use strict' + +class Session { + constructor(store, opt, uuid) { + this.store = store + this.opt = opt + this.uuid = uuid + } + + createSsid(ssid) { + if (ssid) { + if (!this.opt.jwt && this.opt.level > 0 && ssid !== this.uuid) { + ssid = this.uuid + } + } else { + ssid = this.uuid + } + + this.ssid = ssid + // 设置session有效期 + this.store.expire(ssid, this.opt.ttl) + } + + start(ssid) { + this.createSsid(ssid) + return this.ssid + } + + // 获取session字段值, 需要await指令 + get(key) { + var defer = Promise.defer() + + this.store.hgetall(this.ssid, (err, obj) => { + if (err) { + return defer.reject(err) + } + + for (let i in obj) { + if (!obj[i]) { + continue + } + + obj[i] = Number.parse(obj[i]) + } + //不传key时,直接返回全部字段 + if (!key) { + defer.resolve(obj) + } else { + defer.resolve(obj.hasOwnProperty(key) ? obj[key] : null) + } + }) + return defer.promise + } + + //设置session字段值 + set(key, val) { + if (typeof key === 'object') { + for (let i in key) { + this.store.hset(this.ssid, i, key[i]) + } + } else { + this.store.hset(this.ssid, key, val) + } + } + + //删除单个字段 + unset(key) { + this.store.hdel(this.ssid, key) + } + + //清除个人session + clear() { + this.store.del(this.ssid) + } +} + +module.exports = Session diff --git a/lib/module/sendmail.js b/lib/module/sendmail.js new file mode 100644 index 0000000..818cda3 --- /dev/null +++ b/lib/module/sendmail.js @@ -0,0 +1,48 @@ +/** + * + * @authors yutent (yutent@doui.cc) + * @date 2015-11-27 10:50:16 + * + */ +'use strict' + +const mailx = require('mailx') +class Sendmail { + constructor({ host, port, mail, passwd } = {}) { + if (!host || !port || !mail || !passwd) { + throw new Error('smtp options [host, port, mail, passwd] is required.') + } + this.smtp = mailx.transport(host, port, mail, passwd) + this.mail = mailx.message() + } + + // 发件人 + from(info) { + this.mail.setFrom(info.name, info.mail) + return this + } + + // 收件人 + to(info) { + this.mail.addTo(info.name, info.mail) + + return this + } + + // 发送正文 + send(mail) { + this.mail.setSubject(mail.subject) + this.mail.setHtml(mail.content) + var defer = Promise.defer() + this.smtp.send(this.mail, function(err, res) { + if (err) { + defer.reject(err) + } else { + defer.resolve(res) + } + }) + return defer.promise + } +} + +module.exports = Sendmail diff --git a/lib/reg-init.js b/lib/reg-init.js new file mode 100644 index 0000000..38419e1 --- /dev/null +++ b/lib/reg-init.js @@ -0,0 +1,52 @@ +/** + * + * @authors yutent (yutent@doui.cc) + * @date 2016-05-15 14:37:47 + * + */ + +'use strict' + +const init = { + db: {}, + session: { + type: 'native', // native 或 redis + ttl: 3600 * 24 * 7, + domain: '', // NODESSID域, 默认等于domain + level: 0, // 校验级别, 0: 不校验客户端, 1: 校验UA, 2: 校验UA+IP + db: { + host: '127.0.0.1', + port: 6379, + db: 0 + } + }, + website: 'localhost', + domain: '', // cookie域, 默认等于website + port: 3000, + routeMode: 1, + env: process.env.NODE_ENV === 'production' ? 'production' : 'development', + debug: process.env.NODE_ENV !== 'production', // debug模式 + smtp: { + host: 'smtp.example.com', + port: 25, + mail: 'service@example.com', + name: '系统邮件', + passwd: '' + }, + supportCredentials: false, + credentialsRule: null, + credentialsMaxAge: 0, + jwt: null, // jwt secret + regexp: { + // 常用正则 + email: /^[\w\.\-]+@\w+([\.\-]\w+)*\.\w+$/, + uname: /^[A-Za-z\d_]{4,16}$/, + passwd: /^[\S]{6,20}$/, + phone: /^1[34578]\d{9}$/, + idCard: /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X|x)$/, + CN: /^[\u4e00-\u9fa5]+$/, + qq: /^\d{5,12}$/ + } +} + +module.exports = init diff --git a/package.json b/package.json new file mode 100644 index 0000000..665f40d --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "node-five", + "version": "3.2.6", + "type": "module", + "description": "Five.js, 一个轻量级的nodejs mvc框架 旨在简单易用, 5分钟即可上手", + "author": "宇天 ", + "main": "index.js", + "dependencies": { + "crypto.js": "^1.3.1", + "es.shim": "^1.1.2", + "iofs": "^1.3.2", + "mysqli": "^3.0.11", + "http.request": "^1.1.0", + "http.response": "^1.0.2", + "http.cookie": "^1.0.2", + "smartyx": "^1.3.1", + "ioredis": "^3.2.2", + "mailx": "^0.0.11" + }, + "devDependencies": {}, + "repository": { + "type": "git", + "url": "https://github.com/yutent/five.git" + }, + "keywords": ["five, fivejs, node-five, five.js, nodejs, mvc, koa, express"], + "engines": { + "node": ">=8.0.0" + }, + "license": "MIT" +}