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 forms import (TaskForm, EventForm, PeriodForm, SignupForm, LoginForm, SettingsForm) from create_events import createEvents from log import appLogger # 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('/') def index(): 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' @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 createEvents(db, currDay, Period, Event) return redirect(f'/period/edit/{period.period}') return render_template('newPeriod.html', form=form) @app.route('/period/edit/', 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/') @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//', 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//') @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//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//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')