Skip to content

Commit 122ab8a

Browse files
authored
Merge pull request larymak#334 from MBSA-INFINITY/url_shortening_app_flask
URL Shortening Application in Flask #hacktoberfest
2 parents ea00308 + e9e69bf commit 122ab8a

File tree

9 files changed

+399
-0
lines changed

9 files changed

+399
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*.pyc
2+
/__pycache__
3+
.env
4+
client_secret.json
5+
do_not_commit
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<!--Please do not remove this part-->
2+
![Star Badge](https://img.shields.io/static/v1?label=%F0%9F%8C%9F&message=If%20Useful&style=style=flat&color=BC4E99)
3+
![Open Source Love](https://badges.frapsoft.com/os/v1/open-source.svg?v=103)
4+
5+
# URL Shortening Application in Flask
6+
7+
## 🛠️ Description
8+
This project is about developing a url shortening application in **Flask** and **MongoDB**. User will paste their long URLs in this application and will get a shortened url, which will redirect to the same long url once used in a browser.
9+
10+
## ⚙️ Languages or Frameworks Used
11+
- Flask, MongoDB
12+
- HTML, CSS, Bootstrap
13+
14+
15+
## 🌟 How to run
16+
- ### Install all the requirements
17+
Run `pip install -r requirements.txt` to install all the requirements.
18+
- ### MongoDB Setup for Project
19+
20+
- Download monogdb from the [official website](https://www.mongodb.com/try/download/community) and setup in your local system for testing.
21+
- Once it is setup locally, try creating documents and collections in mongodb to familiarize yourself with it.
22+
- You can also download the `MongoDB Compass`, which is the GUI version of Mongo Shell.
23+
- Once all the local testing is done, you can create a free cloud version of MongoDB in [MongoDB Atlas](https://www.mongodb.com/cloud/atlas/register) and get the following credentials from the dashboard of atlas:
24+
```bash
25+
export MONGO_URI=YOUR_MONGO_URI
26+
export MONGO_USERNAME=YOUR_MONGO_USERNAME
27+
export MONGO_PASSWORD=YOUR_MONGO_PASSWORD
28+
```
29+
30+
31+
- ### Setup Environment for the project
32+
- Now create a `.env` file in your project dreictory and include the following parameters as it is :-
33+
```bash
34+
export ENVIRONMENT=local | production (choose on the basis of local or production environment)
35+
export APP_SECRET=YOUR_APP_SECRET
36+
export APP_URL=YOUR_APP_URL (the short url)
37+
export MONGO_URI=YOUR_MONGO_URI
38+
export MONGO_USERNAME=YOUR_MONGO_USERNAME
39+
export MONGO_PASSWORD=YOUR_MONGO_PASSWORD
40+
export DB_NAME=YOUR_DATABASE_NAME
41+
```
42+
43+
- ### Now Just, Run the project
44+
- To the run the project, go to the `bash` terminal of VSCode or any other code editor and run `./start_server.sh`.
45+
- You don't have to care about setting `.env` then yourself then.
46+
47+
48+
## 📺 Demo
49+
- Main screen of the application.
50+
![image](https://github.com/MBSA-INFINITY/Python-project-Scripts/assets/85332648/94825306-1803-4e48-95d1-4f65bd94fcc1)
51+
- Paste you long URL in the input.
52+
![image](https://github.com/MBSA-INFINITY/Python-project-Scripts/assets/85332648/a5dd5bf5-b311-4d72-b84f-ebf197e30009)
53+
- Click on Shorten and copy the `short url` to clipboard
54+
![image](https://github.com/MBSA-INFINITY/Python-project-Scripts/assets/85332648/4eeb3d39-ddfe-48b0-9c2c-23ffe01036cd)
55+
56+
57+
58+
## 🤖 Author
59+
60+
Github - [MBSA-INFINITY](https://github.com/MBSA-INFINITY)
61+
LinkedIn - [MBSAIADITYA](https://www.linkedin.com/in/mbsaiaditya/)
62+
Portfolio - [MBSA](https://mbsaiaditya.in/)
63+
64+
65+
66+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from flask import Flask, request, render_template, redirect, flash
2+
from helpers import random_string_generator, random_string_generator_only_alpha, is_valid_url
3+
from db import url_data_collection
4+
import os
5+
from datetime import datetime
6+
7+
8+
app = Flask(__name__)
9+
app.secret_key = os.environ['APP_SECRET']
10+
11+
url_ = os.environ['APP_URL']
12+
13+
reserved_keywords = ['login', 'logout', 'shorten-url']
14+
15+
16+
@app.route("/", methods = ['GET'])
17+
def start():
18+
return render_template("index.html")
19+
20+
@app.route("/shorten-url", methods=['POST'])
21+
def shorten_url():
22+
data = request.form
23+
url_input = data.get("url_input")
24+
data_id = random_string_generator_only_alpha(10)
25+
if not is_valid_url(str(url_input)):
26+
flash({'type':'error', 'data':"Inavlid URL"})
27+
return redirect("/")
28+
random_slug = random_string_generator(7)
29+
shortened_url = f"{url_}/{random_slug}"
30+
shortened_url_ = shortened_url[len(url_)+1:]
31+
if shortened_url == url_input:
32+
flash({'type':'error', 'data':"Infinite Redirect Error!"})
33+
return redirect("/")
34+
first_keyword = shortened_url_.split("/")[0]
35+
if first_keyword in reserved_keywords:
36+
flash({'type':'error', 'data':f"{first_keyword} is a reserved keyword! Please use any other word!"})
37+
return redirect("/")
38+
incoming_data = {
39+
"no_of_/": shortened_url_.count("/"),
40+
"order": shortened_url_.split("/"),
41+
"redirect_url": url_input,
42+
"shortened_url": shortened_url,
43+
"data_id": data_id,
44+
"created_at": datetime.now()
45+
}
46+
if url_details := url_data_collection.find_one({"no_of_/": incoming_data['no_of_/'], "order":incoming_data['order']}):
47+
flash({'type':'error', 'data':"This Domain is Already Taken!"})
48+
return redirect("/")
49+
url_data_collection.insert_one(incoming_data)
50+
flash({'type':'data', 'data':shortened_url})
51+
return redirect("/")
52+
53+
54+
@app.before_request
55+
def before_request_func():
56+
host_url = request.host_url
57+
url = request.url
58+
url = url[len(host_url):]
59+
incoming_data = {
60+
"no_of_/":url.count("/"),
61+
"order":url.split("/")
62+
}
63+
if url_details := url_data_collection.find_one({"no_of_/": incoming_data['no_of_/'], "order":incoming_data['order']}):
64+
redirect_url = url_details.get("redirect_url")
65+
return redirect(redirect_url)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import os
2+
import pymongo
3+
4+
ENVIRONMENT = os.environ["ENVIRONMENT"]
5+
if ENVIRONMENT == "local":
6+
connection_string = "mongodb://localhost:27017"
7+
DB_NAME = "url_shortener"
8+
else:
9+
MONGO_CLUSTER = os.environ["MONGO_URI"]
10+
MONGO_USERNAME = os.environ["MONGO_USERNAME"]
11+
MONGO_PASSWORD = os.environ["MONGO_PASSWORD"]
12+
DB_NAME = os.environ["DB_NAME"]
13+
connection_string = f"mongodb+srv://{MONGO_USERNAME}:{MONGO_PASSWORD}@{MONGO_CLUSTER}/?retryWrites=true&ssl=true&ssl_cert_reqs=CERT_NONE&w=majority"
14+
15+
16+
db_client = pymongo.MongoClient(connection_string)
17+
db_client = db_client.get_database(DB_NAME)
18+
19+
url_data_collection = db_client['url_data']
20+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import string
2+
import random
3+
import re
4+
5+
def random_string_generator(N):
6+
res = ''.join(random.choices(string.ascii_lowercase +
7+
string.digits, k=N))
8+
return str(res)
9+
10+
def random_string_generator_only_alpha(N):
11+
res = ''.join(random.choices(string.ascii_lowercase +
12+
string.ascii_uppercase, k=N))
13+
return str(res)
14+
15+
16+
def is_valid_url(url):
17+
# Define a regular expression pattern to match URLs
18+
url_pattern = re.compile(
19+
r'^(https?|ftp)://' # Match the scheme (http, https, or ftp)
20+
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|' # Match domain
21+
r'localhost|' # Match "localhost"
22+
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # Match IP address
23+
r'(?::\d+)?' # Match optional port number
24+
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
25+
26+
return bool(url_pattern.match(url))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Flask==2.0.1
2+
pymongo[srv]==4.3.3
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from app import app
2+
if __name__ == '__main__':
3+
app.run(debug=True)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
source .env
2+
python run.py

0 commit comments

Comments
 (0)