diff --git a/api/servers b/api/servers deleted file mode 100644 index 71c755c..0000000 --- a/api/servers +++ /dev/null @@ -1,100 +0,0 @@ -#! /usr/bin/python3 - -import os, sys -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -import MySQLdb -import config -import json - -def print_json_200(data): - # respond with json data - print("Status: 200 OK") - print("Content-Type: application/json\n") - print(json.dumps(data)) - -def print_status_400(body: str): - print("Status: 400 Bad Request") - print("Content-Type: text/plain\n") - print(f"Invalid Request: {body}") - -def print_status_405(extra_path: str, allowed_method: str): - print("Status 405: Method Not Allowed") - print("Content-Type: text/plain\n") - print(f"Method '{allowed_method}' isn't allowed for path: '{extra_path}'\n \ - Allowed method: POST") - -def print_status_500(body: str): - print("Status: 500 Internal Server Error") - print("Content-Type: text/plain\n") - print(f"Database Connection Error: {body}") - -def get_db_connection(): - try: - db = MySQLdb.connect( - host=config.DB_HOST, - user=config.DB_USER, - passwd=config.DB_PASSWORD, - db=config.DB_NAME, - port=config.DB_PORT, - ssl={"ssl": {}} - ) - return db - - except MySQLdb.Error as e: - print_status_500(str(e)) - exit(1) - -def main(): - # extract basic request info - request_method = os.environ["REQUEST_METHOD"] - extra_path = os.environ.get("PATH_INFO", "") - path_component = extra_path.split("/") - if request_method == "GET": - db = get_db_connection() - cursor = db.cursor() - if extra_path == "" or extra_path == "/": - # return all instances in database - cursor.execute("SELECT * FROM servers;") - rows = cursor.fetchall() - resp = [] - for row in rows: - resp.append( - { - "id": row[0], - "owner": row[1], - "description": row[2], - "instance_id": row[3], - "public_ip": row[4], - "ready": row[5] - } - ) - print_json_200(resp) - - elif extra_path.startswith("/") and extra_path[1:].isnumeric(): # /api/servers/ - # return a single instance in db - primary_key = int(extra_path[1:]) - cursor.execute("SELECT * FROM servers WHERE id = %s;", (primary_key,)) - row = cursor.fetchone() - - if row == None: - print_status_400("can't find server with this id in the database") - return - - resp = { - "id": row[0], - "owner": row[1], - "description": row[2], - "instance_id": row[3], - "public_ip": row[4], - "ready": row[5] - } - - print_json_200(resp) - - else: - print_status_400("Page not found: " + extra_path) - else: - print_status_405(extra_path, "GET") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/login b/login deleted file mode 100644 index 5e2c721..0000000 --- a/login +++ /dev/null @@ -1,44 +0,0 @@ -#! /usr/bin/python3 - -import os -import cgi -import config - -def print_status_400(body: str): - print("Status: 400 Bad Request") - print("Content-Type: text/plain\n") - print(f"Invalid Request: {body}") - -def print_status_405(extra_path: str, allowed_method: str): - print("Status 405: Method Not Allowed") - print("Content-Type: text/plain\n") - print(f"Method '{allowed_method}' isn't allowed for path: '{extra_path}'\n \ - Allowed method: POST") - -def print_status_500(body: str): - print("Status: 500 Internal Server Error") - print("Content-Type: text/plain\n") - print(f"Database Connection Error: {body}") - -def main(): - # extract basic request info - request_method = os.environ["REQUEST_METHOD"] - extra_path = os.environ.get("PATH_INFO", "").lstrip("/") - if request_method == "POST": # read request body - form = cgi.FieldStorage() - user = form.getvalue("user", "") - - if not user: - print_status_400("Field 'user' is empty") - return - - print(f"Set-Cookie: user={user}; Path=/; HttpOnly; SameSite=Lax") - - print("Status: 303 See Other") - print(f"Location: {config.ROOT_URL}/index.html") - print() - else: - print_status_405(extra_path, request_method) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/user/lib/cgi-bin/api/servers b/user/lib/cgi-bin/api/servers new file mode 100644 index 0000000..16e39d5 --- /dev/null +++ b/user/lib/cgi-bin/api/servers @@ -0,0 +1,44 @@ +#! /usr/bin/python3 + +import os, sys +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from http.cookies import SimpleCookie +from get_servers import get_all_servers, get_server +from utils import * + +def main(): + # extract basic request info + request_method = os.environ["REQUEST_METHOD"] + extra_path = os.environ.get("PATH_INFO", "") + + # Get user field from cookie + cookie_string = os.environ.get("HTTP_COOKIE", "") + parsed = SimpleCookie(cookie_string) + user = parsed.get("user").value if "user" in parsed else None + + if not user: # Redirect to login + print_redirect_300(f"login.html") + return + + if request_method == "GET": + if extra_path == "" or extra_path == "/": + # return all instances in database + resp = get_all_servers(user) + print_json_200(resp) + + elif extra_path.startswith("/") and extra_path[1:].isnumeric(): # /api/servers/ + # return a single instance in db + primary_key = int(extra_path[1:]) + resp = get_server(primary_key, user) + if resp == {}: + print_status_400("can't find server with this id in the database") + print_json_200(resp) + + else: + print_status_400("Page not found: " + extra_path) + + else: + print_status_405(extra_path, "GET") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/create_server b/user/lib/cgi-bin/create_server similarity index 59% rename from create_server rename to user/lib/cgi-bin/create_server index e040c9d..097ba5c 100644 --- a/create_server +++ b/user/lib/cgi-bin/create_server @@ -6,51 +6,16 @@ import MySQLdb import config from pydo import Client from http.cookies import SimpleCookie +from utils import * config_commands = """#cloud-config runcmd: - apt update - apt install apache2 -y - systemctl enable apache2 - - mkdir -p /etc/systemd/system/apache2.service.d/ - - bash -c 'echo -e "[Unit]\nAfter=network-online.target\nWants=network-online.target" > /etc/systemd/system/apache2.service.d/override.conf' - - systemctl daemon-reload - - systemctl restart apache2 - - echo "hello, world" > /root/dummy.txt + - systemctl start apache2 """ -def print_status_400(body: str): - print("Status: 400 Bad Request") - print("Content-Type: text/plain\n") - print(f"Invalid Request: {body}") - -def print_status_405(extra_path: str, allowed_method: str): - print("Status 405: Method Not Allowed") - print("Content-Type: text/plain\n") - print(f"Method '{allowed_method}' isn't allowed for path: '{extra_path}'\n \ - Allowed method: POST") - -def print_status_500(body: str): - print("Status: 500 Internal Server Error") - print("Content-Type: text/plain\n") - print(f"Database Connection Error: {body}") - -def get_db_connection(): - try: - db = MySQLdb.connect( - host=config.DB_HOST, - user=config.DB_USER, - passwd=config.DB_PASSWORD, - db=config.DB_NAME, - port=config.DB_PORT, - ssl={"ssl": {}} - ) - return db - - except MySQLdb.Error as e: - print_status_500(str(e)) - exit(1) - def create_server(user: str, desc: str) -> int: try: if (user == "" and desc is None): @@ -98,9 +63,7 @@ def add_server_to_db(user: str, desc: str, instance_id: int, ready: bool): # run background script os.system(f"./monitor_new_VM {instance_id} {new_server_id} 1>/dev/null 2>/dev/null &") - print("Status: 303 See Other") - print(f"Location: {config.ROOT_URL}/cgi-bin/api/servers/{new_server_id}") - print() + print_redirect_300("cgi-bin/home") except Exception as e: print_status_500(str(e)) @@ -109,17 +72,12 @@ def main(): # extract basic request info request_method = os.environ["REQUEST_METHOD"] extra_path = os.environ.get("PATH_INFO", "").lstrip("/") - path_component = extra_path.split("/") if request_method == "POST" or request_method == "GET": # read query parameter case of GET/POST method form = cgi.FieldStorage() # Get user field from cookie - cookie_string = os.environ.get("HTTP_COOKIE", "") - parsed = SimpleCookie(cookie_string) - user = parsed.get("user").value if "user" in parsed else None + user = get_cookie() if not user: # Redirect to login - print("Status: 303 See Other") - print(f"Location: {config.ROOT_URL}/login.html") - print() + print_redirect_300("login.html") return # Get desc field from query param desc = form.getvalue("desc", None) diff --git a/user/lib/cgi-bin/get_servers.py b/user/lib/cgi-bin/get_servers.py new file mode 100644 index 0000000..81a5e2a --- /dev/null +++ b/user/lib/cgi-bin/get_servers.py @@ -0,0 +1,48 @@ +from utils import get_db_connection + +def get_all_servers(user: str) -> list[dict]: + db = get_db_connection() + cursor = db.cursor() + # return all instances in database + cursor.execute("SELECT * FROM servers WHERE owner = %s;", (user,)) + rows = cursor.fetchall() + + cursor.close() + db.close() + + resp = [] + for row in rows: + resp.append( + { + "id": row[0], + "owner": row[1], + "description": row[2], + "instance_id": row[3], + "public_ip": row[4], + "ready": row[5] + } + ) + return resp + +def get_server(primary_key: int, user: str) -> dict: + db = get_db_connection() + cursor = db.cursor() + # return a single instance in db + cursor.execute("SELECT * FROM servers WHERE id = %s AND owner = %s;", (primary_key, user,)) + row = cursor.fetchone() + + cursor.close() + db.close() + + if row is None: + return {} + + resp = { + "id": row[0], + "owner": row[1], + "description": row[2], + "instance_id": row[3], + "public_ip": row[4], + "ready": row[5] + } + return resp \ No newline at end of file diff --git a/user/lib/cgi-bin/home b/user/lib/cgi-bin/home new file mode 100644 index 0000000..41afb76 --- /dev/null +++ b/user/lib/cgi-bin/home @@ -0,0 +1,89 @@ +#! /usr/bin/python3 + +from get_servers import get_all_servers +from utils import print_redirect_300, get_cookie + +def generate_html(user: str, servers: list[dict]): + print("Content-Type: text/html\n") + print(f""" + + + + {user} Cloud Servers + + + + +

