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.

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