from datetime import datetime, timezone from typing import Optional from flask import Blueprint, Response, current_app, flash, render_template from flask.typing import ResponseReturnValue from flask_wtf import FlaskForm from sqlalchemy import desc, exc from wtforms import BooleanField, SubmitField from app.extensions import db from app.models.automation import Automation, AutomationLogs from app.models.tfstate import TerraformState from app.portal.util import response_404, view_lifecycle bp = Blueprint("automation", __name__) _SECTION_TEMPLATE_VARS = { "section": "automation", "help_url": "https://bypass.censorship.guide/user/automation.html" } class EditAutomationForm(FlaskForm): # type: ignore enabled = BooleanField('Enabled') submit = SubmitField('Save Changes') @bp.route("/list") def automation_list() -> ResponseReturnValue: automations = list(filter( lambda a: a.short_name not in current_app.config.get('HIDDEN_AUTOMATIONS', []), Automation.query.filter( Automation.destroyed.is_(None)).order_by(Automation.description).all() )) states = {tfs.key: tfs for tfs in TerraformState.query.all()} return render_template("list.html.j2", title="Automation Jobs", item="automation", items=automations, states=states, **_SECTION_TEMPLATE_VARS) @bp.route('/edit/', methods=['GET', 'POST']) def automation_edit(automation_id: int) -> ResponseReturnValue: automation: Optional[Automation] = Automation.query.filter(Automation.id == automation_id).first() if automation is None: return Response(render_template("error.html.j2", header="404 Automation Job Not Found", message="The requested automation job could not be found.", **_SECTION_TEMPLATE_VARS), status=404) form = EditAutomationForm(enabled=automation.enabled) if form.validate_on_submit(): automation.enabled = form.enabled.data automation.updated = datetime.now(tz=timezone.utc) try: db.session.commit() flash("Saved changes to bridge configuration.", "success") except exc.SQLAlchemyError: flash("An error occurred saving the changes to the bridge configuration.", "danger") logs = AutomationLogs.query.filter(AutomationLogs.automation_id == automation.id).order_by( desc(AutomationLogs.added)).limit(5).all() return render_template("automation.html.j2", automation=automation, logs=logs, form=form, **_SECTION_TEMPLATE_VARS) @bp.route("/kick/", methods=['GET', 'POST']) def automation_kick(automation_id: int) -> ResponseReturnValue: automation = Automation.query.filter( Automation.id == automation_id, Automation.destroyed.is_(None)).first() if automation is None: return response_404("The requested bridge configuration could not be found.") return view_lifecycle( header="Kick automation timer?", message=automation.description, section="automation", success_view="portal.automation.automation_list", success_message="This automation job will next run within 1 minute.", resource=automation, action="kick" )