chore(#8): Initial migration into the new frontend UI #12

Merged
anthony merged 3 commits from new-ui into main 2024-12-25 13:20:12 +00:00
9 changed files with 87 additions and 150 deletions

View file

@ -4,11 +4,18 @@ Shortenit is a tool to shorten urls.
**NOTE**: This is a very early draft project. Contributions are welcome.
## Install
To install `shortenit` and all of its dependencies for development, run the following script.
``` shell
$ scripts/install.sh
```
## Running
To run `shortenit`, edit the configuration file found in [config/config.yaml](config/config.yaml) then run the following commands.
To run `shortenit` for development, edit the configuration file found in [config/config.yaml](config/config.yaml) then run the following script.
```text
$ pip install -e .
$ shortenit
$ scripts/run.sh
```

View file

@ -2,6 +2,7 @@ Server:
host: 127.0.0.1
port: 8000
cors: False
static_folder: frontend/build
Web:
host: 127.0.0.1

View file

@ -6,29 +6,28 @@
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'shortenit'
copyright = '2024, Elia el Lazkani'
author = 'Elia el Lazkani'
release = '0.0.0'
project = "shortenit"
copyright = "2024, Elia el Lazkani"
author = "Elia el Lazkani"
release = "0.0.0"
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.viewcode',
'sphinx_autodoc_typehints',
"sphinx.ext.autodoc",
"sphinx.ext.todo",
"sphinx.ext.coverage",
"sphinx.ext.viewcode",
"sphinx_autodoc_typehints",
]
templates_path = ['_templates']
templates_path = ["_templates"]
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = 'alabaster'
html_static_path = ['_static']
html_theme = "alabaster"
html_static_path = ["_static"]

View file

