Parcourir la source

Merge pull request #1 from thedevs-network/develop

update
Akash Joshi il y a 7 ans
Parent
commit
5fe76fa11d

+ 9 - 2
client/actions/__test__/auth.js

@@ -22,6 +22,13 @@ const middlewares = [thunk];
 const mockStore = configureMockStore(middlewares);
 
 describe('auth actions', () => {
+  const jwt = {
+    domain: '',
+    exp: 1529137738725,
+    iat: 1529137738725,
+    iss: 'ApiAuth',
+    sub: 'test@mail.com',
+  };
   const email = 'test@mail.com';
   const password = 'password';
   const token =
@@ -75,7 +82,7 @@ describe('auth actions', () => {
         { type: AUTH_RENEW },
         {
           type: AUTH_USER,
-          payload: email
+          payload: jwt
         },
         {
           type: SET_DOMAIN,
@@ -140,7 +147,7 @@ describe('auth actions', () => {
         { type: AUTH_RENEW },
         {
           type: AUTH_USER,
-          payload: email
+          payload: jwt
         },
         {
           type: SET_DOMAIN,

+ 11 - 2
client/reducers/__test__/auth.js

@@ -12,10 +12,11 @@ import reducer from '../auth';
 
 describe('auth reducer', () => {
   const initialState = {
+    admin: false,
     isAuthenticated: false,
     sentVerification: false,
     user: '',
-    renew: false
+    renew: false,
   };
 
   beforeEach(() => {
@@ -27,11 +28,19 @@ describe('auth reducer', () => {
   });
 
   it('should handle AUTH_USER', () => {
+    const jwt = {
+      domain: '',
+      exp: 1529137738725,
+      iat: 1529137738725,
+      iss: 'ApiAuth',
+      sub: 'test@user.com',
+    };
+
     const user = 'test@user.com';
 
     const state = reducer(initialState, {
       type: AUTH_USER,
-      payload: user
+      payload: jwt
     });
 
     expect(state).not.to.be.undefined;

Fichier diff supprimé car celui-ci est trop grand
+ 458 - 178
package-lock.json


+ 7 - 1
package.json

@@ -4,7 +4,7 @@
   "description": "Modern URL shortener.",
   "main": "./server/server.js",
   "scripts": {
-    "test": "mocha --require babel-core/register ./client/**/__test__/*.js",
+    "test": "mocha --compilers js:@babel/register ./client/**/__test__/*.js",
     "dev": "nodemon -w ./server/**/*.js ./server/server.js",
     "build": "next build ./client",
     "start": "NODE_ENV=production node ./server/server.js",
@@ -41,6 +41,7 @@
     "express-validator": "^4.3.0",
     "geoip-lite": "^1.3.5",
     "helmet": "^3.9.0",
+    "isbot": "^2.2.1",
     "js-cookie": "^2.2.0",
     "jsonwebtoken": "^8.1.0",
     "jwt-decode": "^2.2.0",
@@ -76,6 +77,11 @@
     "useragent": "^2.2.1"
   },
   "devDependencies": {
+    "@babel/cli": "^7.2.3",
+    "@babel/core": "^7.2.2",
+    "@babel/node": "^7.2.2",
+    "@babel/preset-env": "^7.2.3",
+    "@babel/register": "^7.0.0",
     "babel": "^6.23.0",
     "babel-cli": "^6.26.0",
     "babel-core": "^6.26.3",

+ 4 - 0
server/config.example.js

@@ -9,6 +9,10 @@ module.exports = {
   DB_USERNAME: '',
   DB_PASSWORD: '',
 
+  /* Redis host and port */
+  REDIS_HOST: '127.0.0.1',
+  REDIS_PORT: 6379,
+
   /* The daily limit for each user */
   USER_LIMIT_PER_DAY: 50,
 

+ 9 - 5
server/controllers/urlController.js

@@ -8,6 +8,7 @@ const geoip = require('geoip-lite');
 const bcrypt = require('bcryptjs');
 const subDay = require('date-fns/sub_days');
 const ua = require('universal-analytics');
+const isbot = require('isbot');
 const {
   createShortUrl,
   createVisit,
@@ -107,7 +108,6 @@ exports.urlShortener = async ({ body, user }, res) => {
 
 const browsersList = ['IE', 'Firefox', 'Chrome', 'Opera', 'Safari', 'Edge'];
 const osList = ['Windows', 'Mac Os X', 'Linux', 'Chrome OS', 'Android', 'iOS'];
-const botList = ['bot', 'dataminr', 'pinterest', 'yahoo', 'facebook', 'crawl'];
 const filterInBrowser = agent => item =>
   agent.family.toLowerCase().includes(item.toLocaleLowerCase());
 const filterInOs = agent => item =>
@@ -124,8 +124,7 @@ exports.goToUrl = async (req, res, next) => {
   const referrer = req.header('Referer') && URL.parse(req.header('Referer')).hostname;
   const location = geoip.lookup(req.realIp);
   const country = location && location.country;
-  const isBot =
-    botList.some(bot => agent.source.toLowerCase().includes(bot)) || agent.family === 'Other';
+  const isBot = isbot(req.headers['user-agent']);
 
   let url;
 
@@ -187,13 +186,14 @@ exports.goToUrl = async (req, res, next) => {
     });
   }
 
-  if (config.GOOGLE_ANALYTICS) {
+  if (config.GOOGLE_ANALYTICS && !isBot) {
     const visitor = ua(config.GOOGLE_ANALYTICS);
     visitor
       .pageview({
         dp: `/${id}`,
         ua: req.headers['user-agent'],
         uip: req.realIp,
+        aip: 1,
       })
       .send();
   }
@@ -204,7 +204,11 @@ exports.goToUrl = async (req, res, next) => {
 exports.getUrls = async ({ query, user }, res) => {
   const { countAll } = await getCountUrls({ user });
   const urlsList = await getUrls({ options: query, user });
-  return res.json({ ...urlsList, countAll });
+  const isCountMissing = urlsList.list.some(url => typeof url.count === 'undefined');
+  const { list } = isCountMissing
+    ? await getUrls({ options: query, user, setCount: true })
+    : urlsList;
+  return res.json({ list, countAll });
 };
 
 exports.setCustomDomain = async ({ body: { customDomain }, user }, res) => {

+ 2 - 2
server/controllers/validateBodyController.js

@@ -56,8 +56,8 @@ exports.validateUrl = async ({ body, user }, res, next) => {
   if (!body.target) return res.status(400).json({ error: 'No target has been provided.' });
 
   // validate URL length
-  if (body.target.length > 1024) {
-    return res.status(400).json({ error: 'Maximum URL length is 1024.' });
+  if (body.target.length > 3000) {
+    return res.status(400).json({ error: 'Maximum URL length is 3000.' });
   }
 
   // Validate URL

+ 17 - 12
server/db/url.js

@@ -64,8 +64,9 @@ exports.createVisit = params =>
     session
       .writeTransaction(tx =>
         tx.run(
-          'MATCH (l:URL { id: $id })' +
-            `${params.domain ? 'MATCH (l)-[:USES]->({ name: $domain })' : ''}` +
+          'MATCH (l:URL { id: $id }) ' +
+            `${params.domain ? 'MATCH (l)-[:USES]->({ name: $domain })' : ''} ` +
+            'SET l.count = l.count + 1 ' +
             'CREATE (v:VISIT)' +
             'MERGE (b:BROWSER { browser: $browser })' +
             'MERGE (c:COUNTRY { country: $country })' +
@@ -150,21 +151,22 @@ exports.getCountUrls = ({ user }) =>
       .catch(err => session.close() || reject(err));
   });
 
-exports.getUrls = ({ user, options }) =>
+exports.getUrls = ({ user, options, setCount }) =>
   new Promise((resolve, reject) => {
     const session = driver.session();
     const { count = 5, page = 1, search = '' } = options;
     const limit = parseInt(count, 10);
     const skip = parseInt(page, 10);
     const searchQuery = search ? 'WHERE l.id =~ $search OR l.target =~ $search' : '';
+    const setVisitsCount = setCount ? 'SET l.count = size((l)<-[:VISITED]-())' : '';
     session
       .readTransaction(tx =>
         tx.run(
           `MATCH (u:USER { email: $email })-[:CREATED]->(l) ${searchQuery} ` +
             'WITH l ORDER BY l.createdAt DESC ' +
             'WITH l SKIP $skip LIMIT $limit ' +
-            'OPTIONAL MATCH (l)-[:USES]->(d) ' +
-            'RETURN l, d.name AS domain, size((l)<-[:VISITED]-()) as count',
+            `OPTIONAL MATCH (l)-[:USES]->(d) ${setVisitsCount} ` +
+            'RETURN l, d.name AS domain',
           {
             email: user.email,
             limit,
@@ -175,13 +177,16 @@ exports.getUrls = ({ user, options }) =>
       )
       .then(({ records }) => {
         session.close();
-        const urls = records.map(record => ({
-          ...record.get('l').properties,
-          password: !!record.get('l').properties.password,
-          count: record.get('count').toNumber(),
-          shortUrl: `http${!record.get('domain') ? 's' : ''}://${record.get('domain') ||
-            config.DEFAULT_DOMAIN}/${record.get('l').properties.id}`,
-        }));
+        const urls = records.map(record => {
+          const visitCount = record.get('l').properties.count;
+          return {
+            ...record.get('l').properties,
+            count: typeof visitCount === 'object' ? visitCount.toNumber() : visitCount,
+            password: !!record.get('l').properties.password,
+            shortUrl: `http${!record.get('domain') ? 's' : ''}://${record.get('domain') ||
+              config.DEFAULT_DOMAIN}/${record.get('l').properties.id}`,
+          };
+        });
         resolve({ list: urls });
       })
       .catch(err => session.close() || reject(err));

+ 2 - 1
server/redis.js

@@ -1,7 +1,8 @@
 const { promisify } = require('util');
 const redis = require('redis');
+const config = require('./config');
 
-const client = redis.createClient();
+const client = redis.createClient(config.REDIS_PORT || 6379, config.REDIS_HOST || '127.0.0.1');
 
 exports.get = promisify(client.get).bind(client);
 exports.set = promisify(client.set).bind(client);

+ 2 - 2
server/server.js

@@ -104,8 +104,8 @@ app.prepare().then(() => {
     auth.authJwtLoose,
     catchErrors(auth.recaptcha),
     catchErrors(validateUrl),
-    catchErrors(cooldownCheck),
-    catchErrors(malwareCheck),
+    /* Allows running without Google Safe Browsing enabled */
+    config.GOOGLE_SAFE_BROWSING_KEY ? [catchErrors(cooldownCheck), catchErrors(malwareCheck)] : [],
     catchErrors(url.urlShortener)
   );
   server.post('/api/url/deleteurl', auth.authApikey, auth.authJwt, catchErrors(url.deleteUrl));

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff