Switch to Flask_Login for user/session management

This commit is contained in:
Michał Gdula 2023-04-03 18:04:49 +00:00
parent 33f475e828
commit 4e50a66514
10 changed files with 127 additions and 168 deletions

View file

@ -11,6 +11,7 @@ import logging
from flask_compress import Compress from flask_compress import Compress
from flask_caching import Cache from flask_caching import Cache
from flask_assets import Environment, Bundle from flask_assets import Environment, Bundle
from flask_login import LoginManager
from flask import Flask, render_template, abort from flask import Flask, render_template, abort
from werkzeug.exceptions import HTTPException from werkzeug.exceptions import HTTPException
@ -19,18 +20,26 @@ import platformdirs
from dotenv import load_dotenv from dotenv import load_dotenv
from yaml import safe_load from yaml import safe_load
# Import database
from sqlalchemy.orm import sessionmaker
from gallery import db
USER_DIR = platformdirs.user_config_dir('onlylegs') USER_DIR = platformdirs.user_config_dir('onlylegs')
db_session = sessionmaker(bind=db.engine)
db_session = db_session()
login_manager = LoginManager()
assets = Environment()
cache = Cache(config={'CACHE_TYPE': 'SimpleCache', 'CACHE_DEFAULT_TIMEOUT': 300})
compress = Compress()
def create_app(test_config=None): def create_app(test_config=None):
""" """
Create and configure the main app Create and configure the main app
""" """
app = Flask(__name__, instance_path=os.path.join(USER_DIR, 'instance')) app = Flask(__name__, instance_path=os.path.join(USER_DIR, 'instance'))
assets = Environment()
cache = Cache(config={'CACHE_TYPE': 'SimpleCache', 'CACHE_DEFAULT_TIMEOUT': 300})
compress = Compress()
# Get environment variables # Get environment variables
load_dotenv(os.path.join(USER_DIR, '.env')) load_dotenv(os.path.join(USER_DIR, '.env'))
@ -56,6 +65,13 @@ def create_app(test_config=None):
else: else:
app.config.from_mapping(test_config) app.config.from_mapping(test_config)
login_manager.init_app(app)
login_manager.login_view = 'gallery.index'
@login_manager.user_loader
def load_user(user_id):
return db_session.query(db.Users).filter_by(id=user_id).first()
# Load JS assets # Load JS assets
# TODO: disable caching for sass files as it makes it hard to work on when it is enabled # TODO: disable caching for sass files as it makes it hard to work on when it is enabled
assets.register('js_pre', Bundle('js/pre/*.js', output='gen/pre_packed.js')) assets.register('js_pre', Bundle('js/pre/*.js', output='gen/pre_packed.js'))

View file