@ -32,14 +32,14 @@ export default function () {
// Send the POST request to the backend
await axios
.post("http://127.0.0.1:8000/shortenit", {
.post("http://127.0.0.1:8000/api/v1/shorten", {
url: url,
timestamp: timestamp,
})
.then((response) => {
if (response) {
const code: string = response.data.url;
const fullShortenedUrl: string = `http://127.0.0.1:8000/${code}`;
const fullShortenedUrl: string = `http://127.0.0.1:8000/r/${code}`;
setShortenedUrl(fullShortenedUrl);
setShowInput(true);
}

7
scripts/install.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/bash
cd frontend
npm install
cd ..
poetry install

6
scripts/run.sh Executable file
View file

@ -0,0 +1,6 @@
#!/bin/bash
cd frontend
npm run build
cd ..
poetry run shortenit "$@"

View file

@ -54,8 +54,7 @@ def main() -> typing.NoReturn:
def shorten_it(config: dict, session: Session, data: str, ttl: int):
shortener_config = config.get("Shortener", None)
shortener = Shortener(session, shortener_config)
shortener = Shortener(session, config)
identifier = shortener.generate_uuid()
if identifier:
try:

View file

@ -1,121 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="The personal URL Shortener">
<meta name="author" content="">
<title>ShortenIt</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.4/clipboard.min.js"></script>
</head>
<script type="text/javascript">
var href = window.location.href;
$(function() {
$('#submitButton').click(function() {
$.ajax({
type: "POST",
url: "/shortenit",
data: JSON.stringify({'url' : $('#url-input').val(),
'timestamp': Date.now()}),
success: returnSuccess,
error: returnFailure,
dataType: 'json',
contentType: "application/json",
});
});
});
function returnSuccess(data, textStatus, jqXHR) {
var url = href.concat(data.url);
console.log(href)
if(data.url) {
document.getElementById("url-result").value = url;
} else {
document.getElementById("url-result").value = "The URL was too short and somehow got lost on the way, please try generating a new one.";
}
}
function returnFailure(data, textStatus, jqXHR) {
document.getElementById("url-result").value = "Please enter a valid URL!";
}
window.onload=function() {
console.log(ClipboardJS.isSupported())
var clipboard = new ClipboardJS('.btn', {
container: document.getElementById('copy-button')
});
clipboard.on('success', function(e) {
console.info('Action:', e.action);
console.info('Text:', e.text);
console.info('Trigger:', e.trigger);
});
clipboard.on('error', function(e) {
console.error('Action:', e.action);
console.error('Trigger:', e.trigger);
});
}
</script>
<body>
<!-- Page Content -->
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
<h1 class="mt-5">Shorten It!</h1>
</div>
</div>
<div class="row">
<div class="col-lg-12 form-group">
<div class="form-row">
<div class="col-9">
<input type="text" class="form-control" name="url" id="url-input" placeholder="https://www.duckduckgo.com" />
</div>
<div class="col-3">
<button id="submitButton" type="button" class="btn btn-outline-primary">Shorten URL</button>
</div>
</div>
</div>
</div>
<!-- <a href="#" id="url-result">Enter URL</a> -->
<div class="row">
<div class="col-lg-12 form-group">
<div class="form-row">
<div class="col-9">
<input type="text" id="url-result" class="form-control" readonly>
</div>
<div class="col-3">
<div>
<button class="btn btn-outline-primary" data-clipboard-target="#url-result" data-clipboard-action="copy">Copy</button>
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript -->
</body>
</html>

View file

@ -1,10 +1,11 @@
import logging
import os
from pathlib import Path
from flask_cors import CORS
import trafaret
from flask import Flask, abort, redirect, render_template, request
from flask import Flask, abort, redirect, request, send_from_directory
from flask_cors import CORS
from .common import check_file
class Web:
@ -21,6 +22,7 @@ class Web:
self.app.run(host=self.host, port=self.port, debug=self.debug)
def init(self):
server_config = self.handler.configuration.get("Server", None)
self.app = Flask(__name__)
self.setup_routes()
if server_config and server_config.get("cors", False):
@ -28,12 +30,21 @@ class Web:
CORS(self.app)
def setup_routes(self):
self.app.add_url_rule("/", "/", self.handler.index, methods=["GET"])
self.app.add_url_rule(
"/shortenit", "/shortenit", self.handler.shortenit, methods=["POST"]
"/", "/", self.handler.index, methods=["GET"], defaults={"path": ""}
)
self.app.add_url_rule("/<path:path>", "/", self.handler.index, methods=["GET"])
self.app.add_url_rule(
"/static/css/<path:path>", "css", self.handler.css, methods=["GET"]
)
self.app.add_url_rule(
"/<identifier>", "/identifier", self.handler.short_redirect, methods=["GET"]
"/static/js/<path:path>", "js", self.handler.js, methods=["GET"]
)
self.app.add_url_rule(
"/api/v1/shorten", "shorten", self.handler.shortenit, methods=["POST"]
)
self.app.add_url_rule(
"/r/<identifier>", "redirect", self.handler.short_redirect, methods=["GET"]
)
@ -48,6 +59,9 @@ class SiteHandler:
{trafaret.Key("url"): trafaret.URL, trafaret.Key("timestamp"): trafaret.Int}
)
def _get_server_config(self):
return self.configuration.get("Server", None)
def shortenit(self):
data = request.get_json()
try:
@ -59,7 +73,10 @@ class SiteHandler:
abort(400)
try:
short_url = self.shorten_url(
self.configuration, self.database, data["url"], data["timestamp"]
self.configuration.get("Shortener", None),
self.database,
data["url"],
data["timestamp"],
)
except KeyError as e:
self.logger.error(e)
@ -75,7 +92,29 @@ class SiteHandler:
abort(404)
return redirect(url)
def index(self):
return render_template("index.html")
def index(self, path):
if path != "":
return self._fetch_from_directory(path)
else:
return self._fetch_from_directory("index.html")
def css(self, path):
path = "static/css/" + path
return self._fetch_from_directory(path)
def js(self, path):
path = "static/js/" + path
return self._fetch_from_directory(path)
def _fetch_from_directory(self, path):
try:
project_root = Path(__file__).parent.parent
static_folder = (
f"{project_root}/" + self._get_server_config()["static_folder"]
)
if check_file(static_folder + "/" + path):
return send_from_directory(static_folder, path)
else:
abort(404)
except:
abort(500)