From efb74ae413bc26d05ac6689d024d7f53a1cd14d2 Mon Sep 17 00:00:00 2001 From: Iain Learmonth Date: Wed, 11 May 2022 15:47:39 +0100 Subject: [PATCH] portal: allow list destruction fixes #15 --- app/portal/__init__.py | 67 ++------------------- app/portal/automation.py | 1 - app/portal/bridge.py | 2 +- app/portal/forms.py | 13 ----- app/portal/list.py | 91 +++++++++++++++++++++++++++++ app/portal/proxy.py | 2 +- app/portal/templates/base.html.j2 | 2 +- app/portal/templates/tables.html.j2 | 2 +- app/portal/util.py | 7 ++- 9 files changed, 107 insertions(+), 80 deletions(-) create mode 100644 app/portal/list.py diff --git a/app/portal/__init__.py b/app/portal/__init__.py index 75803b1..6e24a31 100644 --- a/app/portal/__init__.py +++ b/app/portal/__init__.py @@ -1,27 +1,27 @@ from datetime import datetime, timedelta, timezone -from flask import Blueprint, render_template, flash, redirect, url_for, request -from sqlalchemy import exc, desc, or_ +from flask import Blueprint, render_template, request +from sqlalchemy import desc, or_ -from app.extensions import db from app.models.alarms import Alarm from app import Origin, Proxy -from app.models.base import Group, MirrorList -from app.portal.forms import LifecycleForm, NewMirrorListForm +from app.models.base import Group +from app.portal.list import NewMirrorListForm from app.portal.automation import bp as automation from app.portal.bridgeconf import bp as bridgeconf from app.portal.bridge import bp as bridge from app.portal.group import bp as group +from app.portal.list import bp as list_ from app.portal.origin import bp as origin from app.portal.onion import bp as onion from app.portal.proxy import bp as proxy -from app.portal.util import response_404, view_lifecycle portal = Blueprint("portal", __name__, template_folder="templates", static_folder="static") portal.register_blueprint(automation, url_prefix="/automation") portal.register_blueprint(bridgeconf, url_prefix="/bridgeconf") portal.register_blueprint(bridge, url_prefix="/bridge") portal.register_blueprint(group, url_prefix="/group") +portal.register_blueprint(list_, url_prefix="/list") portal.register_blueprint(origin, url_prefix="/origin") portal.register_blueprint(onion, url_prefix="/onion") portal.register_blueprint(proxy, url_prefix="/proxy") @@ -74,58 +74,3 @@ def view_alarms(): items=alarms) -@portal.route('/lists') -def view_mirror_lists(): - mirrorlists = MirrorList.query.filter(MirrorList.destroyed == None).all() - return render_template("list.html.j2", - section="list", - title="Mirror Lists", - item="mirror list", - new_link=url_for("portal.new_mirror_list"), - items=mirrorlists) - - -@portal.route("/list/destroy/") -def destroy_mirror_list(list_id): - return "not implemented" - - -@portal.route("/list/new", methods=['GET', 'POST']) -@portal.route("/list/new/", methods=['GET', 'POST']) -def new_mirror_list(group_id=None): - form = NewMirrorListForm() - form.provider.choices = [ - ("github", "GitHub"), - ("gitlab", "GitLab"), - ("s3", "AWS S3"), - ] - form.format.choices = [ - ("bc2", "Bypass Censorship v2"), - ("bc3", "Bypass Censorship v3"), - ("bca", "Bypass Censorship Analytics"), - ("bridgelines", "Tor Bridge Lines") - ] - form.container.description = "GitHub Project, GitLab Project or AWS S3 bucket name." - form.branch.description = "Ignored for AWS S3." - if form.validate_on_submit(): - mirror_list = MirrorList() - mirror_list.provider = form.provider.data - mirror_list.format = form.format.data - mirror_list.description = form.description.data - mirror_list.container = form.container.data - mirror_list.branch = form.branch.data - mirror_list.filename = form.filename.data - mirror_list.created = datetime.utcnow() - mirror_list.updated = datetime.utcnow() - try: - db.session.add(mirror_list) - db.session.commit() - flash(f"Created new mirror list.", "success") - return redirect(url_for("portal.view_mirror_lists")) - except exc.SQLAlchemyError as e: - print(e) - flash("Failed to create new mirror list.", "danger") - return redirect(url_for("portal.view_mirror_lists")) - if group_id: - form.group.data = group_id - return render_template("new.html.j2", section="list", form=form) diff --git a/app/portal/automation.py b/app/portal/automation.py index df39003..6350b70 100644 --- a/app/portal/automation.py +++ b/app/portal/automation.py @@ -4,7 +4,6 @@ from flask import render_template, flash, Response, Blueprint from flask_wtf import FlaskForm from sqlalchemy import exc from wtforms import SubmitField, BooleanField -from wtforms.validators import DataRequired from app.extensions import db from app.models.automation import Automation diff --git a/app/portal/bridge.py b/app/portal/bridge.py index ba2490e..0928a03 100644 --- a/app/portal/bridge.py +++ b/app/portal/bridge.py @@ -2,7 +2,7 @@ from flask import render_template, Response, flash, redirect, url_for, Blueprint from app.extensions import db from app.models.bridges import Bridge -from app.portal.forms import LifecycleForm +from app.portal.util import LifecycleForm bp = Blueprint("bridge", __name__) diff --git a/app/portal/forms.py b/app/portal/forms.py index 9357e27..51be7c3 100644 --- a/app/portal/forms.py +++ b/app/portal/forms.py @@ -1,6 +1,5 @@ from flask_wtf import FlaskForm from wtforms import StringField, SubmitField, SelectField -from wtforms.validators import DataRequired class EditMirrorForm(FlaskForm): @@ -14,15 +13,3 @@ class EditProxyForm(FlaskForm): submit = SubmitField('Save Changes') -class LifecycleForm(FlaskForm): - submit = SubmitField('Confirm') - - -class NewMirrorListForm(FlaskForm): - provider = SelectField('Provider', validators=[DataRequired()]) - format = SelectField('Distribution Method', validators=[DataRequired()]) - description = StringField('Description', validators=[DataRequired()]) - container = StringField('Container', validators=[DataRequired()]) - branch = StringField('Branch') - filename = StringField('Filename', validators=[DataRequired()]) - submit = SubmitField('Save Changes') diff --git a/app/portal/list.py b/app/portal/list.py new file mode 100644 index 0000000..3e74005 --- /dev/null +++ b/app/portal/list.py @@ -0,0 +1,91 @@ +from datetime import datetime + +from flask import render_template, url_for, flash, redirect, Blueprint +from flask_wtf import FlaskForm +from sqlalchemy import exc +from wtforms import SelectField, StringField, SubmitField +from wtforms.validators import DataRequired + +from app import db +from app.models.base import MirrorList +from app.portal.util import response_404, view_lifecycle + +bp = Blueprint("list", __name__) + + +@bp.route('/list') +def list_list(): + lists = MirrorList.query.filter(MirrorList.destroyed == None).all() + return render_template("list.html.j2", + section="list", + title="Mirror Lists", + item="mirror list", + new_link=url_for("portal.list.list_new"), + items=lists) + + +@bp.route("/destroy/", methods=['GET', 'POST']) +def list_destroy(list_id: int): + list_ = MirrorList.query.filter(MirrorList.id == list_id, MirrorList.destroyed == None).first() + if list_ is None: + return response_404("The requested bridge configuration could not be found.") + return view_lifecycle( + header=f"Destroy mirror list?", + message=list_.description, + success_view="portal.list.list_list", + success_message="This list will no longer be updated and may be deleted depending on the provider.", + section="list", + resource=list_, + action="destroy" + ) + + +@bp.route("/new", methods=['GET', 'POST']) +@bp.route("/new/", methods=['GET', 'POST']) +def list_new(group_id=None): + form = NewMirrorListForm() + form.provider.choices = [ + ("github", "GitHub"), + ("gitlab", "GitLab"), + ("s3", "AWS S3"), + ] + form.format.choices = [ + ("bc2", "Bypass Censorship v2"), + ("bc3", "Bypass Censorship v3"), + ("bca", "Bypass Censorship Analytics"), + ("bridgelines", "Tor Bridge Lines") + ] + form.container.description = "GitHub Project, GitLab Project or AWS S3 bucket name." + form.branch.description = "Ignored for AWS S3." + if form.validate_on_submit(): + list_ = MirrorList() + list_.provider = form.provider.data + list_.format = form.format.data + list_.description = form.description.data + list_.container = form.container.data + list_.branch = form.branch.data + list_.filename = form.filename.data + list_.created = datetime.utcnow() + list_.updated = datetime.utcnow() + try: + db.session.add(list_) + db.session.commit() + flash(f"Created new mirror list.", "success") + return redirect(url_for("portal.list.list_list")) + except exc.SQLAlchemyError as e: + print(e) + flash("Failed to create new mirror list.", "danger") + return redirect(url_for("portal.list.list_list")) + if group_id: + form.group.data = group_id + return render_template("new.html.j2", section="list", form=form) + + +class NewMirrorListForm(FlaskForm): + provider = SelectField('Provider', validators=[DataRequired()]) + format = SelectField('Distribution Method', validators=[DataRequired()]) + description = StringField('Description', validators=[DataRequired()]) + container = StringField('Container', validators=[DataRequired()]) + branch = StringField('Branch') + filename = StringField('Filename', validators=[DataRequired()]) + submit = SubmitField('Save Changes') diff --git a/app/portal/proxy.py b/app/portal/proxy.py index 931457f..ad0defd 100644 --- a/app/portal/proxy.py +++ b/app/portal/proxy.py @@ -3,7 +3,7 @@ from sqlalchemy import desc from app.extensions import db from app.models.mirrors import Proxy -from app.portal.forms import LifecycleForm +from app.portal.util import LifecycleForm bp = Blueprint("proxy", __name__) diff --git a/app/portal/templates/base.html.j2 b/app/portal/templates/base.html.j2 index 1db9162..f99da04 100644 --- a/app/portal/templates/base.html.j2 +++ b/app/portal/templates/base.html.j2 @@ -90,7 +90,7 @@ diff --git a/app/portal/templates/tables.html.j2 b/app/portal/templates/tables.html.j2 index fd7cd3e..e2b691d 100644 --- a/app/portal/templates/tables.html.j2 +++ b/app/portal/templates/tables.html.j2 @@ -470,7 +470,7 @@ {{ list.url() }} {{ list.description }} - Destroy diff --git a/app/portal/util.py b/app/portal/util.py index e188fbf..bc0b839 100644 --- a/app/portal/util.py +++ b/app/portal/util.py @@ -1,8 +1,9 @@ from flask import Response, render_template, flash, redirect, url_for +from flask_wtf import FlaskForm +from wtforms import SubmitField from app import db from app.models import AbstractResource -from app.portal.forms import LifecycleForm def response_404(message: str): @@ -44,3 +45,7 @@ def view_lifecycle(*, message=message, section=section, form=form) + + +class LifecycleForm(FlaskForm): + submit = SubmitField('Confirm')