@ -3,17 +3,15 @@ OnlyLegs - Authentication
User registration, login and logout and locking access to pages behind a login User registration, login and logout and locking access to pages behind a login
""" """
import re import re
import uuid
import logging import logging
from datetime import datetime as dt from datetime import datetime as dt
import functools from flask import Blueprint, flash, redirect, request, url_for, abort, jsonify
from flask import Blueprint, flash, g, redirect, request, session, url_for, abort, jsonify
from werkzeug.security import check_password_hash, generate_password_hash from werkzeug.security import check_password_hash, generate_password_hash
from sqlalchemy.orm import sessionmaker from flask_login import login_user, logout_user, login_required
from sqlalchemy import exc
from sqlalchemy.orm import sessionmaker
from gallery import db from gallery import db
@ -22,42 +20,30 @@ db_session = sessionmaker(bind=db.engine)
db_session = db_session() db_session = db_session()
def login_required(view): @blueprint.route('/login', methods=['POST'])
def login():
""" """
Decorator to check if a user is logged in before accessing a page Log in a registered user by adding the user id to the session
""" """
@functools.wraps(view) error = []
def wrapped_view(**kwargs):
if g.user is None or session.get('uuid') is None:
logging.error('Authentication failed')
session.clear()
return redirect(url_for('gallery.index'))
return view(**kwargs) username = request.form['username'].strip()
password = request.form['password'].strip()
return wrapped_view user = db_session.query(db.Users).filter_by(username=username).first()
if not user and not check_password_hash(user.password, password):
logging.error('Login attempt from %s', username, request.remote_addr)
error.append('Username or Password is incorrect!')
@blueprint.before_app_request if error:
def load_logged_in_user(): abort(403)
"""
Runs before every request and checks if a user is logged in
"""
user_id = session.get('user_id')
user_uuid = session.get('uuid')
if user_id is None or user_uuid is None: login_user(user)
g.user = None
session.clear()
else:
is_alive = db_session.query(db.Sessions).filter_by(session_uuid=user_uuid).first()
if is_alive is None: logging.info('User %s logged in from %s', username, request.remote_addr)
logging.info('Session expired') flash(['Logged in successfully!', '4'])
flash(['Session expired!', '3']) return 'gwa gwa'
session.clear()
else:
g.user = db_session.query(db.Users).filter_by(id=user_id).first()
@blueprint.route('/register', methods=['POST']) @blueprint.route('/register', methods=['POST'])
@ -65,17 +51,18 @@ def register():
""" """
Register a new user Register a new user
""" """
error = []
# Thanks Fennec for reminding me to strip out the whitespace lol # Thanks Fennec for reminding me to strip out the whitespace lol
username = request.form['username'].strip() username = request.form['username'].strip()
email = request.form['email'].strip() email = request.form['email'].strip()
password = request.form['password'].strip() password = request.form['password'].strip()
password_repeat = request.form['password-repeat'].strip() password_repeat = request.form['password-repeat'].strip()
error = []
email_regex = re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b') 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') username_regex = re.compile(r'\b[A-Za-z0-9._-]+\b')
# Validate the form
if not username or not username_regex.match(username): if not username or not username_regex.match(username):
error.append('Username is invalid!') error.append('Username is invalid!')
@ -92,74 +79,29 @@ def register():
elif password_repeat != password: elif password_repeat != password:
error.append('Passwords do not match!') error.append('Passwords do not match!')
user_exists = db_session.query(db.Users).filter_by(username=username).first()
if user_exists:
error.append('User already exists!')
# If there are errors, return them
if error: if error:
return jsonify(error) return jsonify(error)
try: register_user = db.Users(username=username, email=email,
register_user = db.Users(username=username, password=generate_password_hash(password, method='sha256'),
email=email, created_at=dt.utcnow())
password=generate_password_hash(password), db_session.add(register_user)
created_at=dt.utcnow()) db_session.commit()
db_session.add(register_user)
db_session.commit()
except exc.IntegrityError:
return f'User {username} is already registered!'
logging.info('User %s registered', username) logging.info('User %s registered', username)
return 'gwa gwa' return 'gwa gwa'
@blueprint.route('/login', methods=['POST'])
def login():
"""
Log in a registered user by adding the user id to the session
"""
username = request.form['username'].strip()
password = request.form['password'].strip()
user = db_session.query(db.Users).filter_by(username=username).first()
error = []
if user is None:
logging.error('User %s does not exist. Login attempt from %s',
username, request.remote_addr)
error.append('Username or Password is incorrect!')
elif not check_password_hash(user.password, password):
logging.error('User %s entered wrong password. Login attempt from %s',
username, request.remote_addr)
error.append('Username or Password is incorrect!')
if error:
abort(403)
try:
session.clear()
session['user_id'] = user.id
session['uuid'] = str(uuid.uuid4())
session_query = db.Sessions(user_id=user.id,
session_uuid=session.get('uuid'),
ip_address=request.remote_addr,
user_agent=request.user_agent.string,
active=True,
created_at=dt.utcnow())
db_session.add(session_query)
db_session.commit()
except Exception as err:
logging.error('User %s could not be logged in: %s', username, err)
abort(500)
logging.info('User %s logged in from %s', username, request.remote_addr)
flash(['Logged in successfully!', '4'])
return 'gwa gwa'
@blueprint.route('/logout') @blueprint.route('/logout')
@login_required
def logout(): def logout():
""" """
Clear the current session, including the stored user id Clear the current session, including the stored user id
""" """
logging.info('User (%s) %s logged out', session.get('user_id'), g.user.username) logout_user()
session.clear()
return redirect(url_for('gallery.index')) return redirect(url_for('gallery.index'))

View file

@ -8,6 +8,8 @@ from sqlalchemy import (
create_engine, Column, Integer, String, Boolean, DateTime, ForeignKey, PickleType) create_engine, Column, Integer, String, Boolean, DateTime, ForeignKey, PickleType)
from sqlalchemy.orm import declarative_base, relationship from sqlalchemy.orm import declarative_base, relationship
from flask_login import UserMixin
USER_DIR = platformdirs.user_config_dir('onlylegs') USER_DIR = platformdirs.user_config_dir('onlylegs')
DB_PATH = os.path.join(USER_DIR, 'gallery.sqlite') DB_PATH = os.path.join(USER_DIR, 'gallery.sqlite')
@ -18,7 +20,7 @@ engine = create_engine(f'sqlite:///{DB_PATH}', echo=False)
base = declarative_base() base = declarative_base()
class Users (base): # pylint: disable=too-few-public-methods, C0103 class Users (base, UserMixin): # pylint: disable=too-few-public-methods, C0103
""" """
User table User table
Joins with post, groups, session and log Joins with post, groups, session and log

