Parcourir la source

Add "reuse" option to the API

Pouria Ezzati il y a 7 ans
Parent
commit
79dc27832a
4 fichiers modifiés avec 42 ajouts et 7 suppressions
  1. 3 0
      README.md
  2. 28 2
      server/controllers/urlController.js
  3. 7 5
      server/db/url.js
  4. 4 0
      server/utils/index.js

+ 3 - 0
README.md

@@ -93,6 +93,9 @@ POST /api/url/submit
 ```
 Body:
   * `target`: Original long URL to be shortened.
+  * `customurl` (optional, users only): Set a custom URL.
+  * `passowrd` (optional, users only): Set a password.
+  * `reuse` (optional, users only): If a URL with the specified target exists returns it, otherwise will send a new shortened URL.
 
 Returns: URL object
 

+ 28 - 2
server/controllers/urlController.js

@@ -1,5 +1,6 @@
 const urlRegex = require('url-regex');
 const URL = require('url');
+const generate = require('nanoid/generate');
 const useragent = require('useragent');
 const geoip = require('geoip-lite');
 const bcrypt = require('bcryptjs');
@@ -14,10 +15,34 @@ const {
   deleteCustomDomain,
   deleteUrl,
 } = require('../db/url');
-const { addProtocol } = require('../utils');
+const { addProtocol, generateShortUrl } = require('../utils');
 const config = require('../config');
 
+const generateId = async () => {
+  const id = generate('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', 6);
+  const urls = await findUrl({ id });
+  if (!urls.length) return id;
+  return generateId();
+};
+
 exports.urlShortener = async ({ body, user }, res) => {
+  // if "reuse" is true, try to return
+  // the existent URL without creating one
+  if (user && body.reuse) {
+    const urls = await findUrl({ target: addProtocol(body.target) });
+    if (urls.length) {
+      urls.sort((a, b) => a.createdAt > b.createdAt);
+      const { domain: d, user: u, ...url } = urls[urls.length - 1];
+      const data = {
+        ...url,
+        password: !!url.password,
+        reuse: true,
+        shortUrl: generateShortUrl(url.id, user.domain),
+      };
+      return res.json(data);
+    }
+  }
+
   // Check if custom URL already exists
   if (user && body.customurl) {
     const urls = await findUrl({ id: body.customurl || '' });
@@ -31,8 +56,9 @@ exports.urlShortener = async ({ body, user }, res) => {
   }
 
   // Create new URL
+  const id = (user && body.customurl) || (await generateId());
   const target = addProtocol(body.target);
-  const url = await createShortUrl({ ...body, target, user });
+  const url = await createShortUrl({ ...body, id, target, user });
 
   return res.json(url);
 };

+ 7 - 5
server/db/url.js

@@ -12,6 +12,7 @@ const {
 } = require('date-fns');
 const driver = require('./neo4j');
 const config = require('../config');
+const { generateShortUrl } = require('../utils');
 
 const getUTCDate = (dateString = Date.now()) => {
   const date = new Date(dateString);
@@ -43,7 +44,7 @@ exports.createShortUrl = params =>
           createdAt: new Date().toJSON(),
           domain: params.user && params.user.domain,
           email: params.user && params.user.email,
-          id: (params.user && params.customurl) || generateId(),
+          id: params.id,
           password: hash || '',
           target: params.target,
         })
@@ -54,8 +55,8 @@ exports.createShortUrl = params =>
         resolve({
           ...data,
           password: !!data.password,
-          shortUrl: `http${!params.user.domain ? 's' : ''}://${params.user.domain ||
-            config.DEFAULT_DOMAIN}/${data.id}`,
+          reuse: !!params.reuse,
+          shortUrl: generateShortUrl(data.id, params.user.domain),
         });
       })
       .catch(reject);
@@ -101,13 +102,13 @@ exports.createVisit = params =>
       .catch(reject);
   });
 
-exports.findUrl = ({ id, domain }) =>
+exports.findUrl = ({ id, domain, target }) =>
   new Promise((resolve, reject) => {
     const session = driver.session();
     session
       .readTransaction(tx =>
         tx.run(
-          'MATCH (l:URL { id: $id })' +
+          `MATCH (l:URL { ${id ? 'id: $id' : 'target: $target'} })` +
             `${
               domain
                 ? 'MATCH (l)-[:USES]->(d:DOMAIN { name: $domain })'
@@ -118,6 +119,7 @@ exports.findUrl = ({ id, domain }) =>
           {
             id,
             domain,
+            target,
           }
         )
       )

+ 4 - 0
server/utils/index.js

@@ -1,6 +1,10 @@
 const URL = require('url');
+const config = require('../config');
 
 exports.addProtocol = url => {
   const hasProtocol = /^https?/.test(URL.parse(url).protocol);
   return hasProtocol ? url : `http://${url}`;
 };
+
+exports.generateShortUrl = (id, domain) =>
+  `http${!domain ? 's' : ''}://${domain || config.DEFAULT_DOMAIN}/${id}`;