Welcome, {user}

+

Create New Server

+
+ + + +
+

Your Servers

+ + + + + + + + + + + + """) + + for server in servers: + sid = server["id"] + desc = server["description"] + ip = server["public_ip"] + ready = server["ready"] + + status = "Ready" if ready else "Pending" + ip_display = ip if ip else "N/A" + print(f""" + + + + + + + + """) + + print(""" + +
IDDescriptionStatusIPActions
{sid}{desc}{status}{ip_display} +
+ + +
+
+ + + """) + + +def main(): + # Get user field from cookie + user = get_cookie() + if not user: # Redirect to login + print_redirect_300("login.html") + return + user = user.strip() + # Fetch user's servers + servers = get_all_servers(user) + print(f"servers: {servers}") + # Generate HTML + generate_html(user, servers) + +if __name__ == "__main__": + main() diff --git a/user/lib/cgi-bin/login b/user/lib/cgi-bin/login new file mode 100644 index 0000000..e68de66 --- /dev/null +++ b/user/lib/cgi-bin/login @@ -0,0 +1,27 @@ +#! /usr/bin/python3 + +import os +import cgi +import config +from utils import * + +def main(): + # extract basic request info + request_method = os.environ["REQUEST_METHOD"] + extra_path = os.environ.get("PATH_INFO", "").lstrip("/") + if request_method == "POST": # read request body + form = cgi.FieldStorage() + user = form.getvalue("user", "") + + if not user: + print_status_400("Field 'user' is empty") + return + + print(f"Set-Cookie: user={user}; Path=/; HttpOnly; SameSite=Lax") + + print_redirect_300("cgi-bin/home") + else: + print_status_405(extra_path, request_method) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/monitor_new_VM b/user/lib/cgi-bin/monitor_new_VM similarity index 86% rename from monitor_new_VM rename to user/lib/cgi-bin/monitor_new_VM index e8b8f31..0e84fac 100644 --- a/monitor_new_VM +++ b/user/lib/cgi-bin/monitor_new_VM @@ -5,22 +5,7 @@ from pydo import Client import time import MySQLdb import config - -def get_db_connection(): - try: - db = MySQLdb.connect( - host=config.DB_HOST, - user=config.DB_USER, - passwd=config.DB_PASSWORD, - db=config.DB_NAME, - port=config.DB_PORT, - ssl={"ssl": {}} - ) - return db - - except MySQLdb.Error as e: - print(str(e)) - exit(1) +from utils import * def main(): try: diff --git a/terminate_server b/user/lib/cgi-bin/terminate_server similarity index 62% rename from terminate_server rename to user/lib/cgi-bin/terminate_server index 981d460..9bdba82 100644 --- a/terminate_server +++ b/user/lib/cgi-bin/terminate_server @@ -2,47 +2,10 @@ import cgi import os -import MySQLdb import config from pydo import Client from http.cookies import SimpleCookie - -def print_status_400(body: str): - print("Status: 400 Bad Request") - print("Content-Type: text/plain\n") - print(f"Invalid Request: {body}") - -def print_status_403(body: str): - print("Status: 403 Forbidden") - print("Content-Type: text/plain\n") - print(f"Forbidden: {body}") - -def print_status_405(extra_path: str, allowed_method: str): - print("Status 405: Method Not Allowed") - print("Content-Type: text/plain\n") - print(f"Method '{allowed_method}' isn't allowed for path: '{extra_path}'\n \ - Allowed method: POST") - -def print_status_500(body: str): - print("Status: 500 Internal Server Error") - print("Content-Type: text/plain\n") - print(f"Database Connection Error: {body}") - -def get_db_connection(): - try: - db = MySQLdb.connect( - host=config.DB_HOST, - user=config.DB_USER, - passwd=config.DB_PASSWORD, - db=config.DB_NAME, - port=config.DB_PORT, - ssl={"ssl": {}} - ) - return db - - except MySQLdb.Error as e: - print_status_500(str(e)) - exit(1) +from utils import * def terminate_server(primary_key: str, user: str): try: @@ -95,9 +58,7 @@ def remove_server_from_db(primary_key: str): cursor.close() db.close() - print("Status: 303 See Other") - print(f"Location: {config.ROOT_URL}/cgi-bin/api/servers") - print() + print_redirect_300("cgi-bin/home") except Exception as e: print_status_500(str(e)) @@ -108,13 +69,9 @@ def main(): extra_path = os.environ.get("PATH_INFO", "").lstrip("/") # Check user field in cookie - cookie_string = os.environ.get("HTTP_COOKIE", "") - parsed = SimpleCookie(cookie_string) - user = parsed.get("user").value if "user" in parsed else None + user = get_cookie() if not user: # Redirect to login - print("Status: 303 See Other") - print(f"Location: {config.ROOT_URL}/login.html") - print() + print_redirect_300("login.html") return if request_method == "POST" or request_method == "GET": # read query parameter case of GET/POST method diff --git a/user/lib/cgi-bin/utils.py b/user/lib/cgi-bin/utils.py new file mode 100644 index 0000000..6917b27 --- /dev/null +++ b/user/lib/cgi-bin/utils.py @@ -0,0 +1,59 @@ +import os +import config +import json +import MySQLdb +from http.cookies import SimpleCookie + +def print_json_200(data: any): + # respond with json data + print("Status: 200 OK") + print("Content-Type: application/json\n") + print(json.dumps(data)) + +def print_redirect_300(location: str): + print("Status: 303 See Other") + print(f"Location: {config.ROOT_URL}/{location}") + print() + +def print_status_400(body: str): + print("Status: 400 Bad Request") + print("Content-Type: text/plain\n") + print(f"Invalid Request: {body}") + +def print_status_403(body: str): + print("Status: 403 Forbidden") + print("Content-Type: text/plain\n") + print(f"Forbidden: {body}") + +def print_status_405(extra_path: str, allowed_method: str): + print("Status 405: Method Not Allowed") + print("Content-Type: text/plain\n") + print(f"Method '{allowed_method}' isn't allowed for path: '{extra_path}'\n \ + Allowed method: POST") + +def print_status_500(body: str): + print("Status: 500 Internal Server Error") + print("Content-Type: text/plain\n") + print(f"Database Connection Error: {body}") + +def get_db_connection(): + try: + db = MySQLdb.connect( + host=config.DB_HOST, + user=config.DB_USER, + passwd=config.DB_PASSWORD, + db=config.DB_NAME, + port=config.DB_PORT, + ssl={"ssl": {}} + ) + return db + + except MySQLdb.Error as e: + print_status_500(str(e)) + exit(1) + +def get_cookie() -> str | None: + # Get user field from cookie + cookie_string = os.environ.get("HTTP_COOKIE", "") + parsed = SimpleCookie(cookie_string) + return parsed.get("user").value if "user" in parsed else None \ No newline at end of file