View file

@ -75,7 +75,7 @@
{% else %} {% else %}
<div class="big-text"> <div class="big-text">
<h1>*crickets chirping*</h1> <h1>*crickets chirping*</h1>
{% if g.user %} {% if current_user.is_authenticated %}
<p>Add some images to the group!</p> <p>Add some images to the group!</p>
{% else %} {% else %}
<p>Login to start managing this image group!</p> <p>Login to start managing this image group!</p>

View file

@ -43,7 +43,7 @@
{% else %} {% else %}
<div class="big-text"> <div class="big-text">
<h1>*crickets chirping*</h1> <h1>*crickets chirping*</h1>
{% if g.user %} {% if current_user.is_authenticated %}
<p>You can get started by creating a new image group!</p> <p>You can get started by creating a new image group!</p>
{% else %} {% else %}
<p>Login to start seeing anything here!</p> <p>Login to start seeing anything here!</p>

View file

@ -33,7 +33,7 @@
} }
} }
{% if g.user.id == image.author_id %} {% if current_user.id == image.author_id %}
cancelBtn = document.createElement('button'); cancelBtn = document.createElement('button');
cancelBtn.classList.add('btn-block'); cancelBtn.classList.add('btn-block');
cancelBtn.innerHTML = 'nuuuuuuuu'; cancelBtn.innerHTML = 'nuuuuuuuu';
@ -144,7 +144,7 @@
</span> </span>
</a> </a>
</div> </div>
{% if g.user.id == image.author_id %} {% if current_user.id == image.author_id %}
<div> <div>
<button class="pill-item pill__critical" onclick="imageDelete()"> <button class="pill-item pill__critical" onclick="imageDelete()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM112,168a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm0-120H96V40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM112,168a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm0-120H96V40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8Z"></path></svg>

View file

