resource pool system

This commit is contained in:
Iain Learmonth 2022-09-26 13:40:59 +01:00
parent dc989dd7cb
commit 16f7e2199d
19 changed files with 382 additions and 105 deletions

View file

@ -21,6 +21,7 @@ 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.pool import bp as pool
from app.portal.proxy import bp as proxy
from app.portal.smart_proxy import bp as smart_proxy
from app.portal.webhook import bp as webhook
@ -34,6 +35,7 @@ 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(pool, url_prefix="/pool")
portal.register_blueprint(proxy, url_prefix="/proxy")
portal.register_blueprint(smart_proxy, url_prefix="/smart")
portal.register_blueprint(webhook, url_prefix="/webhook")

View file

@ -74,6 +74,7 @@ def automation_kick(automation_id: int) -> ResponseReturnValue:
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,

View file

@ -6,8 +6,3 @@ class EditMirrorForm(FlaskForm): # type: ignore
origin = SelectField('Origin')
url = StringField('URL')
submit = SubmitField('Save Changes')
class EditProxyForm(FlaskForm): # type: ignore
origin = SelectField('Origin')
submit = SubmitField('Save Changes')

View file

@ -1,6 +1,6 @@
import json
from datetime import datetime
from typing import Optional
from typing import Optional, Any
from flask import render_template, url_for, flash, redirect, Blueprint, Response
from flask.typing import ResponseReturnValue
@ -13,7 +13,7 @@ from app.extensions import db
from app.lists.bc2 import mirror_sites
from app.lists.bridgelines import bridgelines
from app.lists.mirror_mapping import mirror_mapping
from app.models.base import MirrorList
from app.models.base import MirrorList, Pool
from app.portal.util import response_404, view_lifecycle
bp = Blueprint("list", __name__)
@ -96,6 +96,7 @@ def list_new(group_id: Optional[int] = None) -> ResponseReturnValue:
form.encoding.choices = list(MirrorList.encodings_supported.items())
if form.validate_on_submit():
list_ = MirrorList()
list_.pool_id = form.pool.data
list_.provider = form.provider.data
list_.format = form.format.data
list_.encoding = form.encoding.data
@ -122,6 +123,7 @@ def list_new(group_id: Optional[int] = None) -> ResponseReturnValue:
class NewMirrorListForm(FlaskForm): # type: ignore
pool = SelectField('Resource Pool', validators=[DataRequired()])
provider = SelectField('Provider', validators=[DataRequired()])
format = SelectField('Distribution Method', validators=[DataRequired()])
encoding = SelectField('Encoding', validators=[DataRequired()])
@ -136,6 +138,12 @@ class NewMirrorListForm(FlaskForm): # type: ignore
filename = StringField('Filename', validators=[DataRequired()])
submit = SubmitField('Save Changes')
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.pool.choices = [
(pool.id, pool.pool_name) for pool in Pool.query.all()
]
@bp.route('/edit/<list_id>', methods=['GET', 'POST'])
def list_edit(list_id: int) -> ResponseReturnValue:
@ -160,6 +168,7 @@ def list_edit(list_id: int) -> ResponseReturnValue:
form.format.choices = list(MirrorList.formats_supported.items())
form.encoding.choices = list(MirrorList.encodings_supported.items())
if form.validate_on_submit():
list_.pool_id = form.pool.data
list_.provider = form.provider.data
list_.format = form.format.data
list_.encoding = form.encoding.data

78
app/portal/pool.py Normal file
View file

@ -0,0 +1,78 @@
from datetime import datetime
from flask import render_template, url_for, flash, redirect, Response, Blueprint
from flask.typing import ResponseReturnValue
from flask_wtf import FlaskForm
import sqlalchemy
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from app.extensions import db
from app.models.base import Pool
bp = Blueprint("pool", __name__)
class NewPoolForm(FlaskForm): # type: ignore
group_name = StringField("Short Name", validators=[DataRequired()])
description = StringField("Description", validators=[DataRequired()])
submit = SubmitField('Save Changes', render_kw={"class": "btn btn-success"})
class EditPoolForm(FlaskForm): # type: ignore
description = StringField("Description", validators=[DataRequired()])
submit = SubmitField('Save Changes', render_kw={"class": "btn btn-success"})
@bp.route("/list")
def pool_list() -> ResponseReturnValue:
pools = Pool.query.order_by(Pool.pool_name).all()
return render_template("list.html.j2",
section="pool",
title="Resource Pools",
item="pool",
items=pools,
new_link=url_for("portal.pool.pool_new"))
@bp.route("/new", methods=['GET', 'POST'])
def pool_new() -> ResponseReturnValue:
form = NewPoolForm()
if form.validate_on_submit():
pool = Pool()
pool.pool_name = form.group_name.data
pool.description = form.description.data
pool.created = datetime.utcnow()
pool.updated = datetime.utcnow()
try:
db.session.add(pool)
db.session.commit()
flash(f"Created new pool {pool.pool_name}.", "success")
return redirect(url_for("portal.pool.pool_edit", pool_id=pool.id))
except sqlalchemy.exc.SQLAlchemyError:
flash("Failed to create new pool.", "danger")
return redirect(url_for("portal.pool.pool_list"))
return render_template("new.html.j2", section="pool", form=form)
@bp.route('/edit/<pool_id>', methods=['GET', 'POST'])
def pool_edit(pool_id: int) -> ResponseReturnValue:
pool = Pool.query.filter(Pool.id == pool_id).first()
if pool is None:
return Response(render_template("error.html.j2",
section="pool",
header="404 Pool Not Found",
message="The requested pool could not be found."),
status=404)
form = EditPoolForm(description=pool.description)
if form.validate_on_submit():
pool.description = form.description.data
pool.updated = datetime.utcnow()
try:
db.session.commit()
flash("Saved changes to pool.", "success")
except sqlalchemy.exc.SQLAlchemyError:
flash("An error occurred saving the changes to the pool.", "danger")
return render_template("pool.html.j2",
section="pool",
pool=pool, form=form)

