urlController.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. const urlRegex = require('url-regex');
  2. const URL = require('url');
  3. const useragent = require('useragent');
  4. const geoip = require('geoip-lite');
  5. const bcrypt = require('bcryptjs');
  6. const {
  7. createShortUrl,
  8. createVisit,
  9. findUrl,
  10. getStats,
  11. getUrls,
  12. getCustomDomain,
  13. setCustomDomain,
  14. deleteCustomDomain,
  15. deleteUrl,
  16. } = require('../db/url');
  17. const { addProtocol } = require('../utils');
  18. const config = require('../config');
  19. exports.urlShortener = async ({ body, user }, res) => {
  20. // Check if custom URL already exists
  21. if (user && body.customurl) {
  22. const urls = await findUrl({ id: body.customurl || '' });
  23. if (urls.length) {
  24. const urlWithNoDomain = !user.domain && urls.some(url => !url.domain);
  25. const urlWithDmoain = user.domain && urls.some(url => url.domain === user.domain);
  26. if (urlWithNoDomain || urlWithDmoain) {
  27. return res.status(400).json({ error: 'Custom URL is already in use.' });
  28. }
  29. }
  30. }
  31. // Create new URL
  32. const target = addProtocol(body.target);
  33. const url = await createShortUrl({ ...body, target, user });
  34. return res.json(url);
  35. };
  36. const browsersList = ['IE', 'Firefox', 'Chrome', 'Opera', 'Safari', 'Edge'];
  37. const osList = ['Windows', 'Mac Os X', 'Linux', 'Chrome OS', 'Android', 'iOS'];
  38. const botList = ['bot', 'dataminr', 'pinterest', 'yahoo', 'facebook', 'crawl'];
  39. const filterInBrowser = agent => item =>
  40. agent.family.toLowerCase().includes(item.toLocaleLowerCase());
  41. const filterInOs = agent => item =>
  42. agent.os.family.toLowerCase().includes(item.toLocaleLowerCase());
  43. exports.goToUrl = async (req, res, next) => {
  44. const { host } = req.headers;
  45. const id = req.params.id || req.body.id;
  46. const domain = host !== config.DEFAULT_DOMAIN && host;
  47. const agent = useragent.parse(req.headers['user-agent']);
  48. const [browser = 'Other'] = browsersList.filter(filterInBrowser(agent));
  49. const [os = 'Other'] = osList.filter(filterInOs(agent));
  50. const referrer = req.header('Referer') && URL.parse(req.header('Referer')).hostname;
  51. const location = geoip.lookup(req.realIp);
  52. const country = location && location.country;
  53. const urls = await findUrl({ id, domain });
  54. const isBot =
  55. botList.some(bot => agent.source.toLowerCase().includes(bot)) || agent.family === 'Other';
  56. if (!urls && !urls.length) return next();
  57. const [url] = urls;
  58. if (url.password && !req.body.password) {
  59. req.protectedUrl = id;
  60. return next();
  61. }
  62. if (url.password) {
  63. const isMatch = await bcrypt.compare(req.body.password, url.password);
  64. if (!isMatch) {
  65. return res.status(401).json({ error: 'Password is not correct' });
  66. }
  67. if (url.user && !isBot) {
  68. await createVisit({
  69. browser,
  70. country: country || 'Unknown',
  71. domain,
  72. id: url.id,
  73. os,
  74. referrer: referrer || 'Direct',
  75. });
  76. }
  77. return res.status(200).json({ target: url.target });
  78. }
  79. if (url.user && !isBot) {
  80. await createVisit({
  81. browser,
  82. country: country || 'Unknown',
  83. domain,
  84. id: url.id,
  85. os,
  86. referrer: referrer || 'Direct',
  87. });
  88. }
  89. return res.redirect(url.target);
  90. };
  91. exports.getUrls = async ({ query, user }, res) => {
  92. const urlsList = await getUrls({ options: query, user });
  93. return res.json(urlsList);
  94. };
  95. exports.setCustomDomain = async ({ body: { customDomain }, user }, res) => {
  96. if (customDomain.length > 40) {
  97. return res.status(400).json({ error: 'Maximum custom domain length is 40.' });
  98. }
  99. if (customDomain === config.DEFAULT_DOMAIN) {
  100. return res.status(400).json({ error: "You can't use default domain." });
  101. }
  102. const isValidDomain = urlRegex({ exact: true, strict: false }).test(customDomain);
  103. if (!isValidDomain) return res.status(400).json({ error: 'Domain is not valid.' });
  104. const isOwned = await getCustomDomain({ customDomain });
  105. if (isOwned && isOwned.email !== user.email) {
  106. return res
  107. .status(400)
  108. .json({ error: 'Domain is already taken. Contact us for multiple users.' });
  109. }
  110. const userCustomDomain = await setCustomDomain({ user, customDomain });
  111. if (userCustomDomain) return res.status(201).json({ customDomain: userCustomDomain.name });
  112. return res.status(400).json({ error: "Couldn't set custom domain." });
  113. };
  114. exports.deleteCustomDomain = async ({ user }, res) => {
  115. const response = await deleteCustomDomain({ user });
  116. if (response) return res.status(200).json({ message: 'Domain deleted successfully' });
  117. return res.status(400).json({ error: "Couldn't delete custom domain." });
  118. };
  119. exports.deleteUrl = async ({ body: { id, domain }, user }, res) => {
  120. if (!id) return res.status(400).json({ error: 'No id has been provided.' });
  121. const customDomain = domain !== config.DEFAULT_DOMAIN && domain;
  122. const urls = await findUrl({ id, domain: customDomain });
  123. if (!urls && !urls.length) return res.status(400).json({ error: "Couldn't find the short URL." });
  124. const response = await deleteUrl({ id, domain: customDomain, user });
  125. if (response) return res.status(200).json({ message: 'Sort URL deleted successfully' });
  126. return res.status(400).json({ error: "Couldn't delete short URL." });
  127. };
  128. exports.getStats = async ({ query: { id, domain }, user }, res) => {
  129. if (!id) return res.status(400).json({ error: 'No id has been provided.' });
  130. const customDomain = domain !== config.DEFAULT_DOMAIN && domain;
  131. const stats = await getStats({ id, domain: customDomain, user });
  132. if (!stats) return res.status(400).json({ error: 'Could not get the short URL stats.' });
  133. return res.status(200).json(stats);
  134. };