From ddad1f4c648ae4e1f1197949c2ad864f422ad25c Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Thu, 4 Sep 2025 11:15:50 -0400 Subject: Add User Status Management to PodcastItLater Implement user status tracking with pending, active, and disabled states. This allows administrators to control user access and provides a mechanism for approving new users before granting full system access. Added database migration, admin interface, and authentication checks to support this feature. --- Biz/PodcastItLater/Core.py | 57 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) (limited to 'Biz/PodcastItLater/Core.py') diff --git a/Biz/PodcastItLater/Core.py b/Biz/PodcastItLater/Core.py index 542bb8b..1756fc6 100644 --- a/Biz/PodcastItLater/Core.py +++ b/Biz/PodcastItLater/Core.py @@ -125,6 +125,9 @@ class Database: # noqa: PLR0904 # Run migration to add episode metadata fields Database.migrate_add_episode_metadata(db_path) + # Run migration to add user status field + Database.migrate_add_user_status(db_path) + @staticmethod def add_to_queue( # noqa: PLR0913, PLR0917 url: str, @@ -557,6 +560,34 @@ class Database: # noqa: PLR0904 conn.commit() logger.info("Database migrated to support episode metadata fields") + @staticmethod + def migrate_add_user_status(db_path: str | None = None) -> None: + """Add status field to users table.""" + if db_path is None: + db_path = Database.get_default_db_path() + with Database.get_connection(db_path) as conn: + cursor = conn.cursor() + + # Check if column already exists + cursor.execute("PRAGMA table_info(users)") + users_info = cursor.fetchall() + users_columns = [col[1] for col in users_info] + + if "status" not in users_columns: + # Add status column with default 'pending' + cursor.execute( + "ALTER TABLE users ADD COLUMN status TEXT " + "DEFAULT 'pending'", + ) + + # Set all existing users to 'active' + cursor.execute( + "UPDATE users SET status = 'active' WHERE status IS NULL", + ) + + conn.commit() + logger.info("Database migrated to support user status") + @staticmethod def create_user(email: str, db_path: str | None = None) -> tuple[int, str]: """Create a new user and return (user_id, token). @@ -574,8 +605,8 @@ class Database: # noqa: PLR0904 cursor = conn.cursor() try: cursor.execute( - "INSERT INTO users (email, token) VALUES (?, ?)", - (email, token), + "INSERT INTO users (email, token, status) VALUES (?, ?, ?)", + (email, token, "pending"), ) conn.commit() user_id = cursor.lastrowid @@ -701,6 +732,28 @@ class Database: # noqa: PLR0904 rows = cursor.fetchall() return [dict(row) for row in rows] + @staticmethod + def update_user_status( + user_id: int, + status: str, + db_path: str | None = None, + ) -> None: + """Update user account status.""" + if db_path is None: + db_path = Database.get_default_db_path() + if status not in {"pending", "active", "disabled"}: + msg = f"Invalid status: {status}" + raise ValueError(msg) + + with Database.get_connection(db_path) as conn: + cursor = conn.cursor() + cursor.execute( + "UPDATE users SET status = ? WHERE id = ?", + (status, user_id), + ) + conn.commit() + logger.info("Updated user %s status to %s", user_id, status) + class TestDatabase(Test.TestCase): """Test the Database class.""" -- cgit v1.2.3