Repository: BetterJS/badjs-web Branch: master Commit: 0e8c6e974943 Files: 200 Total size: 2.4 MB Directory structure: gitextract_d1_lp_hk/ ├── .editorconfig ├── .gitignore ├── .jshintrc ├── LICENSE ├── Readme.md ├── app.js ├── controller/ │ ├── action/ │ │ ├── ApplyAction.js │ │ ├── ApproveAction.js │ │ ├── IndexAction.js │ │ ├── LogAction.js │ │ ├── StatisticsAction.js │ │ ├── UserAction.js │ │ └── UserApplyAction.js │ └── router.js ├── dao/ │ ├── ApplyDao.js │ ├── ApproveDao.js │ ├── StatisticsDao.js │ ├── UserApplyDao.js │ └── UserDao.js ├── db/ │ └── create.sql ├── gulpfile.js ├── model/ │ ├── Apply.js │ ├── Approve.js │ ├── User.js │ └── UserApply.js ├── oos/ │ └── demo/ │ └── demooos.js ├── package.json ├── project.debug.json ├── project.json ├── retCode.txt ├── service/ │ ├── ApplyService.js │ ├── ApproveService.js │ ├── BusinessService.js │ ├── EmailService.js │ ├── LogService.js │ ├── OfflineLogService.js │ ├── RealtimeService.js │ ├── StatisticsService.js │ ├── UserApplyService.js │ ├── UserService.js │ └── worker/ │ ├── Processor.js │ ├── ProcessorPool.js │ └── Worker.js ├── static/ │ ├── badjs/ │ │ ├── bj-report.js │ │ └── bj-wrap.js │ ├── common/ │ │ ├── delegator.js │ │ └── dialog/ │ │ ├── dialog.js │ │ └── modal.ejs │ ├── common.js │ ├── entry.apply.js │ ├── entry.applyList.js │ ├── entry.authUserManage.js │ ├── entry.charts.js │ ├── entry.home.js │ ├── entry.log.js │ ├── entry.offlinelog.js │ ├── entry.projectTotal.js │ ├── entry.realtime.js │ ├── entry.statistics.js │ ├── entry.userManage.js │ ├── img/ │ │ └── tmp/ │ │ └── tmp │ ├── js/ │ │ ├── beautify.js │ │ └── require.js │ ├── lib/ │ │ ├── bootstrap/ │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap.css │ │ │ └── bootstrap.js │ │ ├── charts/ │ │ │ ├── highcharts.js │ │ │ └── sand-signika.js │ │ ├── jquery/ │ │ │ └── jquery.datetimepicker.js │ │ └── underscore/ │ │ └── underscore.ext.js │ └── module/ │ ├── apply/ │ │ ├── load.apply.js │ │ └── mod.apply.js │ ├── applyList/ │ │ ├── load.applyList.js │ │ ├── mod.applyList.js │ │ └── template/ │ │ └── applyTable.ejs │ ├── authUserManage/ │ │ ├── load.authUserManage.js │ │ ├── mod.authUserManage.js │ │ └── template/ │ │ └── userTable.ejs │ ├── charts/ │ │ ├── load.charts.js │ │ └── mod.charts.js │ ├── common/ │ │ ├── app.css │ │ └── last.select.js │ ├── home/ │ │ └── load.home.js │ ├── locales/ │ │ ├── bootstrap-datetimepicker.ar.js │ │ ├── bootstrap-datetimepicker.bg.js │ │ ├── bootstrap-datetimepicker.ca.js │ │ ├── bootstrap-datetimepicker.cs.js │ │ ├── bootstrap-datetimepicker.da.js │ │ ├── bootstrap-datetimepicker.de.js │ │ ├── bootstrap-datetimepicker.ee.js │ │ ├── bootstrap-datetimepicker.el.js │ │ ├── bootstrap-datetimepicker.es.js │ │ ├── bootstrap-datetimepicker.fi.js │ │ ├── bootstrap-datetimepicker.fr.js │ │ ├── bootstrap-datetimepicker.he.js │ │ ├── bootstrap-datetimepicker.hr.js │ │ ├── bootstrap-datetimepicker.hu.js │ │ ├── bootstrap-datetimepicker.id.js │ │ ├── bootstrap-datetimepicker.is.js │ │ ├── bootstrap-datetimepicker.it.js │ │ ├── bootstrap-datetimepicker.ja.js │ │ ├── bootstrap-datetimepicker.ko.js │ │ ├── bootstrap-datetimepicker.lt.js │ │ ├── bootstrap-datetimepicker.lv.js │ │ ├── bootstrap-datetimepicker.ms.js │ │ ├── bootstrap-datetimepicker.nb.js │ │ ├── bootstrap-datetimepicker.nl.js │ │ ├── bootstrap-datetimepicker.no.js │ │ ├── bootstrap-datetimepicker.pl.js │ │ ├── bootstrap-datetimepicker.pt-BR.js │ │ ├── bootstrap-datetimepicker.pt.js │ │ ├── bootstrap-datetimepicker.ro.js │ │ ├── bootstrap-datetimepicker.rs-latin.js │ │ ├── bootstrap-datetimepicker.rs.js │ │ ├── bootstrap-datetimepicker.ru.js │ │ ├── bootstrap-datetimepicker.sk.js │ │ ├── bootstrap-datetimepicker.sl.js │ │ ├── bootstrap-datetimepicker.sv.js │ │ ├── bootstrap-datetimepicker.sw.js │ │ ├── bootstrap-datetimepicker.th.js │ │ ├── bootstrap-datetimepicker.tr.js │ │ ├── bootstrap-datetimepicker.ua.js │ │ ├── bootstrap-datetimepicker.uk.js │ │ ├── bootstrap-datetimepicker.zh-CN.js │ │ └── bootstrap-datetimepicker.zh-TW.js │ ├── log/ │ │ ├── load.log.js │ │ ├── load.offlinelog.js │ │ ├── load.realtime.js │ │ ├── logDetailDialog/ │ │ │ ├── logDetailDialog.js │ │ │ └── tpl/ │ │ │ └── dialog.ejs │ │ ├── mod.log.js │ │ ├── mod.offlinelog.js │ │ ├── mod.realtime.js │ │ ├── offlineDialog/ │ │ │ ├── offlineDialog.js │ │ │ └── tpl/ │ │ │ ├── dialog.ejs │ │ │ └── offline_monitor_row.ejs │ │ ├── source.trigger.js │ │ └── template/ │ │ ├── debar.ejs │ │ ├── keyword.ejs │ │ └── logTable.ejs │ ├── projectTotal/ │ │ ├── load.projectTotal.js │ │ ├── mod.projectTotal.js │ │ └── template/ │ │ └── statistics.ejs │ ├── statistics/ │ │ ├── load.statistics.js │ │ ├── mod.statistics.js │ │ └── template/ │ │ └── statistics.ejs │ └── userManage/ │ ├── load.userManage.js │ ├── mod.userManage.js │ └── template/ │ └── userTable.ejs ├── test/ │ ├── test-busiess-services.js │ ├── test-crypto.js │ ├── test-db-connect.js │ ├── test-email-add.js │ ├── test-fetch-statistics-to-today.js │ ├── test-fetch-statistics.js │ ├── test-getProjects.js │ ├── test-processor.js │ ├── test-sendAll.js │ ├── test-sendEmail.js │ ├── test-statistics-find-by-time.js │ └── test-zmq.js ├── tmp/ │ └── tmp ├── utils/ │ ├── auth.js │ ├── dateFormat.js │ ├── des.js │ ├── email.js │ ├── email_tof.js │ ├── express-websocket.js │ └── uniform_msg_client ├── views/ │ ├── account.html │ ├── apply.html │ ├── applyList.html │ ├── approve.html │ ├── authUserManage.html │ ├── charts.html │ ├── common/ │ │ ├── commonLinks.html │ │ ├── footer.html │ │ ├── header.html │ │ ├── indexHeader.html │ │ ├── leftSide.html │ │ ├── logo.html │ │ └── nav.html │ ├── help.html │ ├── index.html │ ├── introduce.html │ ├── log.html │ ├── login.html │ ├── modifyUser.html │ ├── monitor.html │ ├── offlinelog.html │ ├── projectTotal.html │ ├── realtimelog.html │ ├── register.html │ ├── statistics.html │ └── userManage.html ├── webpack.config.js ├── workflow/ │ ├── ExpressInitWorker.js │ ├── InitWorker.js │ └── ServiceInitWorker.js └── workflow.config.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # This file is for unifying the coding style for different editors and IDEs # editorconfig.org root = true [*] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true # Tabs in JS unless otherwise specified [**.js] indent_style = space indent_size = 4 [**.html] indent_style = space indent_size = 4 [**.css] indent_style = space indent_size = 4 [**.sass] indent_style = space indent_size = 4 ================================================ FILE: .gitignore ================================================ ################# ## Eclipse ################# *.pydevproject .project .metadata bin/ *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath # External tool builders .externalToolBuilders/ # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ################# ## Visual Studio ################# ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates # Build results [Dd]ebug/ [Rr]elease/ x64/ build/ [Bb]in/ [Oo]bj/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.log *.scc # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch *.ncrunch* .*crunch*.local.xml # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.Publish.xml *.pubxml # NuGet Packages Directory ## TODO: If you have NuGet Package Restore enabled, uncomment the next line #packages/ # Windows Azure Build Output csx *.build.csdef # Windows Store app package directory AppPackages/ # Others sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.[Pp]ublish.xml *.pfx *.publishsettings # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files App_Data/*.mdf App_Data/*.ldf ############# ## Windows detritus ############# # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Mac crap .DS_Store ############# ## Python ############# *.py[co] # Packages *.egg *.egg-info dist/ build/ eggs/ parts/ var/ sdist/ develop-eggs/ .installed.cfg # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox #Translations *.mo #Mr Developer .mr.developer.cfg ############# ## NodeJS ############# node_modules/ coverage.html web-debug.bat ############# ## webstorm ############# .idea/ ================================================ FILE: .jshintrc ================================================ // 根据 knightli 提供的配置改编, 欢迎试用 // update by kaelyang 2015/07/31 { // 参考 http://jslinterrors.com/ // ==============预定义变量============ "globals":{ "$": true, "GLOBAL": true, "global": true, "jQuery": true, "exports": true, "module": true, "define": true, "require": true, "it": true, "before": true, "describe": true, "beforeEach": true, "afterEach": true, "__inline": true, "__uri": true }, // ==============Legacy Options============ // 注: 这些选项继承自jslint // 当变量名以"_"开头或结尾时,是否告警 "nomen": false, // 是否只允许出现一个var在函数中 "onevar": false, // 是否遇到第一个错误的时候就终止 "passfail": false, // 让JSHint检查你的代码是否违反道格拉斯的JS编码风格(严格的空白规范??). "white": false, // 最大错误数目,超过则停止分析(默认:50) "maxerr": 50, // ==============Environments Options============ // 是否预定义现代浏览器暴露出来的全局变量(不包括alert和console) "browser": true, // 是否预定义console,alert这种开发阶段的调试代码 "devel": true, // 该选项定义了一些非标准但广泛使用的全局变量,像"escape"和"unescape". "nonstandard": true, // ==============Enforcing Options============ // 禁止使用位运算符 "bitwise": true, // 只允许使用camelCase or UPPER_CASE "camelcase": false, // 必须给block加括号 "curly": false, // 不安全的== (会建议换成===或者!==) "eqeqeq": false, // 不安全的for in "forin": false, // 立即函数调用必须用(function(){}())而不是(function(){})() "immed": true, // 必须先定义再使用 "latedef": false, // 构造函数名的首字母必须大写 "newcap": true, // 禁止arguments.caller和arguments.callee的使用(ES5的严格模式中被禁用了,并且不是所有js实现中都有) "noarg": true, // 禁止空的代码块或未赋值的构造器 "noempty": false, // 不允许不做赋值的构造函数 "nonew": true, // 不允许使用++或者--的操作符 "plusplus": false, // 正则中不允许使用.或者[^…] "regexp": false, // 未定义的XXX "undef": true, // 未使用的XXX "unused": false, // 需要使用strict mode,详见http:// ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ "strict": false, // 行末有多余的空格 "trailing": false, // 配置单双引号规则: "false"或者不设置 不做限制 "single" 只许单引号 "double" 只许双引号 "true" 两者选其一,但不能同时出现 "quotmark": "false", // 配置缩进规则: 设置缩进对应的空格数,如不设置,则不对缩进做要求 // 注: 这里配置了indent,则即使上面的white设置了false,也还是会导致 swindent 报warning(https:// github.com/jshint/jshint/issues/935) // TODO:确认jshint解决了 swindent 的支持后可重新配置此项 // "indent": 4, // 配置单行代码的最大长度 // "maxlen": 120, // 配置函数的最多参数个数: 参数过多时,可读性下降,可考虑用options对象字段来减少参数个数 // "maxparams": 5, // 配置函数内代码块嵌套最多层数 // "maxdepth": 5, // 配置函数内最多有多少条语句(一个函数声明会被当作一条语句) // "maxstatements": 50, // 配置最大圈复杂度(http://en.wikipedia.org/wiki/Cyclomatic_complexity) // "maxcomplexity": 10, // TODO:打开来优化代码复杂度 // ==============Relaxing Options============ // 代码末尾缺失分号,是否放过 "asi": false, // for等预期是==的地方用了=,是否放过 "boss": false, // 代码中有debugger语句,是否放过 "debug": false, // 使用"=="来比较变量与null,是否放过 "eqnull": false, // ES5语法校验报警,是否放过 "es5": false, // ECMAScript 6语法校验报警,是否放过 "esnext": true, // 代码中使用了eval,是否放过 "evil": true, // 只允许在函数调用或赋值时使用表达式(原因???) 2 > 1 ? (alert(1)) : (alert(2)); "expr": true, // 在控制语句中定义了变量,却在控制语句之外使用变量 function(){ if(1) {var x = 0;} x = x + 1;} "funcscope": false, // 使用了全局级别的严格模式,是否放过 "globalstrict": false, // 使用了__iterator__这个属性(并非所有浏览器支持), 是否放过 (原因???) "iterator": false, // 是否允许单行语句块中最后一条语句不写分号 "lastsemic": false, // 是否允许出现不安全的换行(示例???) "laxbreak": true, // 是否允许逗号出现在行首的换行方式 "laxcomma": false, // 是否允许在循环里定义函数 "loopfunc": true, // 是否允许多行字符串(原因???) "multistr": true, // 是否允许在代码中使用__proto__属性(并非所有浏览器支持) "proto": false, // 是否允许在switch语句中只出现一个case "onecase": true, // 是否允许unescaped - in the end of regular expressions. "regexdash": false, // 是否允许在代码中使用"javascript:..."这样的url "scripturl": true, // 是否使用smarttab风格混用tab和space(http:// www.emacswiki.org/emacs/SmartTabs) "smarttabs": false, // 是否允许使用shadow变量(shadow变量:当一个变量所在的作用域之外还有一个同名的变量,称为shadow变量) // http:// stackoverflow.com/questions/5373278/variable-shadowing-in-javascript "shadow": false, // 是否允许用obj['name']和obj.name两种方式访问对象的属性 "sub": true, // 是否允许使用像"new function() {...}"这样怪异的构造器 "supernew": true, // 是否允许在严格模式下的非构造函数中使用this(该选项只能用于函数作用域中) "validthis": false, // Missing radix parameter to parseInt (defaults to 10) "-W065": false, // Literal accessor is better written in dot notation "-W069": false } ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 , imweb(http://imweb.io/) Copyright (c) 2015 , chriscai(345446383@qq.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Readme.md ================================================ #badjs-storage > badjs manage system . # 运行 ```javascript node app.js ``` # 启动参数 --debug log 采用debug 级别, 默认使用info --project 使用测试环境( project.debug.json )配置 , 默认使用 project.json # 构建 静态页面使用webpack ,开发阶段使用 ```javascript webpack -w ``` 上线阶段需要打包打包命令 ```javascript webpack ``` # 数据库初始化 db/create.sql 是需要初始化到 mysql 的中。其中默认的超级管理员帐号是 admin , 密码是 admin # 配置说明 ``` { "host" : "http://badjs.server.com/", //配额管理服务器地址,用于邮件中的图片展示 "mysql" : { "url" : "mysql://root:root@localhost:3306/badjs" // mysql 地址 }, "storage" : { // 存储服务器的地址, 这里配置badjs-storage 的地址 "errorMsgTopUrl" : "http://127.0.0.1:9000/errorMsgTop", "errorMsgTopCacheUrl" : "http://127.0.0.1:9000/errorMsgTopCache", "queryUrl" : "http://127.0.0.1:9000/query" }, "acceptor": { //badjs-acceptor 模块的地址, 这里用于同步审核通过的业务的id 到接入层进行验证 "pushProjectUrl" : "http://127.0.0.1:9001/getProjects" }, "openapi": { //badjs-acceptor 模块的地址, 这里用于同步审核通过的业务的appkey 到openapi 进行验证 "pushProjectUrl" : "http://127.0.0.1:9002/getProjects" }, "mq" : { // badjs-mq 的地址 "url" : "tcp://127.0.0.1:10000", "subscribe" : "badjs" // 跟 badjs-aceptor 中的subscribe 对应 "module": "axon" // 指定 mq 模块, }, "email": { // 发送 email 配置 "homepage": "http://badjs.server.com/user/index.html", // 邮件中的 快捷入口 "from": "noreply-badjs@demo.com", //邮件中的发送者名字 "smtp": "smtp.demo.com", // smtp 服务器 "emailSuffix" : "@demo.com", //收件人的邮件后缀,收件人地址 username + emailSuffix "time": "09:00:00", // 几点发送邮件 "top": 20, //邮件只发送错误排名的配置的top20 "module": "email" // 邮件发送模块 }, "oos" : { //接入公司的统一登录, 删掉使用系统自己的用户管理 "module":"demo/demooos" } } ``` # oos 接入 查看当前目录的 oos/demooos.js 如何处理。 > 腾讯内部接入可以[参考](https://github.com/BetterJS/oos-tencent) ================================================ FILE: app.js ================================================ var workflow = require('./workflow.config.json'), _ = require("underscore"); var workflowPath = workflow.path; if(!workflowPath){ workflowPath = "./" } if(!workflow.workflow){ return ; } _.each(workflow.workflow , function (value , key){ require(workflowPath +"/"+ value)(); }) ================================================ FILE: controller/action/ApplyAction.js ================================================ /** * @info : APPLY ACION * @author : coverguo * @date : 2015-01-07 */ var log4js = require('log4js'), logger = log4js.getLogger(), _ = require('underscore'), crypto = require('crypto'), ApplyService = require('../../service/ApplyService'), isError = function(res, error) { if (error) { res.json({ ret: 1, msg: error }); return true; } return false; }; var REG_DOMAIN_STAR = /^\*(\.[^\/]+)?$/; var REG_REFERER = /^https?:\/\/[^\/]+\//i; var processData = function(data) { _.each(data, function(value, key) { if (value.createTime) { value.createTime = (new Date(value.createTime) - 0); } if (value.passTime) { value.passTime = (new Date(value.passTime) - 0); } }); return data; }; var applyAction = { addApply: function(params, req, res) { // 必要信息为空,则报错 var url = params.url; var url_no_match = !REG_DOMAIN_STAR.test(url) && !REG_REFERER.test(url); if (params.name === "" || url_no_match) { res.json({ ret: 1002, msg: "params error" }); return; } var apply = params; apply.userName = params.user.loginName; var applyService = new ApplyService(); if (apply.id) { applyService.update(apply, function(err, items) { if (isError(res, err)) { return; } res.json({ ret: 0, msg: "success-add" }); }); } else { apply.status = 0; apply.createTime = new Date(); apply.appkey = crypto.createHash("md5").update(new Date - 0 + "badjsappkey" + params.user.loginName).digest('hex'); applyService.add(apply, function(err, items) { if (isError(res, err)) { return; } res.json({ ret: 0, msg: "success-add" }); }); } }, queryListByUser: function(params, req, res) { var applyService = new ApplyService(); if (params.user.role != 1) { applyService.queryListByUser(params, function(err, items) { if (isError(res, err)) { return; } res.json({ ret: 0, msg: "success", data: { role: params.user.role, item: processData(items) } }); }); } else { applyService.queryListByAdmin(params, function(err, items) { if (isError(res, err)) { return; } res.json({ ret: 0, msg: "success", data: { role: params.user.role, item: processData(items) } }); }); } }, queryListByAdmin: function(params, req, res) { var applyService = new ApplyService(); //不是管理员的话直接返回错误提示 if (params.user.role != 1) { res.json({ ret: 1003, msg: "权限不足" }); return; } applyService.queryListByAdmin(params, function(err, items) { if (isError(res, err)) { return; } res.json({ ret: 0, msg: "success", data: processData(items) }); }); }, queryListBySearch: function(params, req, res) { var applyService = new ApplyService(); var searchParam = {}; if (params.user.role != 1) { searchParam.userName = params.user.loginName; } //搜索全部 if (params.statusType != 3) { searchParam.status = params.statusType; } applyService.queryListBySearch(searchParam, function(err, items) { if (isError(res, err)) { return; } res.json({ ret: 0, msg: "success", data: { role: params.user.role, item: processData(items) } }); }); }, queryByApplyId: function(params, cb) { var applyService = new ApplyService(); applyService.queryById({ id: params.applyId }, function(err, apply) { cb(err, apply); }); }, update: function(params, req, res) { var as = new ApplyService(); as.update(params, function() { }); }, remove: function(params, req, res) { var applyService = new ApplyService(); applyService.remove(params, function(err) { if (err) { res.json({ ret: 3, msg: "fail remove" }); } else { res.json({ ret: 0, msg: "success remove" }); } }); } }; module.exports = applyAction; ================================================ FILE: controller/action/ApproveAction.js ================================================ /** * @info : APPROVE ACION * @author : coverguo * @date : 2015-01-12 */ var log4js = require('log4js'), logger = log4js.getLogger(), ApproveService = require('../../service/ApproveService'), LogService = require('../../service/LogService'), isError = function (res , error){ if(error){ res.json({ret : 1 , msg : error}); return true; } return false; }; var approveAction = { doApprove: function(params, req , res){ var approve = params; approve.createTime = new Date(); approve.userName = params.user.loginName; logger.debug('add_approve param :' + approve); var approveService = new ApproveService(); var logService = new LogService(); approveService.add(params, function (err, items) { if(isError(res, err)){ return; } if(params.applyStatus == 1 || params.applyStatus == 2){ var pushProject = function (){ logService.pushProject(function (err){ if(err){ logger.warn('push project error ' + err); }else { logger.info('push project success from approve '); } }); } pushProject(); } res.json({ret:0, msg:"审核完成"}); }); }, queryList : function (params,cb) { var as = new ApproveService(); as.query(params,cb); }, update:function(){ var as = new ApproveService(); as.update(params,cb); }, remove: function(params , cb){ var as = new ApproveService(); as.remove(params,cb); } }; module.exports = approveAction; ================================================ FILE: controller/action/IndexAction.js ================================================ /** * Created by chriscai on 2015/1/15. */ var BusinessService = require('../../service/BusinessService'); var fs = require("fs"); var path = require("path"); var log4js = require('log4js'), logger = log4js.getLogger(); var IndexAction = { index : function(parm , req, res){ var params = req.query, user = req.session.user; var businessService = new BusinessService(); businessService.findBusinessByUser(user.loginName , function (err, item){ res.render('log', { layout: false, user: user, index:"log" , items : item} ); }); }, realtime : function(parm , req, res){ var params = req.query, user = req.session.user; var businessService = new BusinessService(); businessService.findBusinessByUser(user.loginName , function (err, item){ res.render('realtimelog', { layout: false, user: user, index:"realtime" , items : item , systime : new Date - 0} ); }); }, offline : function(parm , req, res){ var params = req.query, user = req.session.user; var businessService = new BusinessService(); businessService.findBusinessByUser(user.loginName , function (err, item){ res.render('offlinelog', { layout: false, user: user, index:"offlinelog" , items : item , systime : new Date - 0} ); }); } }; module.exports = IndexAction; ================================================ FILE: controller/action/LogAction.js ================================================ /** * @info : LOG ACION * @author : coverguo * @date : 2014-12-16 */ var LogService = require('../../service/LogService'), log4js = require('log4js'), http = require('http'), logger = log4js.getLogger(), isError = function (res , error){ if(error){ res.json({ret : 1 , msg : error}); return true; } return false; }; var fs = require("fs") var path = require("path") var LogAction = { queryLogList : function (params, req , res) { var logService = new LogService(); params['endDate'] -=0; params['startDate'] -=0; params['id'] -=0; delete params.user; logService.query(params,function(err, items){ if(isError(res, err)){ return; }; res.json({ret:0, msg:"success-query", data:items }); }); }, showOfflineFiles : function (params, req , res) { if(!params.id){ res.json({ret:0, msg:"success-query", data:[] }); return } if(!/^\w+$/i.test( params.id)){ res.json({ret:0, msg:"success-query", data:[] }); return ; } var filePath = path.join(__dirname , '..' , '..' , 'offline_log' , params.id +""); if(!fs.existsSync(filePath)){ res.json({ret:0, msg:"success-query", data:[] }); return } var offlineFiles = fs.readdirSync(filePath); var offlineFilesList = []; offlineFiles.sort(function (a, b){ if(a < b ){ return 1; }else { return -1; } }) offlineFiles = offlineFiles.slice(0,50); offlineFiles.forEach(function (item){ offlineFilesList.push({ id : item }) }) res.json({ret:0, msg:"success-query", data:offlineFilesList }); }, showOfflineLog : function (params, req , res) { if(!params.fileId || !params.id){ res.json({ret:0, msg:"success-query", data:[] }); return } if(!/^\w+$/i.test( params.id)){ res.json({ret:0, msg:"success-query", data:[] }); return ; } if(!/^\w+$/i.test( params.fileId)){ res.json({ret:0, msg:"success-query", data:[] }); return ; } var filePath = path.join(__dirname , '..' , '..' , 'offline_log' ,params.id +"" , params.fileId); if(!fs.existsSync(filePath)){ res.json({ret:0, msg:"success-query", data:[] }); return } var offlineFiles = fs.readFileSync(filePath); res.json({ret:0, msg:"success-query", data:offlineFiles.toString() }); }, deleteOfflineLogConfig : function (params, req , res){ if(!params.id || !params.uin ){ res.json({ret:0, msg:"", data:{} }); return } if (global.offlineLogMonitorInfo[params.id] && global.offlineLogMonitorInfo[params.id][params.uin] ){ delete global.offlineLogMonitorInfo[params.id][params.uin] } res.json({ret:0, msg:"", data:{} }); }, getOfflineLogConfig : function (params, req , res){ if(!params.id ){ res.json({ret:0, msg:"", data:{}}); return } var result = {}; if (global.offlineLogMonitorInfo[params.id] ){ result = global.offlineLogMonitorInfo[params.id]; } res.json({ret:0, msg:"", data:result}); }, addOfflineLogConfig : function (params, req , res){ if(!params.id || !params.uin){ res.json({ret:-1, msg:"", data:{} }); return } if(!global.offlineLogMonitorInfo[params.id]){ global.offlineLogMonitorInfo[params.id] = {} } var hadAdd = false; if(!global.offlineLogMonitorInfo[params.id][params.uin]){ global.offlineLogMonitorInfo[params.id][params.uin] = true; }else { hadAdd = true; } res.json({ret:0, msg:"success-query", data:{hadAdd : hadAdd } }); }, code : function (params, req , res){ http.get(params.target , function(response){ var buffer = ''; response.on('data' , function (chunk){ buffer += chunk.toString(); }).on('end' , function (){ res.json({ret:0, msg:"success-query", data:buffer}); }); }) } }; module.exports = LogAction; ================================================ FILE: controller/action/StatisticsAction.js ================================================ /** * Created by chriscai on 2015/1/15. */ var BusinessService = require('../../service/BusinessService'), _ = require('underscore'), StatisticsService = require('../../service/StatisticsService'); var log4js = require('log4js'), logger = log4js.getLogger(); var StatisticsAction = { index: function(param, req, res) { var params = req.query, user = req.session.user; var businessService = new BusinessService(); businessService.findBusinessByUser(user.loginName, function(err, item) { res.render(param.tpl, { layout: false, user: user, index: 'statistics', statisticsTitle: param.statisticsTitle, items: item }); }); }, projectTotal: function(param, req, res) { var params = req.query, user = req.session.user; var businessService = new BusinessService(); businessService.findBusiness(function(err, items) { res.render(param.tpl, { layout: false, user: user, index: 'projectTotal', statisticsTitle: param.statisticsTitle, items: items }); }); }, queryByChart: function(param, req, res) { var statisticsService = new StatisticsService(); if (!param.projectId || isNaN(param.projectId) || !param.timeScope) { res.json({ ret: 0, msg: 'success', data: {} }); return; } statisticsService.queryByChart({ userName: param.user.loginName, projectId: param.projectId - 0, timeScope: param.timeScope - 0 }, function(err, data) { if (err) { res.json({ ret: -1, msg: "error" }); return; } res.json(data); }); }, queryByChartForAdmin: function(param, req, res) { var statisticsService = new StatisticsService(); if (!param.projectId || isNaN(param.projectId) || !param.timeScope || param.user.role != 1) { res.json({ ret: 0, msg: 'success', data: {} }); return; } statisticsService.queryByChart({ projectId: param.projectId - 0, timeScope: param.timeScope - 0 }, function(err, data) { if (err) { res.json({ ret: -1, msg: "error" }); return; } res.json(data); return; }); }, queryById: function(param, req, res) { var statisticsService = new StatisticsService(); if (!req.query.projectId || isNaN(req.query.projectId) || !req.query.startDate) { res.json({ ret: 0, msg: 'query invalid!', data: {} }); return; } //console.log(param); statisticsService.queryById({ userName: param.user.loginName, projectId: req.query.projectId - 0, startDate: new Date(param.startDate - 0) }, function(err, data) { if (err) { res.json({ ret: -1, msg: 'error', data: {} }); } else { res.json({ ret: 0, msg: 'success', data: data }); } }); } }; module.exports = StatisticsAction; ================================================ FILE: controller/action/UserAction.js ================================================ /** * Created by coverguo on 2015/1/12. */ var log4js = require('log4js'), logger = log4js.getLogger(), UserService = require('../../service/UserService'), crypto = require('crypto'), BusinessService = require('../../service/BusinessService'), isError = function (res , error){ if(error){ res.json({ret : 1 , msg : error}); return true; } return false; }; var userAction = { index: function(params , req , res){ var params = req.query, user = req.session.user; var businessService = new BusinessService(); if(user.role == 1 ){ businessService.findBusiness( function (err, item){ res.render('userManage', { layout: false, user: user, index:'manage', manageTitle: '项目用户权限' , items : item} ); }); }else { businessService.findBusinessByProjectOwner(user.loginName , function (err, item){ res.render('userManage', { layout: false, user: user, index:'manage', manageTitle: '项目用户权限' , items : item} ); }); } }, authUserManger : function (params , req , res){ var user = req.session.user; res.render('authUserManage', { layout: false, user: user, index:'manage', manageTitle: '注册用户列表' } ); }, login : function (params , req , res){ var method = req.method.toLowerCase(); if(method == "post"){ var userDao = req.models.userDao; userDao.one({ loginName : req.body.username } ,function (err , user) { if(err || !user || (crypto.createHash("md5").update(req.body.password).digest('hex') != user.password)){ res.render('login', { name:req.body.username, isUseOA : !! global.pjconfig.oos, index:"login" , message : "帐号或则密码错误"} ); }else { req.session.user = { role : user.role, id : user.id, email : user.email, loginName : user.loginName , chineseName : user.chineseName } res.redirect(req.protocol + "://" + req.get('host') + '/user/index.html'); } }); }else { res.render('login', { isUseOA : !! global.pjconfig.oos, message : "" } ); } }, register : function (params , req , res){ var method = req.method.toLowerCase(); if(method == "post"){ if( !req.body.username || !req.body.password) { res.render('register', { isUseOA : !! global.pjconfig.oos, name: req.body.username , message : "帐号密码不能为空"} ); return ; }else if( req.body.password != req.body.password2) { res.render('register', { isUseOA : !! global.pjconfig.oos, name: req.body.username , message : "密码和确认密码不一致"} ); return ; } var userDao = req.models.userDao; userDao.one({ loginName : req.body.username } ,function (err , user) { if(err){ logger.error("register user error : " + err.toString()); res.render('register', { isUseOA : !! global.pjconfig.oos, name: req.body.username , message : "系统错误,请联系管理员"} ); } else if(user){ res.render('register', {isUseOA : !! global.pjconfig.oos, name: req.body.username , message : "用户已经存在"} ); return ; } else { var newUser = { chineseName: req.body.username, role : 0 ,loginName : req.body.username ,password : crypto.createHash("md5").update(req.body.password).digest('hex')}; userDao.create(newUser , function (err) { if(err){ res.render('register', { isUseOA : !! global.pjconfig.oos, message : "系统错误,请联系管理员"} ); } res.render('register', { isUseOA : !! global.pjconfig.oos, type : "1", message : '注册成功 '} ) }); } }); }else { res.render('register', { isUseOA : !! global.pjconfig.oos, message : "" } ); } }, modify : function (params , req , res){ var params = req.query, user = req.session.user; var method = req.method.toLowerCase(); if(method == "post"){ if( !req.body.newpassword || !req.body.oldpassword) { res.render('modifyUser', { layout:false, user: user , index:'modifyUser', manageTitle: '修改密码', message : "密码不能为空"} ); return ; }if( req.body.newpassword != req.body.newpassword2) { res.render('modifyUser', { layout:false, user: user , index:'modifyUser', manageTitle: '修改密码' , message : "密码和确认密码不一致"} ); return ; }else if( req.body.oldpassword == req.body.newpassword) { res.render('modifyUser', { layout:false, user: user , index:'modifyUser', manageTitle: '修改密码' , message : "新旧密码不能一致"} ); return } var userDao = req.models.userDao; userDao.one({ loginName : user.loginName } ,function (err , user) { var newpassword = crypto.createHash("md5").update(req.body.newpassword).digest('hex') var oldpassword = crypto.createHash("md5").update(req.body.oldpassword).digest('hex') if(err){ logger.error("modifyUser user error : " + err.toString()); res.render('modifyUser', { layout:false, user: user , index:'modifyUser', manageTitle: '修改密码' , message : "系统错误,请联系管理员"} ); }else if(!user){ res.render('modifyUser', { layout:false, user: user , index:'modifyUser', manageTitle: '修改密码' , message : "用户不存在"} ); }else if (oldpassword != user.password){ res.render('modifyUser', { layout:false, user: user , index:'modifyUser', manageTitle: '修改密码' , message : "原始密码不正确"} ); }else { user.password = newpassword; user.save( function (err) { if(err){ res.render('modifyUser', { layout:false, user: user , index:'modifyUser', manageTitle: '修改密码' , message : "系统错误,请联系管理员"} ); } req.session.user = null; res.render('modifyUser', { layout:false, user: user , index:'modifyUser', manageTitle: '修改密码' , type : "1", message : '修改成功 ,3s 后跳转到登录界面'} ); }); } }); }else { res.render('modifyUser', { layout:false, user: user , index:'modifyUser', manageTitle: '修改密码'} ); } }, // addUser: function(params, cb){ // var us = new UserService(); // us.add(params,cb); // }, queryListByCondition : function (params , req , res) { var userService = new UserService(); var isAdmin = req.session.user.role == 1; //用户根据项目查询项目成员 if(params.applyId && params.role){ params.applyId -=0; params.role -=0; }else{ res.json({ret:1002, msg:"need some params"}); return; } if(params.user.role != 1){ params.userId = params.user.id; } userService.queryListByCondition(params,function(err, items){ if(isError(res, err)){ return; } res.json({ret:0, data:items, msg:"success" , isAdmin : isAdmin}); }); }, queryUserByCondition : function (params , req , res){ var userService = new UserService(); var user = req.session.user; var isAdmin = req.session.user.role == 1; if(!isAdmin){ res.json({ret:-1, data:{}, msg:"error" }); return ; } var target = {}; if(params.roleType >= 0){ target.role = params.roleType; } userService.queryUsersByCondition(target , function (err , item){ res.json({ret:0, data:item, msg:"success" }); }) }, authUser : function (params , req , res){ var userService = new UserService(); userService.update({id : params.id, role : params.role} , function (err ){ if(err){ res.json({ret:-1, msg:"error" }); }else { res.json({ret:0, msg:"success" }); } }) }, update:function(req, res){ }, remove: function(){ } }; module.exports = userAction; ================================================ FILE: controller/action/UserApplyAction.js ================================================ /** * Created by coverguo on 2015/1/12. */ var log4js = require('log4js'), logger = log4js.getLogger(), UserApplyService = require('../../service/UserApplyService'), isError = function (res , error){ if(error){ res.json({ret : 1 , msg : error}); return true; } return false; }; var userAction = { addUserApply: function(userApply, req , res){ if(userApply.userName ==""){ res.json({ret: 1002, msg: "userName为空"}); return; } userApply.createTime = new Date(); var userApplyService = new UserApplyService(); userApplyService.add(userApply,function(err, items) { if (isError(res, err)) { return; } res.json({ret: 0, msg: "success-add"}); }); }, //update:function(req, res){ // //}, remove: function(remove, req, res){ if(remove.id ==""){ res.json({ret: 1002, msg: "id为空"}); return; } var userApplyService = new UserApplyService(); userApplyService.remove(remove,function(err, items) { if (isError(res, err)) { return; } res.json({ret: 0, msg: "success-remove"}); }); }, auth: function(auth, req, res){ if(auth.id ==""){ res.json({ret: 1002, msg: "id为空"}); return; } var userApplyService = new UserApplyService(); userApplyService.auth(auth,function(err, items) { if (isError(res, err)) { return; } res.json({ret: 0, msg: "success-update"}); }); } }; module.exports = userAction; ================================================ FILE: controller/router.js ================================================ /** * @info : 页面申请路由 * @author : coverguo * @date : 2014-12-16 */ var LogAction = require('./action/LogAction'), ApplyAction = require('./action/ApplyAction'), UserAction = require("./action/UserAction"), IndexAction = require("./action/IndexAction"), StatisticsAction = require("./action/StatisticsAction"), ApproveAction = require("./action/ApproveAction"), realtimeService = require("../service/RealtimeService"), UserApplyAction = require("./action/UserApplyAction"); var _ = require("underscore"); var log4js = require('log4js'), logger = log4js.getLogger(); module.exports = function(app){ realtimeService(app); //html页面请求 app.get('/', function (req , res){ res.render('index',{} ); }); app.get('/index.html', function (req , res){ res.render('index',{} ); } ); app.get('/user/index.html', function (req , res){ IndexAction.index({} , req , res); } ); app.use('/login.html', function (req , res){ UserAction.login({}, req , res); } ); app.use('/register.html', function (req , res){ UserAction.register({}, req , res); } ); app.get('/user/apply.html', function(req, res){ var user = req.session.user; if(req.query && req.query.applyId){ ApplyAction.queryByApplyId({applyId :req.query.applyId } , function (err , apply){ res.render('apply', { layout: false, user: user, index:'apply' , apply : apply }); }); }else { res.render('apply', { layout: false, user: user, index:'apply' , apply : {} }); } }); app.get('/user/applyList.html', function(req, res){ var user = req.session.user; res.render('applyList', { layout: false, user: user, index:'manage', manageTitle: '申请项目列表'}); }); app.get('/user/userManage.html', function (req , res){ UserAction.index({} , req , res); }); app.use('/user/modifyUser.html', function (req , res){ UserAction.modify(req.param , req , res); }); app.get('/user/authUserManage.html', function (req , res){ UserAction.authUserManger(req.param , req , res); }); app.get('/user/statistics.html' , function (req , res){ StatisticsAction.index({tpl:"statistics", statisticsTitle: "日志统计"} , req , res); }); app.get('/user/realtimelog.html' , function (req , res){ IndexAction.realtime({} , req , res); }); app.get('/user/offlinelog.html' , function (req , res){ IndexAction.offline({} , req , res); }); app.get('/user/charts.html' , function (req , res){ StatisticsAction.index({tpl:"charts", statisticsTitle: "图表统计"} , req , res); }); app.get('/user/projectTotal.html' , function (req , res){ StatisticsAction.projectTotal({tpl:"projectTotal", statisticsTitle: "项目统计"} , req , res); }); app.get('/user/introduce.html' , function (req , res){ res.render('introduce', { layout: false, user: req.session.user, index:'guide', guideTitle: '使用指南'}); }); app.get('/user/monitor.html' , function (req , res){ res.render('monitor', { layout: false, user: req.session.user, index:'guide', guideTitle: '实时监控'}); }); /** * 登出 * */ app.get('/logout', function(req, res){ req.session.user = null; var homeUrl = req.protocol + "://" + req.get('host') + '/index.html'; delete req.session.user; res.redirect(homeUrl); }); // 请求路径为: controller/xxxAction/xxx.do (get || post) app.use("/",function(req, res , next){ //controller 请求action if(!/\/controller/i.test(req.url)){ next(); return ; } var url = req.url; var action = url.match(/controller\/(\w*)Action/i)[1]; var operation = url.match(/\/(\w+)\.do/i)[1]; logger.debug("the operation is: " + action + " --operation: "+ operation); //判断是get还是post请求, 获取参数params var method = req.method.toLowerCase(); var params = method =="post"? req.body : req.query; params = _.extend({} , params ); params.user = req.session.user; if( !params.user ){ res.json({ret : -2 , msg : "should login"}); return ; } //根据不同actionName 调用不同action try{ switch(action){ case "user": UserAction[operation](params,req, res);break; case "apply": ApplyAction[operation](params,req, res);break; case "approve": ApproveAction[operation](params,req, res);break; case "log" : LogAction[operation](params,req, res); break; case "userApply": UserApplyAction[operation](params,req, res);break; case "statistics" : StatisticsAction[operation](params, req, res); break; default : next(); } }catch(e){ logger.warn(e) res.send(404, 'Sorry! can not found action.'); } return; }); }; ================================================ FILE: dao/ApplyDao.js ================================================ /** * @info: ApplyDao * @author: coverGuo * @date: 2014-12-30 */ //status: 0 审核中- 1-审核通过, 2-审核失败 module.exports = function (db){ var apply = db.define("b_apply", { id : Number, userName : String, status : Number, name : String, appkey : String, url : String, blacklist : String, description : String, mail : String, createTime : Date, passTime : Date }); return apply; } ================================================ FILE: dao/ApproveDao.js ================================================ /** * @info: approveDao * @author: coverguo * @date: 2014-12-30 */ module.exports = function (db){ var approve = db.define("b_approve", { id : Number, userName : String, reply : String, createTime : Date, applyId : Number, applyStatus : Number }); return approve; } ================================================ FILE: dao/StatisticsDao.js ================================================ /** * @info: statisticsDao * @author: chriscai * @date: 2014-01-14 */ module.exports = function (db){ var Statistics = db.define("b_statistics", { id : Number, projectId : Number, startDate : Date, endDate : Date, content : String, total : Number }); return Statistics; } ================================================ FILE: dao/UserApplyDao.js ================================================ /** * @info: UserApplyDao * @author: coverguo * @date: 2014-01-15 */ module.exports = function (db){ var user_apply = db.define("b_user_apply", { userId : Number, applyId : Number, role : Number, createTime : Date }); return user_apply; }; ================================================ FILE: dao/UserDao.js ================================================ /** * @info: userDao * @author: coverGuo * @date: 2014-12-30 */ module.exports = function (db){ var user = db.define("b_user", { id : Number, loginName : String, chineseName : String, role : Number, email : String, password : String }); return user; } ================================================ FILE: db/create.sql ================================================ -- -------------------------------------------------------- -- 主机: 127.0.0.1 -- 服务器版本: 5.6.21-log - MySQL Community Server (GPL) -- 服务器操作系统: Win64 -- HeidiSQL 版本: 9.1.0.4867 -- -------------------------------------------------------- /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET NAMES utf8mb4 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; CREATE DATABASE IF NOT EXISTS `badjs` /*!40100 DEFAULT CHARACTER SET latin1 */; USE `badjs`; -- 导出 表 betterjs.b_statistics 结构 DROP TABLE IF EXISTS `b_statistics`; CREATE TABLE IF NOT EXISTS `b_statistics` ( `id` int(11) NOT NULL AUTO_INCREMENT, `projectId` int(11) DEFAULT NULL, `startDate` datetime DEFAULT NULL, `endDate` datetime DEFAULT NULL, `content` longtext COLLATE utf8_unicode_ci, `total` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- -- Table structure for table `b_apply` -- DROP TABLE IF EXISTS `b_apply`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `b_apply` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userName` varchar(25) COLLATE utf8_unicode_ci NOT NULL, `status` int(11) NOT NULL, `name` varchar(25) COLLATE utf8_unicode_ci NOT NULL, `appkey` varchar(200) COLLATE utf8_unicode_ci NOT NULL, `url` varchar(200) COLLATE utf8_unicode_ci NOT NULL, `blacklist` text COLLATE utf8_unicode_ci , `description` text COLLATE utf8_unicode_ci, `mail` varchar(80) COLLATE utf8_unicode_ci DEFAULT NULL, `createTime` datetime DEFAULT NULL, `passTime` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Table structure for table `b_approve` -- DROP TABLE IF EXISTS `b_approve`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `b_approve` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userName` varchar(20) COLLATE utf8_unicode_ci NOT NULL, `reply` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `applyId` int(11) NOT NULL, `createTime` timestamp NULL DEFAULT NULL, `applyStatus` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `applyId` (`applyId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Table structure for table `b_user` -- DROP TABLE IF EXISTS `b_user`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE IF NOT EXISTS `b_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `loginName` varchar(100) COLLATE utf8_unicode_ci NOT NULL, `chineseName` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, `role` int(11) NOT NULL, `email` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, `password` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- /*!40000 ALTER TABLE `b_user` DISABLE KEYS */; INSERT INTO `b_user` (`id`, `loginName`, `chineseName`, `role`, `email`, `password`) VALUES (1, 'admin', 'admin', 1, NULL, '21232f297a57a5a743894a0e4a801fc3'); -- -- Table structure for table `b_user_apply` -- DROP TABLE IF EXISTS `b_user_apply`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `b_user_apply` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userId` int(11) NOT NULL, `applyId` int(11) NOT NULL, `role` int(1) NOT NULL DEFAULT '0', `createTime` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `applyId_idx` (`applyId`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; -- Dump completed on 2015-01-20 9:43:38 ================================================ FILE: gulpfile.js ================================================ var gulp = require("gulp"); var del = require('del'); var webpack = require("webpack"); gulp.task('clean', function(cb) { del(['public'], cb); }); gulp.task('default', function() { }); gulp.task("webpack", function(callback) { // run webpack webpack({ }, function(err, stats) { if(err) throw new gutil.PluginError("webpack", err); gutil.log("[webpack]", stats.toString({ // output options })); callback(); }); }); ================================================ FILE: model/Apply.js ================================================ /** * *@info: Apply 数据实体 * @author: coverGuo * @date: 2014-12-30 */ var _ = require('underscore')._; /** * status 0 审核中 , 1 通过 , 2 不通过 * @param args * @constructor */ var Apply = function (args){ if(!args){ return ; } _.defaults( this , _.pick(args , 'userName' ,'name' , 'id' , 'description' , 'main', 'createTime' , 'passTime' ) ); } Apply.prototype = { userName : undefined, name : undefined, url : undefined, status : undefined, id : undefined, mail : undefined, description : undefined, createTime : undefined, passTime : undefined } Apply.STATUS_APPLYING = 0 ; // 审核中 Apply.STATUS_PASS = 1 ; // 通过 Apply.STATUS_FAIL = 2 ; // 拒绝 module.exports = Apply; ================================================ FILE: model/Approve.js ================================================ /** * *@info: Approve 数据实体 * @author: coverGuo * @date: 2014-12-30 */ var _ = require('underscore')._; var Approve = function (args){ if(!args){ return ; } _.defaults(this , _.pick(args , 'id' ,'reply' , 'applyId' )); } Approve.prototype = { id : undefined, reply : undefined, applyId : undefined, userName : undefined, createTime : undefined, applyStatus : undefined } module.exports = Approve; ================================================ FILE: model/User.js ================================================ /** * *@info: User 数据实体 * @author: coverGuo * @date: 2014-12-30 */ var _ = require("underscore"); /** * role : 0 普通成员 1 管理员 * @param args * @constructor User */ var User = function(args){ if(!args){ return; } _.defaults(this, _.pick(args, 'id', 'loginName', 'chineseName', 'role')); } User.prototype = { id : undefined, loginName : undefined, chineseName: undefined, role : undefined } module.exports = User; ================================================ FILE: model/UserApply.js ================================================ /** * *@info: userApply 数据实体 * @author: coverGuo * @date: 2015-01-15 */ var _ = require('underscore')._; var User_Apply = function (args){ if(!args){ return ; } _.defaults(this , _.pick(args , 'userId' ,'applyId' ,'createTime', 'role' )); } User_Apply.prototype = { userId : undefined, applyId : undefined, createTime : undefined, role : undefined } module.exports = User_Apply; ================================================ FILE: oos/demo/demooos.js ================================================ var oa = require('yourOaModuel'); var crypto = require('crypto'); var log4js = require('log4js'), logger = log4js.getLogger(); var isError = function(res, error) { if (error) { res.json({ ret: 1, msg: error }); return true; } return false; }; module.exports = function(req, res, next) { var params = req.query, user = req.session.user, userDao = req.models.userDao; req.indexUrl = req.protocol + "://" + req.get('host') + '/user/index.html'; //跳转到 oa 进行登录 if (/^\/oalogin/i.test(req.url)) { var redirectUrl = req.indexUrl; res.redirect('http://youroalogin.com?url=' + redirectUrl); return; } //判断是否有ticket ,一般的oa统一登录都会有用 ticket进行统一登录 if (params && params.ticket) { // 调用你的oa 组件,进行登录处理 oa.passport(params.ticket, function(result) { if (result) { // 存入session ,用于判断此用户已经登录 user = req.session.user = { loginName: result.LoginName, chineseName: result.ChineseName, role: 0 }; userDao.one({ loginName: result.LoginName }, function(err, dbUser) { if (isError(res, err)) { return; } // 用户没有登录 badjs, 进行创建用户信息 if (!dbUser) { req.session.user.email = user.loginName + GLOBAL.pjconfig.email.emailSuffix; req.session.user.password = crypto.createHash("md5").update(user.loginName).digest('hex'); userDao.create(req.session.user, function(err, result) { if (isError(res, err)) { return; } req.session.user.role = result.role; req.session.user.id = result.id; logger.info("New User:" + JSON.stringify(req.session.user) + "insert into db-badjs"); next(); }); } else { // 已经登陆过了,将更加详细的信息注入到session中 logger.info("Old User:" + JSON.stringify(req.session.user)); req.session.user.role = dbUser.role; req.session.user.id = dbUser.id; req.session.user.email = dbUser.email; // 用户没有登陆过,但是在 http://badjs.server.com/user/userManage.html 创建过id // 讲oa 回传的信息存入数据库 if (!dbUser.chineseName) { dbUser.chineseName = result.ChineseName; dbUser.save(function(err, result) {}); } next(); } }); } else { res.send(403, 'Sorry! you can not see that.'); } }); } else { next(); } }; ================================================ FILE: package.json ================================================ { "name": "badjs-web", "version": "0.1.0", "description": "", "main": "app.js", "dependencies": { "body-parser": "^1.9.0", "connect-flash": "^0.1.1", "cookie-parser": "^1.3.3", "crypto": "0.0.3", "ejs-loader": "^0.2.1", "emailjs": "0.3.16", "expose-loader": "^0.6.0", "express": "^4.9.8", "express-micro-tpl": "^0.3.1", "express-session": "^1.9.0", "jquery": "^2.1.3", "log4js": "^0.6.2", "map-stream": "0.0.5", "moment": "^2.10.2", "mysql": "^2.11.1", "node-highcharts-exporting": "~0.0.9", "orm": "^3.2.0", "orm-transaction": "0.0.2", "passport": "^0.2.1", "passport-local": "^1.0.0", "request": "^2.63.0", "serve-static": "^1.7.1", "underscore": "1.5.2", "url-join": "0.0.1", "url-valid": "^0.3.0", "ws": "~1.0.0", "axon": "^2.0.0" }, "devDependencies": { "webpack": "~1.8.5", "glob": "~5.0.5" }, "scripts": {}, "author": "", "license": "MIT" } ================================================ FILE: project.debug.json ================================================ { "host" : "http://badjs.server.com/", "port": 8081, "mysql" : { "url" : "mysql://root:root@localhost:3306/badjs" }, "storage" : { "errorMsgTopUrl" : "http://127.0.0.1:9000/errorMsgTop", "queryUrl" : "http://127.0.0.1:9000/query" }, "acceptor": { "pushProjectUrl" : "http://127.0.0.1:9001/getProjects" }, "openapi": { "pushProjectUrl" : "http://127.0.0.1:9002/getProjects" }, "mq": { "url": "tcp://127.0.0.1:10000", "subscribe": "badjs", "module": "axon" }, "email": { "homepage": "http://badjs.server.com/user/index.html", "from": "noreply-badjs@demo.com", "emailSuffix" : "@demo.com", "smtp": "smtp.demo.com", "smtpUser": "username", "smtpPassword":"password", "time": "09:00:00", "top": 20, "module": "email" } } ================================================ FILE: project.json ================================================ { "host" : "http://badjs.server.com/", "port": 8081, "mysql" : { "url" : "mysql://root:root@localhost:3306/badjs" }, "storage" : { "errorMsgTopUrl" : "http://127.0.0.1:9000/errorMsgTop", "queryUrl" : "http://127.0.0.1:9000/query" }, "acceptor": { "pushProjectUrl" : "http://127.0.0.1:9001/getProjects" }, "openapi": { "pushProjectUrl" : "http://127.0.0.1:9002/getProjects" }, "mq": { "url": "tcp://127.0.0.1:10000", "subscribe": "badjs", "module": "axon" }, "email": { "homepage": "http://badjs.server.com/user/index.html", "from": "noreply-badjs@demo.com", "emailSuffix" : "@demo.com", "smtp": "smtp.demo.com", "smtpUser": "username", "smtpPassword":"password", "time": "09:00:00", "top": 20, "module": "email" } } ================================================ FILE: retCode.txt ================================================ ret结果码记录: 0 : 成功 1001: 没有登录 1002: 缺少参数 或参数错误 1003: 权限不足 1004: 查询为空 ================================================ FILE: service/ApplyService.js ================================================ /** * Created by coverguo on 2015/01/08. */ var http = require('http'); var log4js = require('log4js'), logger = log4js.getLogger(); var UserApplyService = require('./UserApplyService'); var LogService = require('./LogService'); var ApplyService = function() { this.applyDao = global.models.applyDao; this.userApplyDao = global.models.userApplyDao; }; var logService = new LogService(); var pushProject = function(from) { logService.pushProject(function(err) { if (err) { logger.warn('push project error ' + err); } else { logger.info('push project success from ' + from + ' .'); } }); } ApplyService.prototype = { queryListByAdmin: function(target, callback) { this.applyDao.find(["createTime", "Z"], function(err, items) { if (err) { callback(err); return; } callback(null, items); }); }, queryListByUser: function(target, callback) { this.applyDao.find({ userName: target.user.loginName }, ["createTime", "Z"], function(err, items) { if (err) { callback(err); return; } callback(null, items); }); }, queryListBySearch: function(searchParam, callback) { this.applyDao.find(searchParam, ["createTime", "Z"], function(err, items) { if (err) { callback(err); return; } callback(null, items); }); }, add: function(target, callback) { var self = this; var userId = target.user.id; this.applyDao.create(target, function(err, newApply) { if (err) { callback(err); return; } logger.debug("Insert into b_apply success! target1: " + newApply.id); //创建项目的即为管理员 故role ==1 var userApply = { userId: userId, applyId: newApply.id, role: 1, createTime: new Date() }; self.userApplyDao.create(userApply, function(err, items) { if (err) { callback(err); return; } callback(null); }) }); }, remove: function(target, callback) { this.applyDao.one({ id: target.id }, function(err, apply) { // SQL: "SELECT * FROM b_apply WHERE name = 'xxxx'" if (err || !apply) { callback(new Error("can not found apply , id" + target.id)); return; } for (var key in target) { apply[key] = target[key]; }; apply.remove(function(err) { if (err) { console.error("remove error : " + err.toString()) throw err; } var userApplyService = new UserApplyService(); userApplyService.removeByApplyId({ applyId: apply.id }, function(err) { if (err) { callback(null); } else { callback(null); } }); pushProject('remove'); }); }); }, update: function(target, callback) { this.applyDao.one({ id: target.id }, function(err, apply) { // SQL: "SELECT * FROM b_apply WHERE name = 'xxxx'" for (var key in target) { apply[key] = target[key]; }; apply.save(function(err) { // err.msg = "under-age"; callback(null, { ret: 0, msg: "success update" }); // update project.db pushProject('update'); }); }); }, queryById: function(target, callback) { this.applyDao.one({ id: target.id }, function(err, apply) { callback(err, apply); }); } } module.exports = ApplyService; ================================================ FILE: service/ApproveService.js ================================================ /** * Created by coverguo on 2015/01/12. */ var http = require('http'); var log4js = require('log4js'), logger = log4js.getLogger(); var ApproveService = function() { this.approveDao = global.models.approveDao; this.applyDao = global.models.applyDao; }; ApproveService.prototype = { query: function(target, callback) { //管理员 if (target.user.role == 1) { this.approveDao.all({}, function(err, items) { if (err) { callback(err); return; } callback(null, { ret: 0, msg: "success", data: items }); }); } else { this.approveDao.find({ userName: target.user.loginName }, function(err, items) { if (err) { callback(err); return; } callback(null, { ret: 0, msg: "success", data: items }); }); } }, add: function(target, callback) { var self = this; this.approveDao.create(target, function(err, items) { if (err) { callback(err); return; } logger.info("Insert into b_approve success! target: ", target); // 改变申请表状态 var apply = { id: parseInt(target.applyId, 10), status: parseInt(target.applyStatus, 10) }; if (target.applyStatus == 1) { apply.passTime = target.createTime; } self.update(apply, function(err, data) { if (err) { callback(err); return; } logger.info("Update b_apply success! apply: ", apply); callback(null, { ret: 0, msg: "success add" }); }) }); }, remove: function(target, callback) { }, update: function(target, callback) { this.applyDao.one({ id: target.id }, function(err, apply) { // SQL: "SELECT * FROM b_apply WHERE name = 'xxxx'" for (key in target) { apply[key] = target[key]; } apply.save(function(err) { if (err) { callback(err); return; } callback(null); }); }); } } module.exports = ApproveService; ================================================ FILE: service/BusinessService.js ================================================ /** * Created by chriscai on 2014/12/17. */ var Apply = require('../model/Apply'); var _ = require('underscore'); var BusinessService = function() { this.db = global.models.db; }; BusinessService.prototype = { findBusinessByUser: function(userName, callback) { var string = "select a.id, u.loginName, u.chineseName, ua.role, a.name " + "from b_user as u join b_user_apply as ua on(ua.userId = u.id) " + "join b_apply as a on (a.id =ua.applyId) " + "where a.status=? and u.loginName=? "; var condition = [Apply.STATUS_PASS]; condition.push(userName); // console.log(string); this.db.driver.execQuery(string, condition, function(err, data) { if (err) { callback(err); return; } callback(null, data); }); }, findBusinessByProjectOwner: function(userName, callback) { var string = "select a.id, u.loginName, u.chineseName, ua.role, a.name " + "from b_user as u join b_user_apply as ua on(ua.userId = u.id) " + "join b_apply as a on (a.id =ua.applyId) " + "where a.status=? and u.loginName=? and ua.role =1"; var condition = [Apply.STATUS_PASS]; condition.push(userName); // console.log(string); this.db.driver.execQuery(string, condition, function(err, data) { if (err) { callback(err); return; } callback(null, data); }); }, findBusiness: function(callback) { var string = "select a.id, a.url, a.blacklist , a.appkey, u.loginName, u.chineseName, a.name " + "from b_apply a join b_user u on (a.userName=u.loginName) " + "where a.status=?"; var condition = [Apply.STATUS_PASS]; // console.log(string); this.db.driver.execQuery(string, condition, function(err, data) { if (err) { return callback(err); } _.each(data, function(value, ke) { value.role = 1; }); callback(null, data); }); } }; module.exports = BusinessService; ================================================ FILE: service/EmailService.js ================================================ /* global global, global, module, __dirname, Buffer */ /** * Created by kaelyang on 2015/5/19. */ var fs = require("fs"); var http = require('http'); var path = require("path"); var _ = require('underscore'); var logger = require('log4js').getLogger(); var UserService = require('./UserService'); var dateFormat = require("../utils/dateFormat"); var exporting = require('node-highcharts-exporting'); var StatisticsService = require('./StatisticsService'); var sendEmail = require("../utils/" + global.pjconfig.email.module); var DAY_LENGTH = 30; var EmailService = function() { this.userService = new UserService(); this.statisticsService = new StatisticsService(); this.top = parseInt(global.pjconfig.email.top, 10) || 20; this.from = global.pjconfig.email.from || "noreply-badjs@tencent.com"; this.homepage = global.pjconfig.email.homepage; }; var getYesterday = function() { var date = new Date(); date.setDate(date.getDate() - 1); date.setHours(0, 0, 0, 0); return date; }; var setChartX = function(number) { var days = []; var nowDay = new Date() - 0; for (var i = number; i > 0; i--) { var day = nowDay - i * 1000 * 60 * 60 * 24; days.push(dateFormat(new Date(day), 'MM-dd')); } return days; }; var getImageData = function(name, data) { var totalArray = []; var categories = setChartX(DAY_LENGTH); for (var i = 0; i < DAY_LENGTH; i++) { totalArray.push(0); } function whichDayIndex(day1) { for (var i = 0, len = categories.length; i < len; i++) { if (day1 == categories[i]) { return i; } } return false; } _.forEach(data, function(value, key) { var index = whichDayIndex(dateFormat(new Date(value.startDate), 'MM-dd')); totalArray[index] = value.total; }); return { data: { width: 800, title: { text: "The last " + DAY_LENGTH + " days line charts" }, xAxis: { categories: categories }, yAxis: { min: 0, title: { text: 'Total' } }, series: [{ data: totalArray, name: "-" }] } }; }; var encodeHtml = function(str) { return (str + '').replace(/&/g, '&').replace(//g, '>').replace(/\x60/g, '`').replace(/\x27/g, ''').replace(/\x22/g, '"').replace(/\u0000/g, ' '); }; EmailService.prototype = { render: function(data, imagePath) { var that = this; data = data || {}; var html = []; html.push(''); html.push('

