From 9821db72c6afc348dc0554444d9426b23e7bedc0 Mon Sep 17 00:00:00 2001
From: Fluffy-Bean <michal-gdula@protonmail.com>
Date: Tue, 26 Sep 2023 13:43:00 +0100
Subject: [PATCH] Yeet old runner script as it was useless Clean up code and
 styling

---
 onlylegs/app.py                               | 115 ++++++-------
 onlylegs/config.py                            |  19 ++-
 onlylegs/static/js/imagePage.js               |  41 -----
 onlylegs/static/js/uploadTab.js               |  77 +--------
 .../static/sass/components/notification.sass  |  28 +---
 onlylegs/static/sass/style.sass               |   6 +-
 onlylegs/utils/startup.py                     | 156 ++++++++++++++++++
 poetry.lock                                   | 118 ++++++++++++-
 pyproject.toml                                |   1 +
 setup/args.py                                 |  33 ----
 setup/configuration.py                        | 156 ------------------
 setup/runner.py                               |  35 ----
 12 files changed, 348 insertions(+), 437 deletions(-)
 create mode 100644 onlylegs/utils/startup.py
 delete mode 100644 setup/args.py
 delete mode 100644 setup/configuration.py
 delete mode 100644 setup/runner.py

diff --git a/onlylegs/app.py b/onlylegs/app.py
index 0dd4349..75533d7 100644
--- a/onlylegs/app.py
+++ b/onlylegs/app.py
@@ -1,67 +1,55 @@
 """
 Onlylegs Gallery
-This is the main app file, it loads all the other files and sets up the app
+This is the main app file, checks on app stability and runs all da shit
 """
 import os
 import logging
 
 from flask_assets import Bundle
 from flask_migrate import init as migrate_init
-
-from flask import Flask, render_template, abort
+from flask import Flask, render_template, abort, request
 from werkzeug.exceptions import HTTPException
-from werkzeug.security import generate_password_hash
 
+from onlylegs.utils import startup
 from onlylegs.extensions import db, migrate, login_manager, assets, compress, cache
-from onlylegs.config import INSTANCE_DIR, MIGRATIONS_DIR
+from onlylegs.config import INSTANCE_DIR, MIGRATIONS_DIR, APPLICATION_ROOT
 from onlylegs.models import Users
