links.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. const promisify = require("util").promisify;
  2. const bcrypt = require("bcryptjs");
  3. const isbot = require("isbot");
  4. const URL = require("url");
  5. const dns = require("dns");
  6. const validators = require("./validators");
  7. // const transporter = require("../mail");
  8. const query = require("../queries");
  9. // const queue = require("../queues");
  10. const utils = require("../utils");
  11. const env = require("../env");
  12. const CustomError = utils.CustomError;
  13. const dnsLookup = promisify(dns.lookup);
  14. // export const get: Handler = async (req, res) => {
  15. // const { limit, skip, all } = req.context;
  16. // const search = req.query.search as string;
  17. // const userId = req.user.id;
  18. // const match = {
  19. // ...(!all && { user_id: userId })
  20. // };
  21. // const [links, total] = await Promise.all([
  22. // query.link.get(match, { limit, search, skip }),
  23. // query.link.total(match, { search })
  24. // ]);
  25. // const data = links.map(utils.sanitize.link);
  26. // return res.send({
  27. // total,
  28. // limit,
  29. // skip,
  30. // data
  31. // });
  32. // };
  33. /**
  34. * @type {import("express").Handler}
  35. */
  36. async function create(req, res) {
  37. const { reuse, password, customurl, description, target, domain, expire_in } = req.body;
  38. const domain_id = domain ? domain.id : null;
  39. const targetDomain = utils.removeWww(URL.parse(target).hostname);
  40. const queries = await Promise.all([
  41. validators.cooldown(req.user),
  42. validators.malware(req.user, target),
  43. validators.linksCount(req.user),
  44. reuse &&
  45. query.link.find({
  46. target,
  47. user_id: req.user.id,
  48. domain_id
  49. }),
  50. customurl &&
  51. query.link.find({
  52. address: customurl,
  53. domain_id
  54. }),
  55. !customurl && utils.generateId(domain_id),
  56. validators.bannedDomain(targetDomain),
  57. validators.bannedHost(targetDomain)
  58. ]);
  59. // if "reuse" is true, try to return
  60. // the existent URL without creating one
  61. if (queries[3]) {
  62. return res.json(utils.sanitize.link(queries[3]));
  63. }
  64. // Check if custom link already exists
  65. if (queries[4]) {
  66. throw new CustomError("Custom URL is already in use.");
  67. }
  68. const accepts = req.accepts(["json", "html"]);
  69. // Create new link
  70. const address = customurl || queries[5];
  71. const link = await query.link.create({
  72. password,
  73. address,
  74. domain_id,
  75. description,
  76. target,
  77. expire_in,
  78. user_id: req.user && req.user.id
  79. });
  80. if (!req.user && env.NON_USER_COOLDOWN) {
  81. query.ip.add(req.realIP);
  82. }
  83. if (accepts === "html") {
  84. const shortURL = utils.getShortURL(link.address, link.domain);
  85. return res.render("partials/shorturl", {
  86. layout: null,
  87. link: shortURL.link,
  88. url: shortURL.url,
  89. });
  90. }
  91. return res
  92. .status(201)
  93. .send(utils.sanitize.link({ ...link, domain: domain?.address }));
  94. }
  95. // export const edit: Handler = async (req, res) => {
  96. // const { address, target, description, expire_in, password } = req.body;
  97. // if (!address && !target) {
  98. // throw new CustomError("Should at least update one field.");
  99. // }
  100. // const link = await query.link.find({
  101. // uuid: req.params.id,
  102. // ...(!req.user.admin && { user_id: req.user.id })
  103. // });
  104. // if (!link) {
  105. // throw new CustomError("Link was not found.");
  106. // }
  107. // const targetDomain = utils.removeWww(URL.parse(target).hostname);
  108. // const domain_id = link.domain_id || null;
  109. // const queries = await Promise.all([
  110. // validators.cooldown(req.user),
  111. // validators.malware(req.user, target),
  112. // address !== link.address &&
  113. // query.link.find({
  114. // address,
  115. // domain_id
  116. // }),
  117. // validators.bannedDomain(targetDomain),
  118. // validators.bannedHost(targetDomain)
  119. // ]);
  120. // // Check if custom link already exists
  121. // if (queries[2]) {
  122. // throw new CustomError("Custom URL is already in use.");
  123. // }
  124. // // Update link
  125. // const [updatedLink] = await query.link.update(
  126. // {
  127. // id: link.id
  128. // },
  129. // {
  130. // ...(address && { address }),
  131. // ...(description && { description }),
  132. // ...(target && { target }),
  133. // ...(expire_in && { expire_in }),
  134. // ...(password && { password })
  135. // }
  136. // );
  137. // return res.status(200).send(utils.sanitize.link({ ...link, ...updatedLink }));
  138. // };
  139. // export const remove: Handler = async (req, res) => {
  140. // const link = await query.link.remove({
  141. // uuid: req.params.id,
  142. // ...(!req.user.admin && { user_id: req.user.id })
  143. // });
  144. // if (!link) {
  145. // throw new CustomError("Could not delete the link");
  146. // }
  147. // return res
  148. // .status(200)
  149. // .send({ message: "Link has been deleted successfully." });
  150. // };
  151. // export const report: Handler = async (req, res) => {
  152. // const { link } = req.body;
  153. // const mail = await transporter.sendMail({
  154. // from: env.MAIL_FROM || env.MAIL_USER,
  155. // to: env.REPORT_EMAIL,
  156. // subject: "[REPORT]",
  157. // text: link,
  158. // html: link
  159. // });
  160. // if (!mail.accepted.length) {
  161. // throw new CustomError("Couldn't submit the report. Try again later.");
  162. // }
  163. // return res
  164. // .status(200)
  165. // .send({ message: "Thanks for the report, we'll take actions shortly." });
  166. // };
  167. // export const ban: Handler = async (req, res) => {
  168. // const { id } = req.params;
  169. // const update = {
  170. // banned_by_id: req.user.id,
  171. // banned: true
  172. // };
  173. // // 1. Check if link exists
  174. // const link = await query.link.find({ uuid: id });
  175. // if (!link) {
  176. // throw new CustomError("No link has been found.", 400);
  177. // }
  178. // if (link.banned) {
  179. // return res.status(200).send({ message: "Link has been banned already." });
  180. // }
  181. // const tasks = [];
  182. // // 2. Ban link
  183. // tasks.push(query.link.update({ uuid: id }, update));
  184. // const domain = utils.removeWww(URL.parse(link.target).hostname);
  185. // // 3. Ban target's domain
  186. // if (req.body.domain) {
  187. // tasks.push(query.domain.add({ ...update, address: domain }));
  188. // }
  189. // // 4. Ban target's host
  190. // if (req.body.host) {
  191. // const dnsRes = await dnsLookup(domain).catch(() => {
  192. // throw new CustomError("Couldn't fetch DNS info.");
  193. // });
  194. // const host = dnsRes?.address;
  195. // tasks.push(query.host.add({ ...update, address: host }));
  196. // }
  197. // // 5. Ban link owner
  198. // if (req.body.user && link.user_id) {
  199. // tasks.push(query.user.update({ id: link.user_id }, update));
  200. // }
  201. // // 6. Ban all of owner's links
  202. // if (req.body.userLinks && link.user_id) {
  203. // tasks.push(query.link.update({ user_id: link.user_id }, update));
  204. // }
  205. // // 7. Wait for all tasks to finish
  206. // await Promise.all(tasks).catch(() => {
  207. // throw new CustomError("Couldn't ban entries.");
  208. // });
  209. // // 8. Send response
  210. // return res.status(200).send({ message: "Banned link successfully." });
  211. // };
  212. // export const redirect = (app) => async (
  213. // req,
  214. // res,
  215. // next
  216. // ) => {
  217. // const isBot = isbot(req.headers["user-agent"]);
  218. // const isPreservedUrl = validators.preservedUrls.some(
  219. // item => item === req.path.replace("/", "")
  220. // );
  221. // if (isPreservedUrl) return next();
  222. // // 1. If custom domain, get domain info
  223. // const host = utils.removeWww(req.headers.host);
  224. // const domain =
  225. // host !== env.DEFAULT_DOMAIN
  226. // ? await query.domain.find({ address: host })
  227. // : null;
  228. // // 2. Get link
  229. // const address = req.params.id.replace("+", "");
  230. // const link = await query.link.find({
  231. // address,
  232. // domain_id: domain ? domain.id : null
  233. // });
  234. // // 3. When no link, if has domain redirect to domain's homepage
  235. // // otherwise redirect to 404
  236. // if (!link) {
  237. // return res.redirect(302, domain ? domain.homepage : "/404");
  238. // }
  239. // // 4. If link is banned, redirect to banned page.
  240. // if (link.banned) {
  241. // return res.redirect("/banned");
  242. // }
  243. // // 5. If wants to see link info, then redirect
  244. // const doesRequestInfo = /.*\+$/gi.test(req.params.id);
  245. // if (doesRequestInfo && !link.password) {
  246. // return app.render(req, res, "/url-info", { target: link.target });
  247. // }
  248. // // 6. If link is protected, redirect to password page
  249. // if (link.password) {
  250. // return res.redirect(`/protected/${link.uuid}`);
  251. // }
  252. // // 7. Create link visit
  253. // if (link.user_id && !isBot) {
  254. // queue.visit.add({
  255. // headers: req.headers,
  256. // realIP: req.realIP,
  257. // referrer: req.get("Referrer"),
  258. // link
  259. // });
  260. // }
  261. // // 8. Redirect to target
  262. // return res.redirect(link.target);
  263. // };
  264. // export const redirectProtected: Handler = async (req, res) => {
  265. // // 1. Get link
  266. // const uuid = req.params.id;
  267. // const link = await query.link.find({ uuid });
  268. // // 2. Throw error if no link
  269. // if (!link || !link.password) {
  270. // throw new CustomError("Couldn't find the link.", 400);
  271. // }
  272. // // 3. Check if password matches
  273. // const matches = await bcrypt.compare(req.body.password, link.password);
  274. // if (!matches) {
  275. // throw new CustomError("Password is not correct.", 401);
  276. // }
  277. // // 4. Create visit
  278. // if (link.user_id) {
  279. // queue.visit.add({
  280. // headers: req.headers,
  281. // realIP: req.realIP,
  282. // referrer: req.get("Referrer"),
  283. // link
  284. // });
  285. // }
  286. // // 5. Send target
  287. // return res.status(200).send({ target: link.target });
  288. // };
  289. // export const redirectCustomDomain: Handler = async (req, res, next) => {
  290. // const { path } = req;
  291. // const host = utils.removeWww(req.headers.host);
  292. // if (host === env.DEFAULT_DOMAIN) {
  293. // return next();
  294. // }
  295. // if (
  296. // path === "/" ||
  297. // validators.preservedUrls
  298. // .filter(l => l !== "url-password")
  299. // .some(item => item === path.replace("/", ""))
  300. // ) {
  301. // const domain = await query.domain.find({ address: host });
  302. // const redirectURL = domain
  303. // ? domain.homepage
  304. // : `https://${env.DEFAULT_DOMAIN + path}`;
  305. // return res.redirect(302, redirectURL);
  306. // }
  307. // return next();
  308. // };
  309. // export const stats: Handler = async (req, res) => {
  310. // const { user } = req;
  311. // const uuid = req.params.id;
  312. // const link = await query.link.find({
  313. // ...(!user.admin && { user_id: user.id }),
  314. // uuid
  315. // });
  316. // if (!link) {
  317. // throw new CustomError("Link could not be found.");
  318. // }
  319. // const stats = await query.visit.find({ link_id: link.id }, link.visit_count);
  320. // if (!stats) {
  321. // throw new CustomError("Could not get the short link stats.");
  322. // }
  323. // return res.status(200).send({
  324. // ...stats,
  325. // ...utils.sanitize.link(link)
  326. // });
  327. // };
  328. module.exports = {
  329. create,
  330. }