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.

521 lines
14 KiB

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