-from onlylegs.views import (
-    index as view_index,
-    image as view_image,
-    group as view_group,
-    settings as view_settings,
-    profile as view_profile,
+
+from onlylegs.views.index import blueprint as view_index
+from onlylegs.views.image import blueprint as view_image
+from onlylegs.views.group import blueprint as view_group
+from onlylegs.views.settings import blueprint as view_settings
+from onlylegs.views.profile import blueprint as view_profile
+from onlylegs.api import blueprint as api
+from onlylegs.auth import blueprint as view_auth
+from onlylegs.filters import blueprint as filters
+
+
+logging.getLogger("werkzeug").disabled = True
+logging.basicConfig(
+    filename=os.path.join(APPLICATION_ROOT, "only.log"),
+    level=logging.INFO,
+    datefmt="%Y-%m-%d %H:%M:%S",
+    format="%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s",
+    encoding="utf-8",
 )
-from onlylegs import api
-from onlylegs import auth as view_auth
-from onlylegs import filters
+
 
 app = Flask(__name__, instance_path=INSTANCE_DIR)
 app.config.from_pyfile("config.py")
 
-# DATABASE
 db.init_app(app)
 migrate.init_app(app, db, directory=MIGRATIONS_DIR)
 
-# If database file doesn't exist, create it
+# App Sanity Checks
+startup.check_dirs()
+startup.check_env()
+startup.check_conf()
+
 if not os.path.exists(os.path.join(INSTANCE_DIR, "gallery.sqlite3")):
-    print("Creating database")
-    with app.app_context():
-        db.create_all()
+    startup.make_admin_user(app)
+    migrate_init(directory=MIGRATIONS_DIR)
 
-        register_user = Users(
-            username=app.config["ADMIN_CONF"]["username"],
-            email=app.config["ADMIN_CONF"]["email"],
-            password=generate_password_hash("changeme!", method="sha256"),
-        )
-        db.session.add(register_user)
-        db.session.commit()
-
-        print(
-            """
-####################################################
-# DEFAULT ADMIN USER GENERATED WITH GIVEN USERNAME #
-# THE DEFAULT PASSWORD "changeme!" HAS BEEN USED,  #
-# PLEASE UPDATE IT IN THE SETTINGS!                #
-####################################################
-        """
-        )
-
-# Check if migrations directory exists, if not create it
-with app.app_context():
-    if not os.path.exists(MIGRATIONS_DIR):
-        print("Creating migrations directory")
-        migrate_init(directory=MIGRATIONS_DIR)
 
 # LOGIN MANAGER
 # can also set session_protection to "strong"
@@ -90,40 +78,41 @@ def error_page(err):
     """
     if not isinstance(err, HTTPException):
         abort(500)
-    return (
-        render_template("error.html", error=err.code, msg=err.description),
-        err.code,
-    )
+
+    if request.method == "GET":
+        return (
+            render_template("error.html", error=err.code, msg=err.description),
+            err.code,
+        )
+    else:
+        return str(err.code) + ": " + err.description, err.code
 
 
 # ASSETS
 assets.init_app(app)
 
-scripts = Bundle(
-    "js/*.js", output="gen/js.js", depends="js/*.js"
-)  # filter jsmin is broken :c
-styles = Bundle(
+page_scripts = Bundle(
+    "js/*.js", filters="jsmin", output="gen/main.js", depends="js/*.js"
+)
+page_styling = Bundle(
     "sass/style.sass",
     filters="libsass, cssmin",
     output="gen/styles.css",
     depends="sass/**/*.sass",
 )
 
-assets.register("scripts", scripts)
-assets.register("styles", styles)
+assets.register("scripts", page_scripts)
+assets.register("styles", page_styling)
 
 # BLUEPRINTS
-app.register_blueprint(view_auth.blueprint)
-app.register_blueprint(view_index.blueprint)
-app.register_blueprint(view_image.blueprint)
-app.register_blueprint(view_group.blueprint)
-app.register_blueprint(view_profile.blueprint)
-app.register_blueprint(view_settings.blueprint)
-
-app.register_blueprint(api.blueprint)
-
-# FILTERS
-app.register_blueprint(filters.blueprint)
+app.register_blueprint(view_auth)
+app.register_blueprint(view_index)
+app.register_blueprint(view_image)
+app.register_blueprint(view_group)
+app.register_blueprint(view_profile)
+app.register_blueprint(view_settings)
+app.register_blueprint(api)
+app.register_blueprint(filters)
 
 # CACHE AND COMPRESS
 cache.init_app(app)
diff --git a/onlylegs/config.py b/onlylegs/config.py
index 06cd271..9bf47a1 100644
--- a/onlylegs/config.py
+++ b/onlylegs/config.py
@@ -9,16 +9,17 @@ from yaml import safe_load
 
 
 # Set dirs
-user_dir = platformdirs.user_config_dir("onlylegs")
-instance_dir = os.path.join(user_dir, "instance")
+APPLICATION_ROOT = platformdirs.user_config_dir("onlylegs")
 
 # Load environment variables
 # print("Loading environment variables...")
-load_dotenv(os.path.join(user_dir, ".env"))
+load_dotenv(os.path.join(APPLICATION_ROOT, ".env"))
 
 # Load config from user dir
 # print("Loading config...")
-with open(os.path.join(user_dir, "conf.yml"), encoding="utf-8", mode="r") as file:
+with open(
+    os.path.join(APPLICATION_ROOT, "conf.yml"), encoding="utf-8", mode="r"
+) as file:
     conf = safe_load(file)
 
 
@@ -34,13 +35,13 @@ UPLOAD_CONF = conf["upload"]
 WEBSITE_CONF = conf["website"]
 
 # Directories
-UPLOAD_FOLDER = os.path.join(user_dir, "media", "uploads")
-CACHE_FOLDER = os.path.join(user_dir, "media", "cache")
-PFP_FOLDER = os.path.join(user_dir, "media", "pfp")
-MEDIA_FOLDER = os.path.join(user_dir, "media")
+UPLOAD_FOLDER = os.path.join(APPLICATION_ROOT, "media", "uploads")
+CACHE_FOLDER = os.path.join(APPLICATION_ROOT, "media", "cache")
+PFP_FOLDER = os.path.join(APPLICATION_ROOT, "media", "pfp")
+MEDIA_FOLDER = os.path.join(APPLICATION_ROOT, "media")
 
 # Database
-INSTANCE_DIR = instance_dir
+INSTANCE_DIR = os.path.join(APPLICATION_ROOT, "instance")
 MIGRATIONS_DIR = os.path.join(INSTANCE_DIR, "migrations")
 
 # App
diff --git a/onlylegs/static/js/imagePage.js b/onlylegs/static/js/imagePage.js
index a1eb24c..40209a7 100644
--- a/onlylegs/static/js/imagePage.js
+++ b/onlylegs/static/js/imagePage.js
@@ -14,47 +14,6 @@ function imageFullscreen() {
 }
 
 function imageShowOptionsPopup(obj) {
-    // let title = 'Options';
-    // let subtitle = null;
-    //
-    // let body = document.createElement('div');
-    //     body.style.cssText = 'display: flex; flex-direction: column; gap: 0.5rem;';
-    //
-    // let copyBtn = document.createElement('button');
-    //     copyBtn.classList.add('btn-block');
-    //     copyBtn.innerHTML = 'Copy URL';
-    //     copyBtn.onclick = () => {
-    //         copyToClipboard(window.location.href)
-    //     }
-    //
-    // let downloadBtn = document.createElement('a');
-    //     downloadBtn.classList.add('btn-block');
-    //     downloadBtn.innerHTML = 'Download';
-    //     downloadBtn.href = '/api/media/uploads/' + image_data["filename"];
-    //     downloadBtn.download = '';
-    //
-    // body.appendChild(copyBtn);
-    // body.appendChild(downloadBtn);
-    //
-    // if (image_data["owner"]) {
-    //     let editBtn = document.createElement('button');
-    //         editBtn.classList.add('btn-block');
-    //         editBtn.classList.add('critical');
-    //         editBtn.innerHTML = 'Edit';
-    //         editBtn.onclick = imageEditPopup;
-    //
-    //     let deleteBtn = document.createElement('button');
-    //         deleteBtn.classList.add('btn-block');
-    //         deleteBtn.classList.add('critical');
-    //         deleteBtn.innerHTML = 'Delete';
-    //         deleteBtn.onclick = imageDeletePopup;
-    //
-    //     body.appendChild(editBtn);
-    //     body.appendChild(deleteBtn);
-    // }
-    //
-    // popupShow(title, subtitle, body, [popupCancelButton]);
-
     showContextMenu(obj, [
         {
             'value': 'Edit',
diff --git a/onlylegs/static/js/uploadTab.js b/onlylegs/static/js/uploadTab.js
index a27e57f..2526a8a 100644
--- a/onlylegs/static/js/uploadTab.js
+++ b/onlylegs/static/js/uploadTab.js
@@ -141,41 +141,14 @@ function clearUpload() {
 }
 
 
-// function createJob(file) {
-//     jobContainer = document.createElement("div");
-//     jobContainer.classList.add("job");
-
-//     jobStatus = document.createElement("span");
-//     jobStatus.classList.add("job__status");
-//     jobStatus.innerHTML = "Uploading...";
-
-//     jobProgress = document.createElement("span");
-//     jobProgress.classList.add("progress");
-
-//     jobImg = document.createElement("img");
-//     jobImg.src = URL.createObjectURL(file);
-
-//     jobImgFilter = document.createElement("span");
-//     jobImgFilter.classList.add("img-filter");
-
-//     jobContainer.appendChild(jobStatus);
-//     jobContainer.appendChild(jobProgress);
-//     jobContainer.appendChild(jobImg);
-//     jobContainer.appendChild(jobImgFilter);
-    
-//     return jobContainer;
-// }
-
-
 document.addEventListener('DOMContentLoaded', () => {
     // Function to upload images
     const uploadTab = document.querySelector(".upload-panel");
 
-    if (!uploadTab) { return; } // If upload tab doesn't exist, don't run this code :3
+    if (!uploadTab) { return }
 
     const uploadTabDrag = uploadTab.querySelector("#dragIndicator");
     const uploadForm = uploadTab.querySelector('#uploadForm');
-    // let jobList = document.querySelector(".upload-jobs");
     
     const fileDrop = uploadForm.querySelector('.fileDrop-block');
     const fileDropTitle = fileDrop.querySelector('.status');
@@ -228,54 +201,6 @@ document.addEventListener('DOMContentLoaded', () => {
         formData.append("description", fileDescription.value);
         formData.append("tags", fileTags.value);
 
-        // jobItem = createJob(fileUpload.files[0]);
-        // jobStatus = jobItem.querySelector(".job__status");
-
-        // Upload the information
-        // $.ajax({
-        //     url: '/api/upload',
-        //     type: 'post',
-        //     data: formData,
-        //     contentType: false,
-        //     processData: false,
-        //     beforeSend: function () {
-        //         // Add job to list
-        //         jobList.appendChild(jobItem);
-        //     },
-        //     success: function (response) {
-        //         jobItem.classList.add("success");
-        //         jobStatus.innerHTML = "Uploaded successfully";
-        //         if (!document.querySelector(".upload-panel").classList.contains("open")) {
-        //             addNotification("Image uploaded successfully", 1);
-        //         }
-        //     },
-        //     error: function (response) {
-        //         jobItem.classList.add("critical");
-        //         switch (response.status) {
-        //             case 500:
-        //                 jobStatus.innerHTML = "Server exploded, F's in chat";
-        //                 break;
-        //             case 400:
-        //             case 404:
-        //                 jobStatus.innerHTML = "Error uploading. Blame yourself";
-        //                 break;
-        //             case 403:
-        //                 jobStatus.innerHTML = "None but devils play past here...";
-        //                 break;
-        //             case 413:
-        //                 jobStatus.innerHTML = "File too large!!!!!!";
-        //                 break;
-        //             default:
-        //                 jobStatus.innerHTML = "Error uploading file, blame someone";
-        //                 break;
-        //         }
-        //         if (!document.querySelector(".upload-panel").classList.contains("open")) {
-        //             addNotification("Error uploading file", 2);
-        //         }
-        //     },
-        // });
-
-
         fetch('/api/media/upload', {
             method: 'POST',
             body: formData
diff --git a/onlylegs/static/sass/components/notification.sass b/onlylegs/static/sass/components/notification.sass
index feea589..2a182dc 100644
--- a/onlylegs/static/sass/components/notification.sass
+++ b/onlylegs/static/sass/components/notification.sass
@@ -1,23 +1,15 @@
 @keyframes notificationTimeout
     0%
-        left: -100%
-        height: 3px
-    90%
-        left: 0%
-        height: 3px
-    95%
-        left: 0%
-        height: 0
+        width: 0
     100%
-        left: 0%
-        height: 0
+        width: 100%
     
 @mixin notification($color)
     color: RGB($color)
 
     &::after
         background-color: RGB($color)
-    
+
 .notifications
     margin: 0
     padding: 0
@@ -60,17 +52,17 @@
     &::after
         content: ""
 
-        width: 100%
+        width: 0
         height: 3px
 
         position: absolute
-        bottom: 0px
-        left: 0px
+        bottom: 0
+        left: 0
 
         background-color: RGB($fg-white)
 
         z-index: +2
-        animation: notificationTimeout 5.1s linear
+        animation: notificationTimeout 5.1s ease-out forwards
 
     &.success
         @include notification($success)
@@ -89,7 +81,7 @@
         margin: 0
         max-height: 0
         opacity: 0
-        transform: translateX(100%)
+        transform: translateY(1rem)
         transition: all 0.4s ease-in-out, max-height 0.2s ease-in-out
 
 .sniffle__notification-icon
@@ -133,10 +125,6 @@
 
     .sniffle__notification
         width: 100%
-
-        &.hide
-            opacity: 0
-            transform: translateY(1rem)
     
     .sniffle__notification-time
         width: 100%
diff --git a/onlylegs/static/sass/style.sass b/onlylegs/static/sass/style.sass
index c363234..cbc602d 100644
--- a/onlylegs/static/sass/style.sass
+++ b/onlylegs/static/sass/style.sass
@@ -60,16 +60,18 @@ body
         padding: 0 0 3.5rem 0
 
 main
+    margin: 0 0.5rem 0.5rem 0
     display: flex
     flex-direction: column
     position: relative
     background: RGBA($white, 1)
     color: RGB($fg-black)
-    border-top-left-radius: $rad
+    border-radius: $rad
     overflow: hidden
 @media (max-width: $breakpoint)
     main
-        border-top-left-radius: 0
+        margin: 0
+        border-radius: 0
 
 .error-page
     min-height: 100%
diff --git a/onlylegs/utils/startup.py b/onlylegs/utils/startup.py
new file mode 100644
index 0000000..a42730f
--- /dev/null
+++ b/onlylegs/utils/startup.py
@@ -0,0 +1,156 @@
+"""
+OnlyLegs - Setup
+Runs when the app detects that there is no user directory
+"""
+import os
+import re
+import platformdirs
+import yaml
+from werkzeug.security import generate_password_hash
+from onlylegs.extensions import db
+from onlylegs.models import Users
+
+
+APPLICATION_ROOT = platformdirs.user_config_dir("onlyLegs")
+REQUIRED_DIRS = {
+    "root": APPLICATION_ROOT,
+    "instance": os.path.join(APPLICATION_ROOT, "instance"),
+    "media": os.path.join(APPLICATION_ROOT, "media"),
+    "uploads": os.path.join(APPLICATION_ROOT, "media", "uploads"),
+    "cache": os.path.join(APPLICATION_ROOT, "media", "cache"),
+    "pfp": os.path.join(APPLICATION_ROOT, "media", "pfp"),
+}
+
+EMAIL_REGEX = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
+USERNAME_REGEX = re.compile(r"\b[A-Za-z0-9._%+-]+\b")
+
+
+def check_dirs():
+    """
+    Create the user directory
+    """
+
+    for directory in REQUIRED_DIRS.values():
+        if os.path.exists(directory):
+            print("User directory already exists at:", directory)
+            return
+        os.makedirs(directory)
+        print("Created directory at:", directory)
+
+
+def check_env():
+    """
+    Create the .env file with default values
+    """
+    if os.path.exists(os.path.join(APPLICATION_ROOT, ".env")):
+        print("Environment file already exists at:", APPLICATION_ROOT)
+        return
+
+    env_conf = {
+        "FLASK_SECRET": os.urandom(32).hex(),
+    }
+
+    with open(
+        os.path.join(APPLICATION_ROOT, ".env"), encoding="utf-8", mode="w+"
+    ) as file:
+        for key, value in env_conf.items():
+            file.write(key + "=" + value + "\n")
+
+    print(
+        "####################################################"
+        "# A NEW KEY WAS GENERATED FOR YOU! PLEASE NOTE     #"
+        "# DOWN THE FLASK_SECRET KEY LOCATED IN YOUR        #"
+        "# ~/.config/onlylegs/.env FOLDER! LOOSING THIS KEY #"
+        "# WILL RESULT IN YOU BEING UNABLE TO LOG IN!       #"
+        "####################################################",
+        sep="\n",
+    )
+
+
+def check_conf():
+    """
+    Create the YAML config file with default values
+    """
+    if os.path.exists(os.path.join(APPLICATION_ROOT, "conf.yml")):
+        print("Config file already exists at:", APPLICATION_ROOT)
+        return
+
+    can_continue = False
+    username = "admin"
+    name = "Admin"
+    email = "admin@example.com"
+
+    print("No config file found, please enter the following information:")
+    while can_continue:
+        username = input("Admin username: ")
+        name = input("Admin name: ")
+        email = input("Admin email: ")
+
+        if not username or not USERNAME_REGEX.match(username):
+            print("Username is invalid!")
+        if not name:
+            print("Name is invalid!")
+        if not email or not EMAIL_REGEX.match(email):
+            print("Email is invalid!")
+
+        # Check if user is happy with the values
+        is_correct = input("Is this correct? (Y/n): ").lower()
+        if is_correct == "y" or is_correct == "":
+            can_continue = True
+
+    yaml_conf = {
+        "admin": {
+            "name": name,
+            "username": username,
+            "email": email,
+        },
+        "upload": {
+            "allowed-extensions": {
+                "jpg": "jpeg",
+                "jpeg": "jpeg",
+                "png": "png",
+                "webp": "webp",
+            },
+            "max-size": 69,
+            "max-load": 50,
+            "rename": "GWA_{{username}}_{{time}}",
+        },
+        "website": {
+            "name": "OnlyLegs",
+            "motto": "A gallery built for fast and simple image management!",
+            "language": "en",
+        },
+    }
+
+    with open(
+        os.path.join(APPLICATION_ROOT, "conf.yml"), encoding="utf-8", mode="w+"
+    ) as file:
+        yaml.dump(yaml_conf, file, default_flow_style=False)
+
+    print(
+        "####################################################"
+        "# A NEW CONFIG HAS BEEN GENERATED AT:              #"
+        "# ~/.config/onlylegs/conf.yml                      #"
+        "####################################################",
+        sep="\n",
+    )
+
+
+def make_admin_user(app):
+    username = app.config["ADMIN_CONF"]["username"]
+    email = app.config["ADMIN_CONF"]["email"]
+    password = generate_password_hash("changeme!", method="scrypt")
+
+    with app.app_context():
+        db.create_all()
+        db.session.add(Users(username=username, email=email, password=password))
+        db.session.commit()
+
+        print(
+            "####################################################"
+            "# DEFAULT ADMIN USER GENERATED WITH GIVEN USERNAME #"
+            '# THE DEFAULT PASSWORD "changeme!" HAS BEEN USED,  #'
+            "# PLEASE RESET IT IN THE SETTINGS!                 #"
+            "####################################################",
+            sep="\n",
+        )
diff --git a/poetry.lock b/poetry.lock
index 8c1c926..3246c75 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
 
 [[package]]
 name = "alembic"
@@ -727,6 +727,22 @@ files = [
     {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
 ]
 
+[[package]]
+name = "material-color-utilities-python"
+version = "0.1.5"
+description = "Python port of material-color-utilities used for Material You colors"
+category = "main"
+optional = false
+python-versions = "*"
+files = [
+    {file = "material-color-utilities-python-0.1.5.tar.gz", hash = "sha256:3c6f02e7ce70595885447bdef37cf76fd3628c7c95fa2198d8174c269c951fae"},
+    {file = "material_color_utilities_python-0.1.5-py2.py3-none-any.whl", hash = "sha256:48abd8695a1355ab3ad43fe314ca8664c66282a86fbf94a717571273bf422bdf"},
+]
+
+[package.dependencies]
+Pillow = ">=9.2.0,<10.0.0"
+regex = "*"
+
 [[package]]
 name = "mccabe"
 version = "0.7.0"
@@ -966,6 +982,104 @@ files = [
     {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
 ]
 
+[[package]]
+name = "regex"
+version = "2023.8.8"
+description = "Alternative regular expression module, to replace re."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "regex-2023.8.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88900f521c645f784260a8d346e12a1590f79e96403971241e64c3a265c8ecdb"},
+    {file = "regex-2023.8.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3611576aff55918af2697410ff0293d6071b7e00f4b09e005d614686ac4cd57c"},
+    {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a0ccc8f2698f120e9e5742f4b38dc944c38744d4bdfc427616f3a163dd9de5"},
+    {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c662a4cbdd6280ee56f841f14620787215a171c4e2d1744c9528bed8f5816c96"},
+    {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf0633e4a1b667bfe0bb10b5e53fe0d5f34a6243ea2530eb342491f1adf4f739"},
+    {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:551ad543fa19e94943c5b2cebc54c73353ffff08228ee5f3376bd27b3d5b9800"},
+    {file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54de2619f5ea58474f2ac211ceea6b615af2d7e4306220d4f3fe690c91988a61"},
+    {file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5ec4b3f0aebbbe2fc0134ee30a791af522a92ad9f164858805a77442d7d18570"},
+    {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ae646c35cb9f820491760ac62c25b6d6b496757fda2d51be429e0e7b67ae0ab"},
+    {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ca339088839582d01654e6f83a637a4b8194d0960477b9769d2ff2cfa0fa36d2"},
+    {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:d9b6627408021452dcd0d2cdf8da0534e19d93d070bfa8b6b4176f99711e7f90"},
+    {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:bd3366aceedf274f765a3a4bc95d6cd97b130d1dda524d8f25225d14123c01db"},
+    {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7aed90a72fc3654fba9bc4b7f851571dcc368120432ad68b226bd593f3f6c0b7"},
+    {file = "regex-2023.8.8-cp310-cp310-win32.whl", hash = "sha256:80b80b889cb767cc47f31d2b2f3dec2db8126fbcd0cff31b3925b4dc6609dcdb"},
+    {file = "regex-2023.8.8-cp310-cp310-win_amd64.whl", hash = "sha256:b82edc98d107cbc7357da7a5a695901b47d6eb0420e587256ba3ad24b80b7d0b"},
+    {file = "regex-2023.8.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1e7d84d64c84ad97bf06f3c8cb5e48941f135ace28f450d86af6b6512f1c9a71"},
+    {file = "regex-2023.8.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce0f9fbe7d295f9922c0424a3637b88c6c472b75eafeaff6f910494a1fa719ef"},
+    {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06c57e14ac723b04458df5956cfb7e2d9caa6e9d353c0b4c7d5d54fcb1325c46"},
+    {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7a9aaa5a1267125eef22cef3b63484c3241aaec6f48949b366d26c7250e0357"},
+    {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b7408511fca48a82a119d78a77c2f5eb1b22fe88b0d2450ed0756d194fe7a9a"},
+    {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14dc6f2d88192a67d708341f3085df6a4f5a0c7b03dec08d763ca2cd86e9f559"},
+    {file = "regex-2023.8.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48c640b99213643d141550326f34f0502fedb1798adb3c9eb79650b1ecb2f177"},
+    {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0085da0f6c6393428bf0d9c08d8b1874d805bb55e17cb1dfa5ddb7cfb11140bf"},
+    {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:964b16dcc10c79a4a2be9f1273fcc2684a9eedb3906439720598029a797b46e6"},
+    {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7ce606c14bb195b0e5108544b540e2c5faed6843367e4ab3deb5c6aa5e681208"},
+    {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:40f029d73b10fac448c73d6eb33d57b34607f40116e9f6e9f0d32e9229b147d7"},
+    {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3b8e6ea6be6d64104d8e9afc34c151926f8182f84e7ac290a93925c0db004bfd"},
+    {file = "regex-2023.8.8-cp311-cp311-win32.whl", hash = "sha256:942f8b1f3b223638b02df7df79140646c03938d488fbfb771824f3d05fc083a8"},
+    {file = "regex-2023.8.8-cp311-cp311-win_amd64.whl", hash = "sha256:51d8ea2a3a1a8fe4f67de21b8b93757005213e8ac3917567872f2865185fa7fb"},
+    {file = "regex-2023.8.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e951d1a8e9963ea51efd7f150450803e3b95db5939f994ad3d5edac2b6f6e2b4"},
+    {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704f63b774218207b8ccc6c47fcef5340741e5d839d11d606f70af93ee78e4d4"},
+    {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22283c769a7b01c8ac355d5be0715bf6929b6267619505e289f792b01304d898"},
+    {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91129ff1bb0619bc1f4ad19485718cc623a2dc433dff95baadbf89405c7f6b57"},
+    {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de35342190deb7b866ad6ba5cbcccb2d22c0487ee0cbb251efef0843d705f0d4"},
+    {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b993b6f524d1e274a5062488a43e3f9f8764ee9745ccd8e8193df743dbe5ee61"},
+    {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3026cbcf11d79095a32d9a13bbc572a458727bd5b1ca332df4a79faecd45281c"},
+    {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:293352710172239bf579c90a9864d0df57340b6fd21272345222fb6371bf82b3"},
+    {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d909b5a3fff619dc7e48b6b1bedc2f30ec43033ba7af32f936c10839e81b9217"},
+    {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:3d370ff652323c5307d9c8e4c62efd1956fb08051b0e9210212bc51168b4ff56"},
+    {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:b076da1ed19dc37788f6a934c60adf97bd02c7eea461b73730513921a85d4235"},
+    {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e9941a4ada58f6218694f382e43fdd256e97615db9da135e77359da257a7168b"},
+    {file = "regex-2023.8.8-cp36-cp36m-win32.whl", hash = "sha256:a8c65c17aed7e15a0c824cdc63a6b104dfc530f6fa8cb6ac51c437af52b481c7"},
+    {file = "regex-2023.8.8-cp36-cp36m-win_amd64.whl", hash = "sha256:aadf28046e77a72f30dcc1ab185639e8de7f4104b8cb5c6dfa5d8ed860e57236"},
+    {file = "regex-2023.8.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:423adfa872b4908843ac3e7a30f957f5d5282944b81ca0a3b8a7ccbbfaa06103"},
+    {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ae594c66f4a7e1ea67232a0846649a7c94c188d6c071ac0210c3e86a5f92109"},
+    {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e51c80c168074faa793685656c38eb7a06cbad7774c8cbc3ea05552d615393d8"},
+    {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:09b7f4c66aa9d1522b06e31a54f15581c37286237208df1345108fcf4e050c18"},
+    {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e73e5243af12d9cd6a9d6a45a43570dbe2e5b1cdfc862f5ae2b031e44dd95a8"},
+    {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941460db8fe3bd613db52f05259c9336f5a47ccae7d7def44cc277184030a116"},
+    {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f0ccf3e01afeb412a1a9993049cb160d0352dba635bbca7762b2dc722aa5742a"},
+    {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2e9216e0d2cdce7dbc9be48cb3eacb962740a09b011a116fd7af8c832ab116ca"},
+    {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5cd9cd7170459b9223c5e592ac036e0704bee765706445c353d96f2890e816c8"},
+    {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4873ef92e03a4309b3ccd8281454801b291b689f6ad45ef8c3658b6fa761d7ac"},
+    {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:239c3c2a339d3b3ddd51c2daef10874410917cd2b998f043c13e2084cb191684"},
+    {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1005c60ed7037be0d9dea1f9c53cc42f836188227366370867222bda4c3c6bd7"},
+    {file = "regex-2023.8.8-cp37-cp37m-win32.whl", hash = "sha256:e6bd1e9b95bc5614a7a9c9c44fde9539cba1c823b43a9f7bc11266446dd568e3"},
+    {file = "regex-2023.8.8-cp37-cp37m-win_amd64.whl", hash = "sha256:9a96edd79661e93327cfeac4edec72a4046e14550a1d22aa0dd2e3ca52aec921"},
+    {file = "regex-2023.8.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2181c20ef18747d5f4a7ea513e09ea03bdd50884a11ce46066bb90fe4213675"},
+    {file = "regex-2023.8.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a2ad5add903eb7cdde2b7c64aaca405f3957ab34f16594d2b78d53b8b1a6a7d6"},
+    {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9233ac249b354c54146e392e8a451e465dd2d967fc773690811d3a8c240ac601"},
+    {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920974009fb37b20d32afcdf0227a2e707eb83fe418713f7a8b7de038b870d0b"},
+    {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2b6c5dfe0929b6c23dde9624483380b170b6e34ed79054ad131b20203a1a63"},
+    {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96979d753b1dc3b2169003e1854dc67bfc86edf93c01e84757927f810b8c3c93"},
+    {file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ae54a338191e1356253e7883d9d19f8679b6143703086245fb14d1f20196be9"},
+    {file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2162ae2eb8b079622176a81b65d486ba50b888271302190870b8cc488587d280"},
+    {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c884d1a59e69e03b93cf0dfee8794c63d7de0ee8f7ffb76e5f75be8131b6400a"},
+    {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf9273e96f3ee2ac89ffcb17627a78f78e7516b08f94dc435844ae72576a276e"},
+    {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:83215147121e15d5f3a45d99abeed9cf1fe16869d5c233b08c56cdf75f43a504"},
+    {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f7454aa427b8ab9101f3787eb178057c5250478e39b99540cfc2b889c7d0586"},
+    {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0640913d2c1044d97e30d7c41728195fc37e54d190c5385eacb52115127b882"},
+    {file = "regex-2023.8.8-cp38-cp38-win32.whl", hash = "sha256:0c59122ceccb905a941fb23b087b8eafc5290bf983ebcb14d2301febcbe199c7"},
+    {file = "regex-2023.8.8-cp38-cp38-win_amd64.whl", hash = "sha256:c12f6f67495ea05c3d542d119d270007090bad5b843f642d418eb601ec0fa7be"},
+    {file = "regex-2023.8.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:82cd0a69cd28f6cc3789cc6adeb1027f79526b1ab50b1f6062bbc3a0ccb2dbc3"},
+    {file = "regex-2023.8.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bb34d1605f96a245fc39790a117ac1bac8de84ab7691637b26ab2c5efb8f228c"},
+    {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:987b9ac04d0b38ef4f89fbc035e84a7efad9cdd5f1e29024f9289182c8d99e09"},
+    {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dd6082f4e2aec9b6a0927202c85bc1b09dcab113f97265127c1dc20e2e32495"},
+    {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7eb95fe8222932c10d4436e7a6f7c99991e3fdd9f36c949eff16a69246dee2dc"},
+    {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7098c524ba9f20717a56a8d551d2ed491ea89cbf37e540759ed3b776a4f8d6eb"},
+    {file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b694430b3f00eb02c594ff5a16db30e054c1b9589a043fe9174584c6efa8033"},
+    {file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2aeab3895d778155054abea5238d0eb9a72e9242bd4b43f42fd911ef9a13470"},
+    {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:988631b9d78b546e284478c2ec15c8a85960e262e247b35ca5eaf7ee22f6050a"},
+    {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:67ecd894e56a0c6108ec5ab1d8fa8418ec0cff45844a855966b875d1039a2e34"},
+    {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:14898830f0a0eb67cae2bbbc787c1a7d6e34ecc06fbd39d3af5fe29a4468e2c9"},
+    {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:f2200e00b62568cfd920127782c61bc1c546062a879cdc741cfcc6976668dfcf"},
+    {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9691a549c19c22d26a4f3b948071e93517bdf86e41b81d8c6ac8a964bb71e5a6"},
+    {file = "regex-2023.8.8-cp39-cp39-win32.whl", hash = "sha256:6ab2ed84bf0137927846b37e882745a827458689eb969028af8032b1b3dac78e"},
+    {file = "regex-2023.8.8-cp39-cp39-win_amd64.whl", hash = "sha256:5543c055d8ec7801901e1193a51570643d6a6ab8751b1f7dd9af71af467538bb"},
+    {file = "regex-2023.8.8.tar.gz", hash = "sha256:fcbdc5f2b0f1cd0f6a56cdb46fe41d2cce1e644e3b68832f3eeebc5fb0f7712e"},
+]
+
 [[package]]
 name = "setuptools"
 version = "68.0.0"
@@ -1232,4 +1346,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.8"
-content-hash = "96ec0d1f7b512afb05455262fa2de8c4f862bf68fdae513f8552dc30c6e5ab49"
+content-hash = "11735dbbdd45dcc5085a04dbbd4e786dd8aeb4ba743f0c20294861e7b4c35dd0"
diff --git a/pyproject.toml b/pyproject.toml
index 3752e8a..ae79283 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -28,6 +28,7 @@ cssmin = "^0.2.0"
 pylint = "^2.16.3"
 black = "^23.3.0"
 cachetools = "^5.3.0"
+material-color-utilities-python = "^0.1.5"
 
 [build-system]
 requires = ["poetry-core"]
diff --git a/setup/args.py b/setup/args.py
deleted file mode 100644
index a443338..0000000
--- a/setup/args.py
+++ /dev/null
@@ -1,33 +0,0 @@
-"""
-Startup arguments for the OnlyLegs gallery
-
-    -p, --port: Port to run on (default: 5000)
-    -a, --address: Address to run on (default: 127.0.0.0)
-    -w, --workers: Number of workers to run (default: 4)
-
-    -d, --debug: Run as Flask app in debug mode (default: False)
-    -S, --scream: Show verbose output (default: False)
-    -h, --help: Show a help message
-"""
-
-import argparse
-
-
-parser = argparse.ArgumentParser(description="Run the OnlyLegs gallery")
-parser.add_argument("-p", "--port", type=int, default=5000, help="Port to run on")
-parser.add_argument(
-    "-a", "--address", type=str, default="127.0.0.0", help="Address to run on"
-)
-parser.add_argument(
-    "-w", "--workers", type=int, default=4, help="Number of workers to run"
-)
-parser.add_argument(
-    "-d", "--debug", action="store_true", help="Run as Flask app in debug mode"
-)
-args = parser.parse_args()
-
-
-PORT = args.port
-ADDRESS = args.address
-WORKERS = args.workers
-DEBUG = args.debug
diff --git a/setup/configuration.py b/setup/configuration.py
deleted file mode 100644
index 2727ce0..0000000
--- a/setup/configuration.py
+++ /dev/null
@@ -1,156 +0,0 @@
-"""
-OnlyLegs - Setup
-Runs when the app detects that there is no user directory
-"""
-import os
-import logging
-import re
-import platformdirs
-import yaml
-
-
-USER_DIR = platformdirs.user_config_dir("onlylegs")
-
-
-class Configuration:
-    """
-    Setup the application on first run
-    """
-
-    def __init__(self):
-        """
-        Main setup function
-        """
-        print("Running startup checks...")
-
-        # Check if the user directory exists
-        if not os.path.exists(USER_DIR):
-            self.make_dir()
-
-        # Check if the .env file exists
-        if not os.path.exists(os.path.join(USER_DIR, ".env")):
-            self.make_env()
-
-        # Check if the conf.yml file exists
-        if not os.path.exists(os.path.join(USER_DIR, "conf.yml")):
-            self.make_yaml()
-
-        # Load the config files
-        self.logging_config()
-
-    @staticmethod
-    def make_dir():
-        """
-        Create the user directory
-        """
-        os.makedirs(USER_DIR)
-        os.makedirs(os.path.join(USER_DIR, "instance"))
-        os.makedirs(os.path.join(USER_DIR, "media"))
-        os.makedirs(os.path.join(USER_DIR, "media", "uploads"))
-        os.makedirs(os.path.join(USER_DIR, "media", "cache"))
-        os.makedirs(os.path.join(USER_DIR, "media", "pfp"))
-
-        print("Created user directory at:", USER_DIR)
-
-    @staticmethod
-    def make_env():
-        """
-        Create the .env file with default values
-        """
-        env_conf = {
-            "FLASK_SECRET": os.urandom(32).hex(),
-        }
-
-        with open(os.path.join(USER_DIR, ".env"), encoding="utf-8", mode="w+") as file:
-            for key, value in env_conf.items():
-                file.write(f"{key}={value}\n")
-
-        print(
-            """
-####################################################
-# A NEW KEY WAS GENERATED FOR YOU! PLEASE NOTE     #
-# DOWN THE FLASK_SECRET KEY LOCATED IN YOUR        #
-# .config/onlylegs/.env FOLDER! LOOSING THIS KEY   #
-# WILL RESULT IN YOU BEING UNABLE TO LOG IN!       #
-####################################################
-              """
-        )
-
-    @staticmethod
-    def make_yaml():
-        """
-        Create the YAML config file with default values
-        """
-        is_correct = False
-        email_regex = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
-        username_regex = re.compile(r"\b[A-Za-z0-9._%+-]+\b")
-
-        print("\nNo config file found, please enter the following information:")
-        while not is_correct:
-            username = input("Admin username: ")
-            name = input("Admin name: ")
-            email = input("Admin email: ")
-
-            # Check if the values are valid
-            if not username or not username_regex.match(username):
-                print("Username is invalid!")
-                continue
-
-            if not name:
-                print("Name is invalid!")
-                continue
-
-            if not email or not email_regex.match(email):
-                print("Email is invalid!")
-                continue
-
-            # Check if user is happy with the values
-            if input("Is this correct? (y/n): ").lower() == "y":
-                is_correct = True
-
-        yaml_conf = {
-            "admin": {
-                "name": name,
-                "username": username,
-                "email": email,
-            },
-            "upload": {
-                "allowed-extensions": {
-                    "jpg": "jpeg",
-                    "jpeg": "jpeg",
-                    "png": "png",
-                    "webp": "webp",
-                },
-                "max-size": 69,
-                "max-load": 50,
-                "rename": "GWA_{{username}}_{{time}}",
-            },
-            "website": {
-                "name": "OnlyLegs",
-                "motto": "A gallery built for fast and simple image management!",
-                "language": "en",
-            },
-        }
-
-        with open(
-            os.path.join(USER_DIR, "conf.yml"), encoding="utf-8", mode="w+"
-        ) as file:
-            yaml.dump(yaml_conf, file, default_flow_style=False)
-
-        print(
-            "Generated config file, you can change these values in the settings of the app"
-        )
-
-    @staticmethod
-    def logging_config():
-        """
-        Set the logging config
-        """
-        logging.getLogger("werkzeug").disabled = True
-        logging.basicConfig(
-            filename=os.path.join(USER_DIR, "only.log"),
-            level=logging.INFO,
-            datefmt="%Y-%m-%d %H:%M:%S",
-            format="%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s",
-            encoding="utf-8",
-        )
diff --git a/setup/runner.py b/setup/runner.py
deleted file mode 100644
index 94cd827..0000000
--- a/setup/runner.py
+++ /dev/null
@@ -1,35 +0,0 @@
-"""
-Gunicorn configuration file
-"""
-from gunicorn.app.base import Application
-from gunicorn import util
-
-
-class OnlyLegs(Application):
-    """
-    Gunicorn application
-    """
-
-    # TODO: Make this not shit, thanks
-    def __init__(self, options={}):  # skipcq: PYL-W0231 # pylint: disable=W0231
-        self.usage = None
-        self.callable = None
-        self.options = options
-        self.do_load_config()
-
-    def init(self, *args):
-        """
-        Initialize the application
-        """
-        cfg = {}
-        for setting, value in self.options.items():
-            if setting.lower() in self.cfg.settings and value is not None:
-                cfg[setting.lower()] = value
-        return cfg
-
-    @staticmethod
-    def prog():  # skipcq: PYL-E0202 # pylint: disable=E0202, C0116
-        return "OnlyLegs"
-
-    def load(self):
-        return util.import_app("onlylegs.app:app")