diff --git a/app.ts b/app.ts deleted file mode 100644 index a8d5e72..0000000 --- a/app.ts +++ /dev/null @@ -1,15 +0,0 @@ -import express from "express"; -import urlShortenerRoutes from "./routes/urlShortenerRoutes" -import connectToDatabase from "./utils/database"; - -const app = express(); - -connectToDatabase(); - -app.use(express.json()); - -app.use("/api/v1", urlShortenerRoutes) - -const PORT = process.env.APP_PORT - -app.listen(PORT, () => console.log(`App listening on port ${PORT}`)); diff --git a/controllers/urlController.ts b/controllers/urlController.ts deleted file mode 100644 index f15e1ee..0000000 --- a/controllers/urlController.ts +++ /dev/null @@ -1,70 +0,0 @@ -import UrlModel from "../models/urlModel"; -import shortid from "shortid" -import utils from "../utils/util" - -export const getAllSavedUrls = async (req: any, res: any, next: any) => { - try { - const url = await UrlModel.find(); - - if (!url) { - return res.status(404).json({message: "No URLs found"}); - } - return res.status(200).json({url}); - } catch (error) { - if (error instanceof Error) { - error.message ? console.error(error.message) : console.error(error); - } - } -} - -export const shortenURL = async (req: any, res: any, next: any) => { - const { url } = req.body; - const base = process.env.API_URL; - - const urlId = shortid.generate(); - if (utils.validateUrl(url)) { - try { - let urlVal = await UrlModel.findOne({ url }); - if (urlVal) { - return res.json(urlVal); - } - const shortUrl = `${base}/${urlId}`; - - urlVal = new UrlModel({ - urlId, - url, - shortUrl, - // date: new Date() - }); - - await urlVal.save(); - return res.json(urlVal); - } catch (error) { - if (error instanceof Error) { - error.message ? console.error(error.message) : console.error(error); - } - res.status(500).json({message: "Server Error"}) - } - } else { - res.status(400).json({message: "Invalid Url"}); - } -}; - -export const redirectUrl = async (req: any, res: any, next: any) => { - try { - const urlVal = await UrlModel.findOne({ urlId: req.params.urlId}) - - if (urlVal) { - urlVal.clicks+=1; - urlVal.save(); - return res.redirect(urlVal.url); - } - return res.status(404).json({message: "URL not found"}) - - } catch (error) { - if (error instanceof Error) { - error.message ? console.error(error.message) : console.error(error); - } - return res.status(500).json({message: "Server Error"}) - } -} \ No newline at end of file diff --git a/models/urlModel.ts b/models/urlModel.ts deleted file mode 100644 index cc3847c..0000000 --- a/models/urlModel.ts +++ /dev/null @@ -1,21 +0,0 @@ -import mongoose from "mongoose"; - -const Schema = mongoose.Schema; - -interface IUrl { - urlId: string; - url: string; - shortUrl: string; - clicks: number; - date: string; -} - -const urlSchema = new Schema({ - urlId: { type: String, required: true }, - url: { type: String, required: true }, - shortUrl: { type: String, required: true }, - clicks: { type: Number, required: true , default: 0 }, - // date: { type: String, default: Date.now } -}); - -export default mongoose.model("url", urlSchema); \ No newline at end of file diff --git a/nodemon.json b/nodemon.json index 14d6560..e24be3e 100644 --- a/nodemon.json +++ b/nodemon.json @@ -2,5 +2,5 @@ "watch": ["*"], "ext": ".ts,.js", "ignore": [], - "exec": "npx ts-node app.ts" -} \ No newline at end of file + "exec": "npx tsnd --respawn src/App.ts" +} diff --git a/package-lock.json b/package-lock.json index 185d516..b68b9b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,16 +12,16 @@ "@types/cors": "^2.8.13", "cors": "^2.8.5", "dotenv": "^16.3.1", - "mongoose": "^7.3.2" + "mongoose": "^7.3.2", + "nanoid": "^3.3.6" }, "devDependencies": { "@types/express": "^4.17.17", + "@types/nanoid": "^3.0.0", "@types/node": "^20.4.0", - "@types/shortid": "^0.0.29", "express": "^4.18.2", "nodemon": "^2.0.22", - "shortid": "^2.2.16", - "ts-node": "^10.9.1", + "ts-node-dev": "^2.0.0", "typescript": "^5.1.6" } }, @@ -149,6 +149,16 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, + "node_modules/@types/nanoid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/nanoid/-/nanoid-3.0.0.tgz", + "integrity": "sha512-UXitWSmXCwhDmAKe7D3hNQtQaHeHt5L8LO1CB8GF8jlYVzOv5cBWDNqiJ+oPEWrWei3i3dkZtHY/bUtd0R/uOQ==", + "deprecated": "This is a stub types definition. nanoid provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "nanoid": "*" + } + }, "node_modules/@types/node": { "version": "20.4.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.0.tgz", @@ -193,6 +203,18 @@ "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", + "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", + "dev": true + }, + "node_modules/@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, "node_modules/@types/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -341,6 +363,12 @@ "node": ">=14.20.1" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -498,6 +526,15 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "node_modules/dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", + "dev": true, + "dependencies": { + "xtend": "^4.0.0" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -618,6 +655,12 @@ "node": ">= 0.6" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -653,6 +696,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -744,6 +807,16 @@ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -776,6 +849,18 @@ "node": ">=8" } }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -895,6 +980,27 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mongodb": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.6.0.tgz", @@ -1009,10 +1115,21 @@ "dev": true }, "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==", - "dev": true + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } }, "node_modules/negotiator": { "version": "0.6.3", @@ -1119,6 +1236,15 @@ "node": ">= 0.8" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1128,6 +1254,21 @@ "node": ">= 0.8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -1224,6 +1365,35 @@ "node": ">=8.10.0" } }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1331,6 +1501,12 @@ "nanoid": "^2.1.0" } }, + "node_modules/shortid/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==", + "dev": true + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -1393,6 +1569,25 @@ "npm": ">= 3.0.0" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -1411,6 +1606,24 @@ "node": ">= 0.8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -1423,6 +1636,18 @@ "node": ">=4" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1467,6 +1692,15 @@ "node": ">=12" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -1510,6 +1744,52 @@ } } }, + "node_modules/ts-node-dev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", + "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.1", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.6", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^10.4.0", + "tsconfig": "^7.0.0" + }, + "bin": { + "ts-node-dev": "lib/bin.js", + "tsnd": "lib/bin.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "peerDependencies": { + "node-notifier": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "dependencies": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1594,6 +1874,21 @@ "node": ">=12" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -1723,6 +2018,15 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, + "@types/nanoid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/nanoid/-/nanoid-3.0.0.tgz", + "integrity": "sha512-UXitWSmXCwhDmAKe7D3hNQtQaHeHt5L8LO1CB8GF8jlYVzOv5cBWDNqiJ+oPEWrWei3i3dkZtHY/bUtd0R/uOQ==", + "dev": true, + "requires": { + "nanoid": "*" + } + }, "@types/node": { "version": "20.4.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.0.tgz", @@ -1767,6 +2071,18 @@ "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", + "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", + "dev": true + }, + "@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, "@types/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -1887,6 +2203,12 @@ "resolved": "https://registry.npmjs.org/bson/-/bson-5.4.0.tgz", "integrity": "sha512-WRZ5SQI5GfUuKnPTNmAYPiKIof3ORXAF4IRU5UcgmivNIon01rWQlw5RUH954dpu8yGL8T59YShVddIPaU/gFA==" }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1999,6 +2321,15 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==" }, + "dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", + "dev": true, + "requires": { + "xtend": "^4.0.0" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2098,6 +2429,12 @@ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, "fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -2123,6 +2460,20 @@ "has-symbols": "^1.0.3" } }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -2187,6 +2538,16 @@ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -2213,6 +2574,15 @@ "binary-extensions": "^2.0.0" } }, + "is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2299,6 +2669,18 @@ "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, "mongodb": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.6.0.tgz", @@ -2375,10 +2757,9 @@ "dev": true }, "nanoid": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", - "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", - "dev": true + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" }, "negotiator": { "version": "0.6.3", @@ -2456,12 +2837,33 @@ "ee-first": "1.1.1" } }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -2531,6 +2933,26 @@ "picomatch": "^2.2.1" } }, + "resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "requires": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2612,6 +3034,14 @@ "dev": true, "requires": { "nanoid": "^2.1.0" + }, + "dependencies": { + "nanoid": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", + "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", + "dev": true + } } }, "side-channel": { @@ -2661,6 +3091,22 @@ "smart-buffer": "^4.2.0" } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -2676,6 +3122,18 @@ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -2685,6 +3143,12 @@ "has-flag": "^3.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2717,6 +3181,12 @@ "punycode": "^2.1.1" } }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, "ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -2738,6 +3208,36 @@ "yn": "3.1.1" } }, + "ts-node-dev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", + "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", + "dev": true, + "requires": { + "chokidar": "^3.5.1", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.6", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^10.4.0", + "tsconfig": "^7.0.0" + } + }, + "tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "requires": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + } + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -2797,6 +3297,18 @@ "webidl-conversions": "^7.0.0" } }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index bc8eb68..b4ee5d4 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,10 @@ "name": "url-shortener", "version": "1.0.0", "description": "a url shortner api", - "main": "app.js", + "main": "src/App.ts", "scripts": { "test": "test", - "start": "nodemon app.ts" + "start": "nodemon src/App.ts" }, "keywords": [ "url", @@ -16,18 +16,18 @@ "license": "ISC", "devDependencies": { "@types/express": "^4.17.17", + "@types/nanoid": "^3.0.0", "@types/node": "^20.4.0", - "@types/shortid": "^0.0.29", "express": "^4.18.2", "nodemon": "^2.0.22", - "shortid": "^2.2.16", - "ts-node": "^10.9.1", + "ts-node-dev": "^2.0.0", "typescript": "^5.1.6" }, "dependencies": { "@types/cors": "^2.8.13", "cors": "^2.8.5", "dotenv": "^16.3.1", - "mongoose": "^7.3.2" + "mongoose": "^7.3.2", + "nanoid": "^3.3.6" } } diff --git a/routes/urlShortenerRoutes.ts b/routes/urlShortenerRoutes.ts deleted file mode 100644 index ae53a1f..0000000 --- a/routes/urlShortenerRoutes.ts +++ /dev/null @@ -1,10 +0,0 @@ -import express from "express"; -import { getAllSavedUrls, shortenURL, redirectUrl } from "../controllers/urlController"; - -const urlShortenerRoutes = express.Router(); - -urlShortenerRoutes.get('/all', getAllSavedUrls); -urlShortenerRoutes.post('/shorten-url', shortenURL); -urlShortenerRoutes.get('/:urlId', redirectUrl); - -export default urlShortenerRoutes; \ No newline at end of file diff --git a/src/App.ts b/src/App.ts new file mode 100644 index 0000000..d4f78bf --- /dev/null +++ b/src/App.ts @@ -0,0 +1,18 @@ +import express from "express"; +import routes from "./Routes"; +import dotenv from "dotenv"; +import connect_To_Db from "./Provider/Db.Provider"; + +dotenv.config(); + +const app = express(); + +app.use(express.json()); + +const PORT = process.env.APP_PORT; + +app.listen(PORT, async () => { + console.log(await connect_To_Db()); + console.log(`Running on Localhost:${PORT}`); + routes(app); +}); diff --git a/src/Controllers/Su.Controller.ts b/src/Controllers/Su.Controller.ts new file mode 100644 index 0000000..af95c91 --- /dev/null +++ b/src/Controllers/Su.Controller.ts @@ -0,0 +1,42 @@ +import { nanoid } from "nanoid"; +import { Request, Response } from "express"; +import Shorten_Url_Service from "../Service/Su.Service"; +import { Shorten_Url_Handler_Types } from "../Types"; + +const Shorten_Url_Handler: Shorten_Url_Handler_Types = { + Get_All_Urls: async (req: Request, res: Response) => { + try { + return res.send(`${await Shorten_Url_Service.Get_All()}`); + } catch (e: any) { + return res.status(409).send(e.message); + } + }, + Create_Shorten_Url: async (req: Request, res: Response) => { + try { + const target_Url: string = req.body.url; + /*verify url const:boolean checked_Url + if(!checked_Url)return res.status(403).send("Invalid Url")*/ + const redirect_Id: string = nanoid(7); + + return res.send( + await Shorten_Url_Service.Create_Url(target_Url as string, redirect_Id) + ); + } catch (e: any) { + return res.status(409).send(e.message); + } + }, + Redirect_Url: async (req: Request, res: Response) => { + try { + const target_Id: string = req.params.url_Id; //get the id from the params of the url + + const redirect_Url: string = await Shorten_Url_Service.Get_Url(target_Id); + if (!redirect_Url) return res.status(404).send("Invalid Url"); //if undefined return error + + return res.redirect(redirect_Url); //return and redirect to url + } catch (e: any) { + return res.status(409).send(e.message); + } + }, +}; + +export default Shorten_Url_Handler; diff --git a/src/Provider/Db.Provider.ts b/src/Provider/Db.Provider.ts new file mode 100644 index 0000000..8f2140d --- /dev/null +++ b/src/Provider/Db.Provider.ts @@ -0,0 +1,17 @@ +import dotenv from "dotenv"; +import mongoose from "mongoose"; +import { Connect_To_Db_Type } from "../Types"; + +dotenv.config(); + +const connect_To_Db: Connect_To_Db_Type = async () => { + try { + await mongoose.connect(process.env.APP_MONGODB_URL as string); + return "Connected to Db"; + } catch (error) { + console.error(error); + process.exit(1); + } +}; + +export default connect_To_Db; diff --git a/src/Provider/Utility.ts b/src/Provider/Utility.ts new file mode 100644 index 0000000..3adf34f --- /dev/null +++ b/src/Provider/Utility.ts @@ -0,0 +1,19 @@ +import { Utils_Types } from "../Types"; + +const utils: Utils_Types = { + validate_Url: (value: string) => { + var urlPattern = new RegExp( + "^(https?:\\/\\/)?" + // validate protocol + "((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // validate domain name + "((\\d{1,3}\\.){3}\\d{1,3}))" + // validate OR ip (v4) address + "(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // validate port and path + "(\\?[;&a-z\\d%_.~+=-]*)?" + // validate query string + "(\\#[-a-z\\d_]*)?$", + "i" + ); + + return !!urlPattern.test(value); + }, +}; //create object with validate function as a property + +export default utils; diff --git a/src/Routes.ts b/src/Routes.ts new file mode 100644 index 0000000..0cda550 --- /dev/null +++ b/src/Routes.ts @@ -0,0 +1,14 @@ +import { Express } from "express"; +import Shorten_Url_Handler from "./Controllers/Su.Controller"; +import { Routes_Type } from "./Types"; + +const routes: Routes_Type = (app: Express) => { + /*Api-Get*/ + app.get("/api/get-all", Shorten_Url_Handler.Get_All_Urls); //get all shortened urls (Probably should secure) + app.get("/:url_Id", Shorten_Url_Handler.Redirect_Url); //redirect with a url + + /*Api-Posts*/ + app.post("/api/shorten-url", Shorten_Url_Handler.Create_Shorten_Url); //create a shorten url +}; + +export default routes; diff --git a/src/Service/Su.Service.ts b/src/Service/Su.Service.ts new file mode 100644 index 0000000..df36022 --- /dev/null +++ b/src/Service/Su.Service.ts @@ -0,0 +1,50 @@ +import { Shorten_Url_Service_Types } from "../Types"; +import Shorten_Url_Model, { Url_Doc_Type } from "../models/Su.Model"; + +const Shorten_Url_Service: Shorten_Url_Service_Types = { + Create_Url: async (Orginal_Url: string, Shortend_Id: string) => { + try { + const recycle_Url: [{}, Url_Doc_Type][] | any = + await Shorten_Url_Model.find({ origin_Url: Orginal_Url }); + if (recycle_Url) return `localhost:3000/${recycle_Url.shortned_Url_Id}`; //save db space by using already used urls + await Shorten_Url_Model.create({ + origin_Url: Orginal_Url, + shortned_Url_Id: Shortend_Id, + clicks: 0, + }); + return `localhost:3000/${Shortend_Id}`; + } catch (e: any) { + return e.message; + } + }, + Get_All: async () => { + try { + return await Shorten_Url_Model.find().lean(); + } catch (e: any) { + return e.message; + } + }, + Get_Url: async (target_Id: string) => { + try { + const redirect_Url: Url_Doc_Type | null = await Shorten_Url_Model.findOne( + { + shortned_Url_Id: target_Id, + } + ).lean(); + if (!redirect_Url) return undefined; //makes sure a url does infact exist + + const add_Click = redirect_Url.clicks++; + + await Shorten_Url_Model.updateOne( + { shortned_Url_Id: target_Id }, + { clicks: add_Click } + ); //Increases Clicks + + return redirect_Url.origin_Url; + } catch (e: any) { + return e.message; + } + }, +}; + +export default Shorten_Url_Service; diff --git a/src/Types.ts b/src/Types.ts new file mode 100644 index 0000000..bc41631 --- /dev/null +++ b/src/Types.ts @@ -0,0 +1,30 @@ +import { Request, Response, Express } from "express"; + +export type Connect_To_Db_Type = () => Promise; + +export type Routes_Type = (app: Express) => void; + +export interface Utils_Types { + validate_Url: (value: string) => boolean; +} + +export interface Shorten_Url_Handler_Types { + Get_All_Urls: ( + req: Request, + res: Response + ) => Promise>>; + Create_Shorten_Url: ( + req: Request, + res: Response + ) => Promise>>; + Redirect_Url: ( + req: Request, + res: Response + ) => Promise>>; +} + +export interface Shorten_Url_Service_Types { + Create_Url: (Orginal_Url: string, Shortend_Id: string) => Promise; + Get_All: () => Promise; + Get_Url: (target_Id: string) => Promise; +} diff --git a/src/models/Su.Model.ts b/src/models/Su.Model.ts new file mode 100644 index 0000000..6446622 --- /dev/null +++ b/src/models/Su.Model.ts @@ -0,0 +1,23 @@ +import mongoose from "mongoose"; + +export interface Url_Doc_Type { + origin_Url: string; // redirect to this url + shortned_Url_Id: string; //use the params as a search key + clicks: number; // analytics for clicks + //Dates is included in timestamp: true below (createdAt and updatedAt) +} + +const url_Schema = new mongoose.Schema( + { + origin_Url: { type: String, required: true }, + shortned_Url_Id: { type: String, required: true }, + clicks: { type: Number, required: true, default: 0 }, + }, + { + timestamps: true, + } +); + +const Shorten_Url_Model = mongoose.model("url", url_Schema); + +export default Shorten_Url_Model; diff --git a/test.ts b/test.ts deleted file mode 100644 index b766412..0000000 --- a/test.ts +++ /dev/null @@ -1 +0,0 @@ -//empty \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index badbb15..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "target": "es2016", - "lib": ["es6"], - "module": "commonjs", - // "rootDir": "src", - "resolveJsonModule": true, - "allowJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitAny": true, - } -} diff --git a/utils/database.ts b/utils/database.ts deleted file mode 100644 index 2de07b1..0000000 --- a/utils/database.ts +++ /dev/null @@ -1,21 +0,0 @@ -import dotenv from "dotenv"; -import mongoose from "mongoose"; - -dotenv.config(); - -const connectToDatabase: any = async() => { - const PORT = process.env.APP_PORT - - try { - await mongoose - .connect( - process.env.APP_MONGODB_URL as string - ) - - } catch (error) { - console.error(error); - process.exit(1); - } -} - -export default connectToDatabase diff --git a/utils/util.js b/utils/util.js deleted file mode 100644 index 163dda2..0000000 --- a/utils/util.js +++ /dev/null @@ -1,12 +0,0 @@ -const validateUrl = (value) => { - var urlPattern = new RegExp('^(https?:\\/\\/)?'+ // validate protocol - '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // validate domain name - '((\\d{1,3}\\.){3}\\d{1,3}))'+ // validate OR ip (v4) address - '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // validate port and path - '(\\?[;&a-z\\d%_.~+=-]*)?'+ // validate query string - '(\\#[-a-z\\d_]*)?$','i'); - - return !!urlPattern.test(value); - } - - module.exports = { validateUrl }; \ No newline at end of file