mirror of
https://github.com/TakeusV1/flask_ipam.git
synced 2026-10-13 17:15:33 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1523fc977d | ||
|
|
969b7dac2f | ||
|
|
a19f59e9a2 | ||
|
|
6bae5cec91 | ||
|
|
275c575e90 | ||
|
|
2956b0abed | ||
|
|
5651121181 | ||
|
|
88c26c931c | ||
|
|
67094b541a | ||
|
|
e2380e6919 | ||
|
|
a5c9664bf1 | ||
|
|
bd40a774d7 | ||
|
|
47d9078473 | ||
|
|
55aaeae1da | ||
|
|
621b2b681a | ||
|
|
1c8ec0c159 | ||
|
|
7d60ce672b | ||
|
|
f3b6270504 | ||
|
|
eaa6499bd0 | ||
|
|
1464767d4e | ||
|
|
a25eb0337a | ||
|
|
c819bf68e2 | ||
|
|
4f2270ff3e | ||
|
|
ce14203857 | ||
|
|
02df3a3aca | ||
|
|
bb1fe1ed17 | ||
|
|
33e2f83470 | ||
|
|
f0c1109a8b | ||
|
|
eda44fa57a | ||
|
|
5bf33d178e | ||
|
|
91c9319147 | ||
|
|
9351f51c1e | ||
|
|
06b7efd58b | ||
|
|
eefc23fad8 |
No files matched your search
+12
@@ -0,0 +1,12 @@
|
||||
FROM python:3.13.2-alpine3.21
|
||||
WORKDIR /fipam
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
COPY app /fipam/app
|
||||
COPY instance /fipam/instance
|
||||
ENV APP_HOME=/fipam
|
||||
ENV PYTHONPATH=${APP_HOME}
|
||||
ENV PATH="$PATH:${APP_HOME}/.local/bin"
|
||||
ENV GUNICORN_CMD_ARGS="--bind=0.0.0.0:5000 --workers=2 --preload"
|
||||
EXPOSE 5000
|
||||
ENTRYPOINT ["gunicorn","app:create_app()"]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Flask IPAM (FIPAM)
|
||||
An IP address management application _(Passive)_ written in **python** with the Flask web framework (and modules).<br>
|
||||
The application is designed to manage **small networks** (**/30** to **/22**) destined for use in a **LAB**. <br>
|
||||
_(I'm not an experienced developer, just a tinkerer)_
|
||||
|
||||
---
|
||||

|
||||

|
||||
|
||||
---
|
||||

|
||||

|
||||
|
||||
---
|
||||
- Installation : [here](install.md)
|
||||
- Sources : [here](sources.md)
|
||||
|
||||
## Docker Compose
|
||||
The Docker image has a built in admin account, **PLEASE CHANGE PASSWORD AFTER INITIAL LOG IN**
|
||||
- Username: admin
|
||||
- Password: _PleaseChangeMe_
|
||||
<br>
|
||||
Use docker-compose.yml file to deploy.
|
||||
@@ -10,6 +10,8 @@ from app.models import *
|
||||
from app.extensions import *
|
||||
|
||||
from app.routes.r_base import base
|
||||
from app.routes.r_networks import network
|
||||
from app.routes.r_inventory import inventory
|
||||
from app.routes.r_auth import auth
|
||||
from app.routes.r_adm import admin
|
||||
|
||||
@@ -30,6 +32,8 @@ def create_app():
|
||||
|
||||
## BLUEPRINTS
|
||||
app.register_blueprint(base, url_prefix='/')
|
||||
app.register_blueprint(network, url_prefix='/network')
|
||||
app.register_blueprint(inventory, url_prefix='/inventory')
|
||||
app.register_blueprint(auth, url_prefix='/auth')
|
||||
app.register_blueprint(admin, url_prefix='/admin')
|
||||
|
||||
|
||||
+7
-4
@@ -1,6 +1,8 @@
|
||||
##########################################################
|
||||
############# CONFIGURATION FILE | Flask IPAM ############
|
||||
|
||||
import random, string
|
||||
|
||||
## Flask Server (For DEV)
|
||||
app_ip = '127.0.0.1'
|
||||
app_port = 80
|
||||
@@ -8,13 +10,14 @@ app_debug = True
|
||||
app_maintenance = False
|
||||
|
||||
app_name = 'takeus-ipam'
|
||||
app_version = 'V1.1'
|
||||
app_version = 'V1.3'
|
||||
app_release_date = '02/2025'
|
||||
app_url = "http://127.0.0.1"
|
||||
app_url = "http://0.0.0.0"
|
||||
|
||||
class Configuration:
|
||||
# openssl rand -hex 32
|
||||
SECRET_KEY = 'change_me'
|
||||
# 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...
|
||||
SECRET_KEY = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(32))
|
||||
# DB CONFIG
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///database.db'
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
|
||||
+26
-9
@@ -1,6 +1,6 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, PasswordField, IntegerField, SelectField
|
||||
from wtforms.validators import DataRequired, Length, IPAddress, NumberRange
|
||||
from wtforms import StringField, PasswordField, IntegerField, SelectField, TextAreaField
|
||||
from wtforms.validators import DataRequired, Length, IPAddress
|
||||
|
||||
class LoginForm(FlaskForm):
|
||||
username = StringField(validators=[DataRequired(),Length(max=128)])
|
||||
@@ -11,22 +11,39 @@ class NewPrefixForm(FlaskForm):
|
||||
prefix = SelectField(choices=[(30,'/30'),(29,'/29'),(28,'/28'),(27,'/27'),(26,'/26'),(25,'/25'),(24,'/24'),(23,'/23'),(22,'/22')],validators=[DataRequired()])
|
||||
description = StringField(validators=[Length(max=128)])
|
||||
|
||||
class NewAllocationForm(FlaskForm):
|
||||
ipv4 = StringField(validators=[DataRequired(),IPAddress(ipv4=True),Length(max=128)])
|
||||
host_name = StringField(validators=[Length(max=128)])
|
||||
host_desc = StringField(validators=[Length(max=128)])
|
||||
#host_type = SelectField(choices=[(0,'None'),(1,'Computer'),(2,'Server'),(3,'Virtual Machine'),(4,'Network Device'),(5,'Other')],validators=[DataRequired()])
|
||||
|
||||
class NewUserForm (FlaskForm):
|
||||
class NewUserForm(FlaskForm):
|
||||
username = StringField(validators=[DataRequired(),Length(max=128)])
|
||||
password = PasswordField(validators=[DataRequired(),Length(max=128)])
|
||||
is_admin = SelectField(choices=[(0,'No'),(1,'Yes')],validators=[DataRequired()])
|
||||
is_readonly = SelectField(choices=[(0,'No'),(1,'Yes')],validators=[DataRequired()])
|
||||
|
||||
class ChangePasswordForm(FlaskForm):
|
||||
password = PasswordField(validators=[DataRequired(),Length(max=128)])
|
||||
is_admin = SelectField(choices=[(0,'No'),(1,'Yes')],validators=[DataRequired()])
|
||||
is_readonly = SelectField(choices=[(0,'No'),(1,'Yes')],validators=[DataRequired()])
|
||||
|
||||
class ChangeAllocationForm(FlaskForm):
|
||||
hostname = StringField(validators=[Length(max=128)])
|
||||
description = StringField(validators=[Length(max=128)])
|
||||
is_special = SelectField(choices=[(0,'None'),(1,'DHCP'),(2,'Gateway')],validators=[DataRequired()],label='Special USE ?')
|
||||
|
||||
class NewInventoryItem(FlaskForm):
|
||||
item_name = StringField(validators=[DataRequired(),Length(max=64)])
|
||||
item_desc = StringField(validators=[Length(max=64)])
|
||||
item_type = SelectField(choices=[(1,'Computer'),(2,'Server'),(3,'Virtual Machine'), (4,'Network Device'), (0,'Other')],validators=[DataRequired()])
|
||||
|
||||
class ChangeInventoryItem(FlaskForm):
|
||||
item_name = StringField(validators=[DataRequired(),Length(max=64)])
|
||||
item_desc = StringField(validators=[Length(max=64)])
|
||||
item_type = SelectField(choices=[(1,'Computer'),(2,'Server'),(3,'Virtual Machine'), (4,'Network Device'), (0,'Other')],validators=[DataRequired()])
|
||||
# Hardware Information
|
||||
item_model = StringField(validators=[Length(max=64)])
|
||||
item_manufacturer = StringField(validators=[Length(max=64)])
|
||||
item_serial_number = StringField(validators=[Length(max=64)])
|
||||
# Network Information
|
||||
item_os = StringField(validators=[Length(max=64)])
|
||||
item_location = StringField(validators=[Length(max=64)])
|
||||
item_owner = StringField(validators=[Length(max=64)])
|
||||
item_contact = StringField(validators=[Length(max=64)])
|
||||
# Allocation(s)
|
||||
item_ip_allocations = StringField()
|
||||
+13
-11
@@ -3,9 +3,10 @@ from flask_login import UserMixin
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
id = db.Column(db.Integer(),primary_key=True,unique=True)
|
||||
username = db.Column(db.String(16))
|
||||
password = db.Column(db.String(32),unique=True)
|
||||
username = db.Column(db.String(16),unique=True)
|
||||
password = db.Column(db.String(32))
|
||||
is_admin = db.Column(db.Boolean(),default=False)
|
||||
is_reado = db.Column(db.Boolean(),default=False)
|
||||
dat_last = db.Column(db.DateTime())
|
||||
|
||||
class Network(db.Model):
|
||||
@@ -28,17 +29,18 @@ class Allocation(db.Model):
|
||||
|
||||
class Inventory(db.Model):
|
||||
id = db.Column(db.Integer(),primary_key=True,unique=True)
|
||||
ip_addr = db.Column(db.Text())
|
||||
# Host Information
|
||||
host_type = db.Column(db.Integer())
|
||||
host_name = db.Column(db.String(32))
|
||||
host_name = db.Column(db.String(64))
|
||||
host_desc = db.Column(db.String(64))
|
||||
# Hardware Information
|
||||
host_model = db.Column(db.String(32))
|
||||
host_manufacturer = db.Column(db.String(32))
|
||||
host_serialnumber = db.Column(db.String(32))
|
||||
host_model = db.Column(db.String(64))
|
||||
host_manufacturer = db.Column(db.String(64))
|
||||
host_serialnumber = db.Column(db.String(64))
|
||||
# Network Information
|
||||
host_os = db.Column(db.String(32))
|
||||
host_location = db.Column(db.String(32))
|
||||
host_owner = db.Column(db.String(32))
|
||||
host_contact = db.Column(db.String(32))
|
||||
host_os = db.Column(db.String(64))
|
||||
host_location = db.Column(db.String(64))
|
||||
host_owner = db.Column(db.String(64))
|
||||
host_contact = db.Column(db.String(64))
|
||||
## Allocation(s)
|
||||
host_allocations = db.Column(db.Text())
|
||||
+7
-1
@@ -26,10 +26,13 @@ def home():
|
||||
return redirect(url_for('admin.home'))
|
||||
|
||||
is_admin = False
|
||||
is_readonly = False
|
||||
if int(form.is_admin.data) == 1:
|
||||
is_admin = True
|
||||
if int(form.is_readonly.data) == 1:
|
||||
is_readonly = True
|
||||
|
||||
db.session.add(User(username=form.username.data, password=generate_password_hash(form.password.data), is_admin=is_admin))
|
||||
db.session.add(User(username=form.username.data, password=generate_password_hash(form.password.data), is_admin=is_admin, is_reado=is_readonly))
|
||||
db.session.commit()
|
||||
flash("User Created", 'success')
|
||||
return redirect(url_for('admin.home'))
|
||||
@@ -67,8 +70,11 @@ def user_modal_edit(user_id):
|
||||
if form.validate_on_submit() and request.method == 'POST':
|
||||
user.password = generate_password_hash(form.password.data)
|
||||
user.is_admin = False
|
||||
user.is_reado = False
|
||||
if int(form.is_admin.data) == 1:
|
||||
user.is_admin = True
|
||||
if int(form.is_readonly.data) == 1:
|
||||
user.is_reado = True
|
||||
db.session.commit()
|
||||
flash("Informations Changed", 'success')
|
||||
return redirect(url_for('admin.home'))
|
||||
|
||||
+4
-262
@@ -1,24 +1,21 @@
|
||||
from datetime import datetime
|
||||
from flask import Blueprint, request, render_template, flash, redirect, url_for, session
|
||||
from flask import Blueprint, render_template, redirect, url_for
|
||||
from flask_login import login_required, current_user
|
||||
from ipaddress import IPv4Network, IPv4Address
|
||||
|
||||
from app._config import app_version
|
||||
|
||||
from app.extensions import *
|
||||
from app.models import *
|
||||
from app.forms import NewPrefixForm, NewAllocationForm, ChangeAllocationForm
|
||||
|
||||
base = Blueprint('base', __name__)
|
||||
|
||||
@base.route('/',methods=['GET','POST'])
|
||||
@base.route('/')
|
||||
def home():
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('base.dashboard'))
|
||||
else:
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
@base.route('/dashboard',methods=['GET','POST'])
|
||||
@base.route('/dashboard')
|
||||
@login_required
|
||||
def dashboard():
|
||||
|
||||
@@ -42,261 +39,6 @@ def dashboard():
|
||||
db_inventory=db_inventory,
|
||||
available_alloc=available_alloc,
|
||||
db_users=db_users,
|
||||
Inventory=Inventory,
|
||||
navcolor='dark'
|
||||
)
|
||||
|
||||
@base.route('/network',methods=['GET','POST'])
|
||||
@login_required
|
||||
def networks():
|
||||
networks = Network.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)}", False)
|
||||
|
||||
add_net = Network(network_address=str(network.network_address),prefix=network.prefixlen,hosts=network.num_addresses-2,description=str(form.description.data))
|
||||
db.session.add(add_net)
|
||||
db.session.commit()
|
||||
|
||||
for ip in network.hosts():
|
||||
db.session.add(Allocation(ipv4=str(ip), net_id=add_net.id))
|
||||
|
||||
db.session.commit()
|
||||
return redirect(url_for('base.networks'))
|
||||
|
||||
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=networks, form=form, Allocation=Allocation, navcolor='dark')
|
||||
|
||||
@base.route('/network/<int:net_id>/allocations',methods=['GET','POST'])
|
||||
@login_required
|
||||
def allocations(net_id):
|
||||
|
||||
active_page = request.args.get('page', type=int)
|
||||
if active_page == None:
|
||||
active_page = 1
|
||||
|
||||
next_page = active_page+1
|
||||
prev_page = active_page-1
|
||||
|
||||
if request.args.get('items', type=int) != None:
|
||||
session['items'] = request.args.get('items', type=int)
|
||||
try:
|
||||
if session['items'] == None:
|
||||
session['items'] = 15
|
||||
except:
|
||||
session['items'] = 15
|
||||
|
||||
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'] )
|
||||
|
||||
form = NewAllocationForm()
|
||||
##if request.method == 'POST' and form.validate_on_submit():
|
||||
## network = IPv4Network(f"{str(db_network.network_address)}/{str(db_network.prefix)}")
|
||||
# Check if IPv4 Address is already allocated
|
||||
## if Allocation.query.filter_by(ipv4=str(form.ipv4.data)).first() != None:
|
||||
## flash("IPv4 Address Already Allocated", 'danger')
|
||||
## return redirect(url_for('base.allocations', net_id=net_id, page=active_page))
|
||||
## # Check if IPv4 Address is in the Network
|
||||
## if IPv4Address(form.ipv4.data) in network.hosts():
|
||||
## add_alloc = Allocation(ipv4=str(form.ipv4.data), net_id=db_network.id, hostname=str(form.host_name.data), description=str(form.host_desc.data))
|
||||
## db.session.add(add_alloc)
|
||||
## db.session.commit()
|
||||
## flash("Allocation Added", 'success')
|
||||
## return redirect(url_for('base.allocations', net_id=net_id, page=active_page))
|
||||
# If not in Network
|
||||
## flash("Invalid IPv4 Address", 'danger')
|
||||
## return redirect(url_for('base.allocations', net_id=net_id, page=active_page))
|
||||
# If Form Errors
|
||||
##elif form.errors:
|
||||
## flash(form.errors, 'danger')
|
||||
|
||||
return render_template('panel/allocations.html', title="Allocations", is_networks=True, app_version=app_version, form=form, allocations=db_allocations, network=db_network, Inventory=Inventory, active_page=active_page, next_page=next_page, prev_page=prev_page, navcolor='dark')
|
||||
|
||||
## MODAL ROUTES
|
||||
|
||||
@base.get('/network/<int:net_id>/del_modal')
|
||||
@login_required
|
||||
def network_del_modal(net_id):
|
||||
db_network = Network.query.filter_by(id=net_id).first()
|
||||
modal = {
|
||||
'title': 'Delete Network',
|
||||
'body': f'Are you sure you want to delete <b>{db_network.network_address}/{db_network.prefix}</b> network ?',
|
||||
'color': 'danger',
|
||||
'action': 'delete',
|
||||
'action_text': 'Delete',
|
||||
'action_url': url_for('base.network_delete',net_id=net_id)
|
||||
}
|
||||
return render_template('panel/_modal.html', modal=modal, net_id=net_id)
|
||||
|
||||
@base.get('/network/<int:net_id>/allocations/<string:ipv4>/del_modal')
|
||||
@login_required
|
||||
def allocation_del_modal(net_id,ipv4):
|
||||
modal = {
|
||||
'title': 'Remove Allocation',
|
||||
'body': f'Are you sure you want to remove <b>{ipv4}</b> allocation ?',
|
||||
'color': 'warning',
|
||||
'action': 'delete',
|
||||
'action_text': 'Remove',
|
||||
'action_url': url_for('base.allocation_delete',net_id=net_id,ipv4=ipv4)
|
||||
}
|
||||
return render_template('panel/_modal.html', modal=modal, net_id=net_id, ipv4=ipv4, old_page=request.args.get('page', type=int), pagination_active=True)
|
||||
|
||||
|
||||
@base.route('/network/<int:net_id>/allocations/<string:ipv4>/edit_modal', methods=['GET','POST'])
|
||||
@login_required
|
||||
def allocation_edit_modal(net_id, ipv4):
|
||||
db_alloc = Allocation.query.filter_by(ipv4=ipv4).first()
|
||||
|
||||
form = ChangeAllocationForm()
|
||||
if form.validate_on_submit() and request.method == 'POST':
|
||||
db_alloc.hostname = str(form.hostname.data)
|
||||
db_alloc.description = str(form.description.data)
|
||||
db_alloc.is_used = True
|
||||
# Special Allocation
|
||||
if int(form.is_special.data) == 1:
|
||||
# DHCP
|
||||
db_alloc.is_dhcp = True
|
||||
db_alloc.is_gateway = False
|
||||
db_alloc.hostname = None
|
||||
db_alloc.description = "DHCP Allocation"
|
||||
elif int(form.is_special.data) == 2:
|
||||
# Gateway
|
||||
db_alloc.is_dhcp = False
|
||||
db_alloc.is_gateway = True
|
||||
else:
|
||||
# None
|
||||
db_alloc.is_dhcp = False
|
||||
db_alloc.is_gateway = False
|
||||
|
||||
db.session.commit()
|
||||
flash("Allocation Updated", 'success')
|
||||
return redirect(url_for('base.allocations',net_id=net_id,page=request.args.get('page', type=int)))
|
||||
|
||||
modal = {
|
||||
'title': 'Update Allocation',
|
||||
'body': f'Change informations for <b>{db_alloc.ipv4}</b> allocation.',
|
||||
'color': 'primary',
|
||||
'modal_type': 'form',
|
||||
'action_text': 'Save',
|
||||
'form_url': url_for('base.allocation_edit_modal',net_id=db_alloc.net_id,ipv4=db_alloc.ipv4),
|
||||
}
|
||||
old_page = request.args.get('page', type=int)
|
||||
if old_page == 0:
|
||||
old_page = 1
|
||||
return render_template('panel/_modal.html', modal=modal, form=form, old_page=old_page, pagination_active=True)
|
||||
|
||||
## MODIFICATION ROUTES
|
||||
@base.get('/network/<int:net_id>/allocations/<string:ipv4>/unallocate')
|
||||
@login_required
|
||||
def allocation_delete(net_id,ipv4):
|
||||
db_alloc = Allocation.query.filter_by(net_id=net_id,ipv4=ipv4).first()
|
||||
db_alloc.is_used = False
|
||||
db_alloc.hostname = None
|
||||
db_alloc.description = None
|
||||
db_alloc.is_dhcp = False
|
||||
db_alloc.is_gateway = False
|
||||
db.session.commit()
|
||||
flash("Allocation Removed", 'warning')
|
||||
return redirect(url_for('base.allocations',net_id=net_id,page=request.args.get('page', type=int)))
|
||||
|
||||
@base.get('/network/<int:net_id>/allocations/<string:ipv4>/decom/<int:action>')
|
||||
@login_required
|
||||
def allocation_decom(net_id,ipv4,action):
|
||||
db_alloc = Allocation.query.filter_by(net_id=net_id,ipv4=ipv4).first()
|
||||
if action == 1:
|
||||
db_alloc.is_decom = True
|
||||
else:
|
||||
db_alloc.is_decom = False
|
||||
db.session.commit()
|
||||
return redirect(url_for('base.allocations',net_id=net_id,page=request.args.get('page', type=int)))
|
||||
|
||||
@base.get('/network/<int:net_id>/delete')
|
||||
@login_required
|
||||
def network_delete(net_id):
|
||||
db_network = Network.query.filter_by(id=net_id).first()
|
||||
db_alloc = Allocation.query.filter_by(net_id=db_network.id).all()
|
||||
|
||||
if db_network != None:
|
||||
if db_alloc != None:
|
||||
for alloc in db_alloc:
|
||||
db.session.delete(alloc)
|
||||
|
||||
db.session.delete(db_network)
|
||||
db.session.commit()
|
||||
flash("Network Deleted", 'warning')
|
||||
return redirect(url_for('base.networks'))
|
||||
else:
|
||||
flash("Network Not Found", 'danger')
|
||||
return redirect(url_for('base.networks'))
|
||||
|
||||
## HTMX SEARCH
|
||||
@base.post('/network/<int:net_id>/allocations/search')
|
||||
@login_required
|
||||
def allocation_search(net_id):
|
||||
search = str(request.form['search']).lower()
|
||||
|
||||
db_network = Network.query.filter_by(id=net_id).first()
|
||||
db_alloc = Allocation.query.filter_by(net_id=net_id).all()
|
||||
obj_list = []
|
||||
|
||||
# Advance Search
|
||||
if '*' in search:
|
||||
search = search.split("*")[1]
|
||||
# AND
|
||||
if ',' in search:
|
||||
for alloc in db_alloc:
|
||||
if alloc.ipv4.lower() in search.split(','):
|
||||
obj_list.append(alloc)
|
||||
# BETWEEN
|
||||
if '-' in search:
|
||||
search = search.split('-')
|
||||
if search[0].split('.')[-1] == '' or search[1].split('.')[-1] == '':
|
||||
return "Invalid(s) Number(s)"
|
||||
temp_1 = int(search[0].split('.')[-1])
|
||||
temp_2 = int(search[1].split('.')[-1])
|
||||
|
||||
for alloc in db_alloc:
|
||||
if int(alloc.ipv4.split('.')[-1]) <= temp_2 and int(alloc.ipv4.split('.')[-1]) >= temp_1:
|
||||
obj_list.append(alloc)
|
||||
# DHCP
|
||||
if 'dhcp' in search:
|
||||
for alloc in db_alloc:
|
||||
if alloc.is_dhcp:
|
||||
obj_list.append(alloc)
|
||||
|
||||
## GATEWAY
|
||||
if 'gateway' in search:
|
||||
for alloc in db_alloc:
|
||||
if alloc.is_gateway:
|
||||
obj_list.append(alloc)
|
||||
|
||||
## DECOM
|
||||
if 'decom' in search:
|
||||
for alloc in db_alloc:
|
||||
if alloc.is_decom:
|
||||
obj_list.append(alloc)
|
||||
|
||||
## DECOM
|
||||
if 'used' in search:
|
||||
for alloc in db_alloc:
|
||||
if alloc.is_used:
|
||||
obj_list.append(alloc)
|
||||
|
||||
else:
|
||||
# BASIC SEARCH
|
||||
for alloc in db_alloc:
|
||||
if alloc.ipv4.lower().startswith(search):
|
||||
obj_list.append(alloc)
|
||||
elif alloc.hostname != None:
|
||||
if alloc.hostname.lower().startswith(search):
|
||||
obj_list.append(alloc)
|
||||
elif alloc.description != None:
|
||||
if alloc.description.lower().startswith(search):
|
||||
obj_list.append(alloc)
|
||||
|
||||
return render_template('panel/_table-alloc.html',allocations=obj_list,Inventory=Inventory,network=db_network)
|
||||
@@ -0,0 +1,234 @@
|
||||
from flask import Blueprint, request, render_template, flash, redirect, url_for, abort
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from app.extensions import *
|
||||
from app.models import *
|
||||
from app.forms import NewInventoryItem, ChangeInventoryItem
|
||||
|
||||
inventory = Blueprint('inventory', __name__)
|
||||
|
||||
@inventory.route('/add', methods=['GET','POST'])
|
||||
@login_required
|
||||
def add_item():
|
||||
if current_user.is_reado:
|
||||
return abort(403)
|
||||
|
||||
form = NewInventoryItem()
|
||||
if form.validate_on_submit() and request.method == 'POST':
|
||||
add_net = Inventory(host_type=int(form.item_type.data),host_name=str(form.item_name.data),host_desc=str(form.item_desc.data))
|
||||
db.session.add(add_net)
|
||||
db.session.commit()
|
||||
flash("Item ADDED", 'success')
|
||||
|
||||
return render_template('panel/inventory_add.html', title='ADD ITEM', form=form, navcolor='success')
|
||||
|
||||
@inventory.route('/list/<int:item_type>', methods=['GET','POST'])
|
||||
@login_required
|
||||
def list_items(item_type):
|
||||
if item_type == 5:
|
||||
db_items = Inventory.query.all()
|
||||
else:
|
||||
db_items = Inventory.query.filter_by(host_type=item_type).all()
|
||||
|
||||
if item_type == 0:
|
||||
title = 'Others'
|
||||
elif item_type == 1:
|
||||
title = 'Computers'
|
||||
elif item_type == 2:
|
||||
title = 'Servers'
|
||||
elif item_type == 3:
|
||||
title = 'VM\'s'
|
||||
elif item_type == 4:
|
||||
title = 'Network'
|
||||
else:
|
||||
title = 'All'
|
||||
|
||||
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'])
|
||||
@login_required
|
||||
def action_item(item_id,action):
|
||||
if current_user.is_reado:
|
||||
return abort(403)
|
||||
|
||||
if action == 1:
|
||||
db_item = Inventory.query.filter_by(id=item_id).first()
|
||||
db_allocs = Allocation.query.filter_by(device_id=db_item.id).all()
|
||||
# Delete Link Allocation(s)
|
||||
if db_allocs != []:
|
||||
for alloc in db_allocs:
|
||||
alloc.device_id = None
|
||||
|
||||
db.session.delete(db_item)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('inventory.list_items',item_type=request.args.get('page', type=int)))
|
||||
|
||||
## MODAL ROUTES
|
||||
|
||||
@inventory.get('/<int:item_id>/view_modal')
|
||||
@login_required
|
||||
def item_view_modal(item_id):
|
||||
db_item = Inventory.query.filter_by(id=item_id).first()
|
||||
modal = {
|
||||
'title': f'Informations of <b>{db_item.host_name}</b> ({db_item.id})',
|
||||
'modal_type': 'infos',
|
||||
'color': 'info',
|
||||
'action': 'delete',
|
||||
'action_text': 'Delete',
|
||||
'action_url': url_for('inventory.action_item',item_id=item_id,action=1)
|
||||
}
|
||||
|
||||
if db_item.host_type == 0:
|
||||
host_type = 'Others'
|
||||
elif db_item.host_type == 1:
|
||||
host_type = 'Computers'
|
||||
elif db_item.host_type == 2:
|
||||
host_type = 'Servers'
|
||||
elif db_item.host_type == 3:
|
||||
host_type = 'VM\'s'
|
||||
elif db_item.host_type == 4:
|
||||
host_type = 'Network'
|
||||
else:
|
||||
host_type = 'Error'
|
||||
|
||||
informations = [
|
||||
('Type',host_type,'col-6'),
|
||||
('Name',db_item.host_name,'col-6'),
|
||||
('Description',db_item.host_desc,'col-6'),
|
||||
('Model',db_item.host_model,'col-6'),
|
||||
('Manufacturer',db_item.host_manufacturer,'col-6'),
|
||||
('SerialNumber',db_item.host_serialnumber,'col-6'),
|
||||
('OS',db_item.host_os,'col-6'),
|
||||
('Location',db_item.host_location,'col-6'),
|
||||
('Owner',db_item.host_owner,'col-6'),
|
||||
('Contact',db_item.host_contact,'col-6'),
|
||||
('Allocations',db_item.host_allocations,'col-12'),
|
||||
]
|
||||
|
||||
return render_template('panel/_modal.html', modal=modal, item_id=item_id, informations=informations, old_page=request.args.get('page', type=int), pagination_active=True)
|
||||
|
||||
@inventory.get('/<int:item_id>/del_modal')
|
||||
@login_required
|
||||
def item_del_modal(item_id):
|
||||
db_item = Inventory.query.filter_by(id=item_id).first()
|
||||
modal = {
|
||||
'title': 'Delete Item',
|
||||
'body': f'Are you sure you want to delete <b>{db_item.host_name}</b> ?',
|
||||
'color': 'danger',
|
||||
'action': 'delete',
|
||||
'action_text': 'Delete',
|
||||
'action_url': url_for('inventory.action_item',item_id=item_id,action=1)
|
||||
}
|
||||
return render_template('panel/_modal.html', modal=modal, item_id=item_id, old_page=request.args.get('page', type=int), pagination_active=True)
|
||||
|
||||
@inventory.route('/<int:item_id>/edit_modal', methods=['GET','POST'])
|
||||
@login_required
|
||||
def item_edit_modal(item_id):
|
||||
if current_user.is_reado:
|
||||
return abort(403)
|
||||
|
||||
db_item = Inventory.query.filter_by(id=item_id).first()
|
||||
|
||||
form = ChangeInventoryItem()
|
||||
if form.validate_on_submit() and request.method == 'POST':
|
||||
|
||||
db_item.host_type = form.item_type.data
|
||||
db_item.host_name = form.item_name.data
|
||||
# Description
|
||||
if form.item_desc.data != '' and form.item_desc.data != 'None':
|
||||
db_item.host_desc = form.item_desc.data
|
||||
else:
|
||||
db_item.host_desc = None
|
||||
# Model
|
||||
if form.item_model.data != '' and form.item_model.data != 'None':
|
||||
db_item.host_model = form.item_model.data
|
||||
else:
|
||||
db_item.host_model = None
|
||||
# Manufacturer
|
||||
if form.item_manufacturer.data != '' and form.item_manufacturer.data != 'None':
|
||||
db_item.host_manufacturer = form.item_manufacturer.data
|
||||
else:
|
||||
db_item.host_serialnumber = None
|
||||
# Serial Number
|
||||
if form.item_serial_number.data != '' and form.item_serial_number.data != 'None':
|
||||
db_item.host_serialnumber = form.item_serial_number.data
|
||||
else:
|
||||
db_item.host_serialnumber = None
|
||||
# OS
|
||||
if form.item_os.data != '' and form.item_os.data != 'None':
|
||||
db_item.host_os = form.item_os.data
|
||||
else:
|
||||
db_item.host_os = None
|
||||
# Location
|
||||
if form.item_location.data != '' and form.item_location.data != 'None':
|
||||
db_item.host_location = form.item_location.data
|
||||
else:
|
||||
db_item.host_location = None
|
||||
# Owner
|
||||
if form.item_owner.data != '' and form.item_owner.data != 'None':
|
||||
db_item.host_owner = form.item_owner.data
|
||||
else:
|
||||
db_item.host_owner = None
|
||||
# Contact
|
||||
if form.item_contact.data != '' and form.item_contact.data != 'None':
|
||||
db_item.host_contact = form.item_contact.data
|
||||
else:
|
||||
db_item.host_contact = None
|
||||
|
||||
# Allocations
|
||||
db_existing_allocs = Allocation.query.filter_by(device_id=db_item.id).all()
|
||||
# Unlink old Allocation(s)
|
||||
if db_existing_allocs != []:
|
||||
for alloc in db_existing_allocs:
|
||||
alloc.device_id = None
|
||||
|
||||
if form.item_ip_allocations.data != '' and form.item_ip_allocations.data != 'None':
|
||||
|
||||
allocs = form.item_ip_allocations.data.split(',')
|
||||
put_allocs_db = ""
|
||||
for alloc in allocs:
|
||||
db_alloc = Allocation.query.filter_by(ipv4=alloc).first()
|
||||
if db_alloc != None:
|
||||
put_allocs_db += db_alloc.ipv4+','
|
||||
db_alloc.device_id = db_item.id
|
||||
elif allocs[-1] == '':
|
||||
pass
|
||||
else:
|
||||
flash('Unknown IPv4', 'warning')
|
||||
return redirect(url_for('inventory.list_items',item_type=request.args.get('page', type=int)))
|
||||
|
||||
db_item.host_allocations = put_allocs_db
|
||||
|
||||
else:
|
||||
db_item.host_allocations = None
|
||||
|
||||
db.session.commit()
|
||||
flash("Item Updated", 'success')
|
||||
return redirect(url_for('inventory.list_items',item_type=request.args.get('page', type=int)))
|
||||
|
||||
# Setting default values
|
||||
form.item_name.default = db_item.host_name
|
||||
form.item_desc.default = db_item.host_desc
|
||||
form.item_type.default = db_item.host_type
|
||||
form.item_model.default =db_item.host_model
|
||||
form.item_manufacturer.default = db_item.host_manufacturer
|
||||
form.item_serial_number.default = db_item.host_serialnumber
|
||||
form.item_os.default = db_item.host_os
|
||||
form.item_location.default = db_item.host_location
|
||||
form.item_owner.default = db_item.host_owner
|
||||
form.item_contact.default = db_item.host_contact
|
||||
form.item_ip_allocations.default = db_item.host_allocations
|
||||
form.process()
|
||||
|
||||
modal = {
|
||||
'title': 'Update Item',
|
||||
'body': f'Change informations for <b>{db_item.host_name}</b>.',
|
||||
'color': 'primary',
|
||||
'modal_type': 'form',
|
||||
'action_text': 'Save',
|
||||
'form_url': url_for('inventory.item_edit_modal',item_id=item_id),
|
||||
'inventory':True,
|
||||
}
|
||||
|
||||
return render_template('panel/_modal.html', modal=modal, form=form, old_page=request.args.get('page', type=int), pagination_active=True)
|
||||
@@ -0,0 +1,270 @@
|
||||
from flask import Blueprint, request, render_template, flash, redirect, url_for, session, abort
|
||||
from flask_login import login_required, current_user
|
||||
from ipaddress import IPv4Network, IPv4Address
|
||||
|
||||
from app._config import app_version
|
||||
|
||||
from app.extensions import *
|
||||
from app.models import *
|
||||
from app.forms import NewPrefixForm, ChangeAllocationForm
|
||||
|
||||
network = Blueprint('network', __name__)
|
||||
|
||||
@network.route('/',methods=['GET','POST'])
|
||||
@login_required
|
||||
def networks():
|
||||
networks = Network.query.all()
|
||||
form = NewPrefixForm()
|
||||
if current_user.is_reado == False:
|
||||
if request.method == 'POST' and form.validate_on_submit():
|
||||
try:
|
||||
network = IPv4Network(f"{str(form.subnet.data)}/{str(form.prefix.data)}", False)
|
||||
|
||||
add_net = Network(network_address=str(network.network_address),prefix=network.prefixlen,hosts=network.num_addresses-2,description=str(form.description.data))
|
||||
db.session.add(add_net)
|
||||
db.session.commit()
|
||||
|
||||
for ip in network.hosts():
|
||||
db.session.add(Allocation(ipv4=str(ip), net_id=add_net.id))
|
||||
|
||||
db.session.commit()
|
||||
return redirect(url_for('network.networks'))
|
||||
|
||||
except:
|
||||
flash("Invalid Subnet or Prefix", 'danger')
|
||||
return redirect(url_for('network.networks'))
|
||||
elif form.errors:
|
||||
flash(form.errors, 'danger')
|
||||
|
||||
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'])
|
||||
@login_required
|
||||
def allocations(net_id):
|
||||
|
||||
active_page = request.args.get('page', type=int)
|
||||
if active_page == None:
|
||||
active_page = 1
|
||||
|
||||
next_page = active_page+1
|
||||
prev_page = active_page-1
|
||||
|
||||
if request.args.get('items', type=int) != None:
|
||||
session['items'] = request.args.get('items', type=int)
|
||||
try:
|
||||
if session['items'] == None:
|
||||
session['items'] = 15
|
||||
except:
|
||||
session['items'] = 15
|
||||
|
||||
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'] )
|
||||
|
||||
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
|
||||
|
||||
@network.get('/<int:net_id>/del_modal')
|
||||
@login_required
|
||||
def network_del_modal(net_id):
|
||||
db_network = Network.query.filter_by(id=net_id).first()
|
||||
modal = {
|
||||
'title': 'Delete Network',
|
||||
'body': f'Are you sure you want to delete <b>{db_network.network_address}/{db_network.prefix}</b> network ?',
|
||||
'color': 'danger',
|
||||
'action': 'delete',
|
||||
'action_text': 'Delete',
|
||||
'action_url': url_for('network.network_delete',net_id=net_id)
|
||||
}
|
||||
return render_template('panel/_modal.html', modal=modal, net_id=net_id)
|
||||
|
||||
@network.get('/<int:net_id>/allocations/<string:ipv4>/del_modal')
|
||||
@login_required
|
||||
def allocation_del_modal(net_id,ipv4):
|
||||
modal = {
|
||||
'title': 'Remove Allocation',
|
||||
'body': f'Are you sure you want to remove <b>{ipv4}</b> allocation ?',
|
||||
'color': 'warning',
|
||||
'action': 'delete',
|
||||
'action_text': 'Remove',
|
||||
'action_url': url_for('network.allocation_delete',net_id=net_id,ipv4=ipv4)
|
||||
}
|
||||
return render_template('panel/_modal.html', modal=modal, net_id=net_id, ipv4=ipv4, old_page=request.args.get('page', type=int), pagination_active=True)
|
||||
|
||||
|
||||
@network.route('/<int:net_id>/allocations/<string:ipv4>/edit_modal', methods=['GET','POST'])
|
||||
@login_required
|
||||
def allocation_edit_modal(net_id, ipv4):
|
||||
if current_user.is_reado:
|
||||
return abort(403)
|
||||
|
||||
db_alloc = Allocation.query.filter_by(ipv4=ipv4).first()
|
||||
|
||||
form = ChangeAllocationForm()
|
||||
if form.validate_on_submit() and request.method == 'POST':
|
||||
db_alloc.hostname = str(form.hostname.data)
|
||||
db_alloc.description = str(form.description.data)
|
||||
db_alloc.is_used = True
|
||||
# Special Allocation
|
||||
if int(form.is_special.data) == 1:
|
||||
# DHCP
|
||||
db_alloc.is_dhcp = True
|
||||
db_alloc.is_gateway = False
|
||||
db_alloc.hostname = None
|
||||
db_alloc.description = "DHCP Allocation"
|
||||
elif int(form.is_special.data) == 2:
|
||||
# Gateway
|
||||
db_alloc.is_dhcp = False
|
||||
db_alloc.is_gateway = True
|
||||
else:
|
||||
# None
|
||||
db_alloc.is_dhcp = False
|
||||
db_alloc.is_gateway = False
|
||||
|
||||
db.session.commit()
|
||||
flash("Allocation Updated", 'success')
|
||||
return redirect(url_for('network.allocations',net_id=net_id,page=request.args.get('page', type=int)))
|
||||
|
||||
modal = {
|
||||
'title': 'Update Allocation',
|
||||
'body': f'Change informations for <b>{db_alloc.ipv4}</b> allocation.',
|
||||
'color': 'primary',
|
||||
'modal_type': 'form',
|
||||
'action_text': 'Save',
|
||||
'form_url': url_for('network.allocation_edit_modal',net_id=db_alloc.net_id,ipv4=db_alloc.ipv4),
|
||||
}
|
||||
|
||||
old_page = request.args.get('page', type=int)
|
||||
if old_page == 0:
|
||||
old_page = 1
|
||||
|
||||
form.hostname.default = db_alloc.hostname
|
||||
form.description.default = db_alloc.description
|
||||
if db_alloc.is_dhcp:
|
||||
form.is_special.default = 1
|
||||
elif db_alloc.is_gateway:
|
||||
form.is_special.default = 2
|
||||
form.process()
|
||||
|
||||
return render_template('panel/_modal.html', modal=modal, form=form, old_page=old_page, pagination_active=True)
|
||||
|
||||
## MODIFICATION ROUTES
|
||||
@network.get('/<int:net_id>/allocations/<string:ipv4>/unallocate')
|
||||
@login_required
|
||||
def allocation_delete(net_id,ipv4):
|
||||
if current_user.is_reado:
|
||||
return abort(403)
|
||||
|
||||
db_alloc = Allocation.query.filter_by(net_id=net_id,ipv4=ipv4).first()
|
||||
db_alloc.is_used = False
|
||||
db_alloc.hostname = None
|
||||
db_alloc.description = None
|
||||
db_alloc.is_dhcp = False
|
||||
db_alloc.is_gateway = False
|
||||
db_alloc.is_decom = False
|
||||
db.session.commit()
|
||||
flash("Allocation Removed", 'warning')
|
||||
return redirect(url_for('network.allocations',net_id=net_id,page=request.args.get('page', type=int)))
|
||||
|
||||
@network.get('/<int:net_id>/allocations/<string:ipv4>/decom/<int:action>')
|
||||
@login_required
|
||||
def allocation_decom(net_id,ipv4,action):
|
||||
if current_user.is_reado:
|
||||
return abort(403)
|
||||
|
||||
db_alloc = Allocation.query.filter_by(net_id=net_id,ipv4=ipv4).first()
|
||||
if action == 1:
|
||||
db_alloc.is_decom = True
|
||||
else:
|
||||
db_alloc.is_decom = False
|
||||
db.session.commit()
|
||||
return redirect(url_for('network.allocations',net_id=net_id,page=request.args.get('page', type=int)))
|
||||
|
||||
@network.get('/<int:net_id>/delete')
|
||||
@login_required
|
||||
def network_delete(net_id):
|
||||
if current_user.is_reado:
|
||||
return abort(403)
|
||||
|
||||
db_network = Network.query.filter_by(id=net_id).first()
|
||||
db_alloc = Allocation.query.filter_by(net_id=db_network.id).all()
|
||||
|
||||
if db_network != None:
|
||||
if db_alloc != None:
|
||||
for alloc in db_alloc:
|
||||
db.session.delete(alloc)
|
||||
|
||||
db.session.delete(db_network)
|
||||
db.session.commit()
|
||||
flash("Network Deleted", 'warning')
|
||||
return redirect(url_for('network.networks'))
|
||||
else:
|
||||
flash("Network Not Found", 'danger')
|
||||
return redirect(url_for('network.networks'))
|
||||
|
||||
## HTMX SEARCH
|
||||
@network.post('/<int:net_id>/allocations/search')
|
||||
@login_required
|
||||
def allocation_search(net_id):
|
||||
search = str(request.form['search']).lower()
|
||||
|
||||
db_network = Network.query.filter_by(id=net_id).first()
|
||||
db_alloc = Allocation.query.filter_by(net_id=net_id).all()
|
||||
obj_list = []
|
||||
|
||||
# Advance Search
|
||||
if '*' in search:
|
||||
search = search.split("*")[1]
|
||||
# AND
|
||||
if ',' in search:
|
||||
for alloc in db_alloc:
|
||||
if alloc.ipv4.lower() in search.split(','):
|
||||
obj_list.append(alloc)
|
||||
# BETWEEN
|
||||
if '-' in search:
|
||||
search = search.split('-')
|
||||
if search[0].split('.')[-1] == '' or search[1].split('.')[-1] == '':
|
||||
return "Invalid(s) Number(s)"
|
||||
temp_1 = int(search[0].split('.')[-1])
|
||||
temp_2 = int(search[1].split('.')[-1])
|
||||
|
||||
for alloc in db_alloc:
|
||||
if int(alloc.ipv4.split('.')[-1]) <= temp_2 and int(alloc.ipv4.split('.')[-1]) >= temp_1:
|
||||
obj_list.append(alloc)
|
||||
# DHCP
|
||||
if 'dhcp' in search:
|
||||
for alloc in db_alloc:
|
||||
if alloc.is_dhcp:
|
||||
obj_list.append(alloc)
|
||||
|
||||
## GATEWAY
|
||||
if 'gateway' in search:
|
||||
for alloc in db_alloc:
|
||||
if alloc.is_gateway:
|
||||
obj_list.append(alloc)
|
||||
|
||||
## DECOM
|
||||
if 'decom' in search:
|
||||
for alloc in db_alloc:
|
||||
if alloc.is_decom:
|
||||
obj_list.append(alloc)
|
||||
|
||||
## Used
|
||||
if 'used' in search:
|
||||
for alloc in db_alloc:
|
||||
if alloc.is_used and alloc.is_dhcp == False:
|
||||
obj_list.append(alloc)
|
||||
|
||||
else:
|
||||
# BASIC SEARCH
|
||||
for alloc in db_alloc:
|
||||
if alloc.ipv4.lower().startswith(search):
|
||||
obj_list.append(alloc)
|
||||
elif alloc.hostname != None:
|
||||
if alloc.hostname.lower().startswith(search):
|
||||
obj_list.append(alloc)
|
||||
elif alloc.description != None:
|
||||
if alloc.description.lower().startswith(search):
|
||||
obj_list.append(alloc)
|
||||
|
||||
return render_template('panel/_table-alloc.html',allocations=obj_list,Inventory=Inventory,network=db_network)
|
||||
@@ -22,6 +22,7 @@
|
||||
<th>ID</th>
|
||||
<th>Username</th>
|
||||
<th>Admin User</th>
|
||||
<th>RO</th>
|
||||
<th>Last Connection</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
@@ -32,6 +33,7 @@
|
||||
<td>{{user.id}}</td>
|
||||
<td>{{user.username}}</td>
|
||||
<td>{{user.is_admin}}</td>
|
||||
<td>{{user.is_reado}}</td>
|
||||
<td>{% if user.dat_last != None %}{{user.dat_last.strftime('%Y-%m-%d %H:%M:%S')}}{%else%}N/A{%endif%}</td>
|
||||
<td>
|
||||
<button
|
||||
@@ -80,6 +82,10 @@
|
||||
<label class="form-label">Is Admin?</label>
|
||||
{{ form.is_admin(class="form-control") }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Is Readonly?</label>
|
||||
{{ form.is_readonly(class="form-control") }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary w-100">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% if modal.modal_type == 'form' %}
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog">
|
||||
<form method="post" action="{{modal.form_url}}{% if pagination_active %}?page={{old_page}}{%endif%}">
|
||||
{{ form.csrf_token }}
|
||||
<div class="modal-content">
|
||||
@@ -8,8 +8,29 @@
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
{% if modal.inventory %}
|
||||
{{ modal.body|safe }}<hr>
|
||||
<div class="row">
|
||||
{% for entry in form %}
|
||||
{% if entry.id != 'csrf_token' %}
|
||||
{% if entry.id == 'item_ip_allocations' %}
|
||||
<div class="col-12">
|
||||
{{ entry.label(class="form-label") }}<br>
|
||||
{{ entry(class="form-control",placeholder='192.168.1.1,192.168.1.2 (separate with ,)') }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-6">
|
||||
<div class="mb-2">
|
||||
{{ entry.label(class="form-label") }}<br>
|
||||
{{ entry(class="form-control") }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
{% for entry in form %}
|
||||
{% if entry.id != 'csrf_token' %}
|
||||
<div class="mb-3">
|
||||
@@ -19,6 +40,7 @@
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
@@ -27,11 +49,36 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% elif modal.modal_type == 'infos' %}
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title text-{{modal.color}}">{{ modal.title|safe }}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
{% for info in informations %}
|
||||
<div class="{{info[2]}}">
|
||||
<div class="mb-3">
|
||||
<label for="{{info[0]}}">{{info[0]}}</label>
|
||||
<input class="form-control" type="text" id="{{info[0]}}" value="{{info[1]}}" readonly>
|
||||
<!--{{ key }} : {{ value }}-->
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title text-{{modal.color}}">{{ modal.title }}</h5>
|
||||
<h5 class="modal-title text-{{modal.color}}">{{ modal.title|safe }}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
@@ -10,16 +10,18 @@
|
||||
<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>
|
||||
<a class="nav-link {% if is_networks %}active{% endif %}" href="{{ url_for('network.networks') }}"><i class="bi bi-globe"></i> Network(s)</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle disabled" role="button" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-archive-fill"></i> Inventory</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">
|
||||
<li><a class="dropdown-item" href="#"><i class="bi bi-pc-display"></i> Computers</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>
|
||||
<li><a class="dropdown-item" href="{{ url_for('inventory.list_items',item_type=1) }}"><i class="bi bi-pc-display"></i> Computers</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('inventory.list_items',item_type=2) }}"><i class="bi bi-server"></i> Servers</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('inventory.list_items',item_type=3) }}"><i class="bi bi-server"></i> Virtual Machines</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('inventory.list_items',item_type=4) }}"><i class="bi bi-ethernet"></i> Networks Devices</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('inventory.list_items',item_type=0) }}"><i class="bi bi-collection"></i> Others</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('inventory.list_items',item_type=5) }}"><i class="bi bi-collection"></i> All</a></li>
|
||||
{% if current_user.is_reado == False %}<li><a class="dropdown-item" href="{{ url_for('inventory.add_item') }}"><i class="bi bi-plus-circle"></i> ADD NEW</a></li>{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
{% if current_user.is_admin %}
|
||||
|
||||
@@ -5,7 +5,16 @@
|
||||
<td>{{allocation.description}}</td>
|
||||
<td>
|
||||
{% if Inventory.query.filter_by(id=allocation.device_id).first() %}
|
||||
<a href="{{Inventory.query.filter_by(id=allocation.device_id).first().id}}" style="text-decoration:none;">link</a>
|
||||
<button
|
||||
hx-get="{{ url_for('inventory.item_view_modal',item_id=allocation.device_id) }}"
|
||||
hx-trigger="click"
|
||||
hx-target="#modal-view"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#modal-view"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
style="padding-top:1px;padding-bottom:1px"
|
||||
> Details
|
||||
</button>
|
||||
{% else %}
|
||||
N/A
|
||||
{% endif %}
|
||||
@@ -13,10 +22,11 @@
|
||||
<td>{{allocation.is_dhcp}}</td>
|
||||
<td>{{allocation.is_gateway}}</td>
|
||||
<td>{{allocation.is_decom}}</td>
|
||||
{% if current_user.is_reado == False %}
|
||||
<td>
|
||||
{% if allocation.is_used %}
|
||||
<button
|
||||
hx-get="{{ url_for('base.allocation_edit_modal',net_id=network.id,ipv4=allocation.ipv4) }}?page={{active_page}}"
|
||||
hx-get="{{ url_for('network.allocation_edit_modal',net_id=network.id,ipv4=allocation.ipv4) }}?page={{active_page}}"
|
||||
hx-trigger="click"
|
||||
hx-target="#modal-edit"
|
||||
data-bs-toggle="modal"
|
||||
@@ -26,12 +36,12 @@
|
||||
> Edit
|
||||
</button>
|
||||
{% if allocation.is_decom %}
|
||||
<a href="{{ url_for('base.allocation_decom',action=2,net_id=network.id,ipv4=allocation.ipv4) }}?page={{active_page}}" class="btn btn-sm btn-outline-primary" style="padding-top:1px;padding-bottom:1px">Undecom</a>
|
||||
<a href="{{ url_for('network.allocation_decom',action=2,net_id=network.id,ipv4=allocation.ipv4) }}?page={{active_page}}" class="btn btn-sm btn-outline-primary" style="padding-top:1px;padding-bottom:1px">Undecom</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('base.allocation_decom',action=1,net_id=network.id,ipv4=allocation.ipv4) }}?page={{active_page}}" class="btn btn-sm btn-outline-warning" style="padding-top:1px;padding-bottom:1px">Decom</a>
|
||||
<a href="{{ url_for('network.allocation_decom',action=1,net_id=network.id,ipv4=allocation.ipv4) }}?page={{active_page}}" class="btn btn-sm btn-outline-warning" style="padding-top:1px;padding-bottom:1px">Decom</a>
|
||||
{% endif %}
|
||||
<button
|
||||
hx-get="{{ url_for('base.allocation_del_modal',net_id=network.id,ipv4=allocation.ipv4) }}?page={{active_page}}"
|
||||
hx-get="{{ url_for('network.allocation_del_modal',net_id=network.id,ipv4=allocation.ipv4) }}?page={{active_page}}"
|
||||
hx-trigger="click"
|
||||
hx-target="#modal-del"
|
||||
data-bs-toggle="modal"
|
||||
@@ -42,7 +52,7 @@
|
||||
</button>
|
||||
{% else %}
|
||||
<button
|
||||
hx-get="{{ url_for('base.allocation_edit_modal',net_id=network.id,ipv4=allocation.ipv4) }}?page={{active_page}}"
|
||||
hx-get="{{ url_for('network.allocation_edit_modal',net_id=network.id,ipv4=allocation.ipv4) }}?page={{active_page}}"
|
||||
hx-trigger="click"
|
||||
hx-target="#modal-edit"
|
||||
data-bs-toggle="modal"
|
||||
@@ -53,5 +63,6 @@
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -8,8 +8,8 @@
|
||||
<div class="col-12">
|
||||
<div class="mb-3">
|
||||
<input class="form-control" type="search"
|
||||
name="search" placeholder="Begin Typing To Search Users..."
|
||||
hx-post="{{ url_for('base.allocation_search',net_id=network.id) }}"
|
||||
name="search" placeholder="Begin Typing To Search Allocation(s)..."
|
||||
hx-post="{{ url_for('network.allocation_search',net_id=network.id) }}"
|
||||
hx-trigger="input changed delay:500ms, keyup[key=='Enter']"
|
||||
hx-target="#search-results"
|
||||
hx-indicator=".htmx-indicator"
|
||||
@@ -18,7 +18,7 @@
|
||||
</div>
|
||||
<!-- IPV4 TABLE -->
|
||||
<div class="col-12">
|
||||
<table class="table table-striped table-hover table-sm">
|
||||
<table class="table table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">IPv4</th>
|
||||
@@ -28,7 +28,7 @@
|
||||
<th scope="col">DHCP</th>
|
||||
<th scope="col">GATEWAY</th>
|
||||
<th scope="col">DECOM</th>
|
||||
<th scope="col">Actions</th>
|
||||
{% if current_user.is_reado == False %}<th scope="col">Actions</th>{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="search-results">
|
||||
@@ -36,28 +36,6 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- IPV4 FORM
|
||||
<div class="col-3">
|
||||
<div class="card p-4 shadow rounded-3">
|
||||
<h2 class="text-center mb-4">Add IPv4</h2>
|
||||
<form method="POST">
|
||||
{{ form.csrf_token }}
|
||||
<div class="mb-3">
|
||||
<label class="form-label">IPv4</label>
|
||||
{{ form.ipv4(class="form-control", placeholder=network.network_address) }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Hostname</label>
|
||||
{{ form.host_name(class="form-control", placeholder="example") }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Description</label>
|
||||
{{ form.host_desc(class="form-control", placeholder="webserver") }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary w-100">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<nav>
|
||||
@@ -68,12 +46,6 @@
|
||||
{% if allocations.has_next %}
|
||||
<li class="page-item"><a class="page-link" href="?page={{next_page}}">Next</a></li>
|
||||
{% endif %}
|
||||
<!--
|
||||
{% for page in range(allocations.pages+1) %}
|
||||
{% if page != 0%}
|
||||
<li class="page-item"><a class="page-link {%if page==active_page%}active{%endif%}" href="?page={{page}}">{{page}}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}-->
|
||||
</ul>
|
||||
</nav>
|
||||
<div>
|
||||
@@ -101,4 +73,13 @@
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal-view"
|
||||
class="modal modal-blur fade"
|
||||
style="display: none"
|
||||
aria-hidden="false"
|
||||
tabindex="-1">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="row">
|
||||
<!-- NETWORKS -->
|
||||
<div class="col-3">
|
||||
<a href="{{ url_for('base.networks') }}" class="card text-bg-info mb-3 text-decoration-none">
|
||||
<a href="{{ url_for('network.networks') }}" class="card text-bg-info mb-3 text-decoration-none">
|
||||
<div class="card-header"><i class="bi bi-globe"></i> <b>Total Networks</b></div>
|
||||
<div class="card-body">
|
||||
<h1 class="card-title text-center">{{db_networks}}</h1>
|
||||
@@ -14,7 +14,7 @@
|
||||
</div>
|
||||
<!-- TOTAL IP -->
|
||||
<div class="col-3">
|
||||
<a href="{{ url_for('base.networks') }}" class="card text-bg-secondary mb-3 text-decoration-none" style="margin-bottom:1vh;">
|
||||
<a href="{{ url_for('network.networks') }}" class="card text-bg-secondary mb-3 text-decoration-none" style="margin-bottom:1vh;">
|
||||
<div class="card-header"><i class="bi bi-broadcast-pin"></i> <b>Total Allocations</b></div>
|
||||
<div class="card-body">
|
||||
<h1 class="card-title text-center">{{db_allocations}} / {{ available_alloc }}</h1>
|
||||
@@ -75,12 +75,17 @@
|
||||
});
|
||||
/* Devices Chart */
|
||||
const ctx1 = document.getElementById('deviceschart');
|
||||
var computers = JSON.parse('{{ Inventory.query.filter_by(host_type=1).count() | tojson }}');
|
||||
var servers = JSON.parse('{{ Inventory.query.filter_by(host_type=2).count() | tojson }}');
|
||||
var virtual_machines = JSON.parse('{{ Inventory.query.filter_by(host_type=3).count() | tojson }}');
|
||||
var networks_devices = JSON.parse('{{ Inventory.query.filter_by(host_type=4).count() | tojson }}');
|
||||
var others = JSON.parse('{{ Inventory.query.filter_by(host_type=0).count() | tojson }}');
|
||||
new Chart(ctx1, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['Computers', 'Servers', 'VM', 'Network Devices', 'Others'],
|
||||
datasets: [{
|
||||
data: [1, 1, 1, 1, 1],
|
||||
data: [computers, servers, virtual_machines, networks_devices, others],
|
||||
borderWidth: 2
|
||||
}]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{% extends 'panel/_base.html' %}
|
||||
{% block content %}
|
||||
{% include 'panel/_nav.html' %}
|
||||
{% include 'panel/_flash.html' %}
|
||||
<div class="container" style="margin-top:5vh;">
|
||||
<div class="row">
|
||||
<div class="col-4"></div>
|
||||
<div class="col-4">
|
||||
<div class="card p-4 shadow rounded-3">
|
||||
<h2 class="text-center mb-4">Add Item</h2>
|
||||
<form method="POST">
|
||||
{{ form.csrf_token }}
|
||||
<div class="mb-3">
|
||||
<label for="subnet" class="form-label">Name</label>
|
||||
{{ form.item_name(class="form-control", placeholder="Computer A") }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="prefix" class="form-label">Description</label>
|
||||
{{ form.item_desc(class="form-control", placeholder="Computer A for IT support...") }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Item Type</label>
|
||||
{{ form.item_type(class="form-control") }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary w-100">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,133 @@
|
||||
{% extends 'panel/_base.html' %}
|
||||
{% block content %}
|
||||
{% include 'panel/_nav.html' %}
|
||||
{% include 'panel/_flash.html' %}
|
||||
<div class="container" style="margin-top:4vh;">
|
||||
<div class="row">
|
||||
<div class="col-2"></div>
|
||||
<div class="col-8">
|
||||
<h1>{{title}}</h1>
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<table class="table table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">ID</th>
|
||||
<th scope="col">Hostname</th>
|
||||
<th scope="col">Description</th>
|
||||
<th scope="col">Type</th>
|
||||
<th scope="col">Owner</th>
|
||||
<th scope="col">Allocation(s)</th>
|
||||
<th scope="col">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in item_list %}
|
||||
<tr>
|
||||
<th scope="row">{{item.id}}</th>
|
||||
<td>{{item.host_name}}</td>
|
||||
<td>{{item.host_desc}}</td>
|
||||
{% if item.host_type == 1 %}
|
||||
<td><i class="bi bi-pc-display"></i> (PC)</td>
|
||||
{% elif item.host_type == 2 %}
|
||||
<td><i class="bi bi-server"></i> (SRV)</td>
|
||||
{% elif item.host_type == 3 %}
|
||||
<td><i class="bi bi-server"></i> (VM)</td>
|
||||
{% elif item.host_type == 4 %}
|
||||
<td><i class="bi bi-ethernet"></i> (NT)</td>
|
||||
{% elif item.host_type == 0 %}
|
||||
<td>Other</td>
|
||||
{% endif %}
|
||||
<!--
|
||||
{% if 'WINDOWS' in item.host_os|upper or 'WS' in item.host_os|upper %}
|
||||
<td><i class="bi bi-windows"></i> {{item.host_os}}</td>
|
||||
{% elif 'UBUNTU' in item.host_os|upper %}
|
||||
<td><i class="bi bi-ubuntu"></i> {{item.host_os}}</td>
|
||||
{% elif 'MACOS' in item.host_os|upper or 'APPLE' in item.host_os|upper %}
|
||||
<td><i class="bi bi-apple"></i> {{item.host_os}}</td>
|
||||
{% else %}
|
||||
<td>{{item.host_os}}</td>
|
||||
{% endif %}
|
||||
-->
|
||||
<td>{{item.host_owner}}</td>
|
||||
<td>
|
||||
{% if item.host_allocations != None %}
|
||||
{% set alloc_count = item.host_allocations.split(',')|length-1 %}
|
||||
{{alloc_count}}
|
||||
{% else %}
|
||||
{{item.host_allocations}}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
hx-get="{{ url_for('inventory.item_view_modal',item_id=item.id) }}?page={{current_page}}"
|
||||
hx-trigger="click"
|
||||
hx-target="#modal-view"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#modal-view"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
style="padding-top:1px;padding-bottom:1px"
|
||||
> Details
|
||||
</button>
|
||||
{% if current_user.is_reado == False %}
|
||||
<button
|
||||
hx-get="{{ url_for('inventory.item_edit_modal',item_id=item.id) }}?page={{current_page}}"
|
||||
hx-trigger="click"
|
||||
hx-target="#modal-edit"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#modal-edit"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
style="padding-top:1px;padding-bottom:1px"
|
||||
> Edit
|
||||
</button>
|
||||
<button
|
||||
hx-get="{{ url_for('inventory.item_del_modal',item_id=item.id) }}?page={{current_page}}"
|
||||
hx-trigger="click"
|
||||
hx-target="#modal-del"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#modal-del"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
style="padding-top:1px;padding-bottom:1px"
|
||||
> Delete
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- MODAL -->
|
||||
<div id="modal-del"
|
||||
class="modal modal-blur fade"
|
||||
style="display: none"
|
||||
aria-hidden="false"
|
||||
tabindex="-1">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal-edit"
|
||||
class="modal modal-blur fade"
|
||||
style="display: none"
|
||||
aria-hidden="false"
|
||||
tabindex="-1">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal-view"
|
||||
class="modal modal-blur fade"
|
||||
style="display: none"
|
||||
aria-hidden="false"
|
||||
tabindex="-1">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="container" style="margin-top:5vh;">
|
||||
<div class="row">
|
||||
<!-- PREFIXES TABLE -->
|
||||
<div class="col-9">
|
||||
<div class="col-{% if current_user.is_reado == False %}9{% else %}12{% endif %}">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -26,9 +26,10 @@
|
||||
<td>{{Allocation.query.filter_by(net_id=network.id,is_used=True).count()}} / {{network.hosts}} </td>
|
||||
<td><b>{{network.description}}</b></td>
|
||||
<td>
|
||||
<a href="{{ url_for('base.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 %}
|
||||
<button
|
||||
hx-get="{{ url_for('base.network_del_modal',net_id=network.id) }}"
|
||||
hx-get="{{ url_for('network.network_del_modal',net_id=network.id) }}"
|
||||
hx-trigger="click"
|
||||
hx-target="#modal-get"
|
||||
data-bs-toggle="modal"
|
||||
@@ -37,6 +38,7 @@
|
||||
style="padding-top:1px;padding-bottom:1px"
|
||||
> Delete
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -44,6 +46,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<!-- PREFIX FORM -->
|
||||
{% if current_user.is_reado == False %}
|
||||
<div class="col-3">
|
||||
<div class="card p-4 shadow rounded-3">
|
||||
<h2 class="text-center mb-4">Add Network</h2>
|
||||
@@ -65,6 +68,7 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- MODAL -->
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
name: Flask-IPAM
|
||||
services:
|
||||
flask-ipam:
|
||||
image: ghcr.io/cafrays/flask_ipam:latest
|
||||
container_name: flask-ipam
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 5000:5000
|
||||
volumes:
|
||||
- flask-ipam:/fipam/instance
|
||||
|
||||
volumes:
|
||||
flask-ipam:
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# Installation GUIDE
|
||||
This documentation is very good, but needs a few changes to make it work (in our case) :
|
||||
https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-22-04<br>
|
||||
|
||||
Before starting the app ensure to install pip packages in requirements.txt Referrenced [Section](https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-22-04#step-4-configuring-gunicorn)
|
||||
- `pip install Flask-Login Flask-SQLAlchemy Flask-WTF mysql-connector-python SQLAlchemy cryptography`
|
||||
- Update the configuration file under _app/\_config.py_.
|
||||
- `app_ip = '127.0.0.1'` to `app_ip = '0.0.0.0'` for easy web access.
|
||||
- Update **SECRET_KEY='CHANGE_ME'** variable, `openssl rand -hex 32` can be used to create a random key.
|
||||
- You don't need to create or continue with the `wsgi.py` section. When asked, replace: `wsgi:app` with `'app:create_app()'` .
|
||||
- EX : For starting APP with gunicorn : `gunicorn --bind 0.0.0.0:5000 'app:create_app()'`
|
||||
- For any troubleshooting, you can add `--preload` at the end of the command for verbose logging.
|
||||
- In Service File, replace "ExecStart" line with :<br>
|
||||
`ExecStart=/YOUR_ENV_LOCATION/bin/gunicorn --workers 1 --bind 0.0.0.0:5000 -m 007 'app:create_app()'`<br>
|
||||
_(Adjust the number of workers to your needs)_
|
||||
- you must first run setup.py to create the admin user.<br>
|
||||
_(it will create a second database in `./var/setup-instance`. It will have to be merged in the application database in `./var/app-instance`)_
|
||||
@@ -1,3 +1,5 @@
|
||||
wheel
|
||||
gunicorn
|
||||
Flask
|
||||
Flask-Login
|
||||
Flask-SQLAlchemy
|
||||
|
||||
@@ -10,9 +10,10 @@ db = SQLAlchemy(app)
|
||||
|
||||
class User(db.Model):
|
||||
id = db.Column(db.Integer(),primary_key=True,unique=True)
|
||||
username = db.Column(db.String(16))
|
||||
password = db.Column(db.String(32),unique=True)
|
||||
username = db.Column(db.String(16),unique=True)
|
||||
password = db.Column(db.String(32))
|
||||
is_admin = db.Column(db.Boolean(),default=False)
|
||||
is_reado = db.Column(db.Boolean(),default=False)
|
||||
dat_last = db.Column(db.DateTime())
|
||||
|
||||
with app.app_context():
|
||||
@@ -21,7 +22,7 @@ with app.app_context():
|
||||
@app.route('/',methods=['GET','POST'])
|
||||
def setup():
|
||||
if request.method == "POST":
|
||||
db.session.add(User(username=request.form["username"],password=generate_password_hash(request.form["password"]),is_admin=True))
|
||||
db.session.add(User(username=request.form["username"],password=generate_password_hash(request.form["password"]),is_admin=True,is_reado=False))
|
||||
db.session.commit()
|
||||
return 'Done, you can delete setup.py'
|
||||
return "<h1>admin user creation</h1><br><form method='post'><input name='username' placeholder='username'><br><input type='password' name='password' placeholder='password'><br><button type='submit'>submit</button></form>"
|
||||
|
||||
+3
-4
@@ -1,14 +1,13 @@
|
||||
# Sources
|
||||
---
|
||||
### Images
|
||||
logo : https://www.flaticon.com
|
||||
|
||||
### CSS
|
||||
Bootstrap : https://getbootstrap.com
|
||||
themes : https://bootswatch.com
|
||||
Bootstrap : https://getbootstrap.com<br>
|
||||
themes : https://bootswatch.com<br>
|
||||
icons : https://icons.getbootstrap.com
|
||||
|
||||
### JS
|
||||
|
||||
HTMX : https://htmx.org
|
||||
HTMX : https://htmx.org<br>
|
||||
CHARTJS : https://www.chartjs.org
|
||||
Reference in new issue
Block a user