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_compress import Compress
from flask.helpers import get_root_path
from dotenv import load_dotenv
import platformdirs
import yaml
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):
# create and configure the app
app = Flask(__name__)
app = Flask(__name__, instance_path=instance_path)
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.config.from_mapping(
SECRET_KEY=os.environ.get('FLASK_SECRET'),
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'],
ALLOWED_EXTENSIONS=conf['upload']['allowed-extensions'],
WEBSITE=conf['website'],
@ -62,10 +121,6 @@ def create_app(test_config=None):
from . import sassy
sassy.compile('default', app.root_path)
# Load logger
from .logger import logger
logger.innit_logger(app)
@app.errorhandler(405)
def method_not_allowed(e):
error = '405'

View file

@ -4,7 +4,7 @@ from werkzeug.utils import secure_filename
from gallery.auth import login_required
from gallery.db import get_db
from PIL import Image, ImageOps
from PIL import Image, ImageOps, ImageFilter
from . import metadata as mt
from .logger import logger
@ -12,20 +12,30 @@ from .logger import logger
from uuid import uuid4
import io
import os
import time
blueprint = Blueprint('api', __name__, url_prefix='/api')
@blueprint.route('/uploads/<file>/<int:quality>', methods=['GET'])
def uploads(file, quality):
# If quality is 0, return original file
if quality == 0:
@blueprint.route('/uploads/<file>', methods=['GET'])
def uploads(file):
# Get args
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'],
secure_filename(file),
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']
buff = io.BytesIO()
@ -33,7 +43,7 @@ def uploads(file, quality):
try:
img = Image.open(
os.path.join(current_app.config['UPLOAD_FOLDER'],
secure_filename(file)))
secure_filename(file)))
except Exception as e:
logger.server(600, f"Error opening image: {e}")
abort(500)
@ -45,8 +55,15 @@ def uploads(file, quality):
"icc_profile") # Get ICC profile as it alters colours
# Resize image and orientate correctly
img.thumbnail((quality, quality), Image.LANCZOS)
img.thumbnail((width, height), Image.LANCZOS)
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:
img.save(buff, img_ext, icc_profile=img_icc)
except OSError:
@ -74,7 +91,8 @@ def upload():
if not form_file:
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}"
if not img_ext in current_app.config['ALLOWED_EXTENSIONS'].keys():
@ -154,6 +172,7 @@ def metadata(id):
return jsonify(exif)
@blueprint.route('/logfile')
@login_required
def logfile():
@ -180,16 +199,10 @@ def logfile():
'message': message[5:].strip()
}
except:
message_data = {
'code': 0,
'message': message
}
message_data = {'code': 0, 'message': message}
log_dict[i] = {
'event': event_data,
'message': message_data
}
log_dict[i] = {'event': event_data, 'message': message_data}
i += 1 # Line number, starts at 0
i += 1 # Line number, starts at 0
return jsonify(log_dict)

View file

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

View file

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

View file

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

View file

@ -158,3 +158,11 @@ function popupDissmiss() {
popup.classList = 'pop-up';
}, 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(
'Upload funny stuff',
'May the world see your stuff 👀',
'',
'<form onsubmit="return uploadFile(event)">\
'<button class="pop-up__btn pop-up__btn-primary-fill" form="uploadForm" type"submit">Upload</button>',
'<form id="uploadForm" onsubmit="return uploadFile(event)">\
<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="description" id="description"/>\
<input class="pop-up__input" type="text" placeholder="tags" id="tags"/>\
<button class="pop-up__btn pop-up__btn-primary-fill">Upload</button>\
</form>'
);
};

View file

@ -2,7 +2,7 @@
{% block header %}
<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>
</div>
{% endblock %}
@ -15,7 +15,7 @@
<div class="image-container">
<img
src="/api/uploads/{{ image['file_name'] }}/1000"
src="/api/uploads/{{ image['file_name'] }}?w=1000&h=1000"
onload="imgFade(this)" style="opacity:0;"
onerror="this.src='/static/images/error.png'"
width="{{ exif['File']['Width']['raw'] }}"
@ -227,14 +227,14 @@
$('.image-fullscreen').removeClass('image-fullscreen__active image-fullscreen__hide');
}, 200);
});
$('#img-fullscreen').click(function() {
$('.image-fullscreen').addClass('image-fullscreen__active');
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() {
try {
navigator.clipboard.writeText(window.location.href);
@ -253,7 +253,7 @@
'DESTRUCTION!!!!!!',
'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>',
'<img src="/api/uploads/{{ image['file_name'] }}/1000" />'
'<img src="/api/uploads/{{ image['file_name'] }}?w=1000&h=1000" />'
);
});
$('#img-edit').click(function() {

View file

@ -43,7 +43,7 @@
var image = images[i];
if (image.getBoundingClientRect().top < window.innerHeight && image.getBoundingClientRect().bottom > 0) {
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
margin: 0
padding: 0.5rem
padding: 0
width: 621px
height: auto
@ -57,7 +57,6 @@
display: flex
flex-direction: column
gap: 0.5rem
background-color: $white
border-radius: $rad
@ -69,7 +68,7 @@
.pop-up-content
margin: 0
padding: 0
padding: 0.5rem 0.5rem 0
width: 100%
height: auto
@ -138,22 +137,21 @@
.pop-up-controlls
margin: 0
padding: 0
padding: 0.5rem
width: 100%
height: auto
display: flex
flex-direction: column
flex-direction: row
justify-content: flex-end
gap: 0.5rem
justify-content: center
.pop-up__btn
margin: 0
padding: 0.5rem
padding: 0.5rem 1rem
width: 100%
width: auto
height: 2.5rem
display: flex
@ -256,6 +254,13 @@
img
max-height: 50vh
.pop-up-controlls
flex-direction: column
justify-content: center
.pop-up__btn
width: 100%
.pop-up__active
opacity: 1
top: unset

View file

@ -86,8 +86,6 @@
object-fit: contain
object-position: center
border-radius: $rad
.image-info__container
margin: 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(
name='onlylegs',
version='23.03.01',
version='23.03.02',
packages=find_packages(),
include_package_data=True,
install_requires=[
@ -13,5 +13,6 @@ setup(
'pillow',
'colorthief',
'pyyaml',
'platformdirs',
],
)