Make sessions persistent across server reboots

- Added sessions table to database for persistent session storage
- Modified SessionManager to use database instead of in-memory storage
- Added database functions for session management
- Remember me functionality now works across server reboots
parent 3a865ff1
...@@ -21,47 +21,33 @@ Authentication and session management for Video AI. ...@@ -21,47 +21,33 @@ Authentication and session management for Video AI.
import time import time
import secrets import secrets
from typing import Optional, Dict, Any from typing import Optional, Dict, Any
from .database import authenticate_user, validate_api_token from .database import authenticate_user, validate_api_token, create_persistent_session, get_persistent_session, destroy_persistent_session
class SessionManager: class SessionManager:
"""Simple session manager using in-memory storage.""" """Session manager using persistent database storage."""
def __init__(self): def __init__(self):
self.sessions: Dict[str, Dict[str, Any]] = {}
self.session_timeout = 3600 # 1 hour self.session_timeout = 3600 # 1 hour
def create_session(self, user: Dict[str, Any]) -> str: def create_session(self, user: Dict[str, Any]) -> str:
"""Create a new session for user.""" """Create a new persistent session for user."""
session_id = secrets.token_hex(32) return create_persistent_session(user['id'])
self.sessions[session_id] = {
'user': user,
'created_at': time.time(),
'last_activity': time.time()
}
return session_id
def get_session(self, session_id: str) -> Optional[Dict[str, Any]]: def get_session(self, session_id: str) -> Optional[Dict[str, Any]]:
"""Get session data.""" """Get session data from database."""
if session_id in self.sessions: return get_persistent_session(session_id)
session = self.sessions[session_id]
if time.time() - session['last_activity'] > self.session_timeout:
# Session expired
del self.sessions[session_id]
return None
session['last_activity'] = time.time()
return session
return None
def destroy_session(self, session_id: str) -> None: def destroy_session(self, session_id: str) -> None:
"""Destroy session.""" """Destroy persistent session."""
if session_id in self.sessions: destroy_persistent_session(session_id)
del self.sessions[session_id]
def get_user_from_session(self, session_id: str) -> Optional[Dict[str, Any]]: def get_user_from_session(self, session_id: str) -> Optional[Dict[str, Any]]:
"""Get user from session.""" """Get user from persistent session."""
session = self.get_session(session_id) session = self.get_session(session_id)
return session['user'] if session else None if session:
return session['user']
return None
# Global session manager instance # Global session manager instance
......
...@@ -345,6 +345,28 @@ def init_db(conn) -> None: ...@@ -345,6 +345,28 @@ def init_db(conn) -> None:
) )
''') ''')
# Sessions table for persistent sessions
if config['type'] == 'mysql':
cursor.execute('''
CREATE TABLE IF NOT EXISTS sessions (
session_id VARCHAR(255) PRIMARY KEY,
user_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_activity TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
''')
else:
cursor.execute('''
CREATE TABLE IF NOT EXISTS sessions (
session_id TEXT PRIMARY KEY,
user_id INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_activity TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)
''')
# Remember tokens table # Remember tokens table
if config['type'] == 'mysql': if config['type'] == 'mysql':
cursor.execute(''' cursor.execute('''
...@@ -1315,3 +1337,76 @@ def delete_expired_remember_tokens() -> None: ...@@ -1315,3 +1337,76 @@ def delete_expired_remember_tokens() -> None:
cursor.execute('DELETE FROM remember_tokens WHERE expires_at <= ?', (int(time.time()),)) cursor.execute('DELETE FROM remember_tokens WHERE expires_at <= ?', (int(time.time()),))
conn.commit() conn.commit()
conn.close() conn.close()
# Session management functions
def create_persistent_session(user_id: int) -> str:
"""Create a persistent session in database."""
import secrets
session_id = secrets.token_hex(32)
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('INSERT INTO sessions (session_id, user_id) VALUES (?, ?)', (session_id, user_id))
conn.commit()
conn.close()
return session_id
def get_persistent_session(session_id: str) -> Optional[Dict[str, Any]]:
"""Get persistent session from database."""
import time
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('''
SELECT s.session_id, s.user_id, s.created_at, s.last_activity, u.id, u.username, u.email, u.role, u.active
FROM sessions s
JOIN users u ON s.user_id = u.id
WHERE s.session_id = ? AND u.active = 1
''', (session_id,))
row = cursor.fetchone()
if row:
# Update last activity
cursor.execute('UPDATE sessions SET last_activity = CURRENT_TIMESTAMP WHERE session_id = ?', (session_id,))
conn.commit()
# Check if session is expired (1 hour)
import time
last_activity = time.mktime(time.strptime(str(row['last_activity']), '%Y-%m-%d %H:%M:%S'))
if time.time() - last_activity > 3600: # 1 hour
cursor.execute('DELETE FROM sessions WHERE session_id = ?', (session_id,))
conn.commit()
conn.close()
return None
user = {
'id': row['id'],
'username': row['username'],
'email': row['email'],
'role': row['role'],
'active': row['active']
}
conn.close()
return {'user': user}
conn.close()
return None
def destroy_persistent_session(session_id: str) -> None:
"""Destroy persistent session."""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('DELETE FROM sessions WHERE session_id = ?', (session_id,))
conn.commit()
conn.close()
def cleanup_expired_sessions() -> None:
"""Clean up expired sessions (older than 1 hour)."""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("DELETE FROM sessions WHERE last_activity < datetime('now', '-1 hour')")
conn.commit()
conn.close()
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment