You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

312 lines
10 KiB
Python

import os
from time import sleep
from hashlib import pbkdf2_hmac
from configparser import ConfigParser
from getpass import getpass
from binascii import hexlify
from datetime import datetime
import random
from flask import (
Flask,
flash,
request,
render_template,
redirect,
send_from_directory,
session,
)
from flask_sqlalchemy import SQLAlchemy
CONFIG_PATH = "./config.ini"
application = Flask(__name__)
if os.path.exists(CONFIG_PATH):
config = ConfigParser()
config.read(CONFIG_PATH)
application.secret_key = config.get('ludo', 'secret_key').encode('utf-8')
application.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///ludo.db'
db = SQLAlchemy(application)
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
is_admin = db.Column(db.Boolean, nullable=False, default=False)
name = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(200), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
class Sub(db.Model):
__tablename__ = 'sub'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=False, nullable=False)
roll_id = db.Column(
db.Integer, db.ForeignKey('roll.id'), nullable=False
)
roll = db.relationship('Roll', back_populates="subs", foreign_keys=[roll_id])
class Roll(db.Model):
__tablename__ = 'roll'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=False, nullable=False)
roll_datetime = db.Column(db.DateTime, unique=False, nullable=False)
subs = db.relationship('Sub', back_populates="roll", foreign_keys=[Sub.roll_id])
winner_id = db.Column(db.Integer, db.ForeignKey('sub.id'), nullable=True)
winner = db.relationship('Sub', foreign_keys=[winner_id])
@property
def roll_date(self):
return self.roll_datetime.strftime('%Y-%m-%d')
@property
def roll_time(self):
return self.roll_datetime.strftime('%H:%M')
@property
def print_datetime(self):
return self.roll_datetime.strftime('%d/%m/%Y à %Hh%M')
def create_user(name, password, is_admin, email):
salt = hexlify(os.urandom(8))
password_hash = hexlify(pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt,
100_000
))
passwd = f'sha256:100000:{salt.decode("utf-8")}:{password_hash.decode("utf-8")}'
user = User(name=name, password=passwd, email=email, is_admin=is_admin)
db.session.add(user)
return user
def check_password(password, db_hash):
algo, iter_count, salt, hashed = db_hash.split(":")
return hashed == hexlify(
pbkdf2_hmac(
algo,
password.encode('utf-8'),
salt.encode('utf-8'),
int(iter_count),
)
).decode('utf-8')
@application.route("/")
def index():
to_roll = Roll.query.filter(
Roll.roll_datetime < datetime.now(),
Roll.winner == None
)
for roll in to_roll:
if not roll.subs:
Roll.filter_by(id=roll.id).delete()
else:
winner = random.choice(roll.subs)
roll.winner = winner
db.session.commit()
rolls = Roll.query.order_by(Roll.roll_datetime.desc()).all()
return render_template("index.html", rolls=rolls)
@application.route("/rolls", methods=['POST'])
def rolls():
if session.get("logged", False):
clean = True
if "roll_date" not in request.form or not request.form['roll_date']:
flash('La date de tirage est obligatoire', category='create_roll')
clean = False
if "roll_time" not in request.form or not request.form['roll_time']:
flash('L\'heure de tirage est obligatoire', category='create_roll')
clean = False
if "name" not in request.form or not request.form['name']:
flash('Le nom du tirage est obligatoire', category='create_roll')
clean = False
if not clean:
return redirect('/')
name = request.form['name']
try:
roll_datetime = datetime.strptime(
f"{request.form['roll_date']} {request.form['roll_time']}",
"%Y-%m-%d %H:%M"
)
except ValueError:
flash(
'La date ou l\'heure n\'est pas au bon format',
category='create_roll'
)
return redirect('/')
if roll_datetime < datetime.now():
flash(
'Le date de tirage ne peut pas être dans le passé',
category='create_roll'
)
return redirect('/')
roll = Roll(name=name, roll_datetime=roll_datetime)
db.session.add(roll)
db.session.commit()
return redirect('/')
@application.route("/rolls/<int:roll_id>", methods=['POST'])
def rolls_update(roll_id):
if session.get("logged", False):
roll = Roll.query.filter_by(id=roll_id).one_or_none()
clean = True
if "roll_date" not in request.form or not request.form['roll_date']:
flash('La date de tirage est obligatoire', category=roll_id)
clean = False
if "roll_time" not in request.form or not request.form['roll_time']:
flash('L\'heure de tirage est obligatoire', category=roll_id)
clean = False
if "name" not in request.form or not request.form['name']:
flash('Le nom du tirage est obligatoire', category=roll_id)
clean = False
if not roll:
clean = False
if roll and roll.roll_datetime > datetime.now():
flash('Les tirages passé ne sont pas editables', category=roll_id)
clean = False
if not clean:
return redirect('/')
name = request.form['name']
try:
roll_datetime = datetime.strptime(
f"{request.form['roll_date']} {request.form['roll_time']}",
"%Y-%m-%d %H:%M"
)
except ValueError:
flash(
'La date ou l\'heure n\'est pas au bon format',
category=roll_id
)
return redirect('/')
if roll_datetime < datetime.now():
flash(
'Le date de tirage ne peut pas être dans le passé',
category=roll_id
)
return redirect('/')
roll.roll_datetime = roll_datetime
roll.name = name
db.session.add(roll)
db.session.commit()
return redirect('/')
@application.route("/rolls/<int:roll_id>/subscribe", methods=['POST'])
def subscribe(roll_id):
if session.get("logged", False):
if "name" not in request.form or not request.form['name']:
flash('Le nom du participant est obligatoire', category=f'subscribe_{roll_id}')
return redirect('/')
name = request.form['name']
roll = Roll.query.filter(Roll.id == roll_id).one_or_none()
if not roll:
return redirect('/')
if Sub.query.filter_by(name=name, roll_id=roll_id).one_or_none():
flash('Ce participant est deja inscrit', category=f'subscribe_{roll_id}')
elif roll.roll_datetime < datetime.now():
flash('Il est trop tard pour inscrire un participant à ce tirage', category=f'subscribe_{roll_id}')
else:
sub = Sub(name=name, roll=roll)
db.session.add(sub)
db.session.commit()
return redirect('/')
@application.route("/unsubscribe")
def unsubscribe():
if session.get("logged", False):
if "sub" not in request.args or not request.args['sub']:
return redirect('/')
sub_id = request.args['sub']
sub = Sub.query.filter_by(id=sub_id).one_or_none()
if sub:
if not sub.roll.roll_datetime < datetime.now():
Sub.query.filter_by(id=sub_id).delete()
db.session.commit()
return redirect('/')
@application.route("/login", methods=['POST'])
def login():
form = request.form
user = User.query.filter_by(name=form['login']).one_or_none()
if user:
if check_password(form['password'], user.password):
session['logged'] = True
return redirect(form['callback'])
sleep(random.random())
return redirect(form['callback'])
@application.route("/logout")
def logout():
del session['logged']
return redirect(request.args['callback'])
@application.route("/style.css")
def send_style():
return send_from_directory("static", "style.css")
@application.cli.command("setup")
def setup():
config = ConfigParser()
if os.path.exists(CONFIG_PATH):
print('The app is already installed.')
config['ludo'] = {
'secret_key': hexlify(os.urandom(32)).decode('utf-8')
}
db.create_all()
name = input("Admin login: ")
email = input("Admin email: ")
while True:
password1 = getpass(prompt='Password: ')
password2 = getpass(prompt='Retype your password: ')
if password1 == password2:
break
print("The passwords dont match")
create_user(
name=name,
password=password1,
email=email,
is_admin=True
)
with open(CONFIG_PATH, 'w') as configfile:
config.write(configfile)
db.session.commit()
print('application setup done')
@application.cli.command("create_user")
def shell_create_user():
name = input("User login: ")
email = input("User email: ")
while True:
password1 = getpass(prompt='Password: ')
password2 = getpass(prompt='Retype your password: ')
if password1 == password2:
break
print("The passwords dont match")
create_user(
name=name,
password=password1,
email=email,
is_admin=False
)
db.session.commit()
if __name__ == "__main__":
application.run(debug=True)