Move user data to ~/.config/onlylegs location

Update location of default themes folder
This commit is contained in:
Michał Gdula 2023-03-02 13:19:10 +00:00
parent 828167f762
commit 512f6f623e
31 changed files with 158 additions and 110 deletions

View file

@ -5,37 +5,96 @@ print("""
| |_| | | | | | |_| | |__| __/ (_| \\__ \\ | |_| | | | | | |_| | |__| __/ (_| \\__ \\
\\___/|_| |_|_|\\__, |_____\\___|\\__, |___/ \\___/|_| |_|_|\\__, |_____\\___|\\__, |___/
|___/ |___/ |___/ |___/
Created by Fluffy Bean - Version 23.03.01 Created by Fluffy Bean - Version 23.03.02
""") """)
from flask import Flask, render_template from flask import Flask, render_template
from flask_compress import Compress from flask_compress import Compress
from flask.helpers import get_root_path
from dotenv import load_dotenv from dotenv import load_dotenv
import platformdirs
import yaml import yaml
import os import os
print(f"Running at {get_root_path(__name__)}\n") # I could use something other than the config dir, but this works well enough
user_dir = platformdirs.user_config_dir('onlylegs')
if not os.path.exists(user_dir):
os.makedirs(user_dir)
print("Created user directory")
# Location of instance folder, where sqlite db is stored
instance_path = os.path.join(user_dir, 'instance')
if not os.path.exists(instance_path):
os.makedirs(instance_path)
print("Created instance directory")
# Get environment variables
if os.path.exists(os.path.join(user_dir, '.env')):
load_dotenv(os.path.join(user_dir, '.env'))
print("Loaded environment variables")
else:
conf = {
'FLASK_SECRETE': 'dev',
}
# Create .env file with default values
with open(os.path.join(user_dir, '.env'), 'w') as f:
for key, value in conf.items():
f.write(f"{key}={value}\n")
print("Created default environment variables at:",
os.path.join(user_dir, '.env'),
"\nCHANGE THESE VALUES USING TEHE GALLERY!")
# Get config file
if os.path.exists(os.path.join(user_dir, 'conf.yml')):
with open(os.path.join(user_dir, 'conf.yml'), 'r') as f:
conf = yaml.load(f, Loader=yaml.FullLoader)
print("Loaded gallery config")
else:
conf = {
'admin': {
'name': 'Real Person',
'username': 'User',
'email': 'real-email@some.place'
},
'upload': {
'allowed-extensions': {
'jpg': 'jpeg',
'jpeg': 'jpeg',
'png': 'png',
'webp': 'webp'
},
'max-size': 69,
'rename': 'GWA_\{\{username\}\}_\{\{time\}\}'
},
'website': {
'name': 'OnlyLegs',
'motto': 'Gwa Gwa',
'language': 'english'
}
}
# Create yaml config file with default values
with open(os.path.join(user_dir, 'conf.yml'), 'w') as f:
yaml.dump(conf, f, default_flow_style=False)
print("Created default gallery config at:",
os.path.join(user_dir, 'conf.yml'),
"\nCHANGE THESE VALUES USING TEHE GALLERY!")
# Load logger
from .logger import logger
logger.innit_logger()
def create_app(test_config=None): def create_app(test_config=None):
# create and configure the app # create and configure the app
app = Flask(__name__) app = Flask(__name__, instance_path=instance_path)
compress = Compress() compress = Compress()
# Get environment variables
load_dotenv(os.path.join(app.root_path, 'user', '.env'))
print("Loaded environment variables")
# Get config file
with open(os.path.join(app.root_path, 'user', 'conf.yml'), 'r') as f:
conf = yaml.load(f, Loader=yaml.FullLoader)
print("Loaded gallery config")
# App configuration # App configuration
app.config.from_mapping( app.config.from_mapping(
SECRET_KEY=os.environ.get('FLASK_SECRET'), SECRET_KEY=os.environ.get('FLASK_SECRET'),
DATABASE=os.path.join(app.instance_path, 'gallery.sqlite'), DATABASE=os.path.join(app.instance_path, 'gallery.sqlite'),
UPLOAD_FOLDER=os.path.join(app.root_path, 'user', 'uploads'), UPLOAD_FOLDER=os.path.join(user_dir, 'uploads'),
MAX_CONTENT_LENGTH=1024 * 1024 * conf['upload']['max-size'], MAX_CONTENT_LENGTH=1024 * 1024 * conf['upload']['max-size'],
ALLOWED_EXTENSIONS=conf['upload']['allowed-extensions'], ALLOWED_EXTENSIONS=conf['upload']['allowed-extensions'],
WEBSITE=conf['website'], WEBSITE=conf['website'],
@ -62,10 +121,6 @@ def create_app(test_config=None):
from . import sassy from . import sassy
sassy.compile('default', app.root_path) sassy.compile('default', app.root_path)
# Load logger
from .logger import logger
logger.innit_logger(app)
@app.errorhandler(405) @app.errorhandler(405)
def method_not_allowed(e): def method_not_allowed(e):
error = '405' error = '405'

View file

@ -4,7 +4,7 @@ from werkzeug.utils import secure_filename
from gallery.auth import login_required from gallery.auth import login_required
from gallery.db import get_db from gallery.db import get_db
from PIL import Image, ImageOps from PIL import Image, ImageOps, ImageFilter
from . import metadata as mt from . import metadata as mt
from .logger import logger from .logger import logger
@ -12,20 +12,30 @@ from .logger import logger
from uuid import uuid4 from uuid import uuid4
import io import io
import os import os
import time
blueprint = Blueprint('api', __name__, url_prefix='/api') blueprint = Blueprint('api', __name__, url_prefix='/api')
@blueprint.route('/uploads/<file>/<int:quality>', methods=['GET']) @blueprint.route('/uploads/<file>', methods=['GET'])
def uploads(file, quality): def uploads(file):
# If quality is 0, return original file # Get args
if quality == 0: width = request.args.get('w', default=0, type=int) # Width of image
height = request.args.get('h', default=0, type=int) # Height of image
filtered = request.args.get('f', default=False, type=bool) # Whether to apply filters to image,
# such as blur for NSFW images
# if no args are passed, return the raw file
if width == 0 and height == 0 and not filtered:
return send_from_directory(current_app.config['UPLOAD_FOLDER'], return send_from_directory(current_app.config['UPLOAD_FOLDER'],
secure_filename(file), secure_filename(file),
as_attachment=True) as_attachment=True)
# Set variables # Of either width or height is 0, set it to the other value to keep aspect ratio
if width > 0 and height == 0:
height = width
elif width == 0 and height > 0:
width = height
set_ext = current_app.config['ALLOWED_EXTENSIONS'] set_ext = current_app.config['ALLOWED_EXTENSIONS']
buff = io.BytesIO() buff = io.BytesIO()
@ -33,7 +43,7 @@ def uploads(file, quality):
try: try:
img = Image.open( img = Image.open(
os.path.join(current_app.config['UPLOAD_FOLDER'], os.path.join(current_app.config['UPLOAD_FOLDER'],
secure_filename(file))) secure_filename(file)))
except Exception as e: except Exception as e:
logger.server(600, f"Error opening image: {e}") logger.server(600, f"Error opening image: {e}")
abort(500) abort(500)
@ -45,8 +55,15 @@ def uploads(file, quality):
"icc_profile") # Get ICC profile as it alters colours "icc_profile") # Get ICC profile as it alters colours
# Resize image and orientate correctly # Resize image and orientate correctly
img.thumbnail((quality, quality), Image.LANCZOS) img.thumbnail((width, height), Image.LANCZOS)
img = ImageOps.exif_transpose(img) img = ImageOps.exif_transpose(img)
# TODO: Add filters
# If has NSFW tag, blur image, etc.
if filtered:
#pass
img = img.filter(ImageFilter.GaussianBlur(20))
try: try:
img.save(buff, img_ext, icc_profile=img_icc) img.save(buff, img_ext, icc_profile=img_icc)
except OSError: except OSError:
@ -74,7 +91,8 @@ def upload():
if not form_file: if not form_file:
return abort(404) return abort(404)
img_ext = os.path.splitext(secure_filename(form_file.filename))[-1].replace('.', '').lower() img_ext = os.path.splitext(secure_filename(
form_file.filename))[-1].replace('.', '').lower()
img_name = f"GWAGWA_{uuid4().__str__()}.{img_ext}" img_name = f"GWAGWA_{uuid4().__str__()}.{img_ext}"
if not img_ext in current_app.config['ALLOWED_EXTENSIONS'].keys(): if not img_ext in current_app.config['ALLOWED_EXTENSIONS'].keys():
@ -154,6 +172,7 @@ def metadata(id):
return jsonify(exif) return jsonify(exif)
@blueprint.route('/logfile') @blueprint.route('/logfile')
@login_required @login_required
def logfile(): def logfile():
@ -180,16 +199,10 @@ def logfile():
'message': message[5:].strip() 'message': message[5:].strip()
} }
except: except:
message_data = { message_data = {'code': 0, 'message': message}
'code': 0,
'message': message
}
log_dict[i] = { log_dict[i] = {'event': event_data, 'message': message_data}
'event': event_data,
'message': message_data
}
i += 1 # Line number, starts at 0 i += 1 # Line number, starts at 0
return jsonify(log_dict) return jsonify(log_dict)

View file

@ -1,14 +1,15 @@
import logging import logging
import os import os
from datetime import datetime from datetime import datetime
import platformdirs
# Prevent werkzeug from logging # Prevent werkzeug from logging
logging.getLogger('werkzeug').disabled = True logging.getLogger('werkzeug').disabled = True
class logger: class logger:
def innit_logger(app): def innit_logger():
filepath = os.path.join(app.root_path, 'user', 'logs') filepath = os.path.join(platformdirs.user_config_dir('onlylegs'), 'logs')
#filename = f'onlylogs_{datetime.now().strftime("%Y%m%d")}.log' #filename = f'onlylogs_{datetime.now().strftime("%Y%m%d")}.log'
filename = 'only.log' filename = 'only.log'

View file

@ -11,8 +11,8 @@ class compile():
def __init__(self, theme, dir): def __init__(self, theme, dir):
print(f"Loading '{theme}' theme...") print(f"Loading '{theme}' theme...")
theme_path = os.path.join(dir, 'user', 'themes', theme) theme_path = os.path.join(dir, 'themes', theme)
font_path = os.path.join(dir, 'user', 'themes', theme, 'fonts') font_path = os.path.join(dir, 'themes', theme, 'fonts')
dest = os.path.join(dir, 'static', 'theme') dest = os.path.join(dir, 'static', 'theme')
# print(f"Theme path: {theme_path}") # print(f"Theme path: {theme_path}")

View file

@ -2,11 +2,10 @@ function showLogin() {
popUpShow( popUpShow(
'idk what to put here, just login please', 'idk what to put here, just login please',
'Need an account? <span class="pop-up__link" onclick="showRegister()">Register!</span>', 'Need an account? <span class="pop-up__link" onclick="showRegister()">Register!</span>',
'', '<button class="pop-up__btn pop-up__btn-primary-fill" form="loginForm" type="submit">Login</button>',
'<form onsubmit="return login(event)">\ '<form id="loginForm" onsubmit="return login(event)">\
<input class="pop-up__input" type="text" placeholder="Namey" id="username"/>\ <input class="pop-up__input" type="text" placeholder="Namey" id="username"/>\
<input class="pop-up__input" type="password" placeholder="Passywassy" id="password"/>\ <input class="pop-up__input" type="password" placeholder="Passywassy" id="password"/>\
<button class="pop-up__btn pop-up__btn-primary-fill">Login</button>\
</form>' </form>'
); );
}; };
@ -14,13 +13,12 @@ function showRegister() {
popUpShow( popUpShow(
'Who are you?', 'Who are you?',
'Already have an account? <span class="pop-up__link" onclick="showLogin()">Login!</span>', 'Already have an account? <span class="pop-up__link" onclick="showLogin()">Login!</span>',
'', '<button class="pop-up__btn pop-up__btn-primary-fill" form="registerForm" type="submit">Register</button>',
'<form onsubmit="return register(event)">\ '<form id="registerForm" onsubmit="return register(event)">\
<input class="pop-up__input" type="text" placeholder="Namey" id="username"/>\ <input class="pop-up__input" type="text" placeholder="Namey" id="username"/>\
<input class="pop-up__input" type="text" placeholder="E mail!" id="email"/>\ <input class="pop-up__input" type="text" placeholder="E mail!" id="email"/>\
<input class="pop-up__input" type="password" placeholder="Passywassy" id="password"/>\ <input class="pop-up__input" type="password" placeholder="Passywassy" id="password"/>\
<input class="pop-up__input" type="password" placeholder="Passywassy again!" id="password-repeat"/>\ <input class="pop-up__input" type="password" placeholder="Passywassy again!" id="password-repeat"/>\
<button class="pop-up__btn pop-up__btn-primary-fill">Register</button>\
</form>' </form>'
); );
}; };

View file

@ -158,3 +158,11 @@ function popupDissmiss() {
popup.classList = 'pop-up'; popup.classList = 'pop-up';
}, 200); }, 200);
} }
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
if (document.querySelector('.pop-up').classList.contains('pop-up__active')) {
popupDissmiss();
}
}
});

View file

@ -2,13 +2,12 @@ function showUpload() {
popUpShow( popUpShow(
'Upload funny stuff', 'Upload funny stuff',
'May the world see your stuff 👀', 'May the world see your stuff 👀',
'', '<button class="pop-up__btn pop-up__btn-primary-fill" form="uploadForm" type"submit">Upload</button>',
'<form onsubmit="return uploadFile(event)">\ '<form id="uploadForm" onsubmit="return uploadFile(event)">\
<input class="pop-up__input" type="file" id="file"/>\ <input class="pop-up__input" type="file" id="file"/>\
<input class="pop-up__input" type="text" placeholder="alt" id="alt"/>\ <input class="pop-up__input" type="text" placeholder="alt" id="alt"/>\
<input class="pop-up__input" type="text" placeholder="description" id="description"/>\ <input class="pop-up__input" type="text" placeholder="description" id="description"/>\
<input class="pop-up__input" type="text" placeholder="tags" id="tags"/>\ <input class="pop-up__input" type="text" placeholder="tags" id="tags"/>\
<button class="pop-up__btn pop-up__btn-primary-fill">Upload</button>\
</form>' </form>'
); );
}; };

View file

@ -2,7 +2,7 @@
{% block header %} {% block header %}
<div class="background-decoration"> <div class="background-decoration">
<img src="/api/uploads/{{ image['file_name'] }}/1000" onload="imgFade(this)" style="opacity:0;"/> <img src="/api/uploads/{{ image['file_name'] }}?w=1000&h=1000" onload="imgFade(this)" style="opacity:0;"/>
<span></span> <span></span>
</div> </div>
{% endblock %} {% endblock %}
@ -15,7 +15,7 @@
<div class="image-container"> <div class="image-container">
<img <img
src="/api/uploads/{{ image['file_name'] }}/1000" src="/api/uploads/{{ image['file_name'] }}?w=1000&h=1000"
onload="imgFade(this)" style="opacity:0;" onload="imgFade(this)" style="opacity:0;"
onerror="this.src='/static/images/error.png'" onerror="this.src='/static/images/error.png'"
width="{{ exif['File']['Width']['raw'] }}" width="{{ exif['File']['Width']['raw'] }}"
@ -227,14 +227,14 @@
$('.image-fullscreen').removeClass('image-fullscreen__active image-fullscreen__hide'); $('.image-fullscreen').removeClass('image-fullscreen__active image-fullscreen__hide');
}, 200); }, 200);
}); });
$('#img-fullscreen').click(function() { $('#img-fullscreen').click(function() {
$('.image-fullscreen').addClass('image-fullscreen__active'); $('.image-fullscreen').addClass('image-fullscreen__active');
if ($('.image-fullscreen img').attr('src') == '') { if ($('.image-fullscreen img').attr('src') == '') {
$('.image-fullscreen img').attr('src', '/api/uploads/{{ image['file_name'] }}/0'); $('.image-fullscreen img').attr('src', '/api/uploads/{{ image['file_name'] }}');
} }
}); });
$('#img-share').click(function() { $('#img-share').click(function() {
try { try {
navigator.clipboard.writeText(window.location.href); navigator.clipboard.writeText(window.location.href);
@ -253,7 +253,7 @@
'DESTRUCTION!!!!!!', 'DESTRUCTION!!!!!!',
'This will delete the image and all of its data!!! This action is irreversible!!!!! Are you sure you want to do this?????', 'This will delete the image and all of its data!!! This action is irreversible!!!!! Are you sure you want to do this?????',
'<button class="pop-up__btn pop-up__btn-critical-fill" onclick="deleteImage()">Dewww eeeet!</button>', '<button class="pop-up__btn pop-up__btn-critical-fill" onclick="deleteImage()">Dewww eeeet!</button>',
'<img src="/api/uploads/{{ image['file_name'] }}/1000" />' '<img src="/api/uploads/{{ image['file_name'] }}?w=1000&h=1000" />'
); );
}); });
$('#img-edit').click(function() { $('#img-edit').click(function() {

View file

@ -43,7 +43,7 @@
var image = images[i]; var image = images[i];
if (image.getBoundingClientRect().top < window.innerHeight && image.getBoundingClientRect().bottom > 0) { if (image.getBoundingClientRect().top < window.innerHeight && image.getBoundingClientRect().bottom > 0) {
if (!image.src) { if (!image.src) {
image.src = `/api/uploads/${image.getAttribute('data-src')}/400` image.src = `/api/uploads/${image.getAttribute('data-src')}?w=500&h=500`
} }
} }
} }

View file

@ -45,7 +45,7 @@
.pop-up-wrapper .pop-up-wrapper
margin: 0 margin: 0
padding: 0.5rem padding: 0
width: 621px width: 621px
height: auto height: auto
@ -57,7 +57,6 @@
display: flex display: flex
flex-direction: column flex-direction: column
gap: 0.5rem
background-color: $white background-color: $white
border-radius: $rad border-radius: $rad
@ -69,7 +68,7 @@
.pop-up-content .pop-up-content
margin: 0 margin: 0
padding: 0 padding: 0.5rem 0.5rem 0
width: 100% width: 100%
height: auto height: auto
@ -138,22 +137,21 @@
.pop-up-controlls .pop-up-controlls
margin: 0 margin: 0
padding: 0 padding: 0.5rem
width: 100% width: 100%
height: auto height: auto
display: flex display: flex
flex-direction: column flex-direction: row
justify-content: flex-end
gap: 0.5rem gap: 0.5rem
justify-content: center
.pop-up__btn .pop-up__btn
margin: 0 margin: 0
padding: 0.5rem padding: 0.5rem 1rem
width: 100% width: auto
height: 2.5rem height: 2.5rem
display: flex display: flex
@ -256,6 +254,13 @@
img img
max-height: 50vh max-height: 50vh
.pop-up-controlls
flex-direction: column
justify-content: center
.pop-up__btn
width: 100%
.pop-up__active .pop-up__active
opacity: 1 opacity: 1
top: unset top: unset

View file

@ -86,8 +86,6 @@
object-fit: contain object-fit: contain
object-position: center object-position: center
border-radius: $rad
.image-info__container .image-info__container
margin: 0 margin: 0
padding: 0 padding: 0

View file

@ -1,9 +0,0 @@
# EMAIL CONFIGURATION
EMAIL_HOST = #smtp.gmail.com
EMAIL_PORT = #465
EMAIL_HOST_USER = #your email
EMAIL_HOST_PASSWORD = #your password
EMAIL_USE_TLS = #True
# FLASK SECRETE KEY, DONT LOOSE IT!!!!!!!
FLASK_SECRET = #your secret key

View file

@ -1,21 +0,0 @@
# THIS IS AN EXAMPLE CONFIG, RENAME THIS TO conf.yml TO USE
admin:
name: Real Person
username: User
email: real-email@some.place
upload:
allowed-extensions:
- .png
- .jpg
- .jpeg
- .webp
max-size: 69MB
rename: GWA_{{username}}_{{time}}
website:
name: OnlyLegs
motto: Gwa Gwa
language: english # Placeholder for future language support endevours

View file

@ -2,7 +2,7 @@ from setuptools import find_packages, setup
setup( setup(
name='onlylegs', name='onlylegs',
version='23.03.01', version='23.03.02',
packages=find_packages(), packages=find_packages(),
include_package_data=True, include_package_data=True,
install_requires=[ install_requires=[
@ -13,5 +13,6 @@ setup(
'pillow', 'pillow',
'colorthief', 'colorthief',
'pyyaml', 'pyyaml',
'platformdirs',
], ],
) )