#3 added util func to validate url on post request

parent 7b39228d
DB_CONN_STRING="mongodb://localhost:27017"
DB_NAME="linksDB"
LINKS_COLLECTION_NAME="links"
PORT='3000'
\ No newline at end of file
PORT='3000'
BASE_URL='http://localhost:3000'
\ No newline at end of file
export const validateUrl = (url: string): boolean => {
const urlPattern: RegExp = new RegExp(
'^(https?:\\/\\/)?' +
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' +
'((\\d{1,3}\\.){3}\\d{1,3}))' +
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' +
'(\\?[;&a-z\\d%_.~+=-]*)?' +
'(\\#[-a-z\\d_]*)?$',
'i'
);
return !!urlPattern.test(url);
};
......@@ -13,11 +13,13 @@
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"mongodb": "^5.1.0"
"mongodb": "^5.1.0",
"shortid": "^2.2.16"
},
"devDependencies": {
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17",
"@types/shortid": "^0.0.29",
"ts-node-dev": "^2.0.0"
}
},
......@@ -166,6 +168,12 @@
"@types/node": "*"
}
},
"node_modules/@types/shortid": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/shortid/-/shortid-0.0.29.tgz",
"integrity": "sha512-9BCYD9btg2CY4kPcpMQ+vCR8U6V8f/KvixYD5ZbxoWlkhddNF5IeZMVL3p+QFUkg+Hb+kPAG9Jgk4bnnF1v/Fw==",
"dev": true
},
"node_modules/@types/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
......@@ -931,6 +939,11 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/nanoid": {
"version": "2.1.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz",
"integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA=="
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
......@@ -1204,6 +1217,14 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/shortid": {
"version": "2.2.16",
"resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.16.tgz",
"integrity": "sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==",
"dependencies": {
"nanoid": "^2.1.0"
}
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
......@@ -1675,6 +1696,12 @@
"@types/node": "*"
}
},
"@types/shortid": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/shortid/-/shortid-0.0.29.tgz",
"integrity": "sha512-9BCYD9btg2CY4kPcpMQ+vCR8U6V8f/KvixYD5ZbxoWlkhddNF5IeZMVL3p+QFUkg+Hb+kPAG9Jgk4bnnF1v/Fw==",
"dev": true
},
"@types/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
......@@ -2246,6 +2273,11 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"nanoid": {
"version": "2.1.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz",
"integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA=="
},
"negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
......@@ -2441,6 +2473,14 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"shortid": {
"version": "2.2.16",
"resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.16.tgz",
"integrity": "sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==",
"requires": {
"nanoid": "^2.1.0"
}
},
"side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
......
......@@ -14,11 +14,13 @@
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"mongodb": "^5.1.0"
"mongodb": "^5.1.0",
"shortid": "^2.2.16"
},
"devDependencies": {
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17",
"@types/shortid": "^0.0.29",
"ts-node-dev": "^2.0.0"
}
}
import {ObjectId} from 'mongodb';
export default class Link {
constructor(public originalUrl: string, public id?: ObjectId) {}
constructor(
public originalUrl: string,
public shortUrl: string,
public id?: ObjectId
) {}
}
......@@ -2,12 +2,15 @@ import express, {Router, Request, Response} from 'express';
import {ObjectId} from 'mongodb';
import {collections} from '../services/database.service';
import Link from '../models/links';
import shortid from 'shortid';
import {validateUrl} from '../../Util/util';
export const linksRouter: Router = express.Router();
linksRouter.get('/', (req: Request, res: Response) => {
linksRouter.get('/', async (req: Request, res: Response) => {
try {
res.send('Hello');
const links = await collections.links?.find({}).toArray();
res.status(200).send(links);
} catch (error: unknown) {
const err = error as Error;
res.status(500).send(err.message);
......@@ -17,14 +20,37 @@ linksRouter.get('/', (req: Request, res: Response) => {
linksRouter.post('/', async (req: Request, res: Response) => {
try {
const newLink = req.body as Link;
const result = await collections.links?.insertOne(newLink);
if (result) {
res
.status(201)
.send(`Successfully created a new link with id ${result.insertedId}`);
} else {
res.status(500).send('Failed to create a new link.');
if (!validateUrl(newLink.originalUrl)) {
res.status(400).send('Invalid url link');
return;
}
const originalLinkExists = await collections.links?.findOne({
originalUrl: newLink.originalUrl,
});
if (originalLinkExists) {
res.status(200).send(originalLinkExists);
return;
}
let shortUrl;
while (true) {
const urlId = shortid.generate();
shortUrl = `${process.env.BASE_URL}/${urlId}`;
const shortUlrExists = await collections.links?.findOne({
shortUrl: shortUrl,
});
if (!shortUlrExists) {
break;
}
}
newLink.shortUrl = shortUrl;
await collections.links?.insertOne(newLink);
res.status(200).send(newLink);
} catch (err: unknown) {
const error = err as Error;
res.status(500).send(error.message);
......
......@@ -5,6 +5,7 @@ declare global {
DB_CONN_STRING: string;
DB_NAME: string;
LINKS_COLLECTION_NAME: string;
BASE_URL: string;
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment