fix dashboard table layout
This commit is contained in:
12
app.py
12
app.py
@@ -330,9 +330,8 @@ def init_app():
|
|||||||
settings.log_level = 'WARNING'
|
settings.log_level = 'WARNING'
|
||||||
db.session.add(settings)
|
db.session.add(settings)
|
||||||
|
|
||||||
# Create default admin if not exists
|
# Create default admin only if no users exist in the database
|
||||||
admin = User.query.filter_by(email='superadmin@example.com').first()
|
if User.query.first() is None:
|
||||||
if not admin:
|
|
||||||
hashed_password = bcrypt.generate_password_hash('adminsuper').decode('utf-8')
|
hashed_password = bcrypt.generate_password_hash('adminsuper').decode('utf-8')
|
||||||
admin = User(
|
admin = User(
|
||||||
username='superadmin',
|
username='superadmin',
|
||||||
@@ -347,12 +346,11 @@ def init_app():
|
|||||||
# Create initial API key for admin
|
# Create initial API key for admin
|
||||||
api_key = ApiKey(
|
api_key = ApiKey(
|
||||||
key=ApiKey.generate_key(),
|
key=ApiKey.generate_key(),
|
||||||
description="Initial Admin API Key",
|
description="Initial Admin API Key"
|
||||||
user_id=admin.id
|
|
||||||
)
|
)
|
||||||
db.session.add(api_key)
|
db.session.add(api_key)
|
||||||
|
db.session.commit()
|
||||||
db.session.commit()
|
logger.info("Created initial admin account and API key")
|
||||||
|
|
||||||
# Initialize the application
|
# Initialize the application
|
||||||
init_app()
|
init_app()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app
|
from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from auth.models import User, Company, UserCompany
|
from auth.models import User, Company, UserCompany, ApiKey
|
||||||
from api.models import Log
|
from api.models import Log
|
||||||
from extensions import db
|
from extensions import db
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
@@ -73,14 +73,21 @@ def dashboard():
|
|||||||
end_date = datetime.now(app_tz) + timedelta(hours=1) # Include 1 hour in the future
|
end_date = datetime.now(app_tz) + timedelta(hours=1) # Include 1 hour in the future
|
||||||
start_date = end_date - timedelta(hours=49) # Look back 48 hours from future end time
|
start_date = end_date - timedelta(hours=49) # Look back 48 hours from future end time
|
||||||
|
|
||||||
# Get company filter if provided
|
# Get companies for the dropdown filter based on user role, ordered by name
|
||||||
company_id = request.args.get('company_id', type=int)
|
if current_user.is_global_admin():
|
||||||
|
# GlobalAdmin can see all companies
|
||||||
|
companies = Company.query.order_by(Company.name).all()
|
||||||
|
else:
|
||||||
|
# Get user's companies, ordered by name
|
||||||
|
user_company_ids = [uc.company_id for uc in current_user.companies]
|
||||||
|
companies = Company.query.filter(Company.id.in_(user_company_ids)).order_by(Company.name).all()
|
||||||
|
|
||||||
# Build base query with date range filter and eagerly load relationships
|
# Get company filter if provided, otherwise use first available company
|
||||||
from auth.models import ApiKey, Company
|
company_id = request.args.get('company_id', type=int)
|
||||||
|
if not company_id and companies:
|
||||||
|
company_id = companies[0].id # Default to first company
|
||||||
|
|
||||||
# Convert timezone-aware dates to naive for comparison with database timestamps
|
# Convert timezone-aware dates to naive for comparison with database timestamps
|
||||||
# since the log timestamps are stored as naive datetime objects
|
|
||||||
start_date_naive = start_date.replace(tzinfo=None)
|
start_date_naive = start_date.replace(tzinfo=None)
|
||||||
end_date_naive = end_date.replace(tzinfo=None)
|
end_date_naive = end_date.replace(tzinfo=None)
|
||||||
|
|
||||||
@@ -90,39 +97,22 @@ def dashboard():
|
|||||||
db.joinedload(Log.company)
|
db.joinedload(Log.company)
|
||||||
).filter(Log.timestamp.between(start_date_naive, end_date_naive))
|
).filter(Log.timestamp.between(start_date_naive, end_date_naive))
|
||||||
|
|
||||||
# Apply company-specific filtering based on user role
|
# Apply company-specific filtering
|
||||||
if current_user.is_global_admin():
|
if current_user.is_global_admin():
|
||||||
# GlobalAdmin should be allowed to see all records, no matter what company/site
|
# GlobalAdmin with specific company selected
|
||||||
if company_id:
|
if company_id:
|
||||||
query = query.filter(Log.company_id == company_id)
|
query = query.filter(Log.company_id == company_id)
|
||||||
# If no company_id specified, show all logs (no additional filtering)
|
|
||||||
else:
|
else:
|
||||||
# CompanyAdmin and User should see only company log events and API Keys (sites)
|
# Regular users always need company filtering
|
||||||
# for companies they are member of
|
|
||||||
user_company_ids = [uc.company_id for uc in current_user.companies]
|
user_company_ids = [uc.company_id for uc in current_user.companies]
|
||||||
|
if company_id and company_id in user_company_ids:
|
||||||
if not user_company_ids:
|
query = query.filter(Log.company_id == company_id)
|
||||||
# If user has no company associations, show no logs
|
|
||||||
query = query.filter(Log.id == -1) # Impossible condition = no results
|
|
||||||
else:
|
else:
|
||||||
if company_id and company_id in user_company_ids:
|
query = query.filter(Log.company_id.in_(user_company_ids))
|
||||||
# Filter by the specific company if requested and user has access
|
|
||||||
query = query.filter(Log.company_id == company_id)
|
|
||||||
else:
|
|
||||||
# Show logs from all companies the user has access to
|
|
||||||
query = query.filter(Log.company_id.in_(user_company_ids))
|
|
||||||
|
|
||||||
# Get the logs ordered by timestamp (newest first)
|
# Get the logs ordered by timestamp (newest first)
|
||||||
logs = query.order_by(Log.timestamp.desc()).all()
|
logs = query.order_by(Log.timestamp.desc()).all()
|
||||||
|
|
||||||
# Get companies for the dropdown filter based on user role
|
|
||||||
if current_user.is_global_admin():
|
|
||||||
# GlobalAdmin can see all companies
|
|
||||||
companies = Company.query.all()
|
|
||||||
else:
|
|
||||||
# CompanyAdmin and User should see only companies they are member of
|
|
||||||
companies = current_user.get_companies()
|
|
||||||
|
|
||||||
return render_template('frontend/dashboard.html',
|
return render_template('frontend/dashboard.html',
|
||||||
title='Dashboard',
|
title='Dashboard',
|
||||||
logs=logs,
|
logs=logs,
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ pillow
|
|||||||
Flask-Migrate
|
Flask-Migrate
|
||||||
|
|
||||||
# serving the application
|
# serving the application
|
||||||
waitress # Production WSGI server for Windows (better than Gunicorn for Windows)
|
#waitress # Production WSGI server for Windows (better than Gunicorn for Windows)
|
||||||
|
redis
|
||||||
gunicorn # Production WSGI server (Linux/Unix only - won't work on Windows)
|
gunicorn # Production WSGI server (Linux/Unix only - won't work on Windows)
|
||||||
#psutil # System monitoring for health checks
|
#psutil # System monitoring for health checks
|
||||||
#click # CLI tools (for database backups)
|
#click # CLI tools (for database backups)
|
||||||
|
|||||||
@@ -181,11 +181,9 @@
|
|||||||
<input type="text" id="daterange" name="daterange" class="form-control"
|
<input type="text" id="daterange" name="daterange" class="form-control"
|
||||||
value="{{ start_date.strftime('%Y-%m-%d %H:%M') if start_date else '' }} - {{ end_date.strftime('%Y-%m-%d %H:%M') if end_date else '' }}"/>
|
value="{{ start_date.strftime('%Y-%m-%d %H:%M') if start_date else '' }} - {{ end_date.strftime('%Y-%m-%d %H:%M') if end_date else '' }}"/>
|
||||||
</div>
|
</div>
|
||||||
{% if companies %}
|
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label for="company_id" class="form-label">Company:</label>
|
<label for="company_id" class="form-label">Company:</label>
|
||||||
<select class="form-select" id="company_id" name="company_id">
|
<select class="form-select" id="company_id" name="company_id" required>
|
||||||
<option value="">All Companies</option>
|
|
||||||
{% for company in companies %}
|
{% for company in companies %}
|
||||||
<option value="{{ company.id }}" {% if selected_company_id == company.id %}selected{% endif %}>
|
<option value="{{ company.id }}" {% if selected_company_id == company.id %}selected{% endif %}>
|
||||||
{{ company.name }}
|
{{ company.name }}
|
||||||
@@ -193,7 +191,6 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<button type="submit" class="btn btn-primary">Apply Filter</button>
|
<button type="submit" class="btn btn-primary">Apply Filter</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -210,9 +207,7 @@
|
|||||||
<th>Timestamp</th>
|
<th>Timestamp</th>
|
||||||
<th>Event Type</th>
|
<th>Event Type</th>
|
||||||
<th>User Name</th>
|
<th>User Name</th>
|
||||||
{% if current_user.is_global_admin() and not selected_company_id %}
|
<th>Company</th>
|
||||||
<th>Company</th>
|
|
||||||
{% endif %}
|
|
||||||
<th>Site</th>
|
<th>Site</th>
|
||||||
<th>Computer Name</th>
|
<th>Computer Name</th>
|
||||||
<th>Local IP</th>
|
<th>Local IP</th>
|
||||||
@@ -227,9 +222,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>{{ log.event_type }}</td>
|
<td>{{ log.event_type }}</td>
|
||||||
<td>{{ log.user_name }}</td>
|
<td>{{ log.user_name }}</td>
|
||||||
{% if current_user.is_global_admin() and not selected_company_id %}
|
<td>{{ log.company.name if log.company else '' }}</td>
|
||||||
<td>{{ log.company.name if log.company else '' }}</td>
|
|
||||||
{% endif %}
|
|
||||||
<td>{{ log.api_key.description if log.api_key else '' }}</td>
|
<td>{{ log.api_key.description if log.api_key else '' }}</td>
|
||||||
<td>{{ log.computer_name }}</td>
|
<td>{{ log.computer_name }}</td>
|
||||||
<td>{{ log.local_ip or '' }}</td>
|
<td>{{ log.local_ip or '' }}</td>
|
||||||
@@ -317,9 +310,14 @@
|
|||||||
'<"row"<"col-sm-12 col-md-5"i><"col-sm-12 col-md-7"p>>',
|
'<"row"<"col-sm-12 col-md-5"i><"col-sm-12 col-md-7"p>>',
|
||||||
columnDefs: [
|
columnDefs: [
|
||||||
{
|
{
|
||||||
targets: [5, 6, 7], // Computer Name, Local IP, and Public IP columns are now at indices 5, 6, and 7
|
targets: [5, 6, 7], // Computer Name, Local IP, and Public IP columns
|
||||||
visible: false, // Hide by default
|
visible: false, // Hide by default
|
||||||
searchable: true // Still allow searching in these columns
|
searchable: true // Still allow searching in these columns
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targets: [3], // Company column
|
||||||
|
visible: {% if current_user.is_global_admin() %}true{% else %}false{% endif %},
|
||||||
|
searchable: {% if current_user.is_global_admin() %}true{% else %}false{% endif %}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
buttons: [
|
buttons: [
|
||||||
@@ -364,11 +362,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Column names for the visibility controls
|
// Column names for the visibility controls
|
||||||
var columnNames = ['Timestamp', 'Event Type', 'User Name'];
|
var columnNames = ['Timestamp', 'Event Type', 'User Name', 'Company', 'Site', 'Computer Name', 'Local IP', 'Public IP'];
|
||||||
{% if current_user.is_global_admin() and not selected_company_id %}
|
|
||||||
columnNames.push('Company');
|
|
||||||
{% endif %}
|
|
||||||
columnNames.push('Site', 'Computer Name', 'Local IP', 'Public IP');
|
|
||||||
|
|
||||||
// Load saved column visibility from localStorage
|
// Load saved column visibility from localStorage
|
||||||
function loadColumnVisibility() {
|
function loadColumnVisibility() {
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ class ConfigManager:
|
|||||||
|
|
||||||
'logging': {
|
'logging': {
|
||||||
'DB_LOGGING_ENABLED': 'true',
|
'DB_LOGGING_ENABLED': 'true',
|
||||||
'DB_LOGGING_FILTERED_LOGGERS': 'watchfiles.main,watchfiles.watcher,watchdog,uvicorn.access,__mp_main__,__main__,app',
|
'DB_LOGGING_FILTERED_LOGGERS': 'watchfiles.main,watchfiles.watcher,watchdog,uvicorn.access,__mp_main__,__main__,app,werkzeug',
|
||||||
'DB_LOGGING_FILTERED_PATTERNS': 'database.db,instance/,file changed,reloading',
|
'DB_LOGGING_FILTERED_PATTERNS': 'database.db,instance/,file changed,reloading',
|
||||||
'FILTER_FILE_WATCHER_LOGS': 'true',
|
'FILTER_FILE_WATCHER_LOGS': 'true',
|
||||||
'DB_LOGGING_DEDUPE_INTERVAL': '1',
|
'DB_LOGGING_DEDUPE_INTERVAL': '1',
|
||||||
|
|||||||
Reference in New Issue
Block a user