-
Notifications
You must be signed in to change notification settings - Fork 0
/
shorty.py
116 lines (93 loc) · 2.86 KB
/
shorty.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import hashlib
import random
import string
import urllib.parse
from datetime import timedelta
import connexion
from connexion.options import SwaggerUIOptions
from redis import Redis
BASE_URL = "http://example.com/"
redis = Redis(host="redis", port=6379)
def create_app():
app = connexion.FlaskApp(
__name__,
specification_dir="./",
swagger_ui_options=SwaggerUIOptions(swagger_ui=False),
)
app.add_api("openapi.yaml")
return app
def generate_short_code() -> str:
"""
Generate a short code for the given URL.
"""
# Generate a random 48 bit number and encode it in base62
random_number = random.getrandbits(48)
return encode_base_n(random_number, 62)
def encode_base_n(num, base):
"""
Encode a number in the given base using digits from 0-9a-zA-Z.
"""
digits = string.digits + string.ascii_letters
if num == 0:
return digits[0]
arr = []
while num:
rem = num % base
num //= base
arr.append(digits[rem])
arr.reverse()
return "".join(arr)
def shorten_url(body):
"""
Create a short URL for the given long URL.
"""
url = body.get("url")
if not url_valid(url):
return {"error": "Invalid URL provided"}, 400
# Check if the URL has been shortened before
url_sha1 = hashlib.sha1(url.encode("utf-8")).digest() # get bytes
url_sha1_base62 = encode_base_n(int.from_bytes(url_sha1), 62) # encode to base62
long_url_key = f"long_url:{url_sha1_base62}"
short_code = redis.get(long_url_key)
if short_code:
return {"url": f"{BASE_URL}{short_code.decode()}"}
short_code = generate_short_code()
short_code_key = f"code:{short_code}"
# Save the short code - URL mapping
redis.setex(short_code_key, timedelta(days=1), url)
# Save the URL - short code mapping
redis.setex(long_url_key, timedelta(days=1), short_code)
return {"url": f"{BASE_URL}{short_code}"}
def redirect_to_url(short_code):
"""
Redirect to the long URL corresponding to the given short code.
"""
url = redis.get(f"code:{short_code}")
if url:
return None, 302, {"Location": url.decode("utf-8")}
else:
return (
"""
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<h1>404 Not Found</h1>
<p>The requested URL was not found on the server.</p>
</body>
</html>
""",
404,
)
def url_valid(url: str) -> bool:
# Validate url using urllib
parsed_url = urllib.parse.urlparse(url)
if parsed_url.scheme not in ["http", "https"]:
return False
if not parsed_url.netloc:
return False
return len(url) <= 2048
if __name__ == "__main__":
app = create_app()
app.run(port=8080)