This commit is contained in:
Takeus committed 2025-02-03 19:49:58 +01:00
1 parent f11aa2d6c6
commit 85969350bc
13 files changed
+146 -29

No files matched your search

-3
View File
@@ -1,8 +1,5 @@
from flask_login import LoginManager from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from flask_turnstile import Turnstile
login_manager = LoginManager() login_manager = LoginManager()
db = SQLAlchemy() db = SQLAlchemy()
turnstile = Turnstile()
+8 -3
View File
@@ -1,7 +1,12 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField from wtforms import StringField, PasswordField, IntegerField
from wtforms.validators import DataRequired, Length from wtforms.validators import DataRequired, Length, IPAddress, NumberRange
class LoginForm(FlaskForm): class LoginForm(FlaskForm):
username = StringField(validators=[DataRequired(),Length(max=128)]) username = StringField(validators=[DataRequired(),Length(max=128)])
password = PasswordField(validators=[DataRequired(),Length(max=128)]) password = PasswordField(validators=[DataRequired(),Length(max=128)])
class NewPrefixForm(FlaskForm):
subnet = StringField(validators=[DataRequired(),IPAddress(ipv4=True),Length(max=128)])
prefix = IntegerField(validators=[DataRequired(),NumberRange(min=0,max=32)])
description = StringField(validators=[DataRequired(),Length(max=128)])
+8 -1
View File
@@ -6,4 +6,11 @@ class User(UserMixin, db.Model):
username = db.Column(db.String(16)) username = db.Column(db.String(16))
password = db.Column(db.String(32),unique=True) password = db.Column(db.String(32),unique=True)
is_admin = db.Column(db.Boolean(),default=False) is_admin = db.Column(db.Boolean(),default=False)
dat_last = db.Column(db.DateTime()) dat_last = db.Column(db.DateTime())
class Prefix(db.Model):
id = db.Column(db.Integer(),primary_key=True,unique=True)
prefix = db.Column(db.Integer())
network_address = db.Column(db.String(16),unique=True)
hosts = db.Column(db.Integer())
description = db.Column(db.String(32),unique=True)
+31 -1
View File
@@ -1,11 +1,13 @@
from datetime import datetime from datetime import datetime
from flask import Blueprint, request, render_template, flash, redirect, url_for 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
from app._config import app_version from app._config import app_version
from app.extensions import * from app.extensions import *
from app.models import * from app.models import *
from app.forms import NewPrefixForm
base = Blueprint('base', __name__) base = Blueprint('base', __name__)
@@ -20,4 +22,32 @@ def home():
@login_required @login_required
def dashboard(): def dashboard():
return render_template('panel/dashboard.html', title="Dashboard", is_dash=True, app_version=app_version) return render_template('panel/dashboard.html', title="Dashboard", is_dash=True, app_version=app_version)
@base.route('/networks',methods=['GET','POST'])
@login_required
def networks():
prefixes = Prefix.query.all()
form = NewPrefixForm()
if request.method == 'POST' and form.validate_on_submit():
try:
network = IPv4Network(f"{str(form.subnet.data)}/{str(form.prefix.data)}")
add_prefix = Prefix(network_address=str(network.network_address),prefix=network.prefixlen,hosts=network.num_addresses,description=str(form.description.data))
db.session.add(add_prefix)
db.session.commit()
return f"done {network.num_addresses}"
except:
flash("Invalid Subnet or Prefix", 'danger')
return redirect(url_for('base.networks'))
elif form.errors:
flash(form.errors, 'danger')
return render_template('panel/networks.html', title="Prefixes", is_networks=True, app_version=app_version, prefixes=prefixes, form=form)
@base.route('/networks/allocations/<string:network>',methods=['GET','POST'])
@login_required
def allocations(network):
return f"Allocations for {network}"
+5
View File
@@ -0,0 +1,5 @@
/*!
* Bootstrap Icons v1.11.3 (https://icons.getbootstrap.com/)
* Copyright 2019-2024 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE)
*/@font-face{font-display:block;font-family:bootstrap-icons;src:url("/static/fonts/bootstrap-icons.woff2?dd67030699838ea613ee6dbda90effa6") format("woff2"),url("/static/fonts/bootstrap-icons.woff?dd67030699838ea613ee6dbda90effa6") format("woff")}.bi::before,[class*=" bi-"]::before,[class^=bi-]::before{display:inline-block;font-family:bootstrap-icons!important;font-style:normal;font-weight:400!important;font-variant:normal;text-transform:none;line-height:1;vertical-align:-.125em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.bi-123::before{content:"\f67f"}.bi-alarm-fill::before{content:"\f101"}.bi-alarm::before{content:"\f102"}.bi-align-bottom::before{content:"\f103"}.bi-align-center::before{content:"\f104"}.bi-align-end::before{content:"\f105"}.bi-align-middle::before{content:"\f106"}.bi-align-start::before{content:"\f107"}.bi-align-top::before{content:"\f108"}.bi-alt::before{content:"\f109"}.bi-app-indicator::before{content:"\f10a"}.bi-app::before{content:"\f10b"}.bi-archive-fill::before{content:"\f10c"}.bi-archive::before{content:"\f10d"}.bi-arrow-90deg-down::before{content:"\f10e"}.bi-arrow-90deg-left::before{content:"\f10f"}.bi-arrow-90deg-right::before{content:"\f110"}.bi-arrow-90deg-up::before{content:"\f111"}.bi-arrow-bar-down::before{content:"\f112"}.bi-arrow-bar-left::before{content:"\f113"}.bi-arrow-bar-right::before{content:"\f114"}.bi-arrow-bar-up::before{content:"\f115"}.bi-arrow-clockwise::before{content:"\f116"}.bi-arrow-counterclockwise::before{content:"\f117"}.bi-arrow-down-circle-fill::before{content:"\f118"}.bi-arrow-down-circle::before{content:"\f119"}.bi-arrow-down-left-circle-fill::before{content:"\f11a"}.bi-arrow-down-left-circle::before{content:"\f11b"}.bi-arrow-down-left-square-fill::before{content:"\f11c"}.bi-arrow-down-left-square::before{content:"\f11d"}.bi-arrow-down-left::before{content:"\f11e"}.bi-arrow-down-right-circle-fill::before{content:"\f11f"}.bi-arrow-down-right-circle::before{content:"\f120"}.bi-arrow-down-right-square-fill::before{content:"\f121"}.bi-arrow-down-right-square::before{content:"\f122"}.bi-arrow-down-right::before{content:"\f123"}.bi-arrow-down-short::before{content:"\f124"}.bi-arrow-down-square-fill::before{content:"\f125"}.bi-arrow-down-square::before{content:"\f126"}.bi-arrow-down-up::before{content:"\f127"}.bi-arrow-down::before{content:"\f128"}.bi-arrow-left-circle-fill::before{content:"\f129"}.bi-arrow-left-circle::before{content:"\f12a"}.bi-arrow-left-right::before{content:"\f12b"}.bi-arrow-left-short::before{content:"\f12c"}.bi-arrow-left-square-fill::before{content:"\f12d"}.bi-arrow-left-square::before{content:"\f12e"}.bi-arrow-left::before{content:"\f12f"}.bi-arrow-repeat::before{content:"\f130"}.bi-arrow-return-left::before{content:"\f131"}.bi-arrow-return-right::before{content:"\f132"}.bi-arrow-right-circle-fill::before{content:"\f133"}.bi-arrow-right-circle::before{content:"\f134"}.bi-arrow-right-short::before{content:"\f135"}.bi-arrow-right-square-fill::before{content:"\f136"}.bi-arrow-right-square::before{content:"\f137"}.bi-arrow-right::before{content:"\f138"}.bi-arrow-up-circle-fill::before{content:"\f139"}.bi-arrow-up-circle::before{content:"\f13a"}.bi-arrow-up-left-circle-fill::before{content:"\f13b"}.bi-arrow-up-left-circle::before{content:"\f13c"}.bi-arrow-up-left-square-fill::before{content:"\f13d"}.bi-arrow-up-left-square::before{content:"\f13e"}.bi-arrow-up-left::before{content:"\f13f"}.bi-arrow-up-right-circle-fill::before{content:"\f140"}.bi-arrow-up-right-circle::before{content:"\f141"}.bi-arrow-up-right-square-fill::before{content:"\f142"}.bi-arrow-up-right-square::before{content:"\f143"}.bi-arrow-up-right::before{content:"\f144"}.bi-arrow-up-short::before{content:"\f145"}.bi-arrow-up-square-fill::before{content:"\f146"}.bi-arrow-up-square::before{content:"\f147"}.bi-arrow-up::before{content:"\f148"}.bi-arrows-angle-contract::before{content:"\f149"}.bi-arrows-angle-expand::before{content:"\f14a"}.bi-arrows-collapse::before{content:"\f14b"}.bi-arrows-expand::before{content:"\f14c"}.bi-arrows-fullscreen::before{content:"\f14d"}.bi-arrows-move::before{content:"\f14e"}.bi-aspect-ratio-fill::before{content:"\f14f"}.bi-aspect-ratio::before{content:"\f150"}.bi-asterisk::before{content:"\f151"}.bi-at::before{content:"\f152"}.bi-award-fill::before{content:"\f153"}.bi-award::before{content:"\f154"}.bi-back::before{content:"\f155"}.bi-backspace-fill::before{content:"\f156"}.bi-backspace-reverse-fill::before{content:"\f157"}.bi-backspace-reverse::before{content:"\f158"}.bi-backspace::before{content:"\f159"}.bi-badge-3d-fill::before{content:"\f15a"}.bi-badge-3d::before{content:"\f15b"}.bi-badge-4k-fill::before{content:"\f15c"}.bi-badge-4k::before{content:"\f15d"}.bi-badge-8k-fill::before{content:"\f15e"}.bi-badge-8k::before{content:"\f15f"}.bi-badge-ad-fill::before{content:"\f160"}.bi-badge-ad::before{content:"\f161"}.bi-badge-ar-fill::before{content:"\f162"}.bi-badge-ar::before{content:"\f163"}.bi-badge-cc-fill::beLine truncated
+4
View File
@@ -0,0 +1,4 @@
btn-exsm {
padding-top: 1px;
padding-bottom: 1px;
}
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -12,7 +12,7 @@
<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') }}"/>
<link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.min.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.min.css') }}"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"> <link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap-icons.min.css') }}"/>
</head> </head>
<body class=" d-flex flex-column"> <body class=" d-flex flex-column">
<style> <style>
+1 -1
View File
@@ -12,7 +12,7 @@
<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') }}"/>
<link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.min.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.min.css') }}"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"> <link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap-icons.min.css') }}"/>
</head> </head>
<body> <body>
{% block content %}{% endblock %} {% block content %}{% endblock %}
+12
View File
@@ -0,0 +1,12 @@
<!-- Flash message -->
{% with errors = get_flashed_messages(with_categories=true,category_filter=["success","warning","danger","error"]) %}
{% if errors %}
{% for category, message in errors %}
<div class="alert alert-dismissible alert-{{ category }} m-0" style="position:fixed;top:5%;left:38%;z-index:10;min-width:25%;">
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
<p class="mb-0"><b>{{ message }}</b></a></p>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<!-- END|Flash message -->
+15 -19
View File
@@ -7,30 +7,26 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link {% if is_dash %}active{% endif %}" href="{{ url_for('base.dashboard') }}">Dashboard</a> <a class="nav-link {% if is_dash %}active{% endif %}" href="{{ url_for('base.dashboard') }}"><i class="bi bi-house-door-fill"></i> Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link {% if is_networks %}active{% endif %}" href="{{ url_for('base.networks') }}"><i class="bi bi-globe"></i> Network(s)</a>
</li> </li>
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">Network</a> <a class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-archive-fill"></i> Inventory</a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Subnets</a></li> <li><a class="dropdown-item" href="#"><i class="bi bi-pc-display"></i> Computers</a></li>
<li><a class="dropdown-item" href="#">Attributions</a></li> <li><a class="dropdown-item" href="#"><i class="bi bi-server"></i> Servers</a></li>
<li><a class="dropdown-item" href="#"><i class="bi bi-server"></i> Virtual Machines</a></li>
<li><a class="dropdown-item" href="#"><i class="bi bi-ethernet"></i> Networks Devices</a></li>
<li><a class="dropdown-item" href="#"><i class="bi bi-collection"></i> Others</a></li>
</ul> </ul>
</li> </li>
<li class="nav-item dropdown"> {% if current_user.admin %}
<a class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-archive-fill"></i> Inventory</a> <li class="nav-item">
<ul class="dropdown-menu"> <a class="nav-link active" href="">Admin</a>
<li><a class="dropdown-item" href="#"><i class="bi bi-pc-display"></i> Computers</a></li> </li>
<li><a class="dropdown-item" href="#"><i class="bi bi-server"></i> Servers</a></li> {% endif %}
<li><a class="dropdown-item" href="#"><i class="bi bi-server"></i> Virtual Machines</a></li>
<li><a class="dropdown-item" href="#"><i class="bi bi-ethernet"></i> Networks Devices</a></li>
<li><a class="dropdown-item" href="#"><i class="bi bi-collection"></i> Others</a></li>
</ul>
</li>
{% if current_user.admin %}
<li class="nav-item">
<a class="nav-link active" href="">Admin</a>
</li>
{% endif %}
</ul> </ul>
<form class="d-flex" role="search"> <form class="d-flex" role="search">
<a class="btn btn-outline-warning" type="submit" href="{{ url_for('auth.logout') }}">logout</a> <a class="btn btn-outline-warning" type="submit" href="{{ url_for('auth.logout') }}">logout</a>
+61
View File
@@ -0,0 +1,61 @@
{% extends 'panel/_base.html' %}
{% block content %}
{% include 'panel/_nav.html' %}
{% include 'panel/_flash.html' %}
<div class="container" style="margin-top:5vh;">
<div class="row">
<!-- PREFIXES TABLE -->
<div class="col-9">
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Subnet</th>
<th scope="col">Prefix</th>
<th scope="col">Usage</th>
<th scope="col">Description</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{% for network in prefixes %}
<tr>
<th scope="row">{{network.id}}</th>
<td>{{network.network_address}}</td>
<td>{{network.prefix}}</td>
<td>X / {{network.hosts}} </td>
<td>{{network.description}}</td>
<td>
<button class="btn btn-sm btn-outline-info" style="padding-top:1px;padding-bottom:1px">calculate</button>
<button class="btn btn-sm btn-outline-danger"style="padding-top:1px;padding-bottom:1px">delete</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- PREFIX FORM -->
<div class="col-3">
<div class="card p-4 shadow rounded-3">
<h2 class="text-center mb-4">Add Network</h2>
<form method="POST">
{{ form.csrf_token }}
<div class="mb-3">
<label for="subnet" class="form-label">Subnet</label>
{{ form.subnet(class="form-control", placeholder="192.168.0.0") }}
</div>
<div class="mb-3">
<label for="prefix" class="form-label">Prefix</label>
{{ form.prefix(class="form-control", placeholder="24") }}
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
{{ form.description(class="form-control", placeholder="local area network...") }}
</div>
<button type="submit" class="btn btn-secondary w-100">Submit</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}