PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/service.js

https://gitlab.com/evatechsoft/PollEndpoint
JavaScript | 465 lines | 422 code | 28 blank | 15 comment | 55 complexity | d025bb8dc16f37fe19091fe35effdb3f MD5 | raw file
Possible License(s): MIT
  1. /*
  2. * PollEndpoint © 2013 -
  3. * Created By Licson Lee (licson0729@gmail.com)
  4. */
  5. //Database stuff
  6. var db = require('./db.js');
  7. var sql = new db.driver();
  8. //Express web server and related stuff
  9. var express = require('express');
  10. var view = require('./view.js');
  11. var app = express();
  12. var http = require('http').createServer(app);
  13. //var https = require('https').createServer(app);
  14. var async = require('async');
  15. //Unique ID generation
  16. var genID = function(len){
  17. var ret = '';
  18. var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789_-".split('');
  19. while(len--){
  20. ret += chars[~~(Math.random() * chars.length)];
  21. }
  22. return ret;
  23. };
  24. //Custom logger
  25. app.use(function(req,res,next){
  26. console.log('Request page %s from %s',req.url,req.ip);
  27. next();
  28. });
  29. //static files and assets
  30. app.use('/assets',express.static(__dirname+'/assets/'));
  31. //help parsing form data
  32. app.use(express.bodyParser());
  33. //Trust the reverse proxy
  34. app.enable('trust proxy');
  35. //our index page
  36. app.get('/',function(req,res){
  37. async.parallel({
  38. latest: function(cb){
  39. sql.conn.query('SELECT * FROM `polls` ORDER BY `created_at` DESC LIMIT 0, 10',function(err,latest){
  40. cb(err,latest);
  41. });
  42. },
  43. popular: function(cb){
  44. sql.conn.query('SELECT p.`id`, p.`name`, p.`created_at` FROM `polls` AS p LEFT JOIN (SELECT COUNT(*) AS `count`, `poll_id`, `time` FROM `stats` GROUP BY `poll_id`) AS s ON p.`id` = s.`poll_id` ORDER BY s.`count` DESC LIMIT 0, 10',function(err,popular){
  45. cb(err,popular);
  46. });
  47. }
  48. },function(err,result){
  49. if(err){
  50. res.send(view.error(err, req.acceptedCharsets[0]));
  51. }
  52. else {
  53. res.send(view.page('index',{
  54. polls:{
  55. latest: result.latest,
  56. popular: result.popular
  57. }
  58. }, req.acceptedCharsets[0]));
  59. }
  60. });
  61. });
  62. app.get('/thanks',function(req,res){
  63. res.send(view.page('thanks', {}, req.acceptedCharsets[0]));
  64. });
  65. app.get('/about',function(req,res){
  66. res.send(view.page('about', {}, req.acceptedCharsets[0]));
  67. });
  68. app.get('/question/:id',function(req,res){
  69. var id = req.params.id;
  70. async.parallel({
  71. info:function(cb){
  72. sql.conn.query('SELECT * FROM `polls` WHERE `id` = ?',[id],function(err,info){
  73. cb(err,info);
  74. });
  75. },
  76. data:function(cb){
  77. sql.conn.query('SELECT * FROM `questions` WHERE `belongs` = ? ORDER BY `time` ASC',[id],function(err,data){
  78. cb(err,data);
  79. });
  80. }
  81. },
  82. function(err,result){
  83. if(err){
  84. res.send(view.error(err, req.acceptedCharsets[0]));
  85. }
  86. else {
  87. if(result.data.length > 0){
  88. res.send(view.page('question',{
  89. id:id,
  90. meta:result.info,
  91. questions:result.data
  92. }, req.acceptedCharsets[0]));
  93. }
  94. else {
  95. res.send(view.error(new Error('The poll specified does not exists anymore.'), req.acceptedCharsets[0]));
  96. }
  97. }
  98. });
  99. });
  100. app.get('/widget/:id',function(req,res){
  101. var id = req.params.id;
  102. async.parallel({
  103. info:function(cb){
  104. sql.conn.query('SELECT * FROM `polls` WHERE `id` = ?',[id],function(err,info){
  105. cb(err,info);
  106. });
  107. },
  108. data:function(cb){
  109. sql.conn.query('SELECT * FROM `questions` WHERE `belongs` = ? ORDER BY `time` ASC',[id],function(err,data){
  110. cb(err,data);
  111. });
  112. }
  113. },
  114. function(err,result){
  115. if(err){
  116. res.send(view.error(err));
  117. }
  118. else {
  119. if(result.data.length > 0){
  120. res.send(view.page('widget',{
  121. id:id,
  122. meta:result.info,
  123. questions:result.data
  124. }, req.acceptedCharsets[0]));
  125. }
  126. else {
  127. res.send(view.error(new Error('The poll specified does not exists anymore.'), req.acceptedCharsets[0]));
  128. }
  129. }
  130. });
  131. });
  132. app.get('/robots.txt',function(req,res){
  133. var lines = [
  134. 'User-agent: *',
  135. 'Disallow: /phpmyadmin/',
  136. 'Disallow: /haproxy-status',
  137. 'Sitemap: https://pollendpoint-licson.rhcloud.com/sitemap',
  138. 'Crawl-delay: 0'
  139. ].join("\n");
  140. res.send(lines);
  141. });
  142. app.get('/sitemap',function(req,res){
  143. sql.conn.query("SELECT `id` FROM `polls` LIMIT 0,100",function(err,data){
  144. if(!err){
  145. var sitemap = [
  146. '<?xml version="1.0" encoding="utf-8"?>',
  147. '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
  148. '<url><loc>https://pollendpoint-licson.rhcloud.com/</loc><changefreq>daily</changefreq><priority>1</priority></url>',
  149. '<url><loc>https://pollendpoint-licson.rhcloud.com/about</loc><priority>0.8</priority></url>',
  150. '<url><loc>https://pollendpoint-licson.rhcloud.com/create</loc><priority>1</priority></url>'
  151. ];
  152. for(var i = 0; i < data.length; i++){
  153. sitemap.push('<url><loc>https://pollendpoint-licson.rhcloud.com/question/'+data[i].id+'</loc></url>');
  154. }
  155. sitemap.push('</urlset>');
  156. res.writeHead(200,{'Content-type':'text/xml'});
  157. res.end(sitemap.join(''));
  158. }
  159. else {
  160. var sitemap = [
  161. '<?xml version="1.0" encoding="utf-8"?>',
  162. '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
  163. '<url><loc>https://pollendpoint-licson.rhcloud.com/</loc><changefreq>daily</changefreq><priority>1</priority></url>',
  164. '<url><loc>https://pollendpoint-licson.rhcloud.com/about</loc><priority>0.8</priority></url>',
  165. '<url><loc>https://pollendpoint-licson.rhcloud.com/create</loc><priority>1</priority></url>',
  166. '</urlset>'
  167. ].join('');
  168. res.writeHead(200,{'Content-type':'text/xml'});
  169. res.end(sitemap);
  170. }
  171. });
  172. });
  173. app.get('/search',function(req,res){
  174. var keywords = db.escape(req.query.q).replace(/^'|'$/g,'');
  175. var page = parseInt(typeof req.query.page === "undefined" ? 1 : req.query.page);
  176. sql.conn.query('SELECT count(*) AS `count` FROM `polls` WHERE `name` LIKE \'%'+keywords+'%\' OR `keywords` LIKE \'%'+keywords+'%\' OR `desc` LIKE \'%'+keywords+'%\'',function(err,rows){
  177. if(!err){
  178. var count = rows[0].count;
  179. sql.conn.query('SELECT * FROM `polls` WHERE `name` LIKE \'%'+keywords+'%\' OR `keywords` LIKE \'%'+keywords+'%\' OR `desc` LIKE \'%'+keywords+'%\' LIMIT '+(page-1)*5+', 5',function(err,result){
  180. if(!err){
  181. res.send(view.page('search',{
  182. keyword: keywords,
  183. result: result,
  184. page: page,
  185. count: count,
  186. limit: 5
  187. }, req.acceptedCharsets[0]));
  188. }
  189. else {
  190. res.send(view.error(err, req.acceptedCharsets[0]));
  191. }
  192. });
  193. }
  194. else {
  195. res.send(view.error(err, req.acceptedCharsets[0]));
  196. }
  197. });
  198. });
  199. app.post('/question/:id/submit',function(req,res){
  200. var success = true;
  201. for(var q in req.body){
  202. var data = {
  203. question_id:q,
  204. poll_id:req.params.id,
  205. id:genID(40),
  206. value:typeof req.body[q] !== "string" ? req.body[q].join(',') : req.body[q]
  207. };
  208. sql.conn.query('INSERT INTO `answers` SET ?',data,function(err){
  209. if(err){
  210. success = false;
  211. }
  212. });
  213. }
  214. sql.conn.query('INSERT INTO `stats` SET ?',{poll_id:req.params.id},function(e){
  215. if(e){
  216. success = false;
  217. }
  218. });
  219. res.json({success:success});
  220. });
  221. app.get('/create',function(req,res){
  222. res.send(view.page('create',{} ,req.acceptedCharsets[0]));
  223. });
  224. app.post('/create/save/basic_info',function(req,res){
  225. var data = req.body;
  226. data.id = genID(25);
  227. if(!(data.name.trim() == '' || data.desc.trim() == '' || data.keywords.trim() == '')){
  228. sql.conn.query('INSERT INTO `polls` SET ?',data,function(err){
  229. if(!err){
  230. res.json({success:true,id:data.id});
  231. }
  232. else {
  233. res.json({success:false,message:'Database Error, please try later.'});
  234. console.log(err);
  235. }
  236. });
  237. }
  238. else {
  239. res.json({success:false,message:'All fields were required.'});
  240. }
  241. });
  242. app.post('/create/save/questions',function(req,res){
  243. var success = true;
  244. var date = new Date();
  245. var data = [];
  246. date.setSeconds(0);
  247. if(Array.isArray(req.body.questions.belongs)){
  248. for(var i = 0; i < req.body.questions.belongs.length; i++){
  249. date.setSeconds(i+1);
  250. var q = {
  251. belongs:req.body.questions.belongs[i],
  252. name:req.body.questions.name[i],
  253. type:req.body.questions.type[i],
  254. choices:req.body.questions.choices[i],
  255. required:req.body.questions.required[i],
  256. id:genID(40),
  257. time:date
  258. };
  259. data.push(q);
  260. }
  261. }
  262. else {
  263. var q = {
  264. belongs:req.body.questions.belongs,
  265. name:req.body.questions.name,
  266. type:req.body.questions.type,
  267. choices:req.body.questions.choices,
  268. required:req.body.questions.required,
  269. id:genID(40),
  270. time:date
  271. };
  272. data.push(q);
  273. }
  274. async.map(data,function(q,cb){
  275. sql.conn.query('INSERT INTO `questions` SET ?',q,function(err){
  276. if(err){
  277. console.log(err);
  278. success = false;
  279. }
  280. cb(err);
  281. });
  282. },function(e){
  283. var message = {success:success};
  284. if(!success){
  285. message.message = "Database error, please try again later.";
  286. }
  287. res.json(message);
  288. });
  289. });
  290. app.get('/stats/:id',function(req,res){
  291. var id = req.params.id;
  292. async.parallel({
  293. questions:function(cb){
  294. sql.conn.query('SELECT * FROM `questions` WHERE `belongs` = ? ORDER BY `time` ASC',[req.params.id],function(err,questions){
  295. cb(err,questions);
  296. });
  297. },
  298. t_count:function(cb){
  299. sql.conn.query('SELECT count(*) AS `count` FROM `stats` WHERE `poll_id` = ?',[id],function(err,data){
  300. var t_count = typeof data[0] == "undefined" ? 0 : data[0].count;
  301. cb(err,t_count);
  302. });
  303. },
  304. q_count:function(cb){
  305. sql.conn.query('SELECT count(*) AS `count` FROM `answers` WHERE `poll_id` = ?',[id],function(err,data){
  306. var q_count = typeof data[0] == "undefined" ? 0 : data[0].count;
  307. cb(err,q_count);
  308. });
  309. },
  310. meta:function(cb){
  311. sql.conn.query('SELECT * FROM `polls` WHERE `id` = ?',[id],function(err,meta){
  312. cb(err,meta);
  313. });
  314. },
  315. questionnaires:function(cb){
  316. sql.conn.query('SELECT COUNT(*) AS `count`, `date` FROM `answers` WHERE `poll_id` = ? GROUP BY `date` ORDER BY `date` DESC',[id],function(err,result){
  317. cb(err,result);
  318. });
  319. }
  320. },function(err,result){
  321. if(err){
  322. res.send(view.error(err));
  323. }
  324. else {
  325. if(result.meta.length === 0){
  326. res.send(view.error(new Error("The poll specified doesn't exists anymore.")));
  327. }
  328. else {
  329. res.send(view.page('stats',{
  330. count:{
  331. users:result.t_count,
  332. total:result.q_count
  333. },
  334. meta:result.meta[0],
  335. id:id,
  336. questions:result.questions,
  337. questionnaires:result.questionnaires
  338. }, req.acceptedCharsets[0]));
  339. }
  340. }
  341. });
  342. });
  343. app.get('/stats/time/:id',function(req,res){
  344. sql.conn.query('SELECT COUNT(*) AS `count`, `time` FROM `stats` WHERE `poll_id` = ? GROUP BY `time` ORDER BY `time` ASC',[req.params.id],function(err,data){
  345. var intital_date = null;
  346. var _return = [];
  347. var _return_i = 0;
  348. for(var i = 0; i < data.length; i++){
  349. data[i].time.setHours(0);
  350. data[i].time.setMinutes(0);
  351. data[i].time.setSeconds(0);
  352. if(intital_date == null || (data[i].time.getFullYear() !== intital_date.getFullYear() || data[i].time.getMonth() !== intital_date.getMonth() || data[i].time.getDate() !== intital_date.getDate())){
  353. _return.push([data[i].time.getTime(),data[i].count]);
  354. _return_i = _return.length == 1 ? 0 : _return_i+1;
  355. }
  356. else {
  357. _return[_return_i][1] += (data[i].count);
  358. }
  359. intital_date = data[i].time;
  360. }
  361. res.json(_return);
  362. });
  363. });
  364. app.get('/stats/questionnaire/:id/:time',function(req,res){
  365. var id = req.params.id;
  366. var time = req.params.time;
  367. sql.conn.query('SELECT q.`name`, a.`value` FROM `questions` AS q INNER JOIN `answers` AS a ON (q.`id` = a.`question_id`) WHERE a.`poll_id` = ? AND a.`date` = ? ORDER BY a.`date` DESC',[id,new Date(time)],function(err,result){
  368. res.json(result);
  369. });
  370. });
  371. app.get('/stats/question/:id',function(req,res){
  372. sql.conn.query('SELECT `type` FROM `questions` WHERE `id` = ?',[req.params.id],function(err,type){
  373. sql.conn.query('SELECT COUNT(*) AS `count`, `value` FROM `answers` WHERE `question_id` = ? GROUP BY `value` ORDER BY `value` ASC',[req.params.id],function(err,data){
  374. switch(type[0].type){
  375. case 'mc':
  376. //MC questions, send the data to the client
  377. var _data = [];
  378. for(var i = 0; i < data.length; i++){
  379. _data.push({
  380. data:[
  381. [0,data[i].count]
  382. ],
  383. label:data[i].value
  384. });
  385. }
  386. res.json(_data);
  387. break;
  388. case 'mmc':
  389. //Multiple answers, show a multiple answers chart
  390. var _data = {}, _final = [];
  391. sql.conn.query('SELECT COUNT(*) AS `count`, `value` FROM `answers` WHERE `question_id` = ? GROUP BY `value` ORDER BY `value` ASC',[req.params.id],function(err,data){
  392. for(var i = 0; i < data.length; i++){
  393. var choices = String(data[i].value).split(',');
  394. for(var j = 0; j < choices.length; j++){
  395. if(_data[choices[j]]){
  396. _data[choices[j]] += data[i].count;
  397. }
  398. else {
  399. _data[choices[j]] = data[i].count;
  400. }
  401. }
  402. }
  403. for(var i in _data){
  404. _final.push({
  405. data:[
  406. [0,_data[i]]
  407. ],
  408. label:i
  409. });
  410. }
  411. res.json(_final);
  412. });
  413. break;
  414. case 'fillin':
  415. sql.conn.query('SELECT `value` FROM `answers` WHERE `question_id` = ? AND `value` != \'\'',[req.params.id],function(err,data){
  416. res.json({
  417. html:view.page('ques_dialog',{
  418. data:data,
  419. type:type[0].type,
  420. id:req.params.id
  421. })
  422. });
  423. });
  424. break;
  425. default:
  426. res.json({});
  427. break;
  428. }
  429. });
  430. });
  431. });
  432. module.exports = {
  433. start:function(){
  434. http.listen(process.env.OPENSHIFT_INTERNAL_PORT||8000,process.env.OPENSHIFT_INTERNAL_IP||'127.0.0.1');
  435. console.log('Server #%d listening at port %d',process.pid,process.env.OPENSHIFT_INTERNAL_PORT||8000);
  436. }
  437. };