@ -26,15 +26,15 @@
media="(prefers-color-scheme: dark)"/> media="(prefers-color-scheme: dark)"/>
{% assets "js_pre" %} {% assets "js_pre" %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script> <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %} {% endassets %}
{% assets "js_post" %} {% assets "js_post" %}
<script type="text/javascript" src="{{ ASSET_URL }}" defer></script> <script type="text/javascript" src="{{ ASSET_URL }}" defer></script>
{% endassets %} {% endassets %}
{% assets "styles" %} {% assets "styles" %}
<link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css" defer> <link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css" defer>
{% endassets %} {% endassets %}
{% block head %}{% endblock %} {% block head %}{% endblock %}
@ -46,9 +46,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M184,216a8,8,0,0,1-8,8H80a8,8,0,0,1,0-16h96A8,8,0,0,1,184,216Zm45.66-101.66-96-96a8,8,0,0,0-11.32,0l-96,96A8,8,0,0,0,32,128H72v24a8,8,0,0,0,8,8h96a8,8,0,0,0,8-8V128h40a8,8,0,0,0,5.66-13.66ZM176,176H80a8,8,0,0,0,0,16h96a8,8,0,0,0,0-16Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M184,216a8,8,0,0,1-8,8H80a8,8,0,0,1,0-16h96A8,8,0,0,1,184,216Zm45.66-101.66-96-96a8,8,0,0,0-11.32,0l-96,96A8,8,0,0,0,32,128H72v24a8,8,0,0,0,8,8h96a8,8,0,0,0,8-8V128h40a8,8,0,0,0,5.66-13.66ZM176,176H80a8,8,0,0,0,0,16h96a8,8,0,0,0,0-16Z"></path></svg>
</button> </button>
{% if request.path == "/" %} {% if request.path == "/" %}
<button class="info-button"> <button class="info-button">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-4,48a12,12,0,1,1-12,12A12,12,0,0,1,124,72Zm12,112a16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40a8,8,0,0,1,0,16Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-4,48a12,12,0,1,1-12,12A12,12,0,0,1,124,72Zm12,112a16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40a8,8,0,0,1,0,16Z"></path></svg>
</button> </button>
{% endif %} {% endif %}
<div class="pop-up"> <div class="pop-up">
@ -80,67 +80,67 @@
</span> </span>
</a> </a>
{% if g.user %} {% if current_user.is_authenticated %}
<button class="navigation-item {% block nav_upload %}{% endblock %}" onclick="toggleUploadTab()"> <button class="navigation-item {% block nav_upload %}{% endblock %}" onclick="toggleUploadTab()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M74.34,77.66a8,8,0,0,1,0-11.32l48-48a8,8,0,0,1,11.32,0l48,48a8,8,0,0,1-11.32,11.32L136,43.31V128a8,8,0,0,1-16,0V43.31L85.66,77.66A8,8,0,0,1,74.34,77.66ZM240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16h68a4,4,0,0,1,4,4v3.46c0,13.45,11,24.79,24.46,24.54A24,24,0,0,0,152,128v-4a4,4,0,0,1,4-4h68A16,16,0,0,1,240,136Zm-40,32a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M74.34,77.66a8,8,0,0,1,0-11.32l48-48a8,8,0,0,1,11.32,0l48,48a8,8,0,0,1-11.32,11.32L136,43.31V128a8,8,0,0,1-16,0V43.31L85.66,77.66A8,8,0,0,1,74.34,77.66ZM240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16h68a4,4,0,0,1,4,4v3.46c0,13.45,11,24.79,24.46,24.54A24,24,0,0,0,152,128v-4a4,4,0,0,1,4-4h68A16,16,0,0,1,240,136Zm-40,32a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg>
<span class="tool-tip"> <span class="tool-tip">
Upload Upload
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
</span> </span>
</button> </button>
{% endif %} {% endif %}
<span class="navigation-spacer"></span> <span class="navigation-spacer"></span>
{% if g.user %} {% if current_user.is_authenticated %}
<a href="{{url_for('gallery.profile')}}" class="navigation-item {% block nav_profile %}{% endblock %}"> <a href="{{url_for('gallery.profile')}}" class="navigation-item {% block nav_profile %}{% endblock %}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M231.73,221.94A8,8,0,0,1,224,232H160A8,8,0,0,1,152.27,222a40,40,0,0,1,17.11-23.33,32,32,0,1,1,45.24,0A40,40,0,0,1,231.73,221.94ZM216,72H130.67L102.93,51.2a16.12,16.12,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V200a16,16,0,0,0,16,16h80a8,8,0,0,0,0-16H40V64H93.33l27.74,20.8a16.12,16.12,0,0,0,9.6,3.2H216v32a8,8,0,0,0,16,0V88A16,16,0,0,0,216,72Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M231.73,221.94A8,8,0,0,1,224,232H160A8,8,0,0,1,152.27,222a40,40,0,0,1,17.11-23.33,32,32,0,1,1,45.24,0A40,40,0,0,1,231.73,221.94ZM216,72H130.67L102.93,51.2a16.12,16.12,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V200a16,16,0,0,0,16,16h80a8,8,0,0,0,0-16H40V64H93.33l27.74,20.8a16.12,16.12,0,0,0,9.6,3.2H216v32a8,8,0,0,0,16,0V88A16,16,0,0,0,216,72Z"></path></svg>
<span class="tool-tip"> <span class="tool-tip">
Profile Profile
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
</span> </span>
</a> </a>
<a href="{{url_for('settings.general')}}" class="navigation-item {% block nav_settings %}{% endblock %}"> <a href="{{url_for('settings.general')}}" class="navigation-item {% block nav_settings %}{% endblock %}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,130.16q.06-2.16,0-4.32l14.92-18.64a8,8,0,0,0,1.48-7.06,107.6,107.6,0,0,0-10.88-26.25,8,8,0,0,0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186,40.54a8,8,0,0,0-3.94-6,107.29,107.29,0,0,0-26.25-10.86,8,8,0,0,0-7.06,1.48L130.16,40Q128,40,125.84,40L107.2,25.11a8,8,0,0,0-7.06-1.48A107.6,107.6,0,0,0,73.89,34.51a8,8,0,0,0-3.93,6L67.32,64.27q-1.56,1.49-3,3L40.54,70a8,8,0,0,0-6,3.94,107.71,107.71,0,0,0-10.87,26.25,8,8,0,0,0,1.49,7.06L40,125.84Q40,128,40,130.16L25.11,148.8a8,8,0,0,0-1.48,7.06,107.6,107.6,0,0,0,10.88,26.25,8,8,0,0,0,6,3.93l23.72,2.64q1.49,1.56,3,3L70,215.46a8,8,0,0,0,3.94,6,107.71,107.71,0,0,0,26.25,10.87,8,8,0,0,0,7.06-1.49L125.84,216q2.16.06,4.32,0l18.64,14.92a8,8,0,0,0,7.06,1.48,107.21,107.21,0,0,0,26.25-10.88,8,8,0,0,0,3.93-6l2.64-23.72q1.56-1.48,3-3L215.46,186a8,8,0,0,0,6-3.94,107.71,107.71,0,0,0,10.87-26.25,8,8,0,0,0-1.49-7.06ZM128,168a40,40,0,1,1,40-40A40,40,0,0,1,128,168Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,130.16q.06-2.16,0-4.32l14.92-18.64a8,8,0,0,0,1.48-7.06,107.6,107.6,0,0,0-10.88-26.25,8,8,0,0,0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186,40.54a8,8,0,0,0-3.94-6,107.29,107.29,0,0,0-26.25-10.86,8,8,0,0,0-7.06,1.48L130.16,40Q128,40,125.84,40L107.2,25.11a8,8,0,0,0-7.06-1.48A107.6,107.6,0,0,0,73.89,34.51a8,8,0,0,0-3.93,6L67.32,64.27q-1.56,1.49-3,3L40.54,70a8,8,0,0,0-6,3.94,107.71,107.71,0,0,0-10.87,26.25,8,8,0,0,0,1.49,7.06L40,125.84Q40,128,40,130.16L25.11,148.8a8,8,0,0,0-1.48,7.06,107.6,107.6,0,0,0,10.88,26.25,8,8,0,0,0,6,3.93l23.72,2.64q1.49,1.56,3,3L70,215.46a8,8,0,0,0,3.94,6,107.71,107.71,0,0,0,26.25,10.87,8,8,0,0,0,7.06-1.49L125.84,216q2.16.06,4.32,0l18.64,14.92a8,8,0,0,0,7.06,1.48,107.21,107.21,0,0,0,26.25-10.88,8,8,0,0,0,3.93-6l2.64-23.72q1.56-1.48,3-3L215.46,186a8,8,0,0,0,6-3.94,107.71,107.71,0,0,0,10.87-26.25,8,8,0,0,0-1.49-7.06ZM128,168a40,40,0,1,1,40-40A40,40,0,0,1,128,168Z"></path></svg>
<span class="tool-tip"> <span class="tool-tip">
Settings Settings
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
</span> </span>
</a> </a>
{% else %} {% else %}
<button class="navigation-item {% block nav_login %}{% endblock %}" onclick="showLogin()"> <button class="navigation-item {% block nav_login %}{% endblock %}" onclick="showLogin()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M141.66,133.66l-40,40A8,8,0,0,1,88,168V136H24a8,8,0,0,1,0-16H88V88a8,8,0,0,1,13.66-5.66l40,40A8,8,0,0,1,141.66,133.66ZM192,32H136a8,8,0,0,0,0,16h56V208H136a8,8,0,0,0,0,16h56a16,16,0,0,0,16-16V48A16,16,0,0,0,192,32Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M141.66,133.66l-40,40A8,8,0,0,1,88,168V136H24a8,8,0,0,1,0-16H88V88a8,8,0,0,1,13.66-5.66l40,40A8,8,0,0,1,141.66,133.66ZM192,32H136a8,8,0,0,0,0,16h56V208H136a8,8,0,0,0,0,16h56a16,16,0,0,0,16-16V48A16,16,0,0,0,192,32Z"></path></svg>
<span class="tool-tip"> <span class="tool-tip">
Login Login
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
</span> </span>
</button> </button>
{% endif %} {% endif %}
</div> </div>
{% if g.user %} {% if current_user.is_authenticated %}
<div class="upload-panel"> <div class="upload-panel">
<span class="click-off" onclick="closeUploadTab()"></span> <span class="click-off" onclick="closeUploadTab()"></span>
<div class="container"> <div class="container">
<span id="dragIndicator"></span> <span id="dragIndicator"></span>
<h3>Upload stuffs</h3> <h3>Upload stuffs</h3>
<p>May the world see your stuff 👀</p> <p>May the world see your stuff 👀</p>
<form id="uploadForm"> <form id="uploadForm">
<button class="fileDrop-block" type="button"> <button class="fileDrop-block" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16H80a8,8,0,0,1,0,16H32v64H224V136H176a8,8,0,0,1,0-16h48A16,16,0,0,1,240,136ZM85.66,77.66,120,43.31V128a8,8,0,0,0,16,0V43.31l34.34,34.35a8,8,0,0,0,11.32-11.32l-48-48a8,8,0,0,0-11.32,0l-48,48A8,8,0,0,0,85.66,77.66ZM200,168a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16H80a8,8,0,0,1,0,16H32v64H224V136H176a8,8,0,0,1,0-16h48A16,16,0,0,1,240,136ZM85.66,77.66,120,43.31V128a8,8,0,0,0,16,0V43.31l34.34,34.35a8,8,0,0,0,11.32-11.32l-48-48a8,8,0,0,0-11.32,0l-48,48A8,8,0,0,0,85.66,77.66ZM200,168a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg>
<span class="status">Choose or Drop file</span> <span class="status">Choose or Drop file</span>
<input type="file" id="file" tab-index="-1"/> <input type="file" id="file" tab-index="-1"/>
</button> </button>
<input class="input-block" type="text" placeholder="alt" id="alt"/> <input class="input-block" type="text" placeholder="alt" id="alt"/>
<input class="input-block" type="text" placeholder="description" id="description"/> <input class="input-block" type="text" placeholder="description" id="description"/>
<input class="input-block" type="text" placeholder="tags" id="tags"/> <input class="input-block" type="text" placeholder="tags" id="tags"/>
<button class="btn-block" type="submit">Upload</button> <button class="btn-block" type="submit">Upload</button>
</form> </form>
<div class="upload-jobs"></div> <div class="upload-jobs"></div>
</div>
</div> </div>
</div>
{% endif %} {% endif %}
<div class="content"> <div class="content">

