Development
This commit is contained in:
parent
fff0835553
commit
19be151c70
7 changed files with 230 additions and 31 deletions
|
|
@ -56,4 +56,10 @@ def form_login():
|
|||
session['apply_changes_immediately'] = False
|
||||
session.permanent = True
|
||||
|
||||
import uuid as _uuid
|
||||
sid = str(_uuid.uuid4())
|
||||
session['session_id'] = sid
|
||||
import config_utils as _cu
|
||||
_cu.record_session_login(sid, account['email_address'], account.get('access_level', 'viewer'))
|
||||
|
||||
return redirect('/overview')
|
||||
|
|
|
|||
|
|
@ -66,6 +66,40 @@ def accounts_add():
|
|||
return redirect(f'/{_PAGE}')
|
||||
|
||||
|
||||
@bp.route('/action/accountmanage/accounts_edit', methods=['POST'])
|
||||
@auth.require_level('manager')
|
||||
def accounts_edit():
|
||||
try:
|
||||
row_index = int(request.form.get('row_index', ''))
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid request.', 'error')
|
||||
return redirect(f'/{_PAGE}')
|
||||
|
||||
access_level = request.form.get('access_level', '').strip()
|
||||
if access_level not in VALID_LEVELS:
|
||||
flash('Invalid access level.', 'error')
|
||||
return redirect(f'/{_PAGE}')
|
||||
|
||||
data = _load_accounts()
|
||||
accounts = data.get('accounts', [])
|
||||
|
||||
if row_index < 0 or row_index >= len(accounts):
|
||||
flash('Account not found.', 'error')
|
||||
return redirect(f'/{_PAGE}')
|
||||
|
||||
target = accounts[row_index]
|
||||
if target.get('email_address', '').lower() == session.get('email_address', '').lower():
|
||||
flash('You cannot change your own access level.', 'error')
|
||||
return redirect(f'/{_PAGE}')
|
||||
|
||||
accounts[row_index]['access_level'] = access_level
|
||||
data['accounts'] = accounts
|
||||
_save_accounts(data)
|
||||
|
||||
flash('Account updated.', 'success')
|
||||
return redirect(f'/{_PAGE}')
|
||||
|
||||
|
||||
@bp.route('/action/accountmanage/accounts_delete', methods=['POST'])
|
||||
@auth.require_level('manager')
|
||||
def accounts_delete():
|
||||
|
|
|
|||
|
|
@ -11,38 +11,48 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"type": "table",
|
||||
"datasource": "config:accounts",
|
||||
"empty_message": "No accounts configured.",
|
||||
"columns": [
|
||||
"type": "card",
|
||||
"label": "Active Sessions",
|
||||
"items": [
|
||||
{
|
||||
"label": "Email Address",
|
||||
"field": "email_address"
|
||||
},
|
||||
{
|
||||
"label": "Access Level",
|
||||
"field": "access_level"
|
||||
},
|
||||
{
|
||||
"label": "Added By",
|
||||
"field": "account_created_by"
|
||||
},
|
||||
{
|
||||
"label": "Added",
|
||||
"field": "account_created_utc"
|
||||
},
|
||||
{
|
||||
"label": "Status",
|
||||
"field": "account_status",
|
||||
"render": "badge_active_inactive"
|
||||
"type": "raw_html",
|
||||
"html": "%ACTIVE_SESSIONS_TABLE%"
|
||||
}
|
||||
],
|
||||
"row_actions": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"label": "User Accounts",
|
||||
"items": [
|
||||
{
|
||||
"action": "/action/accountmanage/accounts_delete",
|
||||
"method": "post",
|
||||
"text": "Remove",
|
||||
"class": "btn-danger btn-sm"
|
||||
"type": "table",
|
||||
"datasource": "config:accounts",
|
||||
"empty_message": "No accounts configured.",
|
||||
"columns": [
|
||||
{"label": "Email Address", "field": "email_address"},
|
||||
{"label": "Access Level", "field": "access_level"},
|
||||
{"label": "Added By", "field": "account_created_by"},
|
||||
{"label": "Added", "field": "account_created_utc"},
|
||||
{
|
||||
"label": "Status",
|
||||
"field": "account_status",
|
||||
"render": "badge_active_inactive"
|
||||
}
|
||||
],
|
||||
"row_actions": [
|
||||
{
|
||||
"method": "js_edit",
|
||||
"target": "edit-form",
|
||||
"text": "Edit",
|
||||
"class": "btn-ghost btn-sm"
|
||||
},
|
||||
{
|
||||
"action": "/action/accountmanage/accounts_delete",
|
||||
"method": "post",
|
||||
"text": "Delete",
|
||||
"class": "btn-danger btn-sm"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -84,6 +94,43 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"id": "edit-form",
|
||||
"label": "Edit Account",
|
||||
"hidden": true,
|
||||
"items": [
|
||||
{
|
||||
"type": "form",
|
||||
"action": "/action/accountmanage/accounts_edit",
|
||||
"method": "post",
|
||||
"items": [
|
||||
{"type": "hidden", "name": "row_index", "value": ""},
|
||||
{
|
||||
"type": "field",
|
||||
"label": "Email Address",
|
||||
"name": "email_address_display",
|
||||
"input_type": "text",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"label": "Access Level",
|
||||
"name": "access_level",
|
||||
"input_type": "select",
|
||||
"options": "%ACCOUNT_LEVEL_OPTIONS%"
|
||||
},
|
||||
{
|
||||
"type": "button_row",
|
||||
"items": [
|
||||
{"type": "button_primary", "text": "Save Changes"},
|
||||
{"type": "button_cancel", "text": "Cancel"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,45 @@
|
|||
import json
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
import config_utils
|
||||
import factory
|
||||
|
||||
|
||||
def _fmt_ts(ts):
|
||||
try:
|
||||
dt = datetime.fromtimestamp(int(ts), tz=timezone.utc)
|
||||
return dt.strftime('%Y-%m-%d %H:%M UTC')
|
||||
except Exception:
|
||||
return '-'
|
||||
|
||||
|
||||
def _active_sessions_table():
|
||||
rows = config_utils.get_active_sessions()
|
||||
no_data = '<p class="text-muted" style="margin:0">No active sessions.</p>'
|
||||
if not rows:
|
||||
return no_data
|
||||
now = int(time.time())
|
||||
trs = ''
|
||||
for email, access_level, logged_in_at, last_seen in rows:
|
||||
ago = config_utils.relative_time(int(last_seen), now)
|
||||
trs += (
|
||||
f'<tr>'
|
||||
f'<td class="table-cell">{factory.e(email)}</td>'
|
||||
f'<td class="table-cell">{factory.e(access_level)}</td>'
|
||||
f'<td class="table-cell">{_fmt_ts(logged_in_at)}</td>'
|
||||
f'<td class="table-cell">{factory.e(ago)} ago</td>'
|
||||
f'</tr>'
|
||||
)
|
||||
return (
|
||||
'<table class="data-table"><thead><tr>'
|
||||
'<th class="table-header">Email</th>'
|
||||
'<th class="table-header">Access Level</th>'
|
||||
'<th class="table-header">Logged In</th>'
|
||||
'<th class="table-header">Last Seen</th>'
|
||||
'</tr></thead><tbody>' + trs + '</tbody></table>'
|
||||
)
|
||||
|
||||
|
||||
def collect_tokens(cfg):
|
||||
tokens = config_utils.collect_layout_tokens(cfg)
|
||||
tokens['ACCOUNT_LEVEL_OPTIONS'] = json.dumps([
|
||||
|
|
@ -10,6 +47,7 @@ def collect_tokens(cfg):
|
|||
{'value': 'administrator', 'label': 'Administrator (can modify configuration)'},
|
||||
{'value': 'manager', 'label': 'Manager (full access including account management)'},
|
||||
])
|
||||
tokens['ACTIVE_SESSIONS_TABLE'] = _active_sessions_table()
|
||||
content = factory.load_json(f'{factory.PAGES_DIR}/accountmanage/content.json')
|
||||
for table_item in factory.iter_table_items(content.get('items', [])):
|
||||
ds = table_item.get('datasource', '')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue