-
Notifications
You must be signed in to change notification settings - Fork 326
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #376 from Fer-Bar/urlshortener
Added a url shortener app for everyone can use it.
- Loading branch information
Showing
19 changed files
with
510 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
FLASK_APP=main.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# 📎UrlShortener | ||
> This is an UrlShortener built with Flask and Postgresql. | ||
> | ||
> | ||
|
||
# 👨💻Installation | ||
## 📄Pre-Requirements | ||
- Python Installed (Recommended version 3.8 or above) | ||
- Pip Package Manager (pip) | ||
## ⚙️How to use it? | ||
1. Download this repository with git clone or by clicking the download as archive on this page | ||
|
||
``` | ||
git clone https://github.com/Fer-Bar/UrlShortener.git | ||
``` | ||
Go to the project directory. | ||
``` | ||
cd url_shortener | ||
``` | ||
2. Create a virtual environment: | ||
### 🪟Windows: | ||
``` | ||
py -m venv venv | ||
``` | ||
Once created you can activate it. | ||
``` | ||
venv\Scripts\activate.bat | ||
``` | ||
### 🐧Unix or MacOS: | ||
``` | ||
pip install virtualenv | ||
virtualenv venv | ||
``` | ||
Once created you can activate it. | ||
``` | ||
source venv/bin/activate | ||
``` | ||
3. Install dependencies with `pip install -r requirements.txt`. Make sure everything is installed properly with `pip freeze`. | ||
4. The last step is run the [main.py](main.py) file, if you want you can change the host and the port. For example: | ||
``` | ||
app.run(host='localhost', port=9000, debug=True) | ||
``` | ||
## 🧪 Tests | ||
- To run tests, run the following command: | ||
``` | ||
python -m pytest -v | ||
``` | ||
## 🔃Migrations | ||
- To run the migrations, just run the following commands: | ||
This command adds a `migrations` directory in the root of your project. This is a directory where all the migration scripts are going to be stored. | ||
``` | ||
flask db init | ||
``` | ||
The next step in the process is to create an initial migration, using the `migrate` command: | ||
``` | ||
flask db migrate -m "Initial migration." | ||
``` | ||
Then you can apply the migration to the database: | ||
``` | ||
flask db migrate -m "Initial migration." | ||
``` | ||
## 😎 Author | ||
👤 **Fernando Barrientos** | ||
<!---* Website: xadec | ||
--> | ||
* Website: [fer-bar.github.io](https://fer-bar.github.io/Portfolio/) | ||
* Github: [Fer-Bar](https://github.com/Fer-Bar) | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from decouple import config | ||
|
||
|
||
DATABASE_URI = config("DATABASE_URL") | ||
if DATABASE_URI.startswith("postgres://"): | ||
DATABASE_URI = DATABASE_URI.replace("postgres://", "postgresql://", 1) | ||
|
||
|
||
class Config(object): | ||
DEBUG = False | ||
TESTING = False | ||
CSRF_ENABLED = True | ||
SECRET_KEY = config('SECRET_KEY', default='guess-me') | ||
SQLALCHEMY_DATABASE_URI = DATABASE_URI | ||
SQLALCHEMY_TRACK_MODIFICATIONS = False | ||
|
||
|
||
class ProductionConfig(Config): | ||
DEBUG = False | ||
|
||
|
||
class StagingConfig(Config): | ||
DEVELOPMENT = True | ||
DEBUG = True | ||
|
||
|
||
class DevelopmentConfig(Config): | ||
DEVELOPMENT = True | ||
DEBUG = True | ||
|
||
|
||
class TestingConfig(Config): | ||
TESTING = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
SECRET_KEY=b48d8c4c124c654036d0250be7ff9dbb | ||
DATABASE_URL=sqlite:///shorty.db | ||
APP_SETTINGS=config.DevelopmentConfig | ||
FLASK_APP=core |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from flask import Flask | ||
from flask_sqlalchemy import SQLAlchemy | ||
from flask_migrate import Migrate | ||
from decouple import config | ||
|
||
|
||
app = Flask(__name__) | ||
app.config.from_object(config("APP_SETTINGS")) | ||
|
||
db = SQLAlchemy(app) | ||
migrate = Migrate(app, db) | ||
|
||
from core import routes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from core import db | ||
from datetime import datetime | ||
|
||
|
||
class ShortUrl(db.Model): | ||
""" | ||
Class that represents a shorturl of our application | ||
The following attributes of a shorturl are stored in this table: | ||
* original_url - url who user give us | ||
* short_id - short_id that serves to redirect to the original_url | ||
* created_at - creation date | ||
""" | ||
id = db.Column(db.Integer, primary_key=True) | ||
original_url = db.Column(db.String(500), nullable=False) | ||
short_id = db.Column(db.String(20), nullable=False, unique=True) | ||
created_at = db.Column(db.DateTime(), default=datetime.now(), nullable=False) | ||
|
||
def __repr__(self): | ||
return f'ShortUrl: {self.short_id}' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import string | ||
import validators | ||
|
||
from datetime import datetime | ||
from core.models import ShortUrl | ||
from core import app, db | ||
from random import choice | ||
from flask import render_template, request, flash, redirect, url_for | ||
|
||
|
||
def generate_short_id(num_of_chars: int) -> str: | ||
"""Function to generate short_id of specified number of characters""" | ||
return ''.join(choice(string.ascii_letters+string.digits) for _ in range(num_of_chars)) | ||
|
||
|
||
@app.route('/', methods=['GET', 'POST']) | ||
def index(): | ||
""" | ||
The main view receives POST and GET requests | ||
""" | ||
if request.method == 'POST': | ||
url = request.form['url'] | ||
short_id = request.form['custom_id'] | ||
|
||
if short_id and ShortUrl.query.filter_by(short_id=short_id).first() is not None: | ||
flash('Please enter different custom id!') | ||
return redirect(url_for('index')) | ||
|
||
if not validators.url(url): | ||
flash('Enter a valid url.') | ||
return redirect(url_for('index')) | ||
|
||
if not url: | ||
flash('The URL is required!') | ||
return redirect(url_for('index')) | ||
|
||
if not short_id: | ||
short_id = generate_short_id(8) | ||
|
||
new_link = ShortUrl(original_url=url, | ||
short_id=short_id, | ||
created_at=datetime.now()) | ||
db.session.add(new_link) | ||
db.session.commit() | ||
short_url = request.host_url + short_id | ||
|
||
return render_template('index.html', short_url=short_url) | ||
|
||
return render_template('index.html') | ||
|
||
@app.route('/<short_id>') | ||
def redirect_url(short_id: str): | ||
""" | ||
This view redirects to the original_url address \n | ||
Only receives GET requests | ||
""" | ||
link = ShortUrl.query.filter_by(short_id=short_id).first() | ||
if link: | ||
return redirect(link.original_url) | ||
else: | ||
flash('Invalid URL') | ||
return redirect(url_for('index')) |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<!-- Required meta tags --> | ||
<meta charset="utf-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | ||
|
||
<!-- Bootstrap CSS --> | ||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous"> | ||
|
||
<title>{% block title %} {% endblock %}</title> | ||
</head> | ||
<body style="background-color: #112A6c"> | ||
<div style="font-family: monospace, sans-serif;" class="container mt-3"> | ||
{% for message in get_flashed_messages() %} | ||
<div class="alert alert-danger">{{ message }}</div> | ||
{% endfor %} | ||
{% block content %} {% endblock %} | ||
</div> | ||
|
||
<!-- Optional JavaScript --> | ||
<!-- jQuery first, then Popper.js, then Bootstrap JS --> | ||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" integrity="sha384-eMNCOe7tC1doHpGoWe/6oMVemdAVTMs2xqW4mwXrXsW0L84Iytr2wi5v2QjrP/xp" crossorigin="anonymous"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-cn7l7gDp0eyniUwwAZgrzD06kc/tftFf19TOAs2zVinnD/C7E91j9yyk5//jjpt/" crossorigin="anonymous"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{% extends 'base.html' %} | ||
|
||
{% block content %} | ||
<h1 style="color: #ffffff"class="text-center mb-3">{% block title %} Welcome to Linkcito {% endblock %}</h1> | ||
<div class="row"> | ||
<div class="col-md-2"></div> | ||
<div class="col-md-8"> | ||
<form method="post" action="{{url_for('index')}}"> | ||
<div class="form-floating mb-3"> | ||
<input type="text" name="url" id="url" | ||
placeholder="Enter looooooooooooong URL" class="form-control" | ||
value="{{ request.form['url'] }}" autofocus></input> | ||
<label style="color: #242424" for="url">URL</label> | ||
</div> | ||
<div class="form-floating mb-3"> | ||
<input type="text" name="custom_id" id="custom_id" | ||
placeholder="Want to customise? (optional)" class="form-control" | ||
value="{{ request.form['custom_id'] }}"></input> | ||
<label style="color: #242424" for="custom_id">Custom Short ID</label> | ||
</div> | ||
|
||
<div class="form-group text-center"> | ||
<button style="background-color: #F95B5C !important; border-color: #F95B5C !important" | ||
type="submit" class="btn btn-lg btn-primary">Shorten</button> | ||
</div> | ||
</form> | ||
|
||
{% if short_url %} | ||
<hr> | ||
<h5 class="text-center" style="color: #ffffff">This is your short URL:</h5 style="color:"> | ||
<div class="text-center"><a style="color:#f8f9fa"href="{{ short_url }}" target="_blank">{{ short_url }}</a></div> | ||
{% endif %} | ||
</div> | ||
<div class="col-md-2"></div> | ||
</div> | ||
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from core import app | ||
|
||
if __name__ == '__main__': | ||
app.run(host='0.0.0.0', port=5000, debug=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Single-database configuration for Flask. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# A generic, single database configuration. | ||
|
||
[alembic] | ||
# template used to generate migration files | ||
# file_template = %%(rev)s_%%(slug)s | ||
|
||
# set to 'true' to run the environment during | ||
# the 'revision' command, regardless of autogenerate | ||
# revision_environment = false | ||
|
||
|
||
# Logging configuration | ||
[loggers] | ||
keys = root,sqlalchemy,alembic,flask_migrate | ||
|
||
[handlers] | ||
keys = console | ||
|
||
[formatters] | ||
keys = generic | ||
|
||
[logger_root] | ||
level = WARN | ||
handlers = console | ||
qualname = | ||
|
||
[logger_sqlalchemy] | ||
level = WARN | ||
handlers = | ||
qualname = sqlalchemy.engine | ||
|
||
[logger_alembic] | ||
level = INFO | ||
handlers = | ||
qualname = alembic | ||
|
||
[logger_flask_migrate] | ||
level = INFO | ||
handlers = | ||
qualname = flask_migrate | ||
|
||
[handler_console] | ||
class = StreamHandler | ||
args = (sys.stderr,) | ||
level = NOTSET | ||
formatter = generic | ||
|
||
[formatter_generic] | ||
format = %(levelname)-5.5s [%(name)s] %(message)s | ||
datefmt = %H:%M:%S |
Oops, something went wrong.