Skip to content

Commit

Permalink
Merge pull request #4 from mehmetemredemir/main
Browse files Browse the repository at this point in the history
feat: server code and setup script created
  • Loading branch information
cinarcivan authored Aug 9, 2024
2 parents 50a3b32 + dfcb31f commit c5ebb3c
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 1 deletion.
6 changes: 6 additions & 0 deletions brevurl_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"domain": "",
"port": "",
"account_available": 0,
"firebase_configdst": ""
}
17 changes: 17 additions & 0 deletions cors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'''
written to bypass CORS when testing on local device. Don't forget to run it!!!
'''

from http.server import SimpleHTTPRequestHandler, HTTPServer

class CORSHTTPRequestHandler(SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header('Access-Control-Allow-Origin', '*')
super().end_headers()

port = 8000
server_address = ('', port)
httpd = HTTPServer(server_address, CORSHTTPRequestHandler)
print(f'Running on port {port}...')
httpd.serve_forever()
13 changes: 13 additions & 0 deletions firebase_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"type": "",
"project_id": "",
"private_key_id": "",
"private_key": "",
"client_email": "",
"client_id": "",
"auth_uri": "",
"token_uri": "",
"auth_provider_x509_cert_url": "",
"client_x509_cert_url": "",
"universe_domain": ""
}
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "brevurl"
version = "0.1.0"
description = "Open source, fast and modern URL shortener"
authors = [
{name = "Mehmet Emre Demir"},
{ name = "Mehmet Emre Demir", email = "[email protected]"},
]
maintainers = [
{name = "Çınar Civan", email = "[email protected]"},
Expand All @@ -19,6 +19,13 @@ keywords = ["url", "url-shortener", "brevurl", "link-shortener", "scihook", "ope

[tool.poetry.dependencies]
python = "^3.10"
flask = "*"
firebase-admin = "*"
flask-cors = "*"
pyfiglet = "*"
colorama = "*"

# package versions will change

[project.urls]
Homepage = "https://opensource.scihook.org"
Expand Down
66 changes: 66 additions & 0 deletions src/server/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from flask import Flask, request, jsonify, redirect
from flask_cors import CORS
import hashlib
import firebase_admin
from firebase_admin import credentials, firestore
import json

with open('brevurl_config.json file path', 'r') as file:
brconfig = json.load(file)

app = Flask(__name__)
CORS(app)
port = brconfig["port"]

#Start firabase
cred = credentials.Certificate(brconfig["firebase_configdst"])
firebase_admin.initialize_app(cred)
db = firestore.client()


def shorten_url(url):
hashObject = hashlib.md5(url.encode())
shortHash = hashObject.hexdigest()[:6]
return shortHash

@app.route('/shorten', methods=['POST'])
def shorten():
data = request.json
originalUrl = data.get('url')
customShort = data.get('short')

if not originalUrl:
return jsonify({'error': 'URL is required'}), 400

if customShort:
doc_ref = db.collection('urls').document(customShort)
if doc_ref.get().exists:
return jsonify({'error': 'Custom short URL already exists'}), 400
short_url = customShort
else:
short_url = shorten_url(originalUrl)
doc_ref = db.collection('urls').document(short_url)
while doc_ref.get().exists:
short_url = shorten_url(short_url + "0")
doc_ref = db.collection('urls').document(short_url)


db.collection('urls').document(short_url).set({
'original_url': originalUrl
})

return jsonify({'short_url': f'{brconfig["domain"]}:{port}/{short_url}'}), 200

@app.route('/<short_url>', methods=['GET'])
def redirect_to_url(short_url):
doc_ref = db.collection('urls').document(short_url)
doc = doc_ref.get()

if not doc.exists:
return jsonify({'error': 'URL not found'}), 404

original_url = doc.to_dict().get('original_url')
return redirect(original_url)

if __name__ == '__main__':
app.run(debug=False,port=port)
87 changes: 87 additions & 0 deletions src/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import pyfiglet
from colorama import Fore, Style, init
import json
import firebase_admin
from firebase_admin import credentials, firestore
import sys


# Initialize colorama
init()

def print_ascii_art(text):
ascii_art = pyfiglet.figlet_format(text)
print(Fore.CYAN + ascii_art + Style.RESET_ALL)

def read_config_file(file_path):
with open(file_path, 'r') as file:
return json.load(file)

def write_config_file(file_path, data):
with open(file_path, 'w') as file:
json.dump(data, file, indent=4)

def validate_firebase_config(config):
for key, value in config.items():
if not value:
print(Fore.RED + f"Empty value found for key: {key}. Please complete firebase config file." + Style.RESET_ALL)
sys.exit()

def get_user_input(prompt, valid_responses=None):
while True:
user_input = input(prompt).strip().lower()
if valid_responses is None or user_input in valid_responses:
return user_input
print(Fore.RED + f"Invalid input. Please try again({valid_responses})" + Style.RESET_ALL)

def main():
print_ascii_art("Brevurl")

brconfig_dst = input("Please enter brevurl_config.json file destination: ")
frconfig_dst = input("Please enter firebase_config.json file destination: ")

try:
firebase_config = read_config_file(frconfig_dst)
brevurl_config = read_config_file(brconfig_dst)
except (FileNotFoundError, json.JSONDecodeError) as e:
print(Fore.RED + f"Error reading configuration files: {e}" + Style.RESET_ALL)
sys.exit()

print(Fore.YELLOW + "Checking firebase config file..." + Style.RESET_ALL)
validate_firebase_config(firebase_config)
print(Fore.GREEN + "Done" + Style.RESET_ALL)

domain = input("Please enter the URL address where the server is located (e.g., https://example.com): ")
port = input("Please enter the port that the server application will use (this port will be used for routing on nginx): ")
account_available = get_user_input(
"Would you like to activate the account system? (y/n): ",
valid_responses={'y', 'n'}
)
account_bool = 1 if account_available == 'y' else 0

print(Fore.YELLOW + "Editing the brevurl config file..." + Style.RESET_ALL)




brevurl_config.update({
"domain": domain,
"account_available": account_bool,
"port": port,
"firebase_configdst": frconfig_dst
})

try:
# Initialize firebase
cred = credentials.Certificate(frconfig_dst)
firebase_admin.initialize_app(cred)
db = firestore.client()
db.collection("urls").document("scihook").set({'original_url': 'https://scihook.org/'}) #create url entry

write_config_file(brconfig_dst, brevurl_config)
print(Fore.GREEN + "Successfully adjusted! If you want to make changes in the future, you can use this script or edit the config JSON files directly." + Style.RESET_ALL)
except Exception as e:
print(Fore.RED + f"Error: {e}" + Style.RESET_ALL)

if __name__ == "__main__":
main()
57 changes: 57 additions & 0 deletions test/web.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>URL Shortener</title>
<script>
fetch('brevurl_config.json file path')
//If you are testing on a local device, run test/cors.py and enter this as the path: http://localhost:8000/brevurl_config.json
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
window.sharedVariableport = data.port;
window.sharedVariabledomain = data.domain;
})
.catch(error => {
console.error('Error:', error);
});
</script>
</head>
<body>
<h1>URL Shortener</h1>
<form id="shorten-form">
<label for="url">URL:</label>
<input type="text" id="url" name="url" required>
<label for="short">Short Name(Optional):</label>
<input type="text" id="short" name="short">
<button type="submit">Shorten it!</button>
</form>
<div id="result"></div>

<script>
document.getElementById('shorten-form').addEventListener('submit', async function(event) {
event.preventDefault();
const url = document.getElementById('url').value;
const short = document.getElementById('short').value || null;
const response = await fetch(`${window.sharedVariabledomain}:${window.sharedVariableport}/shorten`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ url: url, short: short }),
});
const data = await response.json();
if (response.ok) {
document.getElementById('result').innerHTML = `Short URL: <a href="${data.short_url}" target="_blank">${data.short_url}</a>`;
} else {
document.getElementById('result').textContent = `Error: ${data.error}`;
}
});
</script>
</body>
</html>

0 comments on commit c5ebb3c

Please sign in to comment.