Overview
The Admin Panel provides privileged users with tools to manage the FinAI platform, including user administration, AI monitoring, and system maintenance. Access is restricted through a role-based access control (RBAC) system.
Access Control
RBAC Implementation
FinAI uses a decorator-based RBAC system to protect administrative routes:
# From app/routes/admin.py:12-20
def admin_required ( f ):
@wraps (f)
def decorated_function ( * args , ** kwargs ):
# Check if user is logged in
if 'user_id' not in session:
return redirect(url_for( 'auth.login' ))
# Verify admin role
if session.get( 'user_role' ) != 'admin' :
flash( 'Bạn không có quyền truy cập trang này!' , 'error' )
return redirect(url_for( 'views.dashboard' ))
return f( * args, ** kwargs)
return decorated_function
Non-admin users attempting to access admin routes are redirected to the dashboard with an error message.
User Roles
The User model defines two roles:
# From app/models.py:8-18
class User ( db . Model ):
__tablename__ = 'nguoidung'
id = db.Column( 'MaNguoiDung' , db.String( 8 ), primary_key = True )
name = db.Column( 'HoTen' , db.String( 100 ))
email = db.Column( 'Email' , db.String( 100 ), unique = True , nullable = False )
password_hash = db.Column( 'MatKhau' , db.String( 200 ), nullable = False )
role = db.Column( 'VaiTro' , db.String( 20 ), default = 'user' ) # 'user' or 'admin'
status = db.Column( 'TrangThai' , db.Integer, default = 1 )
created_at = db.Column( 'NgayTao' , db.DateTime, default = datetime.now)
last_login = db.Column( 'LanDangNhapCuoi' , db.DateTime)
Role Values:
user - Standard user with access to personal financial features
admin - Administrator with system-wide management capabilities
New accounts are created with role='user' by default. Admin privileges must be granted manually via database updates.
Admin Features
User Management
View and manage all registered users.
Route: /admin/users
# From app/routes/admin.py:23-27
@admin_bp.route ( '/admin/users' )
@admin_required
def users ():
users = User.query.all()
return render_template( 'admin/users.html' , users = users)
Access User List
Navigate to Admin → Users to view all registered accounts.
View User Details
The list displays:
User ID (MaNguoiDung)
Name (HoTen)
Email
Role (VaiTro)
Status (TrangThai)
Registration date (NgayTao)
Last login (LanDangNhapCuoi)
Manage Accounts
Perform actions such as viewing transaction history, modifying roles, or deactivating accounts.
User Relationships
The User model provides relationships to access all user data:
# From app/models.py:20-26
# Relationships for easy data access
settings = db.relationship( 'UserSetting' , backref = 'user' , uselist = False , lazy = True )
wallets = db.relationship( 'Wallet' , backref = 'user' , lazy = True )
categories = db.relationship( 'Category' , backref = 'user' , lazy = True )
transactions = db.relationship( 'Transaction' , backref = 'user' , lazy = True )
budgets = db.relationship( 'Budget' , backref = 'user' , lazy = True )
Example Usage:
user = User.query.get( 'U0001' )
print ( f "Total wallets: { len (user.wallets) } " )
print ( f "Total transactions: { len (user.transactions) } " )
print ( f "AI enabled: { user.settings.ai_suggestions } " )
Category Management
Configure system-wide and user-specific categories.
Route: /admin/categories
# From app/routes/admin.py:29-32
@admin_bp.route ( '/admin/categories' )
@admin_required
def categories ():
return render_template( 'admin/categories.html' )
Category Model Categories can be hierarchical with parent-child relationships: # From app/models.py:63-73
class Category ( db . Model ):
__tablename__ = 'danhmuc'
id = db.Column( 'MaDanhMuc' , db.String( 8 ), primary_key = True )
user_id = db.Column( 'MaNguoiDung' , db.String( 8 ),
db.ForeignKey( 'nguoidung.MaNguoiDung' ,
ondelete = 'CASCADE' ))
name = db.Column( 'TenDanhMuc' , db.String( 100 ), nullable = False )
type = db.Column( 'LoaiDanhMuc' , db.String( 10 ), nullable = False ) # 'thu' or 'chi'
parent_id = db.Column( 'MaDanhMucCha' , db.String( 8 ),
db.ForeignKey( 'danhmuc.MaDanhMuc' ,
ondelete = 'SET NULL' ))
# Recursive relationship for subcategories
children = db.relationship( 'Category' ,
backref = db.backref( 'parent' , remote_side = [ id ]),
lazy = True )
Category Types:
thu - Income categories
chi - Expense categories
AI Monitoring
Route: /admin/ai-monitoring
# From app/routes/admin.py:34-37
@admin_bp.route ( '/admin/ai-monitoring' )
@admin_required
def ai_monitoring ():
return render_template( 'admin/ai_monitoring.html' )
Monitor the performance of AI-powered category suggestions.
Track AI predictions and user feedback: # From app/models.py:134-143
class AILog ( db . Model ):
__tablename__ = 'ai_lichsu'
id = db.Column( 'MaAI_Log' , db.String( 8 ), primary_key = True )
user_id = db.Column( 'MaNguoiDung' , db.String( 8 ),
db.ForeignKey( 'nguoidung.MaNguoiDung' ,
ondelete = 'CASCADE' ))
transaction_id = db.Column( 'MaGiaoDich' , db.String( 8 ),
db.ForeignKey( 'giaodich.MaGiaoDich' ,
ondelete = 'SET NULL' ))
predicted_cat = db.Column( 'DanhMucDuDoan' , db.String( 8 ),
db.ForeignKey( 'danhmuc.MaDanhMuc' ,
ondelete = 'CASCADE' ))
actual_cat = db.Column( 'DanhMucChinhXac' , db.String( 8 ),
db.ForeignKey( 'danhmuc.MaDanhMuc' ,
ondelete = 'CASCADE' ))
confidence = db.Column( 'DoTinCay' , db.Float)
feedback = db.Column( 'PhanHoi' , db.String( 50 )) # 'dung' or 'sai'
created_at = db.Column( 'NgayTao' , db.DateTime, default = datetime.now)
Monitor these key performance indicators: Accuracy Metrics:
Prediction accuracy rate
Average confidence scores
User correction frequency
Usage Metrics:
Total predictions made
Predictions by category
Feedback distribution (dung vs sai)
Query Examples: # Calculate accuracy rate
total_logs = AILog.query.count()
correct_logs = AILog.query.filter(AILog.feedback == 'dung' ).count()
accuracy = (correct_logs / total_logs * 100 ) if total_logs > 0 else 0
# Average confidence
avg_confidence = db.session.query(
func.avg(AILog.confidence)
).scalar()
Chatbot Monitoring
Route: /admin/chatbot-logs
# From app/routes/admin.py:39-42
@admin_bp.route ( '/admin/chatbot-logs' )
@admin_required
def chatbot_logs ():
return render_template( 'admin/chatbot_logs.html' )
View conversation history and analyze chatbot interactions.
ChatbotLog Model:
# From app/models.py:152-158
class ChatbotLog ( db . Model ):
__tablename__ = 'chatbot_lichsu'
id = db.Column( 'MaHoiThoai' , db.String( 8 ), primary_key = True )
user_id = db.Column( 'MaNguoiDung' , db.String( 8 ),
db.ForeignKey( 'nguoidung.MaNguoiDung' ,
ondelete = 'CASCADE' ))
question = db.Column( 'NoiDungHoi' , db.Text)
answer = db.Column( 'NoiDungTraLoi' , db.Text)
created_at = db.Column( 'NgayTao' , db.DateTime, default = datetime.now)
System Maintenance
Log Cleanup
Automatically remove old chatbot logs to maintain database performance.
API Endpoint: DELETE /api/admin/cleanup-logs
# From app/routes/admin.py:44-55
@admin_bp.route ( '/api/admin/cleanup-logs' , methods = [ 'DELETE' ])
@admin_required
def cleanup_logs ():
try :
# Delete logs older than 30 days
expiration_date = datetime.now() - timedelta( days = 30 )
deleted_count = ChatbotLog.query.filter(
ChatbotLog.created_at < expiration_date
).delete()
db.session.commit()
return jsonify({
'status' : 'success' ,
'message' : f 'Đã xóa { deleted_count } tin nhắn cũ hơn 30 ngày.'
})
except Exception as e:
db.session.rollback()
return jsonify({ 'status' : 'error' , 'message' : str (e)}), 500
Navigate to Chatbot Logs
Access Admin → Chatbot Logs .
Review Old Logs
Check the date range of existing logs.
Execute Cleanup
Click Cleanup Old Logs to remove entries older than 30 days.
Verify Results
The system returns the count of deleted records.
Log cleanup is irreversible. Ensure you have backups if historical data is needed for analysis.
Security Features
Two-Factor Authentication
Administrators can manage 2FA settings for users:
# From app/models.py:145-150
class TwoFactorAuth ( db . Model ):
__tablename__ = 'xacthuc2fa'
user_id = db.Column( 'MaNguoiDung' , db.String( 8 ),
db.ForeignKey( 'nguoidung.MaNguoiDung' ,
ondelete = 'CASCADE' ), primary_key = True )
secret_key = db.Column( 'SecretKey' , db.String( 100 ), nullable = False )
is_active = db.Column( 'DaKichHoat' , db.Integer, default = 0 )
backup_code = db.Column( 'MaDuPhong' , db.String( 200 ))
Fields:
secret_key - TOTP secret for authenticator apps
is_active - 1 if 2FA is enabled, 0 if disabled
backup_code - Emergency access codes
Password Reset Management
# From app/models.py:160-164
class PasswordResetToken ( db . Model ):
__tablename__ = 'password_reset_tokens'
email = db.Column( 'Email' , db.String( 100 ),
db.ForeignKey( 'nguoidung.Email' ,
ondelete = 'CASCADE' ), primary_key = True )
token = db.Column( 'Token' , db.String( 100 ), nullable = False )
expires_at = db.Column( 'ThoiGianHetHan' , db.DateTime, nullable = False )
Administrators can:
View active reset tokens
Revoke compromised tokens
Monitor reset request patterns
Admin Blueprint Structure
# From app/routes/admin.py:1-9
from flask import Blueprint, render_template, session, redirect, url_for, flash, jsonify
from functools import wraps
from datetime import datetime, timedelta
from app import db
from app.models import User, ChatbotLog
# Blueprint declaration
admin_bp = Blueprint( 'admin' , __name__ )
Available Routes:
Route Method Purpose /admin/usersGET List all users /admin/categoriesGET Manage categories /admin/ai-monitoringGET View AI performance /admin/chatbot-logsGET View chatbot history /api/admin/cleanup-logsDELETE Remove old logs
All routes are protected by the @admin_required decorator and require an active admin session.
Best Practices
Regular Monitoring
Review AI accuracy metrics weekly
Monitor user growth and activity patterns
Check for unusual login patterns or security issues
Data Maintenance
Run log cleanup monthly to optimize database size
Archive important logs before deletion
Monitor database growth and plan capacity accordingly
Security Audits
Regularly review admin access logs
Verify that only authorized users have admin roles
Monitor password reset requests for suspicious activity
Troubleshooting
Access Denied
If you receive “Bạn không có quyền truy cập trang này!”:
Verify your account has role='admin' in the database
Check that your session is active
Log out and log back in to refresh session variables
Database Errors
If cleanup or other operations fail:
# The system automatically rolls back on errors
db.session.rollback()
Check application logs for specific error messages and stack traces.