mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-11-08 22:50:26 +00:00
Split the server statistics and configuration feature into separate pages.
This commit is contained in:
parent
55e4f5f829
commit
65bfc53acb
@ -4,7 +4,8 @@ import traceback
|
|||||||
import re
|
import re
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from ast import literal_eval
|
from ast import literal_eval
|
||||||
from flask import Blueprint, render_template, render_template_string, make_response, url_for, current_app, request, redirect, jsonify, abort, flash, session
|
from flask import Blueprint, render_template, render_template_string, make_response, url_for, current_app, request, \
|
||||||
|
redirect, jsonify, abort, flash, session
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
|
|
||||||
from ..decorators import operator_role_required, admin_role_required, history_access_required
|
from ..decorators import operator_role_required, admin_role_required, history_access_required
|
||||||
@ -44,6 +45,8 @@ change_type: "addition" or "deletion" or "status" for status change or "unchange
|
|||||||
Note: A change in "content", is considered a deletion and recreation of the same record,
|
Note: A change in "content", is considered a deletion and recreation of the same record,
|
||||||
holding the new content value.
|
holding the new content value.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def get_record_changes(del_rrset, add_rrset):
|
def get_record_changes(del_rrset, add_rrset):
|
||||||
changeSet = []
|
changeSet = []
|
||||||
delSet = del_rrset['records'] if 'records' in del_rrset else []
|
delSet = del_rrset['records'] if 'records' in del_rrset else []
|
||||||
@ -82,14 +85,15 @@ def get_record_changes(del_rrset, add_rrset):
|
|||||||
exists = True
|
exists = True
|
||||||
break
|
break
|
||||||
if not exists:
|
if not exists:
|
||||||
changeSet.append( ( {"disabled":a['disabled'], "content":a['content']}, {"disabled":a['disabled'], "content":a['content']}, "unchanged") )
|
changeSet.append(({"disabled": a['disabled'], "content": a['content']},
|
||||||
|
{"disabled": a['disabled'], "content": a['content']}, "unchanged"))
|
||||||
|
|
||||||
return changeSet
|
return changeSet
|
||||||
|
|
||||||
|
|
||||||
# out_changes is a list of HistoryRecordEntry objects in which we will append the new changes
|
# out_changes is a list of HistoryRecordEntry objects in which we will append the new changes
|
||||||
# a HistoryRecordEntry represents a pair of add_rrset and del_rrset
|
# a HistoryRecordEntry represents a pair of add_rrset and del_rrset
|
||||||
def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_num, record_name=None, record_type=None):
|
def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_num, record_name=None, record_type=None):
|
||||||
|
|
||||||
if history_entry.detail is None:
|
if history_entry.detail is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -101,7 +105,6 @@ def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_n
|
|||||||
add_rrsets = detail_dict['add_rrsets']
|
add_rrsets = detail_dict['add_rrsets']
|
||||||
del_rrsets = detail_dict['del_rrsets']
|
del_rrsets = detail_dict['del_rrsets']
|
||||||
|
|
||||||
|
|
||||||
for add_rrset in add_rrsets:
|
for add_rrset in add_rrsets:
|
||||||
exists = False
|
exists = False
|
||||||
for del_rrset in del_rrsets:
|
for del_rrset in del_rrsets:
|
||||||
@ -114,7 +117,8 @@ def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_n
|
|||||||
if not exists: # this is a new record
|
if not exists: # this is a new record
|
||||||
if change_num not in out_changes:
|
if change_num not in out_changes:
|
||||||
out_changes[change_num] = []
|
out_changes[change_num] = []
|
||||||
out_changes[change_num].append(HistoryRecordEntry(history_entry, [], add_rrset, "+")) # (add_rrset, del_rrset, change_type)
|
out_changes[change_num].append(
|
||||||
|
HistoryRecordEntry(history_entry, [], add_rrset, "+")) # (add_rrset, del_rrset, change_type)
|
||||||
for del_rrset in del_rrsets:
|
for del_rrset in del_rrsets:
|
||||||
exists = False
|
exists = False
|
||||||
for add_rrset in add_rrsets:
|
for add_rrset in add_rrsets:
|
||||||
@ -126,7 +130,6 @@ def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_n
|
|||||||
out_changes[change_num] = []
|
out_changes[change_num] = []
|
||||||
out_changes[change_num].append(HistoryRecordEntry(history_entry, del_rrset, [], "-"))
|
out_changes[change_num].append(HistoryRecordEntry(history_entry, del_rrset, [], "-"))
|
||||||
|
|
||||||
|
|
||||||
# only used for changelog per record
|
# only used for changelog per record
|
||||||
if record_name != None and record_type != None: # then get only the records with the specific (record_name, record_type) tuple
|
if record_name != None and record_type != None: # then get only the records with the specific (record_name, record_type) tuple
|
||||||
if change_num in out_changes:
|
if change_num in out_changes:
|
||||||
@ -134,15 +137,16 @@ def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_n
|
|||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
for hre in changes_i: # for each history record entry in changes_i
|
for hre in changes_i: # for each history record entry in changes_i
|
||||||
if 'type' in hre.add_rrset and hre.add_rrset['name'] == record_name and hre.add_rrset['type'] == record_type:
|
if 'type' in hre.add_rrset and hre.add_rrset['name'] == record_name and hre.add_rrset[
|
||||||
|
'type'] == record_type:
|
||||||
continue
|
continue
|
||||||
elif 'type' in hre.del_rrset and hre.del_rrset['name'] == record_name and hre.del_rrset['type'] == record_type:
|
elif 'type' in hre.del_rrset and hre.del_rrset['name'] == record_name and hre.del_rrset[
|
||||||
|
'type'] == record_type:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
out_changes[change_num].remove(hre)
|
out_changes[change_num].remove(hre)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# records with same (name,type) are considered as a single HistoryRecordEntry
|
# records with same (name,type) are considered as a single HistoryRecordEntry
|
||||||
# history_entry is of type History - used to extract created_by and created_on
|
# history_entry is of type History - used to extract created_by and created_on
|
||||||
# add_rrset is a dictionary of replace
|
# add_rrset is a dictionary of replace
|
||||||
@ -158,7 +162,6 @@ class HistoryRecordEntry:
|
|||||||
self.changed_fields = [] # contains a subset of : [ttl, name, type]
|
self.changed_fields = [] # contains a subset of : [ttl, name, type]
|
||||||
self.changeSet = [] # all changes for the records of this add_rrset-del_rrset pair
|
self.changeSet = [] # all changes for the records of this add_rrset-del_rrset pair
|
||||||
|
|
||||||
|
|
||||||
if change_type == "+": # addition
|
if change_type == "+": # addition
|
||||||
self.changed_fields.append("name")
|
self.changed_fields.append("name")
|
||||||
self.changed_fields.append("type")
|
self.changed_fields.append("type")
|
||||||
@ -175,8 +178,6 @@ class HistoryRecordEntry:
|
|||||||
self.changed_fields.append("ttl")
|
self.changed_fields.append("ttl")
|
||||||
self.changeSet = get_record_changes(del_rrset, add_rrset)
|
self.changeSet = get_record_changes(del_rrset, add_rrset)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def toDict(self):
|
def toDict(self):
|
||||||
return {
|
return {
|
||||||
"add_rrset": self.add_rrset,
|
"add_rrset": self.add_rrset,
|
||||||
@ -191,6 +192,7 @@ class HistoryRecordEntry:
|
|||||||
def __eq__(self, obj2): # used for removal of objects from a list
|
def __eq__(self, obj2): # used for removal of objects from a list
|
||||||
return True if obj2.toDict() == self.toDict() else False
|
return True if obj2.toDict() == self.toDict() else False
|
||||||
|
|
||||||
|
|
||||||
@admin_bp.before_request
|
@admin_bp.before_request
|
||||||
def before_request():
|
def before_request():
|
||||||
# Manage session timeout
|
# Manage session timeout
|
||||||
@ -202,11 +204,10 @@ def before_request():
|
|||||||
session.modified = True
|
session.modified = True
|
||||||
|
|
||||||
|
|
||||||
|
@admin_bp.route('/server/statistics', methods=['GET'])
|
||||||
@admin_bp.route('/pdns', methods=['GET'])
|
|
||||||
@login_required
|
@login_required
|
||||||
@operator_role_required
|
@operator_role_required
|
||||||
def pdns_stats():
|
def server_statistics():
|
||||||
if not Setting().get('pdns_api_url') or not Setting().get(
|
if not Setting().get('pdns_api_url') or not Setting().get(
|
||||||
'pdns_api_key') or not Setting().get('pdns_version'):
|
'pdns_api_key') or not Setting().get('pdns_version'):
|
||||||
return redirect(url_for('admin.setting_pdns'))
|
return redirect(url_for('admin.setting_pdns'))
|
||||||
@ -215,7 +216,6 @@ def pdns_stats():
|
|||||||
users = User.query.all()
|
users = User.query.all()
|
||||||
|
|
||||||
server = Server(server_id='localhost')
|
server = Server(server_id='localhost')
|
||||||
configs = server.get_config()
|
|
||||||
statistics = server.get_statistic()
|
statistics = server.get_statistic()
|
||||||
history_number = History.query.count()
|
history_number = History.query.count()
|
||||||
|
|
||||||
@ -226,12 +226,33 @@ def pdns_stats():
|
|||||||
else:
|
else:
|
||||||
uptime = 0
|
uptime = 0
|
||||||
|
|
||||||
return render_template('admin_pdns_stats.html',
|
return render_template('admin_server_statistics.html',
|
||||||
|
domains=domains,
|
||||||
|
users=users,
|
||||||
|
statistics=statistics,
|
||||||
|
uptime=uptime,
|
||||||
|
history_number=history_number)
|
||||||
|
|
||||||
|
|
||||||
|
@admin_bp.route('/server/configuration', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@operator_role_required
|
||||||
|
def server_configuration():
|
||||||
|
if not Setting().get('pdns_api_url') or not Setting().get(
|
||||||
|
'pdns_api_key') or not Setting().get('pdns_version'):
|
||||||
|
return redirect(url_for('admin.setting_pdns'))
|
||||||
|
|
||||||
|
domains = Domain.query.all()
|
||||||
|
users = User.query.all()
|
||||||
|
|
||||||
|
server = Server(server_id='localhost')
|
||||||
|
configs = server.get_config()
|
||||||
|
history_number = History.query.count()
|
||||||
|
|
||||||
|
return render_template('admin_server_configuration.html',
|
||||||
domains=domains,
|
domains=domains,
|
||||||
users=users,
|
users=users,
|
||||||
configs=configs,
|
configs=configs,
|
||||||
statistics=statistics,
|
|
||||||
uptime=uptime,
|
|
||||||
history_number=history_number)
|
history_number=history_number)
|
||||||
|
|
||||||
|
|
||||||
@ -296,6 +317,7 @@ def edit_user(user_username=None):
|
|||||||
create=create,
|
create=create,
|
||||||
error=result['msg'])
|
error=result['msg'])
|
||||||
|
|
||||||
|
|
||||||
@admin_bp.route('/key/edit/<key_id>', methods=['GET', 'POST'])
|
@admin_bp.route('/key/edit/<key_id>', methods=['GET', 'POST'])
|
||||||
@admin_bp.route('/key/edit', methods=['GET', 'POST'])
|
@admin_bp.route('/key/edit', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@ -381,6 +403,7 @@ def edit_key(key_id=None):
|
|||||||
create=create,
|
create=create,
|
||||||
plain_key=plain_key)
|
plain_key=plain_key)
|
||||||
|
|
||||||
|
|
||||||
@admin_bp.route('/manage-keys', methods=['GET', 'POST'])
|
@admin_bp.route('/manage-keys', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@operator_role_required
|
@operator_role_required
|
||||||
@ -427,6 +450,7 @@ def manage_keys():
|
|||||||
'msg': 'Key has been removed.'
|
'msg': 'Key has been removed.'
|
||||||
}), 200)
|
}), 200)
|
||||||
|
|
||||||
|
|
||||||
@admin_bp.route('/manage-user', methods=['GET', 'POST'])
|
@admin_bp.route('/manage-user', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@operator_role_required
|
@operator_role_required
|
||||||
@ -800,7 +824,9 @@ class DetailedHistory():
|
|||||||
</table>
|
</table>
|
||||||
""",
|
""",
|
||||||
domaintype=detail_dict['domain_type'],
|
domaintype=detail_dict['domain_type'],
|
||||||
account=Account.get_name_by_id(self=None, account_id=detail_dict['account_id']) if detail_dict['account_id'] != "0" else "None")
|
account=Account.get_name_by_id(self=None, account_id=detail_dict[
|
||||||
|
'account_id']) if detail_dict[
|
||||||
|
'account_id'] != "0" else "None")
|
||||||
|
|
||||||
elif 'authenticator' in detail_dict: # this is a user authentication
|
elif 'authenticator' in detail_dict: # this is a user authentication
|
||||||
self.detailed_msg = render_template_string("""
|
self.detailed_msg = render_template_string("""
|
||||||
@ -825,9 +851,11 @@ class DetailedHistory():
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
""",
|
""",
|
||||||
background_rgba="68,157,68" if detail_dict['success'] == 1 else "201,48,44",
|
background_rgba="68,157,68" if detail_dict[
|
||||||
|
'success'] == 1 else "201,48,44",
|
||||||
username=detail_dict['username'],
|
username=detail_dict['username'],
|
||||||
auth_result="success" if detail_dict['success'] == 1 else "failure",
|
auth_result="success" if detail_dict[
|
||||||
|
'success'] == 1 else "failure",
|
||||||
authenticator=detail_dict['authenticator'],
|
authenticator=detail_dict['authenticator'],
|
||||||
ip_address=detail_dict['ip_address'])
|
ip_address=detail_dict['ip_address'])
|
||||||
|
|
||||||
@ -844,7 +872,8 @@ class DetailedHistory():
|
|||||||
</table>
|
</table>
|
||||||
""",
|
""",
|
||||||
template_name=DetailedHistory.get_key_val(detail_dict, "name"),
|
template_name=DetailedHistory.get_key_val(detail_dict, "name"),
|
||||||
description=DetailedHistory.get_key_val(detail_dict, "description"))
|
description=DetailedHistory.get_key_val(detail_dict,
|
||||||
|
"description"))
|
||||||
|
|
||||||
elif 'Change domain' in history.msg and 'access control' in history.msg: # added or removed a user from a domain
|
elif 'Change domain' in history.msg and 'access control' in history.msg: # added or removed a user from a domain
|
||||||
users_with_access = DetailedHistory.get_key_val(detail_dict, "user_has_access")
|
users_with_access = DetailedHistory.get_key_val(detail_dict, "user_has_access")
|
||||||
@ -868,9 +897,12 @@ class DetailedHistory():
|
|||||||
""",
|
""",
|
||||||
keyname=DetailedHistory.get_key_val(detail_dict, "key"),
|
keyname=DetailedHistory.get_key_val(detail_dict, "key"),
|
||||||
rolename=DetailedHistory.get_key_val(detail_dict, "role"),
|
rolename=DetailedHistory.get_key_val(detail_dict, "role"),
|
||||||
description=DetailedHistory.get_key_val(detail_dict, "description"),
|
description=DetailedHistory.get_key_val(detail_dict,
|
||||||
linked_domains=DetailedHistory.get_key_val(detail_dict, "domains" if "domains" in detail_dict else "domain_acl"),
|
"description"),
|
||||||
linked_accounts=DetailedHistory.get_key_val(detail_dict, "accounts"))
|
linked_domains=DetailedHistory.get_key_val(detail_dict,
|
||||||
|
"domains" if "domains" in detail_dict else "domain_acl"),
|
||||||
|
linked_accounts=DetailedHistory.get_key_val(detail_dict,
|
||||||
|
"accounts"))
|
||||||
|
|
||||||
elif 'Delete API key' in history.msg:
|
elif 'Delete API key' in history.msg:
|
||||||
self.detailed_msg = render_template_string("""
|
self.detailed_msg = render_template_string("""
|
||||||
@ -883,8 +915,10 @@ class DetailedHistory():
|
|||||||
""",
|
""",
|
||||||
keyname=DetailedHistory.get_key_val(detail_dict, "key"),
|
keyname=DetailedHistory.get_key_val(detail_dict, "key"),
|
||||||
rolename=DetailedHistory.get_key_val(detail_dict, "role"),
|
rolename=DetailedHistory.get_key_val(detail_dict, "role"),
|
||||||
description=DetailedHistory.get_key_val(detail_dict, "description"),
|
description=DetailedHistory.get_key_val(detail_dict,
|
||||||
linked_domains=DetailedHistory.get_key_val(detail_dict, "domains"))
|
"description"),
|
||||||
|
linked_domains=DetailedHistory.get_key_val(detail_dict,
|
||||||
|
"domains"))
|
||||||
|
|
||||||
elif 'Update type for domain' in history.msg:
|
elif 'Update type for domain' in history.msg:
|
||||||
self.detailed_msg = render_template_string("""
|
self.detailed_msg = render_template_string("""
|
||||||
@ -905,8 +939,10 @@ class DetailedHistory():
|
|||||||
<tr><td>Domain Master IPs:</td><td>{{ domain_master_ips }}</td></tr>
|
<tr><td>Domain Master IPs:</td><td>{{ domain_master_ips }}</td></tr>
|
||||||
</table>
|
</table>
|
||||||
""",
|
""",
|
||||||
domain_type=DetailedHistory.get_key_val(detail_dict, "domain_type"),
|
domain_type=DetailedHistory.get_key_val(detail_dict,
|
||||||
domain_master_ips=DetailedHistory.get_key_val(detail_dict, "domain_master_ips"))
|
"domain_type"),
|
||||||
|
domain_master_ips=DetailedHistory.get_key_val(detail_dict,
|
||||||
|
"domain_master_ips"))
|
||||||
|
|
||||||
elif DetailedHistory.get_key_val(detail_dict, 'msg') and DetailedHistory.get_key_val(detail_dict, 'status'):
|
elif DetailedHistory.get_key_val(detail_dict, 'msg') and DetailedHistory.get_key_val(detail_dict, 'status'):
|
||||||
self.detailed_msg = render_template_string('''
|
self.detailed_msg = render_template_string('''
|
||||||
@ -915,7 +951,8 @@ class DetailedHistory():
|
|||||||
<tr><td>Message:</td><td>{{ history_msg }}</td></tr>
|
<tr><td>Message:</td><td>{{ history_msg }}</td></tr>
|
||||||
</table>
|
</table>
|
||||||
''',
|
''',
|
||||||
history_status=DetailedHistory.get_key_val(detail_dict, 'status'),
|
history_status=DetailedHistory.get_key_val(detail_dict,
|
||||||
|
'status'),
|
||||||
history_msg=DetailedHistory.get_key_val(detail_dict, 'msg'))
|
history_msg=DetailedHistory.get_key_val(detail_dict, 'msg'))
|
||||||
|
|
||||||
elif 'Update domain' in history.msg and 'associate account' in history.msg: # When an account gets associated or dissociate with domains
|
elif 'Update domain' in history.msg and 'associate account' in history.msg: # When an account gets associated or dissociate with domains
|
||||||
@ -925,8 +962,10 @@ class DetailedHistory():
|
|||||||
<tr><td>Dissociate:</td><td>{{ history_dissoc_account }}</td></tr>
|
<tr><td>Dissociate:</td><td>{{ history_dissoc_account }}</td></tr>
|
||||||
</table>
|
</table>
|
||||||
''',
|
''',
|
||||||
history_assoc_account=DetailedHistory.get_key_val(detail_dict, 'assoc_account'),
|
history_assoc_account=DetailedHistory.get_key_val(detail_dict,
|
||||||
history_dissoc_account=DetailedHistory.get_key_val(detail_dict, 'dissoc_account'))
|
'assoc_account'),
|
||||||
|
history_dissoc_account=DetailedHistory.get_key_val(detail_dict,
|
||||||
|
'dissoc_account'))
|
||||||
|
|
||||||
# check for lower key as well for old databases
|
# check for lower key as well for old databases
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -952,6 +991,7 @@ def convert_histories(histories):
|
|||||||
detailedHistories.append(DetailedHistory(histories[i], None))
|
detailedHistories.append(DetailedHistory(histories[i], None))
|
||||||
return detailedHistories
|
return detailedHistories
|
||||||
|
|
||||||
|
|
||||||
@admin_bp.route('/history', methods=['GET', 'POST'])
|
@admin_bp.route('/history', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@history_access_required
|
@history_access_required
|
||||||
@ -989,7 +1029,6 @@ def history():
|
|||||||
'msg': 'Can not remove histories.'
|
'msg': 'Can not remove histories.'
|
||||||
}), 500)
|
}), 500)
|
||||||
|
|
||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
doms = accounts = users = ""
|
doms = accounts = users = ""
|
||||||
if current_user.role.name in ['Administrator', 'Operator']:
|
if current_user.role.name in ['Administrator', 'Operator']:
|
||||||
@ -997,8 +1036,6 @@ def history():
|
|||||||
all_account_names = Account.query.all()
|
all_account_names = Account.query.all()
|
||||||
all_user_names = User.query.all()
|
all_user_names = User.query.all()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for d in all_domain_names:
|
for d in all_domain_names:
|
||||||
doms += d.name + " "
|
doms += d.name + " "
|
||||||
for acc in all_account_names:
|
for acc in all_account_names:
|
||||||
@ -1026,7 +1063,6 @@ def history():
|
|||||||
AccountUser.user_id == current_user.id
|
AccountUser.user_id == current_user.id
|
||||||
)).all()
|
)).all()
|
||||||
|
|
||||||
|
|
||||||
all_user_names = []
|
all_user_names = []
|
||||||
for a in all_account_names:
|
for a in all_account_names:
|
||||||
temp = db.session.query(User) \
|
temp = db.session.query(User) \
|
||||||
@ -1051,7 +1087,9 @@ def history():
|
|||||||
accounts += a.name + " "
|
accounts += a.name + " "
|
||||||
for u in all_user_names:
|
for u in all_user_names:
|
||||||
users += u.username + " "
|
users += u.username + " "
|
||||||
return render_template('admin_history.html', all_domain_names=doms, all_account_names=accounts, all_usernames=users)
|
return render_template('admin_history.html', all_domain_names=doms, all_account_names=accounts,
|
||||||
|
all_usernames=users)
|
||||||
|
|
||||||
|
|
||||||
# local_offset is the offset of the utc to the local time
|
# local_offset is the offset of the utc to the local time
|
||||||
# offset must be int
|
# offset must be int
|
||||||
@ -1059,9 +1097,11 @@ def history():
|
|||||||
def from_utc_to_local(local_offset, timeframe):
|
def from_utc_to_local(local_offset, timeframe):
|
||||||
offset = str(local_offset * (-1))
|
offset = str(local_offset * (-1))
|
||||||
date_split = str(timeframe).split(".")[0]
|
date_split = str(timeframe).split(".")[0]
|
||||||
date_converted = datetime.datetime.strptime(date_split, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(minutes=int(offset))
|
date_converted = datetime.datetime.strptime(date_split, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(
|
||||||
|
minutes=int(offset))
|
||||||
return date_converted
|
return date_converted
|
||||||
|
|
||||||
|
|
||||||
@admin_bp.route('/history_table', methods=['GET', 'POST'])
|
@admin_bp.route('/history_table', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@history_access_required
|
@history_access_required
|
||||||
@ -1115,28 +1155,35 @@ def history_table(): # ajax call data
|
|||||||
))
|
))
|
||||||
|
|
||||||
domain_name = request.args.get('domain_name_filter') if request.args.get('domain_name_filter') != None \
|
domain_name = request.args.get('domain_name_filter') if request.args.get('domain_name_filter') != None \
|
||||||
and len(request.args.get('domain_name_filter')) != 0 else None
|
and len(
|
||||||
|
request.args.get('domain_name_filter')) != 0 else None
|
||||||
account_name = request.args.get('account_name_filter') if request.args.get('account_name_filter') != None \
|
account_name = request.args.get('account_name_filter') if request.args.get('account_name_filter') != None \
|
||||||
and len(request.args.get('account_name_filter')) != 0 else None
|
and len(
|
||||||
|
request.args.get('account_name_filter')) != 0 else None
|
||||||
user_name = request.args.get('auth_name_filter') if request.args.get('auth_name_filter') != None \
|
user_name = request.args.get('auth_name_filter') if request.args.get('auth_name_filter') != None \
|
||||||
and len(request.args.get('auth_name_filter')) != 0 else None
|
and len(request.args.get('auth_name_filter')) != 0 else None
|
||||||
|
|
||||||
min_date = request.args.get('min') if request.args.get('min') != None and len( request.args.get('min')) != 0 else None
|
min_date = request.args.get('min') if request.args.get('min') != None and len(
|
||||||
|
request.args.get('min')) != 0 else None
|
||||||
if min_date != None: # get 1 day earlier, to check for timezone errors
|
if min_date != None: # get 1 day earlier, to check for timezone errors
|
||||||
min_date = str(datetime.datetime.strptime(min_date, '%Y-%m-%d') - datetime.timedelta(days=1))
|
min_date = str(datetime.datetime.strptime(min_date, '%Y-%m-%d') - datetime.timedelta(days=1))
|
||||||
max_date = request.args.get('max') if request.args.get('max') != None and len( request.args.get('max')) != 0 else None
|
max_date = request.args.get('max') if request.args.get('max') != None and len(
|
||||||
|
request.args.get('max')) != 0 else None
|
||||||
if max_date != None: # get 1 day later, to check for timezone errors
|
if max_date != None: # get 1 day later, to check for timezone errors
|
||||||
max_date = str(datetime.datetime.strptime(max_date, '%Y-%m-%d') + datetime.timedelta(days=1))
|
max_date = str(datetime.datetime.strptime(max_date, '%Y-%m-%d') + datetime.timedelta(days=1))
|
||||||
tzoffset = request.args.get('tzoffset') if request.args.get('tzoffset') != None and len(request.args.get('tzoffset')) != 0 else None
|
tzoffset = request.args.get('tzoffset') if request.args.get('tzoffset') != None and len(
|
||||||
|
request.args.get('tzoffset')) != 0 else None
|
||||||
changed_by = request.args.get('user_name_filter') if request.args.get('user_name_filter') != None \
|
changed_by = request.args.get('user_name_filter') if request.args.get('user_name_filter') != None \
|
||||||
and len(request.args.get('user_name_filter')) != 0 else None
|
and len(
|
||||||
|
request.args.get('user_name_filter')) != 0 else None
|
||||||
"""
|
"""
|
||||||
Auth methods: LOCAL, Github OAuth, Azure OAuth, SAML, OIDC OAuth, Google OAuth
|
Auth methods: LOCAL, Github OAuth, Azure OAuth, SAML, OIDC OAuth, Google OAuth
|
||||||
"""
|
"""
|
||||||
auth_methods = []
|
auth_methods = []
|
||||||
if (request.args.get('auth_local_only_checkbox') is None \
|
if (request.args.get('auth_local_only_checkbox') is None \
|
||||||
and request.args.get('auth_oauth_only_checkbox') is None \
|
and request.args.get('auth_oauth_only_checkbox') is None \
|
||||||
and request.args.get('auth_saml_only_checkbox') is None and request.args.get('auth_all_checkbox') is None):
|
and request.args.get('auth_saml_only_checkbox') is None and request.args.get(
|
||||||
|
'auth_all_checkbox') is None):
|
||||||
auth_methods = []
|
auth_methods = []
|
||||||
if request.args.get('auth_all_checkbox') == "on":
|
if request.args.get('auth_all_checkbox') == "on":
|
||||||
auth_methods.append("")
|
auth_methods.append("")
|
||||||
@ -1152,9 +1199,6 @@ def history_table(): # ajax call data
|
|||||||
else:
|
else:
|
||||||
changelog_only = False
|
changelog_only = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# users cannot search for authentication
|
# users cannot search for authentication
|
||||||
if user_name != None and current_user.role.name not in ['Administrator', 'Operator']:
|
if user_name != None and current_user.role.name not in ['Administrator', 'Operator']:
|
||||||
histories = []
|
histories = []
|
||||||
@ -1165,8 +1209,11 @@ def history_table(): # ajax call data
|
|||||||
.filter(
|
.filter(
|
||||||
db.and_(
|
db.and_(
|
||||||
db.or_(
|
db.or_(
|
||||||
History.msg.like("%domain "+ domain_name) if domain_name != "*" else History.msg.like("%domain%"),
|
History.msg.like("%domain " + domain_name) if domain_name != "*" else History.msg.like(
|
||||||
History.msg.like("%domain "+ domain_name + " access control") if domain_name != "*" else History.msg.like("%domain%access control")
|
"%domain%"),
|
||||||
|
History.msg.like(
|
||||||
|
"%domain " + domain_name + " access control") if domain_name != "*" else History.msg.like(
|
||||||
|
"%domain%access control")
|
||||||
),
|
),
|
||||||
History.created_on <= max_date if max_date != None else True,
|
History.created_on <= max_date if max_date != None else True,
|
||||||
History.created_on >= min_date if min_date != None else True,
|
History.created_on >= min_date if min_date != None else True,
|
||||||
@ -1214,14 +1261,19 @@ def history_table(): # ajax call data
|
|||||||
)
|
)
|
||||||
).order_by(History.created_on.desc()) \
|
).order_by(History.created_on.desc()) \
|
||||||
.limit(lim).all()
|
.limit(lim).all()
|
||||||
elif user_name != None and current_user.role.name in [ 'Administrator', 'Operator']: # only admins can see the user login-logouts
|
elif user_name != None and current_user.role.name in ['Administrator',
|
||||||
|
'Operator']: # only admins can see the user login-logouts
|
||||||
|
|
||||||
histories = History.query \
|
histories = History.query \
|
||||||
.filter(
|
.filter(
|
||||||
db.and_(
|
db.and_(
|
||||||
db.or_(
|
db.or_(
|
||||||
History.msg.like("User "+ user_name + " authentication%") if user_name != "*" and user_name != None else History.msg.like("%authentication%"),
|
History.msg.like(
|
||||||
History.msg.like("User "+ user_name + " was not authorized%") if user_name != "*" and user_name != None else History.msg.like("User%was not authorized%")
|
"User " + user_name + " authentication%") if user_name != "*" and user_name != None else History.msg.like(
|
||||||
|
"%authentication%"),
|
||||||
|
History.msg.like(
|
||||||
|
"User " + user_name + " was not authorized%") if user_name != "*" and user_name != None else History.msg.like(
|
||||||
|
"User%was not authorized%")
|
||||||
),
|
),
|
||||||
History.created_on <= max_date if max_date != None else True,
|
History.created_on <= max_date if max_date != None else True,
|
||||||
History.created_on >= min_date if min_date != None else True,
|
History.created_on >= min_date if min_date != None else True,
|
||||||
@ -1236,7 +1288,8 @@ def history_table(): # ajax call data
|
|||||||
temp.append(h)
|
temp.append(h)
|
||||||
break
|
break
|
||||||
histories = temp
|
histories = temp
|
||||||
elif (changed_by != None or max_date != None) and current_user.role.name in [ 'Administrator', 'Operator'] : # select changed by and date filters only
|
elif (changed_by != None or max_date != None) and current_user.role.name in ['Administrator',
|
||||||
|
'Operator']: # select changed by and date filters only
|
||||||
histories = History.query \
|
histories = History.query \
|
||||||
.filter(
|
.filter(
|
||||||
db.and_(
|
db.and_(
|
||||||
@ -1246,7 +1299,8 @@ def history_table(): # ajax call data
|
|||||||
)
|
)
|
||||||
) \
|
) \
|
||||||
.order_by(History.created_on.desc()).limit(lim).all()
|
.order_by(History.created_on.desc()).limit(lim).all()
|
||||||
elif (changed_by != None or max_date != None): # special filtering for user because one user does not have access to log-ins logs
|
elif (
|
||||||
|
changed_by != None or max_date != None): # special filtering for user because one user does not have access to log-ins logs
|
||||||
histories = base_query \
|
histories = base_query \
|
||||||
.filter(
|
.filter(
|
||||||
db.and_(
|
db.and_(
|
||||||
@ -1289,14 +1343,15 @@ def history_table(): # ajax call data
|
|||||||
max_date_split = max_date.split()[0]
|
max_date_split = max_date.split()[0]
|
||||||
for i, history_rec in enumerate(detailedHistories):
|
for i, history_rec in enumerate(detailedHistories):
|
||||||
local_date = str(from_utc_to_local(int(tzoffset), history_rec.history.created_on).date())
|
local_date = str(from_utc_to_local(int(tzoffset), history_rec.history.created_on).date())
|
||||||
if (min_date != None and local_date == min_date_split) or (max_date != None and local_date == max_date_split):
|
if (min_date != None and local_date == min_date_split) or (
|
||||||
|
max_date != None and local_date == max_date_split):
|
||||||
detailedHistories[i] = None
|
detailedHistories[i] = None
|
||||||
|
|
||||||
# Remove elements previously flagged as None
|
# Remove elements previously flagged as None
|
||||||
detailedHistories = [h for h in detailedHistories if h is not None]
|
detailedHistories = [h for h in detailedHistories if h is not None]
|
||||||
|
|
||||||
return render_template('admin_history_table.html', histories=detailedHistories, len_histories=len(detailedHistories), lim=lim)
|
return render_template('admin_history_table.html', histories=detailedHistories,
|
||||||
|
len_histories=len(detailedHistories), lim=lim)
|
||||||
|
|
||||||
|
|
||||||
@admin_bp.route('/setting/basic', methods=['GET'])
|
@admin_bp.route('/setting/basic', methods=['GET'])
|
||||||
@ -1542,7 +1597,6 @@ def setting_authentication():
|
|||||||
Setting().set('purge', True
|
Setting().set('purge', True
|
||||||
if request.form.get('purge') == 'ON' else False)
|
if request.form.get('purge') == 'ON' else False)
|
||||||
|
|
||||||
|
|
||||||
result = {'status': True, 'msg': 'Saved successfully'}
|
result = {'status': True, 'msg': 'Saved successfully'}
|
||||||
elif conf_type == 'google':
|
elif conf_type == 'google':
|
||||||
google_oauth_enabled = True if request.form.get(
|
google_oauth_enabled = True if request.form.get(
|
||||||
@ -2003,6 +2057,7 @@ def global_search():
|
|||||||
|
|
||||||
return render_template('admin_global_search.html', domains=domains, records=records, comments=comments)
|
return render_template('admin_global_search.html', domains=domains, records=records, comments=comments)
|
||||||
|
|
||||||
|
|
||||||
def validateURN(value):
|
def validateURN(value):
|
||||||
NID_PATTERN = re.compile(r'^[0-9a-z][0-9a-z-]{1,31}$', flags=re.IGNORECASE)
|
NID_PATTERN = re.compile(r'^[0-9a-z][0-9a-z-]{1,31}$', flags=re.IGNORECASE)
|
||||||
NSS_PCHAR = '[a-z0-9-._~]|%[a-f0-9]{2}|[!$&\'()*+,;=]|:|@'
|
NSS_PCHAR = '[a-z0-9-._~]|%[a-f0-9]{2}|[!$&\'()*+,;=]|:|@'
|
||||||
|
@ -1,127 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% set active_page = "admin_console" %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
<title>
|
|
||||||
Admin Console - {{ SITE_NAME }}
|
|
||||||
</title>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block dashboard_stat %}
|
|
||||||
<div class="content-header">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row mb-2">
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<h1 class="m-0 text-dark">
|
|
||||||
PowerDNS
|
|
||||||
<small>Server Statistics & Configuration</small>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<ol class="breadcrumb float-sm-right">
|
|
||||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
|
||||||
<li class="breadcrumb-item active">PowerDNS Server Statistics & Configuration</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<section class="content">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="card shadow">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title">PowerDNS Server Statistics</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<table id="tbl_statistics" class="table table-bordered table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="30%">Statistic</th>
|
|
||||||
<th>Value</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for statistic in statistics %}
|
|
||||||
<tr class="odd gradeX">
|
|
||||||
<td>
|
|
||||||
<a href="https://doc.powerdns.com/authoritative/search.html?q={{ statistic['name'] }}"
|
|
||||||
target="_blank" class="btn btn-primary">
|
|
||||||
<i class="fa fa-search"></i> {{ statistic['name'] }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>{{ statistic['value'] }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="card shadow">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title">PowerDNS Server Configuration</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<table id="tbl_configuration" class="table table-bordered table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="30%">Configuration</th>
|
|
||||||
<th>Value</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for config in configs %}
|
|
||||||
<tr class="odd gradeX">
|
|
||||||
<td>
|
|
||||||
<a href="https://doc.powerdns.com/authoritative/search.html?q={{ config['name'] }}"
|
|
||||||
target="_blank" class="btn btn-primary">
|
|
||||||
<i class="fa-solid fa-search"></i> {{ config['name'] }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ config['value'] }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block extrascripts %}
|
|
||||||
<script>
|
|
||||||
// set up statistics data table
|
|
||||||
$("#tbl_statistics").DataTable({
|
|
||||||
"paging": true,
|
|
||||||
"lengthChange": true,
|
|
||||||
"searching": true,
|
|
||||||
"ordering": true,
|
|
||||||
"info": true,
|
|
||||||
"autoWidth": false
|
|
||||||
});
|
|
||||||
|
|
||||||
// set up configuration data table
|
|
||||||
$("#tbl_configuration").DataTable({
|
|
||||||
"paging": true,
|
|
||||||
"lengthChange": true,
|
|
||||||
"searching": true,
|
|
||||||
"ordering": true,
|
|
||||||
"info": true,
|
|
||||||
"autoWidth": false
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
84
powerdnsadmin/templates/admin_server_configuration.html
Normal file
84
powerdnsadmin/templates/admin_server_configuration.html
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% set active_page = "server_configuration" %}
|
||||||
|
{% block title %}<title>Server Configuration - {{ SITE_NAME }}</title>{% endblock %}
|
||||||
|
|
||||||
|
{% block dashboard_stat %}
|
||||||
|
<div class="content-header">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mb-2">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<h1 class="m-0 text-dark">
|
||||||
|
Server Configuration
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<ol class="breadcrumb float-sm-right">
|
||||||
|
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||||
|
<li class="breadcrumb-item active">Server Configuration</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<section class="content">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">Server Configuration</h3>
|
||||||
|
</div>
|
||||||
|
<!-- /.card-header -->
|
||||||
|
<div class="card-body table-responsive">
|
||||||
|
<table id="tbl_configuration" class="table table-bordered table-striped table-hover table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Configuration</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for config in configs %}
|
||||||
|
<tr class="odd gradeX">
|
||||||
|
<td>
|
||||||
|
<a href="https://doc.powerdns.com/authoritative/search.html?q={{ config['name'] }}"
|
||||||
|
target="_blank" class="btn btn-primary">
|
||||||
|
<i class="fa-solid fa-search"></i> {{ config['name'] }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ config['value'] }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- /.card-body -->
|
||||||
|
</div>
|
||||||
|
<!-- /.card -->
|
||||||
|
</div>
|
||||||
|
<!-- /.col -->
|
||||||
|
</div>
|
||||||
|
<!-- /.row -->
|
||||||
|
</div>
|
||||||
|
<!-- /.container-fluid -->
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extrascripts %}
|
||||||
|
<script>
|
||||||
|
// Initialize DataTables
|
||||||
|
$("#tbl_configuration").DataTable({
|
||||||
|
"paging": true,
|
||||||
|
"lengthChange": true,
|
||||||
|
"searching": true,
|
||||||
|
"ordering": true,
|
||||||
|
"info": true,
|
||||||
|
"autoWidth": false
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
82
powerdnsadmin/templates/admin_server_statistics.html
Normal file
82
powerdnsadmin/templates/admin_server_statistics.html
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% set active_page = "server_statistics" %}
|
||||||
|
{% block title %}<title>Server Statistics - {{ SITE_NAME }}</title>{% endblock %}
|
||||||
|
|
||||||
|
{% block dashboard_stat %}
|
||||||
|
<div class="content-header">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mb-2">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<h1 class="m-0 text-dark">
|
||||||
|
Server Statistics
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<ol class="breadcrumb float-sm-right">
|
||||||
|
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||||
|
<li class="breadcrumb-item active">Server Statistics</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<section class="content">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">Server Statistics</h3>
|
||||||
|
</div>
|
||||||
|
<!-- /.card-header -->
|
||||||
|
<div class="card-body table-responsive">
|
||||||
|
<table id="tbl_statistics" class="table table-bordered table-striped table-hover table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="30%">Statistic</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for statistic in statistics %}
|
||||||
|
<tr class="odd gradeX">
|
||||||
|
<td>
|
||||||
|
<a href="https://doc.powerdns.com/authoritative/search.html?q={{ statistic['name'] }}"
|
||||||
|
target="_blank" class="btn btn-primary">
|
||||||
|
<i class="fa fa-search"></i> {{ statistic['name'] }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>{{ statistic['value'] }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- /.card-body -->
|
||||||
|
</div>
|
||||||
|
<!-- /.card -->
|
||||||
|
</div>
|
||||||
|
<!-- /.col -->
|
||||||
|
</div>
|
||||||
|
<!-- /.row -->
|
||||||
|
</div>
|
||||||
|
<!-- /.container-fluid -->
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extrascripts %}
|
||||||
|
<script>
|
||||||
|
// Initialize DataTables
|
||||||
|
$("#tbl_statistics").DataTable({
|
||||||
|
"paging": true,
|
||||||
|
"lengthChange": true,
|
||||||
|
"searching": true,
|
||||||
|
"ordering": true,
|
||||||
|
"info": true,
|
||||||
|
"autoWidth": false
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
@ -115,10 +115,16 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
||||||
<li class="nav-header">Administration</li>
|
<li class="nav-header">Administration</li>
|
||||||
<li class="{{ 'nav-item active' if active_page == 'admin_console' else 'nav-item' }}">
|
<li class="{{ 'nav-item active' if active_page == 'server_statistics' else 'nav-item' }}">
|
||||||
<a href="{{ url_for('admin.pdns_stats') }}" class="nav-link">
|
<a href="{{ url_for('admin.server_statistics') }}" class="nav-link">
|
||||||
<i class="nav-icon fa-solid fa-info-circle"></i>
|
<i class="nav-icon fa-solid fa-chart-simple"></i>
|
||||||
<p>PowerDNS Info</p>
|
<p>Server Statistics</p>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="{{ 'nav-item active' if active_page == 'server_configuration' else 'nav-item' }}">
|
||||||
|
<a href="{{ url_for('admin.server_configuration') }}" class="nav-link">
|
||||||
|
<i class="nav-icon fa-solid fa-cog"></i>
|
||||||
|
<p>Server Configuration</p>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="{{ 'nav-item active' if active_page == 'admin_global_search' else 'nav-item' }}">
|
<li class="{{ 'nav-item active' if active_page == 'admin_global_search' else 'nav-item' }}">
|
||||||
|
Loading…
Reference in New Issue
Block a user