View file

@ -87,6 +87,12 @@
{{ icon("collection") }} Groups
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if section == "pool" %} active{% endif %}"
href="{{ url_for("portal.pool.pool_list") }}">
{{ icon("stack") }} Resource Pools
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if section == "origin" %} active{% endif %}"
href="{{ url_for("portal.origin.origin_list") }}">

View file

@ -98,5 +98,10 @@
<path d="M1.333 6.334v3C1.333 10.805 4.318 12 8 12s6.667-1.194 6.667-2.667V6.334a6.51 6.51 0 0 1-1.458.79C11.81 7.684 9.967 8 8 8c-1.966 0-3.809-.317-5.208-.876a6.508 6.508 0 0 1-1.458-.79z"/>
<path d="M14.667 11.668a6.51 6.51 0 0 1-1.458.789c-1.4.56-3.242.876-5.21.876-1.966 0-3.809-.316-5.208-.876a6.51 6.51 0 0 1-1.458-.79v1.666C1.333 14.806 4.318 16 8 16s6.667-1.194 6.667-2.667v-1.665z"/>
</svg>
{% elif i == "stack" %}
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-stack" viewBox="0 0 16 16">
<path d="m14.12 10.163 1.715.858c.22.11.22.424 0 .534L8.267 15.34a.598.598 0 0 1-.534 0L.165 11.555a.299.299 0 0 1 0-.534l1.716-.858 5.317 2.659c.505.252 1.1.252 1.604 0l5.317-2.66zM7.733.063a.598.598 0 0 1 .534 0l7.568 3.784a.3.3 0 0 1 0 .535L8.267 8.165a.598.598 0 0 1-.534 0L.165 4.382a.299.299 0 0 1 0-.535L7.733.063z"/>
<path d="m14.12 6.576 1.715.858c.22.11.22.424 0 .534l-7.568 3.784a.598.598 0 0 1-.534 0L.165 7.968a.299.299 0 0 1 0-.534l1.716-.858 5.317 2.659c.505.252 1.1.252 1.604 0l5.317-2.659z"/>
</svg>
{% endif %}
{% endmacro %}

View file

@ -1,7 +1,7 @@
{% extends "base.html.j2" %}
{% from "tables.html.j2" import alarms_table, automations_table, bridgeconfs_table, bridges_table, eotk_table,
groups_table, instances_table, mirrorlists_table, origins_table, origin_onion_table, onions_table, proxies_table,
webhook_table %}
groups_table, instances_table, mirrorlists_table, origins_table, origin_onion_table, onions_table, pools_table,
proxies_table, webhook_table %}
{% block content %}
<h1 class="h2 mt-3">{{ title }}</h1>
@ -33,6 +33,8 @@
{% endif %}
{% elif item == "origin" %}
{{ origins_table(items) }}
{% elif item == "pool" %}
{{ pools_table(items) }}
{% elif item == "proxy" %}
{{ proxies_table(items) }}
{% elif item == "smart proxy" %}

View file

@ -0,0 +1,21 @@
{% extends "base.html.j2" %}
{% from 'bootstrap5/form.html' import render_form %}
{% from "tables.html.j2" import groups_table, mirrorlists_table, proxies_table %}
{% block content %}
<h1 class="h2 mt-3">Resource Pool</h1>
<h2 class="h3">{{ pool.pool_name }}</h2>
<div style="border: 1px solid #666;" class="p-3">
{{ render_form(form) }}
</div>
<h3>Groups</h3>
{{ groups_table(pool.groups) }}
<h3>Distribution Lists</h3>
{{ mirrorlists_table(pool.lists) }}
<h3>Simple Proxies</h3>
{{ proxies_table(pool.proxies) }}
{% endblock %}

View file

@ -334,6 +334,29 @@
</div>
{% endmacro %}
{% macro pools_table(pools) %}
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Description</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{% for pool in pools %}
<tr>
<td>{{ pool.pool_name }}</td>
<td>{{ pool.description }}</td>
<td><a href="{{ url_for("portal.pool.pool_edit", pool_id=pool.id) }}" class="btn btn-primary btn-sm">View/Edit</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endmacro %}
{% macro proxies_table(proxies) %}
<div class="table-responsive">
<table class="table table-striped table-sm">