mirror of
https://github.com/Derpy-Leggies/OnlyLegs.git
synced 2025-06-28 19:16:16 +00:00
Yeet old runner script as it was useless
Clean up code and styling
This commit is contained in:
parent
8a4fe891ef
commit
9821db72c6
12 changed files with 348 additions and 437 deletions
115
onlylegs/app.py
115
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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%
|
||||
|
|
|
@ -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%
|
||||
|
|
156
onlylegs/utils/startup.py
Normal file
156
onlylegs/utils/startup.py
Normal file
|
@ -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",
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue