diff --git a/db.py b/db.py index 77d8115..1fffbbf 100644 --- a/db.py +++ b/db.py @@ -547,4 +547,30 @@ ORDER BY invocation_time DESC""", [http_function_id]) return (True, f"Imported shared environment '{env_data['name']}'", result['id']) except Exception as e: - return (False, f"Error importing environment '{env_data.get('name', 'unknown')}': {str(e)}", None) \ No newline at end of file + return (False, f"Error importing environment '{env_data.get('name', 'unknown')}': {str(e)}", None) + + def record_login(self, user_id, ip_address, user_agent, success=True, failure_reason=None): + """Record a login attempt""" + try: + self.execute( + """INSERT INTO login_history + (user_id, ip_address, user_agent, success, failure_reason) + VALUES (%s, %s, %s, %s, %s)""", + (user_id, ip_address, user_agent, success, failure_reason), + commit=True + ) + return True + except Exception as e: + print(f"Error recording login: {e}") + return False + + def get_login_history(self, user_id, limit=50): + """Get login history for a user""" + return self.execute( + """SELECT id, login_time, ip_address, user_agent, success, failure_reason + FROM login_history + WHERE user_id = %s + ORDER BY login_time DESC + LIMIT %s""", + (user_id, limit) + ) diff --git a/routes/auth.py b/routes/auth.py index 27064f2..f77504b 100644 --- a/routes/auth.py +++ b/routes/auth.py @@ -41,13 +41,20 @@ def login(): user_data = db.get_user_by_username(username) if not user_data: + # Record failed login attempt + db.record_login(None, request.remote_addr, str(request.user_agent), False, "User not found") return render_template("login.html", error="User does not exist") if not check_password_hash(user_data['password_hash'], password): + # Record failed login attempt + db.record_login(user_data['id'], request.remote_addr, str(request.user_agent), False, "Invalid password") return render_template("login.html", error="Invalid username or password") user = User(id=str(user_data['id']), username=user_data['username'], password_hash=user_data['password_hash'], created_at=user_data['created_at'], theme_preference=user_data.get('theme_preference', 'light')) + # Record successful login + db.record_login(user.id, request.remote_addr, str(request.user_agent), True) + login_user(user) next = request.args.get('next') diff --git a/routes/settings.py b/routes/settings.py index d7d7a07..414aac0 100644 --- a/routes/settings.py +++ b/routes/settings.py @@ -133,6 +133,23 @@ def database_schema(): ) return render_template("dashboard/settings/database_schema.html", schema_info=schema_info) +@settings.route("/login-history", methods=["GET"]) +@login_required +def login_history(): + """Display login history for the current user""" + user_id = current_user.id + history = db.get_login_history(user_id, limit=50) + + if htmx: + return render_block( + environment, + "dashboard/settings/login_history.html", + "page", + history=history + ) + return render_template("dashboard/settings/login_history.html", history=history) + + def get_database_schema(): """Fetch database schema information for ERD generation""" # Get all tables @@ -413,3 +430,4 @@ def import_data(): except Exception as e: return {"error": f"Import failed: {str(e)}"}, 500 + diff --git a/templates/dashboard/settings/api_keys.html b/templates/dashboard/settings/api_keys.html index 612db4e..1e18e04 100644 --- a/templates/dashboard/settings/api_keys.html +++ b/templates/dashboard/settings/api_keys.html @@ -18,6 +18,11 @@ class="border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300 py-4 px-1 text-sm font-medium cursor-pointer"> Database Schema + + Login History + diff --git a/templates/dashboard/settings/database_schema.html b/templates/dashboard/settings/database_schema.html index ac28d70..e78ac16 100644 --- a/templates/dashboard/settings/database_schema.html +++ b/templates/dashboard/settings/database_schema.html @@ -18,6 +18,11 @@ class="border-b-2 border-blue-500 text-blue-600 dark:text-blue-400 py-4 px-1 text-sm font-medium cursor-pointer"> Database Schema + + Login History + diff --git a/templates/dashboard/settings/export.html b/templates/dashboard/settings/export.html index a940121..8d426b1 100644 --- a/templates/dashboard/settings/export.html +++ b/templates/dashboard/settings/export.html @@ -18,6 +18,11 @@ class="border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300 py-4 px-1 text-sm font-medium cursor-pointer"> Database Schema + + Login History + diff --git a/templates/dashboard/settings/login_history.html b/templates/dashboard/settings/login_history.html new file mode 100644 index 0000000..e000373 --- /dev/null +++ b/templates/dashboard/settings/login_history.html @@ -0,0 +1,115 @@ +{% extends 'dashboard.html' %} + +{% block page %} +
View your recent login activity and security events
+| + Date & Time + | ++ IP Address + | ++ Browser / Device + | ++ Status + | +
|---|---|---|---|
|
+ {{ entry.login_time.strftime('%b %d, %Y') }}
+
+ {{ entry.login_time.strftime('%I:%M %p') }}
+
+ |
+ + {{ entry.ip_address or 'N/A' }} + | +
+
+ {{ entry.user_agent or 'Unknown' }}
+
+ |
+ + {% if entry.success %} + + Success + + {% else %} + + Failed + + {% endif %} + | +
+ Your login activity will appear here +
+Showing last {{ history|length }} login{% if history|length != 1 %}s{% endif %}. Login history is kept for + security purposes.
+