【BadJS日报邮件】 ' + data.title + '

'); that.homepage && html.push('

日志查看点这: {{homepage}}

' .replace(/{{homepage}}/g, that.homepage)); var content = data.content; if (content && content.length) { if (imagePath) { html.push('

最近' + DAY_LENGTH + '天图表统计

'); html.push('

'); } html.push(''); var total_top = 0; var index = 0; content.forEach(function(v) { v = typeof v === 'object' ? v : null; if (v) { html.push('' .replace(/{{index}}/g, index + 1) .replace(/{{times}}/g, v.total) .replace(/{{desc}}/g, encodeHtml(v.title)) .replace(/{{bgc}}/g, index % 2 ? '#fff' : '#eee') ); total_top += v.total; ++index; } }); html.push('
#出现次数错误内容
{{index}}{{times}}{{desc}}
'); var total = data.total; total > 0 && html.push('

共 {{total}} 条记录, Top {{top}} 占 {{per}}.

' .replace(/{{total}}/g, total) .replace(/{{top}}/g, that.top) .replace(/{{per}}/g, (total_top * 100 / total).toFixed(2) + '%') ); } else { html.push('

暂无数据

'); } html.push(''); return html.join(''); }, queryAll: function(isRetry, sendObject) { var that = this; that.date = getYesterday(); logger.info('Send mail query all start'); that.userService.queryListByCondition({ applyId: -1, role: -1 }, function(err, userlist) { if (err) { return logger.error('Send email userService queryListByCondition error'); } else { var orderByApplyId = {}; userlist.forEach(function(v) { // 兼容没有登陆过的用户,自动拼接 邮箱后缀 if (!v.email) { v.email = v.loginName + global.pjconfig.email.emailSuffix; } if (orderByApplyId[v.applyId]) { orderByApplyId[v.applyId].push(v); } else { orderByApplyId[v.applyId] = [v]; } }); var count = 0 ; for (var applyId in orderByApplyId) { (function(users, applyId) { var to_list = []; // 收件方 var cc_list = []; // 抄送方 var name = ''; users.forEach(function(v) { v.role === 0 ? cc_list.push(v.email) : to_list.push(v.email); name = v.name; }); // jshint ignore:line // 测试代码 if (sendObject) { if (sendObject.sendId && sendObject.sendId != applyId) { return; } if (sendObject.sendToList.length) { to_list = sendObject.sendToList; } cc_list = []; } that.statisticsService.queryById({ top: that.top, projectId: applyId, startDate: that.date }, function(err, data) { if (err) return logger.error('Send email statisticsService queryById error ' + applyId); if (data && data.length > 0) { that.statisticsService.queryByChart({ projectId: applyId, timeScope: 2 }, function(err, chartData) { if (err || chartData.data.length <= 0) { that.sendEmail({ to: to_list, cc: cc_list, title: name }, data[0]); } else { count ++ ; setTimeout(function (){ exporting(getImageData(name, chartData.data), function(err, image) { if (err) { logger.info("generate image error " + err.toString() + ", id =" + applyId); that.sendEmail({ to: to_list, cc: cc_list, title: name }, data[0]); } else { var imagePath = "static/img/tmp/" + (new Date - 0 + applyId) + ".png"; fs.writeFile(path.join(__dirname, "..", imagePath), new Buffer(image, 'base64'), function() { that.sendEmail({ to: to_list, cc: cc_list, title: name, imagePath: imagePath }, data[0]); }); } }); }, 1000 * count) } }); } else { logger.error('Send email data format error by ' + applyId ); } }); // jshint ignore:line })(orderByApplyId[applyId], applyId); // jshint ignore:line } } }); // if( isRetry === undefined ? true : !!isRetry) { setTimeout(function() { that.queryAll(); }, 86400000); // } }, sendEmail: function(emails, data) { var title = "【BadJS 日报 " + dateFormat(this.date, "yyyy-MM-dd") + "】- " + emails.title; data.title = emails.title; var content = this.render(data, emails.imagePath); sendEmail(this.from, emails.to, emails.cc, title, content); }, start: function() { var that = this; var date = new Date(); date.setDate(date.getDate() + 1); var time = global.pjconfig.email.time.toString().split(':'); date.setHours(parseInt(time[0], 10) || 9, parseInt(time[1], 10) || 0, parseInt(time[2], 10) || 0, 0); var timeDiff = date.valueOf() - (new Date()).valueOf(); setTimeout(function() { that.queryAll(); }, timeDiff); logger.info('Email service will start after: ' + timeDiff); } }; module.exports = EmailService; ================================================ FILE: service/LogService.js ================================================ /* global global */ /** * Created by chriscai on 2014/12/16. */ var http = require('http'); var log4js = require('log4js'), BusinessService = require('./BusinessService'), _ = require('underscore'), logger = log4js.getLogger(); var request = require("request"); var LogService = function() { /* if(global.DEBUG){ this.queryUrl = 'http://localhost:9000/query'; }else { this.queryUrl = 'http://10.143.132.205:9000/query'; this.pushProjectUrl = 'http://10.143.132.205:9001/getProjects'; } */ this.queryUrl = global.pjconfig.storage.queryUrl; this.pushProjectUrl = global.pjconfig.acceptor.pushProjectUrl; this.pushProjectUrl2 = global.pjconfig.openapi.pushProjectUrl; // this.url = 'http://127.0.0.1:9000/query'; logger.debug('query url : ' + this.queryUrl); }; LogService.prototype = { query: function(params, callback) { var startDate = new Date; var strParams = ''; for (var key in params) { if (key == 'index') { strParams += key + "=" + params[key] + "&"; } else { strParams += key + "=" + encodeURIComponent(JSON.stringify(params[key])) + "&"; } } strParams += '_=1'; logger.debug('query param : ' + strParams); http.get(this.queryUrl + '?' + strParams, function(res) { var buffer = ''; res.on('data', function(chunk) { buffer += chunk.toString(); }).on('end', function() { try { callback(null, JSON.parse(buffer)); } catch (e) { callback(e); } logger.info('query log spend : ' + (new Date - startDate) + "ms by " + params.id); }); }).on('error', function(err) { logger.warn('error :' + err); callback(err); }); }, pushProject: function(callback) { var self = this; callback || (callback = function() {}); var businessService = new BusinessService(); var push = function() { businessService.findBusiness(function(err, item) { var projectsInfo = {}; _.each(item, function(value) { try{ value.blacklist = JSON.parse(value.blacklist || {}) }catch(e){ value.blacklist = {} } projectsInfo[value.id] = {id : value.id , url : value.url , blacklist : value.blacklist , appkey : value.appkey}; }); var result = [0, 0]; var resultCall = function() { if (result[0] < 0 && result[1] < 0) { callback(new Error("error")); } else if (result[0] > 0 && result[1] > 0) { callback(); } }; request.post(self.pushProjectUrl, { form: { projectsInfo: JSON.stringify(projectsInfo), auth: "badjsAccepter" } }, function(err) { if (err) { logger.warn('push projectIds to acceptor error :' + err); result[0] = -1; } else { logger.info('push projectIds to acceptor success'); result[0] = 1; } resultCall(); }); request.post(self.pushProjectUrl2, { form: { projectsInfo: JSON.stringify(projectsInfo), auth: "badjsOpen" } }, function(err) { if (err) { logger.warn('push projectIds to open error :' + err); result[1] = -1; } else { logger.info('push projectIds to openapi success'); result[1] = 1; } resultCall(); }); }); }; push(); } }; module.exports = LogService; ================================================ FILE: service/OfflineLogService.js ================================================ /** * Created by chriscai on 2015/4/29. */ var fs = require("fs"); var http = require("http"); var path = require("path"); var bodyParser = require('body-parser'); var express = require('express'); var app = express(); global.offlineLogMonitorInfo = {} var log4js = require('log4js'), logger = log4js.getLogger(); var offlineLogMonitorPath = path.join(__dirname , '..' , 'offline_log' , "offline_log_monitor.db"); try{ global.offlineLogMonitorInfo = JSON.parse( fs.readFileSync(offlineLogMonitorPath).toString()); logger.info("offline_log_monitor.db success " ) }catch(e){ logger.error("offline_log_monitor.db error " , e) } setInterval(function (){ fs.writeFileSync(offlineLogMonitorPath , JSON.stringify(global.offlineLogMonitorInfo)); },3600000 ) app.use(bodyParser.urlencoded({ extended: false })); app.post("/offlineLogReport" , function (req, res){ var param = req.body; if(param && param.offline_log){ try{ var offline_log = JSON.parse(param.offline_log); if(!/[\w]{1,7}/.test(offline_log.id) ){ throw new Error("invalid id " + offline_log.id) } global.models.applyDao.one({ id: offline_log.id }, function(err, apply) { if(!apply || err || apply.status != 1){ logger.info('invaild offlineLog id: ' + offline_log.id); return ; } if(!/^\w+$/i.test( offline_log.id )){ logger.info('error log_id: ' + offline_log.id); return ; } var filePath = path.join(__dirname , '..' , 'offline_log' , offline_log.id +""); var fileName = offline_log.uin +"_"+ offline_log.startDate + "_" + offline_log.endDate; if(!fs.existsSync(filePath)){ fs.mkdirSync(filePath) } fs.writeFile(path.join(filePath , fileName ) , param.offline_log, function(err) { if (err) { logger.info('write offline log error', err); throw err; } }); logger.info('get offline log : ' + path.join(filePath , fileName )); }); }catch(e){ logger.warn("invaild offlineLog error "+ e); } } res.end(); }) app.use("/offlineLogCheck" , function (req, res){ var param = req.query; if(param.id && param.uin && global.offlineLogMonitorInfo[param.id] && global.offlineLogMonitorInfo[param.id][param.uin]){ delete global.offlineLogMonitorInfo[param.id][param.uin] logger.info('should download offline log: ' + (param.id + "_" + param.uin)); res.end("true") }else { res.end("false") } }) /** * dispatcher * @returns {Stream} */ module.exports = function () { logger.info("offline service start ok...") app.listen(9010); }; ================================================ FILE: service/RealtimeService.js ================================================ /** * Created by chriscai on 2015/4/29. */ var ProcessorThread = require("../service/worker/ProcessorPool"); var WebSocketServer = require('ws').Server; var http = require("http"); var path = require("path"); var log4js = require('log4js'), logger = log4js.getLogger(); /** * dispatcher * @returns {Stream} */ module.exports = function (app) { logger.info("starting mq ..." ); var server = http.createServer(app); app.listen = function(){ return server.listen.apply(server, arguments) }; var webSocketServer = new WebSocketServer({ server: server, path: "/ws/realtimeLog" }); ProcessorThread.createPool(); webSocketServer.on('connection', function (ws) { try{ logger.info("one client connected , ip: " + ws._socket.remoteAddress) }catch(e){} createProcessor(ws); }); webSocketServer.on('close', function (ws) { try{ logger.info("one client closed , ip: " + ws._socket.remoteAddress) }catch(e){} }); var createProcessor = function (ws){ ProcessorThread.getProcessor().start({wbClient : ws}); } }; ================================================ FILE: service/StatisticsService.js ================================================ /** * Created by chriscai on 2015/1/12. */ var http = require('http'); var log4js = require('log4js'), Apply = require('../model/Apply'), http = require('http'), _ = require('underscore'), logger = log4js.getLogger(), ORM = require("orm"); var dateFormat = function(date, fmt) { var o = { "M+": date.getMonth() + 1, //月份 "d+": date.getDate(), //日 "h+": date.getHours(), //小时 "m+": date.getMinutes(), //分 "s+": date.getSeconds(), //秒 "q+": Math.floor((date.getMonth() + 3) / 3), //季度 "S": date.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)); } for (var k in o) { if (new RegExp("(" + k + ")").test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); } } return fmt; }; var StatisticsService = function() { this.statisticsDao = global.models.statisticsDao; this.applyDao = global.models.applyDao; //this.triggerUrl = global.pjconfig.storage.errorMsgTopCacheUrl; this.url = global.pjconfig.storage.errorMsgTopUrl; logger.debug('query url : ' + this.url); }; StatisticsService.prototype = { queryById: function(param, callback) { this.statisticsDao.find({ projectId: param.projectId, startDate: dateFormat(param.startDate, 'yyyy-MM-dd hh:mm:ss') }, function(err, items) { if (err) { callback(err); return; } try { if (items[0]) { items[0].content = JSON.parse(items[0].content); if (param.top) { items[0].content = items[0].content.slice(0, param.top); } } } catch (e) { callback(e); logger.error("queryById error : " + param.projectId + ", error :" + e.toString()); return; } callback(null, items); }); }, queryByChart: function(param, callback) { //筛选参数 var s_params = {}; if (param.projectId != -1) { s_params.projectId = param.projectId; } //时间开始范围 7天内 或者1个月内 var oneDay = 1000 * 60 * 60 * 24; var day = param.timeScope == 1 ? 7 : 30; param.startTime = new Date() - oneDay * day; param.startTime = dateFormat(new Date(param.startTime), 'yyyy-MM-dd'); if (global.DEBUG) { logger.debug("query start time is " + param.startTime); } this.statisticsDao.find(s_params) .only("endDate", "startDate", "projectId", "id", "total") .where("startDate >=?", [param.startTime]) .all(function(err, items) { if (err) { callback(err); return; } callback(null, { ret: 0, msg: "success", data: items }); }).where(function() { }); }, fetchAndSave: function(id, startDate, cb) { var self = this; http.get((this.url + '?id=' + id + '&startDate=' + (startDate - 0)), function(res) { var buffer = ''; res.on('data', function(chunk) { buffer += chunk.toString(); }).on('end', function() { var saveModel = {}; try { //replace emoji to empty , mysql unsupported emoji code buffer=buffer.replace(/\ud83d[\udc00-\udfff]/gi , "") var result = JSON.parse(buffer); _.forEach(result.item, function(value, key) { value.title = value._id; delete value._id; }); saveModel = { startDate: new Date(result.startDate), endDate: new Date(result.endDate), content: JSON.stringify(result.item), projectId: id, total: result.pv }; } catch (err) { logger.error('parse statistic result error(id='+id+') :' + err); saveModel = { startDate: startDate, endDate: new Date(+startDate + 86400000-1), content: "[]", projectId: id, total: 0 }; } self.statisticsDao.create(saveModel, function(err, items) { if (err) { logger.error("Insert into b_statistics error(id=", id + ") : " + err); } logger.info("Insert into b_statistics success(id=", id + ") : "); cb && cb(err); }); }); }).on('error', function(err) { logger.error('error :' + err); }); }, //triggerStorageCache: function(ids, startDate, cb) { // http.get((this.triggerUrl + '?ids=' + ids + '&startDate=' + (startDate - 0)), function(res) { // // res.on("end" , function (){ // cb(); // // }); // }).on('error', function(err) { // cb(err); // logger.error('triggerStorageCache error :' + err); // }); //}, startMonitor: function() { var self = this; var getFetchDate = function() { var tomorrow = new Date(nowDate); tomorrow.setHours(2, 0, 0, 0); tomorrow.setDate(tomorrow.getDate() + 1); return tomorrow; }; var getStartDay = function() { var startDate = new Date(nowDate); startDate.setHours(0, 0, 0, 0); return startDate; }; var nowDate = new Date; var targetDate = getFetchDate(); var startTimeout = function() { var afterDate = targetDate - nowDate; // after date 有误,取消循环 if (isNaN(afterDate) || afterDate < 1000 * 60 * 60) { logger.info("afterDate error : targetDate" + targetDate + " , now:" + nowDate); return; } setTimeout(function() { var startDate = getStartDay(); self.applyDao.find({ status: Apply.STATUS_PASS }, function(err, item) { if (err) { logger.error("find apply error : " + err); } var ids = "0"; _.each(item, function(value, key) { ids += "_" + value.id; }); //self.triggerStorageCache(ids, startDate, function(err) { // logger.info("trigger success and after 5400000s fetch result"); //if (!err) { // setTimeout(function() { logger.info("start fetching result ... "); var count = 0; _.each(item, function(value, key) { setTimeout(function (){ self.fetchAndSave(value.id, startDate); }, count * 500) count ++; }); //}, 5400000); // 1个半小时候后,拉取统计 //} //}); nowDate = new Date(); targetDate = getFetchDate(); startTimeout(); }); }, afterDate); logger.info("after " + ((afterDate) / 1000) + "s will fetch again "); }; startTimeout(); } }; module.exports = StatisticsService; ================================================ FILE: service/UserApplyService.js ================================================ /** * Created by coverguo on 2015/01/15. */ var http = require('http'), log4js = require('log4js'), logger = log4js.getLogger(); var userApplyService = function (){ this.userApplyDao = global.models.userApplyDao; this.userDao = global.models.userDao; }; userApplyService.prototype = { query : function (target , callback){ }, add: function(target, callback){ var self = this; var userApply ={ applyId : target.applyId, role : 0, createTime : new Date() }; this.userDao.one({loginName:target.userName}, function(err, item){ if(err){ callback(err); return; } if(item){ userApply.userId = item.id; self.userApplyDao.create(userApply , function (err , items){ if(err){ callback(err); return; } logger.info("Insert into b_user_apply success! target1: " + items.id); callback(null); return; }); }else { var newUser = { loginName : target.userName, role : 0, createTime : new Date() }; self.userDao.create(newUser,function(err){ if(err){ callback(err); return; } logger.info("Insert into b_user success! "); self.add(target, callback); }) } }) }, removeByApplyId : function(target, callback){ this.userApplyDao.find({applyId: target.applyId}).remove( function (err) { if(err ){ callback(err); }else { callback(null); } }) }, remove : function(target, callback){ this.userApplyDao.one({id: target.id}, function (err, item) { if(err){ callback(err); return; } item.remove(function(err) { if (err) { callback(err); return; } if (global.DEBUG) { logger.info("remove success item: " + item); } callback(null); }); }) }, auth : function(target, callback){ this.userApplyDao.one({id: target.id}, function (err, item) { if(err){ callback(err); return; } if(item.role == 1){ callback("had authed"); return ; } item.role = 1; item.save(function(err) { if (err) { callback(err); return; } if (global.DEBUG) { logger.info("auth success item: " + item); } callback(null); }); }) } } module.exports = userApplyService; ================================================ FILE: service/UserService.js ================================================ /** /** * Created by coverguo on 2015/1/12. */ var http = require('http'); var Apply = require('../model/Apply'); var log4js = require('log4js'), logger = log4js.getLogger(); var UserService = function (){ this.userDao = global.models.userDao; this.userApplyDao = global.models.userApplyDao; this.db = global.models.db; }; UserService.prototype = { queryListByCondition : function (target , callback){ var string = "select ua.id, u.loginName, u.email, u.chineseName, ua.applyId, ua.role, a.name "+ "from b_user as u join b_user_apply as ua on(ua.userId = u.id) "+ "join b_apply as a on (a.id =ua.applyId) where a.status=? "; var condition = [Apply.STATUS_PASS ]; if(target.userId){ string += "and applyId in(select applyId from b_user_apply where userId =? and role = 1)"; condition.push(target.userId); } if(target.applyId !=-1){ string += "and applyId =? "; condition.push(target.applyId); } if(target.role !=-1){ string += "and ua.role =? "; condition.push(target.role); } this.db.driver.execQuery(string,condition, function (err, data) { if(err){ callback(err); return; } callback(null, data); }); }, //查询用户创建项目的项目成员列表 queryListByUserProject : function(target, callback){ var string = "select ua.id, u.loginName, u.chineseName, ua.applyId, ua.role, a.name "+ "from b_user as u join b_user_apply as ua on(ua.userId = u.id) "+ "join b_apply as a on (a.id =ua.applyId) "+ "where applyId in(select applyId from b_user_apply where userId =?"+ " and role = 1);"; //console.log(string); this.db.driver.execQuery(string,[target.user.id], function (err, data) { if(err){ callback(err); return; } callback(null, data); }); }, queryUsersByCondition : function (target ,callback){ this.userDao.find(target , function (err , items){ if(err){ callback(err); return; } callback(null, items); }); }, add: function(target, callback){ this.userDao.create(target , function (err , items){ if(err){ callback(err); return; } logger.info("Insert into b_user success! target1: ",target); callback(null,{ret:0, msg:"success add"}); }); }, remove : function(target, callback){ }, update : function(target, callback){ this.userDao.one({id: target.id }, function (err, user) { // SQL: "SELECT * FROM b_apply WHERE name = 'xxxx'" for(key in target){ user[key] = target[key]; }; user.save(function (err) { callback(err); // err.msg = "under-age"; }); }); } } module.exports = UserService; ================================================ FILE: service/worker/Processor.js ================================================ var mq = require(global.pjconfig.mq.module) , client = mq.socket('sub') , mqUrl = global.pjconfig.mq.url , service = global.pjconfig.mq.subscribe; var log4js = require('log4js'), logger = log4js.getLogger(); var events = require('events'); var _ = require("underscore"); var path = require("path"); var cluster = require('cluster'); cluster.setupMaster({ exec: path.join(__dirname , "Worker.js") }); var support_signal = false; var log4js = require('log4js'), logger = log4js.getLogger(); var Processor = function (){ this.worker = cluster.fork({mqUrl : mqUrl , service :service , debug : !!global.DEBUG , mqModule : global.pjconfig.mq.module }); this.__pid__ = this.worker.process.pid; this.eventEmitter = new events.EventEmitter(); } Processor.prototype = { start : function (obj){ this.wbClient = obj.wbClient; this.id = 0; logger.info("processor("+this.__pid__+") start monitor"); var self = this; var messageQueue = [{type:"READY"}]; this.wbClient.on("message" , function (data){ if(self.worker.state == "online"){ // 新建一个worker 是有延迟的, 这个时候有message ,怎么加入队列,待其 OK 后在 send 给他 if(messageQueue.length){ _.each(messageQueue , function (value){ self.worker.send(value); }); messageQueue = []; } self.worker.send(JSON.parse(data)); }else { messageQueue.push(JSON.parse(data)); } }); this.worker.on('message', function (data) { if(data.type == "_STOP_"){ self.destroy(); }else { try{ self.wbClient && self.wbClient.send(JSON.stringify(data)); }catch(e){ logger.info("one client had closed"); self.destroy(); } } }); this.wbClient.on("close" , function (data){ self.worker.send({type : "STOP"}); self.destroy(); }); }, isDead : function (){ if(!this.worker){ return true; } return this.worker.isDead(); }, destroy : function (isKill){ if(isKill){ this.worker.kill(); logger.info("processor("+this.__pid__+") killed " ); }else { logger.info("processor("+this.__pid__+") stop monitor " ); } this.worker.removeAllListeners(); this.wbClient = null; this.eventEmitter.emit("destroy"); }, wait : function (){ support_signal && this.worker.kill("SIGSTOP"); }, notify : function (){ support_signal && this.worker.kill("SIGCONT"); }, on : function (key , cb){ var self = this; this.eventEmitter.on(key , function (){ cb.apply(self,arguments); }) } }; module.exports = Processor; ================================================ FILE: service/worker/ProcessorPool.js ================================================ var Processor = require("./Processor"); var _ = require("underscore"); var log4js = require('log4js'), logger = log4js.getLogger(); var STATUS_IDLE = "IDLE"; var STATUS_RUNNING = "RUNNING"; // 半个小时,回收新增的线程 var IDLE_TIMEOUT = 1800000; var maxIdle = 15; var ProcessorPool = function (){ var currentId = 0; var idlePool = []; var runningPool = []; var processorMapping = {}; var init = function (){ monitorError(); recovery(); } /* private */ var createProcessor = function (obj){ var p = {id : currentId++ , processor : new Processor() , status : obj.status || STATUS_IDLE , role : obj.role , createTime : new Date -0 }; p.processor.__id__ = p.id; processorMapping[p.id] = p; bindProcessorEvent(p); return p; } var bindProcessorEvent = function (p){ p.processor.on("destroy" , function (){ ProcessorPool.destroy(this); }); } var monitorError = function (){ process.on("exit" , function (){ _.each(runningPool , function (value){ value.processor.destroy(true); }); _.each(idlePool , function (value){ value.processor.destroy(true); }); }) } var idleProcessor = function (p){ var processor = processorMapping[p.__id__]; if(processor.status == STATUS_IDLE || processor.processor.isDead() ){ return ; }else { var index = 0; for(var i = 0 ; i < runningPool.length ; i ++){ if(runningPool[i].id == p.__id__){ index = i; break; } } processor.status = STATUS_IDLE; processor.processor.wait(); idlePool.push(runningPool.splice(index , 1)[0]); logger.debug("idleProcessor id="+ p.__id__ + ", idle count: " + ProcessorPool.idleProcessors() +", running count: " + ProcessorPool.runningProcessors()); } } var recovery = function (){ setInterval(function (){ if( idlePool.length <= maxIdle){ return ; } var diff = idlePool.length - maxIdle; logger.info("had "+diff + " should remove ... "); for(var i = 0 ; i < diff ; i++){ var value = idlePool.splice(0,1)[0]; if(value){ value.processor.destroy(true); logger.info("processor("+value.processor.__pid__+") remove"); } } logger.info("current length of idlePool " + idlePool.length); //}, 1000 * 10 ); },900000); //15分钟,检测一次是否有多余的进程 } ProcessorPool.createPool = function (number){ if(number){ maxIdle = number; } for(var i= 0 ; i = 0) { result = result && true; } else { result = result && false; } }); return result; }; var isExclude = function(str, regs) { var result = true; regs.forEach(function(value, key) { if (str.indexOf(value) >= 0) { result = result && true; } else { result = result && false; } }); return result; }; var getSubscribe = function (str){ if(process.env.mqModule == "axon") { return str + "*" } return str; } var monitorWorker = { wbClient: {}, filter: {}, monitorKey: '', mqClient: null, _resetTimeoutFlag: function() { this.wbClient._keepalive = new Date - 0; this.wbClient._timeoutTimes = 0; }, _keepAliveMonitor: function() { var self = this; if (this._monitorTimeoutId) { clearInterval(this._monitorTimeoutId); } this._monitorTimeoutId = setInterval(function() { var currentDate = new Date - 0; if (!self.wbClient._keepalive) { self._resetTimeoutFlag(); } if (currentDate - self.wbClient._keepalive > 5000) { self.wbClient._timeoutTimes++; } if (self.wbClient._timeoutTimes > 2) { logger.info("one client timeout "); process.send({ type: "_STOP_" }); self.stopMonitor(); } }, 5000); }, init: function() { var self = this; process.on("message", function(data) { switch (data.type) { case "KEEPALIVE": self._resetTimeoutFlag(); break; case "READY": self._keepAliveMonitor(); break; case "INIT": self.startMonitor(data); break; case "STOP": self.stopMonitor(); break; default: break; } if (isDebug) { logger.debug("pid=" + process.pid + " , " + JSON.stringify(data)); } }); }, isMatch: function(data) { try { var msg = data.msg + "||" + data.uin + "||" + data.url + "||" + data.userAgent + "||" + data.from; if (filter.level.indexOf(data.level) < 0) { return false; } if (filter.include && filter.include.length !== 0) { if (!isInclude(msg, filter.include)) { return false; } } if (filter.exclude && filter.exclude.length !== 0) { if (isExclude(msg, filter.exclude)) { return false; } } } catch (e) { logger.error("isMatch error : " + e + "data :" + JSON.stringify(data)); } return true; }, startMonitor: function(data) { var self = this; this.monitorKey = service + data.id + "|"; this.mqClient = mq.socket('sub'); this.mqClient.connect(mqUrl); this.mqClient.subscribe(getSubscribe(this.monitorKey)); filter = { level: data.level, include: data.include, exclude: data.exclude }; logger.info("worker(" + process.pid + ") start accept service:" + this.monitorKey); this.mqClient.on("message", function(data) { var dataStr = data.toString(); data = dataStr.substring(dataStr.indexOf(' ')); try { data = JSON.parse(data); } catch (e) {} if (self.isMatch(data)) { process.send({ type: "MESSAGE", message: data }); } }); }, stopMonitor: function() { logger.info("worker(" + process.pid + ") stop accept, service:" + this.monitorKey); try { this.mqClient && this.mqClient.close(); } catch (ex) { logger.error('mq client close error!'); } this.wbClient = {}; clearInterval(this._monitorTimeoutId); } }; monitorWorker.init(); ================================================ FILE: static/badjs/bj-report.js ================================================ /*! * @module report * @author kael, chriscai * @date @DATE * Copyright (c) 2014 kael, chriscai * Licensed under the MIT license. */ var BJ_REPORT = (function(global) { if (global.BJ_REPORT) return global.BJ_REPORT; var _error = []; var _error_map = {}; var _config = { id: 0, // 上报 id uin: 0, // user id url: "", // 上报 接口 combo: 1, // 是否合并 !0-合并 0-不合并 ext: null, // 扩展参数 用于自定义上报 level: 4, // 错误级别 1-debug 2-info 4-error ignore: [], // 忽略某个错误, 支持 Regexp 和 Function random: 1, // 抽样 (0-1] 1-全量 delay: 1000, // 延迟上报 combo 为 true 时有效 submit: null, // 自定义上报方式 repeat: 1 // 重复上报次数(对于同一个错误超过多少次不上报) }; var _isOBJByType = function(o, type) { return Object.prototype.toString.call(o) === "[object " + (type || "Object") + "]"; }; var _isOBJ = function(obj) { var type = typeof obj; return type === "object" && !!obj; }; var _isEmpty = function(obj) { if (obj === null) return true; if (_isOBJByType(obj, "Number")) { return false; } return !obj; }; var orgError = global.onerror; // rewrite window.oerror global.onerror = function(msg, url, line, col, error) { var newMsg = msg; if (error && error.stack) { newMsg = _processStackMsg(error); } if (_isOBJByType(newMsg, "Event")) { newMsg += newMsg.type ? ("--" + newMsg.type + "--" + (newMsg.target ? (newMsg.target.tagName + "::" + newMsg.target.src) : "")) : ""; } report.push({ msg: newMsg, target: url, rowNum: line, colNum: col }); _send(); orgError && orgError.apply(global, arguments); }; var _processError = function(errObj) { try { if (errObj.stack) { var url = errObj.stack.match("https?://[^\n]+"); url = url ? url[0] : ""; var rowCols = url.match(":(\\d+):(\\d+)"); if (!rowCols) { rowCols = [0, 0, 0]; } var stack = _processStackMsg(errObj); return { msg: stack, rowNum: rowCols[1], colNum: rowCols[2], target: url.replace(rowCols[0], "") }; } else { //ie 独有 error 对象信息,try-catch 捕获到错误信息传过来,造成没有msg if (errObj.name && errObj.message && errObj.description) { return { msg: JSON.stringify(errObj) }; } return errObj; } } catch (err) { return errObj; } }; var _processStackMsg = function(error) { var stack = error.stack .replace(/\n/gi, "") .split(/\bat\b/) .slice(0, 9) .join("@") .replace(/\?[^:]+/gi, ""); var msg = error.toString(); if (stack.indexOf(msg) < 0) { stack = msg + "@" + stack; } return stack; }; var _error_tostring = function(error, index) { var param = []; var params = []; var stringify = []; if (_isOBJ(error)) { error.level = error.level || _config.level; for (var key in error) { var value = error[key]; if (!_isEmpty(value)) { if (_isOBJ(value)) { try { value = JSON.stringify(value); } catch (err) { value = "[BJ_REPORT detect value stringify error] " + err.toString(); } } stringify.push(key + ":" + value); param.push(key + "=" + encodeURIComponent(value)); params.push(key + "[" + index + "]=" + encodeURIComponent(value)); } } } // msg[0]=msg&target[0]=target -- combo report // msg:msg,target:target -- ignore // msg=msg&target=target -- report with out combo return [params.join("&"), stringify.join(","), param.join("&")]; }; var _imgs = []; var _submit = function(url) { if (_config.submit) { _config.submit(url); } else { var _img = new Image(); _imgs.push(_img); _img.src = url; } }; var _is_repert = function(error) { if (!_isOBJ(error)) return true; var msg = error.msg; var times = _error_map[msg] = (parseInt(_error_map[msg], 10) || 0) + 1; return times > _config.repeat; }; var error_list = []; var comboTimeout = 0; var _send = function(isReoprtNow) { if (!_config.report) return; while (_error.length) { var isIgnore = false; var error = _error.shift(); // 重复上报 if (_is_repert(error)) continue; var error_str = _error_tostring(error, error_list.length); if (_isOBJByType(_config.ignore, "Array")) { for (var i = 0, l = _config.ignore.length; i < l; i++) { var rule = _config.ignore[i]; if ((_isOBJByType(rule, "RegExp") && rule.test(error_str[1])) || (_isOBJByType(rule, "Function") && rule(error, error_str[1]))) { isIgnore = true; break; } } } if (!isIgnore) { if (_config.combo) { error_list.push(error_str[0]); } else { _submit(_config.report + error_str[2] + "&_t=" + (+new Date)); } _config.onReport && (_config.onReport(_config.id, error)); } } // 合并上报 var count = error_list.length; if (count) { var comboReport = function() { clearTimeout(comboTimeout); _submit(_config.report + error_list.join("&") + "&count=" + error_list.length + "&_t=" + (+new Date)); comboTimeout = 0; error_list = []; }; if (isReoprtNow) { comboReport(); // 立即上报 } else if (!comboTimeout) { comboTimeout = setTimeout(comboReport, _config.delay); // 延迟上报 } } }; var report = global.BJ_REPORT = { push: function(msg) { // 将错误推到缓存池 // 抽样 if (Math.random() >= _config.random) { return report; } var data = _isOBJ(msg) ? _processError(msg) : { msg: msg }; // ext 有默认值, 且上报不包含 ext, 使用默认 ext if (_config.ext && !data.ext) { data.ext = _config.ext; } _error.push(data); _send(); return report; }, report: function(msg) { // error report msg && report.push(msg); _send(true); return report; }, info: function(msg) { // info report if (!msg) { return report; } if (_isOBJ(msg)) { msg.level = 2; } else { msg = { msg: msg, level: 2 }; } report.push(msg); return report; }, debug: function(msg) { // debug report if (!msg) { return report; } if (_isOBJ(msg)) { msg.level = 1; } else { msg = { msg: msg, level: 1 }; } report.push(msg); return report; }, init: function(config) { // 初始化 if (_isOBJ(config)) { for (var key in config) { _config[key] = config[key]; } } // 没有设置id将不上报 var id = parseInt(_config.id, 10); if (id) { // set default report url and uin if (/qq\.com$/gi.test(location.hostname)) { if (!_config.url) { _config.url = "//badjs2.qq.com/badjs"; } if (!_config.uin) { _config.uin = parseInt((document.cookie.match(/\buin=\D+(\d+)/) || [])[1], 10); } } _config.report = (_config.url || "/badjs") + "?id=" + id + "&uin=" + _config.uin + "&from=" + encodeURIComponent(location.href) + "&"; } // if had error in cache , report now if (_error.length) { _send(); } return report; }, __onerror__: global.onerror }; typeof console !== "undefined" && console.error && setTimeout(function() { var err = ((location.hash || "").match(/([#&])BJ_ERROR=([^&$]+)/) || [])[2]; err && console.error("BJ_ERROR", decodeURIComponent(err).replace(/(:\d+:\d+)\s*/g, "$1\n")); }, 0); return report; }(window)); if (typeof module !== "undefined") { module.exports = BJ_REPORT; } ================================================ FILE: static/badjs/bj-wrap.js ================================================ (function(global) { if (!global.BJ_REPORT) { console.error("please load bg-report first"); return; } var _onthrow = function(errObj) { global.BJ_REPORT.report(errObj); }; var tryJs = {}; global.BJ_REPORT.tryJs = function(throwCb) { throwCb && (_onthrow = throwCb); return tryJs; }; // merge var _merge = function(org, obj) { for (var key in obj) { org[key] = obj[key]; } }; // function or not var _isFunction = function(foo) { return typeof foo === "function"; }; var timeoutkey; var cat = function(foo, args) { return function() { try { return foo.apply(this, args || arguments); } catch (error) { _onthrow(error); //some browser throw error (chrome) , can not find error where it throw, so print it on console; if (error.stack && console && console.error) { console.error("[BJ-REPORT]", error.stack); } // hang up browser and throw , but it should trigger onerror , so rewrite onerror then recover it if (!timeoutkey) { var orgOnerror = global.onerror; global.onerror = function() {}; timeoutkey = setTimeout(function() { global.onerror = orgOnerror; timeoutkey = null; }, 50); } throw error; } }; }; var catArgs = function(foo) { return function() { var arg, args = []; for (var i = 0, l = arguments.length; i < l; i++) { arg = arguments[i]; _isFunction(arg) && (arg = cat(arg)); args.push(arg); } return foo.apply(this, args); }; }; var catTimeout = function(foo) { return function(cb, timeout) { // for setTimeout(string, delay) if (typeof cb === "string") { try { cb = new Function(cb); } catch (err) { throw err; } } var args = [].slice.call(arguments, 2); // for setTimeout(function, delay, param1, ...) cb = cat(cb, args.length && args); return foo(cb, timeout); }; }; /** * makeArgsTry * wrap a function's arguments with try & catch * @param {Function} foo * @param {Object} self * @returns {Function} */ var makeArgsTry = function(foo, self) { return function() { var arg, tmp, args = []; for (var i = 0, l = arguments.length; i < l; i++) { arg = arguments[i]; _isFunction(arg) && (tmp = cat(arg)) && (arg.tryWrap = tmp) && (arg = tmp); args.push(arg); } return foo.apply(self || this, args); }; }; /** * makeObjTry * wrap a object's all value with try & catch * @param {Function} foo * @param {Object} self * @returns {Function} */ var makeObjTry = function(obj) { var key, value; for (key in obj) { value = obj[key]; if (_isFunction(value)) obj[key] = cat(value); } return obj; }; /** * wrap jquery async function ,exp : event.add , event.remove , ajax * @returns {Function} */ tryJs.spyJquery = function() { var _$ = global.$; if (!_$ || !_$.event) { return tryJs; } var _add, _remove; if (_$.zepto) { _add = _$.fn.on, _remove = _$.fn.off; _$.fn.on = makeArgsTry(_add); _$.fn.off = function() { var arg, args = []; for (var i = 0, l = arguments.length; i < l; i++) { arg = arguments[i]; _isFunction(arg) && arg.tryWrap && (arg = arg.tryWrap); args.push(arg); } return _remove.apply(this, args); }; } else if (window.jQuery) { _add = _$.event.add, _remove = _$.event.remove; _$.event.add = makeArgsTry(_add); _$.event.remove = function() { var arg, args = []; for (var i = 0, l = arguments.length; i < l; i++) { arg = arguments[i]; _isFunction(arg) && arg.tryWrap && (arg = arg.tryWrap); args.push(arg); } return _remove.apply(this, args); }; } var _ajax = _$.ajax; if (_ajax) { _$.ajax = function(url, setting) { if (!setting) { setting = url; url = undefined; } makeObjTry(setting); if (url) return _ajax.call(_$, url, setting); return _ajax.call(_$, setting); }; } return tryJs; }; /** * wrap amd or commonjs of function ,exp : define , require , * @returns {Function} */ tryJs.spyModules = function() { var _require = global.require, _define = global.define; if (_define && _define.amd && _require) { global.require = catArgs(_require); _merge(global.require, _require); global.define = catArgs(_define); _merge(global.define, _define); } if (global.seajs && _define) { global.define = function() { var arg, args = []; for (var i = 0, l = arguments.length; i < l; i++) { arg = arguments[i]; if (_isFunction(arg)) { arg = cat(arg); //seajs should use toString parse dependencies , so rewrite it arg.toString = (function(orgArg) { return function() { return orgArg.toString(); }; }(arguments[i])); } args.push(arg); } return _define.apply(this, args); }; global.seajs.use = catArgs(global.seajs.use); _merge(global.define, _define); } return tryJs; }; /** * wrap async of function in window , exp : setTimeout , setInterval * @returns {Function} */ tryJs.spySystem = function() { global.setTimeout = catTimeout(global.setTimeout); global.setInterval = catTimeout(global.setInterval); return tryJs; }; /** * wrap custom of function , * @param obj - obj or function * @returns {Function} */ tryJs.spyCustom = function(obj) { if (_isFunction(obj)) { return cat(obj); } else { return makeObjTry(obj); } }; /** * run spyJquery() and spyModules() and spySystem() * @returns {Function} */ tryJs.spyAll = function() { tryJs .spyJquery() .spyModules() .spySystem(); return tryJs; }; }(window)); ================================================ FILE: static/common/delegator.js ================================================ /** * Map * @class */ function Map() { this.map = {}; this.length = 0; } Map.prototype = { constructor: Map, /** * has * @param {String} key * @returns {Boolean} */ has: function (key) { return (key in this.map); }, /** * get * @param {String} key * @returns {Any} */ get: function (key) { return this.map[key]; }, /** * set * @param {String} key * @param {Any} value */ set: function (key, value) { !this.has(key) && this.length++; return (this.map[key] = value); }, /** * count * @returns {Number} */ count: function () { return this.length; }, /** * remove * @param {String} key */ remove: function (key) { if (this.has(key)) { this.map[key] = null; delete this.map[key]; this.length--; } } }; var cache = new Map(), set = cache.set, uid = 0; cache.set = function (node, value) { if (!value) { value = node; set.call(cache, ++uid + '', value); return uid; } else { typeof node === 'string' && (node = $(node)[0]); $.data(node, 'event-data', value); return this; } }; function _key(arr) { if (!arr) return {}; arr = arr.split(' '); var obj = {}; for (var i = 0, l = arr.length; i < l; i++) { obj[arr[i]] = true; } return obj; } /** * Delegator * @class * @param {Selector} container */ function Delegator(container) { this.container = $(container); this.listenerMap = new Map(); } /** * getKey * @param {Any} value * @returns {Number} */ Delegator.set = cache.set; /** * cache * @class * @static */ Delegator.cache = cache; Delegator.prototype = { constructor: Delegator, _getListener: function (type) { if (this.listenerMap.has(type)) { return this.listenerMap.get(type); } function listener(e) { var data = $.data(this), routes = data['event-' + type + '-routes'], eventData = data['event-data'], handle, dataKey; // preprocessing if (!routes && (routes = this.getAttribute('data-event-' + type))) { (routes = routes.split(' ')) && (data['event-' + type + '-routes'] = routes); !eventData && (dataKey = this.getAttribute('data-event-data')) && (eventData = cache.get(dataKey)) && (data['event-data'] = eventData) && (cache.remove(dataKey)); !data['event-stop-propagation'] && (data['event-stop-propagation'] = _key(this.getAttribute('data-event-stop-propagation'))); } if (routes) { for (var i = 0, l = routes.length; i < l; i++) { handle = listener.handleMap.get(routes[i]); if (handle) { handle.call(this, e, eventData); } data['event-stop-propagation'][type] && e.stopPropagation(); } } } listener.handleMap = new Map(); this.listenerMap.set(type, listener); this.container.on(type, '[data-event-' + type + ']', listener); return listener; }, /** * on * @param {String} type * @param {String} name * @param {Function} handle */ on: function (type, name, handle) { var listener = this._getListener(type); listener.handleMap.set(name, handle); return this; }, /** * off * @param {String} type * @param {String} name */ off: function (type, name) { var listener = this._getListener(type), handleMap = listener.handleMap; handleMap.remove(name); if (!handleMap.count()) { this.container.off(type, '[data-event-' + type + ']', listener); this.listenerMap.remove(type); } } }; module.exports = Delegator; ================================================ FILE: static/common/dialog/dialog.js ================================================ var Delegator = require("delegator"); var modal = require("./modal.ejs"); var container; function hide() { container.removeClass('in'); container.find('.modal-backdrop').removeClass('in'); setTimeout(function () { container.remove(); container = undefined; }, 300); } function Dialog (param) { if (container) { container.remove(); container = undefined; } container = $(modal({it :param})) .appendTo(document.body) .show(); var key, action, delegator, on = param.on || {}; delegator = (new Delegator(container)) .on('click', 'close', hide); for (key in on) { action = key.split('/'); delegator.on(action[0], action[1], on[key]); } setTimeout(function () { container.addClass('in'); container.find('.modal-backdrop').addClass('in'); }, 0); } Dialog.hide = hide; module.exports = Dialog; ================================================ FILE: static/common/dialog/modal.ejs ================================================ ================================================ FILE: static/common.js ================================================ /******/ (function(modules) { // webpackBootstrap /******/ // install a JSONP callback for chunk loading /******/ var parentJsonpFunction = window["webpackJsonp"]; /******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) { /******/ // add "moreModules" to the modules object, /******/ // then flag all "chunkIds" as loaded and fire callback /******/ var moduleId, chunkId, i = 0, callbacks = []; /******/ for(;i < chunkIds.length; i++) { /******/ chunkId = chunkIds[i]; /******/ if(installedChunks[chunkId]) /******/ callbacks.push.apply(callbacks, installedChunks[chunkId]); /******/ installedChunks[chunkId] = 0; /******/ } /******/ for(moduleId in moreModules) { /******/ modules[moduleId] = moreModules[moduleId]; /******/ } /******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules); /******/ while(callbacks.length) /******/ callbacks.shift().call(null, __webpack_require__); /******/ if(moreModules[0]) { /******/ installedModules[0] = 0; /******/ return __webpack_require__(0); /******/ } /******/ }; /******/ // The module cache /******/ var installedModules = {}; /******/ // object to store loaded and loading chunks /******/ // "0" means "already loaded" /******/ // Array means "loading", array contains callbacks /******/ var installedChunks = { /******/ 3:0 /******/ }; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // This file contains only the entry chunk. /******/ // The chunk loading function for additional chunks /******/ __webpack_require__.e = function requireEnsure(chunkId, callback) { /******/ // "0" is the signal for "already loaded" /******/ if(installedChunks[chunkId] === 0) /******/ return callback.call(null, __webpack_require__); /******/ // an array means "currently loading". /******/ if(installedChunks[chunkId] !== undefined) { /******/ installedChunks[chunkId].push(callback); /******/ } else { /******/ // start chunk loading /******/ installedChunks[chunkId] = [callback]; /******/ var head = document.getElementsByTagName('head')[0]; /******/ var script = document.createElement('script'); /******/ script.type = 'text/javascript'; /******/ script.charset = 'utf-8'; /******/ script.async = true; /******/ script.src = __webpack_require__.p + "" + chunkId + "../" + ({"0":"entry.home","1":"entry.apply","2":"entry.realtime","4":"entry.charts","5":"entry.authUserManage","6":"entry.userManage","7":"entry.projectTotal","8":"entry.statistics","9":"entry.offlinelog","10":"entry.log","11":"entry.applyList"}[chunkId]||chunkId) + ".js"; /******/ head.appendChild(script); /******/ } /******/ }; /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { __webpack_require__(4); __webpack_require__(1); __webpack_require__(5); __webpack_require__(6); __webpack_require__(2); module.exports = __webpack_require__(3); /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(_) { if(!_){ _={}; } _.formatDate= function(date, formatString){ /* * eg:formatString="YYYY-MM-DD hh:mm:ss"; */ var o = { "M+" : date.getMonth()+1, //month "D+" : date.getDate(), //day "h+" : date.getHours(), //hour "m+" : date.getMinutes(), //minute "s+" : date.getSeconds(), //second "q+" : Math.floor((date.getMonth()+3)/3), //quarter "S" : date.getMilliseconds() //millisecond } if(/(Y+)/.test(formatString)){ formatString = formatString.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length)); } for(var k in o){ if(new RegExp("("+ k +")").test(formatString)){ formatString = formatString.replace(RegExp.$1, RegExp.$1.length==1 ? o[k] : ("00"+ o[k]).substr((""+ o[k]).length)); } } return formatString; } /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4))) /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(jQuery) {/*! * Bootstrap v3.3.0 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') } +function ($) { var version = $.fn.jquery.split(' ')[0].split('.') if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) { throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher') } }(jQuery); /* ======================================================================== * Bootstrap: transition.js v3.3.0 * http://getbootstrap.com/javascript/#transitions * ======================================================================== * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) // ============================================================ function transitionEnd() { var el = document.createElement('bootstrap') var transEndEventNames = { WebkitTransition : 'webkitTransitionEnd', MozTransition : 'transitionend', OTransition : 'oTransitionEnd otransitionend', transition : 'transitionend' } for (var name in transEndEventNames) { if (el.style[name] !== undefined) { return { end: transEndEventNames[name] } } } return false // explicit for ie8 ( ._.) } // http://blog.alexmaccaw.com/css-transitions $.fn.emulateTransitionEnd = function (duration) { var called = false var $el = this $(this).one('bsTransitionEnd', function () { called = true }) var callback = function () { if (!called) $($el).trigger($.support.transition.end) } setTimeout(callback, duration) return this } $(function () { $.support.transition = transitionEnd() if (!$.support.transition) return $.event.special.bsTransitionEnd = { bindType: $.support.transition.end, delegateType: $.support.transition.end, handle: function (e) { if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) } } }) }(jQuery); /* ======================================================================== * Bootstrap: alert.js v3.3.0 * http://getbootstrap.com/javascript/#alerts * ======================================================================== * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // ALERT CLASS DEFINITION // ====================== var dismiss = '[data-dismiss="alert"]' var Alert = function (el) { $(el).on('click', dismiss, this.close) } Alert.VERSION = '3.3.0' Alert.TRANSITION_DURATION = 150 Alert.prototype.close = function (e) { var $this = $(this) var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = $(selector) if (e) e.preventDefault() if (!$parent.length) { $parent = $this.closest('.alert') } $parent.trigger(e = $.Event('close.bs.alert')) if (e.isDefaultPrevented()) return $parent.removeClass('in') function removeElement() { // detach from parent, fire event then clean up data $parent.detach().trigger('closed.bs.alert').remove() } $.support.transition && $parent.hasClass('fade') ? $parent .one('bsTransitionEnd', removeElement) .emulateTransitionEnd(Alert.TRANSITION_DURATION) : removeElement() } // ALERT PLUGIN DEFINITION // ======================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.alert') if (!data) $this.data('bs.alert', (data = new Alert(this))) if (typeof option == 'string') data[option].call($this) }) } var old = $.fn.alert $.fn.alert = Plugin $.fn.alert.Constructor = Alert // ALERT NO CONFLICT // ================= $.fn.alert.noConflict = function () { $.fn.alert = old return this } // ALERT DATA-API // ============== $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) }(jQuery); /* ======================================================================== * Bootstrap: button.js v3.3.0 * http://getbootstrap.com/javascript/#buttons * ======================================================================== * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // BUTTON PUBLIC CLASS DEFINITION // ============================== var Button = function (element, options) { this.$element = $(element) this.options = $.extend({}, Button.DEFAULTS, options) this.isLoading = false } Button.VERSION = '3.3.0' Button.DEFAULTS = { loadingText: 'loading...' } Button.prototype.setState = function (state) { var d = 'disabled' var $el = this.$element var val = $el.is('input') ? 'val' : 'html' var data = $el.data() state = state + 'Text' if (data.resetText == null) $el.data('resetText', $el[val]()) // push to event loop to allow forms to submit setTimeout($.proxy(function () { $el[val](data[state] == null ? this.options[state] : data[state]) if (state == 'loadingText') { this.isLoading = true $el.addClass(d).attr(d, d) } else if (this.isLoading) { this.isLoading = false $el.removeClass(d).removeAttr(d) } }, this), 0) } Button.prototype.toggle = function () { var changed = true var $parent = this.$element.closest('[data-toggle="buttons"]') if ($parent.length) { var $input = this.$element.find('input') if ($input.prop('type') == 'radio') { if ($input.prop('checked') && this.$element.hasClass('active')) changed = false else $parent.find('.active').removeClass('active') } if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') } else { this.$element.attr('aria-pressed', !this.$element.hasClass('active')) } if (changed) this.$element.toggleClass('active') } // BUTTON PLUGIN DEFINITION // ======================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.button') var options = typeof option == 'object' && option if (!data) $this.data('bs.button', (data = new Button(this, options))) if (option == 'toggle') data.toggle() else if (option) data.setState(option) }) } var old = $.fn.button $.fn.button = Plugin $.fn.button.Constructor = Button // BUTTON NO CONFLICT // ================== $.fn.button.noConflict = function () { $.fn.button = old return this } // BUTTON DATA-API // =============== $(document) .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { var $btn = $(e.target) if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') Plugin.call($btn, 'toggle') e.preventDefault() }) .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { $(e.target).closest('.btn').toggleClass('focus', e.type == 'focus') }) }(jQuery); /* ======================================================================== * Bootstrap: carousel.js v3.3.0 * http://getbootstrap.com/javascript/#carousel * ======================================================================== * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CAROUSEL CLASS DEFINITION // ========================= var Carousel = function (element, options) { this.$element = $(element) this.$indicators = this.$element.find('.carousel-indicators') this.options = options this.paused = this.sliding = this.interval = this.$active = this.$items = null this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) } Carousel.VERSION = '3.3.0' Carousel.TRANSITION_DURATION = 600 Carousel.DEFAULTS = { interval: 5000, pause: 'hover', wrap: true, keyboard: true } Carousel.prototype.keydown = function (e) { switch (e.which) { case 37: this.prev(); break case 39: this.next(); break default: return } e.preventDefault() } Carousel.prototype.cycle = function (e) { e || (this.paused = false) this.interval && clearInterval(this.interval) this.options.interval && !this.paused && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) return this } Carousel.prototype.getItemIndex = function (item) { this.$items = item.parent().children('.item') return this.$items.index(item || this.$active) } Carousel.prototype.getItemForDirection = function (direction, active) { var delta = direction == 'prev' ? -1 : 1 var activeIndex = this.getItemIndex(active) var itemIndex = (activeIndex + delta) % this.$items.length return this.$items.eq(itemIndex) } Carousel.prototype.to = function (pos) { var that = this var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) if (pos > (this.$items.length - 1) || pos < 0) return if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" if (activeIndex == pos) return this.pause().cycle() return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) } Carousel.prototype.pause = function (e) { e || (this.paused = true) if (this.$element.find('.next, .prev').length && $.support.transition) { this.$element.trigger($.support.transition.end) this.cycle(true) } this.interval = clearInterval(this.interval) return this } Carousel.prototype.next = function () { if (this.sliding) return return this.slide('next') } Carousel.prototype.prev = function () { if (this.sliding) return return this.slide('prev') } Carousel.prototype.slide = function (type, next) { var $active = this.$element.find('.item.active') var $next = next || this.getItemForDirection(type, $active) var isCycling = this.interval var direction = type == 'next' ? 'left' : 'right' var fallback = type == 'next' ? 'first' : 'last' var that = this if (!$next.length) { if (!this.options.wrap) return $next = this.$element.find('.item')[fallback]() } if ($next.hasClass('active')) return (this.sliding = false) var relatedTarget = $next[0] var slideEvent = $.Event('slide.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) this.$element.trigger(slideEvent) if (slideEvent.isDefaultPrevented()) return this.sliding = true isCycling && this.pause() if (this.$indicators.length) { this.$indicators.find('.active').removeClass('active') var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) $nextIndicator && $nextIndicator.addClass('active') } var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" if ($.support.transition && this.$element.hasClass('slide')) { $next.addClass(type) $next[0].offsetWidth // force reflow $active.addClass(direction) $next.addClass(direction) $active .one('bsTransitionEnd', function () { $next.removeClass([type, direction].join(' ')).addClass('active') $active.removeClass(['active', direction].join(' ')) that.sliding = false setTimeout(function () { that.$element.trigger(slidEvent) }, 0) }) .emulateTransitionEnd(Carousel.TRANSITION_DURATION) } else { $active.removeClass('active') $next.addClass('active') this.sliding = false this.$element.trigger(slidEvent) } isCycling && this.cycle() return this } // CAROUSEL PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.carousel') var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) var action = typeof option == 'string' ? option : options.slide if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) if (typeof option == 'number') data.to(option) else if (action) data[action]() else if (options.interval) data.pause().cycle() }) } var old = $.fn.carousel $.fn.carousel = Plugin $.fn.carousel.Constructor = Carousel // CAROUSEL NO CONFLICT // ==================== $.fn.carousel.noConflict = function () { $.fn.carousel = old return this } // CAROUSEL DATA-API // ================= var clickHandler = function (e) { var href var $this = $(this) var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 if (!$target.hasClass('carousel')) return var options = $.extend({}, $target.data(), $this.data()) var slideIndex = $this.attr('data-slide-to') if (slideIndex) options.interval = false Plugin.call($target, options) if (slideIndex) { $target.data('bs.carousel').to(slideIndex) } e.preventDefault() } $(document) .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) $(window).on('load', function () { $('[data-ride="carousel"]').each(function () { var $carousel = $(this) Plugin.call($carousel, $carousel.data()) }) }) }(jQuery); /* ======================================================================== * Bootstrap: collapse.js v3.3.0 * http://getbootstrap.com/javascript/#collapse * ======================================================================== * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // COLLAPSE PUBLIC CLASS DEFINITION // ================================ var Collapse = function (element, options) { this.$element = $(element) this.options = $.extend({}, Collapse.DEFAULTS, options) this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]') this.transitioning = null if (this.options.parent) { this.$parent = this.getParent() } else { this.addAriaAndCollapsedClass(this.$element, this.$trigger) } if (this.options.toggle) this.toggle() } Collapse.VERSION = '3.3.0' Collapse.TRANSITION_DURATION = 350 Collapse.DEFAULTS = { toggle: true, trigger: '[data-toggle="collapse"]' } Collapse.prototype.dimension = function () { var hasWidth = this.$element.hasClass('width') return hasWidth ? 'width' : 'height' } Collapse.prototype.show = function () { if (this.transitioning || this.$element.hasClass('in')) return var activesData var actives = this.$parent && this.$parent.find('> .panel').children('.in, .collapsing') if (actives && actives.length) { activesData = actives.data('bs.collapse') if (activesData && activesData.transitioning) return } var startEvent = $.Event('show.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return if (actives && actives.length) { Plugin.call(actives, 'hide') activesData || actives.data('bs.collapse', null) } var dimension = this.dimension() this.$element .removeClass('collapse') .addClass('collapsing')[dimension](0) .attr('aria-expanded', true) this.$trigger .removeClass('collapsed') .attr('aria-expanded', true) this.transitioning = 1 var complete = function () { this.$element .removeClass('collapsing') .addClass('collapse in')[dimension]('') this.transitioning = 0 this.$element .trigger('shown.bs.collapse') } if (!$.support.transition) return complete.call(this) var scrollSize = $.camelCase(['scroll', dimension].join('-')) this.$element .one('bsTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) } Collapse.prototype.hide = function () { if (this.transitioning || !this.$element.hasClass('in')) return var startEvent = $.Event('hide.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return var dimension = this.dimension() this.$element[dimension](this.$element[dimension]())[0].offsetHeight this.$element .addClass('collapsing') .removeClass('collapse in') .attr('aria-expanded', false) this.$trigger .addClass('collapsed') .attr('aria-expanded', false) this.transitioning = 1 var complete = function () { this.transitioning = 0 this.$element .removeClass('collapsing') .addClass('collapse') .trigger('hidden.bs.collapse') } if (!$.support.transition) return complete.call(this) this.$element [dimension](0) .one('bsTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION) } Collapse.prototype.toggle = function () { this[this.$element.hasClass('in') ? 'hide' : 'show']() } Collapse.prototype.getParent = function () { return $(this.options.parent) .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') .each($.proxy(function (i, element) { var $element = $(element) this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) }, this)) .end() } Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { var isOpen = $element.hasClass('in') $element.attr('aria-expanded', isOpen) $trigger .toggleClass('collapsed', !isOpen) .attr('aria-expanded', isOpen) } function getTargetFromTrigger($trigger) { var href var target = $trigger.attr('data-target') || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 return $(target) } // COLLAPSE PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.collapse') var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data && options.toggle && option == 'show') options.toggle = false if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.collapse $.fn.collapse = Plugin $.fn.collapse.Constructor = Collapse // COLLAPSE NO CONFLICT // ==================== $.fn.collapse.noConflict = function () { $.fn.collapse = old return this } // COLLAPSE DATA-API // ================= $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { var $this = $(this) if (!$this.attr('data-target')) e.preventDefault() var $target = getTargetFromTrigger($this) var data = $target.data('bs.collapse') var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this }) Plugin.call($target, option) }) }(jQuery); /* ======================================================================== * Bootstrap: dropdown.js v3.3.0 * http://getbootstrap.com/javascript/#dropdowns * ======================================================================== * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // DROPDOWN CLASS DEFINITION // ========================= var backdrop = '.dropdown-backdrop' var toggle = '[data-toggle="dropdown"]' var Dropdown = function (element) { $(element).on('click.bs.dropdown', this.toggle) } Dropdown.VERSION = '3.3.0' Dropdown.prototype.toggle = function (e) { var $this = $(this) if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') clearMenus() if (!isActive) { if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { // if mobile we use a backdrop because click events don't delegate $('