Personal blog written from scratch using Node.js, Bootstrap, and MySQL. https://jrtechs.net
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

523 lines
14 KiB

  1. /**
  2. * Boated file which handles all the SQL
  3. * queries ran by the server
  4. *
  5. * @author Jeffery Russell
  6. */
  7. const mysql = require('mysql');
  8. /** Sanitizer to clean user inputs and prevent SQL injections */
  9. const sanitizer = require('sanitizer');
  10. /** Crypto package used for hashing */
  11. const crypto = require('crypto');
  12. /** Used to parse post data */
  13. const qs = require('querystring');
  14. /** Used to load the config file from the disk */
  15. const config = require('../utils/configLoader').getConfig();
  16. /** SQL connection */
  17. const con = mysql.createConnection({
  18. host: config.SQL_HOST,
  19. user: config.SQL_USER,
  20. password: config.SQL_PASSWORD,
  21. database: config.SQL_DATABASE
  22. });
  23. con.connect(function(err) {
  24. if (err)
  25. console.log(err);
  26. });
  27. /**
  28. * Function used to query the database for records
  29. *
  30. * @param sqlStatement
  31. * @returns {Array}
  32. */
  33. const fetch = function(sqlStatement)
  34. {
  35. return new Promise(function(resolve, reject)
  36. {
  37. con.query(sanitizer.sanitize(sqlStatement), function (err, result)
  38. {
  39. if(err)
  40. {
  41. console.log(err);
  42. reject(err);
  43. }
  44. resolve(result);
  45. });
  46. });
  47. };
  48. module.exports=
  49. {
  50. /**
  51. * Function used to use insert statements into the database
  52. *
  53. * Don't worry, the input gets sanitized
  54. *
  55. * @param sqlStatement
  56. * @return the id of the new record - if there is one
  57. */
  58. insert : function(sqlStatement)
  59. {
  60. return new Promise(function(resolve, reject)
  61. {
  62. con.query(sanitizer.sanitize(sqlStatement), function (err, result)
  63. {
  64. if (err)
  65. {
  66. console.log(err);
  67. reject();
  68. }
  69. resolve(result.insertId);
  70. });
  71. })
  72. },
  73. /**
  74. * function which fetches the sql info on a post based on it's sql id
  75. * @param id
  76. * @returns {Array}
  77. */
  78. getPostById: function(id)
  79. {
  80. console.log("select * from posts where post_id='" + id + "' limit 1");
  81. return new Promise(function(resolve, reject)
  82. {
  83. fetch("select * from posts where post_id='" + id + "' limit 1")
  84. .then(function(post)
  85. {
  86. resolve(post[0]);
  87. }).catch(function(error)
  88. {
  89. reject(error);
  90. });
  91. });
  92. },
  93. /**
  94. * Not to be mistaken for getPostData() in @file utils/utils.js,
  95. * this function extracts a post entry from the sql server
  96. *
  97. * @param requestURL url user used to request blog post
  98. * @return {*} the entry found in the data base -- if any
  99. */
  100. getPost : function(requestURL)
  101. {
  102. return new Promise(function(resolve, reject)
  103. {
  104. var splitURL = requestURL.split("/")
  105. var q = "select * from categories where url='" + splitURL[1] + "'";
  106. fetch(q).then(function (result_category)
  107. {
  108. if(result_category.length != 0)
  109. {
  110. var q2 = "select * from posts where category_id='" +
  111. result_category[0].category_id +
  112. "' and url='" + splitURL[2] + "'";
  113. fetch(q2).then(function (result_posts)
  114. {
  115. resolve(result_posts);
  116. });
  117. }
  118. else
  119. {
  120. resolve(0);
  121. }
  122. });
  123. });
  124. },
  125. /**
  126. * Function used to retrieve all categories when making the sidebar
  127. *
  128. * @return {Promise<Response> | * | Array}
  129. */
  130. getCategories : function()
  131. {
  132. var q = "select * from categories";
  133. return fetch(q);
  134. },
  135. /**
  136. * Function which currently returns all blog of a particular
  137. * category from the database
  138. * @param requestURL
  139. * @return {*|Promise}
  140. */
  141. getPostsFromCategory: function(requestURL)
  142. {
  143. return new Promise(function(resolve, reject)
  144. {
  145. var q = "select * from categories where url ='" + requestURL + "'";
  146. fetch(q).then(function(categories)
  147. {
  148. if(categories.length != 0)
  149. {
  150. var qPosts = "select * from posts where category_id='" +
  151. categories[0].category_id + "' order by published desc";
  152. resolve(fetch(qPosts));
  153. }
  154. else
  155. {
  156. resolve([]);
  157. }
  158. });
  159. });
  160. },
  161. /**
  162. * Fetches the recent posts from the database.
  163. * @returns {Array}
  164. */
  165. getRecentPostSQL: function()
  166. {
  167. return fetch("select * from posts order by post_id desc");
  168. },
  169. /**
  170. * Helper method which returns a list of objects which contains the url
  171. * and name of thee ten most recent posts
  172. *
  173. * {[name: , url: ],[name: , url: ],[name: , url: ],...}
  174. *
  175. * @return {*|Promise}
  176. */
  177. getRecentPosts: function()
  178. {
  179. return new Promise(function(resolve, reject)
  180. {
  181. var q = "select name,url, category_id from posts order " +
  182. "by post_id desc limit 10";
  183. fetch(q).then(function(sqlPosts)
  184. {
  185. var promises = [];
  186. sqlPosts.forEach(function(post)
  187. {
  188. promises.push(new Promise(function(res, rej)
  189. {
  190. var getCategory = "select url from categories where " +
  191. "category_id='" + post.category_id + "'";
  192. fetch(getCategory).then(function(urls)
  193. {
  194. var obj = new Object();
  195. obj.name = post.name;
  196. obj.url = post.url;
  197. obj.category = urls[0].url;
  198. res(obj);
  199. });
  200. }));
  201. });
  202. Promise.all(promises).then(function(goodies)
  203. {
  204. resolve(goodies);
  205. });
  206. });
  207. });
  208. },
  209. /**
  210. * TODO
  211. * @returns {*|Promise}
  212. */
  213. getPopularPosts: function()
  214. {
  215. return new Promise(function(resolve, reject)
  216. {
  217. var q = "select * from popular_posts";
  218. fetch(q).then(function(sqlPosts)
  219. {
  220. });
  221. });
  222. },
  223. /**
  224. * Function which checks to see if a user successfully logged in based on
  225. * the post data which they sent
  226. *
  227. * @param postData the post data
  228. * @return {*|Promise} a json object with {pass: , user: }
  229. * the pass is whether or not they logged in successfully and the user is
  230. * the username they successfully logged in with
  231. */
  232. checkLogin: function(postData)
  233. {
  234. const post = qs.parse(postData);
  235. return new Promise(function(resolve, reject)
  236. {
  237. var result = Object();
  238. result.pass = false;
  239. if(post.username && post.password)
  240. {
  241. const cleanName = sanitizer.sanitize(post.username);
  242. const cleanPassword = sanitizer.sanitize(post.password);
  243. const getSalt = "select * from users where user_name='" +
  244. cleanName + "'";
  245. fetch(getSalt).then(function(saltResult)
  246. {
  247. if(saltResult.length == 1)
  248. {
  249. const hashedPassword = crypto.createHash('sha256')
  250. .update(cleanPassword + saltResult[0].salt)
  251. .digest('hex');
  252. if(saltResult[0].password === hashedPassword)
  253. {
  254. result.pass = true;
  255. result.user = cleanName;
  256. resolve(result);
  257. }
  258. else
  259. {
  260. resolve(result)
  261. }
  262. }
  263. else
  264. {
  265. //incorrect username
  266. resolve(result);
  267. }
  268. })
  269. }
  270. else
  271. {
  272. //no login attempts were made
  273. resolve(result);
  274. }
  275. });
  276. },
  277. /**
  278. * Fetches a promise containing every post in the database
  279. * @returns {Array}
  280. */
  281. getAllPosts: function()
  282. {
  283. return fetch("select * from posts order by published desc");
  284. },
  285. /**
  286. * Fetches the sql category information based on it's id
  287. * @param categoryId
  288. * @returns {Array}
  289. */
  290. getCategory: function(categoryId)
  291. {
  292. return fetch("select * from categories where category_id='"
  293. + categoryId + "'");
  294. },
  295. /**Returns download information associated with a download name
  296. *
  297. * @param downloadURL
  298. * @returns {Array}
  299. */
  300. getDownload: function(downloadURL)
  301. {
  302. var cleanD = sanitizer.sanitize(downloadURL);
  303. var q = "select * from downloads where name='" + cleanD + "' limit 1";
  304. return new Promise(function(resolve, reject)
  305. {
  306. fetch(q).then(function(sqlData)
  307. {
  308. return module.exports.incrementDownloadCount(sqlData);
  309. }).then(function(sqlData)
  310. {
  311. resolve(sqlData)
  312. }).catch(function(error)
  313. {
  314. reject(error);
  315. })
  316. });
  317. },
  318. /** Increments the download count in the database
  319. *
  320. * @param sqlRow
  321. * @returns {*|Promise}
  322. */
  323. incrementDownloadCount: function(sqlRow)
  324. {
  325. return new Promise(function(resolve, reject)
  326. {
  327. if(sqlRow.length == 1)
  328. {
  329. var q = "update downloads set download_count='" +
  330. (sqlRow[0].download_count + 1) + "' where download_id='" +
  331. sqlRow[0].download_id + "'";
  332. console.log(q);
  333. module.exports.insert(q).then(function(r)
  334. {
  335. resolve(sqlRow);
  336. }).catch(function(err)
  337. {
  338. reject(err);
  339. })
  340. }
  341. else
  342. {
  343. resolve(sqlRow);
  344. }
  345. });
  346. },
  347. /**
  348. * Fetches all the downloads from the database
  349. *
  350. * @returns {Array}
  351. */
  352. getAllDownloads: function()
  353. {
  354. return fetch("select * from downloads");
  355. },
  356. /**
  357. * Inserts a download row into the database
  358. *
  359. * @param name of the download
  360. * @param file name of file
  361. * @returns {*|the}
  362. */
  363. addDownload: function(name, file)
  364. {
  365. const q = "insert into downloads (name, file, download_count) " +
  366. "values('" + name + "', '" + file + "', '0')";
  367. return module.exports.insert(q);
  368. },
  369. /**
  370. *
  371. * @param id
  372. */
  373. removeDownload: function(id)
  374. {
  375. const q = "delete from downloads where download_id='" + id + "'";
  376. return module.exports.insert(q);
  377. },
  378. /**
  379. * Based on the post data submitted by the user this function updates
  380. * the information on the post in the database
  381. * @param postData
  382. * @returns {*|the}
  383. */
  384. editPost: function(postData)
  385. {
  386. const url = postData.edit_name_new.split(" ").join("-").toLowerCase();
  387. const q = "update posts " +
  388. "set category_id='" + postData.edit_cat_num + "' " +
  389. ",name='" + postData.edit_name_new + "' " +
  390. ",url='" + url + "' " +
  391. ",picture_url='" + postData.edit_pic + "' " +
  392. ",published='" + postData.edit_date + "' " +
  393. " where post_id='" + postData.edit_post_2 + "'";
  394. return module.exports.insert(q);
  395. },
  396. /**
  397. * Function which returns a promise which contains the string of the
  398. * entire sitemap for the blog.
  399. * @returns {Promise|*}
  400. */
  401. getSiteMap: function()
  402. {
  403. return new Promise(function(resolve, reject)
  404. {
  405. const base = "http://jrtechs.net/";
  406. var sm = base + "\n";
  407. var promises = [];
  408. module.exports.getCategories().then(function(categories)
  409. {
  410. categories.forEach(function(cat)
  411. {
  412. promises.push(new Promise(function(res, rej)
  413. {
  414. sm += base + "category/" + cat.url + "\n";
  415. module.exports.getPostsFromCategory(cat.url).then(function(posts)
  416. {
  417. posts.forEach(function(post)
  418. {
  419. sm += base + cat.url + "/" + post.url + "\n";
  420. });
  421. res()
  422. })
  423. }));
  424. });
  425. Promise.all(promises).then(function()
  426. {
  427. resolve(sm);
  428. }).catch(function(error)
  429. {
  430. throw error;
  431. });
  432. });
  433. });
  434. },
  435. /**
  436. * Logs visited page for backend server analytics.
  437. *
  438. * @param ip
  439. * @param page
  440. */
  441. logTraffic: function(ip, page)
  442. {
  443. if(page.length > 40)
  444. {
  445. console.log("Error, request too long to log ip:"
  446. + ip + " page: " + page);
  447. return;
  448. }
  449. if(ip.length > 20)
  450. {
  451. ip = "";
  452. }
  453. const q = "insert into traffic_log (url, ip, date) values " +
  454. "('" + page + "', '" + ip + "', now())";
  455. module.exports.insert(q);
  456. }
  457. };