Merge pull request #13 from TakeusV1/DEV

V1.5
This commit is contained in:
Takeus authored and GitHub committed 2025-06-19 14:10:06 +02:00
commit 1d8b295e1a
11 files changed
+76 -24

No files matched your search

+2 -3
View File
@@ -10,12 +10,11 @@ app_debug = True
app_maintenance = False app_maintenance = False
app_name = 'takeus-ipam' app_name = 'takeus-ipam'
app_version = 'V1.4' app_version = 'V1.5'
app_release_date = '04/2025' app_release_date = '04/2025'
app_url = "http://0.0.0.0" app_url = "http://0.0.0.0"
app_theme = 'default' # default, lumen, simplex, zephyr app_theme = ['default', 'lumen', 'simplex', 'zephyr']
class Configuration: class Configuration:
# replace SECRET_KEY with value from "openssl rand -hex 32" or similar for STATIC sessions # replace SECRET_KEY with value from "openssl rand -hex 32" or similar for STATIC sessions
# With actual configuration, every application restart, user have to re-login... # With actual configuration, every application restart, user have to re-login...
+3
View File
@@ -7,14 +7,17 @@ class User(UserMixin, db.Model):
password = db.Column(db.String(32)) password = db.Column(db.String(32))
is_admin = db.Column(db.Boolean(),default=False) is_admin = db.Column(db.Boolean(),default=False)
is_reado = db.Column(db.Boolean(),default=False) is_reado = db.Column(db.Boolean(),default=False)
ui_theme = db.Column(db.String(16))
dat_last = db.Column(db.DateTime()) dat_last = db.Column(db.DateTime())
class Network(db.Model): class Network(db.Model):
id = db.Column(db.Integer(),primary_key=True,unique=True) id = db.Column(db.Integer(),primary_key=True,unique=True)
vlan_id = db.Column(db.Integer(),unique=True)
prefix = db.Column(db.Integer()) prefix = db.Column(db.Integer())
network_address = db.Column(db.String(16),unique=True) network_address = db.Column(db.String(16),unique=True)
hosts = db.Column(db.Integer()) hosts = db.Column(db.Integer())
description = db.Column(db.String(32)) description = db.Column(db.String(32))
is_blacklisted = db.Column(db.Boolean(),default=False)
class Allocation(db.Model): class Allocation(db.Model):
ipv4 = db.Column(db.String(16),primary_key=True,unique=True) ipv4 = db.Column(db.String(16),primary_key=True,unique=True)
+1 -1
View File
@@ -31,7 +31,7 @@ def login():
flash("Bad username or password !", 'danger') flash("Bad username or password !", 'danger')
return render_template("auth/login.html", title="Login", form=form, bad_password=True) return render_template("auth/login.html", title="Login", form=form, bad_password=True)
return render_template('auth/login.html', title="Login", form=form, app_theme=app_theme) return render_template('auth/login.html', title="Login", form=form)
@auth.route("/change",methods=['GET','POST']) @auth.route("/change",methods=['GET','POST'])
@login_required @login_required
+15 -6
View File
@@ -20,15 +20,16 @@ def home():
def dashboard(): def dashboard():
db_networks = Network.query.count() db_networks = Network.query.count()
db_allocations = Allocation.query.filter_by(is_used=True).count()
db_inventory = Inventory.query.count() db_inventory = Inventory.query.count()
db_users = User.query.count() db_users = User.query.count()
available_alloc = 0 available_alloc = 0
db_allocations = 0
for available_ip in Network.query.all(): for network in Network.query.all():
available_alloc += int(available_ip.hosts) if network.is_blacklisted == False:
available_alloc += int(network.hosts)
db_allocations += Allocation.query.filter_by(net_id=network.id, is_used=True).count()
print(app_theme)
return render_template( return render_template(
'panel/dashboard.html', 'panel/dashboard.html',
title="Dashboard", title="Dashboard",
@@ -41,6 +42,14 @@ def dashboard():
available_alloc=available_alloc, available_alloc=available_alloc,
db_users=db_users, db_users=db_users,
Inventory=Inventory, Inventory=Inventory,
navcolor='dark', navcolor='dark'
app_theme=app_theme
) )
@base.route("/theme/<string:name>",methods=['GET','POST'])
@login_required
def change_theme(name):
if name in app_theme:
current_user.ui_theme = name
db.session.commit()
return redirect(url_for('base.dashboard'))
return 'Invalid theme name', 400
+2 -3
View File
@@ -4,7 +4,6 @@ from flask_login import login_required, current_user
from app.extensions import * from app.extensions import *
from app.models import * from app.models import *
from app.forms import NewInventoryItem, ChangeInventoryItem from app.forms import NewInventoryItem, ChangeInventoryItem
from app._config import app_theme
inventory = Blueprint('inventory', __name__) inventory = Blueprint('inventory', __name__)
@@ -21,7 +20,7 @@ def add_item():
db.session.commit() db.session.commit()
flash("Item ADDED", 'success') flash("Item ADDED", 'success')
return render_template('panel/inventory_add.html', title='ADD ITEM', form=form, navcolor='success', app_theme=app_theme) return render_template('panel/inventory_add.html', title='ADD ITEM', form=form, navcolor='success')
@inventory.route('/list/<int:item_type>', methods=['GET','POST']) @inventory.route('/list/<int:item_type>', methods=['GET','POST'])
@login_required @login_required
@@ -44,7 +43,7 @@ def list_items(item_type):
else: else:
title = 'All' title = 'All'
return render_template('panel/inventory_view.html', title=title, item_list=db_items, current_page=item_type, navcolor='success', app_theme=app_theme) return render_template('panel/inventory_view.html', title=title, item_list=db_items, current_page=item_type, navcolor='success')
@inventory.route('/action/<int:item_id>/<int:action>', methods=['GET','POST']) @inventory.route('/action/<int:item_id>/<int:action>', methods=['GET','POST'])
@login_required @login_required
+28 -3
View File
@@ -2,7 +2,7 @@ from flask import Blueprint, request, render_template, flash, redirect, url_for,
from flask_login import login_required, current_user from flask_login import login_required, current_user
from ipaddress import IPv4Network, IPv4Address from ipaddress import IPv4Network, IPv4Address
from app._config import app_version, app_theme from app._config import app_version
from app.extensions import * from app.extensions import *
from app.models import * from app.models import *
@@ -36,7 +36,7 @@ def networks():
elif form.errors: elif form.errors:
flash(form.errors, 'danger') flash(form.errors, 'danger')
return render_template('panel/networks.html', title="Prefixes", is_networks=True, app_version=app_version, app_theme=app_theme, prefixes=networks, form=form, Allocation=Allocation, navcolor='dark') return render_template('panel/networks.html', title="Prefixes", is_networks=True, app_version=app_version, prefixes=networks, form=form, Allocation=Allocation, navcolor='dark')
@network.route('/<int:net_id>/allocations',methods=['GET','POST']) @network.route('/<int:net_id>/allocations',methods=['GET','POST'])
@login_required @login_required
@@ -60,7 +60,7 @@ def allocations(net_id):
db_network = Network.query.filter_by(id=net_id).first() db_network = Network.query.filter_by(id=net_id).first()
db_allocations = db.paginate(db.select(Allocation).filter_by(net_id=net_id), per_page=session['items'] ) db_allocations = db.paginate(db.select(Allocation).filter_by(net_id=net_id), per_page=session['items'] )
return render_template('panel/allocations.html', title="Allocations", is_networks=True, app_version=app_version, app_theme=app_theme, allocations=db_allocations, network=db_network, Inventory=Inventory, active_page=active_page, next_page=next_page, prev_page=prev_page, navcolor='dark') return render_template('panel/allocations.html', title="Allocations", is_networks=True, app_version=app_version, allocations=db_allocations, network=db_network, Inventory=Inventory, active_page=active_page, next_page=next_page, prev_page=prev_page, navcolor='dark')
## MODAL ROUTES ## MODAL ROUTES
@@ -202,6 +202,31 @@ def network_delete(net_id):
flash("Network Not Found", 'danger') flash("Network Not Found", 'danger')
return redirect(url_for('network.networks')) return redirect(url_for('network.networks'))
@network.get('/<int:net_id>/blacklist/<action>')
@login_required
def network_blacklist(net_id, action):
if current_user.is_reado:
return abort(403)
db_network = Network.query.filter_by(id=net_id).first()
if db_network == None:
flash("Network Not Found", 'danger')
return redirect(url_for('network.networks'))
if action == 'blacklist':
db_network.is_blacklisted = True
db.session.commit()
flash("Network Blacklisted", 'warning')
elif action == 'unblacklist':
db_network.is_blacklisted = False
db.session.commit()
flash("Network Unblacklisted", 'success')
return redirect(url_for('network.networks'))
## HTMX SEARCH ## HTMX SEARCH
@network.post('/<int:net_id>/allocations/search') @network.post('/<int:net_id>/allocations/search')
@login_required @login_required
+3 -3
View File
@@ -11,11 +11,11 @@
<!-- LINKS --> <!-- LINKS -->
<link rel="icon" type="image/png" href="{{ url_for('static',filename='img/logo.png') }}" /> <link rel="icon" type="image/png" href="{{ url_for('static',filename='img/logo.png') }}" />
<link rel="stylesheet" href="{{ url_for('static',filename='css/custom.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/custom.css') }}"/>
{% if app_theme == 'zephyr'%} {% if current_user.ui_theme == 'zephyr'%}
<link rel="stylesheet" href="{{ url_for('static',filename='css/Bootstrap/zephyr.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/Bootstrap/zephyr.css') }}"/>
{% elif app_theme == 'lumen' %} {% elif current_user.ui_theme == 'lumen' %}
<link rel="stylesheet" href="{{ url_for('static',filename='css/Bootstrap/lumen.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/Bootstrap/lumen.css') }}"/>
{% elif app_theme == 'simplex' %} {% elif current_user.ui_theme == 'simplex' %}
<link rel="stylesheet" href="{{ url_for('static',filename='css/Bootstrap/simplex.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/Bootstrap/simplex.css') }}"/>
{% else %} {% else %}
<link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.min.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.min.css') }}"/>
+3 -3
View File
@@ -11,11 +11,11 @@
<!-- LINKS --> <!-- LINKS -->
<link rel="icon" type="image/png" href="{{ url_for('static',filename='img/logo.png') }}" /> <link rel="icon" type="image/png" href="{{ url_for('static',filename='img/logo.png') }}" />
<link rel="stylesheet" href="{{ url_for('static',filename='css/custom.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/custom.css') }}"/>
{% if app_theme == 'zephyr' %} {% if current_user.ui_theme == 'zephyr' %}
<link rel="stylesheet" href="{{ url_for('static',filename='css/Bootstrap/zephyr.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/Bootstrap/zephyr.css') }}"/>
{% elif app_theme == 'lumen' %} {% elif current_user.ui_theme == 'lumen' %}
<link rel="stylesheet" href="{{ url_for('static',filename='css/Bootstrap/lumen.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/Bootstrap/lumen.css') }}"/>
{% elif app_theme == 'simplex' %} {% elif current_user.ui_theme == 'simplex' %}
<link rel="stylesheet" href="{{ url_for('static',filename='css/Bootstrap/simplex.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/Bootstrap/simplex.css') }}"/>
{% else %} {% else %}
<link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.min.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.min.css') }}"/>
+13 -2
View File
@@ -30,8 +30,19 @@
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
<div class="d-flex"> <div class="d-grid gap-2 d-md-flex justify-content-md-end">
<a class="btn btn-outline-warning" type="submit" href="{{ url_for('auth.logout') }}">logout</a> <div class="btn-group dropstart">
<button type="button" class="btn btn-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
UI
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{{ url_for('base.change_theme',name='default') }}">default</a></li>
<li><a class="dropdown-item" href="{{ url_for('base.change_theme',name='zephyr') }}">Zephyr</a></li>
<li><a class="dropdown-item" href="{{ url_for('base.change_theme',name='simplex') }}">Simplex</a></li>
<li><a class="dropdown-item" href="{{ url_for('base.change_theme',name='lumen') }}">Lumen</a></li>
</ul>
</div>
<a class="btn btn-outline-warning btn-sm" type="submit" href="{{ url_for('auth.logout') }}">logout</a>
</div> </div>
</div> </div>
</div> </div>
+5
View File
@@ -28,6 +28,11 @@
<td> <td>
<a href="{{ url_for('network.allocations',net_id=network.id) }}?page=1" class="btn btn-sm btn-outline-info" style="padding-top:1px;padding-bottom:1px">Allocations</a> <a href="{{ url_for('network.allocations',net_id=network.id) }}?page=1" class="btn btn-sm btn-outline-info" style="padding-top:1px;padding-bottom:1px">Allocations</a>
{% if current_user.is_reado == False %} {% if current_user.is_reado == False %}
{% if network.is_blacklisted %}
<a href="{{ url_for('network.network_blacklist',net_id=network.id,action='unblacklist') }}" class="btn btn-sm btn-outline-warning" style="padding-top:1px;padding-bottom:1px">UnBlacklist</a>
{% else %}
<a href="{{ url_for('network.network_blacklist',net_id=network.id,action='blacklist') }}" class="btn btn-sm btn-outline-warning" style="padding-top:1px;padding-bottom:1px">Blacklist</a>
{% endif %}
<button <button
hx-get="{{ url_for('network.network_del_modal',net_id=network.id) }}" hx-get="{{ url_for('network.network_del_modal',net_id=network.id) }}"
hx-trigger="click" hx-trigger="click"
+1
View File
@@ -14,6 +14,7 @@ class User(db.Model):
password = db.Column(db.String(32)) password = db.Column(db.String(32))
is_admin = db.Column(db.Boolean(),default=False) is_admin = db.Column(db.Boolean(),default=False)
is_reado = db.Column(db.Boolean(),default=False) is_reado = db.Column(db.Boolean(),default=False)
ui_theme = db.Column(db.String(16))
dat_last = db.Column(db.DateTime()) dat_last = db.Column(db.DateTime())
with app.app_context(): with app.app_context():