diff --git a/src/anonchat/__init__.py b/src/anonchat/__init__.py index 9df3d48..b98b35d 100644 --- a/src/anonchat/__init__.py +++ b/src/anonchat/__init__.py @@ -5,7 +5,7 @@ import os from dotenv import load_dotenv from flask_limiter import Limiter from flask_limiter.util import get_remote_address -from flask import render_template, request, jsonify +from flask import jsonify from flask_wtf.csrf import CSRFProtect from flask_session import Session import redis @@ -43,13 +43,15 @@ DEFAULTS = { } def get(key, type: type = str): - env_value = os.environ.get(key) + value = os.environ.get(key) - if env_value is not None: - if type == bool: - env_value = env_value.lower() == 'true' - - return env_value or DEFAULTS[key] + if value is None: + return DEFAULTS[key] + + if type == bool: + value = value.lower() == 'true' + + return value app = Flask(__name__) app.config['SECRET_KEY'] = get('SECRET_KEY') diff --git a/src/anonchat/migrations/add_deletion_timestamp.py b/src/anonchat/migrations/add_deletion_timestamp.py new file mode 100644 index 0000000..3421e53 --- /dev/null +++ b/src/anonchat/migrations/add_deletion_timestamp.py @@ -0,0 +1,32 @@ +from sqlalchemy import Column, DateTime, inspect +from sqlalchemy.sql import text +from flask import current_app + +def run_migration(db): + """Add deletion_timestamp column to the Inquiry table if it doesn't exist.""" + current_app.logger.info("Running migration: add_deletion_timestamp") + + # Check if the column already exists + inspector = inspect(db.engine) + columns = [col['name'] for col in inspector.get_columns('inquiry')] + + # Track if we need to commit changes + changes_made = False + + # Add deletion_timestamp column if it doesn't exist + if 'deletion_timestamp' not in columns: + current_app.logger.info("Adding deletion_timestamp column to inquiry table") + with db.engine.connect() as conn: + conn.execute(text("ALTER TABLE inquiry ADD COLUMN deletion_timestamp TIMESTAMP DEFAULT '9999-12-31 23:59:59'")) + # We need to commit within the connection context for some database types + conn.commit() + changes_made = True + else: + current_app.logger.info("deletion_timestamp column already exists in inquiry table") + + if changes_made: + current_app.logger.info("Migration completed successfully") + else: + current_app.logger.info("No changes needed, column already exists") + + return changes_made \ No newline at end of file diff --git a/src/anonchat/models/inquiry.py b/src/anonchat/models/inquiry.py index a96fb2d..26c93b3 100644 --- a/src/anonchat/models/inquiry.py +++ b/src/anonchat/models/inquiry.py @@ -1,7 +1,6 @@ from .. import db import secrets -from datetime import datetime, timedelta -from flask import current_app +from datetime import datetime, timedelta, timezone class Inquiry(db.Model): id = db.Column(db.String(16), primary_key=True, unique=True, default=lambda: secrets.token_hex(8)) @@ -10,31 +9,25 @@ class Inquiry(db.Model): order_by='Message.message_number') is_closed = db.Column(db.Boolean, default=False) closing_timestamp = db.Column(db.DateTime, nullable=True) - - def close(self): + deletion_timestamp = db.Column(db.DateTime, nullable=True) + + def close(self, auto_delete_hours: int): """Mark an inquiry as closed""" self.is_closed = True - self.closing_timestamp = datetime.utcnow() - + self.closing_timestamp = datetime.now(timezone.utc) + self.deletion_timestamp = self.closing_timestamp + timedelta(hours=auto_delete_hours) + def reopen(self): """Reopen a closed inquiry""" self.is_closed = False self.closing_timestamp = None - + self.deletion_timestamp = None + @staticmethod - def get_expired_inquiries(hours=None): - """Get inquiries that have been closed for more than the specified hours""" - if hours is None: - hours = current_app.config.get('AUTO_DELETE_HOURS') - expiry_date = datetime.utcnow() - timedelta(hours=hours) + def get_expired_inquiries(): + """Get inquiries that have been closed for more than the specified hours and should be deleted""" + now = datetime.now(timezone.utc) return Inquiry.query.filter( Inquiry.is_closed == True, - Inquiry.closing_timestamp <= expiry_date - ).all() - - def get_deletion_date(self): - """Get the deletion date for a closed inquiry""" - if self.is_closed: - hours = current_app.config.get('AUTO_DELETE_HOURS') - return self.closing_timestamp + timedelta(hours=hours) - return None \ No newline at end of file + Inquiry.deletion_timestamp <= now + ).all() \ No newline at end of file diff --git a/src/anonchat/notifiers/__init__.py b/src/anonchat/notifiers/__init__.py index 0541005..cf63aae 100644 --- a/src/anonchat/notifiers/__init__.py +++ b/src/anonchat/notifiers/__init__.py @@ -50,7 +50,7 @@ def _send_event(event_type: str, inquiry_id: str, message: str = None, is_async: title=EVENT_TYPES[event_type].format(inquiry_id), inquiry_id=inquiry_id, data={ - 'message': _get_message_preview(message) + 'message': _get_message_preview(message) if message else None }, is_async=is_async ) @@ -59,7 +59,7 @@ def _send_event(event_type: str, inquiry_id: str, message: str = None, is_async: event_type=event_type, data={ 'inquiry_id': inquiry_id, - 'message': _get_message_preview(message) + 'message': _get_message_preview(message) if message else None }, is_async=is_async ) diff --git a/src/anonchat/routes.py b/src/anonchat/routes.py index 20e1090..1463204 100644 --- a/src/anonchat/routes.py +++ b/src/anonchat/routes.py @@ -1,8 +1,7 @@ -from flask import request, jsonify, render_template, redirect, url_for, flash, session, make_response +from flask import request, jsonify, render_template, redirect, url_for, flash, make_response -from . import app, db, limiter, csrf, notifiers -from .models import Inquiry, Message, Settings, Admin -import os +from . import app, db, limiter, notifiers +from .models import Inquiry, Message, Settings import json from datetime import datetime, timedelta, timezone @@ -225,7 +224,8 @@ def close_inquiry(inquiry_id): # Only close if not already closed if not inquiry.is_closed: - inquiry.close() + settings = Settings.query.first() + inquiry.close(settings.auto_delete_hours) db.session.commit() # Send notifications for closed inquiry diff --git a/src/anonchat/tasks.py b/src/anonchat/tasks.py index f381388..9f69a6f 100644 --- a/src/anonchat/tasks.py +++ b/src/anonchat/tasks.py @@ -1,7 +1,6 @@ from flask import current_app from . import db from .models import Inquiry -from datetime import datetime def check_and_delete_expired_inquiries(): """Check and delete inquiries that have been closed for more than the configured number of hours""" @@ -9,8 +8,7 @@ def check_and_delete_expired_inquiries(): app = current_app._get_current_object() with app.app_context(): - auto_delete_hours = app.config.get('AUTO_DELETE_HOURS') - app.logger.info(f"Running scheduled task: check_and_delete_expired_inquiries (delete after {auto_delete_hours} hours)") + app.logger.info(f"Running scheduled task: check_and_delete_expired_inquiries") expired_inquiries = Inquiry.get_expired_inquiries() deleted_count = 0 diff --git a/src/anonchat/templates/admin/dashboard.html b/src/anonchat/templates/admin/dashboard.html index 2bf4a4d..f788578 100644 --- a/src/anonchat/templates/admin/dashboard.html +++ b/src/anonchat/templates/admin/dashboard.html @@ -28,11 +28,9 @@ {% if item.inquiry.is_closed %} CLOSED - {% if item.inquiry.closing_timestamp %} Closed on {{ item.inquiry.closing_timestamp.strftime('%Y-%m-%d %H:%M') }} - {% endif %} {% else %} OPEN {% endif %} diff --git a/src/anonchat/templates/inquiry.html b/src/anonchat/templates/inquiry.html index 36cbfec..04e4801 100644 --- a/src/anonchat/templates/inquiry.html +++ b/src/anonchat/templates/inquiry.html @@ -22,7 +22,7 @@ {% if inquiry.is_closed %}
This inquiry is closed. - {% if inquiry.closing_timestamp %}It will be deleted on {{ inquiry.get_deletion_date().strftime('%Y-%m-%d %H:%M') }} UTC.{% endif %} + {% if inquiry.closing_timestamp %}It will be deleted on {{ inquiry.deletion_timestamp.strftime('%Y-%m-%d %H:%M') }} UTC.{% endif %}
{% endif %}