291 lines
11 KiB
Python
291 lines
11 KiB
Python
import os
|
|
import re
|
|
from flask import Flask, render_template, redirect, url_for, request, flash
|
|
from flask_migrate import Migrate
|
|
from werkzeug.security import generate_password_hash, check_password_hash
|
|
from flask_login import (LoginManager, login_user, login_required, logout_user, current_user)
|
|
from misc import datetime, date, time, currDay, prevDay, ZoneInfo, currVersion, currCommit
|
|
from db import (db, Period, Task, Event, User)
|
|
from sqlalchemy import inspect
|
|
from forms import (TaskForm, EventForm, PeriodForm, SignupForm, LoginForm, SettingsForm)
|
|
from log import appLogger
|
|
|
|
from worker import runCreateEvents
|
|
|
|
# init logger
|
|
logger = appLogger('app')
|
|
|
|
basedir = os.path.abspath(os.path.dirname(__file__))
|
|
|
|
app = Flask(__name__)
|
|
app.config['SECRET_KEY'] = os.environ['SECRET_KEY']
|
|
app.config['SQLALCHEMY_DATABASE_URI'] =\
|
|
'mysql://' + os.environ['MYSQL_USER'] + \
|
|
':' + os.environ['MYSQL_PASSWORD'] + \
|
|
'@' + os.environ['MYSQL_HOST'] + \
|
|
':' + os.environ['MYSQL_PORT'] + \
|
|
'/' + os.environ['MYSQL_DB']
|
|
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
|
db.init_app(app)
|
|
|
|
# init migration
|
|
migrate = Migrate()
|
|
migrate.init_app(app, db)
|
|
|
|
# Authentication stuff
|
|
|
|
login_manager = LoginManager()
|
|
login_manager.login_view = 'login'
|
|
login_manager.init_app(app)
|
|
|
|
if 'SIGNUP_ENABLED' in os.environ:
|
|
if os.environ['SIGNUP_ENABLED'] == "YES":
|
|
signup_enabled = True
|
|
else:
|
|
signup_enabled = False
|
|
else:
|
|
signup_enabled = False
|
|
|
|
@login_manager.user_loader
|
|
def load_user(user_id):
|
|
return User.query.get(int(user_id))
|
|
|
|
# Context processor injects current version and commit
|
|
@app.context_processor
|
|
def injectVerCommit():
|
|
return dict(currVersion=currVersion, currCommit=currCommit, datetime=datetime, ZoneInfo=ZoneInfo)
|
|
|
|
# Error handling
|
|
# Pass env variables for debugging in prod
|
|
NODE_NAME = os.environ['NODE_NAME']
|
|
POD_NAME = os.environ['POD_NAME']
|
|
|
|
# Error Routes
|
|
@app.errorhandler(404)
|
|
def notFound(e):
|
|
return render_template('errors/404.html', NODE_NAME=NODE_NAME, POD_NAME=POD_NAME), 404
|
|
|
|
@app.errorhandler(403)
|
|
def forbidden(e):
|
|
return render_template('errors/403.html', NODE_NAME=NODE_NAME, POD_NAME=POD_NAME), 403
|
|
|
|
@app.errorhandler(500)
|
|
def ISEerror(e):
|
|
return render_template('errors/500.html', NODE_NAME=NODE_NAME, POD_NAME=POD_NAME), 500
|
|
|
|
# Pod Status route
|
|
@app.route('/podStatus')
|
|
def podStatus():
|
|
blankPage = ""
|
|
try:
|
|
db.engine.connect().close()
|
|
return blankPage, 200
|
|
except:
|
|
return blankPage, 500
|
|
|
|
# Index route
|
|
@app.route('/', methods=['GET', 'POST'])
|
|
def index():
|
|
# Check for empty database, go to setup page if not setup
|
|
global tablesSetup
|
|
required_tables = ['period', 'task', 'event', 'user']
|
|
inspector = inspect(db.engine)
|
|
tables = inspector.get_table_names()
|
|
tablesSetup = all(table in tables for table in required_tables)
|
|
if not tablesSetup:
|
|
# Initial setup page, should only appear if database is ready but not set up
|
|
db.create_all()
|
|
logger.info('DB initialized on first run')
|
|
return redirect(url_for('createAccount'))
|
|
# Otherwise, redirect to /events
|
|
else:
|
|
return redirect('/events')
|
|
|
|
# Authentication routes
|
|
@app.route('/login', methods=['GET', 'POST'])
|
|
def login():
|
|
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
|
|
form = LoginForm()
|
|
if form.validate_on_submit():
|
|
userName = form.userName.data
|
|
password = form.password.data
|
|
remember = form.rememberMe.data
|
|
|
|
user = User.query.filter_by(userName=userName).first()
|
|
if not user or not check_password_hash(user.password, password):
|
|
flash('Credentials incorrect! Please try again')
|
|
logger.info(f'User \'{userName}\' logon FAILED (bad password) from {sourceIP}')
|
|
return redirect(url_for('login'))
|
|
login_user(user, remember=remember)
|
|
logger.info(f'User \'{userName}\' logged in successfully from {sourceIP}')
|
|
return redirect(url_for('events'))
|
|
return render_template('login.html', form=form)
|
|
|
|
@app.route('/createaccount', methods=['GET', 'POST'])
|
|
def createAccount():
|
|
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
|
|
if signup_enabled == True:
|
|
form = SignupForm()
|
|
if form.validate_on_submit():
|
|
usernameExists = User.query.filter_by(userName=form.userName.data).first()
|
|
if usernameExists:
|
|
flash('Username already exists')
|
|
return redirect(url_for('createAccount'))
|
|
new_user = User(email=form.email.data, userName=form.userName.data, realName=form.realName.data,
|
|
password=generate_password_hash(form.password.data, method='sha256'))
|
|
db.session.add(new_user)
|
|
db.session.commit()
|
|
logger.info(f'New user \'{new_user.userName}\' created from {sourceIP}')
|
|
|
|
return redirect(url_for('login'))
|
|
return render_template('createAccount.html', form=form)
|
|
else:
|
|
return 'Account creation is currently disabled. <br> Set env var SIGNUP_ENABLED=YES to enable account creation'
|
|
@app.route('/logout')
|
|
@login_required
|
|
def logout():
|
|
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
|
|
logger.info(f'User \'{current_user.userName}\' logged out from {sourceIP} ')
|
|
logout_user()
|
|
return redirect(url_for('index'))
|
|
|
|
@app.route('/settings', methods=('GET', 'POST'))
|
|
@login_required
|
|
def settings():
|
|
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
|
|
user = User.query.get_or_404(current_user.id)
|
|
form = SettingsForm(obj=user)
|
|
if form.validate_on_submit():
|
|
user.realName = form.realName.data
|
|
user.timezone = form.timezone.data
|
|
if form.password.data != '':
|
|
user.password = generate_password_hash(form.password.data, method='sha256')
|
|
db.session.commit()
|
|
logger.info(f'User \'{current_user.userName}\' settings changed from {sourceIP}')
|
|
return render_template('settings.html', form=form)
|
|
|
|
# Periods routes
|
|
@app.route('/periods')
|
|
@login_required
|
|
def periods():
|
|
periods = Period.query.all()
|
|
return render_template('periods.html', periods=periods, datetime=datetime)
|
|
|
|
@app.route('/period/new', methods=('GET', 'POST'))
|
|
@login_required
|
|
def newPeriod():
|
|
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
|
|
form = PeriodForm()
|
|
if form.validate_on_submit():
|
|
period = Period(periodTime=form.periodTime.data,
|
|
weekendSchedule=form.weekendSchedule.data
|
|
)
|
|
db.session.add(period)
|
|
db.session.commit()
|
|
logger.info(f'New period added by \'{current_user.userName}\' from {sourceIP}')
|
|
# Run createEvents upon adding new period
|
|
runCreateEvents()
|
|
return redirect(f'/period/edit/{period.period}')
|
|
return render_template('newPeriod.html', form=form)
|
|
|
|
|
|
@app.route('/period/edit/<int:periodNum>', methods=('GET', 'POST'))
|
|
@login_required
|
|
def editPeriod(periodNum):
|
|
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
|
|
period = Period.query.get_or_404(periodNum)
|
|
form = PeriodForm(obj=period)
|
|
if form.validate_on_submit():
|
|
period.periodTime = form.periodTime.data
|
|
period.weekendSchedule = form.weekendSchedule.data
|
|
db.session.commit()
|
|
logger.info(f'Period {periodNum} edited by \'{current_user.userName}\' from {sourceIP}')
|
|
return redirect(f'/period/edit/{periodNum}')
|
|
return render_template('editPeriod.html', period=period, form=form, datetime=datetime)
|
|
|
|
@app.post('/period/delete/<int:periodNum>')
|
|
@login_required
|
|
def delete_period(periodNum):
|
|
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
|
|
period = Period.query.get_or_404(periodNum)
|
|
db.session.delete(period)
|
|
db.session.commit()
|
|
logger.info(f'Period {periodNum} deleted by \'{current_user.userName}\' from {sourceIP}')
|
|
return redirect('/periods')
|
|
|
|
|
|
# Events routes
|
|
@app.route('/events')
|
|
@login_required
|
|
def events():
|
|
events = Event.query.all()
|
|
periods = Period.query.all()
|
|
|
|
return render_template('events.html', events=events, periods=periods, datetime=datetime, date=date, ZoneInfo=ZoneInfo, re=re)
|
|
|
|
@app.route('/event/edit/<int:event_id>/', methods=('GET', 'POST'))
|
|
@login_required
|
|
def editEvent(event_id):
|
|
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
|
|
event = Event.query.get_or_404(event_id)
|
|
form = EventForm(obj=event)
|
|
if form.validate_on_submit():
|
|
if hasattr(form.selectedTask.data, 'id'):
|
|
event.task_id = form.selectedTask.data.id
|
|
else:
|
|
event.task_id = None
|
|
db.session.commit()
|
|
logger.info(f'Event {event_id} edited by \'{current_user.userName}\' from {sourceIP}')
|
|
return redirect('/events')
|
|
return render_template('editEvent.html', event=event, form=form, datetime=datetime)
|
|
|
|
# Tasks routes
|
|
@app.route('/tasks')
|
|
@login_required
|
|
def tasks():
|
|
tasks = Task.query.all()
|
|
return render_template('tasks.html', str=str, tasks=tasks, datetime=datetime, date=date, re=re)
|
|
|
|
@app.route('/task/<int:task_id>/')
|
|
@login_required
|
|
def task(task_id):
|
|
task = Task.query.get_or_404(task_id)
|
|
return render_template('task.html', str=str, task=task, datetime=datetime, date=date, re=re)
|
|
|
|
@app.route('/task/new', methods=('GET', 'POST'))
|
|
@login_required
|
|
def newTask():
|
|
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
|
|
form = TaskForm()
|
|
if form.validate_on_submit():
|
|
task = Task(title=form.title.data,
|
|
description=form.description.data,
|
|
created_timestamp=int(time.time()))
|
|
db.session.add(task)
|
|
db.session.commit()
|
|
logger.info(f'New task added by \'{current_user.userName}\' from {sourceIP}')
|
|
return redirect(f'/task/{task.id}')
|
|
return render_template('newtask.html', form=form)
|
|
|
|
@app.route('/task/<int:task_id>/edit', methods=('GET', 'POST'))
|
|
@login_required
|
|
def editTask(task_id):
|
|
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
|
|
task = Task.query.get_or_404(task_id)
|
|
form = TaskForm(obj=task)
|
|
if form.validate_on_submit():
|
|
task.title=form.title.data
|
|
task.description=form.description.data
|
|
db.session.commit()
|
|
logger.info(f'Task {task_id} edited by \'{current_user.userName}\' from {sourceIP}')
|
|
return redirect(f'/task/{task_id}')
|
|
return render_template('edittask.html', task=task, form=form)
|
|
@app.post('/task/<int:task_id>/delete')
|
|
@login_required
|
|
def delete_task(task_id):
|
|
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
|
|
task = Task.query.get_or_404(task_id)
|
|
db.session.delete(task)
|
|
db.session.commit()
|
|
logger.info(f'Task {task_id} deleted by \'{current_user.userName}\' from {sourceIP}')
|
|
return redirect('/tasks') |