View file

@ -8,7 +8,7 @@
<div class="banner-content"> <div class="banner-content">
<h1>Profile</h1> <h1>Profile</h1>
<p>Hello {{ g.user['username'] }}</p> <p>Hello {{ current_user.username }}</p>
</div> </div>
</div> </div>

View file

@ -8,13 +8,14 @@ import logging
from datetime import datetime as dt from datetime import datetime as dt
import platformdirs import platformdirs
from flask import Blueprint, send_from_directory, abort, flash, jsonify, request, g, current_app from flask import Blueprint, send_from_directory, abort, flash, jsonify, request, current_app
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from flask_login import login_required, current_user
from colorthief import ColorThief from colorthief import ColorThief
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from gallery.auth import login_required
from gallery import db from gallery import db
from gallery.utils import metadata as mt from gallery.utils import metadata as mt
@ -83,7 +84,7 @@ def upload():
img_colors = ColorThief(img_path).get_palette(color_count=3) # Get color palette img_colors = ColorThief(img_path).get_palette(color_count=3) # Get color palette
# Save to database # Save to database
query = db.Posts(author_id=g.user.id, query = db.Posts(author_id=current_user.id,
created_at=dt.utcnow(), created_at=dt.utcnow(),
file_name=img_name+'.'+img_ext, file_name=img_name+'.'+img_ext,
file_type=img_ext, file_type=img_ext,
@ -109,7 +110,7 @@ def delete_image(image_id):
# Check if image exists and if user is allowed to delete it (author) # Check if image exists and if user is allowed to delete it (author)
if img is None: if img is None:
abort(404) abort(404)
if img.author_id != g.user.id: if img.author_id != current_user.id:
abort(403) abort(403)
# Delete file # Delete file
@ -148,7 +149,7 @@ def create_group():
""" """
new_group = db.Groups(name=request.form['name'], new_group = db.Groups(name=request.form['name'],
description=request.form['description'], description=request.form['description'],
author_id=g.user.id, author_id=current_user.id,
created_at=dt.utcnow()) created_at=dt.utcnow())
db_session.add(new_group) db_session.add(new_group)
@ -170,7 +171,7 @@ def modify_group():
if group is None: if group is None:
abort(404) abort(404)
elif group.author_id != g.user.id: elif group.author_id != current_user.id:
abort(403) abort(403)
if request.form['action'] == 'add': if request.form['action'] == 'add':

View file

@ -2,9 +2,7 @@
OnlyLegs - Settings page OnlyLegs - Settings page
""" """
from flask import Blueprint, render_template from flask import Blueprint, render_template
from flask_login import login_required
from gallery.auth import login_required
blueprint = Blueprint('settings', __name__, url_prefix='/settings') blueprint = Blueprint('settings', __name__, url_prefix='/settings')