| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var fs = require('fs'); |
| 2 | 1 | var path = require('path'); |
| 3 | 1 | var util = require('util'); |
| 4 | 1 | var crypto = require('crypto'); |
| 5 | 1 | var net = require('net'); |
| 6 | ||
| 7 | /** | |
| 8 | * 由于非常依赖promise,所以将promise设置为全局变量 | |
| 9 | * @type {[type]} | |
| 10 | */ | |
| 11 | 1 | global.Promise = require('es6-promise').Promise; |
| 12 | ||
| 13 | /** | |
| 14 | * 动态创建一个类 | |
| 15 | * 提供了继承、扩展、调用父级别方法等方法 | |
| 16 | * @return {[type]} [description] | |
| 17 | */ | |
| 18 | 1 | global.Class = function (prop, superCls) { |
| 19 | 35 | 'use strict'; |
| 20 | 35 | var cls = function () { |
| 21 | 15 | function T(args) { |
| 22 | 15 | for(var name in cls.__prop){ |
| 23 | 23 | var val = cls.__prop[name]; |
| 24 | 23 | this[name] = isObject(val) ? extend({}, val) : val; |
| 25 | } | |
| 26 | //自动执行init方法 | |
| 27 | 15 | if(isFunction(this.init)){ |
| 28 | //获取init返回值,如果返回一个promise,可以让后续执行在then之后 | |
| 29 | 15 | this.__initReturn = this.init.apply(this, args); |
| 30 | } | |
| 31 | 15 | return this; |
| 32 | } | |
| 33 | 15 | T.prototype = cls.prototype; |
| 34 | 15 | T.constructor = cls; |
| 35 | 15 | return new T(arguments); |
| 36 | }; | |
| 37 | //类的属性,不放在原型上,实例化的时候调用 | |
| 38 | 35 | cls.__prop = {}; |
| 39 | 35 | cls.extend = function(prop){ |
| 40 | 36 | if (isFunction(prop)) { |
| 41 | 34 | prop = prop(); |
| 42 | } | |
| 43 | 36 | if (isObject(prop)) { |
| 44 | 34 | for(var name in prop){ |
| 45 | 353 | var val = prop[name]; |
| 46 | 353 | if (isFunction(val)) { |
| 47 | 308 | this.prototype[name] = val; |
| 48 | }else{ | |
| 49 | 45 | cls.__prop[name] = isObject(val) ? extend({}, val) : val; |
| 50 | } | |
| 51 | } | |
| 52 | } | |
| 53 | 36 | return this; |
| 54 | }; | |
| 55 | 35 | cls.inherits = function(superCls){ |
| 56 | 22 | util.inherits(this, superCls); |
| 57 | //将父级的属性复制到当前类上 | |
| 58 | 22 | extend(cls.__prop, superCls.__prop); |
| 59 | 22 | return this; |
| 60 | }; | |
| 61 | 35 | if (superCls === true && isFunction(prop)) { |
| 62 | 1 | superCls = prop; |
| 63 | 1 | prop = undefined; |
| 64 | } | |
| 65 | 35 | if (isFunction(superCls)) { |
| 66 | 22 | cls.inherits(superCls); |
| 67 | } | |
| 68 | //调用父级方法 | |
| 69 | 35 | cls.prototype.super = cls.prototype.super_ = function(name, data){ |
| 70 | //如果当前类没有这个方法,则直接返回。 | |
| 71 | //用于在a方法调用父级的b方法 | |
| 72 | 6 | if (!this[name]) { |
| 73 | 1 | return; |
| 74 | } | |
| 75 | 5 | var super_ = this.constructor.super_; |
| 76 | //如果父级没有这个方法,那么直接返回 | |
| 77 | 5 | if (!isFunction(super_.prototype[name])) { |
| 78 | 1 | return; |
| 79 | } | |
| 80 | //如果参数不是数组,自动转为数组 | |
| 81 | 4 | if (!isArray(data)) { |
| 82 | 4 | data = [data]; |
| 83 | } | |
| 84 | 4 | while(1){ |
| 85 | 5 | if (this[name] === super_.prototype[name] && super_.super_) { |
| 86 | 1 | super_ = super_.super_; |
| 87 | }else{ | |
| 88 | 4 | break; |
| 89 | } | |
| 90 | } | |
| 91 | 4 | var method = super_.prototype[name]; |
| 92 | 4 | delete super_.prototype[name]; |
| 93 | 4 | var ret = method.apply(this, data); |
| 94 | 4 | super_.prototype[name] = method; |
| 95 | 4 | return ret; |
| 96 | }; | |
| 97 | 35 | if (prop) { |
| 98 | 34 | cls.extend(prop); |
| 99 | } | |
| 100 | 35 | return cls; |
| 101 | }; | |
| 102 | /** | |
| 103 | * extend, from jquery,具有深度复制功能 | |
| 104 | * @return {[type]} [description] | |
| 105 | */ | |
| 106 | 1 | global.extend = function(){ |
| 107 | 104 | 'use strict'; |
| 108 | 104 | var args = [].slice.call(arguments); |
| 109 | 104 | var deep = true; |
| 110 | 104 | var target = args.shift(); |
| 111 | 104 | if (isBoolean(target)) { |
| 112 | 40 | deep = target; |
| 113 | 40 | target = args.shift(); |
| 114 | } | |
| 115 | 104 | target = target || {}; |
| 116 | 104 | var length = args.length; |
| 117 | 104 | var options, name, src, copy, copyAsArray, clone; |
| 118 | 104 | for(var i = 0; i < length; i++){ |
| 119 | 104 | options = args[i] || {}; |
| 120 | 104 | if (isFunction(options)) { |
| 121 | 1 | options = options(); |
| 122 | } | |
| 123 | 104 | for(name in options){ |
| 124 | 240 | src = target[name]; |
| 125 | 240 | copy = options[name]; |
| 126 | 240 | if (src === copy) { |
| 127 | 26 | continue; |
| 128 | } | |
| 129 | 214 | if (deep && copy && (isObject(copy) || (copyAsArray = isArray(copy) ))) { |
| 130 | 38 | if (copyAsArray) { |
| 131 | 12 | copyAsArray = false; |
| 132 | 12 | clone = src && isArray(src) ? src : []; |
| 133 | }else{ | |
| 134 | 26 | clone = src && isObject(src) ? src : {}; |
| 135 | } | |
| 136 | 38 | target[name] = extend(deep, clone, copy); |
| 137 | 176 | }else if (copy !== undefined) { |
| 138 | 176 | target[name] = copy; |
| 139 | } | |
| 140 | } | |
| 141 | } | |
| 142 | 104 | return target; |
| 143 | }; | |
| 144 | ||
| 145 | ||
| 146 | //Object上toString方法 | |
| 147 | 1 | var toString = Object.prototype.toString; |
| 148 | ||
| 149 | /** | |
| 150 | * 是否是boolean | |
| 151 | * @param {[type]} obj | |
| 152 | * @return {Boolean} | |
| 153 | */ | |
| 154 | 1 | global.isBoolean = function(obj){ |
| 155 | 115 | 'use strict'; |
| 156 | 115 | return toString.call(obj) === '[object Boolean]'; |
| 157 | }; | |
| 158 | /** | |
| 159 | * 是否是数字 | |
| 160 | * @param {[type]} obj [description] | |
| 161 | * @return {Boolean} [description] | |
| 162 | */ | |
| 163 | 1 | global.isNumber = function(obj){ |
| 164 | 18 | 'use strict'; |
| 165 | 18 | return toString.call(obj) === '[object Number]'; |
| 166 | }; | |
| 167 | /** | |
| 168 | * 是否是个对象 | |
| 169 | * @param {[type]} obj [description] | |
| 170 | * @return {Boolean} [description] | |
| 171 | */ | |
| 172 | 1 | global.isObject = function(obj){ |
| 173 | 288 | 'use strict'; |
| 174 | 288 | return toString.call(obj) === '[object Object]'; |
| 175 | }; | |
| 176 | /** | |
| 177 | * 是否是字符串 | |
| 178 | * @param {[type]} obj [description] | |
| 179 | * @return {Boolean} [description] | |
| 180 | */ | |
| 181 | 1 | global.isString = function(obj){ |
| 182 | 201 | 'use strict'; |
| 183 | 201 | return toString.call(obj) === '[object String]'; |
| 184 | }; | |
| 185 | /** | |
| 186 | * 是否是个function | |
| 187 | * @param {[type]} obj [description] | |
| 188 | * @return {Boolean} [description] | |
| 189 | */ | |
| 190 | 1 | global.isFunction = function(obj){ |
| 191 | 670 | 'use strict'; |
| 192 | 670 | return typeof obj === 'function'; |
| 193 | }; | |
| 194 | /** | |
| 195 | * 是否是日期 | |
| 196 | * @return {Boolean} [description] | |
| 197 | */ | |
| 198 | 1 | global.isDate = function(obj){ |
| 199 | 2 | 'use strict'; |
| 200 | 2 | return util.isDate(obj); |
| 201 | }; | |
| 202 | /** | |
| 203 | * 是否是正则 | |
| 204 | * @param {[type]} reg [description] | |
| 205 | * @return {Boolean} [description] | |
| 206 | */ | |
| 207 | 1 | global.isRegexp = function(obj){ |
| 208 | 2 | 'use strict'; |
| 209 | 2 | return util.isRegExp(obj); |
| 210 | }; | |
| 211 | /** | |
| 212 | * 是否是个错误 | |
| 213 | * @param {[type]} obj [description] | |
| 214 | * @return {Boolean} [description] | |
| 215 | */ | |
| 216 | 1 | global.isError = function(obj){ |
| 217 | 2 | 'use strict'; |
| 218 | 2 | return util.isError(obj); |
| 219 | }; | |
| 220 | /** | |
| 221 | * 判断对象是否为空 | |
| 222 | * @param {[type]} obj | |
| 223 | * @return {Boolean} | |
| 224 | */ | |
| 225 | 1 | global.isEmpty = function(obj){ |
| 226 | 12 | 'use strict'; |
| 227 | 12 | if (isObject(obj)) { |
| 228 | 2 | var key; |
| 229 | 2 | for(key in obj){ |
| 230 | 1 | return false; |
| 231 | } | |
| 232 | 1 | return true; |
| 233 | 10 | }else if (isArray(obj)) { |
| 234 | 1 | return obj.length === 0; |
| 235 | 9 | }else if (isString(obj)) { |
| 236 | 2 | return obj.length === 0; |
| 237 | 7 | }else if (isNumber(obj)) { |
| 238 | 2 | return obj === 0; |
| 239 | 5 | }else if (obj === null || obj === undefined) { |
| 240 | 2 | return true; |
| 241 | 3 | }else if (isBoolean(obj)) { |
| 242 | 2 | return !obj; |
| 243 | } | |
| 244 | 1 | return false; |
| 245 | }; | |
| 246 | /** | |
| 247 | * 是否是个标量 | |
| 248 | * @param {[type]} obj [description] | |
| 249 | * @return {Boolean} [description] | |
| 250 | */ | |
| 251 | 1 | global.isScalar = function(obj){ |
| 252 | 1 | 'use strict'; |
| 253 | 1 | return isBoolean(obj) || isNumber(obj) || isString(obj); |
| 254 | }; | |
| 255 | /** | |
| 256 | * 是否是个数组 | |
| 257 | * @type {Boolean} | |
| 258 | */ | |
| 259 | 1 | global.isArray = Array.isArray; |
| 260 | /** | |
| 261 | * 是否是IP | |
| 262 | * @type {Boolean} | |
| 263 | */ | |
| 264 | 1 | global.isIP = net.isIP; |
| 265 | 1 | global.isIP4 = net.isIP4; |
| 266 | 1 | global.isIP6 = net.isIP6; |
| 267 | /** | |
| 268 | * 是否是个文件 | |
| 269 | * @param {[type]} p [description] | |
| 270 | * @return {Boolean} [description] | |
| 271 | */ | |
| 272 | 1 | global.isFile = function(p){ |
| 273 | 49 | 'use strict'; |
| 274 | 49 | if (!fs.existsSync(p)) { |
| 275 | 24 | return false; |
| 276 | } | |
| 277 | 25 | var stats = fs.statSync(p); |
| 278 | 25 | return stats.isFile(); |
| 279 | }; | |
| 280 | /** | |
| 281 | * 是否是个目录 | |
| 282 | * @param {[type]} p [description] | |
| 283 | * @return {Boolean} [description] | |
| 284 | */ | |
| 285 | 1 | global.isDir = function(p){ |
| 286 | 3 | 'use strict'; |
| 287 | 3 | if (!fs.existsSync(p)) { |
| 288 | 1 | return false; |
| 289 | } | |
| 290 | 2 | var stats = fs.statSync(p); |
| 291 | 2 | return stats.isDirectory(); |
| 292 | }; | |
| 293 | /** | |
| 294 | * 是否是buffer | |
| 295 | * @type {Boolean} | |
| 296 | */ | |
| 297 | 1 | global.isBuffer = Buffer.isBuffer; |
| 298 | /** | |
| 299 | * 是否是个数字的字符串 | |
| 300 | * @param {[type]} obj [description] | |
| 301 | * @return {Boolean} [description] | |
| 302 | */ | |
| 303 | 1 | var numberReg = /^((\d*\.?\d*(?:e[+-]?\d*(?:\d?\.?|\.?\d?)\d*)?)|(0[0-7]+)|(0x[0-9a-f]+))$/i; |
| 304 | 1 | global.isNumberString = function(obj){ |
| 305 | 3 | 'use strict'; |
| 306 | 3 | return numberReg.test(obj); |
| 307 | }; | |
| 308 | /** | |
| 309 | * 判断是否是个promise | |
| 310 | * @param {[type]} obj [description] | |
| 311 | * @return {Boolean} [description] | |
| 312 | */ | |
| 313 | 1 | global.isPromise = function(obj){ |
| 314 | 33 | 'use strict'; |
| 315 | 33 | return !!(obj && typeof obj.then === 'function'); |
| 316 | }; | |
| 317 | ||
| 318 | /** | |
| 319 | * 判断一个文件或者目录是否可写 | |
| 320 | * @param {[type]} p [description] | |
| 321 | * @return {Boolean} [description] | |
| 322 | */ | |
| 323 | 1 | global.isWritable = function(p){ |
| 324 | 3 | 'use strict'; |
| 325 | 3 | if (!fs.existsSync(p)) { |
| 326 | 1 | return false; |
| 327 | } | |
| 328 | 2 | var stats = fs.statSync(p); |
| 329 | 2 | var mode = stats.mode; |
| 330 | 2 | var uid = process.getuid ? process.getuid() : 0; |
| 331 | 2 | var gid = process.getgid ? process.getgid() : 0; |
| 332 | 2 | var owner = uid === stats.uid; |
| 333 | 2 | var group = gid === stats.gid; |
| 334 | 2 | return !!(owner && (mode & parseInt('00200', 8)) || |
| 335 | group && (mode & parseInt('00020', 8)) || | |
| 336 | (mode & parseInt('00002', 8))); | |
| 337 | }; | |
| 338 | ||
| 339 | /** | |
| 340 | * 递归创建目录,同步模式 | |
| 341 | * @param {[type]} p [description] | |
| 342 | * @param {[type]} mode [description] | |
| 343 | * @return {[type]} [description] | |
| 344 | */ | |
| 345 | 1 | global.mkdir = function(p, mode){ |
| 346 | 1 | 'use strict'; |
| 347 | 1 | mode = mode || '0777'; |
| 348 | 1 | if (fs.existsSync(p)) { |
| 349 | 0 | chmod(p, mode); |
| 350 | 0 | return true; |
| 351 | } | |
| 352 | 1 | var pp = path.dirname(p); |
| 353 | 1 | if (fs.existsSync(pp)) { |
| 354 | 1 | fs.mkdirSync(p, mode); |
| 355 | }else{ | |
| 356 | 0 | mkdir(pp, mode); |
| 357 | 0 | mkdir(p, mode); |
| 358 | } | |
| 359 | 1 | return true; |
| 360 | }; | |
| 361 | /** | |
| 362 | * 修改目录或者文件权限 | |
| 363 | * @param {[type]} p [description] | |
| 364 | * @param {[type]} mode [description] | |
| 365 | * @return {[type]} [description] | |
| 366 | */ | |
| 367 | 1 | global.chmod = function(p, mode){ |
| 368 | 0 | 'use strict'; |
| 369 | 0 | mode = mode || '0777'; |
| 370 | 0 | if (!fs.existsSync(p)) { |
| 371 | 0 | return true; |
| 372 | } | |
| 373 | 0 | return fs.chmodSync(p, mode); |
| 374 | }; | |
| 375 | /** | |
| 376 | * 获取文件内容 | |
| 377 | * @param {[type]} file [description] | |
| 378 | * @return {[type]} [description] | |
| 379 | */ | |
| 380 | 1 | global.getFileContent = function(file, encoding){ |
| 381 | 1 | 'use strict'; |
| 382 | 1 | if (!fs.existsSync(file)) { |
| 383 | 0 | return ''; |
| 384 | } | |
| 385 | 1 | return fs.readFileSync(file, { |
| 386 | encoding: encoding || 'utf8' | |
| 387 | }); | |
| 388 | }; | |
| 389 | /** | |
| 390 | * 设置文件内容 | |
| 391 | * @param {[type]} file [description] | |
| 392 | * @param {[type]} data [description] | |
| 393 | * @return {[type]} [description] | |
| 394 | */ | |
| 395 | 1 | global.setFileContent = function(file, data){ |
| 396 | 0 | 'use strict'; |
| 397 | 0 | return fs.writeFileSync(file, data); |
| 398 | }; | |
| 399 | /** | |
| 400 | * 大写首字符 | |
| 401 | * @param {[type]} name [description] | |
| 402 | * @return {[type]} [description] | |
| 403 | */ | |
| 404 | 1 | global.ucfirst = function(name){ |
| 405 | 11 | 'use strict'; |
| 406 | 11 | name = (name || '') + ''; |
| 407 | 11 | return name.substr(0,1).toUpperCase() + name.substr(1).toLowerCase(); |
| 408 | }; | |
| 409 | /** | |
| 410 | * 获取字符串的md5 | |
| 411 | * @param {[type]} str [description] | |
| 412 | * @return {[type]} [description] | |
| 413 | */ | |
| 414 | 1 | global.md5 = function(str){ |
| 415 | 2 | 'use strict'; |
| 416 | 2 | var instance = crypto.createHash('md5'); |
| 417 | 2 | instance.update(str + ''); |
| 418 | 2 | return instance.digest('hex'); |
| 419 | }; | |
| 420 | /** | |
| 421 | * 生成一个promise,如果传入的参数是promise则直接返回 | |
| 422 | * @param {[type]} obj [description] | |
| 423 | * @return {[type]} [description] | |
| 424 | */ | |
| 425 | 1 | global.getPromise = function(obj, reject){ |
| 426 | 28 | 'use strict'; |
| 427 | 28 | if (isPromise(obj)) { |
| 428 | 7 | return obj; |
| 429 | } | |
| 430 | 21 | if (reject) { |
| 431 | 2 | return Promise.reject(obj); |
| 432 | } | |
| 433 | 19 | return Promise.resolve(obj); |
| 434 | }; | |
| 435 | /** | |
| 436 | * 生成一个defer对象 | |
| 437 | * @return {[type]} [description] | |
| 438 | */ | |
| 439 | 1 | global.getDefer = function(){ |
| 440 | 2 | 'use strict'; |
| 441 | 2 | var deferred = {}; |
| 442 | 2 | deferred.promise = new Promise(function(resolve, reject){ |
| 443 | 2 | deferred.resolve = resolve; |
| 444 | 2 | deferred.reject = reject; |
| 445 | }); | |
| 446 | 2 | return deferred; |
| 447 | }; | |
| 448 | /** | |
| 449 | * 快速生成一个object | |
| 450 | * @param {[type]} key [description] | |
| 451 | * @param {[type]} value [description] | |
| 452 | * @return {[type]} [description] | |
| 453 | */ | |
| 454 | 1 | global.getObject = function(key, value){ |
| 455 | 5 | 'use strict'; |
| 456 | 5 | var obj = {}; |
| 457 | 5 | if (!isArray(key)) { |
| 458 | 2 | obj[key] = value; |
| 459 | 2 | return obj; |
| 460 | } | |
| 461 | 3 | key.forEach(function(item, i){ |
| 462 | 5 | obj[item] = value[i]; |
| 463 | }); | |
| 464 | 3 | return obj; |
| 465 | }; | |
| 466 | /** | |
| 467 | * 将数组变成对象 | |
| 468 | * @param {[type]} arr [description] | |
| 469 | * @param {[type]} key [description] | |
| 470 | * @param {[type]} valueKeys [description] | |
| 471 | * @return {[type]} [description] | |
| 472 | */ | |
| 473 | 1 | global.arrToObj = function(arr, key, valueKey){ |
| 474 | 4 | 'use strict'; |
| 475 | 4 | var result = {}; |
| 476 | 4 | var arrResult = []; |
| 477 | 4 | arr.forEach(function(item){ |
| 478 | 8 | var keyValue = item[key]; |
| 479 | 8 | if (valueKey === null) { |
| 480 | 4 | arrResult.push(keyValue); |
| 481 | 4 | }else if (valueKey) { |
| 482 | 2 | result[keyValue] = item[valueKey]; |
| 483 | }else{ | |
| 484 | 2 | result[keyValue] = item; |
| 485 | } | |
| 486 | }) | |
| 487 | 4 | return valueKey === null ? arrResult : result; |
| 488 | } |
| Line | Hits | Source |
|---|---|---|
| 1 | //该文件内容为原生对象的扩展 | |
| 2 | ||
| 3 | /** | |
| 4 | * 获取对象的值 | |
| 5 | * @param {[type]} obj [description] | |
| 6 | * @return {[type]} [description] | |
| 7 | */ | |
| 8 | 1 | Object.values = function(obj){ |
| 9 | 4 | 'use strict'; |
| 10 | 4 | var values = []; |
| 11 | 4 | for(var key in obj){ |
| 12 | 1 | if (obj.hasOwnProperty(key)) { |
| 13 | 1 | values.push(obj[key]) |
| 14 | } | |
| 15 | } | |
| 16 | 4 | return values; |
| 17 | }; | |
| 18 | /** | |
| 19 | * 数组求和 | |
| 20 | * @return {[type]} [description] | |
| 21 | */ | |
| 22 | 1 | Array.prototype.sum = function(){ |
| 23 | 7 | 'use strict'; |
| 24 | 7 | var count = 0; |
| 25 | 7 | this.forEach(function(item){ |
| 26 | 23 | count += parseFloat(item) || 0; |
| 27 | }); | |
| 28 | 7 | return count; |
| 29 | }; | |
| 30 |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var fs = require('fs'); |
| 2 | 1 | var path = require('path'); |
| 3 | ||
| 4 | ||
| 5 | 1 | var _alias = {}; |
| 6 | 1 | var _autoload_callbacks = []; |
| 7 | /** | |
| 8 | * thinkRequire获取到的路径 | |
| 9 | * @param {[type]} name [description] | |
| 10 | * @return {[type]} [description] | |
| 11 | */ | |
| 12 | 1 | function getThinkRequirePath(name){ |
| 13 | 72 | 'use strict'; |
| 14 | 72 | if (_alias[name]) { |
| 15 | 53 | return _alias[name]; |
| 16 | } | |
| 17 | 19 | var result = ''; |
| 18 | 19 | _autoload_callbacks.some(function(callback){ |
| 19 | 19 | result = callback && callback(name); |
| 20 | 19 | if (result) { |
| 21 | 18 | return true; |
| 22 | } | |
| 23 | }); | |
| 24 | 19 | return result; |
| 25 | } | |
| 26 | /** | |
| 27 | * 自定义的require, 加入别名功能 | |
| 28 | * @type {[type]} | |
| 29 | */ | |
| 30 | 1 | global.thinkRequire = function(name){ |
| 31 | 72 | 'use strict'; |
| 32 | //如果不是字符串则直接返回 | |
| 33 | 72 | if (!isString(name)) { |
| 34 | 1 | return name; |
| 35 | } | |
| 36 | 71 | var path = name; |
| 37 | 71 | if (path[0] !== '/') { |
| 38 | 71 | path = getThinkRequirePath(name); |
| 39 | } | |
| 40 | 71 | if (path) { |
| 41 | 70 | var obj = require(path); |
| 42 | 70 | if (isFunction(obj)) { |
| 43 | //修正子类继承的方法获取到正确的文件名 | |
| 44 | 64 | obj.prototype.__filename = path; |
| 45 | } | |
| 46 | 70 | return obj; |
| 47 | } | |
| 48 | 1 | return require(name); |
| 49 | }; | |
| 50 | /** | |
| 51 | * 注册require | |
| 52 | * @param {Function} callback [description] | |
| 53 | * @return {[type]} [description] | |
| 54 | */ | |
| 55 | 1 | global.registerAutoload = function(callback){ |
| 56 | 1 | 'use strict'; |
| 57 | 1 | _autoload_callbacks.push(callback); |
| 58 | }; | |
| 59 | /** | |
| 60 | * 别名 | |
| 61 | * @return {[type]} [description] | |
| 62 | */ | |
| 63 | 1 | global.aliasImport = function(alias, classFile){ |
| 64 | 19 | 'use strict'; |
| 65 | 19 | if (isString(alias)) { |
| 66 | 18 | _alias[alias] = classFile; |
| 67 | }else{ | |
| 68 | 1 | _alias = extend(_alias, alias); |
| 69 | } | |
| 70 | }; | |
| 71 | ||
| 72 | //常用类的基类 | |
| 73 | 1 | ['Cache', 'Behavior', 'Controller', 'Session', 'Model', 'Db'].forEach(function(item){ |
| 74 | 6 | 'use strict'; |
| 75 | 6 | global[item] = function(super_, obj){ |
| 76 | 17 | if (isString(super_)) { |
| 77 | 1 | return Class(obj, thinkRequire(super_)); |
| 78 | } | |
| 79 | 16 | return Class(super_, thinkRequire(item)); |
| 80 | }; | |
| 81 | }); | |
| 82 | ||
| 83 | ||
| 84 | /** | |
| 85 | * 调用一个指定的行为 | |
| 86 | * @param {[type]} name [description] | |
| 87 | */ | |
| 88 | 1 | global.B = function(name, http, data){ |
| 89 | 6 | 'use strict'; |
| 90 | 6 | if (typeof name === 'function') { |
| 91 | 0 | return name(http, data); |
| 92 | } | |
| 93 | 6 | return thinkRequire(name + 'Behavior')(http).run(data); |
| 94 | }; | |
| 95 | ||
| 96 | /** | |
| 97 | * 处理标签扩展 | |
| 98 | * @return {[type]} [description] | |
| 99 | */ | |
| 100 | 1 | global.tag = function(name, http, data){ |
| 101 | 12 | 'use strict'; |
| 102 | 12 | var sys_tags = (C('sys_tag.' + name) || []).slice(); |
| 103 | 12 | var tags = (C('tag.' + name) || []).slice(); |
| 104 | //tag处理的数据 | |
| 105 | 12 | http.tag_data = data; |
| 106 | 12 | if (tags.length) { |
| 107 | 0 | if (typeof tags[0] === 'boolean') { |
| 108 | 0 | var flag = tags.shift(); |
| 109 | //为true时自定义tag覆盖sys tag, false是自定义tag添加到sys tag之前 | |
| 110 | 0 | if (!flag) { |
| 111 | 0 | tags = tags.concat(sys_tags); |
| 112 | } | |
| 113 | }else{ | |
| 114 | //默认是自定义tag放在sys tag之后 | |
| 115 | 0 | tags = sys_tags.concat(tags); |
| 116 | } | |
| 117 | }else{ | |
| 118 | 12 | tags = sys_tags; |
| 119 | } | |
| 120 | 12 | function runBehavior(){ |
| 121 | 12 | var behavior = tags.shift(); |
| 122 | 12 | if (!behavior) { |
| 123 | 6 | return http.tag_data; |
| 124 | } | |
| 125 | 6 | var result = B(behavior, http, http.tag_data); |
| 126 | 6 | return getPromise(result).then(function(data){ |
| 127 | 6 | if (data !== undefined) { |
| 128 | 6 | http.tag_data = data; |
| 129 | } | |
| 130 | 6 | return runBehavior(); |
| 131 | }) | |
| 132 | } | |
| 133 | 12 | return getPromise(tags.length ? runBehavior() : http.tag_data); |
| 134 | }; | |
| 135 | /** | |
| 136 | * 配置读取和写入 | |
| 137 | */ | |
| 138 | 1 | var _config = {}; |
| 139 | 1 | global.C = function(name, value){ |
| 140 | 75 | 'use strict'; |
| 141 | //获取所有的配置 | |
| 142 | 75 | if (arguments.length === 0) { |
| 143 | 1 | return _config; |
| 144 | } | |
| 145 | //清除所有的配置 | |
| 146 | 74 | if (name === null) { |
| 147 | 1 | _config = {}; |
| 148 | } | |
| 149 | 74 | if (isString(name)) { |
| 150 | 70 | name = name.toLowerCase(); |
| 151 | 70 | if (name.indexOf('.') === -1) { |
| 152 | 46 | if (value === undefined) { |
| 153 | 45 | return _config[name]; |
| 154 | } | |
| 155 | 1 | _config[name] = value; |
| 156 | 1 | return; |
| 157 | } | |
| 158 | 24 | name = name.split('.'); |
| 159 | 24 | if (value === undefined) { |
| 160 | 24 | value = _config[name[0]] || {}; |
| 161 | 24 | return value[name[1]]; |
| 162 | } | |
| 163 | 0 | if (!_config[name[0]]) { |
| 164 | 0 | _config[name[0]] = {}; |
| 165 | } | |
| 166 | 0 | _config[name[0]][name[1]] = value; |
| 167 | }else{ | |
| 168 | 4 | _config = extend(_config, name); |
| 169 | } | |
| 170 | }; | |
| 171 | /** | |
| 172 | * 实例化Controller类,可以调用一个具体的Action | |
| 173 | * A('Home/Index'), A('Admin/Index/test') | |
| 174 | * @param {[type]} name [description] | |
| 175 | */ | |
| 176 | 1 | global.A = function(name, http, data){ |
| 177 | 1 | 'use strict'; |
| 178 | 1 | if (name.indexOf('/') > -1) { |
| 179 | 1 | name = name.split('/'); |
| 180 | 0 | }else if (name.indexOf(':') > -1) { |
| 181 | 0 | name = name.split(':'); |
| 182 | }else{ | |
| 183 | 0 | name = [name]; |
| 184 | } | |
| 185 | 1 | var action; |
| 186 | 1 | if (name.length === 3) { |
| 187 | 0 | action = name.pop(); |
| 188 | } | |
| 189 | 1 | var controller = name.pop() || http.controller; |
| 190 | 1 | var group = name.pop() || http.group; |
| 191 | 1 | var gm = ucfirst(group) + '/' + ucfirst(controller); |
| 192 | 1 | var path = getThinkRequirePath(gm + 'Controller'); |
| 193 | 1 | if (path) { |
| 194 | 1 | var instance = require(path)(http); |
| 195 | 1 | if (action) { |
| 196 | 0 | action += C('action_suffix'); |
| 197 | 0 | if (arguments.length === 2) { |
| 198 | 0 | data = []; |
| 199 | 0 | }else if (!isArray(data)) { |
| 200 | 0 | data = [data]; |
| 201 | } | |
| 202 | 0 | return instance[action].apply(instance, data); |
| 203 | } | |
| 204 | 1 | return instance; |
| 205 | } | |
| 206 | 0 | return null; |
| 207 | }; | |
| 208 | ||
| 209 | /** | |
| 210 | * 快速文件读取和写入 | |
| 211 | * 默认写入到App/Runtime/Data目录下 | |
| 212 | */ | |
| 213 | 1 | global.F = function(name, value, rootPath){ |
| 214 | 0 | 'use strict'; |
| 215 | 0 | if (rootPath === undefined) { |
| 216 | 0 | rootPath = DATA_PATH; |
| 217 | } | |
| 218 | 0 | var filePath = rootPath + '/' + name + '.json'; |
| 219 | 0 | if (value !== undefined) { |
| 220 | 0 | mkdir(path.dirname(filePath)); |
| 221 | 0 | fs.writeFile(filePath, JSON.stringify(value), function(){ |
| 222 | 0 | chmod(filePath); |
| 223 | }); | |
| 224 | 0 | return; |
| 225 | } | |
| 226 | 0 | if (isFile(filePath)) { |
| 227 | 0 | var content = getFileContent(filePath); |
| 228 | 0 | if (content) { |
| 229 | 0 | return JSON.parse(content); |
| 230 | } | |
| 231 | } | |
| 232 | 0 | return false; |
| 233 | }; | |
| 234 | /** | |
| 235 | * 实例化模型 | |
| 236 | */ | |
| 237 | 1 | global.D = function(name, tablePrefix, config){ |
| 238 | 0 | 'use strict'; |
| 239 | 0 | if (name === undefined) { |
| 240 | 0 | return thinkRequire('Model')(name, tablePrefix, config); |
| 241 | } | |
| 242 | 0 | name = name.split(':'); |
| 243 | 0 | var path = getThinkRequirePath(name[0] + 'Model'); |
| 244 | 0 | if (path) { |
| 245 | 0 | return thinkRequire(name[0] + 'Model')(name[1], tablePrefix, config); |
| 246 | }else{ | |
| 247 | 0 | return thinkRequire(name[1] === 'AdvModel' ? 'AdvModel' : 'Model')(name[0], tablePrefix, config); |
| 248 | } | |
| 249 | }; | |
| 250 | /** | |
| 251 | * 实例化模型基类 | |
| 252 | * @param {[type]} name [description] | |
| 253 | * @param {[type]} tablePrefix [description] | |
| 254 | * @param {[type]} config [description] | |
| 255 | */ | |
| 256 | 1 | global.M = function(name, tablePrefix, config){ |
| 257 | 0 | 'use strict'; |
| 258 | 0 | if (name === undefined) { |
| 259 | 0 | return thinkRequire('Model')(name, tablePrefix, config); |
| 260 | } | |
| 261 | 0 | name = name.split(':'); |
| 262 | 0 | var model = name[1] === 'AdvModel' ? 'AdvModel' : 'Model'; |
| 263 | 0 | return thinkRequire(model)(name[0], tablePrefix, config) |
| 264 | } | |
| 265 | /** | |
| 266 | * 缓存的设置和读取 | |
| 267 | * 获取返回的是一个promise | |
| 268 | */ | |
| 269 | 1 | global.S = function(name, value, options){ |
| 270 | 0 | 'use strict'; |
| 271 | 0 | if (options && !isObject(options)) { |
| 272 | 0 | options = {timeout: options}; |
| 273 | } | |
| 274 | 0 | options = options || {}; |
| 275 | 0 | var type = options.type === undefined ? C('cache_type') : options.type; |
| 276 | 0 | var instance = thinkRequire(ucfirst(type.toLowerCase()) + 'Cache')(options); |
| 277 | //获取缓存 | |
| 278 | 0 | if (value === undefined) { |
| 279 | 0 | return instance.get(name); |
| 280 | } | |
| 281 | //移除缓存 | |
| 282 | 0 | if (value === null) { |
| 283 | 0 | return instance.rm(name); |
| 284 | } | |
| 285 | 0 | return instance.set(name, value, options.timeout); |
| 286 | }; | |
| 287 | /** | |
| 288 | * 语言 | |
| 289 | * @param {[type]} name [description] | |
| 290 | */ | |
| 291 | 1 | global.L = function(name){ |
| 292 | 0 | 'use strict'; |
| 293 | 0 | return name; |
| 294 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 模块别名,模块名到具体的路径,模块名不能有重复 | |
| 3 | * 使用thinkRequire加载模块时有效 | |
| 4 | * @type {Object} | |
| 5 | */ | |
| 6 | 1 | module.exports = { |
| 7 | Controller: THINK_LIB_PATH + '/Core/Controller.js', | |
| 8 | App: THINK_LIB_PATH + '/Core/App.js', | |
| 9 | Behavior: THINK_LIB_PATH + '/Util/Behavior.js', | |
| 10 | Cache: THINK_LIB_PATH + '/Util/Cache.js', | |
| 11 | Db: THINK_LIB_PATH + '/Core/Db.js', | |
| 12 | Dispatcher: THINK_LIB_PATH + '/Core/Dispatcher.js', | |
| 13 | Filter: THINK_LIB_PATH + '/Util/Filter.js', | |
| 14 | Http: THINK_LIB_PATH + '/Core/Http.js', | |
| 15 | //Log: THINK_LIB_PATH + '/Util/Log.js', | |
| 16 | Model: THINK_LIB_PATH + '/Core/Model.js', | |
| 17 | Session: THINK_LIB_PATH + '/Util/Session.js', | |
| 18 | Think: THINK_LIB_PATH + '/Core/Think.js', | |
| 19 | Valid: THINK_LIB_PATH + '/Util/Valid.js', | |
| 20 | View: THINK_LIB_PATH + '/Core/View.js', | |
| 21 | Cookie: THINK_LIB_PATH + '/Util/Cookie.js', | |
| 22 | WebSocket: THINK_LIB_PATH + '/Util/WebSocket.js' | |
| 23 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 框架默认配置 | |
| 3 | * 可以在App/Conf/config.js里修改下面的配置值 | |
| 4 | * @type {Object} | |
| 5 | */ | |
| 6 | 1 | module.exports = { |
| 7 | port: 8360, //监听端口 | |
| 8 | use_proxy: false, //是否使用代理访问,如:nginx。开启后不能通过ip+端口直接访问 | |
| 9 | encoding: 'utf8', //输出数据的编码 | |
| 10 | url_pathname_prefix: '', //不解析的pathname前缀 | |
| 11 | url_pathname_suffix: '.html', //不解析的pathname后缀,这样利于seo | |
| 12 | app_tag_on: true, //是否支持标签功能 | |
| 13 | url_resource_on: true, //是否监听静态资源类请求 | |
| 14 | url_resource_reg: /^(resource\/|static\/|favicon\.ico)/, //判断是否是静态资源的正则 | |
| 15 | url_route_on: true, //是否开启自定义路由功能 | |
| 16 | ||
| 17 | post_json_content_type: ['application/json'], //post数据为json时的content-type | |
| 18 | post_data_async: false, //POST提交的数据异步获取,默认为同步获取 | |
| 19 | post_max_file_size: 1024 * 1024 * 1024, //上传文件大小限制,默认1G | |
| 20 | post_max_fields: 1000, //最大表单数 | |
| 21 | post_max_fields_size: 2 * 1024, //单个表单最大值 | |
| 22 | ||
| 23 | app_group_list: ['Home', 'Admin'], //分组列表 | |
| 24 | default_group: 'Home', //默认分组 | |
| 25 | default_controller: 'Index', //默认模块 | |
| 26 | default_action: 'index', //默认Action | |
| 27 | call_controller: 'Home:Index:_404', //controller不存在时执行方法,此配置表示调用Home分组下IndexController的_404Action方法 | |
| 28 | call_method: '__call', //当找不到方法时调用什么方法,这个方法存在时才有效 | |
| 29 | before_action_name: '__before', //调用一个action前调用的方法,会将action名传递进去 | |
| 30 | after_action_name: '__after', //调用一个action之后调用的方法,会将action名传递进去 | |
| 31 | url_params_bind: true, //方法参数绑定,将URL参数值绑定到action的参数上 | |
| 32 | action_suffix: 'Action', //action后缀 | |
| 33 | url_callback_name: 'callback', //jsonp格式的callback名字 | |
| 34 | json_content_type: 'application/json', //发送json时的content-type | |
| 35 | auto_send_content_type: true, //是否自动发送Content-Type,默认值为`tpl_content_type`配置值 | |
| 36 | log_process_pid: true, //记录进程的id,方便其他脚本处理。 | |
| 37 | use_cluster: false, //是否使用cluster,默认不使用,0:为cpu的数量,可以自定义值 | |
| 38 | autoload_path: {}, //autoload查找的path,用于thinkRequire加载自定义库的时候查找 | |
| 39 | create_server_fn: '', //自定义create server全局函数名,可以在Common/common.js里实现 | |
| 40 | ||
| 41 | load_ext_config: [], //加载额外的配置文件 CONF_PATH | |
| 42 | load_ext_file: [], //加载额外的文件 COMMON_PATH | |
| 43 | ||
| 44 | use_websocket: false, //是否使用websocket | |
| 45 | websocket_allow_origin: '', //允许从那里发送过来的websocket,可以是字符串、数组、回调函数,为空表示不检测 | |
| 46 | websocket_sub_protocal: '', //websocket子协议,可以是个字符串也可以是回调函数 | |
| 47 | websocket_message_handle: undefined, //websocket消息处理函数 | |
| 48 | ||
| 49 | error_tpl_path: THINK_PATH + '/View/error.html', //错误页模版 | |
| 50 | error_no_key: 'errno', //错误number的key | |
| 51 | error_no_default_value: 1000, //错误号默认值 | |
| 52 | error_msg_key: 'errmsg', //错误消息的key | |
| 53 | ||
| 54 | cookie_domain: '', //cookie有效域名 | |
| 55 | cookie_path: '/', //cookie路径 | |
| 56 | cookie_timeout: 0, //cookie失效时间,0为浏览器关闭,单位:秒 | |
| 57 | ||
| 58 | session_name: 'thinkjs', //session对应的cookie名称 | |
| 59 | session_type: 'File', //session存储类型, 空为内存,还可以为File | |
| 60 | session_path: '', //File类型下文件存储位置,默认为系统的tmp目录 | |
| 61 | session_options: {}, //session对应的cookie选项 | |
| 62 | session_sign: '', //session对应的cookie使用签名 | |
| 63 | session_timeout: 24 * 3600, //session失效时间,单位:秒 | |
| 64 | ||
| 65 | db_type: 'mysql', // 数据库类型 | |
| 66 | db_host: 'localhost', // 服务器地址 | |
| 67 | db_port: '', // 端口 | |
| 68 | db_name: '', // 数据库名 | |
| 69 | db_user: 'root', // 用户名 | |
| 70 | db_pwd: '', // 密码 | |
| 71 | db_prefix: 'think_', // 数据库表前缀 | |
| 72 | db_fieldtype_check: false, // 是否进行字段类型检查 | |
| 73 | db_fields_cache: true, // 启用字段缓存 | |
| 74 | db_charset: 'utf8', // 数据库编码默认采用utf8 | |
| 75 | db_nums_per_page: 20, //默认每页显示的条数 | |
| 76 | db_like_fields: [], //自动进行模糊查询,|连接,如: ['title', 'content'] | |
| 77 | db_cache_on: true, //是否启用查询缓存,如果关闭那么cache方法则无效 | |
| 78 | db_cache_type: '', //缓存类型,默认为内存缓存 | |
| 79 | db_cache_path: CACHE_PATH + '/db', //缓存路径,File类型下有效 | |
| 80 | db_cache_timeout: 3600, //缓存时间,默认为1个小时 | |
| 81 | ||
| 82 | tpl_content_type: 'text/html', //模版输出类型 | |
| 83 | tpl_file_suffix: '.html', //模版文件名后缀 | |
| 84 | tpl_file_depr: '_', //controller和action之间的分隔符 | |
| 85 | tpl_engine_type: 'ejs', //模版引擎名称 | |
| 86 | tpl_engine_config: {}, | |
| 87 | ||
| 88 | cache_type: 'File', //数据缓存类型 | |
| 89 | cache_timeout: 6 * 3600, //数据缓存有效期,单位: 秒 | |
| 90 | cache_path: CACHE_PATH, //缓存路径设置 (File缓存方式有效) | |
| 91 | cache_file_suffix: '.json', //File缓存方式下文件后缀名 | |
| 92 | cache_gc_hour: [4], //缓存清除的时间点,数据为小时 | |
| 93 | ||
| 94 | html_cache_on: false, //HTML静态缓存 | |
| 95 | html_cache_timeout: 3600, //缓存时间,单位为秒 | |
| 96 | html_cache_rules: {}, //缓存规则 | |
| 97 | html_cache_path: CACHE_PATH + '/html', | |
| 98 | html_cache_file_callback: undefined, //生成缓存文件的回调函数 | |
| 99 | html_cache_file_suffix: '.html', //缓存文件后缀名 | |
| 100 | ||
| 101 | memcache_host: '127.0.0.1', //memcache host | |
| 102 | memcache_port: 11211, //memecache端口 | |
| 103 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 不同模式下的配置文件 | |
| 3 | * 由于每个模式下的配置可能都比较少,所以放在一个文件里 | |
| 4 | * @type {Object} | |
| 5 | */ | |
| 6 | 1 | module.exports = { |
| 7 | cli: { | |
| 8 | use_cluster: false, | |
| 9 | html_cache_on: false, | |
| 10 | log_process_pid: false, | |
| 11 | clear_require_cache: false | |
| 12 | }, | |
| 13 | cli_debug: { | |
| 14 | clear_require_cache: false | |
| 15 | } | |
| 16 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 系统标签配置 | |
| 3 | * 可以在App/Conf/tag.js里进行修改 | |
| 4 | * @type {Object} | |
| 5 | */ | |
| 6 | 1 | module.exports = { |
| 7 | //应用初始化 | |
| 8 | app_init: [], | |
| 9 | //pathinfo解析 | |
| 10 | path_info: [], | |
| 11 | //静态资源请求检测 | |
| 12 | resource_check: ['CheckResource'], | |
| 13 | //路由检测 | |
| 14 | route_check: ['CheckRoute'], | |
| 15 | //应用开始 | |
| 16 | app_begin: ['ReadHtmlCache'], | |
| 17 | //action执行初始化 | |
| 18 | action_init: [], | |
| 19 | //模版解析初始化 | |
| 20 | view_init: [], | |
| 21 | //定位模版文件 | |
| 22 | view_template: ['LocationTemplate'], | |
| 23 | //模版解析 | |
| 24 | view_parse: ['ParseTemplate'], | |
| 25 | //模版内容过滤 | |
| 26 | view_filter: [], | |
| 27 | //模版解析结束 | |
| 28 | view_end: ['WriteHtmlCache'], | |
| 29 | //action结束 | |
| 30 | action_end: [], | |
| 31 | //应用结束 | |
| 32 | app_end: [] | |
| 33 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var fs = require('fs'); |
| 2 | 1 | var mime = require('mime'); |
| 3 | /** | |
| 4 | * 静态资源请求 | |
| 5 | * @return {[type]} [description] | |
| 6 | */ | |
| 7 | 1 | module.exports = Behavior(function(){ |
| 8 | 1 | 'use strict'; |
| 9 | 1 | return { |
| 10 | options: { | |
| 11 | 'url_resource_on': false | |
| 12 | }, | |
| 13 | run: function(){ | |
| 14 | 1 | if (!RESOURCE_PATH || !this.options.url_resource_on || !this.http.pathname) { |
| 15 | 0 | return false; |
| 16 | } | |
| 17 | 1 | var pathname = this.http.pathname; |
| 18 | 1 | if (pathname.indexOf('/') === 0) { |
| 19 | 1 | pathname = pathname.substr(1); |
| 20 | } | |
| 21 | 1 | var reg = C('url_resource_reg'); |
| 22 | //通过正则判断是否是静态资源请求 | |
| 23 | 1 | if (!reg.test(pathname)) { |
| 24 | 1 | return false; |
| 25 | } | |
| 26 | ||
| 27 | 0 | var file = RESOURCE_PATH + '/' + pathname; |
| 28 | 0 | var res = this.http.res; |
| 29 | 0 | if (fs.existsSync(file)) { |
| 30 | 0 | var contentType = mime.lookup(file); |
| 31 | 0 | var fileStream = fs.createReadStream(file); |
| 32 | 0 | res.setHeader('Content-Type', contentType + '; charset=' + C('encoding')); |
| 33 | 0 | fileStream.pipe(res); |
| 34 | 0 | fileStream.on('end', function(){ |
| 35 | 0 | res.end(); |
| 36 | }); | |
| 37 | }else{ | |
| 38 | 0 | res.statusCode = 404; |
| 39 | 0 | res.end(); |
| 40 | } | |
| 41 | //返回一个pendding promise, 不让后续执行 | |
| 42 | 0 | return getDefer().promise; |
| 43 | } | |
| 44 | }; | |
| 45 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 检测路由行为 | |
| 3 | * 通过自定义路由识别到对应的URL上 | |
| 4 | * @return {[type]} [description] | |
| 5 | */ | |
| 6 | 1 | var url = require('url'); |
| 7 | 1 | var Dispatcher = thinkRequire('Dispatcher'); |
| 8 | ||
| 9 | 1 | module.exports = Behavior(function(){ |
| 10 | 1 | 'use strict'; |
| 11 | 1 | return { |
| 12 | options: { | |
| 13 | 'url_route_on': false, //是否开启自定义URL路由 | |
| 14 | 'url_route_rules': [] //自定义URL路由规则 | |
| 15 | }, | |
| 16 | run: function(){ | |
| 17 | 1 | if (!this.options.url_route_on) { |
| 18 | 0 | return false; |
| 19 | } | |
| 20 | 1 | var routes = this.options.url_route_rules; |
| 21 | 1 | var length = routes.length; |
| 22 | 1 | if (length === 0) { |
| 23 | 1 | return false; |
| 24 | } | |
| 25 | 0 | var pathname = this.http.pathname; |
| 26 | 0 | var match; |
| 27 | 0 | for(var i = 0; i < length; i++){ |
| 28 | 0 | var route = routes[i]; |
| 29 | 0 | var rule = route[0]; |
| 30 | //正则路由 | |
| 31 | 0 | if (isRegexp(rule)) { |
| 32 | 0 | match = pathname.match(rule); |
| 33 | 0 | if (match) { |
| 34 | 0 | var result = this.parseRegExp(match, route[1], pathname); |
| 35 | 0 | if (result) { |
| 36 | 0 | return result; |
| 37 | } | |
| 38 | } | |
| 39 | }else{ | |
| 40 | //字符串路由 | |
| 41 | 0 | match = this.checkUrlMatch(pathname, rule); |
| 42 | 0 | if (match) { |
| 43 | 0 | return this.parseRule(rule, route[1], pathname); |
| 44 | } | |
| 45 | } | |
| 46 | } | |
| 47 | 0 | return false; |
| 48 | }, | |
| 49 | /** | |
| 50 | * 解析字符串路由 | |
| 51 | * @param {[type]} rule [description] | |
| 52 | * @param {[type]} route [description] | |
| 53 | * @param {[type]} pathname [description] | |
| 54 | * @return {[type]} [description] | |
| 55 | */ | |
| 56 | parseRule: function(rule, route, pathname){ | |
| 57 | 0 | route = this.getRoute(route); |
| 58 | 0 | if (!route) { |
| 59 | 0 | return false; |
| 60 | } | |
| 61 | 0 | pathname = pathname.split('/').filter(function(item){ |
| 62 | 0 | return item.trim(); |
| 63 | }); | |
| 64 | 0 | rule = rule.split('/').filter(function(item){ |
| 65 | 0 | return item.trim(); |
| 66 | }); | |
| 67 | 0 | var matches = {}; |
| 68 | 0 | rule.forEach(function(item){ |
| 69 | 0 | var pathitem = pathname.shift(); |
| 70 | 0 | if (item.indexOf(':') === 0) { |
| 71 | 0 | matches[item] = pathitem; |
| 72 | } | |
| 73 | }); | |
| 74 | //将剩余的pathname分割为querystring | |
| 75 | 0 | if (pathname.length) { |
| 76 | 0 | for(var i = 0,length = Math.ceil(pathname.length)/2; i < length; i++){ |
| 77 | 0 | this.http.get[pathname[i * 2]] = pathname[i * 2 + 1] || ''; |
| 78 | } | |
| 79 | } | |
| 80 | 0 | var values = Object.values(matches); |
| 81 | 0 | route = route.replace(/:(\d+)/g, function(a, b){ |
| 82 | 0 | return values[b - 1] || ''; |
| 83 | }); | |
| 84 | 0 | this.parseUrl(route); |
| 85 | 0 | return true; |
| 86 | }, | |
| 87 | /** | |
| 88 | * 检测URL是否匹配 | |
| 89 | * @param {[type]} pathname [description] | |
| 90 | * @param {[type]} rule [description] | |
| 91 | * @return {[type]} [description] | |
| 92 | */ | |
| 93 | checkUrlMatch: function(pathname, rule){ | |
| 94 | 0 | pathname = pathname.split('/').filter(function(item){ |
| 95 | 0 | return item.trim(); |
| 96 | }); | |
| 97 | 0 | rule = rule.split('/').filter(function(item){ |
| 98 | 0 | return item.trim(); |
| 99 | }); | |
| 100 | 0 | return rule.every(function(item, i){ |
| 101 | 0 | if (item.indexOf(':') === 0) { |
| 102 | 0 | if (item.indexOf('\\') > -1) { |
| 103 | 0 | var type = item.substr(-1); |
| 104 | 0 | if (type === 'd' && !isNumberString(pathname[i])) { |
| 105 | 0 | return false; |
| 106 | } | |
| 107 | } | |
| 108 | }else{ | |
| 109 | 0 | var pitem = pathname[i] || ''; |
| 110 | 0 | if (pitem.toLowerCase() !== item.toLowerCase()) { |
| 111 | 0 | return false; |
| 112 | } | |
| 113 | } | |
| 114 | 0 | return true; |
| 115 | }); | |
| 116 | }, | |
| 117 | /** | |
| 118 | * 解析转化后的url | |
| 119 | * @param {[type]} urlInfo [description] | |
| 120 | * @return {[type]} [description] | |
| 121 | */ | |
| 122 | parseUrl: function(urlInfo){ | |
| 123 | 0 | urlInfo = url.parse(urlInfo, true); |
| 124 | 0 | if (urlInfo.query) { |
| 125 | 0 | for(var key in urlInfo.query){ |
| 126 | 0 | if (urlInfo.query[key] || !(key in this.http.get)) { |
| 127 | 0 | this.http.get[key] = urlInfo.query[key]; |
| 128 | } | |
| 129 | } | |
| 130 | } | |
| 131 | 0 | var pathname = urlInfo.pathname || ''; |
| 132 | // 过滤调用pathname最后有/的情况 | |
| 133 | 0 | pathname = pathname.split('/').filter(function(item){ |
| 134 | 0 | return item.trim(); |
| 135 | }); | |
| 136 | 0 | this.http.action = Dispatcher.getAction(pathname.pop()); |
| 137 | 0 | this.http.controller = Dispatcher.getController(pathname.pop()); |
| 138 | 0 | this.http.group = Dispatcher.getGroup(pathname.pop()); |
| 139 | }, | |
| 140 | /** | |
| 141 | * 获取route | |
| 142 | * @param {[type]} route [description] | |
| 143 | * @return {[type]} [description] | |
| 144 | */ | |
| 145 | getRoute: function(route){ | |
| 146 | 0 | if (isObject(route)) { |
| 147 | //对应的请求类型 | |
| 148 | 0 | for(var method in route){ |
| 149 | //由于请求类型没有包含关系,这里可以直接用indexOf判断 | |
| 150 | 0 | if (method.toUpperCase().indexOf(this.http.method) > -1) { |
| 151 | 0 | return route[method]; |
| 152 | } | |
| 153 | } | |
| 154 | 0 | return; |
| 155 | } | |
| 156 | 0 | return route; |
| 157 | }, | |
| 158 | /** | |
| 159 | * 正则匹配路由 | |
| 160 | * @param {[type]} matches [description] | |
| 161 | * @param {[type]} route [description] | |
| 162 | * @param {[type]} pathname [description] | |
| 163 | * @return {[type]} [description] | |
| 164 | */ | |
| 165 | parseRegExp: function(matches, route, pathname){ | |
| 166 | 0 | route = this.getRoute(route); |
| 167 | 0 | if (!route) { |
| 168 | 0 | return false; |
| 169 | } | |
| 170 | 0 | route = route.replace(/:(\d+)/g, function(a, b){ |
| 171 | 0 | return matches[b] || ''; |
| 172 | }); | |
| 173 | 0 | pathname = pathname.replace(matches[0], ''); |
| 174 | 0 | pathname = pathname.split('/').filter(function(item){ |
| 175 | 0 | return item; |
| 176 | }); | |
| 177 | //将剩余的pathname分割为querystring | |
| 178 | 0 | if (pathname.length) { |
| 179 | 0 | for(var i = 0,length = Math.ceil(pathname.length)/2; i < length; i++){ |
| 180 | 0 | this.http.get[pathname[i * 2]] = pathname[i * 2 + 1] || ''; |
| 181 | } | |
| 182 | } | |
| 183 | 0 | this.parseUrl(route); |
| 184 | 0 | return true; |
| 185 | } | |
| 186 | }; | |
| 187 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 阻止ip来源访问 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | module.exports = Behavior(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | 1 | return { |
| 8 | options: { | |
| 9 | deny_ip: [] //阻止的ip列表 | |
| 10 | }, | |
| 11 | run: function(){ | |
| 12 | 0 | if (this.options.deny_ip.length === 0) { |
| 13 | 0 | return true; |
| 14 | } | |
| 15 | 0 | var clientIps = this.http.ip().split('.'); |
| 16 | 0 | var flag = this.options.deny_ip.some(function(item){ |
| 17 | 0 | return item.split('.').every(function(num, i){ |
| 18 | 0 | if (num === '*' || num === clientIps[i]) { |
| 19 | 0 | return true; |
| 20 | } | |
| 21 | }); | |
| 22 | }); | |
| 23 | //如果在阻止的ip在列表里,则返回一个pendding promise,让后面的代码不执行 | |
| 24 | 0 | if (flag) { |
| 25 | 0 | this.http.res.statusCode = 403; |
| 26 | 0 | this.http.res.end(); |
| 27 | 0 | return getDefer().promise; |
| 28 | } | |
| 29 | 0 | return true; |
| 30 | } | |
| 31 | }; | |
| 32 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 定位模版的行为 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | ||
| 6 | 1 | module.exports = Behavior(function(){ |
| 7 | 1 | 'use strict'; |
| 8 | 1 | return { |
| 9 | run: function(templateFile){ | |
| 10 | 1 | if (!isFile(templateFile)) { |
| 11 | 1 | return this.parseTemplateFile(templateFile); |
| 12 | } | |
| 13 | }, | |
| 14 | /** | |
| 15 | * 解析模版文件 | |
| 16 | * @param {[type]} templateFile [description] | |
| 17 | * @return {[type]} [description] | |
| 18 | */ | |
| 19 | parseTemplateFile: function(templateFile){ | |
| 20 | 1 | templateFile = templateFile || ''; |
| 21 | 1 | if (!templateFile) { |
| 22 | 1 | templateFile = [ |
| 23 | VIEW_PATH, '/', this.http.group, '/', | |
| 24 | this.http.controller.toLowerCase(), | |
| 25 | C('tpl_file_depr'), | |
| 26 | this.http.action.toLowerCase(), | |
| 27 | C('tpl_file_suffix') | |
| 28 | ].join(''); | |
| 29 | 0 | }else if(templateFile.indexOf('/') > -1){ |
| 30 | //自动追加VIEW_PATH前缀 | |
| 31 | 0 | if (templateFile.indexOf('/') !== 0) { |
| 32 | 0 | templateFile = VIEW_PATH + '/' + templateFile; |
| 33 | } | |
| 34 | 0 | }else if(templateFile.indexOf(C('tpl_file_suffix')) === -1){ |
| 35 | 0 | var path = templateFile.split(':'); |
| 36 | 0 | var action = path.pop(); |
| 37 | 0 | var controller = path.pop() || this.http.controller.toLowerCase(); |
| 38 | 0 | var group = ucfirst(path.pop()) || this.http.group; |
| 39 | 0 | templateFile = [ |
| 40 | VIEW_PATH, '/', group, '/', | |
| 41 | controller, | |
| 42 | C('tpl_file_depr'), | |
| 43 | action, | |
| 44 | C('tpl_file_suffix') | |
| 45 | ].join(''); | |
| 46 | } | |
| 47 | 1 | if (!isFile(templateFile)) { |
| 48 | 0 | console.log(templateFile + ' is not exist', this.http); |
| 49 | 0 | return false; |
| 50 | } | |
| 51 | 1 | return templateFile; |
| 52 | } | |
| 53 | }; | |
| 54 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 调用对应的模版引擎解析模版 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | module.exports = Behavior(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | 1 | return { |
| 8 | run: function(data){ | |
| 9 | 1 | var file = data.content || data.file; |
| 10 | //将模版文件路径写入到http对象上,供writehtmlcache里使用 | |
| 11 | 1 | this.http.tpl_file = file; |
| 12 | 1 | var engine = C('tpl_engine_type'); |
| 13 | //不使用模版引擎,直接返回文件内容 | |
| 14 | 1 | if (!engine) { |
| 15 | 0 | return getFileContent(file); |
| 16 | } | |
| 17 | 1 | var engineClass = ucfirst(engine) + 'Template'; |
| 18 | 1 | return thinkRequire(engineClass).fetch(file, data.var); |
| 19 | } | |
| 20 | }; | |
| 21 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | //获取模版文件 | |
| 2 | 1 | var getViewFile = thinkRequire('WriteHtmlCacheBehavior').getViewFile; |
| 3 | 1 | var fs = require('fs'); |
| 4 | /** | |
| 5 | * 读取HTML缓存 | |
| 6 | * @return {[type]} [description] | |
| 7 | */ | |
| 8 | 1 | module.exports = Behavior(function(){ |
| 9 | 1 | 'use strict'; |
| 10 | 1 | return { |
| 11 | options:{ | |
| 12 | 'html_cache_on': false, //是否开启缓存 | |
| 13 | 'html_cache_timeout': 3600, //缓存时间 | |
| 14 | 'html_cache_rules': {}, //缓存规则 | |
| 15 | 'html_cache_path': '', | |
| 16 | 'html_cache_file_callback': undefined, //生成缓存文件的回调函数 | |
| 17 | 'html_cache_file_suffix': '.html', //缓存文件扩展名 | |
| 18 | }, | |
| 19 | run: function(){ | |
| 20 | 1 | if (!this.options.html_cache_on || isEmpty(this.options.html_cache_rules)) { |
| 21 | 1 | return false; |
| 22 | } | |
| 23 | 0 | var cacheTime = this.getCacheTime(); |
| 24 | 0 | if (cacheTime === false) { |
| 25 | 0 | return false; |
| 26 | } | |
| 27 | 0 | if (this.checkCacheTime(cacheTime)) { |
| 28 | 0 | this.responseCacheContent(); |
| 29 | //return a pending promise | |
| 30 | 0 | return getDefer().promise; |
| 31 | } | |
| 32 | 0 | return false; |
| 33 | }, | |
| 34 | /** | |
| 35 | * 返回缓存内容 | |
| 36 | * @return {[type]} [description] | |
| 37 | */ | |
| 38 | responseCacheContent: function(){ | |
| 39 | 0 | var http = this.http; |
| 40 | 0 | var fileStream = fs.createReadStream(this.options.html_cache_path + '/' + http.html_filename); |
| 41 | 0 | http.setHeader('Content-Type', 'text/html'); |
| 42 | 0 | http.sendTime('Exec-Time'); |
| 43 | 0 | http.sendCookie(); |
| 44 | 0 | fileStream.pipe(http.res); |
| 45 | 0 | fileStream.on('end', function(){ |
| 46 | 0 | http.end(); |
| 47 | }); | |
| 48 | }, | |
| 49 | /** | |
| 50 | * 获取缓存时间 | |
| 51 | * @return {[type]} [description] | |
| 52 | */ | |
| 53 | getCacheTime: function(){ | |
| 54 | /** | |
| 55 | * rules数据格式 | |
| 56 | * { | |
| 57 | * 'index:index': ['index_home', 1800, html_cache_callback] | |
| 58 | * } | |
| 59 | * @type {[type]} | |
| 60 | */ | |
| 61 | 0 | var rules = this.options.html_cache_rules; |
| 62 | 0 | var group = this.http.group.toLowerCase(); |
| 63 | 0 | var controller = this.http.controller.toLowerCase(); |
| 64 | 0 | var action = this.http.action.toLowerCase(); |
| 65 | 0 | var list = [ |
| 66 | group + ':' + controller + ':' + action, | |
| 67 | controller + ':' + action, | |
| 68 | action, | |
| 69 | '*' | |
| 70 | ]; | |
| 71 | 0 | var html = []; |
| 72 | 0 | list.some(function(item){ |
| 73 | 0 | if (item in rules) { |
| 74 | 0 | html = rules[item]; |
| 75 | 0 | return true; |
| 76 | } | |
| 77 | }); | |
| 78 | 0 | if (isEmpty(html)) { |
| 79 | 0 | return false; |
| 80 | } | |
| 81 | 0 | if (!isArray(html)) { |
| 82 | 0 | html = [html]; |
| 83 | } | |
| 84 | 0 | var rule = html[0]; |
| 85 | //将cookie变量传递进去 | |
| 86 | 0 | var cookiePars = {}; |
| 87 | 0 | for(var name in this.http.cookie){ |
| 88 | 0 | cookiePars['cookie.' + name] = this.http.cookie[name]; |
| 89 | } | |
| 90 | 0 | var pars = extend({}, this.http.get, cookiePars, { |
| 91 | ':group': group, | |
| 92 | ':controller': controller, | |
| 93 | ':action': action | |
| 94 | }); | |
| 95 | 0 | rule = rule.replace(/\{([\w\:]+)\}/g, function(a, name){ |
| 96 | 0 | return pars[name] || ''; |
| 97 | }); | |
| 98 | 0 | var callback = html[2] || C('html_cache_file_callback') || this.getCacheFilename; |
| 99 | 0 | var filename = callback(rule, this.http) + this.options.html_cache_file_suffix; |
| 100 | //静态缓存文件名 | |
| 101 | 0 | this.http.html_filename = filename; |
| 102 | 0 | var cacheTime = html[1] || this.options.html_cache_timeout; |
| 103 | 0 | return cacheTime; |
| 104 | }, | |
| 105 | /** | |
| 106 | * | |
| 107 | * @param {[type]} key [description] | |
| 108 | * @return {[type]} [description] | |
| 109 | */ | |
| 110 | getCacheFilename: function(key){ | |
| 111 | 0 | var value = md5(key); |
| 112 | 0 | return value.substr(0, 1) + '/' + value; |
| 113 | }, | |
| 114 | /** | |
| 115 | * [checkCacheTime description] | |
| 116 | * @return {[type]} [description] | |
| 117 | */ | |
| 118 | checkCacheTime: function(cacheTime){ | |
| 119 | 0 | var cacheFile = this.options.html_cache_path + '/' + this.http.html_filename; |
| 120 | 0 | if (!isFile(cacheFile)) { |
| 121 | 0 | return false; |
| 122 | } | |
| 123 | 0 | var cacheFileMtime = fs.statSync(cacheFile).mtime.getTime(); |
| 124 | 0 | var tplFile = getViewFile(this.http); |
| 125 | 0 | if (tplFile) { |
| 126 | 0 | if (!isFile(tplFile)) { |
| 127 | 0 | return false; |
| 128 | } | |
| 129 | 0 | var tplFileMtime = fs.statSync(tplFile).mtime.getTime(); |
| 130 | //模版文件有更新 | |
| 131 | 0 | if (tplFileMtime > cacheFileMtime) { |
| 132 | 0 | return false; |
| 133 | } | |
| 134 | } | |
| 135 | 0 | if (Date.now() > (cacheFileMtime + cacheTime * 1000)) { |
| 136 | 0 | return false; |
| 137 | } | |
| 138 | 0 | return true; |
| 139 | } | |
| 140 | }; | |
| 141 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 模版文件列表 | |
| 3 | * @type {Object} | |
| 4 | */ | |
| 5 | 1 | var path = require('path'); |
| 6 | 1 | var fs = require('fs'); |
| 7 | ||
| 8 | 1 | var tplFiles = {}; |
| 9 | /** | |
| 10 | * 写入html缓存 | |
| 11 | * @return {[type]} [description] | |
| 12 | */ | |
| 13 | 1 | module.exports = Behavior(function(){ |
| 14 | 1 | 'use strict'; |
| 15 | 1 | return { |
| 16 | options: { | |
| 17 | 'html_cache_on': false, //是否开启缓存 | |
| 18 | 'html_cache_path': '' | |
| 19 | }, | |
| 20 | run: function(content){ | |
| 21 | 1 | if (!this.options.html_cache_on || !this.http.html_filename) { |
| 22 | 1 | return content; |
| 23 | } | |
| 24 | 0 | this.recordViewFile(); |
| 25 | 0 | var file = this.options.html_cache_path + '/' + this.http.html_filename; |
| 26 | 0 | mkdir(path.dirname(file)); |
| 27 | //异步方式写入缓存 | |
| 28 | 0 | fs.writeFile(file, content); |
| 29 | 0 | return content; |
| 30 | }, | |
| 31 | /** | |
| 32 | * 记录模版文件名 | |
| 33 | * @return {[type]} [description] | |
| 34 | */ | |
| 35 | recordViewFile: function(){ | |
| 36 | 0 | var tplFile = this.http.tpl_file; |
| 37 | 0 | var key = this.http.group + ':' + this.http.controller + ':' + this.http.action; |
| 38 | 0 | tplFiles[key] = tplFile; |
| 39 | } | |
| 40 | }; | |
| 41 | }); | |
| 42 | /** | |
| 43 | * 获取模版文件 | |
| 44 | * @param {[type]} http [description] | |
| 45 | * @return {[type]} [description] | |
| 46 | */ | |
| 47 | 1 | module.exports.getViewFile = function(http){ |
| 48 | 0 | 'use strict'; |
| 49 | 0 | var key = http.group + ':' + http.controller + ':' + http.action; |
| 50 | 0 | return tplFiles[key]; |
| 51 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var cluster = require('cluster'); |
| 2 | 1 | var fs = require('fs'); |
| 3 | 1 | var domain = require('domain'); |
| 4 | 1 | var thinkHttp = thinkRequire('Http'); |
| 5 | 1 | var Dispatcher = thinkRequire('Dispatcher'); |
| 6 | ||
| 7 | /** | |
| 8 | * 应用程序 | |
| 9 | * @type {Object} | |
| 10 | */ | |
| 11 | 1 | var App = module.exports = Class(function(){ |
| 12 | 1 | 'use strict'; |
| 13 | //controller和action的校验正则 | |
| 14 | 1 | var nameReg = /^[A-Za-z\_](\w)*$/; |
| 15 | //注释的正则 | |
| 16 | 1 | var commentReg = /((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg; |
| 17 | //获取形参的正则 | |
| 18 | 1 | var parsReg = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; |
| 19 | ||
| 20 | 1 | return { |
| 21 | init: function(http){ | |
| 22 | 1 | this.http = http; |
| 23 | }, | |
| 24 | /** | |
| 25 | * 解析路由 | |
| 26 | * @return {[type]} [description] | |
| 27 | */ | |
| 28 | dispatch: function(){ | |
| 29 | 1 | return Dispatcher(this.http).run(); |
| 30 | }, | |
| 31 | /** | |
| 32 | * 获取controller | |
| 33 | * @return {[type]} [description] | |
| 34 | */ | |
| 35 | getController: function(){ | |
| 36 | 1 | var group = this.http.group; |
| 37 | 1 | var controller = ''; |
| 38 | //检测controller名 | |
| 39 | 1 | if (nameReg.test(this.http.controller)) { |
| 40 | 1 | controller = A(group + '/' + this.http.controller, this.http); |
| 41 | 1 | if (controller) { |
| 42 | 1 | return controller; |
| 43 | } | |
| 44 | } | |
| 45 | //controller不存在时调用的魔术方法 | |
| 46 | 0 | var controllerConf = C('call_controller'); |
| 47 | 0 | if (controllerConf) { |
| 48 | 0 | if (isString(controllerConf)) { |
| 49 | 0 | controllerConf = controllerConf.split(':'); |
| 50 | } | |
| 51 | 0 | var action = Dispatcher.getAction(controllerConf.pop()); |
| 52 | 0 | controller = Dispatcher.getController(controllerConf.pop()); |
| 53 | 0 | group = Dispatcher.getGroup(controllerConf.pop()); |
| 54 | 0 | controller = A(group + '/' + controller, this.http); |
| 55 | 0 | if (controller && isFunction(controller[action + C('action_suffix')])) { |
| 56 | 0 | this.http.group = group; |
| 57 | 0 | this.http.controller = controller; |
| 58 | 0 | this.http.action = action; |
| 59 | 0 | return controller; |
| 60 | } | |
| 61 | } | |
| 62 | }, | |
| 63 | /** | |
| 64 | * 执行 | |
| 65 | * @return {[type]} [description] | |
| 66 | */ | |
| 67 | exec: function(){ | |
| 68 | 1 | var controller = this.getController(); |
| 69 | 1 | if (!controller) { |
| 70 | 0 | var err = new Error('Controller `' + this.http.controller + '` not found. ' + this.http.pathname); |
| 71 | 0 | return getPromise(err, true); |
| 72 | } | |
| 73 | 1 | var self = this; |
| 74 | 1 | var action = this.http.action; |
| 75 | 1 | var act = action; |
| 76 | //添加action后缀 | |
| 77 | 1 | action += C('action_suffix') || ''; |
| 78 | //检测action名 | |
| 79 | 1 | if (!nameReg.test(action)) { |
| 80 | 0 | return getPromise(new Error('action `' + act + '` is not valid. ' + this.http.pathname), true); |
| 81 | } | |
| 82 | 1 | var initReturnPromise = getPromise(controller.__initReturn); |
| 83 | //对应的action方法存在 | |
| 84 | 1 | if (isFunction(controller[action])) { |
| 85 | //方法参数自动绑定,直接从形参里拿到对应的值 | |
| 86 | 1 | if (C('url_params_bind')) { |
| 87 | 1 | var toString = controller[action].toString().replace(commentReg, ''); |
| 88 | 1 | var match = toString.match(parsReg)[1].split(/,/).filter(function(item){ |
| 89 | 1 | return item; |
| 90 | }); | |
| 91 | //匹配到形参 | |
| 92 | 1 | if (match && match.length) { |
| 93 | 0 | return initReturnPromise.then(function(){ |
| 94 | 0 | var data = match.map(function(item){ |
| 95 | 0 | return self.http.post[item] || self.http.get[item] || ''; |
| 96 | }); | |
| 97 | 0 | return self.execAction(controller, action, act, data); |
| 98 | }); | |
| 99 | } | |
| 100 | } | |
| 101 | 1 | return initReturnPromise.then(function(){ |
| 102 | 1 | return self.execAction(controller, action, act); |
| 103 | }); | |
| 104 | }else{ | |
| 105 | //当指定的方法不存在时,调用魔术方法 | |
| 106 | //默认为__call方法 | |
| 107 | 0 | var callMethod = C('call_method'); |
| 108 | 0 | if (callMethod && isFunction(controller[callMethod])) { |
| 109 | 0 | return initReturnPromise.then(function(){ |
| 110 | 0 | return controller[callMethod](act, action); |
| 111 | }); | |
| 112 | } | |
| 113 | } | |
| 114 | 0 | return getPromise(new Error('action `' + action + '` not found. ' + self.http.pathname), true); |
| 115 | }, | |
| 116 | /** | |
| 117 | * 执行一个action, 支持before和after的统一操作 | |
| 118 | * 不对每个action都增加一个before和after,而是使用统一的策略 | |
| 119 | * 默认before和after调用名__before和__after | |
| 120 | * @param {[type]} controller [description] | |
| 121 | * @param {[type]} action [description] | |
| 122 | * @param {[type]} act [description] | |
| 123 | * @param {[type]} data [description] | |
| 124 | * @return {[type]} [description] | |
| 125 | */ | |
| 126 | execAction: function(controller, action, act, data){ | |
| 127 | 1 | var promise = getPromise(); |
| 128 | //before action | |
| 129 | 1 | var before = C('before_action_name'); |
| 130 | 1 | if (before && isFunction(controller[before])) { |
| 131 | 0 | promise = getPromise(controller[before](act, action)); |
| 132 | } | |
| 133 | 1 | return promise.then(function(){ |
| 134 | 1 | if (data) { |
| 135 | 0 | return controller[action].apply(controller, data) |
| 136 | }else{ | |
| 137 | 1 | return controller[action]() |
| 138 | } | |
| 139 | }).then(function(){ | |
| 140 | //after action | |
| 141 | 1 | var after = C('after_action_name'); |
| 142 | 1 | if (after && isFunction(controller[after])) { |
| 143 | 0 | return controller[after](act, action); |
| 144 | } | |
| 145 | }); | |
| 146 | }, | |
| 147 | /** | |
| 148 | * 发送错误信息 | |
| 149 | * @param {[type]} error [description] | |
| 150 | * @return {[type]} [description] | |
| 151 | */ | |
| 152 | sendError: function(error){ | |
| 153 | 0 | var message = isError(error) ? error.stack : error; |
| 154 | 0 | var http = this.http; |
| 155 | 0 | console.log(message); |
| 156 | 0 | if (!http.res) { |
| 157 | 0 | return; |
| 158 | } | |
| 159 | 0 | if (APP_DEBUG) { |
| 160 | 0 | http.res.statusCode = 500; |
| 161 | 0 | http.res.end(message); |
| 162 | }else{ | |
| 163 | 0 | http.res.statusCode = 500; |
| 164 | 0 | http.setHeader('Content-Type', 'text/html; charset=' + C('encoding')); |
| 165 | 0 | var readStream = fs.createReadStream(C('error_tpl_path')); |
| 166 | 0 | readStream.pipe(http.res); |
| 167 | 0 | readStream.on('end', function(){ |
| 168 | 0 | http.res.end(); |
| 169 | }); | |
| 170 | } | |
| 171 | } | |
| 172 | }; | |
| 173 | }); | |
| 174 | ||
| 175 | /** | |
| 176 | * run | |
| 177 | * @return {[type]} [description] | |
| 178 | */ | |
| 179 | 1 | App.run = function(){ |
| 180 | 1 | 'use strict'; |
| 181 | 1 | return App.mode[APP_MODE](); |
| 182 | }; | |
| 183 | /** | |
| 184 | * 不同模式下的run | |
| 185 | * @type {Object} | |
| 186 | */ | |
| 187 | 1 | App.mode = { |
| 188 | //命令行模式 | |
| 189 | cli: function(){ | |
| 190 | 1 | 'use strict'; |
| 191 | 1 | var defaultHttp = thinkHttp.getDefaultHttp(process.argv[2]); |
| 192 | 1 | thinkHttp(defaultHttp.req, defaultHttp.res).run(App.listener); |
| 193 | }, | |
| 194 | //HTTP模式 | |
| 195 | http: function(){ | |
| 196 | 0 | 'use strict'; |
| 197 | 0 | var clusterNums = C('use_cluster'); |
| 198 | //不使用cluster | |
| 199 | 0 | if (!clusterNums) { |
| 200 | 0 | return App.createServer(); |
| 201 | } | |
| 202 | //使用cpu的个数 | |
| 203 | 0 | if (clusterNums === true) { |
| 204 | 0 | clusterNums = require('os').cpus().length; |
| 205 | } | |
| 206 | 0 | if (cluster.isMaster) { |
| 207 | 0 | for (var i = 0; i < clusterNums; i++) { |
| 208 | 0 | cluster.fork(); |
| 209 | } | |
| 210 | 0 | cluster.on('exit', function(worker) { |
| 211 | 0 | console.log('worker ' + worker.process.pid + ' died'); |
| 212 | 0 | process.nextTick(function(){ |
| 213 | 0 | cluster.fork(); |
| 214 | }); | |
| 215 | }); | |
| 216 | }else { | |
| 217 | 0 | App.createServer(); |
| 218 | } | |
| 219 | } | |
| 220 | }; | |
| 221 | /** | |
| 222 | * 创建服务 | |
| 223 | * @return {[type]} [description] | |
| 224 | */ | |
| 225 | 1 | App.createServer = function(){ |
| 226 | 0 | 'use strict'; |
| 227 | //自定义创建server | |
| 228 | 0 | var createServerFn = C('create_server_fn'); |
| 229 | 0 | if (createServerFn) { |
| 230 | 0 | if (isFunction(createServerFn)) { |
| 231 | 0 | return createServerFn(App); |
| 232 | 0 | }else if (isFunction(global[createServerFn])) { |
| 233 | 0 | return global[createServerFn](App); |
| 234 | } | |
| 235 | } | |
| 236 | 0 | var server = require('http').createServer(function (req, res) { |
| 237 | 0 | thinkHttp(req, res).run(App.listener); |
| 238 | }); | |
| 239 | 0 | thinkRequire('WebSocket')(server, App).run(); |
| 240 | 0 | server.listen(C('port')); |
| 241 | } | |
| 242 | /** | |
| 243 | * 监听回调函数 | |
| 244 | * @param {[type]} http [description] | |
| 245 | * @return {[type]} [description] | |
| 246 | */ | |
| 247 | 1 | App.listener = function(http){ |
| 248 | 1 | 'use strict'; |
| 249 | //自动发送thinkjs和版本的header | |
| 250 | 1 | http.setHeader('X-Powered-By', 'thinkjs-' + THINK_VERSION); |
| 251 | //禁止远程直接用带端口的访问,websocket下允许 | |
| 252 | 1 | if (C('use_proxy') && http.host !== http.hostname && !http.websocket) { |
| 253 | 0 | http.res.statusCode = 403; |
| 254 | 0 | http.res.end(); |
| 255 | 0 | return getDefer().promise; |
| 256 | } | |
| 257 | 1 | var instance = App(http); |
| 258 | 1 | var domainInstance = domain.create(); |
| 259 | 1 | var deferred = getDefer(); |
| 260 | 1 | domainInstance.on('error', function(err){ |
| 261 | 0 | instance.sendError(err); |
| 262 | 0 | deferred.reject(err); |
| 263 | }); | |
| 264 | 1 | domainInstance.run(function(){ |
| 265 | 1 | return tag('app_init', http).then(function(){ |
| 266 | 1 | return instance.dispatch(); |
| 267 | }).then(function(){ | |
| 268 | 1 | return tag('app_begin', http); |
| 269 | }).then(function(){ | |
| 270 | 1 | return tag('action_init', http); |
| 271 | }).then(function(){ | |
| 272 | 1 | return instance.exec(); |
| 273 | }).then(function(){ | |
| 274 | 1 | return tag('app_end', http); |
| 275 | }).catch(function(err){ | |
| 276 | 0 | instance.sendError(err); |
| 277 | }).then(function(){ | |
| 278 | 1 | deferred.resolve(); |
| 279 | }) | |
| 280 | }); | |
| 281 | 1 | return deferred.promise; |
| 282 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * Controller 基类 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | var fs = require('fs'); |
| 6 | 1 | var path = require('path'); |
| 7 | 1 | var url = require('url'); |
| 8 | ||
| 9 | 1 | module.exports = Class(function() { |
| 10 | 1 | 'use strict'; |
| 11 | //callback正则 | |
| 12 | 1 | var callbackReg = /[^\w\.]/g; |
| 13 | ||
| 14 | 1 | return { |
| 15 | /** | |
| 16 | * 初始化执行方法 | |
| 17 | * @param {[type]} http [description] | |
| 18 | * @return {[type]} [description] | |
| 19 | */ | |
| 20 | init: function(http) { | |
| 21 | 1 | this.http = http; |
| 22 | 1 | this.view = null; |
| 23 | //将http数据打到模版里 | |
| 24 | 1 | this.assign('http', this.http); |
| 25 | //将配置信息打到模版里 | |
| 26 | 1 | this.assign('config', C()); |
| 27 | //设置变量别名 | |
| 28 | 1 | this.set = this.assign; |
| 29 | }, | |
| 30 | /** | |
| 31 | * 获取客户端的ip | |
| 32 | * @return {[type]} [description] | |
| 33 | */ | |
| 34 | ip: function() { | |
| 35 | 0 | return this.http.ip(); |
| 36 | }, | |
| 37 | /** | |
| 38 | * 实例化View类 | |
| 39 | * @return {[type]} [description] | |
| 40 | */ | |
| 41 | initView: function() { | |
| 42 | 3 | if (!this.view) { |
| 43 | 1 | this.view = thinkRequire('View')(this.http); |
| 44 | } | |
| 45 | 3 | return this.view; |
| 46 | }, | |
| 47 | /** | |
| 48 | * 是否是GET请求 | |
| 49 | * @return {Boolean} [description] | |
| 50 | */ | |
| 51 | isGet: function() { | |
| 52 | 0 | return this.http.method === 'GET'; |
| 53 | }, | |
| 54 | /** | |
| 55 | * 是否是POST请求 | |
| 56 | * @return {Boolean} [description] | |
| 57 | */ | |
| 58 | isPost: function() { | |
| 59 | 0 | return this.http.method === 'POST'; |
| 60 | }, | |
| 61 | /** | |
| 62 | * 是否是特定METHOD请求 | |
| 63 | * @param {[type]} method [description] | |
| 64 | * @return {Boolean} [description] | |
| 65 | */ | |
| 66 | isMethod: function(method) { | |
| 67 | 0 | return this.http.method === method.toUpperCase(); |
| 68 | }, | |
| 69 | /** | |
| 70 | * 是否是AJAX请求 | |
| 71 | * @return {Boolean} [description] | |
| 72 | */ | |
| 73 | isAjax: function(method) { | |
| 74 | //请求类型判断 | |
| 75 | 0 | if (method && this.http.method !== method) { |
| 76 | 0 | return false; |
| 77 | } | |
| 78 | 0 | return this.header('x-requested-with') === 'XMLHttpRequest'; |
| 79 | }, | |
| 80 | /** | |
| 81 | * 是否是websocket请求 | |
| 82 | * @return {Boolean} [description] | |
| 83 | */ | |
| 84 | isWebSocket: function(){ | |
| 85 | 0 | return !!this.http.websocket; |
| 86 | }, | |
| 87 | /** | |
| 88 | * 是否是命令行模式 | |
| 89 | * @return {Boolean} [description] | |
| 90 | */ | |
| 91 | isCli: function(){ | |
| 92 | 0 | return APP_MODE === 'cli'; |
| 93 | }, | |
| 94 | /** | |
| 95 | * 是否是jsonp接口 | |
| 96 | * @return {Boolean} [description] | |
| 97 | */ | |
| 98 | isJsonp: function(name){ | |
| 99 | 0 | name = name || C('url_callback_name'); |
| 100 | 0 | return !!this.http.get(name); |
| 101 | }, | |
| 102 | /** | |
| 103 | * 获取QUERY参数 | |
| 104 | * @param {[type]} name [description] | |
| 105 | * @return {[type]} [description] | |
| 106 | */ | |
| 107 | get: function(name) { | |
| 108 | 0 | if (name === undefined) { |
| 109 | 0 | return this.http.get; |
| 110 | } | |
| 111 | 0 | return this.http.get[name] || ''; |
| 112 | }, | |
| 113 | /** | |
| 114 | * 获取POST参数 | |
| 115 | * @param {[type]} name [description] | |
| 116 | * @return {[type]} [description] | |
| 117 | */ | |
| 118 | post: function(name) { | |
| 119 | 0 | var http = this.http; |
| 120 | 0 | if (http.postPromise) { |
| 121 | 0 | return http.postPromise.then(function(data){ |
| 122 | 0 | return name ? (data[name] || '') : data; |
| 123 | }) | |
| 124 | } | |
| 125 | 0 | return name ? (http.post[name] || '') : http.post; |
| 126 | }, | |
| 127 | /** | |
| 128 | * 获取参数 | |
| 129 | * @param {[type]} name [description] | |
| 130 | * @return {[type]} [description] | |
| 131 | */ | |
| 132 | param: function(name) { | |
| 133 | 0 | return this.post(name) || this.get(name); |
| 134 | }, | |
| 135 | /** | |
| 136 | * 获取上传的文件 | |
| 137 | * @param {[type]} name [description] | |
| 138 | * @return {[type]} [description] | |
| 139 | */ | |
| 140 | file: function(name) { | |
| 141 | 0 | var http = this.http; |
| 142 | 0 | if (http.postPromise) { |
| 143 | 0 | return http.postPromise.then(function(){ |
| 144 | 0 | return name ? (http.file[name] || '') : http.file; |
| 145 | }) | |
| 146 | } | |
| 147 | 0 | return name ? (http.file[name] || '') : http.file; |
| 148 | }, | |
| 149 | /** | |
| 150 | * header操作 | |
| 151 | * @param {[type]} name [description] | |
| 152 | * @param {[type]} value [description] | |
| 153 | * @return {[type]} [description] | |
| 154 | */ | |
| 155 | header: function(name, value) { | |
| 156 | 0 | if (name === undefined) { |
| 157 | 0 | return this.http.headers; |
| 158 | 0 | }else if (isObject(name)) { |
| 159 | 0 | for (var key in name) { |
| 160 | 0 | this.header(key, name[key]); |
| 161 | } | |
| 162 | 0 | return this; |
| 163 | 0 | }else if (value !== undefined) { |
| 164 | 0 | this.http.setHeader(name, value); |
| 165 | 0 | return this; |
| 166 | }else{ | |
| 167 | 0 | return this.http.getHeader(name); |
| 168 | } | |
| 169 | }, | |
| 170 | /** | |
| 171 | * 获取userAgent | |
| 172 | * @return {[type]} [description] | |
| 173 | */ | |
| 174 | userAgent: function(){ | |
| 175 | 0 | return this.http.headers['user-agent'] || ''; |
| 176 | }, | |
| 177 | /** | |
| 178 | * 获取referrer | |
| 179 | * @return {[type]} [description] | |
| 180 | */ | |
| 181 | referer: function(host){ | |
| 182 | 0 | var referer = this.http.headers.referer || this.http.headers.referfer || ''; |
| 183 | 0 | if (!referer || !host) { |
| 184 | 0 | return referer; |
| 185 | } | |
| 186 | 0 | var info = url.parse(referer); |
| 187 | 0 | return info.hostname; |
| 188 | }, | |
| 189 | /** | |
| 190 | * cookie操作 | |
| 191 | * @param {[type]} name [description] | |
| 192 | * @param {[type]} value [description] | |
| 193 | * @param {[type]} options [description] | |
| 194 | * @return {[type]} [description] | |
| 195 | */ | |
| 196 | cookie: function(name, value, options) { | |
| 197 | 0 | if (value !== undefined) { |
| 198 | 0 | this.http.setCookie(name, value, options); |
| 199 | 0 | return this; |
| 200 | } | |
| 201 | 0 | return name === undefined ? this.http.cookie : (this.http.cookie[name] || ''); |
| 202 | }, | |
| 203 | /** | |
| 204 | * session | |
| 205 | * 如果是get操作,则返回一个promise | |
| 206 | * @param {[type]} name [description] | |
| 207 | * @param {[type]} value [description] | |
| 208 | * @return {[type]} [description] | |
| 209 | */ | |
| 210 | session: function(name, value) { | |
| 211 | 0 | thinkRequire('Session').start(this.http); |
| 212 | 0 | var instance = this.http.session; |
| 213 | 0 | if (name === undefined) { |
| 214 | 0 | return instance.rm(); |
| 215 | } | |
| 216 | 0 | if (value !== undefined) { |
| 217 | 0 | return instance.set(name, value); |
| 218 | } | |
| 219 | 0 | return instance.get(name); |
| 220 | }, | |
| 221 | /** | |
| 222 | * 跳转,返回一个pendding promise阻止后面继续执行 | |
| 223 | * @param {[type]} url [description] | |
| 224 | * @param {[type]} code [description] | |
| 225 | * @return {[type]} [description] | |
| 226 | */ | |
| 227 | redirect: function(url, code) { | |
| 228 | 0 | this.http.redirect(url, code); |
| 229 | 0 | return getDefer().promise; |
| 230 | }, | |
| 231 | /** | |
| 232 | * 赋值变量到模版 | |
| 233 | * @param {[type]} name [description] | |
| 234 | * @param {[type]} value [description] | |
| 235 | * @return {[type]} [description] | |
| 236 | */ | |
| 237 | assign: function(name, value) { | |
| 238 | 2 | if (arguments.length === 1) { |
| 239 | 0 | return this.initView().assign(name); |
| 240 | } | |
| 241 | 2 | return this.initView().assign(name, value); |
| 242 | }, | |
| 243 | /** | |
| 244 | * 获取解析后的模版内容 | |
| 245 | * @param {[type]} templateFile [description] | |
| 246 | * @param {[type]} content [description] | |
| 247 | * @return {[type]} [description] | |
| 248 | */ | |
| 249 | fetch: function(templateFile, content) { | |
| 250 | 0 | return this.initView().fetch(templateFile, content); |
| 251 | }, | |
| 252 | /** | |
| 253 | * 输出模版内容 | |
| 254 | * @param {[type]} templateFile [description] | |
| 255 | * @param {[type]} charset [description] | |
| 256 | * @param {[type]} contentType [description] | |
| 257 | * @param {[type]} content [description] | |
| 258 | * @return {[type]} [description] | |
| 259 | */ | |
| 260 | display: function(templateFile, charset, contentType, content) { | |
| 261 | 1 | return this.initView().display(templateFile, charset, contentType, content); |
| 262 | }, | |
| 263 | /** | |
| 264 | * 调用另一个controll里的aciton | |
| 265 | * 可以跨分组 | |
| 266 | * A('Admin/Test/index') | |
| 267 | * @param {[type]} action [description] | |
| 268 | * @return {[type]} [description] | |
| 269 | */ | |
| 270 | action: function(action, data) { | |
| 271 | //自动补group | |
| 272 | 0 | if (action.split(':').length === 2) { |
| 273 | 0 | action = this.http.group + ':' + action; |
| 274 | } | |
| 275 | 0 | return A(action, this.http, data); |
| 276 | }, | |
| 277 | /** | |
| 278 | * jsonp格式输出 | |
| 279 | * @param {[type]} data [description] | |
| 280 | * @param {[type]} jsonp [description] | |
| 281 | * @return {[type]} [description] | |
| 282 | */ | |
| 283 | jsonp: function(data) { | |
| 284 | 0 | this.type(C('json_content_type')); |
| 285 | 0 | var callback = this.get(C('url_callback_name')); |
| 286 | //过滤callback值里的非法字符 | |
| 287 | 0 | callback = callback.replace(callbackReg, ''); |
| 288 | 0 | if (callback) { |
| 289 | 0 | this.echo(callback + '('); |
| 290 | 0 | this.echo(data); |
| 291 | 0 | this.end(')'); |
| 292 | } else { | |
| 293 | 0 | this.end(data); |
| 294 | } | |
| 295 | }, | |
| 296 | /** | |
| 297 | * json格式输出 | |
| 298 | * @param {[type]} data [description] | |
| 299 | * @return {[type]} [description] | |
| 300 | */ | |
| 301 | json: function(data){ | |
| 302 | 0 | this.type(C('json_content_type')); |
| 303 | 0 | return this.end(data); |
| 304 | }, | |
| 305 | /** | |
| 306 | * 设置http响应状态码 | |
| 307 | * @param {[type]} status [description] | |
| 308 | * @return {[type]} [description] | |
| 309 | */ | |
| 310 | status: function(status) { | |
| 311 | 0 | var res = this.http.res; |
| 312 | 0 | if (!res.headersSent) { |
| 313 | 0 | res.statusCode = status || 404; |
| 314 | } | |
| 315 | 0 | return this; |
| 316 | }, | |
| 317 | /** | |
| 318 | * 阻止访问 | |
| 319 | * @param {[type]} status [description] | |
| 320 | * @return {[type]} [description] | |
| 321 | */ | |
| 322 | deny: function(status){ | |
| 323 | 0 | var res = this.http.res; |
| 324 | 0 | if (!res.headersSent) { |
| 325 | 0 | res.statusCode = status || 403; |
| 326 | 0 | this.http.end(); |
| 327 | } | |
| 328 | 0 | return getDefer().promise; |
| 329 | }, | |
| 330 | /** | |
| 331 | * 输出内容 | |
| 332 | * 自动JSON.stringify | |
| 333 | * 自定将数字等转化为字符串 | |
| 334 | * @param {[type]} obj [description] | |
| 335 | * @return {[type]} [description] | |
| 336 | */ | |
| 337 | echo: function(obj, encoding) { | |
| 338 | //自动发送Content-Type的header | |
| 339 | 0 | if (C('auto_send_content_type')) { |
| 340 | 0 | this.type(C('tpl_content_type')); |
| 341 | } | |
| 342 | 0 | return this.http.echo(obj, encoding); |
| 343 | }, | |
| 344 | /** | |
| 345 | * 结束输出,输出完成时一定要调用这个方法 | |
| 346 | * @param {[type]} obj [description] | |
| 347 | * @return {[type]} [description] | |
| 348 | */ | |
| 349 | end: function(obj, encoding) { | |
| 350 | 0 | if (obj) { |
| 351 | 0 | this.echo(obj, encoding); |
| 352 | } | |
| 353 | 0 | this.http.end(); |
| 354 | }, | |
| 355 | /** | |
| 356 | * 发送Content-Type | |
| 357 | * @param {[type]} type [description] | |
| 358 | * @return {[type]} [description] | |
| 359 | */ | |
| 360 | type: function(ext){ | |
| 361 | 0 | if (ext.indexOf('/') === -1) { |
| 362 | 0 | ext = require('mime').lookup(ext); |
| 363 | } | |
| 364 | 0 | if (!this.http.cthIsSend) { |
| 365 | 0 | if (ext.toLowerCase().indexOf('charset=') === -1) { |
| 366 | 0 | ext += '; charset=' + C('encoding'); |
| 367 | } | |
| 368 | //Content-Type Header has been Send | |
| 369 | 0 | this.http.cthIsSend = true; |
| 370 | 0 | this.http.setHeader('Content-Type', ext); |
| 371 | }else{ | |
| 372 | 0 | console.log('Content-Type has been send'); |
| 373 | } | |
| 374 | }, | |
| 375 | /** | |
| 376 | * 下载文件 | |
| 377 | * @return {[type]} [description] | |
| 378 | */ | |
| 379 | download: function(file, contentType, callback) { | |
| 380 | 0 | if (isFunction(contentType)) { |
| 381 | 0 | callback = contentType; |
| 382 | 0 | contentType = ''; |
| 383 | } | |
| 384 | 0 | if (!contentType || contentType.indexOf('/') === -1) { |
| 385 | 0 | contentType = require('mime').lookup(contentType || file); |
| 386 | } | |
| 387 | 0 | var http = this.http; |
| 388 | 0 | var fileStream = fs.createReadStream(file); |
| 389 | 0 | this.type(contentType); |
| 390 | 0 | http.setHeader('Content-Disposition', 'attachment; filename="' + path.basename(file) + '"'); |
| 391 | 0 | fileStream.pipe(http.res); |
| 392 | 0 | fileStream.on('end', function() { |
| 393 | 0 | http.end(); |
| 394 | 0 | return callback && callback(); |
| 395 | }); | |
| 396 | }, | |
| 397 | /** | |
| 398 | * 正常json数据输出 | |
| 399 | * @param {[type]} data [description] | |
| 400 | * @return {[type]} [description] | |
| 401 | */ | |
| 402 | success: function(data){ | |
| 403 | 0 | var obj = getObject([C('error_no_key'), C('error_msg_key')], [0, '']); |
| 404 | 0 | if (data !== undefined) { |
| 405 | 0 | obj.data = data; |
| 406 | } | |
| 407 | 0 | this.type(C('json_content_type')); |
| 408 | 0 | this.end(obj); |
| 409 | 0 | return getDefer().promise; |
| 410 | }, | |
| 411 | /** | |
| 412 | * 异常json数据数据 | |
| 413 | * @param {[type]} errno [description] | |
| 414 | * @param {[type]} errmsg [description] | |
| 415 | * @param {[type]} extra [description] | |
| 416 | * @return {[type]} [description] | |
| 417 | */ | |
| 418 | error: function(errno, errmsg, data){ | |
| 419 | 0 | var obj; |
| 420 | 0 | if (isObject(errno)) { |
| 421 | 0 | data = errmsg; |
| 422 | 0 | obj = extend({}, errno); |
| 423 | }else{ | |
| 424 | 0 | if (!isNumber(errno)) { |
| 425 | 0 | data = errmsg; |
| 426 | 0 | errmsg = errno; |
| 427 | 0 | errno = C('error_no_default_value'); |
| 428 | } | |
| 429 | 0 | obj = getObject([C('error_no_key'), C('error_msg_key')], [errno, errmsg]); |
| 430 | } | |
| 431 | 0 | if (data !== undefined) { |
| 432 | 0 | obj.data = data; |
| 433 | } | |
| 434 | 0 | this.type(C('json_content_type')); |
| 435 | 0 | this.end(obj); |
| 436 | 0 | return getDefer().promise; |
| 437 | }, | |
| 438 | /** | |
| 439 | * 发送执行时间 | |
| 440 | * @param {[type]} name [description] | |
| 441 | * @return {[type]} [description] | |
| 442 | */ | |
| 443 | sendTime: function(name){ | |
| 444 | 0 | return this.http.sendTime(name); |
| 445 | }, | |
| 446 | /** | |
| 447 | * 对数据进行过滤 | |
| 448 | * @param {[type]} data [description] | |
| 449 | * @param {[type]} type [description] | |
| 450 | * @return {[type]} [description] | |
| 451 | */ | |
| 452 | filter: function() { | |
| 453 | 0 | var filter = thinkRequire('Filter').filter; |
| 454 | 0 | return filter.apply(null, arguments); |
| 455 | }, | |
| 456 | /** | |
| 457 | * 校验一个值是否合法 | |
| 458 | * @param {[type]} data [description] | |
| 459 | * @param {[type]} validType [description] | |
| 460 | * @return {[type]} [description] | |
| 461 | */ | |
| 462 | valid: function(data, validType) { | |
| 463 | //单个值检测,只返回是否正常 | |
| 464 | 0 | if (validType !== undefined) { |
| 465 | 0 | data = [{ |
| 466 | value: data, | |
| 467 | valid: validType | |
| 468 | }]; | |
| 469 | 0 | var result = thinkRequire('Valid').check(data); |
| 470 | 0 | return result.length === 0; |
| 471 | } | |
| 472 | 0 | return thinkRequire('Valid').check(data); |
| 473 | } | |
| 474 | }; | |
| 475 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var url = require('url'); |
| 2 | 1 | var querystring = require('querystring'); |
| 3 | /** | |
| 4 | * 数据库基类 | |
| 5 | * @return {[type]} [description] | |
| 6 | */ | |
| 7 | 1 | var Db = module.exports = Class(function(){ |
| 8 | 1 | 'use strict'; |
| 9 | //用于查询的sql语句,所有select语句根据该语句解析 | |
| 10 | 1 | var selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%'; |
| 11 | //where条件里的表达式 | |
| 12 | 1 | var comparison = { |
| 13 | 'EQ': '=', | |
| 14 | 'NEQ': '!=', | |
| 15 | '<>': '!=', | |
| 16 | 'GT': '>', | |
| 17 | 'EGT': '>=', | |
| 18 | 'LT': '<', | |
| 19 | 'ELT': '<=', | |
| 20 | 'NOTLIKE': 'NOT LIKE', | |
| 21 | 'LIKE': 'LIKE', | |
| 22 | 'IN': 'IN', | |
| 23 | 'NOTIN': 'NOT IN' | |
| 24 | }; | |
| 25 | 1 | return { |
| 26 | // 数据库类型 | |
| 27 | dbType: null, | |
| 28 | // 当前操作所属的模型名 | |
| 29 | model: '_think_', | |
| 30 | // 当前SQL指令 | |
| 31 | queryStr: '', | |
| 32 | // 操作的sql列表 | |
| 33 | modelSql: [], | |
| 34 | // 数据库连接ID 支持多个连接 | |
| 35 | linkIds: [], | |
| 36 | // 当前连接ID | |
| 37 | linkId: null, | |
| 38 | // 是否已经连接数据库 | |
| 39 | connected: false, | |
| 40 | // 数据库连接参数配置 | |
| 41 | config: '', | |
| 42 | /** | |
| 43 | * 初始化,类似于constrcut,类实例化时自动调用 | |
| 44 | * @return {[type]} [description] | |
| 45 | */ | |
| 46 | init: function(){ | |
| 47 | ||
| 48 | }, | |
| 49 | /** | |
| 50 | * 连接数据库 | |
| 51 | * @return {[type]} [description] | |
| 52 | */ | |
| 53 | initConnect: function(){ | |
| 54 | 0 | if (!this.connected) { |
| 55 | 0 | this.linkId = this.connect(); |
| 56 | } | |
| 57 | }, | |
| 58 | /** | |
| 59 | * 解析set集合 | |
| 60 | * @param {[type]} data [description] | |
| 61 | * @return {[type]} [description] | |
| 62 | */ | |
| 63 | parseSet: function(data){ | |
| 64 | 0 | data = data || {}; |
| 65 | 0 | var set = []; |
| 66 | 0 | for(var key in data){ |
| 67 | 0 | var value = this.parseValue(data[key]); |
| 68 | 0 | if (isScalar(value)) { |
| 69 | 0 | set.push(this.parseKey(key) + '=' + value); |
| 70 | } | |
| 71 | } | |
| 72 | 0 | return ' SET ' + set.join(','); |
| 73 | }, | |
| 74 | /** | |
| 75 | * 解析字段名,具体的数据库里实现 | |
| 76 | * @param {[type]} key [description] | |
| 77 | * @return {[type]} [description] | |
| 78 | */ | |
| 79 | parseKey: function(key){ | |
| 80 | 0 | return key; |
| 81 | }, | |
| 82 | /** | |
| 83 | * value分析 | |
| 84 | * @param {[type]} value [description] | |
| 85 | * @return {[type]} [description] | |
| 86 | */ | |
| 87 | parseValue: function(value){ | |
| 88 | 0 | if (isString(value)) { |
| 89 | 0 | value = '\'' + this.escapeString(value) + '\''; |
| 90 | 0 | }else if(isArray(value)){ |
| 91 | 0 | if ((value[0] + '').toLowerCase() === 'exp') { |
| 92 | 0 | value = this.escapeString(value[1]); |
| 93 | }else{ | |
| 94 | 0 | var self = this; |
| 95 | 0 | value = value.map(function(item){ |
| 96 | 0 | return self.parseValue(item); |
| 97 | }); | |
| 98 | } | |
| 99 | 0 | }else if(isBoolean(value)){ |
| 100 | 0 | value = value ? '1' : '0'; |
| 101 | 0 | }else if (value === null) { |
| 102 | 0 | value = 'null'; |
| 103 | } | |
| 104 | 0 | return value; |
| 105 | }, | |
| 106 | /** | |
| 107 | * field分析 | |
| 108 | * parseField('name'); | |
| 109 | * parseField('name,email'); | |
| 110 | * parseField({ | |
| 111 | * xx_name: 'name', | |
| 112 | * xx_email: 'email' | |
| 113 | * }) | |
| 114 | * @return {[type]} [description] | |
| 115 | */ | |
| 116 | parseField: function(fields){ | |
| 117 | 0 | if (isString(fields) && fields.indexOf(',') > -1) { |
| 118 | 0 | fields = fields.split(','); |
| 119 | } | |
| 120 | 0 | if (isArray(fields)) { |
| 121 | 0 | var self = this; |
| 122 | 0 | return fields.map(function(item){ |
| 123 | 0 | return self.parseKey(item); |
| 124 | }).join(','); | |
| 125 | 0 | }else if(isObject(fields)){ |
| 126 | 0 | var data = []; |
| 127 | 0 | for(var key in fields){ |
| 128 | 0 | data.push(this.parseKey(key) + ' AS ' + this.parseKey(fields[key])); |
| 129 | } | |
| 130 | 0 | return data.join(','); |
| 131 | 0 | }else if(isString(fields) && fields){ |
| 132 | 0 | return this.parseKey(fields); |
| 133 | } | |
| 134 | 0 | return '*'; |
| 135 | }, | |
| 136 | /** | |
| 137 | * table别名分析 | |
| 138 | * @param {[type]} tables [description] | |
| 139 | * @return {[type]} [description] | |
| 140 | */ | |
| 141 | parseTable: function(tables){ | |
| 142 | 0 | if (isString(tables)) { |
| 143 | 0 | tables = tables.split(','); |
| 144 | } | |
| 145 | 0 | if (isArray(tables)) { |
| 146 | 0 | var self = this; |
| 147 | 0 | return tables.map(function(item){ |
| 148 | 0 | return self.parseKey(item); |
| 149 | }).join(','); | |
| 150 | 0 | }else if (isObject(tables)) { |
| 151 | 0 | var data = []; |
| 152 | 0 | for(var key in tables){ |
| 153 | 0 | data.push(this.parseKey(key) + ' AS ' + this.parseKey(tables[key])); |
| 154 | } | |
| 155 | 0 | return data.join(','); |
| 156 | } | |
| 157 | 0 | return ''; |
| 158 | }, | |
| 159 | /** | |
| 160 | * where条件分析 | |
| 161 | * @param {[type]} where [description] | |
| 162 | * @return {[type]} [description] | |
| 163 | */ | |
| 164 | parseWhere: function(where){ | |
| 165 | 0 | var whereStr = ''; |
| 166 | 0 | var self = this; |
| 167 | 0 | where = where || {}; |
| 168 | 0 | if (isString(where)) { |
| 169 | 0 | whereStr = where; |
| 170 | }else{ | |
| 171 | // 定义逻辑运算规则 例如 OR XOR AND NOT | |
| 172 | 0 | var oList = ['AND', 'OR', 'XOR']; |
| 173 | 0 | var operate = (where._logic + '').toUpperCase(); |
| 174 | 0 | delete where._logic; |
| 175 | 0 | operate = oList.indexOf(operate) > -1 ? ' ' + operate + ' ' : ' AND '; |
| 176 | //key值的安全检测正则 | |
| 177 | 0 | var keySafeRegExp = /^[\w\|\&\-\.\(\)\,]+$/; |
| 178 | 0 | var multi = where._multi; |
| 179 | 0 | delete where._multi; |
| 180 | ||
| 181 | 0 | var val; |
| 182 | 0 | var fn = function(item, i){ |
| 183 | 0 | var v = multi ? val[i] : val; |
| 184 | 0 | return '(' + self.parseWhereItem(self.parseKey(item), v) + ')'; |
| 185 | }; | |
| 186 | 0 | for(var key in where){ |
| 187 | 0 | key = key.trim(); |
| 188 | 0 | val = where[key]; |
| 189 | 0 | whereStr += '( '; |
| 190 | 0 | if (key.indexOf('_') === 0) { |
| 191 | // 解析特殊条件表达式 | |
| 192 | 0 | whereStr += this.parseThinkWhere(key, val); |
| 193 | }else{ | |
| 194 | 0 | if (!keySafeRegExp.test(key)) { |
| 195 | 0 | console.log(key + ' is not safe'); |
| 196 | 0 | continue; |
| 197 | } | |
| 198 | 0 | var arr; |
| 199 | // 支持 name|title|nickname 方式定义查询字段 | |
| 200 | 0 | if (key.indexOf('|') > -1) { |
| 201 | 0 | arr = key.split('|'); |
| 202 | 0 | whereStr += arr.map(fn).join(' OR '); |
| 203 | 0 | }else if (key.indexOf('&') > -1) { |
| 204 | 0 | arr = key.split('&'); |
| 205 | 0 | whereStr += arr.map(fn).join(' AND '); |
| 206 | }else{ | |
| 207 | 0 | whereStr += this.parseWhereItem(this.parseKey(key), val); |
| 208 | } | |
| 209 | } | |
| 210 | 0 | whereStr += ' )' + operate; |
| 211 | } | |
| 212 | 0 | whereStr = whereStr.substr(0, whereStr.length - operate.length); |
| 213 | } | |
| 214 | ||
| 215 | 0 | return whereStr ? (' WHERE ' + whereStr) : ''; |
| 216 | }, | |
| 217 | /** | |
| 218 | * 解析单个查询条件 | |
| 219 | * @param {[type]} key [description] | |
| 220 | * @param {[type]} val [description] | |
| 221 | * @return {[type]} [description] | |
| 222 | */ | |
| 223 | parseWhereItem: function(key, val){ | |
| 224 | 0 | if (!isArray(val)) { |
| 225 | //对字符串类型字段采用模糊匹配 | |
| 226 | 0 | if (C('db_like_fields').indexOf(key) > -1) { |
| 227 | 0 | return key + ' LIKE ' + this.parseValue('%' + val + '%'); |
| 228 | }else{ | |
| 229 | 0 | return key + ' = ' + this.parseValue(val); |
| 230 | } | |
| 231 | } | |
| 232 | 0 | var whereStr = ''; |
| 233 | 0 | var data; |
| 234 | 0 | if (isString(val[0])) { |
| 235 | 0 | var val0 = val[0].toUpperCase(); |
| 236 | 0 | val0 = comparison[val0] || val0; |
| 237 | 0 | if (/^(=|!=|>|>=|<|<=)$/.test(val0)) { // 比较运算 |
| 238 | 0 | whereStr += key + ' ' + val0 + ' ' + this.parseValue(val[1]); |
| 239 | 0 | }else if (/^(NOT\s+LIKE|LIKE)$/.test(val0)) { // 模糊查找 |
| 240 | 0 | if (isArray(val[1])) { |
| 241 | 0 | var likeLogic = (val[2] ? val[2] : 'OR').toUpperCase(); |
| 242 | 0 | var likesLogic = ['AND','OR','XOR']; |
| 243 | 0 | if (likesLogic.indexOf(likeLogic) > -1) { |
| 244 | 0 | var like = val[1].map(function(item){ |
| 245 | 0 | return key + ' ' + val0 + ' ' + this.parseValue(item); |
| 246 | }).join(likeLogic); | |
| 247 | 0 | whereStr += '(' + like + ')'; |
| 248 | } | |
| 249 | }else{ | |
| 250 | 0 | whereStr += key + ' ' + val0 + ' ' + this.parseValue(val[1]); |
| 251 | } | |
| 252 | 0 | }else if(val0 === 'EXP'){ // 使用表达式 |
| 253 | 0 | whereStr += '(' + key + ' ' + val[1] + ')'; |
| 254 | 0 | }else if(val0 === 'IN' || val0 === 'NOT IN'){ // IN 运算 |
| 255 | 0 | if (val[2] === 'exp') { |
| 256 | 0 | whereStr += key + ' ' + val0 + ' ' + val[1]; |
| 257 | }else{ | |
| 258 | 0 | if (isString(val[1])) { |
| 259 | 0 | val[1] = val[1].split(','); |
| 260 | } | |
| 261 | 0 | val[1] = this.parseValue(val[1]); |
| 262 | //如果只有一个值,那么变成=或者!= | |
| 263 | 0 | if (val[1].length === 1) { |
| 264 | 0 | whereStr += key + (val0 === 'IN' ? ' = ' : ' != ') + val[1]; |
| 265 | }else{ | |
| 266 | 0 | whereStr += key + ' ' + val0 + ' (' + val[1].join(',') + ')'; |
| 267 | } | |
| 268 | } | |
| 269 | 0 | }else if(val0 === 'BETWEEN'){ // BETWEEN运算 |
| 270 | 0 | data = isString(val[1]) ? val[1].split(',') : val[1]; |
| 271 | 0 | if (!isArray(data)) { |
| 272 | 0 | data = [val[1], val[2]]; |
| 273 | } | |
| 274 | 0 | whereStr += ' (' + key + ' ' + val0 + ' ' + this.parseValue(data[0]); |
| 275 | 0 | whereStr += ' AND ' + this.parseValue(data[1]) + ')'; |
| 276 | }else{ | |
| 277 | 0 | console.log('_EXPRESS_ERROR_', key, val); |
| 278 | 0 | return ''; |
| 279 | } | |
| 280 | }else{ | |
| 281 | 0 | var length = val.length; |
| 282 | 0 | var rule = val[val.length - 1] || ''; |
| 283 | 0 | if (rule && ['AND','OR','XOR'].indexOf(rule) > -1) { |
| 284 | 0 | length = length - 1; |
| 285 | }else{ | |
| 286 | 0 | rule = 'AND'; |
| 287 | } | |
| 288 | 0 | for(var i = 0; i < length; i++){ |
| 289 | 0 | data = isArray(val[i]) ? val[i][1] : val[i]; |
| 290 | 0 | var exp = ((isArray(val[i]) && val[i][0]) + '').toLowerCase(); |
| 291 | 0 | if (exp === 'exp') { |
| 292 | 0 | whereStr += '(' + key + ' ' + data + ') ' + rule + ' '; |
| 293 | }else{ | |
| 294 | 0 | var op = isArray(val[i]) ? comparison[val[i][0].toUpperCase()] : '='; |
| 295 | 0 | whereStr += '(' + key + ' ' + op + ' ' + this.parseValue(data) + ') ' + rule + ' '; |
| 296 | } | |
| 297 | } | |
| 298 | 0 | whereStr = whereStr.substr(0, whereStr.length - 4); |
| 299 | } | |
| 300 | 0 | return whereStr; |
| 301 | }, | |
| 302 | /** | |
| 303 | * 解析一些特殊的where条件 | |
| 304 | * @param {[type]} key [description] | |
| 305 | * @param {[type]} val [description] | |
| 306 | * @return {[type]} [description] | |
| 307 | */ | |
| 308 | parseThinkWhere: function(key, val){ | |
| 309 | 0 | switch(key){ |
| 310 | // 字符串模式查询条件 | |
| 311 | case '_string': | |
| 312 | 0 | return val; |
| 313 | // 复合查询条件 | |
| 314 | case '_complex': | |
| 315 | 0 | return this.parseWhere(val).substr(6); |
| 316 | // 字符串模式查询条件 | |
| 317 | case '_query': | |
| 318 | 0 | var where = querystring.parse(val); |
| 319 | 0 | var op = ' AND '; |
| 320 | 0 | if ('_logic' in where) { |
| 321 | 0 | op = ' ' + where._logic.toLowerCase() + ' '; |
| 322 | 0 | delete where._logic; |
| 323 | } | |
| 324 | 0 | var arr = []; |
| 325 | 0 | for(var name in where){ |
| 326 | 0 | val = where[name]; |
| 327 | 0 | val = this.parseKey(name) + ' = ' + this.parseValue(val); |
| 328 | 0 | arr.push(val); |
| 329 | } | |
| 330 | 0 | return arr.join(op); |
| 331 | default: | |
| 332 | 0 | return ''; |
| 333 | } | |
| 334 | 0 | return ''; |
| 335 | }, | |
| 336 | /** | |
| 337 | * 解析limit,对非法的limit进行过滤 | |
| 338 | * @param {[type]} limit [description] | |
| 339 | * @return {[type]} [description] | |
| 340 | */ | |
| 341 | parseLimit: function(limit){ | |
| 342 | 0 | limit = (limit + '').split(',').slice(0, 2); |
| 343 | 0 | var flag = limit.every(function(item){ |
| 344 | 0 | return isNumberString(item); |
| 345 | }); | |
| 346 | 0 | if (!flag) { |
| 347 | 0 | return ''; |
| 348 | } | |
| 349 | 0 | limit = limit.join(','); |
| 350 | 0 | return limit ? (' LIMIT ' + limit) : ''; |
| 351 | }, | |
| 352 | /** | |
| 353 | * 解析join | |
| 354 | * @param {[type]} join [description] | |
| 355 | * @return {[type]} [description] | |
| 356 | */ | |
| 357 | parseJoin: function(join){ | |
| 358 | 0 | var joinStr = ''; |
| 359 | 0 | if (!join) { |
| 360 | 0 | return ''; |
| 361 | } | |
| 362 | 0 | if (isArray(join)) { |
| 363 | 0 | join.forEach(function(val){ |
| 364 | 0 | if (val.toLowerCase().indexOf('join') > -1) { |
| 365 | 0 | joinStr += val; |
| 366 | }else{ | |
| 367 | 0 | joinStr += ' LEFT JOIN ' + val; |
| 368 | } | |
| 369 | }); | |
| 370 | }else{ | |
| 371 | 0 | joinStr += ' LEFT JOIN ' + join; |
| 372 | } | |
| 373 | 0 | return joinStr; |
| 374 | }, | |
| 375 | /** | |
| 376 | * 解析order | |
| 377 | * @param {[type]} order [description] | |
| 378 | * @return {[type]} [description] | |
| 379 | */ | |
| 380 | parseOrder: function(order){ | |
| 381 | 0 | var self = this; |
| 382 | 0 | if (isArray(order)) { |
| 383 | 0 | order = order.map(function(item){ |
| 384 | 0 | return self.parseKey(item); |
| 385 | }).join(','); | |
| 386 | 0 | }else if (isObject(order)) { |
| 387 | 0 | var arr = []; |
| 388 | 0 | for(var key in order){ |
| 389 | 0 | var val = order[key]; |
| 390 | 0 | val = this.parseKey(key) + ' ' + val; |
| 391 | 0 | arr.push(val); |
| 392 | } | |
| 393 | 0 | order = arr.join(','); |
| 394 | } | |
| 395 | 0 | return order ? (' ORDER BY ' + order) : ''; |
| 396 | }, | |
| 397 | /** | |
| 398 | * 解析group | |
| 399 | * @param {[type]} group [description] | |
| 400 | * @return {[type]} [description] | |
| 401 | */ | |
| 402 | parseGroup: function(group){ | |
| 403 | 0 | return group ? (' GROUP BY ' + group) : ''; |
| 404 | }, | |
| 405 | /** | |
| 406 | * 解析having | |
| 407 | * @param {[type]} having [description] | |
| 408 | * @return {[type]} [description] | |
| 409 | */ | |
| 410 | parseHaving: function(having){ | |
| 411 | 0 | return having ? (' HAVING ' + having) : ''; |
| 412 | }, | |
| 413 | /** | |
| 414 | * 解析注释,一般情况下用不到 | |
| 415 | * @param {[type]} comment [description] | |
| 416 | * @return {[type]} [description] | |
| 417 | */ | |
| 418 | parseComment: function(comment){ | |
| 419 | 0 | return comment ? (' /* ' + comment + '*/') : ''; |
| 420 | }, | |
| 421 | /** | |
| 422 | * 解析Distinct | |
| 423 | * @param {[type]} distinct [description] | |
| 424 | * @return {[type]} [description] | |
| 425 | */ | |
| 426 | parseDistinct: function(distinct){ | |
| 427 | 0 | return distinct ? ' Distinct ' : ''; |
| 428 | }, | |
| 429 | /** | |
| 430 | * 解析Union | |
| 431 | * @param {[type]} union [description] | |
| 432 | * @return {[type]} [description] | |
| 433 | */ | |
| 434 | parseUnion: function(union){ | |
| 435 | 0 | if (!union) { |
| 436 | 0 | return ''; |
| 437 | } | |
| 438 | 0 | var str = ''; |
| 439 | 0 | if ('_all' in union) { |
| 440 | 0 | str = 'UNION ALL '; |
| 441 | 0 | delete union._all; |
| 442 | }else{ | |
| 443 | 0 | str = 'UNION '; |
| 444 | } | |
| 445 | 0 | var sql = []; |
| 446 | 0 | for(var key in union){ |
| 447 | 0 | var val = union[key]; |
| 448 | 0 | val = str + (isArray(val) ? this.buildSelectSql(val) : val); |
| 449 | 0 | sql.push(sql); |
| 450 | } | |
| 451 | 0 | return sql.join(' '); |
| 452 | }, | |
| 453 | /** | |
| 454 | * 解析Lock | |
| 455 | * @param {[type]} lock [description] | |
| 456 | * @return {[type]} [description] | |
| 457 | */ | |
| 458 | parseLock: function(lock){ | |
| 459 | 0 | if (!lock) { |
| 460 | 0 | return ''; |
| 461 | } | |
| 462 | 0 | return ' FOR UPDATE '; |
| 463 | }, | |
| 464 | /** | |
| 465 | * 将page转化为sql里的limit | |
| 466 | * @return {[type]} [description] | |
| 467 | */ | |
| 468 | pageToLimit: function(options){ | |
| 469 | 0 | options = options || {}; |
| 470 | //根据page生成limit | |
| 471 | 0 | if ('page' in options) { |
| 472 | 0 | var page = options.page + ''; |
| 473 | 0 | var listRows = 0; |
| 474 | 0 | if (page.indexOf(',') > -1) { |
| 475 | 0 | page = page.split(','); |
| 476 | 0 | listRows = page[1]; |
| 477 | 0 | page = page[0]; |
| 478 | } | |
| 479 | 0 | page = parseInt(page, 10) || 1; |
| 480 | 0 | if (!listRows) { |
| 481 | 0 | listRows = isNumberString(options.limit) ? options.limit : C('db_nums_per_page'); |
| 482 | } | |
| 483 | 0 | var offset = listRows * (page - 1); |
| 484 | 0 | options.limit = offset + ',' + listRows; |
| 485 | } | |
| 486 | 0 | return options; |
| 487 | }, | |
| 488 | /** | |
| 489 | * 拼接select查询语句 | |
| 490 | * @param {[type]} options [description] | |
| 491 | * @return {[type]} [description] | |
| 492 | */ | |
| 493 | buildSelectSql: function(options){ | |
| 494 | 0 | options = this.pageToLimit(options); |
| 495 | 0 | var sql = this.parseSql(selectSql, options); |
| 496 | 0 | sql += this.parseLock(options.lock); |
| 497 | 0 | return sql; |
| 498 | }, | |
| 499 | /** | |
| 500 | * 解析sql语句 | |
| 501 | * @param {[type]} sql [description] | |
| 502 | * @param {[type]} options [description] | |
| 503 | * @return {[type]} [description] | |
| 504 | */ | |
| 505 | parseSql: function(sql, options){ | |
| 506 | 0 | options = options || {}; |
| 507 | 0 | var self = this; |
| 508 | 0 | return sql.replace(/\%([A-Z]+)\%/g, function(a, type){ |
| 509 | 0 | type = type.toLowerCase(); |
| 510 | 0 | return self['parse' + ucfirst(type)](options[type] || ''); |
| 511 | }).replace(/__([A-Z_-]+)__/g, function(a, b){ | |
| 512 | 0 | return '`' + C('db_prefix') + b.toLowerCase() + '`'; |
| 513 | }); | |
| 514 | }, | |
| 515 | /** | |
| 516 | * 插入一条记录 | |
| 517 | * @param {[type]} data [description] | |
| 518 | * @param {[type]} options [description] | |
| 519 | * @param {[type]} replace [description] | |
| 520 | * @return {[type]} [description] | |
| 521 | */ | |
| 522 | insert: function(data, options, replace){ | |
| 523 | 0 | data = data || {}; |
| 524 | 0 | options = options || {}; |
| 525 | 0 | var values = []; |
| 526 | 0 | var fields = []; |
| 527 | 0 | this.model = options.model; |
| 528 | 0 | for(var key in data){ |
| 529 | 0 | var val = data[key]; |
| 530 | 0 | val = this.parseValue(val); |
| 531 | 0 | if (isScalar(val)) { |
| 532 | 0 | values.push(val); |
| 533 | 0 | fields.push(this.parseKey(key)); |
| 534 | } | |
| 535 | } | |
| 536 | 0 | var sql = (replace ? 'REPLACE' : 'INSERT') + ' INTO '; |
| 537 | 0 | sql += this.parseTable(options.table) + ' (' + fields.join(',') + ') '; |
| 538 | 0 | sql += 'VALUES(' + values.join(',') + ')'; |
| 539 | 0 | sql += this.parseLock(options.lock) + this.parseComment(options.comment); |
| 540 | 0 | return this.execute(sql); |
| 541 | }, | |
| 542 | /** | |
| 543 | * 插入多条记录 | |
| 544 | * @param {[type]} data [description] | |
| 545 | * @param {[type]} options [description] | |
| 546 | * @param {[type]} replace [description] | |
| 547 | * @return {[type]} [description] | |
| 548 | */ | |
| 549 | insertAll: function(data, options, replace){ | |
| 550 | 0 | var fields = Object.keys(data[0]); |
| 551 | 0 | var self = this; |
| 552 | 0 | fields = fields.map(function(item){ |
| 553 | 0 | return self.parseKey(item); |
| 554 | }).join(','); | |
| 555 | 0 | var values = data.map(function(item){ |
| 556 | 0 | var value = []; |
| 557 | 0 | for(var key in item){ |
| 558 | 0 | var val = item[key]; |
| 559 | 0 | val = self.parseValue(val); |
| 560 | 0 | if (isScalar(val)) { |
| 561 | 0 | value.push(val); |
| 562 | } | |
| 563 | } | |
| 564 | 0 | return '(' + value.join(',') + ')'; |
| 565 | }).join(','); | |
| 566 | 0 | var sql = replace ? 'REPLACE' : 'INSERT'; |
| 567 | 0 | sql += ' INTO ' + this.parseTable(options.table) + '(' + fields + ') VALUES ' + values; |
| 568 | 0 | return this.execute(sql); |
| 569 | }, | |
| 570 | /** | |
| 571 | * 从一个选择条件的结果插入记录 | |
| 572 | * @param {[type]} fields [description] | |
| 573 | * @param {[type]} table [description] | |
| 574 | * @param {[type]} options [description] | |
| 575 | * @return {[type]} [description] | |
| 576 | */ | |
| 577 | selectInsert: function(fields, table, options){ | |
| 578 | 0 | options = options || {}; |
| 579 | 0 | this.model = options.model; |
| 580 | 0 | if (isString(fields)) { |
| 581 | 0 | fields = fields.split(','); |
| 582 | } | |
| 583 | 0 | var self = this; |
| 584 | 0 | fields = fields.map(function(item){ |
| 585 | 0 | return self.parseKey(item); |
| 586 | }); | |
| 587 | 0 | var sql = 'INSERT INTO ' + this.parseTable(options.table) + ' (' + fields.join(',') + ')'; |
| 588 | 0 | sql += this.buildSelectSql(options); |
| 589 | 0 | return this.execute(sql); |
| 590 | }, | |
| 591 | /** | |
| 592 | * 删除记录 | |
| 593 | * @param {[type]} options [description] | |
| 594 | * @return {[type]} [description] | |
| 595 | */ | |
| 596 | delete: function(options){ | |
| 597 | 0 | options = options || {}; |
| 598 | 0 | this.model = options.model; |
| 599 | 0 | var sql = [ |
| 600 | 'DELETE FROM ', | |
| 601 | this.parseTable(options.table), | |
| 602 | this.parseWhere(options.where), | |
| 603 | this.parseOrder(options.order), | |
| 604 | this.parseLimit(options.limit), | |
| 605 | this.parseLock(options.lock), | |
| 606 | this.parseComment(options.comment) | |
| 607 | ].join(''); | |
| 608 | 0 | return this.execute(sql); |
| 609 | }, | |
| 610 | /** | |
| 611 | * 更新数据 | |
| 612 | * @param {[type]} data [description] | |
| 613 | * @param {[type]} options [description] | |
| 614 | * @return {[type]} [description] | |
| 615 | */ | |
| 616 | update: function(data, options){ | |
| 617 | 0 | options = options || {}; |
| 618 | 0 | this.model = options.model; |
| 619 | 0 | var sql = [ |
| 620 | 'UPDATE ', | |
| 621 | this.parseTable(options.table), | |
| 622 | this.parseSet(data), | |
| 623 | this.parseWhere(options.where), | |
| 624 | this.parseOrder(options.order), | |
| 625 | this.parseLimit(options.limit), | |
| 626 | this.parseLock(options.lock), | |
| 627 | this.parseComment(options.comment) | |
| 628 | ].join(''); | |
| 629 | 0 | return this.execute(sql); |
| 630 | }, | |
| 631 | /** | |
| 632 | * 数据查询 | |
| 633 | * @todo 返回是个promise,缓存调用需要修改 | |
| 634 | * @param {[type]} options [description] | |
| 635 | * @return {[type]} [description] | |
| 636 | */ | |
| 637 | select: function(options){ | |
| 638 | 0 | var sql, cache; |
| 639 | 0 | if (isString(options) && options.indexOf('SELECT') > -1) { |
| 640 | 0 | sql = options; |
| 641 | 0 | cache = arguments[1]; |
| 642 | }else{ | |
| 643 | 0 | options = options || {}; |
| 644 | 0 | this.model = options.model; |
| 645 | 0 | sql = this.buildSelectSql(options); |
| 646 | 0 | cache = options.cache; |
| 647 | } | |
| 648 | 0 | var self = this; |
| 649 | 0 | var cacheOn = !isEmpty(cache) && C('db_cache_on'); |
| 650 | //获取数据 | |
| 651 | 0 | function queryData(){ |
| 652 | 0 | return self.query(sql).then(function(data){ |
| 653 | 0 | if (cacheOn) { |
| 654 | 0 | S(key, data, cache); |
| 655 | } | |
| 656 | 0 | return data; |
| 657 | }); | |
| 658 | } | |
| 659 | 0 | if (cacheOn) { |
| 660 | 0 | var key = isString(cache.key) && cache.key ? cache.key : md5(sql); |
| 661 | 0 | return S(key, undefined, cache).then(function(value){ |
| 662 | 0 | return value || queryData(); |
| 663 | }); | |
| 664 | } | |
| 665 | 0 | return queryData(); |
| 666 | }, | |
| 667 | /** | |
| 668 | * 转义字符 | |
| 669 | * @param {[type]} str [description] | |
| 670 | * @return {[type]} [description] | |
| 671 | */ | |
| 672 | escapeString: function(str){ | |
| 673 | 0 | if (!str) { |
| 674 | 0 | return ''; |
| 675 | } | |
| 676 | 0 | return str.replace(/[\0\n\r\b\t\\\'\"\x1a]/g, function(s) { |
| 677 | 0 | switch(s) { |
| 678 | case '\0': | |
| 679 | 0 | return '\\0'; |
| 680 | case '\n': | |
| 681 | 0 | return '\\n'; |
| 682 | case '\r': | |
| 683 | 0 | return '\\r'; |
| 684 | case '\b': | |
| 685 | 0 | return '\\b'; |
| 686 | case '\t': | |
| 687 | 0 | return '\\t'; |
| 688 | case '\x1a': | |
| 689 | 0 | return '\\Z'; |
| 690 | default: | |
| 691 | 0 | return '\\'+s; |
| 692 | } | |
| 693 | }); | |
| 694 | }, | |
| 695 | /** | |
| 696 | * 获取上次的sql语句 | |
| 697 | * @param {[type]} model [description] | |
| 698 | * @return {[type]} [description] | |
| 699 | */ | |
| 700 | getLastSql: function(model){ | |
| 701 | 0 | return model ? this.modelSql[model] : this.queryStr; |
| 702 | }, | |
| 703 | /** | |
| 704 | * 设置模型 | |
| 705 | * @param {[type]} model [description] | |
| 706 | */ | |
| 707 | setModel: function(model){ | |
| 708 | 0 | this.model = model; |
| 709 | 0 | return this; |
| 710 | } | |
| 711 | }; | |
| 712 | }); | |
| 713 | /** | |
| 714 | * 解析dsn | |
| 715 | * 格式: mysql://username:passwd@localhost:3306/DbName | |
| 716 | * @param string dsn [description] | |
| 717 | * @return {[type]} [description] | |
| 718 | */ | |
| 719 | 1 | Db.parseDSN = function(dsn){ |
| 720 | 0 | 'use strict'; |
| 721 | 0 | if (!dsn) { |
| 722 | 0 | return false; |
| 723 | } | |
| 724 | 0 | var info = url.parse(dsn); |
| 725 | 0 | var auth = (info.auth || '').split(':'); |
| 726 | 0 | return { |
| 727 | 'dbms': info.protocol, | |
| 728 | 'username': auth[0] || '', | |
| 729 | 'password': auth[1] || '', | |
| 730 | 'hostname': info.hostname || '', | |
| 731 | 'hostport': info.port || '', | |
| 732 | 'database': (info.pathname || '').substr(1), | |
| 733 | 'dsn': '' | |
| 734 | }; | |
| 735 | }; | |
| 736 | /** | |
| 737 | * 解析配置 | |
| 738 | * @param {[type]} config [description] | |
| 739 | * @return {[type]} [description] | |
| 740 | */ | |
| 741 | 1 | Db.parseConfig = function(config){ |
| 742 | 0 | 'use strict'; |
| 743 | 0 | if (config && isString(config)) { |
| 744 | 0 | return this.parseDSN(config); |
| 745 | 0 | }else if(isObject(config)){ |
| 746 | 0 | return { |
| 747 | 'dbms': config.db_type || 'mysql', | |
| 748 | 'username': config.db_user, | |
| 749 | 'password': config.db_pwd, | |
| 750 | 'hostname': config.db_host, | |
| 751 | 'hostport': config.db_port, | |
| 752 | 'database': config.db_name, | |
| 753 | 'dsn': config.db_dsn, | |
| 754 | 'params': config.db_params | |
| 755 | }; | |
| 756 | 0 | }else if(!config){ |
| 757 | 0 | if (C('db_dsn')) { |
| 758 | 0 | return this.parseDSN(C('db_dsn')); |
| 759 | } | |
| 760 | 0 | return { |
| 761 | 'dbms' : C('db_type'), | |
| 762 | 'username' : C('db_user'), | |
| 763 | 'password' : C('db_pwd'), | |
| 764 | 'hostname' : C('db_host'), | |
| 765 | 'hostport' : C('db_port'), | |
| 766 | 'database' : C('db_name'), | |
| 767 | 'dsn' : C('db_dsn'), | |
| 768 | 'params' : C('db_params'), | |
| 769 | }; | |
| 770 | } | |
| 771 | 0 | return config; |
| 772 | }; | |
| 773 | /** | |
| 774 | * 根据配置获取对应的数据库实例 | |
| 775 | * @param {[type]} config [description] | |
| 776 | * @return {[type]} [description] | |
| 777 | */ | |
| 778 | 1 | Db.getInstance = function(config){ |
| 779 | 0 | 'use strict'; |
| 780 | 0 | config = this.parseConfig(config); |
| 781 | 0 | if (!config.dbms) { |
| 782 | 0 | console.log('no dbms config'); |
| 783 | 0 | return false; |
| 784 | } | |
| 785 | //数据库类型 | |
| 786 | 0 | var dbType = config.dbms.toLowerCase(); |
| 787 | 0 | dbType = dbType.substr(0, 1).toUpperCase() + dbType.substr(1); |
| 788 | 0 | var instance = thinkRequire(dbType + 'Db')(config); |
| 789 | 0 | instance.dbType = dbType.toUpperCase(); |
| 790 | 0 | return instance; |
| 791 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 路由识别 | |
| 3 | * @type {Object} | |
| 4 | */ | |
| 5 | 1 | var Dispatcher = module.exports = Class(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | 1 | return { |
| 8 | /** | |
| 9 | * 初始化 | |
| 10 | * @param {[type]} http [description] | |
| 11 | * @return {[type]} [description] | |
| 12 | */ | |
| 13 | init: function(http){ | |
| 14 | 1 | this.http = http; |
| 15 | }, | |
| 16 | /** | |
| 17 | * 准备pathanem | |
| 18 | * @return {[type]} [description] | |
| 19 | */ | |
| 20 | preparePathName: function(){ | |
| 21 | 1 | var pathname = this.http.pathname.split('/').filter(function(item){ |
| 22 | 2 | return item.trim(); |
| 23 | }).join('/'); | |
| 24 | //去除pathname前缀 | |
| 25 | 1 | var prefix = C('url_pathname_prefix'); |
| 26 | 1 | if (prefix && pathname.indexOf(prefix) === 0) { |
| 27 | 0 | pathname = pathname.substr(prefix.length); |
| 28 | } | |
| 29 | //判断URL后缀 | |
| 30 | 1 | var suffix = C('url_pathname_suffix'); |
| 31 | 1 | if (suffix && pathname.substr(0 - suffix.length) === suffix) { |
| 32 | 0 | pathname = pathname.substr(0, pathname.length - suffix.length); |
| 33 | } | |
| 34 | 1 | this.http.pathname = pathname; |
| 35 | }, | |
| 36 | /** | |
| 37 | * 解析pathname | |
| 38 | * @return {[type]} [description] | |
| 39 | */ | |
| 40 | parsePathName: function(){ | |
| 41 | 1 | if (this.http.group) { |
| 42 | 0 | return true; |
| 43 | } | |
| 44 | 1 | var paths = this.http.pathname.split('/'); |
| 45 | //将group list变为小写 | |
| 46 | 1 | var groupList = C('app_group_list').map(function(item){ |
| 47 | 2 | return item.toLowerCase(); |
| 48 | }); | |
| 49 | 1 | var group = ''; |
| 50 | 1 | if (groupList.length && paths[0] && groupList.indexOf(paths[0].toLowerCase()) > -1) { |
| 51 | 0 | group = paths.shift(); |
| 52 | } | |
| 53 | 1 | var controller = paths.shift(); |
| 54 | 1 | var action = paths.shift(); |
| 55 | //解析剩余path的参数 | |
| 56 | 1 | if (paths.length) { |
| 57 | 0 | for(var i = 0,length = Math.ceil(paths.length) / 2; i < length; i++){ |
| 58 | 0 | this.http.get[paths[i * 2]] = paths[i * 2 + 1] || ''; |
| 59 | } | |
| 60 | } | |
| 61 | 1 | this.http.group = Dispatcher.getGroup(group); |
| 62 | 1 | this.http.controller = Dispatcher.getController(controller); |
| 63 | 1 | this.http.action = Dispatcher.getAction(action); |
| 64 | }, | |
| 65 | /** | |
| 66 | * run | |
| 67 | * @return {[type]} [description] | |
| 68 | */ | |
| 69 | run: function(){ | |
| 70 | 1 | var self = this; |
| 71 | 1 | return tag('resource_check', this.http).then(function(){ |
| 72 | 1 | return self.preparePathName(); |
| 73 | }).then(function(){ | |
| 74 | 1 | return tag('path_info', self.http); |
| 75 | }).then(function(){ | |
| 76 | 1 | return tag('route_check', self.http); |
| 77 | }).then(function(){ | |
| 78 | 1 | return self.parsePathName(); |
| 79 | }); | |
| 80 | } | |
| 81 | }; | |
| 82 | }); | |
| 83 | ||
| 84 | /** | |
| 85 | * 获取group | |
| 86 | * @param {[type]} group [description] | |
| 87 | * @return {[type]} [description] | |
| 88 | */ | |
| 89 | 1 | Dispatcher.getGroup = function(group){ |
| 90 | 1 | 'use strict'; |
| 91 | 1 | group = group || C('default_group'); |
| 92 | 1 | return ucfirst(group); |
| 93 | }; | |
| 94 | /** | |
| 95 | * 获取controller | |
| 96 | * @param {[type]} controller [description] | |
| 97 | * @return {[type]} [description] | |
| 98 | */ | |
| 99 | 1 | Dispatcher.getController = function(controller){ |
| 100 | 1 | 'use strict'; |
| 101 | 1 | controller = controller || C('default_controller'); |
| 102 | 1 | return ucfirst(controller); |
| 103 | }; | |
| 104 | /** | |
| 105 | * 获取action | |
| 106 | * @param {[type]} action [description] | |
| 107 | * @return {[type]} [description] | |
| 108 | */ | |
| 109 | 1 | Dispatcher.getAction = function(action){ |
| 110 | 1 | 'use strict'; |
| 111 | 1 | action = action || C('default_action'); |
| 112 | 1 | return action; |
| 113 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 网络方法处理 | |
| 3 | * @type {Object} | |
| 4 | */ | |
| 5 | 1 | var querystring = require('querystring'); |
| 6 | 1 | var url = require('url'); |
| 7 | 1 | var cookie = thinkRequire('Cookie'); |
| 8 | 1 | var EventEmitter = require('events').EventEmitter; |
| 9 | 1 | var multiparty = require('multiparty'); |
| 10 | ||
| 11 | 1 | var localIp = '127.0.0.1'; |
| 12 | 1 | module.exports = Class(function(){ |
| 13 | 1 | 'use strict'; |
| 14 | 1 | var multiReg = /^multipart\/(form-data|related);\s*boundary=(?:"([^"]+)"|([^;]+))$/i; |
| 15 | 1 | return { |
| 16 | init: function(req, res){ | |
| 17 | 1 | this.req = req; |
| 18 | 1 | this.res = res; |
| 19 | 1 | this.http = new EventEmitter(); |
| 20 | //记录当前请求的开始时间 | |
| 21 | 1 | this.http.startTime = Date.now(); |
| 22 | }, | |
| 23 | run: function(callback){ | |
| 24 | 1 | this._request(); |
| 25 | 1 | this._response(); |
| 26 | //数组的indexOf要比字符串的indexOf略快 | |
| 27 | 1 | var methods = ['POST', 'PUT', 'PATCH']; |
| 28 | 1 | if (methods.indexOf(this.req.method) > -1) { |
| 29 | 0 | return this.getPostData(callback); |
| 30 | } | |
| 31 | 1 | return callback && callback(this.http); |
| 32 | }, | |
| 33 | /** | |
| 34 | * 检测含有post数据 | |
| 35 | * @return {Boolean} [description] | |
| 36 | */ | |
| 37 | hasPostData: function(){ | |
| 38 | 0 | if ('transfer-encoding' in this.req.headers) { |
| 39 | 0 | return true; |
| 40 | } | |
| 41 | 0 | var contentLength = this.req.headers['content-length'] | 0; |
| 42 | 0 | return contentLength > 0; |
| 43 | }, | |
| 44 | /** | |
| 45 | * 获取POST过来的数据,包含上传的文件 | |
| 46 | * 依赖multiparty库 | |
| 47 | * @param {Function} callback [description] | |
| 48 | * @return {[type]} [description] | |
| 49 | */ | |
| 50 | getPostData: function(callback){ | |
| 51 | //没有post数据,直接回调 | |
| 52 | 0 | if (!this.hasPostData()) { |
| 53 | 0 | return callback && callback(this.http); |
| 54 | } | |
| 55 | 0 | var self = this; |
| 56 | 0 | var contentType = (this.req.headers['content-type'] || '').split(';')[0].trim(); |
| 57 | 0 | var deferred = getDefer(); |
| 58 | //异步获取post数据 | |
| 59 | 0 | var postDataAsync = C('post_data_async'); |
| 60 | //表单数据提交 | |
| 61 | 0 | if (multiReg.test(contentType)) { |
| 62 | 0 | var form = new multiparty.Form({ |
| 63 | maxFieldsSize: C('post_max_fields_size'), | |
| 64 | maxFields: C('post_max_fields'), | |
| 65 | maxFilesSize: C('post_max_file_size') | |
| 66 | }); | |
| 67 | 0 | form.on('file', function(name, value){ |
| 68 | 0 | self.http.file[name] = value; |
| 69 | }); | |
| 70 | 0 | form.on('field', function(name, value){ |
| 71 | 0 | self.http.post[name] = value; |
| 72 | }); | |
| 73 | 0 | form.on('close', function(){ |
| 74 | 0 | if (postDataAsync) { |
| 75 | 0 | return deferred.resolve(self.http.post); |
| 76 | } | |
| 77 | 0 | return callback && callback(self.http); |
| 78 | }); | |
| 79 | //有错误后直接拒绝当前请求 | |
| 80 | 0 | form.on('error', function(){ |
| 81 | 0 | self.res.statusCode = 413; |
| 82 | 0 | self.res.end(); |
| 83 | }); | |
| 84 | 0 | form.parse(this.req); |
| 85 | }else{ | |
| 86 | 0 | var buffer = ''; |
| 87 | 0 | this.req.setEncoding(C('encoding')); |
| 88 | 0 | this.req.on('data', function(chunk){ |
| 89 | 0 | buffer += chunk; |
| 90 | }); | |
| 91 | 0 | this.req.on('end', function(){ |
| 92 | //json数据格式 | |
| 93 | 0 | var jsonConentType = C('post_json_content_type'); |
| 94 | 0 | if (!isArray(jsonConentType)) { |
| 95 | 0 | jsonConentType = [jsonConentType]; |
| 96 | } | |
| 97 | 0 | if (jsonConentType.indexOf(contentType) > -1) { |
| 98 | 0 | self.http.post = JSON.parse(buffer) || {}; |
| 99 | }else{ | |
| 100 | 0 | self.http.post = querystring.parse(buffer) || {}; |
| 101 | } | |
| 102 | //请求内容 | |
| 103 | 0 | self.http.payload = buffer; |
| 104 | 0 | if (postDataAsync) { |
| 105 | 0 | return deferred.resolve(self.http.post); |
| 106 | } | |
| 107 | 0 | return callback && callback(self.http); |
| 108 | }); | |
| 109 | } | |
| 110 | 0 | if (postDataAsync) { |
| 111 | 0 | self.http.postPromise = deferred.promise; |
| 112 | 0 | return callback && callback(self.http); |
| 113 | } | |
| 114 | }, | |
| 115 | _request: function(){ | |
| 116 | 1 | var req = { |
| 117 | version: this.req.httpVersion, | |
| 118 | method: this.req.method, | |
| 119 | headers: this.req.headers, | |
| 120 | getHeader: function(name){ | |
| 121 | 0 | return this.headers[name] || ''; |
| 122 | }, | |
| 123 | post: {}, | |
| 124 | file: {}, | |
| 125 | ip: function(){ | |
| 126 | 0 | var ip = this.req.connection.remoteAddress || this.req.socket.remoteAddress; |
| 127 | 0 | if (ip && ip !== localIp) { |
| 128 | 0 | return ip; |
| 129 | } | |
| 130 | 0 | return this.headers['x-forwarded-for'] || this.headers['x-real-ip'] || localIp; |
| 131 | }, | |
| 132 | cookie: cookie.parse(this.req.headers.cookie || '') | |
| 133 | }; | |
| 134 | 1 | extend(this.http, req); |
| 135 | ||
| 136 | //解析url中的参数 | |
| 137 | 1 | var urlInfo = url.parse('//' + req.headers.host + this.req.url, true, true); |
| 138 | 1 | this.http.pathname = urlInfo.pathname; |
| 139 | //query只记录?后面的参数 | |
| 140 | 1 | this.http.query = urlInfo.query; |
| 141 | //get包含路由解析追加的参数 | |
| 142 | 1 | this.http.get = extend({}, urlInfo.query); |
| 143 | //主机名,带端口 | |
| 144 | 1 | this.http.host = urlInfo.host; |
| 145 | //主机名,不带端口 | |
| 146 | 1 | this.http.hostname = urlInfo.hostname; |
| 147 | //将原生的request对象放在http上,方便后续在controller等地方使用 | |
| 148 | 1 | this.http.req = this.req; |
| 149 | }, | |
| 150 | _response: function(){ | |
| 151 | 1 | var res = { |
| 152 | /** | |
| 153 | * 一次请求下,可能会发送多个Cookie,所以这里不能立即发送 | |
| 154 | * 需要临时存起来,到输出内容前统一发送 | |
| 155 | * @type {Object} | |
| 156 | */ | |
| 157 | _cookie: {}, | |
| 158 | /** | |
| 159 | * 发送header | |
| 160 | * @param {[type]} name [description] | |
| 161 | * @param {[type]} value [description] | |
| 162 | */ | |
| 163 | setHeader: function(name, value){ | |
| 164 | 2 | if (this.res.headersSent) { |
| 165 | 0 | if (APP_DEBUG) { |
| 166 | 0 | console.log('headers has been sent.', name, value); |
| 167 | } | |
| 168 | 0 | return; |
| 169 | } | |
| 170 | 2 | this.res.setHeader(name, value); |
| 171 | }, | |
| 172 | /** | |
| 173 | * 设置cookie | |
| 174 | * @param {[type]} name [description] | |
| 175 | * @param {[type]} value [description] | |
| 176 | * @param {[type]} options [description] | |
| 177 | */ | |
| 178 | setCookie: function(name, value, options){ | |
| 179 | 0 | options = options || {}; |
| 180 | 0 | if (typeof options === 'number') { |
| 181 | 0 | options = {timeout: options}; |
| 182 | } | |
| 183 | 0 | var timeout = options.timeout; |
| 184 | 0 | if (timeout === undefined) { |
| 185 | 0 | timeout = C('cookie_timeout'); |
| 186 | } | |
| 187 | 0 | delete options.timeout; |
| 188 | //if value is null, remove cookie | |
| 189 | 0 | if (value === null) { |
| 190 | 0 | timeout = -1000; |
| 191 | } | |
| 192 | 0 | var defaultOptions = { |
| 193 | path: C('cookie_path'), | |
| 194 | domain: C('cookie_domain'), | |
| 195 | expires: new Date (Date.now() + timeout * 1000) | |
| 196 | }; | |
| 197 | 0 | if (timeout === 0) { |
| 198 | 0 | delete defaultOptions.expires; |
| 199 | } | |
| 200 | 0 | for(var key in options){ |
| 201 | 0 | defaultOptions[key.toLowerCase()] = options[key]; |
| 202 | } | |
| 203 | 0 | defaultOptions.name = name; |
| 204 | 0 | defaultOptions.value = encodeURIComponent(value + ''); |
| 205 | 0 | this._cookie[name] = defaultOptions; |
| 206 | }, | |
| 207 | /** | |
| 208 | * 将队列中的cookie发送出去 | |
| 209 | * @return {[type]} [description] | |
| 210 | */ | |
| 211 | sendCookie: function(){ | |
| 212 | 2 | var cookies = Object.values(this._cookie).map(function(item){ |
| 213 | 0 | return cookie.stringify(item.name, item.value, item); |
| 214 | }); | |
| 215 | 2 | if (cookies.length) { |
| 216 | 0 | this.setHeader('Set-Cookie', cookies); |
| 217 | //发送Cookie后不清除_cookie内容,websocket里需要读取 | |
| 218 | //this._cookie = {}; | |
| 219 | } | |
| 220 | }, | |
| 221 | /** | |
| 222 | * url跳转 | |
| 223 | * @param {[type]} url [description] | |
| 224 | * @param {[type]} code [description] | |
| 225 | * @return {[type]} [description] | |
| 226 | */ | |
| 227 | redirect: function(url, code){ | |
| 228 | 0 | this.res.statusCode = code || 302; |
| 229 | 0 | this.setHeader('Location', url || '/'); |
| 230 | 0 | this.end(); |
| 231 | }, | |
| 232 | /** | |
| 233 | * 发送执行时间 | |
| 234 | * @param {[type]} name [description] | |
| 235 | * @return {[type]} [description] | |
| 236 | */ | |
| 237 | sendTime: function(name){ | |
| 238 | 0 | var time = Date.now() - this.startTime; |
| 239 | 0 | this.setHeader('X-' + name, time + 'ms'); |
| 240 | }, | |
| 241 | /** | |
| 242 | * 输出内容 | |
| 243 | * @param {[type]} obj [description] | |
| 244 | * @param {[type]} encoding [description] | |
| 245 | * @return {[type]} [description] | |
| 246 | */ | |
| 247 | echo: function(obj, encoding){ | |
| 248 | 1 | this.sendCookie(); |
| 249 | 1 | if (isArray(obj) || isObject(obj)) { |
| 250 | 0 | obj = JSON.stringify(obj); |
| 251 | } | |
| 252 | 1 | if (!isString(obj) && !(obj instanceof Buffer)) { |
| 253 | 0 | obj += ''; |
| 254 | } | |
| 255 | 1 | this.res.write(obj, encoding || C('encoding')); |
| 256 | }, | |
| 257 | /** | |
| 258 | * 结束URL | |
| 259 | * @return {[type]} [description] | |
| 260 | */ | |
| 261 | end: function(){ | |
| 262 | 1 | this.emit('beforeEnd', this); |
| 263 | 1 | this.sendCookie(); |
| 264 | 1 | this.res.end(); |
| 265 | 1 | this.emit('afterEnd', this); |
| 266 | } | |
| 267 | }; | |
| 268 | 1 | extend(this.http, res); |
| 269 | //将原生的response对象放在http上,方便后续controller等地方使用 | |
| 270 | 1 | this.http.res = this.res; |
| 271 | } | |
| 272 | }; | |
| 273 | }); | |
| 274 | /** | |
| 275 | * 获取默认的http信息 | |
| 276 | * @param {[type]} data [description] | |
| 277 | * @return {[type]} [description] | |
| 278 | */ | |
| 279 | 1 | module.exports.getDefaultHttp = function(data){ |
| 280 | 2 | 'use strict'; |
| 281 | 2 | data = data || {}; |
| 282 | 2 | if (isString(data)) { |
| 283 | 2 | if (data[0] === '{') { |
| 284 | 0 | data = JSON.parse(data); |
| 285 | 2 | }else if (/^[\w]+\=/.test(data)) { |
| 286 | 0 | data = querystring.parse(data); |
| 287 | }else{ | |
| 288 | 2 | data = {url: data}; |
| 289 | } | |
| 290 | } | |
| 291 | 2 | var fn = function(){ |
| 292 | 4 | return ''; |
| 293 | }; | |
| 294 | 2 | var url = data.url || ''; |
| 295 | 2 | if (url.indexOf('/') !== 0) { |
| 296 | 0 | url = '/' + url; |
| 297 | } | |
| 298 | 2 | return { |
| 299 | req: { | |
| 300 | httpVersion: '1.1', | |
| 301 | method: data.method || 'GET', | |
| 302 | url: url, | |
| 303 | headers: extend({ | |
| 304 | host: data.host || localIp | |
| 305 | }, data.headers || {}), | |
| 306 | connection: { | |
| 307 | remoteAddress: data.ip || localIp | |
| 308 | } | |
| 309 | }, | |
| 310 | res: { | |
| 311 | end: data.end || data.close || fn, | |
| 312 | write: data.write || data.send || fn, | |
| 313 | setHeader: fn | |
| 314 | } | |
| 315 | }; | |
| 316 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var util = require('util'); |
| 2 | 1 | var querystring = require('querystring'); |
| 3 | ||
| 4 | //数据库实例化对象 | |
| 5 | 1 | var dbInstances = {}; |
| 6 | //数据表的字段信息 | |
| 7 | 1 | var tableFields = {}; |
| 8 | //db缓存数据 | |
| 9 | 1 | var dbCacheData = {}; |
| 10 | ||
| 11 | /** | |
| 12 | * Model类 | |
| 13 | * @type {[type]} | |
| 14 | */ | |
| 15 | 1 | var Model = module.exports = Class(function(){ |
| 16 | 1 | 'use strict'; |
| 17 | //解析page参数 | |
| 18 | 1 | var parsePage = function(options){ |
| 19 | 0 | if ('page' in options) { |
| 20 | 0 | var page = options.page + ''; |
| 21 | 0 | var num = 0; |
| 22 | 0 | if (page.indexOf(',') > -1) { |
| 23 | 0 | page = page.split(','); |
| 24 | 0 | num = parseInt(page[1], 10); |
| 25 | 0 | page = page[0]; |
| 26 | } | |
| 27 | 0 | num = num || C('db_nums_per_page'); |
| 28 | 0 | page = parseInt(page, 10) || 1; |
| 29 | 0 | return { |
| 30 | page: page, | |
| 31 | num: num | |
| 32 | }; | |
| 33 | } | |
| 34 | 0 | return { |
| 35 | page: 1, | |
| 36 | num: C('db_nums_per_page') | |
| 37 | }; | |
| 38 | }; | |
| 39 | /** | |
| 40 | * 字符串命名风格转换 | |
| 41 | * @param {[type]} name [description] | |
| 42 | * @param {[type]} type [description] | |
| 43 | * @return {[type]} [description] | |
| 44 | */ | |
| 45 | 1 | var parseName = function(name, type){ |
| 46 | 0 | name = (name + '').trim(); |
| 47 | 0 | if (type) { |
| 48 | 0 | name = name.replace(/_([a-zA-Z])/g, function(a, b){ |
| 49 | 0 | return b.toUpperCase(); |
| 50 | }); | |
| 51 | 0 | return name.substr(0, 1).toUpperCase() + name.substr(1); |
| 52 | } else { | |
| 53 | //首字母如果是大写,不转义为_x | |
| 54 | 0 | if (name.length >= 1) { |
| 55 | 0 | name = name.substr(0, 1).toLowerCase() + name.substr(1); |
| 56 | } | |
| 57 | 0 | return name.replace(/[A-Z]/g, function(a){ |
| 58 | 0 | return '_' + a; |
| 59 | }).toLowerCase(); | |
| 60 | } | |
| 61 | }; | |
| 62 | ||
| 63 | 1 | return { |
| 64 | // 当前数据库操作对象 | |
| 65 | db: null, | |
| 66 | // 主键名称 | |
| 67 | pk: 'id', | |
| 68 | // 数据表前缀 | |
| 69 | tablePrefix: '', | |
| 70 | // 数据库配置信息 | |
| 71 | config: null, | |
| 72 | // 配置信息key | |
| 73 | configKey: '', | |
| 74 | // 模型名称 | |
| 75 | name: '', | |
| 76 | // 数据库名称 | |
| 77 | dbName: '', | |
| 78 | // 数据表名(不包含表前缀) | |
| 79 | tableName: '', | |
| 80 | // 实际数据表名(包含表前缀) | |
| 81 | trueTableName: '', | |
| 82 | // 数据表子度信息 | |
| 83 | fields: {}, | |
| 84 | // 数据信息 | |
| 85 | _data: {}, | |
| 86 | // 参数 | |
| 87 | _options: {}, | |
| 88 | /** | |
| 89 | * 取得DB类的实例对象 字段检查 | |
| 90 | * @access public | |
| 91 | * @param string $name 模型名称 | |
| 92 | * @param string $tablePrefix 表前缀 | |
| 93 | * @param mixed config 数据库连接信息 | |
| 94 | */ | |
| 95 | init: function(name, tablePrefix, config){ | |
| 96 | // 获取模型名称 | |
| 97 | 0 | if (name) { |
| 98 | 0 | if (name.indexOf('.') > -1) { |
| 99 | 0 | name = name.split('.'); |
| 100 | 0 | this.dbName = name[0]; |
| 101 | 0 | this.name = name[1]; |
| 102 | }else{ | |
| 103 | 0 | this.name = name; |
| 104 | } | |
| 105 | 0 | }else if(!this.name){ |
| 106 | 0 | this.getModelName(); |
| 107 | } | |
| 108 | 0 | if (!isString(tablePrefix)) { |
| 109 | 0 | config = tablePrefix; |
| 110 | 0 | tablePrefix = undefined; |
| 111 | } | |
| 112 | 0 | this.config = config || ''; |
| 113 | //设置数据表前缀 | |
| 114 | 0 | if (tablePrefix || this.config.db_prefix) { |
| 115 | 0 | this.tablePrefix = tablePrefix || this.config.db_prefix; |
| 116 | }else{ | |
| 117 | 0 | if (!this.tablePrefix) { |
| 118 | 0 | this.tablePrefix = C('db_prefix'); |
| 119 | } | |
| 120 | } | |
| 121 | //子类的init方法 | |
| 122 | 0 | if (this._init) { |
| 123 | 0 | this._init(); |
| 124 | } | |
| 125 | }, | |
| 126 | /** | |
| 127 | * 初始化数据库连接 | |
| 128 | * @access public | |
| 129 | * @param integer $linkNum 连接序号 | |
| 130 | * @param mixed $config 数据库连接信息 | |
| 131 | * @param array $params 模型参数 | |
| 132 | * @return Model | |
| 133 | */ | |
| 134 | initDb: function(){ | |
| 135 | 0 | if (this.db) { |
| 136 | 0 | return this.db; |
| 137 | } | |
| 138 | 0 | var config = this.config; |
| 139 | 0 | var configKey = md5(JSON.stringify(config)); |
| 140 | 0 | if (!dbInstances[configKey]) { |
| 141 | 0 | if (config && isString(config) && config.indexOf('/') === -1) { |
| 142 | 0 | config = C(config); |
| 143 | } | |
| 144 | 0 | dbInstances[configKey] = thinkRequire('Db').getInstance(config); |
| 145 | } | |
| 146 | 0 | this.db = dbInstances[configKey]; |
| 147 | 0 | this.configKey = configKey; |
| 148 | 0 | return this.db; |
| 149 | }, | |
| 150 | /** | |
| 151 | * 获取模型名 | |
| 152 | * @access public | |
| 153 | * @return string | |
| 154 | */ | |
| 155 | getModelName: function(){ | |
| 156 | 0 | if (this.name) { |
| 157 | 0 | return this.name; |
| 158 | } | |
| 159 | 0 | var filename = this.__filename || __filename; |
| 160 | 0 | var name = filename.split('/').pop(); |
| 161 | 0 | this.name = name.substr(0, name.length - 8); |
| 162 | 0 | return this.name; |
| 163 | }, | |
| 164 | /** | |
| 165 | * 获取表名 | |
| 166 | * @return {[type]} [description] | |
| 167 | */ | |
| 168 | getTableName: function(){ | |
| 169 | 0 | var tableName; |
| 170 | 0 | if (!this.trueTableName) { |
| 171 | 0 | tableName = this.tablePrefix || ''; |
| 172 | 0 | tableName += this.tableName || parseName(this.getModelName()); |
| 173 | 0 | this.trueTableName = tableName.toLowerCase(); |
| 174 | } | |
| 175 | 0 | tableName = (this.dbName ? this.dbName + '.' : '') + this.trueTableName; |
| 176 | 0 | return tableName; |
| 177 | }, | |
| 178 | /** | |
| 179 | * 获取数据表信息 | |
| 180 | * @access protected | |
| 181 | * @return Promise | |
| 182 | */ | |
| 183 | getTableFields: function(table, all){ | |
| 184 | 0 | this.initDb(); |
| 185 | 0 | if (table === true) { |
| 186 | 0 | table = undefined; |
| 187 | 0 | all = true; |
| 188 | } | |
| 189 | 0 | if (!isEmpty(this.fields)) { |
| 190 | 0 | return getPromise(all ? this.fields : this.fields._field); |
| 191 | } | |
| 192 | 0 | var tableName = table || this.getTableName(); |
| 193 | 0 | var fields = tableFields[tableName]; |
| 194 | 0 | if (!isEmpty(fields)) { |
| 195 | 0 | this.fields = fields; |
| 196 | 0 | return getPromise(all ? fields : fields._field); |
| 197 | } | |
| 198 | 0 | var self = this; |
| 199 | //从数据表里查询字段信息 | |
| 200 | 0 | return this.flushFields().then(function(fields){ |
| 201 | 0 | self.fields = fields; |
| 202 | 0 | if (C('db_fields_cache')) { |
| 203 | 0 | tableFields[tableName] = fields; |
| 204 | } | |
| 205 | 0 | return getPromise(all ? fields : fields._field); |
| 206 | }); | |
| 207 | }, | |
| 208 | /** | |
| 209 | * 获取数据表信息 | |
| 210 | * @param {[type]} table [description] | |
| 211 | * @return Promise [description] | |
| 212 | */ | |
| 213 | flushFields: function(table){ | |
| 214 | 0 | table = table || this.getTableName(); |
| 215 | 0 | return this.initDb().getFields(table).then(function(data){ |
| 216 | 0 | var fields = { |
| 217 | '_field': Object.keys(data || {}), | |
| 218 | '_autoinc': false, | |
| 219 | '_unique': [] | |
| 220 | }; | |
| 221 | 0 | var types = {}; |
| 222 | 0 | for(var key in data){ |
| 223 | 0 | var val = data[key]; |
| 224 | 0 | types[key] = val.type; |
| 225 | 0 | if (val.primary) { |
| 226 | 0 | fields._pk = key; |
| 227 | 0 | if (val.autoinc) { |
| 228 | 0 | fields._autoinc = true; |
| 229 | } | |
| 230 | 0 | }else if (val.unique) { |
| 231 | 0 | fields._unique.push(key); |
| 232 | } | |
| 233 | } | |
| 234 | 0 | fields._type = types; |
| 235 | 0 | return fields; |
| 236 | }) | |
| 237 | }, | |
| 238 | /** | |
| 239 | * 获取类型为唯一的字段 | |
| 240 | * @return {[type]} [description] | |
| 241 | */ | |
| 242 | getUniqueField: function(data){ | |
| 243 | 0 | var unqiueFileds = this.fields._unique; |
| 244 | 0 | var unqiue = ''; |
| 245 | 0 | unqiueFileds.some(function(item){ |
| 246 | 0 | if (!data || data[item]) { |
| 247 | 0 | unqiue = item; |
| 248 | 0 | return unqiue; |
| 249 | } | |
| 250 | }); | |
| 251 | 0 | return unqiue; |
| 252 | }, | |
| 253 | /** | |
| 254 | * 获取上一次操作的sql | |
| 255 | * @return {[type]} [description] | |
| 256 | */ | |
| 257 | getLastSql: function(){ | |
| 258 | 0 | return this.db.getLastSql(); |
| 259 | }, | |
| 260 | /** | |
| 261 | * 获取主键名称 | |
| 262 | * @access public | |
| 263 | * @return string | |
| 264 | */ | |
| 265 | getPk: function(){ | |
| 266 | 0 | return this.fields._pk || this.pk; |
| 267 | }, | |
| 268 | /** | |
| 269 | * 缓存 | |
| 270 | * @param {[type]} key [description] | |
| 271 | * @param {[type]} expire [description] | |
| 272 | * @param {[type]} type [description] | |
| 273 | * @return {[type]} [description] | |
| 274 | */ | |
| 275 | cache: function(key, timeout){ | |
| 276 | 0 | if (key === undefined) { |
| 277 | 0 | return this; |
| 278 | } | |
| 279 | 0 | var options = this.getCacheOptions(key, timeout); |
| 280 | 0 | this._options.cache = options; |
| 281 | 0 | return this; |
| 282 | }, | |
| 283 | /** | |
| 284 | * 获取缓存的选项 | |
| 285 | * @param {[type]} key [description] | |
| 286 | * @param {[type]} timeout [description] | |
| 287 | * @return {[type]} [description] | |
| 288 | */ | |
| 289 | getCacheOptions: function(key, timeout, type){ | |
| 290 | 0 | if (isObject(key)) { |
| 291 | 0 | return key; |
| 292 | } | |
| 293 | 0 | if (isNumber(key)) { |
| 294 | 0 | timeout = key; |
| 295 | 0 | key = ''; |
| 296 | } | |
| 297 | 0 | var cacheType = type === undefined ? C('db_cache_type') : type; |
| 298 | 0 | var options = { |
| 299 | key: key, | |
| 300 | timeout: timeout || C('db_cache_timeout'), | |
| 301 | type: cacheType, | |
| 302 | gcType: 'dbCache' | |
| 303 | } | |
| 304 | 0 | if (cacheType === 'File') { |
| 305 | 0 | options.cache_path = C('db_cache_path'); |
| 306 | }else{ | |
| 307 | 0 | options.cacheData = dbCacheData; |
| 308 | } | |
| 309 | 0 | return options; |
| 310 | }, | |
| 311 | /** | |
| 312 | * 指定查询数量 | |
| 313 | * @param {[type]} offset [description] | |
| 314 | * @param {[type]} length [description] | |
| 315 | * @return {[type]} [description] | |
| 316 | */ | |
| 317 | limit: function(offset, length){ | |
| 318 | 0 | this._options.limit = length === undefined ? offset : offset + ',' + length; |
| 319 | 0 | return this; |
| 320 | }, | |
| 321 | /** | |
| 322 | * 指定分页 | |
| 323 | * @return {[type]} [description] | |
| 324 | */ | |
| 325 | page: function(page, listRows){ | |
| 326 | 0 | this._options.page = listRows === undefined ? page : page + ',' + listRows; |
| 327 | 0 | return this; |
| 328 | }, | |
| 329 | /** | |
| 330 | * where条件 | |
| 331 | * @return {[type]} [description] | |
| 332 | */ | |
| 333 | where: function(where){ | |
| 334 | 0 | if (isString(where)) { |
| 335 | 0 | where = {_string: where}; |
| 336 | } | |
| 337 | 0 | this._options.where = extend(this._options.where || {}, where); |
| 338 | 0 | return this; |
| 339 | }, | |
| 340 | /** | |
| 341 | * 要查询的字段 | |
| 342 | * @param {[type]} field [description] | |
| 343 | * @param {[type]} reverse [description] | |
| 344 | * @return {[type]} [description] | |
| 345 | */ | |
| 346 | field: function(field, reverse){ | |
| 347 | 0 | if (isArray(field)) { |
| 348 | 0 | field = field.join(','); |
| 349 | 0 | }else if (!field) { |
| 350 | 0 | field = '*'; |
| 351 | } | |
| 352 | 0 | this._options.field = field; |
| 353 | 0 | this._options.fieldReverse = reverse; |
| 354 | 0 | return this; |
| 355 | }, | |
| 356 | /** | |
| 357 | * 联合查询 | |
| 358 | * @return {[type]} [description] | |
| 359 | */ | |
| 360 | union: function(union){ | |
| 361 | 0 | if (!this._options.union) { |
| 362 | 0 | this._options.union = []; |
| 363 | } | |
| 364 | 0 | this._options.union.push(union); |
| 365 | 0 | return this; |
| 366 | }, | |
| 367 | /** | |
| 368 | * 联合查询 | |
| 369 | * @param {[type]} join [description] | |
| 370 | * @return {[type]} [description] | |
| 371 | */ | |
| 372 | join: function(join){ | |
| 373 | 0 | if (isArray(join)) { |
| 374 | 0 | this._options.join = join; |
| 375 | }else{ | |
| 376 | 0 | if (!this._options.join) { |
| 377 | 0 | this._options.join = []; |
| 378 | } | |
| 379 | 0 | this._options.join.push(join); |
| 380 | } | |
| 381 | 0 | return this; |
| 382 | }, | |
| 383 | /** | |
| 384 | * 生成查询SQL 可用于子查询 | |
| 385 | * @param {[type]} options [description] | |
| 386 | * @return {[type]} [description] | |
| 387 | */ | |
| 388 | buildSql: function(options){ | |
| 389 | 0 | var self = this; |
| 390 | 0 | return this.parseOptions(options).then(function(options){ |
| 391 | 0 | return '( ' + self.db.buildSelectSql(options) + ' )'; |
| 392 | }); | |
| 393 | }, | |
| 394 | /** | |
| 395 | * 解析参数 | |
| 396 | * @param {[type]} options [description] | |
| 397 | * @return promise [description] | |
| 398 | */ | |
| 399 | parseOptions: function(options, extraOptions){ | |
| 400 | 0 | options = extend({}, this._options, this.parseWhereOptions(options), extraOptions); |
| 401 | // 查询过后清空sql表达式组装 避免影响下次查询 | |
| 402 | 0 | this._options = {}; |
| 403 | 0 | options.table = options.table || this.getTableName(); |
| 404 | 0 | options.model = this.name; |
| 405 | 0 | var promise = this.getTableFields(options.table); |
| 406 | //数据表别名 | |
| 407 | 0 | if (options.alias) { |
| 408 | 0 | options.table += ' ' + options.alias; |
| 409 | } | |
| 410 | 0 | var self = this; |
| 411 | 0 | var keyReg = /[\.\|\&]/; |
| 412 | 0 | return promise.then(function(fields){ |
| 413 | // 字段类型验证 | |
| 414 | 0 | if (isObject(options.where) && !isEmpty(fields)) { |
| 415 | // 对数组查询条件进行字段类型检查 | |
| 416 | 0 | for(var key in options.where){ |
| 417 | 0 | var val = options.where[key]; |
| 418 | 0 | key = key.trim(); |
| 419 | 0 | if (fields.indexOf(key) > -1) { |
| 420 | 0 | if (isScalar(val)) { |
| 421 | 0 | options.where = self.parseType(options.where, key); |
| 422 | } | |
| 423 | 0 | }else if(key.substr(0, 1) !== '_' && !keyReg.test(key)){ |
| 424 | 0 | delete options.where[key]; |
| 425 | } | |
| 426 | } | |
| 427 | } | |
| 428 | //field反选 | |
| 429 | 0 | if (options.field && options.fieldReverse) { |
| 430 | 0 | var optionsField = options.field.split(','); |
| 431 | 0 | options.field = fields.filter(function(item){ |
| 432 | 0 | if (optionsField.indexOf(item) > -1) { |
| 433 | 0 | return; |
| 434 | } | |
| 435 | 0 | return item; |
| 436 | }).join(','); | |
| 437 | } | |
| 438 | 0 | return self._optionsFilter(options, fields); |
| 439 | }); | |
| 440 | }, | |
| 441 | /** | |
| 442 | * 选项过滤器 | |
| 443 | * 具体的Model类里进行实现 | |
| 444 | * @param {[type]} options [description] | |
| 445 | * @return {[type]} [description] | |
| 446 | */ | |
| 447 | _optionsFilter: function(options){ | |
| 448 | 0 | return options; |
| 449 | }, | |
| 450 | /** | |
| 451 | * 数据类型检测 | |
| 452 | * @param {[type]} data [description] | |
| 453 | * @param {[type]} key [description] | |
| 454 | * @return {[type]} [description] | |
| 455 | */ | |
| 456 | parseType: function(data, key){ | |
| 457 | 0 | var fieldType = this.fields._type[key] || ''; |
| 458 | 0 | if (fieldType.indexOf('bigint') === -1 && fieldType.indexOf('int') > -1) { |
| 459 | 0 | data[key] = parseInt(data[key], 10) || 0; |
| 460 | 0 | }else if(fieldType.indexOf('double') > -1 || fieldType.indexOf('float') > -1){ |
| 461 | 0 | data[key] = parseFloat(data[key]) || 0.0; |
| 462 | 0 | }else if(fieldType.indexOf('bool') > -1){ |
| 463 | 0 | data[key] = !! data[key]; |
| 464 | } | |
| 465 | 0 | return data; |
| 466 | }, | |
| 467 | /** | |
| 468 | * 对插入到数据库中的数据进行处理,要在parseOptions后执行 | |
| 469 | * @param {[type]} data [description] | |
| 470 | * @return {[type]} [description] | |
| 471 | */ | |
| 472 | parseData: function(data){ | |
| 473 | 0 | data = extend({}, data); |
| 474 | 0 | var key; |
| 475 | 0 | if (!isEmpty(this.fields)) { |
| 476 | 0 | for(key in data){ |
| 477 | 0 | var val = data[key]; |
| 478 | 0 | if (this.fields._field.indexOf(key) === -1) { |
| 479 | 0 | delete data[key]; |
| 480 | 0 | }else if(isScalar(val)){ |
| 481 | 0 | data = this.parseType(data, key); |
| 482 | } | |
| 483 | } | |
| 484 | } | |
| 485 | //安全过滤 | |
| 486 | 0 | if (typeof this._options.filter === 'function') { |
| 487 | 0 | for(key in data){ |
| 488 | 0 | data[key] = this._options.filter.call(this, key, data[key]); |
| 489 | } | |
| 490 | 0 | delete this._options.filter; |
| 491 | } | |
| 492 | 0 | data = this._dataFilter(data); |
| 493 | 0 | return data; |
| 494 | }, | |
| 495 | /** | |
| 496 | * 数据过滤器 | |
| 497 | * 具体的Model类里进行实现 | |
| 498 | * @param {[type]} data [description] | |
| 499 | * @return {[type]} [description] | |
| 500 | */ | |
| 501 | _dataFilter: function(data){ | |
| 502 | 0 | return data; |
| 503 | }, | |
| 504 | /** | |
| 505 | * 数据插入之前操作,可以返回一个promise | |
| 506 | * @param {[type]} data [description] | |
| 507 | * @param {[type]} options [description] | |
| 508 | * @return {[type]} [description] | |
| 509 | */ | |
| 510 | _beforeAdd: function(data){ | |
| 511 | 0 | return data; |
| 512 | }, | |
| 513 | /** | |
| 514 | * 数据插入之后操作,可以返回一个promise | |
| 515 | * @param {[type]} data [description] | |
| 516 | * @param {[type]} options [description] | |
| 517 | * @return {[type]} [description] | |
| 518 | */ | |
| 519 | _afterAdd: function(data){ | |
| 520 | 0 | return data; |
| 521 | }, | |
| 522 | /** | |
| 523 | * 添加一条数据 | |
| 524 | * @param {[type]} data [description] | |
| 525 | * @param {[type]} options [description] | |
| 526 | * @param int 返回插入的id | |
| 527 | */ | |
| 528 | add: function(data, options, replace){ | |
| 529 | //copy data | |
| 530 | 0 | data = extend({}, data); |
| 531 | 0 | if (isEmpty(data)) { |
| 532 | 0 | if (this._data) { |
| 533 | 0 | data = this._data; |
| 534 | 0 | this._data = {}; |
| 535 | }else{ | |
| 536 | 0 | return getPromise(L('_DATA_TYPE_INVALID_'), true); |
| 537 | } | |
| 538 | } | |
| 539 | 0 | var self = this; |
| 540 | //解析后的选项 | |
| 541 | 0 | var parsedOptions = {}; |
| 542 | //解析后的数据 | |
| 543 | 0 | var parsedData = {}; |
| 544 | 0 | return this.parseOptions(options).then(function(options){ |
| 545 | 0 | parsedOptions = options; |
| 546 | 0 | return self._beforeAdd(data, parsedOptions); |
| 547 | }).then(function(data){ | |
| 548 | 0 | parsedData = data; |
| 549 | 0 | data = self.parseData(data); |
| 550 | 0 | return self.db.insert(data, parsedOptions, replace); |
| 551 | }).then(function(){ | |
| 552 | 0 | parsedData[self.getPk()] = self.db.getLastInsertId(); |
| 553 | 0 | return self._afterAdd(parsedData, parsedOptions); |
| 554 | }).then(function(){ | |
| 555 | 0 | return parsedData[self.getPk()]; |
| 556 | }); | |
| 557 | }, | |
| 558 | /** | |
| 559 | * 如果当前条件的数据不存在,才添加 | |
| 560 | * @param {[type]} data 要插入的数据 | |
| 561 | * @param {[type]} where where条件 | |
| 562 | * @param boolean returnType 返回值是否包含type | |
| 563 | * @return {[type]} promise | |
| 564 | */ | |
| 565 | thenAdd: function(data, where, returnType){ | |
| 566 | 0 | this.where(where); |
| 567 | 0 | var self = this; |
| 568 | 0 | return this.find().then(function(findData){ |
| 569 | 0 | if (!isEmpty(findData)) { |
| 570 | 0 | var idValue = findData[self.getPk()]; |
| 571 | 0 | return returnType ? getObject([self.getPk(), 'type'], [idValue, 'exist']) : idValue; |
| 572 | } | |
| 573 | 0 | return self.add(data).then(function(insertId){ |
| 574 | 0 | return returnType ? getObject([self.getPk(), 'type'], [insertId, 'add']) : insertId; |
| 575 | }); | |
| 576 | }); | |
| 577 | }, | |
| 578 | /** | |
| 579 | * 插入多条数据 | |
| 580 | * @param {[type]} data [description] | |
| 581 | * @param {[type]} options [description] | |
| 582 | * @param {[type]} replace [description] | |
| 583 | */ | |
| 584 | addAll: function(data, options, replace){ | |
| 585 | 0 | if (!isArray(data) || !isObject(data[0])) { |
| 586 | 0 | return getPromise(L('_DATA_TYPE_INVALID_'), true); |
| 587 | } | |
| 588 | 0 | var self = this; |
| 589 | 0 | return this.parseOptions(options).then(function(options){ |
| 590 | 0 | return self.db.insertAll(data, options, replace); |
| 591 | }).then(function(){ | |
| 592 | 0 | return self.db.getLastInsertId(); |
| 593 | }); | |
| 594 | }, | |
| 595 | /** | |
| 596 | * 删除后续操作 | |
| 597 | * @return {[type]} [description] | |
| 598 | */ | |
| 599 | _afterDelete: function(data){ | |
| 600 | 0 | return data; |
| 601 | }, | |
| 602 | /** | |
| 603 | * 删除数据 | |
| 604 | * @return {[type]} [description] | |
| 605 | */ | |
| 606 | delete: function(options){ | |
| 607 | 0 | var self = this; |
| 608 | 0 | var parsedOptions = {}; |
| 609 | 0 | var affectedRows = 0; |
| 610 | 0 | return this.parseOptions(options).then(function(options){ |
| 611 | 0 | parsedOptions = options; |
| 612 | 0 | return self.db.delete(options); |
| 613 | }).then(function(rows){ | |
| 614 | 0 | affectedRows = rows; |
| 615 | 0 | return self._afterDelete(parsedOptions.where || {}, parsedOptions); |
| 616 | }).then(function(){ | |
| 617 | 0 | return affectedRows; |
| 618 | }) | |
| 619 | }, | |
| 620 | /** | |
| 621 | * 更新前置操作 | |
| 622 | * @param {[type]} data [description] | |
| 623 | * @param {[type]} options [description] | |
| 624 | * @return {[type]} [description] | |
| 625 | */ | |
| 626 | _beforeUpdate: function(data){ | |
| 627 | 0 | return data; |
| 628 | }, | |
| 629 | /** | |
| 630 | * 更新后置操作 | |
| 631 | * @param {[type]} data [description] | |
| 632 | * @param {[type]} options [description] | |
| 633 | * @return {[type]} [description] | |
| 634 | */ | |
| 635 | _afterUpdate: function(data){ | |
| 636 | 0 | return data; |
| 637 | }, | |
| 638 | /** | |
| 639 | * 更新数据 | |
| 640 | * @return {[type]} [description] | |
| 641 | */ | |
| 642 | update: function(data, options){ | |
| 643 | 0 | data = extend({}, data); |
| 644 | 0 | if (isEmpty(data)) { |
| 645 | 0 | if (this._data) { |
| 646 | 0 | data = this._data; |
| 647 | 0 | this._data = {}; |
| 648 | }else{ | |
| 649 | 0 | return getPromise(L('_DATA_TYPE_INVALID_'), true); |
| 650 | } | |
| 651 | } | |
| 652 | 0 | var self = this; |
| 653 | 0 | var pk = self.getPk(); |
| 654 | 0 | var parsedOptions = {}; |
| 655 | 0 | var parsedData = {}; |
| 656 | 0 | var affectedRows = 0; |
| 657 | 0 | return this.parseOptions(options).then(function(options){ |
| 658 | 0 | parsedOptions = options; |
| 659 | 0 | return self._beforeUpdate(data, options); |
| 660 | }).then(function(data){ | |
| 661 | 0 | parsedData = data; |
| 662 | 0 | data = self.parseData(data); |
| 663 | 0 | if (isEmpty(parsedOptions.where)) { |
| 664 | // 如果存在主键数据 则自动作为更新条件 | |
| 665 | 0 | if (!isEmpty(data[pk])) { |
| 666 | 0 | parsedOptions.where = getObject(pk, data[pk]); |
| 667 | 0 | delete data[pk]; |
| 668 | }else{ | |
| 669 | 0 | return getPromise(L('_OPERATION_WRONG_'), true); |
| 670 | } | |
| 671 | }else{ | |
| 672 | 0 | parsedData[pk] = parsedOptions.where[pk]; |
| 673 | } | |
| 674 | 0 | return self.db.update(data, parsedOptions); |
| 675 | }).then(function(rows){ | |
| 676 | 0 | affectedRows = rows; |
| 677 | 0 | return self._afterUpdate(parsedData, parsedOptions); |
| 678 | }).then(function(){ | |
| 679 | 0 | return affectedRows; |
| 680 | }); | |
| 681 | }, | |
| 682 | /** | |
| 683 | * 更新多个数据,自动用主键作为查询条件 | |
| 684 | * @param {[type]} dataList [description] | |
| 685 | * @return {[type]} [description] | |
| 686 | */ | |
| 687 | updateAll: function(dataList){ | |
| 688 | 0 | if (!isArray(dataList) || !isObject(dataList[0])) { |
| 689 | 0 | return getPromise(L('_DATA_TYPE_INVALID_'), true); |
| 690 | } | |
| 691 | 0 | var self = this; |
| 692 | 0 | var promises = dataList.map(function(data){ |
| 693 | 0 | return self.update(data); |
| 694 | }); | |
| 695 | 0 | return Promise.all(promises); |
| 696 | }, | |
| 697 | /** | |
| 698 | * 更新某个字段的值 | |
| 699 | * @param {[type]} field [description] | |
| 700 | * @param {[type]} value [description] | |
| 701 | * @return {[type]} [description] | |
| 702 | */ | |
| 703 | updateField: function(field, value){ | |
| 704 | 0 | var data = {}; |
| 705 | 0 | if (isObject(field)) { |
| 706 | 0 | data = field; |
| 707 | }else{ | |
| 708 | 0 | data[field] = value; |
| 709 | } | |
| 710 | 0 | return this.update(data); |
| 711 | }, | |
| 712 | /** | |
| 713 | * 字段值增长 | |
| 714 | * @return {[type]} [description] | |
| 715 | */ | |
| 716 | updateInc: function(field, step){ | |
| 717 | 0 | step = parseInt(step, 10) || 1; |
| 718 | 0 | return this.updateField(field, ['exp', field + '+' + step]); |
| 719 | }, | |
| 720 | /** | |
| 721 | * 字段值减少 | |
| 722 | * @return {[type]} [description] | |
| 723 | */ | |
| 724 | updateDec: function(field, step){ | |
| 725 | 0 | step = parseInt(step, 10) || 1; |
| 726 | 0 | return this.updateField(field, ['exp', field + '-' + step]); |
| 727 | }, | |
| 728 | /** | |
| 729 | * 解析options中简洁的where条件 | |
| 730 | * @return {[type]} [description] | |
| 731 | */ | |
| 732 | parseWhereOptions: function(options){ | |
| 733 | 0 | if (isNumber(options) || isString(options)) { |
| 734 | 0 | var pk = this.getPk(); |
| 735 | 0 | options += ''; |
| 736 | 0 | var where = {}; |
| 737 | 0 | if (options.indexOf(',') > -1) { |
| 738 | 0 | where[pk] = ['IN', options]; |
| 739 | }else{ | |
| 740 | 0 | where[pk] = options; |
| 741 | } | |
| 742 | 0 | options = { |
| 743 | where: where | |
| 744 | }; | |
| 745 | } | |
| 746 | 0 | return options || {}; |
| 747 | }, | |
| 748 | /** | |
| 749 | * find查询后置操作 | |
| 750 | * @return {[type]} [description] | |
| 751 | */ | |
| 752 | _afterFind: function(result){ | |
| 753 | 0 | return result; |
| 754 | }, | |
| 755 | /** | |
| 756 | * 查询一条数据 | |
| 757 | * @return 返回一个promise | |
| 758 | */ | |
| 759 | find: function(options){ | |
| 760 | 0 | var self = this; |
| 761 | 0 | var parsedOptions = {}; |
| 762 | 0 | return this.parseOptions(options, { |
| 763 | limit: 1 | |
| 764 | }).then(function(options){ | |
| 765 | 0 | parsedOptions = options; |
| 766 | 0 | return self.db.select(options); |
| 767 | }).then(function(data){ | |
| 768 | 0 | return self._afterFind(data[0] || {}, parsedOptions); |
| 769 | }); | |
| 770 | }, | |
| 771 | /** | |
| 772 | * 查询后置操作 | |
| 773 | * @param {[type]} result [description] | |
| 774 | * @param {[type]} options [description] | |
| 775 | * @return {[type]} [description] | |
| 776 | */ | |
| 777 | _afterSelect: function(result){ | |
| 778 | 0 | return result; |
| 779 | }, | |
| 780 | /** | |
| 781 | * 查询数据 | |
| 782 | * @return 返回一个promise | |
| 783 | */ | |
| 784 | select: function(options){ | |
| 785 | 0 | var self = this; |
| 786 | 0 | var parsedOptions = {}; |
| 787 | 0 | return this.parseOptions(options).then(function(options){ |
| 788 | 0 | parsedOptions = options; |
| 789 | 0 | return self.db.select(options); |
| 790 | }).then(function(result){ | |
| 791 | 0 | return self._afterSelect(result, parsedOptions); |
| 792 | }); | |
| 793 | }, | |
| 794 | selectAdd: function(fields, table, options){ | |
| 795 | 0 | var self = this; |
| 796 | 0 | return this.parseOptions(options).then(function(options){ |
| 797 | 0 | fields = fields || options.field; |
| 798 | 0 | table = table || self.getTableName(); |
| 799 | 0 | return self.db.selectInsert(fields, table, options); |
| 800 | }); | |
| 801 | }, | |
| 802 | /** | |
| 803 | * 返回数据里含有count信息的查询 | |
| 804 | * @param options 查询选项 | |
| 805 | * @param pageFlag 当页面不合法时的处理方式,true为获取第一页,false为获取最后一页,undefined获取为空 | |
| 806 | * @return promise | |
| 807 | */ | |
| 808 | countSelect: function(options, pageFlag){ | |
| 809 | 0 | if (isBoolean(options)) { |
| 810 | 0 | pageFlag = options; |
| 811 | 0 | options = {}; |
| 812 | } | |
| 813 | 0 | var self = this; |
| 814 | //解析后的options | |
| 815 | 0 | var parsedOptions = {}; |
| 816 | 0 | var result = {}; |
| 817 | 0 | return this.parseOptions(options).then(function(options){ |
| 818 | 0 | delete options.table; |
| 819 | 0 | parsedOptions = options; |
| 820 | 0 | return self.options({ |
| 821 | where: options.where, | |
| 822 | cache: options.cache, | |
| 823 | join: options.join | |
| 824 | }).count(self.getTableName() + '.' + self.getPk()); | |
| 825 | }).then(function(count){ | |
| 826 | 0 | var pageOptions = parsePage(parsedOptions); |
| 827 | 0 | var totalPage = Math.ceil(count / pageOptions.num); |
| 828 | 0 | if (isBoolean(pageFlag)) { |
| 829 | 0 | if (pageOptions.page > totalPage) { |
| 830 | 0 | pageOptions.page = pageFlag === true ? 1 : totalPage; |
| 831 | } | |
| 832 | 0 | parsedOptions.page = pageOptions.page + ',' + pageOptions.num; |
| 833 | } | |
| 834 | 0 | result = extend({count: count, total: totalPage}, pageOptions); |
| 835 | 0 | return self.select(parsedOptions); |
| 836 | }).then(function(data){ | |
| 837 | 0 | result.data = data; |
| 838 | 0 | return result; |
| 839 | }); | |
| 840 | }, | |
| 841 | /** | |
| 842 | * 获取一条记录的某个字段值 | |
| 843 | * @return {[type]} [description] | |
| 844 | */ | |
| 845 | getField: function(field, sepa){ | |
| 846 | 0 | field = field.trim(); |
| 847 | 0 | var self = this; |
| 848 | 0 | var multi = false; |
| 849 | 0 | var parseOptions; |
| 850 | 0 | return this.parseOptions({ |
| 851 | 'field': field | |
| 852 | }).then(function(options){ | |
| 853 | 0 | parseOptions = options; |
| 854 | 0 | if (field.indexOf(',') > -1) { |
| 855 | 0 | if (options.limit === undefined && isNumber(sepa)) { |
| 856 | 0 | options.limit = sepa; |
| 857 | } | |
| 858 | 0 | multi = true; |
| 859 | }else{ | |
| 860 | 0 | options.limit = isNumber(sepa) ? sepa : 1; |
| 861 | } | |
| 862 | 0 | return self.db.select(options); |
| 863 | }).then(function(data){ | |
| 864 | 0 | if (multi) { |
| 865 | 0 | var length = field.split(',').length; |
| 866 | 0 | field = Object.keys(data[0] || {}); |
| 867 | 0 | var key = field.shift(); |
| 868 | 0 | var key2 = field.shift(); |
| 869 | 0 | var cols = {}; |
| 870 | 0 | data.forEach(function(item){ |
| 871 | 0 | var name = item[key]; |
| 872 | 0 | if (length === 2) { |
| 873 | 0 | cols[name] = item[key2]; |
| 874 | }else{ | |
| 875 | 0 | cols[name] = isString(sepa) ? item.join(sepa) : item; |
| 876 | } | |
| 877 | }); | |
| 878 | 0 | return cols; |
| 879 | }else{ | |
| 880 | 0 | if (sepa !== true && parseOptions.limit === 1) { |
| 881 | 0 | return data[0]; |
| 882 | } | |
| 883 | 0 | return Object.values(data[0] || {})[0]; |
| 884 | } | |
| 885 | }); | |
| 886 | }, | |
| 887 | /** | |
| 888 | * 根据某个字段值获取一条数据 | |
| 889 | * @param {[type]} name [description] | |
| 890 | * @param {[type]} value [description] | |
| 891 | * @return {[type]} [description] | |
| 892 | */ | |
| 893 | getBy: function(name, value){ | |
| 894 | 0 | var where = getObject(name, value); |
| 895 | 0 | return this.where(where).find(); |
| 896 | }, | |
| 897 | /** | |
| 898 | * SQL查询 | |
| 899 | * @return {[type]} [description] | |
| 900 | */ | |
| 901 | query: function(sql, parse){ | |
| 902 | 0 | if (parse !== undefined && !isBoolean(parse) && !isArray(parse)) { |
| 903 | 0 | parse = [].slice.call(arguments); |
| 904 | 0 | parse.shift(); |
| 905 | } | |
| 906 | 0 | var self = this; |
| 907 | 0 | return this.parseSql(sql, parse).then(function(sql){ |
| 908 | 0 | return self.db.query(sql); |
| 909 | }); | |
| 910 | }, | |
| 911 | /** | |
| 912 | * 执行SQL语法,非查询类的SQL语句,返回值为影响的行数 | |
| 913 | * @param {[type]} sql [description] | |
| 914 | * @param {[type]} parse [description] | |
| 915 | * @return {[type]} [description] | |
| 916 | */ | |
| 917 | execute: function(sql, parse){ | |
| 918 | 0 | if (parse !== undefined && !isBoolean(parse) && !isArray(parse)) { |
| 919 | 0 | parse = [].slice.call(arguments); |
| 920 | 0 | parse.shift(); |
| 921 | } | |
| 922 | 0 | var self = this; |
| 923 | 0 | return this.parseSql(sql, parse).then(function(sql){ |
| 924 | 0 | return self.db.execute(sql); |
| 925 | }); | |
| 926 | }, | |
| 927 | /** | |
| 928 | * 解析SQL语句 | |
| 929 | * @return promise [description] | |
| 930 | */ | |
| 931 | parseSql: function(sql, parse){ | |
| 932 | 0 | var promise = null; |
| 933 | 0 | var self = this; |
| 934 | 0 | if (parse === true) { |
| 935 | 0 | promise = this.parseOptions().then(function(options){ |
| 936 | 0 | return self.db.parseSql(options); |
| 937 | }); | |
| 938 | }else{ | |
| 939 | 0 | if (parse === undefined) { |
| 940 | 0 | parse = []; |
| 941 | }else{ | |
| 942 | 0 | parse = isArray(parse) ? parse : [parse]; |
| 943 | } | |
| 944 | 0 | parse.unshift(sql); |
| 945 | 0 | sql = util.format.apply(null, parse); |
| 946 | 0 | var map = { |
| 947 | '__TABLE__': '`' + this.getTableName() + '`' | |
| 948 | }; | |
| 949 | 0 | sql = sql.replace(/__([A-Z]+)__/g, function(a, b){ |
| 950 | 0 | return map[a] || ('`' + C('db_prefix') + b.toLowerCase() + '`'); |
| 951 | }); | |
| 952 | 0 | promise = getPromise(sql); |
| 953 | } | |
| 954 | 0 | this.initDb().setModel(self.name); |
| 955 | 0 | return promise; |
| 956 | }, | |
| 957 | /** | |
| 958 | * 设置数据对象值 | |
| 959 | * @return {[type]} [description] | |
| 960 | */ | |
| 961 | data: function(data){ | |
| 962 | 0 | if (data === true) { |
| 963 | 0 | return this._data; |
| 964 | } | |
| 965 | 0 | if (isString(data)) { |
| 966 | 0 | data = querystring.parse(data); |
| 967 | } | |
| 968 | 0 | this._data = data; |
| 969 | 0 | return this; |
| 970 | }, | |
| 971 | /** | |
| 972 | * 设置操作选项 | |
| 973 | * @param {[type]} options [description] | |
| 974 | * @return {[type]} [description] | |
| 975 | */ | |
| 976 | options: function(options){ | |
| 977 | 0 | if (options === true) { |
| 978 | 0 | return this._options; |
| 979 | } | |
| 980 | 0 | this._options = options; |
| 981 | 0 | return this; |
| 982 | }, | |
| 983 | /** | |
| 984 | * 关闭数据库连接 | |
| 985 | * @return {[type]} [description] | |
| 986 | */ | |
| 987 | close: function(){ | |
| 988 | 0 | delete dbInstances[this.configKey]; |
| 989 | 0 | if (this.db) { |
| 990 | 0 | this.db.close(); |
| 991 | } | |
| 992 | } | |
| 993 | }; | |
| 994 | }).extend(function(){ | |
| 995 | 1 | 'use strict'; |
| 996 | //追加的方法 | |
| 997 | 1 | var methods = {}; |
| 998 | // 链操作方法列表 | |
| 999 | 1 | var methodNameList = [ |
| 1000 | 'table','order','alias','having','group', | |
| 1001 | 'lock','auto','filter','validate' | |
| 1002 | ]; | |
| 1003 | 1 | methodNameList.forEach(function(item){ |
| 1004 | 9 | methods[item] = function(data){ |
| 1005 | 0 | this._options[item] = data; |
| 1006 | 0 | return this; |
| 1007 | }; | |
| 1008 | }); | |
| 1009 | 1 | methods.distinct = function(data){ |
| 1010 | 0 | this._options.distinct = data; |
| 1011 | //如果传过来一个字段,则映射到field上 | |
| 1012 | 0 | if (isString(data)) { |
| 1013 | 0 | this._options.field = data; |
| 1014 | } | |
| 1015 | 0 | return this; |
| 1016 | }; | |
| 1017 | 1 | ['count','sum','min','max','avg'].forEach(function(item){ |
| 1018 | 5 | methods[item] = function(data){ |
| 1019 | 0 | var field = data || '*'; |
| 1020 | 0 | return this.getField(item.toUpperCase() + '(' + field + ') AS thinkjs_' + item, true); |
| 1021 | }; | |
| 1022 | }); | |
| 1023 | //方法别名 | |
| 1024 | 1 | var aliasMethodMap = { |
| 1025 | update: 'save', | |
| 1026 | updateField: 'setField', | |
| 1027 | updateInc: 'setInc', | |
| 1028 | updateDec: 'setDec' | |
| 1029 | }; | |
| 1030 | 1 | Object.keys(aliasMethodMap).forEach(function(key){ |
| 1031 | 4 | var value = aliasMethodMap[key]; |
| 1032 | 4 | methods[value] = function(){ |
| 1033 | 0 | return this[key].apply(this, arguments); |
| 1034 | }; | |
| 1035 | }); | |
| 1036 | 1 | return methods; |
| 1037 | }); | |
| 1038 | /** | |
| 1039 | * 关闭所有的数据库连接 | |
| 1040 | * @return {[type]} [description] | |
| 1041 | */ | |
| 1042 | 1 | Model.close = global.closeDb = function(){ |
| 1043 | 0 | 'use strict'; |
| 1044 | 0 | for(var key in dbInstances) { |
| 1045 | 0 | dbInstances[key].close(); |
| 1046 | } | |
| 1047 | ||
| 1048 | 0 | dbInstances = {}; |
| 1049 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var fs = require('fs'); |
| 2 | 1 | var cluster = require('cluster'); |
| 3 | ||
| 4 | //自动加载进行识别的路径 | |
| 5 | 1 | var autoloadPaths = {}; |
| 6 | /** | |
| 7 | * [exports description] | |
| 8 | * @type {Object} | |
| 9 | */ | |
| 10 | 1 | module.exports = { |
| 11 | /** | |
| 12 | * [start description] | |
| 13 | * @return {[type]} [description] | |
| 14 | */ | |
| 15 | start: function(){ | |
| 16 | 1 | 'use strict'; |
| 17 | 1 | this.init(); |
| 18 | 1 | this.processEvent(); |
| 19 | //加载文件 | |
| 20 | 1 | this.loadFiles(); |
| 21 | //合并自动加载的路径 | |
| 22 | 1 | this.mergeAutoloadPath(); |
| 23 | //thinkRequire的autoload | |
| 24 | 1 | registerAutoload(this.autoload); |
| 25 | //debug模式 | |
| 26 | 1 | if (APP_DEBUG) { |
| 27 | 0 | this.debug(); |
| 28 | } | |
| 29 | //记录进程的id | |
| 30 | 1 | this.logPid(); |
| 31 | ||
| 32 | 1 | thinkRequire('App').run(); |
| 33 | }, | |
| 34 | /** | |
| 35 | * 定义一些目录,加载框架的基础文件 | |
| 36 | * @return {[type]} [description] | |
| 37 | */ | |
| 38 | init: function(){ | |
| 39 | 1 | 'use strict'; |
| 40 | //系统路径设置 | |
| 41 | 1 | global.THINK_LIB_PATH = THINK_PATH + '/Lib'; |
| 42 | 1 | global.THINK_EXTEND_PATH = THINK_LIB_PATH + '/Extend'; |
| 43 | //应用路径设置 | |
| 44 | 1 | var config = { |
| 45 | COMMON_PATH: APP_PATH + '/Common', | |
| 46 | LIB_PATH: APP_PATH + '/Lib', | |
| 47 | CONF_PATH: APP_PATH + '/Conf', | |
| 48 | LANG_PATH: APP_PATH + '/Lang', | |
| 49 | VIEW_PATH: APP_PATH + '/View', | |
| 50 | //HTML_PATH: RUNTIME_PATH + '/Html', | |
| 51 | LOG_PATH: RUNTIME_PATH + '/Log', | |
| 52 | TEMP_PATH: RUNTIME_PATH + '/Temp', | |
| 53 | DATA_PATH: RUNTIME_PATH + '/Data', | |
| 54 | CACHE_PATH: RUNTIME_PATH + '/Cache' | |
| 55 | }; | |
| 56 | 1 | for (var name in config) { |
| 57 | 9 | if (global[name] === undefined) { |
| 58 | 9 | global[name] = config[name]; |
| 59 | } | |
| 60 | } | |
| 61 | 1 | require(THINK_PATH + '/Common/extend.js'); |
| 62 | 1 | require(THINK_PATH + '/Common/common.js'); |
| 63 | 1 | require(THINK_PATH + '/Common/function.js'); |
| 64 | //别名导入 | |
| 65 | 1 | aliasImport(require(THINK_PATH + '/Conf/alias.js')); |
| 66 | }, | |
| 67 | /** | |
| 68 | * 注册异常处理 | |
| 69 | * @return {[type]} [description] | |
| 70 | */ | |
| 71 | processEvent: function(){ | |
| 72 | 1 | 'use strict'; |
| 73 | 1 | process.on('uncaughtException', function(err) { |
| 74 | 0 | console.log(isError(err) ? err.stack : err); |
| 75 | }); | |
| 76 | }, | |
| 77 | /** | |
| 78 | * 加载项目下对应的文件 | |
| 79 | * @return {[type]} [description] | |
| 80 | */ | |
| 81 | loadFiles: function(){ | |
| 82 | 1 | 'use strict'; |
| 83 | 1 | C(null); //移除之前的所有配置 |
| 84 | //加载系统默认配置 | |
| 85 | 1 | C(require(THINK_PATH + '/Conf/config.js')); |
| 86 | //加载用户配置 | |
| 87 | 1 | var file = CONF_PATH + '/config.js'; |
| 88 | 1 | if (isFile(file)) { |
| 89 | 1 | C(require(file)); |
| 90 | } | |
| 91 | //加载模式的配置文件 | |
| 92 | 1 | if (APP_MODE) { |
| 93 | 1 | var modeFiles = [ |
| 94 | THINK_PATH + '/Conf/mode.js', | |
| 95 | CONF_PATH + '/mode.js' | |
| 96 | ]; | |
| 97 | 1 | modeFiles.forEach(function(file){ |
| 98 | 2 | if (isFile(file)) { |
| 99 | 1 | var conf = require(file); |
| 100 | 1 | if (conf[APP_MODE]) { |
| 101 | 1 | C(conf[APP_MODE]); |
| 102 | } | |
| 103 | } | |
| 104 | }); | |
| 105 | } | |
| 106 | //自定义路由 | |
| 107 | 1 | if (C('url_route_on') && isFile(CONF_PATH + '/route.js')) { |
| 108 | 0 | C('url_route_rules', require(CONF_PATH + '/route.js')); |
| 109 | } | |
| 110 | //加载行为配置 | |
| 111 | 1 | if (C('app_tag_on')) { |
| 112 | //加载系统行为配置 | |
| 113 | 1 | C('sys_tag', require(THINK_PATH + '/Conf/tag.js')); |
| 114 | //加载用户的行为配置 | |
| 115 | 1 | var tagFile = CONF_PATH + '/tag.js'; |
| 116 | 1 | if (isFile(tagFile)) { |
| 117 | 0 | C('tag', require(tagFile)); |
| 118 | } | |
| 119 | } | |
| 120 | //common文件 | |
| 121 | 1 | if (isFile(COMMON_PATH + '/common.js')) { |
| 122 | 1 | require(COMMON_PATH + '/common.js'); |
| 123 | } | |
| 124 | //别名文件 | |
| 125 | 1 | if (isFile(COMMON_PATH + '/alias.js')) { |
| 126 | 0 | aliasImport(require(COMMON_PATH + '/alias.js')); |
| 127 | } | |
| 128 | 1 | this.loadExtConfig(); |
| 129 | 1 | this.loadExtFiles(); |
| 130 | }, | |
| 131 | //加载自定义外部文件 | |
| 132 | loadExtFiles: function(){ | |
| 133 | 1 | 'use strict'; |
| 134 | 1 | var files = C('load_ext_file'); |
| 135 | 1 | if (files) { |
| 136 | 1 | if (isString(files)) { |
| 137 | 0 | files = files.split(','); |
| 138 | } | |
| 139 | 1 | files.forEach(function(file){ |
| 140 | 0 | file = COMMON_PATH + '/' + file + '.js'; |
| 141 | 0 | if (isFile(file)) { |
| 142 | 0 | require(file); |
| 143 | } | |
| 144 | }); | |
| 145 | } | |
| 146 | }, | |
| 147 | //加载额外的配置 | |
| 148 | loadExtConfig: function(){ | |
| 149 | 1 | 'use strict'; |
| 150 | 1 | var files = C('load_ext_config'); |
| 151 | 1 | if (files) { |
| 152 | 1 | if (isString(files)) { |
| 153 | 0 | files = files.split(','); |
| 154 | } | |
| 155 | 1 | files.forEach(function(file){ |
| 156 | 0 | file = CONF_PATH + '/' + file + '.js'; |
| 157 | 0 | if (isFile(file)) { |
| 158 | 0 | C(require(file)); |
| 159 | } | |
| 160 | }); | |
| 161 | } | |
| 162 | }, | |
| 163 | //加载debug模式配置文件 | |
| 164 | loadDebugFiles: function(){ | |
| 165 | 0 | 'use strict'; |
| 166 | //加载debug模式下的配置 | |
| 167 | 0 | C(require(THINK_PATH + '/Conf/debug.js')); |
| 168 | //debug下自定义状态的配置 | |
| 169 | 0 | var status = C('app_status'); |
| 170 | 0 | if (status) { |
| 171 | 0 | if (isFile(CONF_PATH + '/' + status + '.js')) { |
| 172 | 0 | C(require(CONF_PATH + '/' + status + '.js')); |
| 173 | } | |
| 174 | }else{ | |
| 175 | 0 | if (isFile(CONF_PATH + '/debug.js')) { |
| 176 | 0 | C(require(CONF_PATH + '/debug.js')); |
| 177 | } | |
| 178 | } | |
| 179 | 0 | if (APP_MODE) { |
| 180 | 0 | var modeFiles = [ |
| 181 | THINK_PATH + '/Conf/mode.js', | |
| 182 | CONF_PATH + '/mode.js' | |
| 183 | ]; | |
| 184 | 0 | modeFiles.forEach(function(file){ |
| 185 | 0 | if (isFile(file)) { |
| 186 | 0 | var conf = require(file); |
| 187 | 0 | var key = APP_MODE + '_debug'; |
| 188 | 0 | if (conf[key]) { |
| 189 | 0 | C(conf[key]); |
| 190 | } | |
| 191 | } | |
| 192 | }); | |
| 193 | } | |
| 194 | }, | |
| 195 | /** | |
| 196 | * debug模式下一些特殊处理 | |
| 197 | * @return {[type]} [description] | |
| 198 | */ | |
| 199 | debug: function(){ | |
| 200 | 0 | 'use strict'; |
| 201 | 0 | this.loadDebugFiles(); |
| 202 | //清除require的缓存 | |
| 203 | 0 | if (C('clear_require_cache')) { |
| 204 | //这些文件不清除缓存 | |
| 205 | 0 | var retainFiles = C('debug_retain_files'); |
| 206 | 0 | var self = this; |
| 207 | 0 | setInterval(function(){ |
| 208 | 0 | var fn = function(item){ |
| 209 | //windows目录定界符为\ | |
| 210 | 0 | if (process.platform === 'win32') { |
| 211 | 0 | item = item.replace(/\//g, '\\'); |
| 212 | } | |
| 213 | 0 | if (file.indexOf(item) > -1) { |
| 214 | 0 | return true; |
| 215 | } | |
| 216 | }; | |
| 217 | 0 | for(var file in require.cache){ |
| 218 | 0 | var flag = retainFiles.some(fn); |
| 219 | 0 | if (!flag) { |
| 220 | 0 | delete require.cache[file]; |
| 221 | } | |
| 222 | } | |
| 223 | 0 | self.loadFiles(); |
| 224 | 0 | self.loadDebugFiles(); |
| 225 | }, 100); | |
| 226 | } | |
| 227 | }, | |
| 228 | /** | |
| 229 | * 记录当前进程的id | |
| 230 | * 记录在Runtime/Data/app.pid文件里 | |
| 231 | * @return {[type]} [description] | |
| 232 | */ | |
| 233 | logPid: function(){ | |
| 234 | 1 | 'use strict'; |
| 235 | 1 | if (C('log_process_pid') && cluster.isMaster) { |
| 236 | 0 | mkdir(DATA_PATH); |
| 237 | 0 | var pidFile = DATA_PATH + '/app.pid'; |
| 238 | 0 | fs.writeFileSync(pidFile, process.pid); |
| 239 | 0 | chmod(pidFile); |
| 240 | //进程退出时删除该文件 | |
| 241 | 0 | process.on('SIGTERM', function () { |
| 242 | 0 | if (fs.existsSync(pidFile)) { |
| 243 | 0 | fs.unlinkSync(pidFile); |
| 244 | } | |
| 245 | 0 | process.exit(0); |
| 246 | }); | |
| 247 | } | |
| 248 | }, | |
| 249 | /** | |
| 250 | * 合并autoload的path | |
| 251 | * @return {[type]} [description] | |
| 252 | */ | |
| 253 | mergeAutoloadPath: function(){ | |
| 254 | 1 | 'use strict'; |
| 255 | 1 | var file = '__CLASS__.js'; |
| 256 | 1 | var sysAutoloadPath = { |
| 257 | 'Behavior': [ | |
| 258 | LIB_PATH + '/Behavior/' + file, | |
| 259 | THINK_LIB_PATH + '/Behavior/' + file | |
| 260 | ], | |
| 261 | 'Model': [ | |
| 262 | LIB_PATH + '/Model/' + file, | |
| 263 | THINK_EXTEND_PATH + '/Model/' + file | |
| 264 | ], | |
| 265 | 'Controller': [ | |
| 266 | LIB_PATH + '/Controller/' + file, | |
| 267 | THINK_EXTEND_PATH + '/Controller/' + file | |
| 268 | ], | |
| 269 | 'Cache': [ | |
| 270 | LIB_PATH + '/Driver/Cache/' + file, | |
| 271 | THINK_LIB_PATH + '/Driver/Cache/' + file | |
| 272 | ], | |
| 273 | 'Db': [ | |
| 274 | LIB_PATH + '/Driver/Db/' + file, | |
| 275 | THINK_LIB_PATH + '/Driver/Db/' + file | |
| 276 | ], | |
| 277 | 'Template': [ | |
| 278 | LIB_PATH + '/Driver/Template/' + file, | |
| 279 | THINK_LIB_PATH + '/Driver/Template/' + file | |
| 280 | ], | |
| 281 | 'Socket': [ | |
| 282 | LIB_PATH + '/Driver/Socket/' + file, | |
| 283 | THINK_LIB_PATH + '/Driver/Socket/' + file | |
| 284 | ], | |
| 285 | 'Session': [ | |
| 286 | LIB_PATH + '/Driver/Session/' + file, | |
| 287 | THINK_LIB_PATH + '/Driver/Session/' + file | |
| 288 | ] | |
| 289 | }; | |
| 290 | 1 | var autoloadPath = C('autoload_path'); |
| 291 | 1 | for(var type in autoloadPath){ |
| 292 | 0 | var paths = autoloadPath[type]; |
| 293 | 0 | var override = false; |
| 294 | 0 | if (!isArray(paths)) { |
| 295 | 0 | paths = [paths]; |
| 296 | 0 | }else if (isBoolean(paths[0])) { |
| 297 | 0 | override = paths.shift(); |
| 298 | } | |
| 299 | 0 | if (override) { |
| 300 | 0 | sysAutoloadPath[type] = paths; |
| 301 | }else{ | |
| 302 | 0 | paths.push.apply(paths, sysAutoloadPath[type]); |
| 303 | 0 | sysAutoloadPath[type] = paths; |
| 304 | } | |
| 305 | } | |
| 306 | 1 | autoloadPaths = sysAutoloadPath; |
| 307 | }, | |
| 308 | //thinkRequire的自动加载 | |
| 309 | autoload: function(cls){ | |
| 310 | 19 | 'use strict'; |
| 311 | 19 | var filepath = ''; |
| 312 | 19 | var fn = function(item){ |
| 313 | 35 | item = item.replace(/__CLASS__/g, cls); |
| 314 | 35 | if (isFile(item)) { |
| 315 | 18 | filepath = item; |
| 316 | 18 | return true; |
| 317 | } | |
| 318 | }; | |
| 319 | 19 | for(var name in autoloadPaths){ |
| 320 | 72 | var length = name.length; |
| 321 | 72 | if (cls.substr(0 - length) === name) { |
| 322 | 18 | var list = autoloadPaths[name]; |
| 323 | 18 | list.some(fn); |
| 324 | 18 | if (filepath) { |
| 325 | 18 | if (!APP_DEBUG) { |
| 326 | 18 | aliasImport(cls, filepath); |
| 327 | } | |
| 328 | 18 | return filepath; |
| 329 | } | |
| 330 | } | |
| 331 | } | |
| 332 | } | |
| 333 | }; | |
| 334 |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * view | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | module.exports = Class(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | 1 | return { |
| 8 | tVar: {}, | |
| 9 | init: function(http){ | |
| 10 | 1 | this.http = http; |
| 11 | }, | |
| 12 | /** | |
| 13 | * 给变量赋值 | |
| 14 | * @param {[type]} name [description] | |
| 15 | * @param {[type]} value [description] | |
| 16 | * @return {[type]} [description] | |
| 17 | */ | |
| 18 | assign: function(name, value){ | |
| 19 | 2 | if (name === undefined) { |
| 20 | 0 | return this.tVar; |
| 21 | } | |
| 22 | 2 | if (isString(name) && arguments.length === 1) { |
| 23 | 0 | return this.tVar[name]; |
| 24 | } | |
| 25 | 2 | if (isObject(name)) { |
| 26 | 0 | this.tVar = extend(this.tVar, name); |
| 27 | }else{ | |
| 28 | 2 | this.tVar[name] = value; |
| 29 | } | |
| 30 | }, | |
| 31 | /** | |
| 32 | * 获取变量的值 | |
| 33 | * @param {[type]} name [description] | |
| 34 | * @return {[type]} [description] | |
| 35 | */ | |
| 36 | get: function(name){ | |
| 37 | 0 | if (!name) { |
| 38 | 0 | return this.tVar; |
| 39 | } | |
| 40 | 0 | return this.tVar[name]; |
| 41 | }, | |
| 42 | /** | |
| 43 | * 输出模版文件内容 | |
| 44 | * @param {[type]} templateFile [description] | |
| 45 | * @param {[type]} charset [description] | |
| 46 | * @param {[type]} contentType [description] | |
| 47 | * @param {[type]} content [description] | |
| 48 | * @return {[type]} [description] | |
| 49 | */ | |
| 50 | display: function(templateFile, charset, contentType, content){ | |
| 51 | 1 | var self = this; |
| 52 | 1 | return tag('view_init', this.http).then(function(){ |
| 53 | 1 | return self.fetch(templateFile, content); |
| 54 | }).then(function(content){ | |
| 55 | 1 | self.render(content, charset, contentType); |
| 56 | 1 | return tag('view_end', self.http, content); |
| 57 | }).then(function(){ | |
| 58 | 1 | return self.http.end(); |
| 59 | }).catch(function(){ | |
| 60 | 0 | return self.http.end(); |
| 61 | }); | |
| 62 | }, | |
| 63 | /** | |
| 64 | * 渲染模版 | |
| 65 | * @param {[type]} content [description] | |
| 66 | * @param {[type]} charset [description] | |
| 67 | * @param {[type]} contentType [description] | |
| 68 | * @return {[type]} [description] | |
| 69 | */ | |
| 70 | render: function(content, charset, contentType){ | |
| 71 | 1 | if (!this.http.cthIsSend) { |
| 72 | 1 | if (charset === undefined) { |
| 73 | 1 | charset = C('encoding'); |
| 74 | } | |
| 75 | 1 | if (contentType === undefined) { |
| 76 | 1 | contentType = C('tpl_content_type'); |
| 77 | } | |
| 78 | 1 | this.http.setHeader('Content-Type', contentType + '; charset=' + charset); |
| 79 | } | |
| 80 | 1 | if (C('show_exec_time')) { |
| 81 | 0 | this.http.sendTime('Exec-Time'); |
| 82 | } | |
| 83 | 1 | this.http.echo(content || '', C('encoding')); |
| 84 | }, | |
| 85 | /** | |
| 86 | * 获取模版文件内容 | |
| 87 | * @param {[type]} templateFile [description] | |
| 88 | * @param {[type]} content [description] | |
| 89 | * @return {[type]} [description] | |
| 90 | */ | |
| 91 | fetch: function(templateFile, content){ | |
| 92 | 1 | var self = this; |
| 93 | 1 | var promise = getPromise(''); |
| 94 | 1 | if (content === undefined) { |
| 95 | 1 | promise = tag('view_template', this.http, templateFile).then(function(file){ |
| 96 | 1 | if (file && isFile(file)) { |
| 97 | 1 | templateFile = file; |
| 98 | } | |
| 99 | }); | |
| 100 | } | |
| 101 | 1 | return promise.then(function(){ |
| 102 | 1 | if (!templateFile) { |
| 103 | 0 | return ''; |
| 104 | } | |
| 105 | 1 | return tag('view_parse', self.http, { |
| 106 | 'var': self.tVar, | |
| 107 | 'file': templateFile, | |
| 108 | 'content': content | |
| 109 | }).then(function(content){ | |
| 110 | 1 | return tag('view_filter', self.http, content); |
| 111 | }); | |
| 112 | }).catch(function(err){ | |
| 113 | //输出模版解析异常 | |
| 114 | 0 | console.log(isError(err) ? err.stack : err); |
| 115 | }); | |
| 116 | } | |
| 117 | }; | |
| 118 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var fs = require('fs'); |
| 2 | /** | |
| 3 | * 基于文件的缓存 | |
| 4 | * @return {[type]} [description] | |
| 5 | */ | |
| 6 | 1 | module.exports = Cache(function(){ |
| 7 | 1 | 'use strict'; |
| 8 | 1 | return { |
| 9 | gcType: 'FileCache', | |
| 10 | init: function(options){ | |
| 11 | 0 | this.options = extend({ |
| 12 | cache_path: C('cache_path'), //缓存目录 | |
| 13 | cache_path_level: 2, //缓存子目录深度 | |
| 14 | cache_file_suffix: C('cache_file_suffix') //缓存文件后缀名 | |
| 15 | }, options); | |
| 16 | 0 | mkdir(this.options.cache_path); |
| 17 | 0 | this.gcType += ':' + this.options.cache_path; |
| 18 | ||
| 19 | 0 | this.super_('init', this.options); |
| 20 | }, | |
| 21 | /** | |
| 22 | * 存储的缓存文件 | |
| 23 | * @param {[type]} name [description] | |
| 24 | * @return {[type]} [description] | |
| 25 | */ | |
| 26 | getStoredFile: function(name){ | |
| 27 | 0 | name = md5(this.key || name); |
| 28 | 0 | var dir = name.split('').slice(0, this.options.cache_path_level).join('/'); |
| 29 | 0 | mkdir(this.options.cache_path + '/' + dir); |
| 30 | 0 | var path = this.options.cache_path + '/' + dir + '/' + name + this.options.cache_file_suffix; |
| 31 | 0 | return path; |
| 32 | }, | |
| 33 | /** | |
| 34 | * 获取缓存,返回promise | |
| 35 | * @param {[type]} name [description] | |
| 36 | * @return {[type]} [description] | |
| 37 | */ | |
| 38 | getData: function(name){ | |
| 39 | 0 | var filePath = this.getStoredFile(name); |
| 40 | 0 | if (!isFile(filePath)) { |
| 41 | 0 | return getPromise(); |
| 42 | } | |
| 43 | 0 | var deferred = getDefer(); |
| 44 | 0 | fs.exists(filePath, function(exists){ |
| 45 | 0 | if (!exists) { |
| 46 | 0 | return deferred.resolve(); |
| 47 | } | |
| 48 | 0 | fs.readFile(filePath, {encoding: 'utf8'}, function(error, content){ |
| 49 | 0 | if (error || !content) { |
| 50 | 0 | return deferred.resolve(); |
| 51 | } | |
| 52 | 0 | try{ |
| 53 | 0 | var data = JSON.parse(content); |
| 54 | 0 | if (Date.now() > data.expire) { |
| 55 | 0 | fs.unlink(filePath, function(){ |
| 56 | 0 | return deferred.resolve(); |
| 57 | }); | |
| 58 | }else{ | |
| 59 | 0 | deferred.resolve(data.data); |
| 60 | } | |
| 61 | }catch(e){ | |
| 62 | //异常时删除该文件 | |
| 63 | 0 | fs.unlink(filePath, function(){ |
| 64 | 0 | return deferred.resolve(); |
| 65 | }); | |
| 66 | } | |
| 67 | }); | |
| 68 | }); | |
| 69 | 0 | return deferred.promise; |
| 70 | }, | |
| 71 | get: function(name){ | |
| 72 | 0 | return this.getData().then(function(data){ |
| 73 | 0 | return (data || {})[name]; |
| 74 | }) | |
| 75 | }, | |
| 76 | setData: function(name, value, timeout){ | |
| 77 | 0 | if (isObject(name)) { |
| 78 | 0 | timeout = value; |
| 79 | } | |
| 80 | 0 | if (timeout === undefined) { |
| 81 | 0 | timeout = this.options.timeout; |
| 82 | } | |
| 83 | 0 | var filePath = this.getStoredFile(name); |
| 84 | 0 | var data = { |
| 85 | data: isObject(name) ? name : getObject(name, value), | |
| 86 | expire: Date.now() + timeout * 1000, | |
| 87 | timeout: timeout | |
| 88 | }; | |
| 89 | 0 | var deferred = getDefer(); |
| 90 | 0 | fs.writeFile(filePath, JSON.stringify(data), function(){ |
| 91 | //修改缓存文件权限,避免不同账号下启动时可能会出现无权限的问题 | |
| 92 | 0 | chmod(filePath); |
| 93 | 0 | deferred.resolve(); |
| 94 | }) | |
| 95 | 0 | return deferred.promise; |
| 96 | }, | |
| 97 | /** | |
| 98 | * 设置缓存 | |
| 99 | * @param {[type]} name [description] | |
| 100 | * @param {[type]} value [description] | |
| 101 | * @param {[type]} expire [description] | |
| 102 | */ | |
| 103 | set: function(name, value, timeout){ | |
| 104 | 0 | return this.setData(name, value, timeout); |
| 105 | }, | |
| 106 | /** | |
| 107 | * 删除缓存 | |
| 108 | * @param {[type]} name [description] | |
| 109 | * @return {[type]} [description] | |
| 110 | */ | |
| 111 | rm: function(name){ | |
| 112 | 0 | var filePath = this.getStoredFile(name); |
| 113 | 0 | if (isFile(filePath)) { |
| 114 | 0 | var deferred = getDefer(); |
| 115 | 0 | fs.unlink(filePath, function(){ |
| 116 | 0 | deferred.resolve(); |
| 117 | }) | |
| 118 | 0 | return deferred.promise; |
| 119 | } | |
| 120 | 0 | return getPromise(); |
| 121 | }, | |
| 122 | /** | |
| 123 | * gc | |
| 124 | * @param {[type]} now [description] | |
| 125 | * @return {[type]} [description] | |
| 126 | */ | |
| 127 | gc: function(now, path){ | |
| 128 | 0 | path = path || this.options.cache_path; |
| 129 | 0 | var self = this; |
| 130 | 0 | var files = fs.readdirSync(path); |
| 131 | 0 | files.forEach(function(item){ |
| 132 | 0 | var filePath = path + '/' + item; |
| 133 | 0 | var stat = fs.statSync(filePath); |
| 134 | 0 | if (stat.isDirectory()) { |
| 135 | 0 | self.gc(now, filePath); |
| 136 | 0 | }else if (stat.isFile()) { |
| 137 | 0 | var data = getFileContent(filePath); |
| 138 | 0 | try{ |
| 139 | 0 | data = JSON.parse(data); |
| 140 | 0 | if (now > data.expire) { |
| 141 | 0 | fs.unlink(filePath, function(){}); |
| 142 | } | |
| 143 | }catch(e){} | |
| 144 | } | |
| 145 | }); | |
| 146 | } | |
| 147 | }; | |
| 148 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var memcache = thinkRequire('MemcacheSocket'); |
| 2 | 1 | module.exports = Cache(function(){ |
| 3 | 1 | 'use strict'; |
| 4 | 1 | var instance = null; |
| 5 | 1 | return { |
| 6 | namePrefix: '__thinkjs__', | |
| 7 | init: function(options){ | |
| 8 | 0 | this.super_('init', options); |
| 9 | 0 | if (!instance) { |
| 10 | 0 | instance = memcache(C('memcache_port'), C('memcache_host')); |
| 11 | } | |
| 12 | 0 | this.handle = instance; |
| 13 | }, | |
| 14 | get: function(name){ | |
| 15 | 0 | return this.handle.get(this.namePrefix + name).then(function(value){ |
| 16 | 0 | return value ? JSON.parse(value) : value; |
| 17 | }) | |
| 18 | }, | |
| 19 | set: function(name, value, timeout){ | |
| 20 | 0 | timeout = timeout || this.options.timeout; |
| 21 | 0 | return this.handle.set(this.namePrefix + name, JSON.stringify(value), timeout); |
| 22 | }, | |
| 23 | rm: function(name){ | |
| 24 | 0 | return this.handle.delete(this.namePrefix + name); |
| 25 | } | |
| 26 | }; | |
| 27 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * mysql数据库 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | var mysqlSocket = thinkRequire('MysqlSocket'); |
| 6 | 1 | module.exports = Db(function(){ |
| 7 | 1 | 'use strict'; |
| 8 | 1 | var keyReg = /[,\'\"\*\(\)`.\s]/; |
| 9 | 1 | return { |
| 10 | init: function(config){ | |
| 11 | 0 | this.super_('init'); |
| 12 | 0 | if (config) { |
| 13 | 0 | this.config = config; |
| 14 | } | |
| 15 | 0 | this.lastInsertId = 0; |
| 16 | //查询等待 | |
| 17 | 0 | this.queryWaiting = {}; |
| 18 | }, | |
| 19 | /** | |
| 20 | * 连接数据库 | |
| 21 | * @param {[type]} config [description] | |
| 22 | * @param {[type]} linknum [description] | |
| 23 | * @return {[type]} [description] | |
| 24 | */ | |
| 25 | connect: function(config, linknum){ | |
| 26 | 0 | linknum = linknum || 0; |
| 27 | 0 | if (!this.linkIds[linknum]) { |
| 28 | 0 | config = config || this.config; |
| 29 | 0 | this.linkIds[linknum] = mysqlSocket(config); |
| 30 | 0 | this.connected = true; |
| 31 | } | |
| 32 | 0 | return this.linkIds[linknum]; |
| 33 | }, | |
| 34 | /** | |
| 35 | * 查询一条sql | |
| 36 | * @param string str | |
| 37 | * @return promise | |
| 38 | */ | |
| 39 | query: function(str){ | |
| 40 | 0 | this.initConnect(false); |
| 41 | 0 | if (!this.linkId) { |
| 42 | 0 | return getPromise('linkId is null', true); |
| 43 | } | |
| 44 | 0 | this.queryStr = str; |
| 45 | 0 | var self = this; |
| 46 | 0 | if (!(str in this.queryWaiting)) { |
| 47 | 0 | this.queryWaiting[str] = []; |
| 48 | 0 | return this.linkId.query(str).then(function(data){ |
| 49 | 0 | process.nextTick(function(){ |
| 50 | 0 | self.queryWaiting[str].forEach(function(deferred){ |
| 51 | 0 | deferred.resolve(data); |
| 52 | }); | |
| 53 | 0 | delete self.queryWaiting[str]; |
| 54 | }) | |
| 55 | 0 | return data; |
| 56 | }); | |
| 57 | }else{ | |
| 58 | 0 | var deferred = getDefer(); |
| 59 | 0 | this.queryWaiting[str].push(deferred); |
| 60 | 0 | return deferred.promise; |
| 61 | } | |
| 62 | }, | |
| 63 | /** | |
| 64 | * 执行一条sql, 返回影响的行数 | |
| 65 | * @param {[type]} str [description] | |
| 66 | * @return {[type]} [description] | |
| 67 | */ | |
| 68 | execute: function(str){ | |
| 69 | 0 | this.initConnect(false); |
| 70 | 0 | if (!this.linkId) { |
| 71 | 0 | return getPromise('linkId is null', true); |
| 72 | } | |
| 73 | 0 | this.queryStr = str; |
| 74 | 0 | var self = this; |
| 75 | 0 | return this.linkId.query(str).then(function(data){ |
| 76 | 0 | self.lastInsertId = data.insertId; |
| 77 | 0 | return data.affectedRows || 0; |
| 78 | }); | |
| 79 | }, | |
| 80 | /** | |
| 81 | * 获取数据表字段信息 | |
| 82 | * @param string tableName 数据表名 | |
| 83 | * @return promise 返回一个promise | |
| 84 | */ | |
| 85 | getFields: function(tableName){ | |
| 86 | 0 | var sql = 'SHOW COLUMNS FROM ' + this.parseKey(tableName); |
| 87 | 0 | return this.query(sql).then(function(data){ |
| 88 | 0 | var ret = {}; |
| 89 | 0 | data.forEach(function(item){ |
| 90 | 0 | ret[item.Field] = { |
| 91 | 'name': item.Field, | |
| 92 | 'type': item.Type, | |
| 93 | 'notnull': item.Null === '', | |
| 94 | 'default': item.Default, | |
| 95 | 'primary': item.Key === 'PRI', | |
| 96 | 'unique': item.Key === 'UNI', | |
| 97 | 'autoinc': item.Extra.toLowerCase() === 'auto_increment' | |
| 98 | }; | |
| 99 | }); | |
| 100 | 0 | return ret; |
| 101 | }); | |
| 102 | }, | |
| 103 | /** | |
| 104 | * 获取数据库的表信息 | |
| 105 | * @param {[type]} dbName [description] | |
| 106 | * @return {[type]} [description] | |
| 107 | */ | |
| 108 | getTables: function(dbName){ | |
| 109 | 0 | var sql = 'SHOW TABLES'; |
| 110 | 0 | if (dbName) { |
| 111 | 0 | sql += ' FROM ' + dbName; |
| 112 | } | |
| 113 | 0 | return this.query(sql).then(function(data){ |
| 114 | 0 | return data.map(function(item){ |
| 115 | 0 | for(var key in item){ |
| 116 | 0 | return item[key]; |
| 117 | } | |
| 118 | }); | |
| 119 | }); | |
| 120 | }, | |
| 121 | /** | |
| 122 | * 关闭连接 | |
| 123 | * @return {[type]} [description] | |
| 124 | */ | |
| 125 | close: function(){ | |
| 126 | 0 | if (this.linkId) { |
| 127 | 0 | this.linkId.close(); |
| 128 | 0 | this.linkId = null; |
| 129 | } | |
| 130 | }, | |
| 131 | /** | |
| 132 | * 解析key | |
| 133 | * @param {[type]} key [description] | |
| 134 | * @return {[type]} [description] | |
| 135 | */ | |
| 136 | parseKey: function(key){ | |
| 137 | 0 | key = (key || '').trim(); |
| 138 | 0 | if (!keyReg.test(key)) { |
| 139 | 0 | key = '`' + key + '`'; |
| 140 | } | |
| 141 | 0 | return key; |
| 142 | }, | |
| 143 | /** | |
| 144 | * 获取最后插入的id | |
| 145 | * @return {[type]} [description] | |
| 146 | */ | |
| 147 | getLastInsertId: function(){ | |
| 148 | 0 | return this.lastInsertId; |
| 149 | } | |
| 150 | }; | |
| 151 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * DbSession | |
| 3 | * 需要在数据库中建立对应的数据表 | |
| 4 | * | |
| 5 | * DROP TABLE IF EXISTS `think_session`; | |
| 6 | CREATE TABLE `think_session` ( | |
| 7 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, | |
| 8 | `key` varchar(255) NOT NULL DEFAULT '', | |
| 9 | `data` text, | |
| 10 | `expire` bigint(11) NOT NULL, | |
| 11 | PRIMARY KEY (`id`), | |
| 12 | UNIQUE KEY `cookie` (`key`), | |
| 13 | KEY `expire` (`expire`) | |
| 14 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |
| 15 | * | |
| 16 | * | |
| 17 | * @return {[type]} [description] | |
| 18 | */ | |
| 19 | 1 | module.exports = Cache(function(){ |
| 20 | 1 | 'use strict'; |
| 21 | 1 | return { |
| 22 | /** | |
| 23 | * gc类型 | |
| 24 | * @type {String} | |
| 25 | */ | |
| 26 | gcType: 'DbSession', | |
| 27 | /** | |
| 28 | * [init description] | |
| 29 | * @param {[type]} options [description] | |
| 30 | * @return {[type]} [description] | |
| 31 | */ | |
| 32 | init: function(options){ | |
| 33 | 0 | this.super_('init', options); |
| 34 | 0 | this.key = this.options.cookie; |
| 35 | 0 | this.data = {}; |
| 36 | 0 | this.initData(); |
| 37 | }, | |
| 38 | /** | |
| 39 | * 初始化数据 | |
| 40 | * @return {[type]} [description] | |
| 41 | */ | |
| 42 | initData: function(){ | |
| 43 | 0 | if (!this.promise) { |
| 44 | 0 | var model = D('Session'); |
| 45 | 0 | var self = this; |
| 46 | 0 | this.promise = model.where({key: this.key}).find().then(function(data){ |
| 47 | 0 | self.data = {}; |
| 48 | 0 | if (isEmpty(data)) { |
| 49 | 0 | return model.add({ |
| 50 | key: self.key, | |
| 51 | expire: Date.now() + self.options.timeout * 1000 | |
| 52 | }); | |
| 53 | } | |
| 54 | 0 | if (Date.now() > data.expire) { |
| 55 | 0 | return; |
| 56 | } | |
| 57 | 0 | self.data = JSON.parse(data.data || '{}'); |
| 58 | }); | |
| 59 | } | |
| 60 | 0 | return this.promise; |
| 61 | }, | |
| 62 | /** | |
| 63 | * 获取 | |
| 64 | * @param {[type]} name [description] | |
| 65 | * @return {[type]} [description] | |
| 66 | */ | |
| 67 | get: function(name){ | |
| 68 | 0 | var self = this; |
| 69 | 0 | return this.initData().then(function(){ |
| 70 | 0 | return self.data[name]; |
| 71 | }); | |
| 72 | }, | |
| 73 | /** | |
| 74 | * 设置 | |
| 75 | * @param {[type]} name [description] | |
| 76 | * @param {[type]} value [description] | |
| 77 | */ | |
| 78 | set: function(name, value){ | |
| 79 | 0 | var self = this; |
| 80 | 0 | return this.initData().then(function(){ |
| 81 | 0 | self.data[name] = value; |
| 82 | }); | |
| 83 | }, | |
| 84 | /** | |
| 85 | * 删除 | |
| 86 | * @param {[type]} name [description] | |
| 87 | * @return {[type]} [description] | |
| 88 | */ | |
| 89 | rm: function(name){ | |
| 90 | 0 | if (this.data) { |
| 91 | 0 | delete this.data[name]; |
| 92 | } | |
| 93 | 0 | return getPromise(); |
| 94 | }, | |
| 95 | /** | |
| 96 | * 将数据保存到数据库中 | |
| 97 | * @return {[type]} [description] | |
| 98 | */ | |
| 99 | flush: function(){ | |
| 100 | 0 | var model = D('Session'); |
| 101 | 0 | var self = this; |
| 102 | 0 | var data = { |
| 103 | expire: Date.now() + self.options.timeout * 1000, | |
| 104 | data: JSON.stringify(self.data) | |
| 105 | }; | |
| 106 | 0 | return this.initData().then(function(){ |
| 107 | 0 | return model.where({key: self.key}).update(data); |
| 108 | }); | |
| 109 | }, | |
| 110 | /** | |
| 111 | * [gc description] | |
| 112 | * @param {[type]} now [description] | |
| 113 | * @return {[type]} [description] | |
| 114 | */ | |
| 115 | gc: function(now){ | |
| 116 | 0 | return D('Session').where({ |
| 117 | expire: ['<', now] | |
| 118 | }).delete(); | |
| 119 | } | |
| 120 | }; | |
| 121 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 文件Session | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | ||
| 6 | 1 | var os = require('os'); |
| 7 | 1 | module.exports = Class(function(){ |
| 8 | 1 | 'use strict'; |
| 9 | 1 | return { |
| 10 | gcType: 'FileSession', | |
| 11 | /** | |
| 12 | * 差异化的init | |
| 13 | * @return {[type]} [description] | |
| 14 | */ | |
| 15 | init: function(options){ | |
| 16 | 0 | options = options || {}; |
| 17 | 0 | options.cache_path = C('session_path') || (os.tmpdir() + '/thinkjs'); |
| 18 | 0 | this.super_('init', options); |
| 19 | 0 | this.key = options.cookie; |
| 20 | }, | |
| 21 | initData: function(){ | |
| 22 | 0 | if (!this.promise) { |
| 23 | 0 | var self = this; |
| 24 | 0 | this.promise = this.getData().then(function(data){ |
| 25 | 0 | self.sessionData = data || {}; |
| 26 | }) | |
| 27 | } | |
| 28 | 0 | return this.promise; |
| 29 | }, | |
| 30 | get: function(name){ | |
| 31 | 0 | var self = this; |
| 32 | 0 | return this.initData().then(function(){ |
| 33 | 0 | return self.sessionData[name]; |
| 34 | }); | |
| 35 | }, | |
| 36 | set: function(name, value, timeout){ | |
| 37 | 0 | var self = this; |
| 38 | 0 | return this.initData().then(function(){ |
| 39 | 0 | self.sessionData[name] = value; |
| 40 | 0 | if (timeout) { |
| 41 | 0 | self.options.timeout = timeout; |
| 42 | } | |
| 43 | }); | |
| 44 | }, | |
| 45 | rm: function(){ | |
| 46 | 0 | this.sessionData = {}; |
| 47 | 0 | return getPromise(); |
| 48 | }, | |
| 49 | /** | |
| 50 | * 将数据写入到文件中 | |
| 51 | * @return {[type]} [description] | |
| 52 | */ | |
| 53 | flush: function(){ | |
| 54 | 0 | var self = this; |
| 55 | 0 | return this.initData().then(function(){ |
| 56 | 0 | return self.setData(self.sessionData); |
| 57 | }) | |
| 58 | } | |
| 59 | }; | |
| 60 | }, thinkRequire('FileCache')); |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var net = require('net'); |
| 2 | 1 | var EventEmitter = require('events').EventEmitter; |
| 3 | ||
| 4 | 1 | var CRLF = '\r\n'; //换行符 |
| 5 | 1 | var CRLF_LENGTH = CRLF.length; |
| 6 | 1 | var ERRORS = ['ERROR', 'NOT_FOUND', 'CLIENT_ERROR', 'SERVER_ERROR']; //错误 |
| 7 | 1 | var ERRORS_LENGTH = ERRORS.length; |
| 8 | ||
| 9 | //读取一行数据 | |
| 10 | 1 | function readLine(string){ |
| 11 | 0 | 'use strict'; |
| 12 | 0 | var pos = string.indexOf(CRLF); |
| 13 | 0 | if (pos > -1) { |
| 14 | 0 | return string.substr(0, pos); |
| 15 | } | |
| 16 | 0 | return string; |
| 17 | } | |
| 18 | /** | |
| 19 | * memcache类 | |
| 20 | * @return {[type]} [description] | |
| 21 | */ | |
| 22 | 1 | module.exports = Class(function(){ |
| 23 | 1 | 'use strict'; |
| 24 | 1 | return { |
| 25 | init: function(port, hostname){ | |
| 26 | 0 | EventEmitter.call(this); |
| 27 | 0 | this.port = port || 11211; |
| 28 | 0 | this.hostname = hostname || 'localhost'; |
| 29 | 0 | this.buffer = ''; |
| 30 | 0 | this.callbacks = []; //回调函数 |
| 31 | 0 | this.handle = null; //socket连接句柄 |
| 32 | }, | |
| 33 | /** | |
| 34 | * 建立连接 | |
| 35 | * @return {[type]} [description] | |
| 36 | */ | |
| 37 | connect: function(){ | |
| 38 | 0 | if (this.handle) { |
| 39 | 0 | return this; |
| 40 | } | |
| 41 | 0 | var self = this; |
| 42 | 0 | var deferred = getDefer(); |
| 43 | 0 | this.handle = net.createConnection(this.port, this.host); |
| 44 | 0 | this.handle.on('connect', function(){ |
| 45 | 0 | this.setTimeout(0); |
| 46 | 0 | this.setNoDelay(); |
| 47 | 0 | self.emit('connect'); |
| 48 | 0 | deferred.resolve(); |
| 49 | }); | |
| 50 | 0 | this.handle.on('data', function(data){ |
| 51 | 0 | self.buffer += data.toString(); |
| 52 | 0 | self.handleData(); |
| 53 | }); | |
| 54 | 0 | this.handle.on('end', function(){ |
| 55 | 0 | self.handle.end(); |
| 56 | 0 | self.handle = null; |
| 57 | }); | |
| 58 | 0 | this.handle.on('close', function(){ |
| 59 | 0 | self.handle = null; |
| 60 | 0 | self.emit('close'); |
| 61 | }); | |
| 62 | 0 | this.handle.on('timeout', function(){ |
| 63 | 0 | self.handle = null; |
| 64 | 0 | self.emit('timeout'); |
| 65 | }); | |
| 66 | 0 | this.handle.on('error', function(error){ |
| 67 | 0 | self.handle = null; |
| 68 | 0 | self.emit('error', error); |
| 69 | }); | |
| 70 | 0 | this.promise = deferred.promise; |
| 71 | 0 | return this; |
| 72 | }, | |
| 73 | /** | |
| 74 | * 处理接收的数据 | |
| 75 | * @return {[type]} [description] | |
| 76 | */ | |
| 77 | handleData: function(){ | |
| 78 | 0 | while(this.buffer.length > 0){ |
| 79 | 0 | var result = this.getHandleResult(this.buffer); |
| 80 | 0 | if(result === false){ |
| 81 | 0 | break; |
| 82 | } | |
| 83 | 0 | var value = result[0]; |
| 84 | 0 | var pos = result[1]; |
| 85 | 0 | var error = result[2]; |
| 86 | 0 | if (pos > this.buffer.length) { |
| 87 | 0 | break; |
| 88 | } | |
| 89 | 0 | this.buffer = this.buffer.substring(pos); |
| 90 | 0 | var callback = this.callbacks.shift(); |
| 91 | 0 | if (callback && callback.callback) { |
| 92 | 0 | callback.callback(error, value); |
| 93 | } | |
| 94 | } | |
| 95 | }, | |
| 96 | getHandleResult: function(buffer){ | |
| 97 | 0 | if (buffer.indexOf(CRLF) === -1) { |
| 98 | 0 | return false; |
| 99 | } | |
| 100 | 0 | for(var i = 0; i < ERRORS_LENGTH; i++){ |
| 101 | 0 | var item = ERRORS[i]; |
| 102 | 0 | if (buffer.indexOf(item) > -1) { |
| 103 | 0 | return this.handleError(buffer); |
| 104 | } | |
| 105 | } | |
| 106 | 0 | var callback = this.callbacks[0]; |
| 107 | 0 | if (callback && callback.type) { |
| 108 | 0 | return this['handle' + ucfirst(callback.type)](buffer); |
| 109 | } | |
| 110 | 0 | return false; |
| 111 | }, | |
| 112 | /** | |
| 113 | * 处理错误 | |
| 114 | * @param {[type]} buffer [description] | |
| 115 | * @return {[type]} [description] | |
| 116 | */ | |
| 117 | handleError: function(buffer){ | |
| 118 | 0 | var line = readLine(buffer); |
| 119 | 0 | return [null, line.length + CRLF_LENGTH, line]; |
| 120 | }, | |
| 121 | /** | |
| 122 | * 处理获取数据 | |
| 123 | * @param {[type]} buffer [description] | |
| 124 | * @return {[type]} [description] | |
| 125 | */ | |
| 126 | handleGet: function(buffer){ | |
| 127 | 0 | var value = null; |
| 128 | 0 | var end = 3; |
| 129 | 0 | var resultLen = 0; |
| 130 | 0 | var firstPos; |
| 131 | 0 | if (buffer.indexOf('END') === 0) { |
| 132 | 0 | return [value, end + CRLF_LENGTH]; |
| 133 | 0 | }else if (buffer.indexOf('VALUE') === 0 && buffer.indexOf('END') > -1) { |
| 134 | 0 | firstPos = buffer.indexOf(CRLF) + CRLF_LENGTH; |
| 135 | 0 | var endPos = buffer.indexOf('END'); |
| 136 | 0 | resultLen = endPos - firstPos - CRLF_LENGTH; |
| 137 | 0 | value = buffer.substr(firstPos, resultLen); |
| 138 | 0 | return [value, firstPos + parseInt(resultLen, 10) + CRLF_LENGTH + end + CRLF_LENGTH]; |
| 139 | }else{ | |
| 140 | 0 | firstPos = buffer.indexOf(CRLF) + CRLF_LENGTH; |
| 141 | 0 | resultLen = buffer.substr(0, firstPos).split(' ')[3]; |
| 142 | 0 | value = buffer.substr(firstPos, resultLen); |
| 143 | 0 | return [value, firstPos + parseInt(resultLen) + CRLF_LENGTH + end + CRLF_LENGTH]; |
| 144 | } | |
| 145 | }, | |
| 146 | /** | |
| 147 | * 处理简单数据 | |
| 148 | * @param {[type]} buffer [description] | |
| 149 | * @return {[type]} [description] | |
| 150 | */ | |
| 151 | handleSimple: function(buffer){ | |
| 152 | 0 | var line = readLine(buffer); |
| 153 | 0 | return [line, line.length + CRLF_LENGTH, null]; |
| 154 | }, | |
| 155 | /** | |
| 156 | * 版本号 | |
| 157 | * @param {[type]} buffer [description] | |
| 158 | * @return {[type]} [description] | |
| 159 | */ | |
| 160 | handleVersion: function(buffer){ | |
| 161 | 0 | var pos = buffer.indexOf(CRLF); |
| 162 | //8 is length of 'VERSION ' | |
| 163 | 0 | var value = buffer.substr(8, pos - 8); |
| 164 | 0 | return [value, pos + CRLF_LENGTH, null]; |
| 165 | }, | |
| 166 | /** | |
| 167 | * 查询 | |
| 168 | * @param {[type]} query [description] | |
| 169 | * @param {[type]} type [description] | |
| 170 | * @param {Function} callback [description] | |
| 171 | * @return {[type]} [description] | |
| 172 | */ | |
| 173 | query: function(query, type){ | |
| 174 | 0 | this.connect(); |
| 175 | 0 | var self = this; |
| 176 | 0 | var deferred = getDefer(); |
| 177 | 0 | var callback = function(error, value){ |
| 178 | 0 | return error ? deferred.reject(error) : deferred.resolve(value); |
| 179 | } | |
| 180 | 0 | this.promise.then(function(){ |
| 181 | 0 | self.callbacks.push({type: type, callback: callback}); |
| 182 | 0 | self.handle.write(query + CRLF); |
| 183 | }); | |
| 184 | 0 | return deferred.promise; |
| 185 | }, | |
| 186 | /** | |
| 187 | * 获取 | |
| 188 | * @param {[type]} key [description] | |
| 189 | * @param {Function} callback [description] | |
| 190 | * @return {[type]} [description] | |
| 191 | */ | |
| 192 | get: function(key){ | |
| 193 | 0 | return this.query('get ' + key, 'get'); |
| 194 | }, | |
| 195 | /** | |
| 196 | * 存储 | |
| 197 | * @return {[type]} [description] | |
| 198 | */ | |
| 199 | store: function(key, value, type, lifetime, flags){ | |
| 200 | 0 | lifetime = lifetime || 0; |
| 201 | 0 | flags = flags || 0; |
| 202 | 0 | var length = Buffer.byteLength(value.toString()); |
| 203 | 0 | var query = [type, key, flags, lifetime, length].join(' ') + CRLF + value; |
| 204 | 0 | return this.query(query, 'simple'); |
| 205 | }, | |
| 206 | /** | |
| 207 | * 删除 | |
| 208 | * @param {[type]} key [description] | |
| 209 | * @param {Function} callback [description] | |
| 210 | * @return {[type]} [description] | |
| 211 | */ | |
| 212 | delete: function(key){ | |
| 213 | 0 | return this.query('delete ' + key, 'simple'); |
| 214 | }, | |
| 215 | /** | |
| 216 | * 获取版本号 | |
| 217 | * @param {Function} callback [description] | |
| 218 | * @return {[type]} [description] | |
| 219 | */ | |
| 220 | version: function(){ | |
| 221 | 0 | return this.query('version', 'version'); |
| 222 | }, | |
| 223 | /** | |
| 224 | * 增长 | |
| 225 | * @param {[type]} key [description] | |
| 226 | * @param {[type]} step [description] | |
| 227 | * @param {Function} callback [description] | |
| 228 | * @return {[type]} [description] | |
| 229 | */ | |
| 230 | increment: function(key, step){ | |
| 231 | 0 | step = step || 1; |
| 232 | 0 | return this.query('incr ' + key + ' ' + step, 'simple'); |
| 233 | }, | |
| 234 | /** | |
| 235 | * 减少 | |
| 236 | * @param {[type]} key [description] | |
| 237 | * @param {[type]} step [description] | |
| 238 | * @param {Function} callback [description] | |
| 239 | * @return {[type]} [description] | |
| 240 | */ | |
| 241 | decrement: function(key, step){ | |
| 242 | 0 | step = step || 1; |
| 243 | 0 | return this.query('decr ' + key + ' ' + step, 'simple'); |
| 244 | }, | |
| 245 | /** | |
| 246 | * 关闭 | |
| 247 | * @return {[type]} [description] | |
| 248 | */ | |
| 249 | close: function(){ | |
| 250 | 0 | if (this.handle && this.handle.readyState === 'open') { |
| 251 | 0 | this.handle.end(); |
| 252 | 0 | this.handle = null; |
| 253 | } | |
| 254 | } | |
| 255 | } | |
| 256 | }, EventEmitter).extend(function(){ | |
| 257 | 1 | 'use strict'; |
| 258 | 1 | var result = {}; |
| 259 | 1 | ['set', 'add', 'replace', 'append', 'prepend'].forEach(function(item){ |
| 260 | 5 | result[item] = function(key, value, callback, lifetime, flags){ |
| 261 | 0 | return this.store(key, value, item, callback, lifetime, flags); |
| 262 | } | |
| 263 | }); | |
| 264 | 1 | return result; |
| 265 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * mysql socket | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | ||
| 6 | //暂时使用mysql库 | |
| 7 | 1 | var mysql = require('mysql'); |
| 8 | 1 | module.exports = Class(function(){ |
| 9 | 1 | 'use strict'; |
| 10 | 1 | return { |
| 11 | init: function(config){ | |
| 12 | 0 | this.handle = null; |
| 13 | 0 | this.config = config; |
| 14 | 0 | this.deferred = null; |
| 15 | 0 | this.tryTimes = 0; |
| 16 | }, | |
| 17 | /** | |
| 18 | * 建立数据库连接 | |
| 19 | * @return {[type]} [description] | |
| 20 | */ | |
| 21 | connect: function(){ | |
| 22 | 0 | if (this.handle) { |
| 23 | 0 | return this.deferred.promise; |
| 24 | } | |
| 25 | 0 | var self = this; |
| 26 | 0 | var deferred = getDefer(); |
| 27 | //创建连接 | |
| 28 | 0 | var connection = mysql.createConnection({ |
| 29 | host : this.config.hostname || 'localhost', | |
| 30 | user : this.config.username || 'root', | |
| 31 | password : this.config.password || '', | |
| 32 | database : this.config.database || '' | |
| 33 | }); | |
| 34 | //连接 | |
| 35 | 0 | connection.connect(function(err){ |
| 36 | //连接失败 | |
| 37 | 0 | if (err) { |
| 38 | 0 | deferred.reject(err); |
| 39 | 0 | self.close(); |
| 40 | }else{ | |
| 41 | 0 | deferred.resolve(); |
| 42 | } | |
| 43 | }); | |
| 44 | //错误时关闭当前连接 | |
| 45 | 0 | connection.on('error', function(){ |
| 46 | 0 | self.close(); |
| 47 | }); | |
| 48 | //PROTOCOL_CONNECTION_LOST | |
| 49 | 0 | connection.on('end', function(){ |
| 50 | 0 | self.close(); |
| 51 | }) | |
| 52 | //连接句柄 | |
| 53 | 0 | this.handle = connection; |
| 54 | //把上一次的promise reject | |
| 55 | 0 | if (this.deferred) { |
| 56 | 0 | this.deferred.reject(new Error('connection closed')); |
| 57 | } | |
| 58 | 0 | this.deferred = deferred; |
| 59 | 0 | return this.deferred.promise; |
| 60 | }, | |
| 61 | /** | |
| 62 | * 查询sql语句,返回一个promise | |
| 63 | * @param {[type]} sql [description] | |
| 64 | * @return {[type]} [description] | |
| 65 | */ | |
| 66 | query: function(sql){ | |
| 67 | 0 | if (APP_DEBUG) { |
| 68 | 0 | console.log('sql: ' + sql); |
| 69 | } | |
| 70 | 0 | var self = this; |
| 71 | 0 | return this.connect().then(function(){ |
| 72 | 0 | var deferred = getDefer(); |
| 73 | 0 | self.handle.query(sql, function(err, rows){ |
| 74 | 0 | if (err) { |
| 75 | //当数据量非常大时,可能会出现连接丢失,这里进行重连 | |
| 76 | 0 | if (err.code === 'PROTOCOL_CONNECTION_LOST' && self.tryTimes < 3) { |
| 77 | 0 | self.tryTimes++; |
| 78 | 0 | self.close(); |
| 79 | 0 | return self.query(sql); |
| 80 | } | |
| 81 | 0 | return deferred.reject(err); |
| 82 | } | |
| 83 | 0 | self.tryTimes = 0; |
| 84 | 0 | return deferred.resolve(rows || []); |
| 85 | }); | |
| 86 | 0 | return deferred.promise; |
| 87 | }); | |
| 88 | }, | |
| 89 | /** | |
| 90 | * 关闭连接 | |
| 91 | * @return {[type]} [description] | |
| 92 | */ | |
| 93 | close: function(){ | |
| 94 | 0 | if (this.handle) { |
| 95 | 0 | this.handle.destroy(); |
| 96 | 0 | this.handle = null; |
| 97 | } | |
| 98 | } | |
| 99 | }; | |
| 100 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * ejs | |
| 3 | * https://github.com/visionmedia/ejs | |
| 4 | * @type {[type]} | |
| 5 | */ | |
| 6 | 1 | var ejs = require('ejs'); |
| 7 | 1 | module.exports = { |
| 8 | fetch: function(templateFile, tVar){ | |
| 9 | 1 | 'use strict'; |
| 10 | 1 | var content = getFileContent(templateFile); |
| 11 | 1 | var conf = extend({ |
| 12 | filename: templateFile, | |
| 13 | cache: true | |
| 14 | }, C('tpl_engine_config')); | |
| 15 | 1 | return ejs.compile(content, conf)(tVar); |
| 16 | } | |
| 17 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * REST Controller | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | module.exports = Controller(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | 1 | return { |
| 8 | __before: function(){ | |
| 9 | ||
| 10 | }, | |
| 11 | __call: function(){ | |
| 12 | 0 | this.end('method is not allowed'); |
| 13 | } | |
| 14 | } | |
| 15 | }) |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 高级模型 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | module.exports = Model(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | //关联类型 | |
| 8 | 1 | global.HAS_ONE = 1; |
| 9 | 1 | global.BELONGS_TO = 2; |
| 10 | 1 | global.HAS_MANY = 3; |
| 11 | 1 | global.MANY_TO_MANY = 4; |
| 12 | ||
| 13 | //post的操作类型 | |
| 14 | 1 | var ADD = 'ADD'; |
| 15 | 1 | var UPDATE = 'UPDATE'; |
| 16 | 1 | var DELETE = 'DELETE'; |
| 17 | ||
| 18 | //get时不同的type对应的回调 | |
| 19 | 1 | var mapTypeGetFn = { |
| 20 | 1: '_getHasOneRelation', | |
| 21 | 2: '_getBelongsToRelation', | |
| 22 | 3: '_getHasManyRelation', | |
| 23 | 4: '_getManyToManyRelation' | |
| 24 | }; | |
| 25 | ||
| 26 | //post时不同的type对应的回调 | |
| 27 | 1 | var mapTypePostFn = { |
| 28 | 1: '_postHasOneRelation', | |
| 29 | 2: '_postBelongsToRelation', | |
| 30 | 3: '_postHasManyRelation', | |
| 31 | 4: '_postManyToManyRelation' | |
| 32 | }; | |
| 33 | ||
| 34 | 1 | return { |
| 35 | /** | |
| 36 | * 关联定义 | |
| 37 | * 数据格式: | |
| 38 | * 'Profile': { | |
| 39 | type: 1, | |
| 40 | model: 'Profile', | |
| 41 | name: 'Profile', | |
| 42 | key: 'id', | |
| 43 | fKey: 'user_id', | |
| 44 | field: 'id,name', | |
| 45 | where: 'name=xx', | |
| 46 | order: '', | |
| 47 | limit: '' | |
| 48 | * } | |
| 49 | * @type {Object} | |
| 50 | */ | |
| 51 | relation: {}, | |
| 52 | /** | |
| 53 | * 本次使用的关联名称,默认是全部使用 | |
| 54 | * @type {Boolean} | |
| 55 | */ | |
| 56 | _relationName: true, | |
| 57 | /** | |
| 58 | * 只读字段 | |
| 59 | * @type {String} | |
| 60 | */ | |
| 61 | readonlyField: '', | |
| 62 | /** | |
| 63 | * 保存时对数据进行校验 | |
| 64 | * @type {Boolean} | |
| 65 | */ | |
| 66 | _validateField: true, | |
| 67 | /** | |
| 68 | * 字段类型 | |
| 69 | * @type {Object} | |
| 70 | */ | |
| 71 | fieldType: {}, | |
| 72 | /** | |
| 73 | * 设置本次使用的relation | |
| 74 | * @param {[type]} name [description] | |
| 75 | */ | |
| 76 | setRelation: function(name, value){ | |
| 77 | 0 | if (isObject(name) || !isEmpty(value)) { |
| 78 | 0 | var obj = isObject(name) ? name : getObject(name, value); |
| 79 | 0 | extend(this.relation, obj); |
| 80 | 0 | return this; |
| 81 | } | |
| 82 | 0 | if (isString(name)) { |
| 83 | 0 | name = name.split(','); |
| 84 | } | |
| 85 | 0 | this._relationName = name; |
| 86 | 0 | return this; |
| 87 | }, | |
| 88 | /** | |
| 89 | * find后置操作 | |
| 90 | * @param {[type]} data [description] | |
| 91 | * @return {[type]} [description] | |
| 92 | */ | |
| 93 | _afterFind: function(data, parsedOptions){ | |
| 94 | 0 | return this.getRelation(data, parsedOptions); |
| 95 | }, | |
| 96 | /** | |
| 97 | * select后置操作 | |
| 98 | * @param {[type]} data [description] | |
| 99 | * @return {[type]} [description] | |
| 100 | */ | |
| 101 | _afterSelect: function(data, parsedOptions){ | |
| 102 | 0 | return this.getRelation(data, parsedOptions); |
| 103 | }, | |
| 104 | /** | |
| 105 | * 获取关联的数据 | |
| 106 | * @param {[type]} data [description] | |
| 107 | * @param Boolean isDataList 是否是数据列表 | |
| 108 | * @return {[type]} | |
| 109 | */ | |
| 110 | getRelation: function(data, parsedOptions){ | |
| 111 | 0 | if (isEmpty(data) || isEmpty(this.relation) || isEmpty(this._relationName)) { |
| 112 | 0 | return data; |
| 113 | } | |
| 114 | 0 | var self = this; |
| 115 | 0 | var promises = Object.keys(this.relation).map(function(key){ |
| 116 | 0 | var mapName, mapType, model, mapKey, mapfKey; |
| 117 | 0 | var value = self.relation[key]; |
| 118 | 0 | if (!isObject(value)) { |
| 119 | 0 | value = {type: value}; |
| 120 | } | |
| 121 | 0 | mapName = value.name || key; |
| 122 | //如果不在开启的relation内,则直接返回 | |
| 123 | 0 | if (self._relationName !== true && self._relationName.indexOf(mapName) === -1) { |
| 124 | 0 | return; |
| 125 | } | |
| 126 | 0 | mapType = value.type || HAS_ONE; |
| 127 | 0 | mapKey = value.key || self.getPk(); |
| 128 | 0 | mapfKey = value.fKey || (self.name.toLowerCase() + '_id'); |
| 129 | 0 | model = D(value.model || key); |
| 130 | 0 | model.where(value.where).cache(parsedOptions.cache).field(value.field).order(value.order).limit(value.limit); |
| 131 | //调用不同的类型解析 | |
| 132 | 0 | return self[mapTypeGetFn[mapType]](data, value, { |
| 133 | model: model, | |
| 134 | mapName: mapName, | |
| 135 | mapKey: mapKey, | |
| 136 | mapfKey: mapfKey | |
| 137 | }, parsedOptions); | |
| 138 | }); | |
| 139 | 0 | return Promise.all(promises).then(function(){ |
| 140 | 0 | return data; |
| 141 | }); | |
| 142 | }, | |
| 143 | _getHasOneRelation: function(data, value, mapOptions){ | |
| 144 | 0 | var self = this; |
| 145 | 0 | var where = self.parseRelationWhere(data, mapOptions.mapKey, mapOptions.mapfKey); |
| 146 | 0 | if (where === false) { |
| 147 | 0 | return {}; |
| 148 | } | |
| 149 | 0 | mapOptions.model.where(where); |
| 150 | 0 | return mapOptions.model.select().then(function(mapData){ |
| 151 | 0 | return self.parseRelationData(data, mapData, mapOptions.mapName, mapOptions.mapKey, mapOptions.mapfKey); |
| 152 | }); | |
| 153 | }, | |
| 154 | _getBelongsToRelation: function(data, value, mapOptions){ | |
| 155 | 0 | var self = this; |
| 156 | 0 | var mapKey, mapfKey; |
| 157 | 0 | return mapOptions.model.getTableFields().then(function(){ |
| 158 | 0 | mapKey = mapOptions.model.getModelName().toLowerCase() + '_id'; |
| 159 | 0 | mapfKey = mapOptions.model.getPk(); |
| 160 | 0 | var where = self.parseRelationWhere(data, mapKey, mapfKey); |
| 161 | 0 | if (where === false) { |
| 162 | 0 | return {}; |
| 163 | } | |
| 164 | 0 | mapOptions.model.where(where); |
| 165 | 0 | return mapOptions.model.select().then(function(mapData){ |
| 166 | 0 | return self.parseRelationData(data, mapData, mapOptions.mapName, mapKey, mapfKey); |
| 167 | }) | |
| 168 | }) | |
| 169 | }, | |
| 170 | _getHasManyRelation: function(data, value, mapOptions){ | |
| 171 | 0 | var self = this; |
| 172 | 0 | var where = self.parseRelationWhere(data, mapOptions.mapKey, mapOptions.mapfKey); |
| 173 | 0 | if (where === false) { |
| 174 | 0 | return []; |
| 175 | } | |
| 176 | 0 | mapOptions.model.where(where); |
| 177 | 0 | return mapOptions.model.select().then(function(mapData){ |
| 178 | 0 | return self.parseRelationData(data, mapData, mapOptions.mapName, mapOptions.mapKey, mapOptions.mapfKey, true); |
| 179 | }); | |
| 180 | }, | |
| 181 | _getManyToManyRelation: function(data, value, mapOptions, parsedOptions){ | |
| 182 | 0 | var self = this; |
| 183 | 0 | return mapOptions.model.getTableFields().then(function(){ |
| 184 | 0 | var where = self.parseRelationWhere(data, mapOptions.mapKey, mapOptions.mapfKey); |
| 185 | 0 | if (where === false) { |
| 186 | 0 | return []; |
| 187 | } | |
| 188 | 0 | var whereStr = self.db.parseWhere(where); |
| 189 | //关联的实体表和关系表联合查询 | |
| 190 | 0 | var sql = 'SELECT b.%s, a.%s FROM %s as a, %s as b %s AND a.%s=b.%s %s'; |
| 191 | 0 | var queryData = [ |
| 192 | value.field || '*', | |
| 193 | mapOptions.mapfKey, | |
| 194 | value.rTable || self.getRelationTableName(mapOptions.model), | |
| 195 | mapOptions.model.getTableName(), | |
| 196 | whereStr || 'WHERE ', | |
| 197 | value.rfKey || (mapOptions.model.getModelName().toLowerCase() + '_id'), | |
| 198 | mapOptions.model.getPk(), | |
| 199 | value.where ? (' AND ' + value.where) : '' | |
| 200 | ]; | |
| 201 | 0 | return self.parseSql(sql, queryData).then(function(sql){ |
| 202 | 0 | return self.db.select(sql, parsedOptions.cache); |
| 203 | }).then(function(mapData){ | |
| 204 | 0 | return self.parseRelationData(data, mapData, mapOptions.mapName, mapOptions.mapKey, mapOptions.mapfKey, true); |
| 205 | }); | |
| 206 | }); | |
| 207 | }, | |
| 208 | /** | |
| 209 | * 多对多关系下,获取对应的关联表 | |
| 210 | * @return {[type]} [description] | |
| 211 | */ | |
| 212 | getRelationTableName: function(model){ | |
| 213 | 0 | var table = [ |
| 214 | this.tablePrefix, | |
| 215 | this.tableName || this.name, | |
| 216 | '_', | |
| 217 | model.getModelName() | |
| 218 | ].join(''); | |
| 219 | 0 | return table.toLowerCase(); |
| 220 | }, | |
| 221 | /** | |
| 222 | * 多堆垛关系下,回去对应关联表的模型 | |
| 223 | * @param {[type]} model [description] | |
| 224 | * @return {[type]} [description] | |
| 225 | */ | |
| 226 | getRelationModel: function(model){ | |
| 227 | 0 | var name = ucfirst(this.tableName || this.name) + ucfirst(model.getModelName()); |
| 228 | 0 | return D(name); |
| 229 | }, | |
| 230 | /** | |
| 231 | * 解析relation的where条件 | |
| 232 | * @param {[type]} data [description] | |
| 233 | * @param {[type]} mapKey [description] | |
| 234 | * @param {[type]} mapfKey [description] | |
| 235 | * @return {[type]} [description] | |
| 236 | */ | |
| 237 | parseRelationWhere: function(data, mapKey, mapfKey){ | |
| 238 | 0 | if (isArray(data)) { |
| 239 | 0 | var keys = {}; |
| 240 | 0 | data.forEach(function(item){ |
| 241 | 0 | keys[item[mapKey]] = 1; |
| 242 | }) | |
| 243 | 0 | var value = Object.keys(keys); |
| 244 | 0 | if (value.length === 0) { |
| 245 | 0 | return false; |
| 246 | } | |
| 247 | 0 | return getObject(mapfKey, ['IN', value]); |
| 248 | } | |
| 249 | 0 | return getObject(mapfKey, data[mapKey]); |
| 250 | }, | |
| 251 | /** | |
| 252 | * 解析查询后的数据 | |
| 253 | * @param {[type]} data [description] | |
| 254 | * @param {[type]} mapData [description] | |
| 255 | * @param {[type]} mapName [description] | |
| 256 | * @param {[type]} mapKey [description] | |
| 257 | * @param {[type]} mapfKey [description] | |
| 258 | * @param {Boolean} isArrMap [description] | |
| 259 | * @return {[type]} [description] | |
| 260 | */ | |
| 261 | parseRelationData: function(data, mapData, mapName, mapKey, mapfKey, isArrMap){ | |
| 262 | 0 | if (isArray(data)) { |
| 263 | //提前初始化,防止mapData为空导致data里的数据没有初始化的情况 | |
| 264 | 0 | data.forEach(function(item, i){ |
| 265 | 0 | data[i][mapName] = isArrMap ? [] : {}; |
| 266 | }); | |
| 267 | 0 | mapData.forEach(function(mapItem){ |
| 268 | 0 | data.forEach(function(item, i){ |
| 269 | 0 | if (mapItem[mapfKey] !== item[mapKey]) { |
| 270 | 0 | return; |
| 271 | } | |
| 272 | 0 | if (isArrMap) { |
| 273 | 0 | data[i][mapName].push(mapItem); |
| 274 | }else{ | |
| 275 | 0 | data[i][mapName] = mapItem; |
| 276 | } | |
| 277 | }); | |
| 278 | }); | |
| 279 | }else{ | |
| 280 | 0 | data[mapName] = isArrMap ? (mapData || []) : (mapData[0] || {}); |
| 281 | } | |
| 282 | 0 | return data; |
| 283 | }, | |
| 284 | /** | |
| 285 | * 添加后置操作 | |
| 286 | * @param {[type]} data [description] | |
| 287 | * @param {[type]} parsedOptions [description] | |
| 288 | * @return {[type]} [description] | |
| 289 | */ | |
| 290 | _afterAdd: function(data, parsedOptions){ | |
| 291 | 0 | return this.postRelation(ADD, data, parsedOptions); |
| 292 | }, | |
| 293 | /** | |
| 294 | * 删除后置操作 | |
| 295 | * @param {[type]} data [description] | |
| 296 | * @param {[type]} parsedOptions [description] | |
| 297 | * @return {[type]} [description] | |
| 298 | */ | |
| 299 | _afterDelete: function(data, parsedOptions){ | |
| 300 | 0 | return this.postRelation(DELETE, data, parsedOptions); |
| 301 | }, | |
| 302 | /** | |
| 303 | * 更新前置操作 | |
| 304 | * @param {[type]} data [description] | |
| 305 | * @param {[type]} parsedOptions [description] | |
| 306 | * @return {[type]} [description] | |
| 307 | */ | |
| 308 | _beforeUpdate: function(data){ | |
| 309 | //只读字段处理 | |
| 310 | 0 | if (!isEmpty(this.readonlyField)) { |
| 311 | 0 | if (isString(this.readonlyField)) { |
| 312 | 0 | this.readonlyField = this.readonlyField.split(','); |
| 313 | } | |
| 314 | 0 | this.readonlyField.forEach(function(field){ |
| 315 | 0 | delete data[field]; |
| 316 | }); | |
| 317 | } | |
| 318 | 0 | return data; |
| 319 | }, | |
| 320 | /** | |
| 321 | * 更新后置操作 | |
| 322 | * @param {[type]} data [description] | |
| 323 | * @param {[type]} parsedOptions [description] | |
| 324 | * @return {[type]} [description] | |
| 325 | */ | |
| 326 | _afterUpdate: function(data, parsedOptions){ | |
| 327 | 0 | return this.postRelation(UPDATE, data, parsedOptions); |
| 328 | }, | |
| 329 | /** | |
| 330 | * 提交类关联操作 | |
| 331 | * @param {[type]} postType [description] | |
| 332 | * @param {[type]} data [description] | |
| 333 | * @param {[type]} parsedOptions [description] | |
| 334 | * @return {[type]} [description] | |
| 335 | */ | |
| 336 | postRelation: function(postType, data, parsedOptions){ | |
| 337 | 0 | if (isEmpty(data) || isEmpty(this.relation) || isEmpty(this._relationName)) { |
| 338 | 0 | return data; |
| 339 | } | |
| 340 | 0 | var self = this; |
| 341 | 0 | var promises = []; |
| 342 | 0 | Object.keys(this.relation).forEach(function(key){ |
| 343 | 0 | var promise, mapName, mapType, model, mapKey, mapfKey, mapData; |
| 344 | 0 | var value = self.relation[key]; |
| 345 | 0 | if (!isObject(value)) { |
| 346 | 0 | value = {type: value}; |
| 347 | } | |
| 348 | 0 | mapName = value.name || key; |
| 349 | //如果没有开启对应的relation,则直接返回 | |
| 350 | 0 | if (self._relationName !== true && self._relationName.indexOf(mapName) === -1) { |
| 351 | 0 | return; |
| 352 | } | |
| 353 | 0 | mapData = data[mapName]; |
| 354 | //如果没有对应的数据,则直接返回 | |
| 355 | 0 | if (isEmpty(mapData) && postType !== DELETE) { |
| 356 | 0 | return; |
| 357 | } | |
| 358 | 0 | mapKey = value.key || self.getPk(); |
| 359 | 0 | if (isEmpty(data[mapKey])) { |
| 360 | 0 | return; |
| 361 | } | |
| 362 | 0 | mapType = value.type || HAS_ONE; |
| 363 | 0 | mapfKey = value.fKey || (self.name.toLowerCase() + '_id'); |
| 364 | 0 | model = D(value.model || key); |
| 365 | 0 | model.where(value.where); |
| 366 | //调用不同的类型解析 | |
| 367 | 0 | promise = self[mapTypePostFn[mapType]](data, value, { |
| 368 | model: model, | |
| 369 | mapName: mapName, | |
| 370 | mapKey: mapKey, | |
| 371 | mapfKey: mapfKey, | |
| 372 | mapData: mapData, | |
| 373 | type: postType | |
| 374 | }, parsedOptions); | |
| 375 | ||
| 376 | 0 | promises.push(promise); |
| 377 | }); | |
| 378 | 0 | return Promise.all(promises).then(function(){ |
| 379 | 0 | return data; |
| 380 | }); | |
| 381 | }, | |
| 382 | /** | |
| 383 | * 一对一提交 | |
| 384 | * @param {[type]} data [description] | |
| 385 | * @param {[type]} value [description] | |
| 386 | * @param {[type]} mapOptions [description] | |
| 387 | * @param {[type]} parsedOptions [description] | |
| 388 | * @return {[type]} [description] | |
| 389 | */ | |
| 390 | _postHasOneRelation: function(data, value, mapOptions){ | |
| 391 | 0 | var promise = null; |
| 392 | 0 | var where; |
| 393 | 0 | switch(mapOptions.type){ |
| 394 | case ADD: | |
| 395 | 0 | mapOptions.mapData[mapOptions.mapfKey] = data[mapOptions.mapKey]; |
| 396 | 0 | promise = mapOptions.model.add(mapOptions.mapData); |
| 397 | 0 | break; |
| 398 | case DELETE: | |
| 399 | 0 | where = getObject(mapOptions.mapfKey, data[mapOptions.mapKey]); |
| 400 | 0 | promise = mapOptions.model.where(where).delete(); |
| 401 | 0 | break; |
| 402 | case UPDATE: | |
| 403 | 0 | where = getObject(mapOptions.mapfKey, data[mapOptions.mapKey]); |
| 404 | 0 | promise = mapOptions.model.where(where).update(mapOptions.mapData); |
| 405 | 0 | break; |
| 406 | default: | |
| 407 | 0 | break; |
| 408 | } | |
| 409 | 0 | return promise; |
| 410 | }, | |
| 411 | _postBelongsToRelation: function(data){ | |
| 412 | 0 | return data; |
| 413 | }, | |
| 414 | /** | |
| 415 | * 一对多提交 | |
| 416 | * @param {[type]} data [description] | |
| 417 | * @param {[type]} value [description] | |
| 418 | * @param {[type]} mapOptions [description] | |
| 419 | * @param {[type]} parsedOptions [description] | |
| 420 | * @return {[type]} [description] | |
| 421 | */ | |
| 422 | _postHasManyRelation: function(data, value, mapOptions){ | |
| 423 | 0 | var type = mapOptions.type; |
| 424 | 0 | var mapData = mapOptions.mapData; |
| 425 | 0 | var model = mapOptions.model; |
| 426 | 0 | var promise; |
| 427 | 0 | if (!isArray(mapData)) { |
| 428 | 0 | mapData = [mapData]; |
| 429 | } | |
| 430 | 0 | switch(type){ |
| 431 | case ADD: | |
| 432 | 0 | mapData = mapData.map(function(item){ |
| 433 | 0 | item[mapOptions.mapfKey] = data[mapOptions.mapKey]; |
| 434 | }); | |
| 435 | 0 | promise = model.addAll(mapData); |
| 436 | 0 | break; |
| 437 | case UPDATE: | |
| 438 | 0 | promise = model.getTableFields().then(function(){ |
| 439 | 0 | var promises = []; |
| 440 | 0 | var pk = model.getPk(); |
| 441 | 0 | mapData.forEach(function(item){ |
| 442 | 0 | var pro; |
| 443 | 0 | if (item[pk]) { |
| 444 | 0 | pro = model.update(item); |
| 445 | }else{ | |
| 446 | 0 | item[mapOptions.mapfKey] = data[mapOptions.mapKey]; |
| 447 | 0 | pro = model.add(item); |
| 448 | } | |
| 449 | 0 | promises.push(pro); |
| 450 | }); | |
| 451 | 0 | return Promise.all(promises); |
| 452 | }); | |
| 453 | 0 | break; |
| 454 | case DELETE: | |
| 455 | 0 | var where = getObject(mapOptions.mapfKey, data[mapOptions.mapKey]); |
| 456 | 0 | promise = model.where(where).delete(); |
| 457 | 0 | break; |
| 458 | } | |
| 459 | 0 | return promise; |
| 460 | }, | |
| 461 | /** | |
| 462 | * 多对多提交 | |
| 463 | * @param Object data [description] | |
| 464 | * @param object value [description] | |
| 465 | * @param {[type]} mapOptions [description] | |
| 466 | * @param {[type]} parsedOptions [description] | |
| 467 | * @return {[type]} [description] | |
| 468 | */ | |
| 469 | _postManyToManyRelation: function(data, value, mapOptions){ | |
| 470 | 0 | var self = this; |
| 471 | 0 | var model = mapOptions.model; |
| 472 | 0 | var promise = model.getTableFields(); |
| 473 | 0 | var rfKey = value.rfKey || (model.getModelName().toLowerCase() + '_id'); |
| 474 | //var relationTable = value.rTable || self.getRelationTableName(model); | |
| 475 | 0 | var where; |
| 476 | 0 | var type = mapOptions.type; |
| 477 | 0 | var mapData = mapOptions.mapData; |
| 478 | 0 | var relationModel = self.getRelationModel(model); |
| 479 | 0 | if (type === DELETE || type === UPDATE) { |
| 480 | 0 | where = getObject(mapOptions.mapfKey, data[mapOptions.mapKey]); |
| 481 | 0 | promise = promise.then(function(){ |
| 482 | 0 | return relationModel.where(where).delete(); |
| 483 | }); | |
| 484 | } | |
| 485 | 0 | if (type === ADD || type === UPDATE) { |
| 486 | 0 | promise = promise.then(function(){ |
| 487 | 0 | if (!isArray(mapData)) { |
| 488 | 0 | mapData = isString(mapData) ? mapData.split(',') : [mapData]; |
| 489 | } | |
| 490 | 0 | var firstItem = mapData[0]; |
| 491 | //关系数据 | |
| 492 | 0 | if (isNumberString(firstItem) || (isObject(firstItem) && (rfKey in firstItem))) { |
| 493 | //生成要更新的数据 | |
| 494 | 0 | var postData = mapData.map(function(item){ |
| 495 | 0 | var key = [mapOptions.mapfKey, rfKey]; |
| 496 | 0 | var val = [data[mapOptions.mapKey], item[rfKey] || item]; |
| 497 | 0 | return getObject(key, val); |
| 498 | }); | |
| 499 | 0 | return relationModel.addAll(postData); |
| 500 | }else{ //实体数据 | |
| 501 | 0 | var unqiueField = model.getUniqueField(); |
| 502 | 0 | if (!unqiueField) { |
| 503 | 0 | return getPromise(model.getTableName() + ' table has no unqiue field', true); |
| 504 | } | |
| 505 | 0 | return self._getRalationAddIds(mapData, model, unqiueField).then(function(ids){ |
| 506 | 0 | var postData = ids.map(function(id){ |
| 507 | 0 | var key = [mapOptions.mapfKey, rfKey]; |
| 508 | 0 | var val = [data[mapOptions.mapKey], id]; |
| 509 | 0 | return getObject(key, val); |
| 510 | }); | |
| 511 | 0 | return relationModel.addAll(postData); |
| 512 | }); | |
| 513 | } | |
| 514 | }); | |
| 515 | } | |
| 516 | 0 | return promise; |
| 517 | }, | |
| 518 | /** | |
| 519 | * 插入数据,并获取插入的id集合 | |
| 520 | * @param {[type]} dataList [description] | |
| 521 | * @param {[type]} model [description] | |
| 522 | * @param {[type]} unqiueField [description] | |
| 523 | * @return {[type]} [description] | |
| 524 | */ | |
| 525 | _getRalationAddIds: function(dataList, model, unqiueField){ | |
| 526 | 0 | var promises = []; |
| 527 | 0 | var ids = []; |
| 528 | 0 | dataList.forEach(function(item){ |
| 529 | 0 | if (!isObject(item)) { |
| 530 | 0 | item = getObject(unqiueField, item); |
| 531 | } | |
| 532 | 0 | var value = item[unqiueField]; |
| 533 | 0 | if (!value) { |
| 534 | 0 | return true; |
| 535 | } | |
| 536 | 0 | var where = getObject(unqiueField, value); |
| 537 | 0 | var promise = model.where(where).field(model.getPk()).find().then(function(data){ |
| 538 | 0 | if (isEmpty(data)) { |
| 539 | 0 | return model.add(item).then(function(insertId){ |
| 540 | 0 | ids.push(insertId); |
| 541 | }); | |
| 542 | }else{ | |
| 543 | 0 | ids.push(data[model.getPk()]); |
| 544 | } | |
| 545 | }); | |
| 546 | 0 | promises.push(promise); |
| 547 | }); | |
| 548 | 0 | return Promise.all(promises).then(function(){ |
| 549 | 0 | return ids; |
| 550 | }); | |
| 551 | }, | |
| 552 | /** | |
| 553 | * 设置是否对数据进行校验 | |
| 554 | * @param {[type]} validate [description] | |
| 555 | * @return {[type]} [description] | |
| 556 | */ | |
| 557 | validate: function(validate){ | |
| 558 | 0 | this._validateField = validate; |
| 559 | } | |
| 560 | }; | |
| 561 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 行为类 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | module.exports = Class(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | 1 | return { |
| 8 | options: {}, //行为选项 | |
| 9 | http: null, | |
| 10 | init: function(http){ | |
| 11 | 6 | this.http = http; |
| 12 | 6 | for(var name in this.options){ |
| 13 | 10 | if (C(name) !== undefined) { |
| 14 | 9 | this.options[name] = C(name); |
| 15 | } | |
| 16 | } | |
| 17 | }, | |
| 18 | run: function(){ | |
| 19 | ||
| 20 | } | |
| 21 | }; | |
| 22 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 缓存基类 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | /** | |
| 6 | * 缓存数据 | |
| 7 | * @type {Object} | |
| 8 | */ | |
| 9 | 1 | var cacheData = {}; |
| 10 | /** | |
| 11 | * 定时器 | |
| 12 | * @type {Number} | |
| 13 | */ | |
| 14 | 1 | var gcTimer = {}; |
| 15 | /** | |
| 16 | * 清除已经过期的Cache | |
| 17 | * @return {[type]} [description] | |
| 18 | */ | |
| 19 | 1 | var gc = function(instance){ |
| 20 | 0 | 'use strict'; |
| 21 | 0 | if (APP_DEBUG || APP_MODE === 'cli' || gcTimer[instance.gcType]) { |
| 22 | 0 | return; |
| 23 | } | |
| 24 | 0 | gcTimer[instance.gcType] = setInterval(function(){ |
| 25 | 0 | var hour = (new Date()).getHours(); |
| 26 | 0 | if (C('cache_gc_hour').indexOf(hour) === -1) { |
| 27 | 0 | return; |
| 28 | } | |
| 29 | 0 | var now = Date.now(); |
| 30 | 0 | if (instance.gc) { |
| 31 | 0 | console.log('gc clean: ', instance.gcType, (new Date()).toUTCString()); |
| 32 | 0 | instance.gc(now); |
| 33 | } | |
| 34 | }, 3600 * 1000); | |
| 35 | }; | |
| 36 | ||
| 37 | 1 | module.exports = Class(function(){ |
| 38 | 1 | 'use strict'; |
| 39 | 1 | return { |
| 40 | /** | |
| 41 | * gc的类型,用于定时器类型判断 | |
| 42 | * @type {String} | |
| 43 | */ | |
| 44 | gcType: 'Cache', | |
| 45 | /** | |
| 46 | * 初始化 | |
| 47 | * @param {[type]} options [description] | |
| 48 | * @return {[type]} [description] | |
| 49 | */ | |
| 50 | init: function(options){ | |
| 51 | 0 | options = options || {}; |
| 52 | 0 | if (options.cacheData) { |
| 53 | 0 | this.cacheData = options.cacheData; |
| 54 | }else{ | |
| 55 | 0 | this.cacheData = cacheData; |
| 56 | } | |
| 57 | 0 | if (options.gcType) { |
| 58 | 0 | this.gcType = options.gcType; |
| 59 | } | |
| 60 | 0 | if (!options.timeout) { |
| 61 | 0 | options.timeout = C('cache_timeout') |
| 62 | } | |
| 63 | 0 | this.options = options; |
| 64 | //操作的key | |
| 65 | 0 | this.key = ''; |
| 66 | //是否更新expire值 | |
| 67 | 0 | this.updateExpire = false; |
| 68 | 0 | gc(this); |
| 69 | }, | |
| 70 | /** | |
| 71 | * 获取缓存值,返回一个promise | |
| 72 | * @param {[type]} name [description] | |
| 73 | * @return {[type]} [description] | |
| 74 | */ | |
| 75 | get: function(name){ | |
| 76 | 0 | var key = this.key || name; |
| 77 | 0 | if (!(key in this.cacheData)) { |
| 78 | 0 | return getPromise(); |
| 79 | } | |
| 80 | 0 | var value = this.cacheData[key]; |
| 81 | 0 | if (Date.now() > value.expire) { |
| 82 | 0 | delete this.cacheData[key]; |
| 83 | 0 | return getPromise(); |
| 84 | } | |
| 85 | 0 | if (this.updateExpire) { |
| 86 | 0 | this.cacheData[key].expire = Date.now() + value.timeout * 1000; |
| 87 | } | |
| 88 | 0 | return getPromise(value.data[name]); |
| 89 | }, | |
| 90 | /** | |
| 91 | * 设置缓存值 | |
| 92 | * @param {[type]} name [description] | |
| 93 | * @param {[type]} value [description] | |
| 94 | */ | |
| 95 | set: function(name, value, timeout){ | |
| 96 | 0 | if (timeout === undefined) { |
| 97 | 0 | timeout = this.options.timeout; |
| 98 | } | |
| 99 | 0 | var key = this.key || name; |
| 100 | 0 | if (key in this.cacheData) { |
| 101 | 0 | this.cacheData[key].data[name] = value; |
| 102 | }else{ | |
| 103 | 0 | this.cacheData[key] = { |
| 104 | data: getObject(name, value), | |
| 105 | timeout: timeout, | |
| 106 | expire: Date.now() + timeout * 1000 | |
| 107 | }; | |
| 108 | } | |
| 109 | 0 | return getPromise(); |
| 110 | }, | |
| 111 | /** | |
| 112 | * 移除缓存值 | |
| 113 | * @param {[type]} name [description] | |
| 114 | * @return {[type]} [description] | |
| 115 | */ | |
| 116 | rm: function(name){ | |
| 117 | 0 | var key = this.key || name; |
| 118 | 0 | if (key in this.cacheData) { |
| 119 | 0 | delete this.cacheData[key].data[name]; |
| 120 | } | |
| 121 | 0 | return getPromise(); |
| 122 | }, | |
| 123 | /** | |
| 124 | * gc | |
| 125 | * @param {[type]} now [description] | |
| 126 | * @return {[type]} [description] | |
| 127 | */ | |
| 128 | gc: function(now){ | |
| 129 | 0 | for(var key in this.cacheData){ |
| 130 | 0 | var item = this.cacheData[key] || {}; |
| 131 | 0 | if (now > item.expire) { |
| 132 | 0 | delete this.cacheData[key]; |
| 133 | } | |
| 134 | } | |
| 135 | } | |
| 136 | }; | |
| 137 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * cookie操作 | |
| 3 | * @type {Object} | |
| 4 | */ | |
| 5 | 1 | module.exports = { |
| 6 | /** | |
| 7 | * 解析 | |
| 8 | * @param {[type]} str [description] | |
| 9 | * @return {[type]} [description] | |
| 10 | */ | |
| 11 | parse: function(str){ | |
| 12 | 1 | 'use strict'; |
| 13 | 1 | var data = {}; |
| 14 | 1 | str.split(/[;,] */).forEach(function(item) { |
| 15 | 1 | var pos = item.indexOf('='); |
| 16 | 1 | if (pos === -1) { |
| 17 | 1 | return; |
| 18 | } | |
| 19 | 0 | var key = item.substr(0, pos).trim(); |
| 20 | 0 | var val = item.substr(++pos, item.length).trim(); |
| 21 | 0 | if ('"' === val[0]) { |
| 22 | 0 | val = val.slice(1, -1); |
| 23 | } | |
| 24 | // only assign once | |
| 25 | 0 | if (undefined === data[key]) { |
| 26 | 0 | try { |
| 27 | 0 | data[key] = decodeURIComponent(val); |
| 28 | } catch (e) { | |
| 29 | 0 | data[key] = val; |
| 30 | } | |
| 31 | } | |
| 32 | }); | |
| 33 | 1 | return data; |
| 34 | }, | |
| 35 | /** | |
| 36 | * 格式化 | |
| 37 | * @param {[type]} name [description] | |
| 38 | * @param {[type]} val [description] | |
| 39 | * @param {[type]} options [description] | |
| 40 | * @return {[type]} [description] | |
| 41 | */ | |
| 42 | stringify: function(name, value, options){ | |
| 43 | 0 | 'use strict'; |
| 44 | 0 | options = options || {}; |
| 45 | 0 | var item = [name + '=' + encodeURIComponent(value)]; |
| 46 | 0 | if (options.maxage) { |
| 47 | 0 | item.push('Max-Age=' + options.maxage); |
| 48 | } | |
| 49 | 0 | if (options.domain) { |
| 50 | 0 | item.push('Domain=' + options.domain); |
| 51 | } | |
| 52 | 0 | if (options.path) { |
| 53 | 0 | item.push('Path=' + options.path); |
| 54 | } | |
| 55 | 0 | var expires = options.expires; |
| 56 | 0 | if (expires){ |
| 57 | 0 | if (!isDate(expires)) { |
| 58 | 0 | expires = new Date(expires); |
| 59 | } | |
| 60 | 0 | item.push('Expires=' + expires.toUTCString()); |
| 61 | } | |
| 62 | 0 | if (options.httponly) { |
| 63 | 0 | item.push('HttpOnly'); |
| 64 | } | |
| 65 | 0 | if (options.secure) { |
| 66 | 0 | item.push('Secure'); |
| 67 | } | |
| 68 | 0 | return item.join('; '); |
| 69 | } | |
| 70 | } |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 过滤器 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | var Filter = module.exports = { |
| 6 | /** | |
| 7 | * 分页 | |
| 8 | * @param {[type]} value [description] | |
| 9 | * @return {[type]} [description] | |
| 10 | */ | |
| 11 | page: function(value){ | |
| 12 | 0 | 'use strict'; |
| 13 | 0 | return this.id(value) || 1; |
| 14 | }, | |
| 15 | /** | |
| 16 | * xxx asc,yyy desc | |
| 17 | * @return {[type]} [description] | |
| 18 | */ | |
| 19 | order: function(value){ | |
| 20 | 0 | 'use strict'; |
| 21 | 0 | if (isString(value)) { |
| 22 | 0 | value = value.split(','); |
| 23 | } | |
| 24 | 0 | if (!isArray(value)) { |
| 25 | 0 | return ''; |
| 26 | } | |
| 27 | 0 | return value.filter(function(item){ |
| 28 | 0 | item = item.trim().split(' '); |
| 29 | 0 | var field = item[0]; |
| 30 | 0 | var type = item[1]; |
| 31 | 0 | if (/^(ASC|DESC)$/i.test(type) && /^[\w]+$/.test(field)) { |
| 32 | 0 | return field + ' ' + type; |
| 33 | } | |
| 34 | }).join(','); | |
| 35 | }, | |
| 36 | /** | |
| 37 | * 大于0 | |
| 38 | * @return {[type]} [description] | |
| 39 | */ | |
| 40 | id: function(value){ | |
| 41 | 0 | 'use strict'; |
| 42 | 0 | value = parseInt(value + '', 10); |
| 43 | 0 | if (value > 0) { |
| 44 | 0 | return value; |
| 45 | } | |
| 46 | 0 | return 0; |
| 47 | }, | |
| 48 | /** | |
| 49 | * id列表 | |
| 50 | * @return {[type]} [description] | |
| 51 | */ | |
| 52 | ids: function(value, split){ | |
| 53 | 0 | 'use strict'; |
| 54 | 0 | if (isNumber(value)) { |
| 55 | 0 | value = this.id(value); |
| 56 | 0 | if (value) { |
| 57 | 0 | return [value]; |
| 58 | } | |
| 59 | 0 | return []; |
| 60 | } | |
| 61 | 0 | if (isString(value)) { |
| 62 | 0 | value = value.split(split || ','); |
| 63 | } | |
| 64 | 0 | if (!isArray(value)) { |
| 65 | 0 | return []; |
| 66 | } | |
| 67 | 0 | return value.filter(function(item){ |
| 68 | 0 | item = (item + '').trim(); |
| 69 | 0 | item = parseInt(item, 10); |
| 70 | 0 | return item; |
| 71 | }); | |
| 72 | }, | |
| 73 | /** | |
| 74 | * 是否在一个中 | |
| 75 | * @param {[type]} value [description] | |
| 76 | * @param {[type]} arr [description] | |
| 77 | * @return {[type]} [description] | |
| 78 | */ | |
| 79 | in: function(value, arr){ | |
| 80 | 0 | 'use strict'; |
| 81 | 0 | if (!isArray(arr)) { |
| 82 | 0 | arr = [arr]; |
| 83 | } | |
| 84 | 0 | if(arr.indexOf(value) > -1){ |
| 85 | 0 | return value; |
| 86 | } | |
| 87 | 0 | return ''; |
| 88 | }, | |
| 89 | /** | |
| 90 | * 将字符串切割为数组 | |
| 91 | * @param {[type]} value [description] | |
| 92 | * @param {[type]} split [description] | |
| 93 | * @return {[type]} [description] | |
| 94 | */ | |
| 95 | strs: function(value, split){ | |
| 96 | 0 | 'use strict'; |
| 97 | 0 | if (isString(value)) { |
| 98 | 0 | value = value.split(split || ','); |
| 99 | } | |
| 100 | 0 | if (!isArray(value)) { |
| 101 | 0 | return []; |
| 102 | } | |
| 103 | 0 | return value.filter(function(item){ |
| 104 | 0 | return (item + '').trim(); |
| 105 | }); | |
| 106 | } | |
| 107 | }; | |
| 108 | /** | |
| 109 | * 调用一个过滤器 | |
| 110 | * @param {[type]} data [description] | |
| 111 | * @param {[type]} type [description] | |
| 112 | * @return {[type]} [description] | |
| 113 | */ | |
| 114 | 1 | Filter.filter = function(value, type){ |
| 115 | 0 | 'use strict'; |
| 116 | 0 | var fn = Filter[type]; |
| 117 | 0 | if (typeof fn === 'function') { |
| 118 | 0 | var args = [].slice.call(arguments, 2); |
| 119 | 0 | args.unshift(value); |
| 120 | 0 | return Filter[type].apply(Filter, args); |
| 121 | } | |
| 122 | 0 | return false; |
| 123 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var crypto = require('crypto'); |
| 2 | /** | |
| 3 | * 生成uid | |
| 4 | * @param int length | |
| 5 | * @return string | |
| 6 | */ | |
| 7 | 1 | var uid = function(length){ |
| 8 | 0 | 'use strict'; |
| 9 | 0 | var ratio = Math.log(64) / Math.log(256); |
| 10 | 0 | var numbytes = Math.ceil(length * ratio); |
| 11 | 0 | var str = crypto.randomBytes(numbytes).toString('base64').slice(0, length); |
| 12 | 0 | return str.replace(/\+/g, '_').replace(/\//g, '-'); |
| 13 | }; | |
| 14 | /** | |
| 15 | * 生成cookie签名 | |
| 16 | * @param string val | |
| 17 | * @param string secret | |
| 18 | * @return string | |
| 19 | */ | |
| 20 | 1 | var cookieSign = function(val, secret){ |
| 21 | 0 | 'use strict'; |
| 22 | 0 | secret = crypto.createHmac('sha256', secret).update(val).digest('base64'); |
| 23 | 0 | secret = secret.replace(/\=+$/, ''); |
| 24 | 0 | return val + '.' + secret; |
| 25 | }; | |
| 26 | /** | |
| 27 | * 解析cookie签名 | |
| 28 | * @param {[type]} val | |
| 29 | * @param {[type]} secret | |
| 30 | * @return {[type]} | |
| 31 | */ | |
| 32 | 1 | var cookieUnsign = function(val, secret){ |
| 33 | 0 | 'use strict'; |
| 34 | 0 | var str = val.slice(0, val.lastIndexOf('.')); |
| 35 | 0 | return cookieSign(str, secret) === val ? str : false; |
| 36 | }; | |
| 37 | ||
| 38 | 1 | var Session = module.exports = Cache(function(){ |
| 39 | 1 | 'use strict'; |
| 40 | 1 | return { |
| 41 | init: function(options){ | |
| 42 | 0 | this.super_('init', options); |
| 43 | 0 | this.key = this.options.cookie; |
| 44 | 0 | this.updateExpire = true; |
| 45 | } | |
| 46 | }; | |
| 47 | }); | |
| 48 | ||
| 49 | 1 | Session.start = function(http){ |
| 50 | 0 | 'use strict'; |
| 51 | 0 | if (http.session) { |
| 52 | 0 | return http.session; |
| 53 | } | |
| 54 | 0 | var name = C('session_name'); |
| 55 | //是否使用签名 | |
| 56 | 0 | var secret = C('session_sign'); |
| 57 | 0 | var cookie = http.cookie[name]; |
| 58 | 0 | if (cookie && secret) { |
| 59 | 0 | cookie = cookieUnsign(cookie, secret); |
| 60 | } | |
| 61 | 0 | var session_cookie = cookie; |
| 62 | 0 | if (!cookie) { |
| 63 | 0 | cookie = uid(32); |
| 64 | 0 | session_cookie = cookie; |
| 65 | 0 | if (secret) { |
| 66 | 0 | cookie = cookieSign(cookie, secret); |
| 67 | } | |
| 68 | 0 | http.setCookie(name, cookie, C('session_options')); |
| 69 | } | |
| 70 | 0 | var type = C('session_type'); |
| 71 | 0 | if (!type && APP_DEBUG) { |
| 72 | 0 | type = 'File'; |
| 73 | 0 | console.log("in debug mode, session can't use memory type for storage, convert to File type"); |
| 74 | } | |
| 75 | //使用cluster的时候,不能使用内存来缓存Session | |
| 76 | 0 | if (!type && C('use_cluster')) { |
| 77 | 0 | type = 'File'; |
| 78 | 0 | console.log("in cluster mode, session can't use memory type for storage, convert to File type") |
| 79 | } | |
| 80 | 0 | name = type + 'Session'; |
| 81 | //session类 | |
| 82 | 0 | var session = http.session = thinkRequire(name)({ |
| 83 | cookie: session_cookie, | |
| 84 | timeout: C('session_timeout') | |
| 85 | }); | |
| 86 | //afterend时刷新缓存 | |
| 87 | 0 | http.on('afterEnd', function(){ |
| 88 | //刷新session | |
| 89 | 0 | return session.flush && session.flush(); |
| 90 | }) | |
| 91 | 0 | return cookie; |
| 92 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * Valid类 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | var net = require('net'); |
| 6 | 1 | var Valid = module.exports = Class(function(){ |
| 7 | 1 | 'use strict'; |
| 8 | 1 | return { |
| 9 | field: '', | |
| 10 | init: function(field){ | |
| 11 | 0 | this.field = field; |
| 12 | }, | |
| 13 | /** | |
| 14 | * 长度区域 | |
| 15 | * @param {[type]} min [description] | |
| 16 | * @param {[type]} max [description] | |
| 17 | * @return {[type]} [description] | |
| 18 | */ | |
| 19 | length: function(min, max){ | |
| 20 | 0 | min = min | 0; |
| 21 | 0 | var length = this.field.length; |
| 22 | 0 | if (length < min) { |
| 23 | 0 | return false; |
| 24 | } | |
| 25 | 0 | if (max) { |
| 26 | 0 | if (length > max) { |
| 27 | 0 | return false; |
| 28 | } | |
| 29 | } | |
| 30 | 0 | return true; |
| 31 | }, | |
| 32 | /** | |
| 33 | * 必填 | |
| 34 | * @return {[type]} [description] | |
| 35 | */ | |
| 36 | required: function(){ | |
| 37 | 0 | return (this.field + '').length > 0; |
| 38 | }, | |
| 39 | /** | |
| 40 | * 自定义正则校验 | |
| 41 | * @param {[type]} reg [description] | |
| 42 | * @return {[type]} [description] | |
| 43 | */ | |
| 44 | regexp: function(reg){ | |
| 45 | 0 | return reg.test(this.field); |
| 46 | }, | |
| 47 | /** | |
| 48 | * 邮箱 | |
| 49 | * @return {[type]} [description] | |
| 50 | */ | |
| 51 | email: function(){ | |
| 52 | 0 | return this.regexp(/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/); |
| 53 | }, | |
| 54 | /** | |
| 55 | * 时间戳 | |
| 56 | * @return {[type]} [description] | |
| 57 | */ | |
| 58 | time: function(){ | |
| 59 | 0 | return this.regexp(/^(([0-1]\d)|(2[0-3])):[0-5]\d:[0-5]\d$/); |
| 60 | }, | |
| 61 | /** | |
| 62 | * 中文名 | |
| 63 | * @return {[type]} [description] | |
| 64 | */ | |
| 65 | cnname: function(){ | |
| 66 | 0 | return this.regexp(/^[\u4e00-\u9fa5a-zA-Z.\u3002\u2022]{2,32}$/); |
| 67 | }, | |
| 68 | /** | |
| 69 | * 身份证号码 | |
| 70 | * @return {[type]} [description] | |
| 71 | */ | |
| 72 | idnumber: function(){ | |
| 73 | 0 | if (/^\d{15}$/.test(this.field)) { |
| 74 | 0 | return true; |
| 75 | } | |
| 76 | 0 | if ((/^\d{17}[0-9xX]$/).test(this.field)) { |
| 77 | 0 | var vs = '1,0,x,9,8,7,6,5,4,3,2'.split(','), |
| 78 | ps = '7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2'.split(','), | |
| 79 | ss = this.field.toLowerCase().split(''), | |
| 80 | r = 0; | |
| 81 | 0 | for (var i = 0; i < 17; i++) { |
| 82 | 0 | r += ps[i] * ss[i]; |
| 83 | } | |
| 84 | 0 | var isOk = (vs[r % 11] === ss[17]); |
| 85 | 0 | return isOk; |
| 86 | } | |
| 87 | 0 | return false; |
| 88 | }, | |
| 89 | /** | |
| 90 | * 手机号 | |
| 91 | * @return {[type]} [description] | |
| 92 | */ | |
| 93 | mobile: function(){ | |
| 94 | 0 | return this.regexp(/^(13|15|18|14)\d{9}$/); |
| 95 | }, | |
| 96 | /** | |
| 97 | * 邮编 | |
| 98 | * @return {[type]} [description] | |
| 99 | */ | |
| 100 | zipcode: function(){ | |
| 101 | 0 | return this.regexp(/^\d{6}$/); |
| 102 | }, | |
| 103 | /** | |
| 104 | * 2次值是否一致 | |
| 105 | * @param {[type]} field [description] | |
| 106 | * @return {[type]} [description] | |
| 107 | */ | |
| 108 | confirm: function(field){ | |
| 109 | 0 | return this.field === field; |
| 110 | }, | |
| 111 | /** | |
| 112 | * url | |
| 113 | * @return {[type]} [description] | |
| 114 | */ | |
| 115 | url: function(){ | |
| 116 | 0 | return this.regexp(/^http(s?):\/\/(?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}(?:[\/\?#][\/=\?%\-&~`@[\]\':+!\.#\w]*)?$/); |
| 117 | }, | |
| 118 | /** | |
| 119 | * 整数 | |
| 120 | * @param {[type]} o [description] | |
| 121 | * @return {[type]} [description] | |
| 122 | */ | |
| 123 | int: function(){ | |
| 124 | 0 | var value = parseInt(this.field, 0 || 10); |
| 125 | 0 | if (isNaN(value)) { |
| 126 | 0 | return false; |
| 127 | } | |
| 128 | 0 | return (value + '').length === this.field.length; |
| 129 | }, | |
| 130 | /** | |
| 131 | * 浮点数 | |
| 132 | * @return {[type]} [description] | |
| 133 | */ | |
| 134 | float: function(){ | |
| 135 | 0 | var value = parseFloat(this.field); |
| 136 | 0 | if (isNaN(value)) { |
| 137 | 0 | return false; |
| 138 | } | |
| 139 | 0 | return (value + '').length === this.field.length; |
| 140 | }, | |
| 141 | /** | |
| 142 | * 整数范围 | |
| 143 | * @param {[type]} min [description] | |
| 144 | * @param {[type]} max [description] | |
| 145 | * @return {[type]} [description] | |
| 146 | */ | |
| 147 | range: function(min, max){ | |
| 148 | 0 | var isInt = this.int(); |
| 149 | 0 | if (!isInt) { |
| 150 | 0 | return false; |
| 151 | } | |
| 152 | 0 | return this.field >= min && this.field <= max; |
| 153 | }, | |
| 154 | /** | |
| 155 | * ip4校验 | |
| 156 | * @return {[type]} [description] | |
| 157 | */ | |
| 158 | ip4: function(){ | |
| 159 | 0 | return net.isIPv4(this.field); |
| 160 | }, | |
| 161 | /** | |
| 162 | * ip6校验 | |
| 163 | * @return {[type]} [description] | |
| 164 | */ | |
| 165 | ip6: function(){ | |
| 166 | 0 | return net.isIPv6(this.field); |
| 167 | }, | |
| 168 | /** | |
| 169 | * ip校验 | |
| 170 | * @return {[type]} [description] | |
| 171 | */ | |
| 172 | ip: function(){ | |
| 173 | 0 | return net.isIP(this.field); |
| 174 | }, | |
| 175 | /** | |
| 176 | * 日期校验 | |
| 177 | * @return {[type]} [description] | |
| 178 | */ | |
| 179 | date: function(){ | |
| 180 | 0 | var reg = /^\d{4}-\d{1,2}-\d{1,2}$/; |
| 181 | 0 | return this.regexp(reg); |
| 182 | } | |
| 183 | }; | |
| 184 | }); | |
| 185 | /** | |
| 186 | * data格式 | |
| 187 | * [{ | |
| 188 | * value: xxx, | |
| 189 | * name: '', | |
| 190 | * valid: ['required', 'range'], | |
| 191 | * range_args: [], | |
| 192 | * msg:{ | |
| 193 | * required: '', | |
| 194 | * range: '' | |
| 195 | * } | |
| 196 | * },{ | |
| 197 | * value: xxx, | |
| 198 | * name: '', | |
| 199 | * valid: ['required', 'range'], | |
| 200 | * range_args: [], | |
| 201 | * msg:{ | |
| 202 | * required: '', | |
| 203 | * range: '' | |
| 204 | * } | |
| 205 | * }] | |
| 206 | * @param {[type]} data [description] | |
| 207 | * @return {[type]} [description] | |
| 208 | */ | |
| 209 | 1 | Valid.check = function(data){ |
| 210 | 0 | 'use strict'; |
| 211 | 0 | data = data ||[]; |
| 212 | 0 | var result = {}; |
| 213 | 0 | data.forEach(function(item){ |
| 214 | 0 | var valid = item.valid; |
| 215 | 0 | if (!isArray(valid)) { |
| 216 | 0 | valid = [valid]; |
| 217 | } | |
| 218 | 0 | var instance = Valid(item.value); |
| 219 | 0 | valid.some(function(validItem){ |
| 220 | 0 | var flag; |
| 221 | 0 | if (typeof validItem === 'function') { |
| 222 | 0 | flag = validItem(item.value, item); |
| 223 | 0 | if (typeof flag === 'string') { |
| 224 | 0 | result[item.name] = flag; |
| 225 | 0 | return true; |
| 226 | } | |
| 227 | 0 | return false; |
| 228 | } | |
| 229 | 0 | flag = instance[validItem].apply(instance, item[validItem + '_args'] || []); |
| 230 | 0 | if (!flag) { |
| 231 | 0 | result[item.name] = item.msg[validItem]; |
| 232 | 0 | return true; |
| 233 | } | |
| 234 | }); | |
| 235 | }); | |
| 236 | 0 | return result; |
| 237 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var thinkHttp = thinkRequire('Http'); |
| 2 | 1 | var url = require('url'); |
| 3 | 1 | var websocket = require('websocket').server; |
| 4 | 1 | var querystring = require('querystring'); |
| 5 | ||
| 6 | 1 | var WebSocket = module.exports = Class(function(){ |
| 7 | 1 | 'use strict'; |
| 8 | /** | |
| 9 | * socket初始化id | |
| 10 | * @type {Number} | |
| 11 | */ | |
| 12 | 1 | var socketId = 1000; |
| 13 | 1 | return { |
| 14 | init: function(httpServer, app){ | |
| 15 | 0 | this.httpServer = httpServer; |
| 16 | 0 | this.app = app; |
| 17 | }, | |
| 18 | /** | |
| 19 | * 检测origin是否合法 | |
| 20 | * @param {[type]} origin [description] | |
| 21 | * @return {[type]} [description] | |
| 22 | */ | |
| 23 | originIsAllowed: function(origin){ | |
| 24 | 0 | var allowOrigins = C('websocket_allow_origin'); |
| 25 | 0 | if (!allowOrigins) { |
| 26 | 0 | return true; |
| 27 | } | |
| 28 | 0 | var info = url.parse(origin); |
| 29 | 0 | var hostname = info.hostname; |
| 30 | 0 | if (isString(allowOrigins) && allowOrigins === hostname) { |
| 31 | 0 | return true; |
| 32 | 0 | }else if (isArray(allowOrigins) && allowOrigins.indexOf(hostname) > -1) { |
| 33 | 0 | return true; |
| 34 | 0 | }else if (isFunction(allowOrigins)) { |
| 35 | 0 | return allowOrigins(hostname, info); |
| 36 | } | |
| 37 | 0 | return false; |
| 38 | }, | |
| 39 | /** | |
| 40 | * 选择子协议 | |
| 41 | * @param {[type]} protocolFullCaseMap [description] | |
| 42 | * @return {[type]} [description] | |
| 43 | */ | |
| 44 | getSubProtocal: function(protocolFullCaseMap){ | |
| 45 | 0 | var selectedProtocal = C('websocket_sub_protocal'); |
| 46 | 0 | if (isFunction(selectedProtocal)) { |
| 47 | 0 | var subProtocals = Object.values(protocolFullCaseMap); |
| 48 | 0 | selectedProtocal = selectedProtocal(subProtocals); |
| 49 | } | |
| 50 | 0 | return selectedProtocal; |
| 51 | }, | |
| 52 | /** | |
| 53 | * 建立连接处理 | |
| 54 | * @param {[type]} request [description] | |
| 55 | * @return {[type]} [description] | |
| 56 | */ | |
| 57 | openHandle: function(request, protocal){ | |
| 58 | 0 | var req = request.httpRequest; |
| 59 | 0 | if (req.url === '/') { |
| 60 | 0 | return getPromise([]); |
| 61 | } | |
| 62 | 0 | var deferred = getDefer(); |
| 63 | 0 | var fn = function(){}; |
| 64 | 0 | var res = {setHeader: fn, end: fn, write: fn}; |
| 65 | 0 | var self = this; |
| 66 | 0 | thinkHttp(req, res).run(function(http){ |
| 67 | 0 | http.websocket = request.socket; |
| 68 | //子协议 | |
| 69 | 0 | http.websocket_sub_protocal = protocal; |
| 70 | 0 | self.app.listener(http).then(function(){ |
| 71 | 0 | deferred.resolve({ |
| 72 | cookie: Object.values(http._cookie), | |
| 73 | http: http | |
| 74 | }); | |
| 75 | }).catch(function(err){ | |
| 76 | 0 | deferred.reject(err); |
| 77 | }) | |
| 78 | }); | |
| 79 | 0 | return deferred.promise; |
| 80 | }, | |
| 81 | /** | |
| 82 | * 消息处理 | |
| 83 | * @return {[type]} [description] | |
| 84 | */ | |
| 85 | messageHandle: function(message, connection, app, type){ | |
| 86 | 0 | if (type !== 'utf8') { |
| 87 | 0 | connection.socket.send(WebSocket.ERROR_MESSAGE.TYPE_ERROR, message + ' is not valid json'); |
| 88 | 0 | return; |
| 89 | } | |
| 90 | //解析数据 | |
| 91 | 0 | try{ |
| 92 | 0 | message = JSON.parse(message); |
| 93 | }catch(e){ | |
| 94 | 0 | connection.socket.send(WebSocket.ERROR_MESSAGE.INVALID_JSON, message + ' is not valid json'); |
| 95 | 0 | return; |
| 96 | } | |
| 97 | 0 | if (message.jsonrpc !== '2.0') { |
| 98 | 0 | connection.socket.send(WebSocket.ERROR_MESSAGE.INVALID_JSONRPC, 'data.jsonrpc must be 2.0'); |
| 99 | 0 | return; |
| 100 | } | |
| 101 | 0 | var method = message.method + ''; |
| 102 | 0 | if (!method) { |
| 103 | 0 | connection.socket.send(WebSocket.ERROR_MESSAGE.INVALID_METHOD, 'data.method is not valid'); |
| 104 | 0 | return; |
| 105 | } | |
| 106 | 0 | var pars = message.params; |
| 107 | 0 | var headers = {}; |
| 108 | 0 | if (isObject(message.params.headers)) { |
| 109 | 0 | headers = message.params.headers; |
| 110 | 0 | pars = message.params.data; |
| 111 | } | |
| 112 | 0 | if (isObject(pars)) { |
| 113 | 0 | method += (method.indexOf('?') > -1 ? '&' : '?') + querystring.stringify(pars) |
| 114 | } | |
| 115 | 0 | var self = this; |
| 116 | 0 | var data = { |
| 117 | host: '', | |
| 118 | url: method, | |
| 119 | headers: headers, | |
| 120 | write: function(data, encoding, errMsg){ | |
| 121 | 0 | var pars = self.getRPCData(JSON.parse(data), errMsg); |
| 122 | 0 | pars.id = message.id; |
| 123 | 0 | connection.send(JSON.stringify(pars)); |
| 124 | }, | |
| 125 | end: function(data){ | |
| 126 | 0 | if (data) { |
| 127 | 0 | this.write(data); |
| 128 | } | |
| 129 | 0 | connection.close(); |
| 130 | } | |
| 131 | } | |
| 132 | 0 | var defaultHttp = thinkHttp.getDefaultHttp(data); |
| 133 | 0 | var httpInstance = thinkHttp(defaultHttp.req, defaultHttp.res); |
| 134 | //将websocket实例添加到http对象上 | |
| 135 | 0 | httpInstance.http.websocket = connection.socket; |
| 136 | 0 | httpInstance.run(app.listener); |
| 137 | }, | |
| 138 | /** | |
| 139 | * 获取rpc数据对象 | |
| 140 | * @param {[type]} data [description] | |
| 141 | * @param {[type]} errMsg [description] | |
| 142 | * @return {[type]} [description] | |
| 143 | */ | |
| 144 | getRPCData: function(data, errMsg){ | |
| 145 | 0 | var pars = {jsonrpc: '2.0'}; |
| 146 | 0 | if (errMsg) { |
| 147 | 0 | pars.error = {code: data, message: errMsg}; |
| 148 | }else{ | |
| 149 | 0 | pars.result = data; |
| 150 | } | |
| 151 | 0 | return pars; |
| 152 | }, | |
| 153 | run: function(){ | |
| 154 | 0 | var instance = new websocket({ |
| 155 | httpServer: this.httpServer, | |
| 156 | autoAcceptConnections: false | |
| 157 | }); | |
| 158 | 0 | var self = this; |
| 159 | 0 | instance.on('request', function(request){ |
| 160 | //检测origin | |
| 161 | 0 | if (!self.originIsAllowed(request.origin)) { |
| 162 | 0 | return request.reject(); |
| 163 | } | |
| 164 | 0 | var socket = request.socket; |
| 165 | 0 | socket.id = socketId++; |
| 166 | 0 | socket.activeTime = Date.now(); |
| 167 | //选择子协议 | |
| 168 | 0 | var protocal = self.getSubProtocal(request.protocolFullCaseMap); |
| 169 | 0 | return self.openHandle(request, protocal).then(function(data){ |
| 170 | 0 | var connection = socket.connection = request.accept(protocal, request.origin, data.cookie); |
| 171 | 0 | socket.close = function(){ |
| 172 | 0 | connection.close(); |
| 173 | } | |
| 174 | 0 | if (!socket.send) { |
| 175 | 0 | socket.send = function(data, errMsg){ |
| 176 | 0 | var pars = self.getRPCData(data, errMsg); |
| 177 | 0 | connection.send(JSON.stringify(pars)); |
| 178 | } | |
| 179 | } | |
| 180 | 0 | var messageHandle = C('websocket_message_handle'); |
| 181 | 0 | connection.on('message', function(message) { |
| 182 | 0 | socket.activeTime = Date.now(); |
| 183 | 0 | var data = message.type === 'utf8' ? message.utf8Data : message.binaryData; |
| 184 | 0 | if (isFunction(messageHandle)) { |
| 185 | 0 | messageHandle(data, connection, self.app, message.type); |
| 186 | }else{ | |
| 187 | 0 | self.messageHandle(data, connection, self.app, message.type); |
| 188 | } | |
| 189 | }); | |
| 190 | 0 | connection.on('close', function() { |
| 191 | 0 | data.http.emit('websocket.close'); |
| 192 | }); | |
| 193 | }).catch(function(err){ | |
| 194 | 0 | request.reject(err); |
| 195 | }) | |
| 196 | }) | |
| 197 | } | |
| 198 | } | |
| 199 | }); | |
| 200 | /** | |
| 201 | * 错误信息 | |
| 202 | * @type {Object} | |
| 203 | */ | |
| 204 | 1 | WebSocket.ERROR_MESSAGE = { |
| 205 | TYPE_ERROR: -100001, //数据类型错误 | |
| 206 | INVALID_JSON: -100002, //不是合法的json | |
| 207 | INVALID_JSONRPC: -100003, //不是jsonrpc数据格式 | |
| 208 | INVALID_METHOD: -100004 //请求方法不合法 | |
| 209 | } |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var path = require('path'); |
| 2 | 1 | var fs = require('fs'); |
| 3 | ||
| 4 | //APP根目錄 | |
| 5 | 1 | global.APP_PATH = global.APP_PATH ? path.normalize(global.APP_PATH) : path.dirname(__dirname) + '/App'; |
| 6 | //RUNTIME目录 | |
| 7 | 1 | global.RUNTIME_PATH = global.RUNTIME_PATH ? path.normalize(global.RUNTIME_PATH) : global.APP_PATH + '/Runtime'; |
| 8 | //DEBUG模式 | |
| 9 | 1 | global.APP_DEBUG = global.APP_DEBUG || false; |
| 10 | //静态资源文件的根目录 | |
| 11 | 1 | global.RESOURCE_PATH = global.RESOURCE_PATH || ''; |
| 12 | //THINKJS的根目录 | |
| 13 | 1 | global.THINK_PATH = __dirname; |
| 14 | //默认为http模式 | |
| 15 | 1 | global.APP_MODE = 'http'; |
| 16 | //命令行模式 | |
| 17 | 1 | if (process.argv[2]) { |
| 18 | 1 | APP_MODE = 'cli'; |
| 19 | } | |
| 20 | //从package.json文件里获取版本号 | |
| 21 | 1 | global.THINK_VERSION = JSON.parse(fs.readFileSync(global.THINK_PATH + '/../package.json', 'utf8')).version; |
| 22 | //启动 | |
| 23 | 1 | require(global.THINK_PATH + '/Lib/Core/Think.js').start(); |