From ab7757cf46d64999fe0654bb0e209dc5087e5720 Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Mon, 7 Apr 2025 09:14:05 +0200 Subject: [PATCH] Refactor init and readme --- .env.dev | 11 + README.md | 295 ++++++------------ src/anonchat/__init__.py | 181 +++++------ src/anonchat/migrations/add_email_settings.py | 3 +- src/anonchat/models/settings.py | 4 +- src/anonchat/templates/admin/settings.html | 33 +- 6 files changed, 226 insertions(+), 301 deletions(-) create mode 100644 .env.dev diff --git a/.env.dev b/.env.dev new file mode 100644 index 0000000..f3d6b52 --- /dev/null +++ b/.env.dev @@ -0,0 +1,11 @@ +# .env for development + +SECRET_KEY=123456 +FLASK_ENV=development + +DATABASE_URL=sqlite:///anonchat.db +SESSION_TYPE=filesystem +RATELIMIT_STORAGE_URL=memory:// + +ADMIN_USERNAME=admin +ADMIN_PASSWORD=admin diff --git a/README.md b/README.md index 86e0d60..85dc215 100644 --- a/README.md +++ b/README.md @@ -1,229 +1,122 @@ -# AnonChat +# anonchat -An anonymous chat application built with Flask. +A simple Flask-based web application for anonymous inquiries or messages, designed for easy deployment and management. ## Features -- Anonymous inquiries and messaging -- Admin dashboard to manage inquiries -- Customizable site title -- Redis-based session storage for improved scalability -- Integrated error tracking with Sentry +* **Anonymous Inquiries:** Allows users to submit inquiries without revealing their identity. +* **Admin Interface:** Secure area for administrators to view and manage inquiries. +* **Webhook Notifications:** Optionally sends notifications to a specified URL upon new inquiry submissions. +* **Automatic Data Deletion:** Periodically deletes inquiries older than a configurable duration (default: 48 hours) to maintain privacy. +* **Rate Limiting:** Protects against abuse by limiting the number of requests from a single IP address. +* **CSRF Protection:** Implemented using Flask-WTF to prevent cross-site request forgery attacks. +* **Configurable:** Easily configured using environment variables. +* **Containerized:** Ready for deployment using Docker and Docker Compose. +* **Database Migrations:** Simple migration system managed via Flask CLI commands. +* **Optional Sentry Integration:** Supports error tracking with Sentry. -## Development Approach +## Technology Stack -AnonChat was created using "vibe coding" - a programming approach where developers leverage AI tools to generate code through natural language prompts rather than writing code manually. This modern development method allows focusing on high-level problem-solving and design while letting AI handle implementation details. - -Rest assured though, I know what I'm (or the AI is) doing. Here's what would happen if I didn't: -1. [my saas was built with Cursor, zero hand written code \ - AI is no longer just an assistant, it's also the builder \ - Now, you can continue to whine about it or start building.](https://xcancel.com/leojr94_/status/1900767509621674109) -2. [random thing are happening, maxed out usage on api keys, people bypassing the subscription, creating random shit on db \ - there are just some weird ppl out there](https://xcancel.com/leojr94_/status/1901560276488511759) +* **Backend:** Python 3.11+, Flask +* **Database:** PostgreSQL (recommended) or SQLite (default) via Flask-SQLAlchemy +* **Session Management:** Redis (recommended) or Filesystem via Flask-Session +* **Rate Limiting:** Redis via Flask-Limiter +* **Task Scheduling:** Flask-APScheduler +* **Password Hashing:** Argon2 via argon2-cffi +* **WSGI Server:** Gunicorn +* **Dependency Management:** Poetry +* **Containerization:** Docker, Docker Compose +* **Deployment:** Configured for Fly.io (optional) ## Configuration -AnonChat can be configured using environment variables: +The application is configured via environment variables. You can set these in a `.env` file in the project root or directly in your deployment environment. -- `SECRET_KEY`: Secret key for session management -- `DATABASE_URL`: Database connection string (defaults to SQLite) -- `ADMIN_USERNAME`: Admin username for admin dashboard -- `ADMIN_PASSWORD`: Admin password for admin dashboard -- `ADMIN_FORCE_RESET`: When set to "true", forces a reset of the admin password to the value in ADMIN_PASSWORD (defaults to "false") -- `SITE_TITLE`: Customizable site title (defaults to "AnonChat") -- `BEHIND_PROXY`: Set to "true" when running behind a reverse proxy to properly handle client IP addresses (defaults to "false") -- `RATELIMIT_STORAGE_URL`: Storage backend for rate limiting (defaults to memory storage) -- `REDIS_URL`: Redis connection URL for session storage (defaults to "redis://localhost:6379/0") -- `AUTO_DELETE_HOURS`: Number of hours after which closed inquiries are automatically deleted (defaults to 48) -- `SENTRY_DSN`: Sentry Data Source Name for error tracking and monitoring (optional) +**Required:** -You can set these variables in a `.env` file: +* `SECRET_KEY`: A long, random string used for session signing and security. Generate one using `python -c 'import secrets; print(secrets.token_hex(24))'`. +* `DATABASE_URL`: The connection string for your database (e.g., `postgresql://user:password@host:port/dbname` or `sqlite:///instance/anonchat.db`). +* `REDIS_URL`: The connection string for your Redis instance (e.g., `redis://localhost:6379/0`). Used for sessions (if `SESSION_TYPE=redis`) and rate limiting. +* `ADMIN_PASSWORD`: The password for the admin user. Set this on first run or when needing to reset. -``` -SECRET_KEY=your_secret_key_here -FLASK_APP=src/anonchat -FLASK_ENV=development -SITE_TITLE=My Custom Chat -BEHIND_PROXY=true -REDIS_URL=redis://redis:6379/0 -AUTO_DELETE_HOURS=72 -SENTRY_DSN=https://your-sentry-dsn +**Optional:** + +* `SITE_TITLE`: The title displayed on the website (default: `AnonChat`). +* `WEBHOOK_ENABLED`: Set to `true` to enable webhook notifications (default: `false`). +* `WEBHOOK_URL`: The URL to send webhook notifications to. +* `WEBHOOK_SECRET`: A secret key to sign webhook payloads (recommended if webhooks are enabled). +* `ADMIN_USERNAME`: The username for the admin user (default: `admin`). +* `ADMIN_FORCE_RESET`: Set to `true` to force reset the admin password on startup using `ADMIN_PASSWORD`. +* `AUTO_DELETE_HOURS`: The number of hours after which inquiries are automatically deleted (default: `48`). +* `RATELIMIT_STORAGE_URI`: Overrides `REDIS_URL` specifically for rate limiting. +* `BEHIND_PROXY`: Set to `true` if the application is running behind a reverse proxy (e.g., Nginx, Traefik) to correctly identify client IPs (default: `false`). +* `SESSION_TYPE`: `redis` (default) or `filesystem`. +* `SESSION_FILE_DIR`: Directory for filesystem sessions (if `SESSION_TYPE=filesystem`, default: `/dev/shm/flask_session`). +* `SENTRY_DSN`: Your Sentry DSN to enable error tracking. + +## Setup and Installation + +**Prerequisites:** + +* Python 3.11+ +* Poetry +* Docker & Docker Compose (Recommended for ease of setup) +* Redis Server (if using Redis for sessions/rate limiting) +* PostgreSQL Server (if using PostgreSQL) + +**1. Clone the Repository:** + +```bash +git clone +cd anonchat ``` -## Reverse Proxy Configuration +**2. Using Docker (Recommended):** -When running AnonChat behind a reverse proxy (like Nginx or Apache), set the `BEHIND_PROXY` environment variable to "true" to ensure rate limiting works correctly. This enables the application to use the X-Forwarded-For header to determine the client's real IP address. +This is the easiest way to get started. -Your reverse proxy should be configured to pass the client IP address in the X-Forwarded-For header: +* **Create a `.env` file:** Copy `.env.example` (if it exists) or create a new `.env` file and fill in the required environment variables (see Configuration section). Pay special attention to `SECRET_KEY`, `DATABASE_URL`, `REDIS_URL`, and `ADMIN_PASSWORD`. +* **Build and Run:** + ```bash + docker-compose up --build -d + ``` -### Nginx Example +The application should now be accessible at `http://localhost:5000` (or the port mapped in `docker-compose.yml`). -```nginx -server { - listen 80; - server_name your-domain.com; +**3. Local Development (Without Docker):** - location / { - proxy_pass http://localhost:5000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} -``` +* **Install Dependencies:** + ```bash + poetry install + ``` +* **Set Environment Variables:** Create a `.env` file in the project root and define the necessary variables (see Configuration). +* **Ensure Services are Running:** Make sure your PostgreSQL (if used) and Redis servers are running and accessible based on your `.env` configuration. +* **Run the Development Server:** + ```bash + poetry run start + ``` -## Error Tracking with Sentry +The application should be running at `http://localhost:5000`. -AnonChat includes integration with Sentry for error tracking and performance monitoring. This helps identify and diagnose issues in production environments. +## Running the Application -### Features +* **Docker:** Use `docker-compose up -d` to start and `docker-compose down` to stop. +* **Local:** Use `poetry run start` for the development server. For production-like environments without Docker, use `gunicorn`: + ```bash + # Example gunicorn command (adjust workers as needed) + poetry run gunicorn --workers 4 --bind 0.0.0.0:5000 "anonchat:app" + ``` -- Automatic error capturing and reporting -- Performance monitoring -- Contextual information for better debugging -- Real-time alerts for critical issues +## Deployment -### Configuration +This application includes configuration files for deployment: -To enable Sentry integration: +* `Dockerfile`: Defines the container image. +* `docker-compose.yml`: For local multi-container setups (app, db, redis). +* `fly.toml`: Configuration for deploying to Fly.io. -1. Sign up for a free Sentry account at [sentry.io](https://sentry.io) -2. Create a new project and get your DSN (Data Source Name) -3. Set the `SENTRY_DSN` environment variable in your `.env` file or deployment environment: +Refer to the specific platform's documentation for deployment steps using these files. Ensure all necessary environment variables are set in your deployment environment. -``` -SENTRY_DSN=https://your-sentry-project-key@sentry.io/your-project-id -``` +## License -When the `SENTRY_DSN` variable is set, error tracking will be automatically enabled when the application starts. - -## Installation - -1. Clone the repository -2. Install dependencies with Poetry: `poetry install` -3. Create `.env` file with your configuration -4. Run the application: `poetry run start` - -## Development - -This project uses Poetry for dependency management. - -- Install dependencies: `poetry install` -- Run tests: `poetry run pytest` -- Run the application: `poetry run start` - -### Database Migrations - -AnonChat includes a custom database migration system to handle schema changes. When you make changes to the database models, you should create a migration script to apply these changes to existing databases. - -#### Running Migrations - -- Run all pending migrations: `poetry run flask --app src/anonchat run-migrations` -- The migrations are also automatically run when using the `init-db` command or when starting the application with the entrypoint script. - -#### Creating New Migrations - -To create a new migration: - -1. Create a new Python file in the `src/anonchat/migrations` directory with a descriptive name (e.g., `add_new_column.py`) -2. Implement a `run_migration(db)` function that performs the necessary schema changes -3. The migration script should be idempotent (safe to run multiple times) - -Example migration script: - -```python -from sqlalchemy import inspect -from sqlalchemy.sql import text -from flask import current_app - -def run_migration(db): - """Add a new column to a table.""" - # Check if the column already exists - inspector = inspect(db.engine) - columns = [col['name'] for col in inspector.get_columns('your_table')] - - # Only apply changes if needed - if 'your_new_column' not in columns: - current_app.logger.info("Adding new column to table") - with db.engine.connect() as conn: - conn.execute(text("ALTER TABLE your_table ADD COLUMN your_new_column TEXT")) - conn.commit() - return True - - return False # Return True if changes were made, False otherwise -``` - -Migrations are run in alphabetical order, so you may want to prefix migration filenames with a timestamp or sequence number for more complex projects. - -## Admin Authentication - -AnonChat includes a secure admin authentication system that protects administrative routes and functions. This ensures that only authorized users can access the admin dashboard, manage inquiries, and configure system settings. - -### Security Features - -- **Secure Password Storage**: Admin passwords are securely hashed using SHA-256 with the application's secret key as salt -- **Session-Based Authentication**: Uses Flask sessions to maintain admin login state -- **Protected Routes**: All admin routes are protected by middleware that verifies authentication -- **Password Management**: Admins can change their password through the Admin Settings page -- **Logout Functionality**: Secure logout to clear session data - -### Setting Admin Credentials - -Admin credentials are set using environment variables: - -``` -ADMIN_USERNAME=admin -ADMIN_PASSWORD=your-secure-password -ADMIN_FORCE_RESET=false -``` - -These values should be set in your `.env` file or server environment. The default admin user is created automatically when the application first runs. - -#### Password Reset - -You can force a reset of the admin password by setting `ADMIN_FORCE_RESET=true` in your environment variables. This is useful when: - -- You need to recover from a forgotten admin password -- You're deploying to a new environment and want to ensure the admin credentials are set correctly -- You want to update the admin password during deployment without accessing the admin interface - -When enabled, the application will update the admin user's password to match the value in `ADMIN_PASSWORD` during initialization or when running the `init-db` command. - -### Admin Functions - -- View and respond to user inquiries -- Delete inquiries -- Configure webhook settings -- Change admin password - -### Security Best Practices - -- Always use a strong, unique password for the admin account -- Keep your SECRET_KEY secure and unique for each deployment -- In production, ensure you're using HTTPS to protect admin credentials during transmission -- Change the default admin password immediately after deployment - -## TODO: Security Improvements - -The following security enhancements are planned for future releases: - -- [ ] Implement CAPTCHA protection for admin login - - Add CAPTCHA verification to prevent brute force attacks - - Support multiple CAPTCHA providers (reCAPTCHA, hCaptcha) - - Implement rate limiting for failed login attempts - - Add IP-based blocking after multiple failed attempts - -### Authentication Methods -- [ ] Add OAuth 2.0 support for admin authentication - - Integrate with common providers (Google, GitHub, Microsoft) - - Implement proper PKCE flow for added security - - Support for custom OAuth providers for enterprise deployments - - Add multi-factor authentication options - -### Read-Only Links -- [ ] Implement read-only sharing links for inquiries - - Generate unique, cryptographically secure sharing links - - Allow users to create links that provide view-only access - - Set optional expiration times for sharing links - - Allow users to revoke sharing links at any time +This project is licensed under the 0BSD license - see the [LICENSE.txt](LICENSE.txt) file for details. diff --git a/src/anonchat/__init__.py b/src/anonchat/__init__.py index 6a50ef0..9df3d48 100644 --- a/src/anonchat/__init__.py +++ b/src/anonchat/__init__.py @@ -1,3 +1,4 @@ +import logging from flask import Flask from flask_sqlalchemy import SQLAlchemy import os @@ -15,43 +16,65 @@ from flask_apscheduler import APScheduler # Load environment variables from .env file load_dotenv() +DEFAULTS = { + 'SECRET_KEY': '', + 'SITE_TITLE': 'AnonChat', + 'BEHIND_PROXY': False, + + 'RATELIMIT_STORAGE_URI': os.environ.get('REDIS_URL'), + 'RATELIMIT_HEADERS_ENABLED': True, + 'RATELIMIT_KEY_PREFIX': 'anonchat_rate_limit', + + 'DATABASE_URL': 'sqlite:///anonchat.db', # Actual key: SQLALCHEMY_DATABASE_URI + + 'SESSION_TYPE': 'redis', + 'SESSION_PERMANENT': False, + 'SESSION_USE_SIGNER': True, + + # If SESSION_TYPE is redis + 'SESSION_REDIS': redis.from_url(os.environ.get('REDIS_URL', 'redis://localhost:6379/0')), + + # If SESSION_TYPE is filesystem + 'SESSION_FILE_DIR': '/dev/shm/flask_session', + 'SESSION_FILE_THRESHOLD': 100, + 'SESSION_FILE_MODE': 384, + + 'SESSION_KEY_PREFIX': 'anonchat_session:' +} + +def get(key, type: type = str): + env_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] + app = Flask(__name__) -app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///anonchat.db') -app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False -app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', os.urandom(24).hex()) -app.config['SITE_TITLE'] = os.environ.get('SITE_TITLE', 'AnonChat') -# Webhook configurations -app.config['WEBHOOK_ENABLED'] = os.environ.get('WEBHOOK_ENABLED', 'false').lower() == 'true' -app.config['WEBHOOK_URL'] = os.environ.get('WEBHOOK_URL', '') -app.config['WEBHOOK_SECRET'] = os.environ.get('WEBHOOK_SECRET', '') -# Admin configurations -app.config['ADMIN_USERNAME'] = os.environ.get('ADMIN_USERNAME', 'admin') -app.config['ADMIN_PASSWORD'] = os.environ.get('ADMIN_PASSWORD', None) -app.config['ADMIN_FORCE_RESET'] = os.environ.get('ADMIN_FORCE_RESET', 'false').lower() == 'true' -# Auto-deletion configuration -app.config['AUTO_DELETE_HOURS'] = int(os.environ.get('AUTO_DELETE_HOURS', 48)) +app.config['SECRET_KEY'] = get('SECRET_KEY') +app.config['SITE_TITLE'] = get('SITE_TITLE') +app.config['BEHIND_PROXY'] = get('BEHIND_PROXY', bool) + # Rate limit configurations -app.config['RATELIMIT_STORAGE_URI'] = os.environ.get('RATELIMIT_STORAGE_URI', os.environ.get('REDIS_URL')) -app.config['RATELIMIT_HEADERS_ENABLED'] = True -app.config['RATELIMIT_KEY_PREFIX'] = 'anonchat_rate_limit' -# Whether app is behind a proxy (get from env, default to False) -app.config['BEHIND_PROXY'] = os.environ.get('BEHIND_PROXY', 'false').lower() == 'true' +app.config['RATELIMIT_STORAGE_URI'] = get('RATELIMIT_STORAGE_URI') +app.config['RATELIMIT_HEADERS_ENABLED'] = get('RATELIMIT_HEADERS_ENABLED') +app.config['RATELIMIT_KEY_PREFIX'] = get('RATELIMIT_KEY_PREFIX') + +# Database configuration +app.config['SQLALCHEMY_DATABASE_URI'] = get('DATABASE_URL') # Redis session configuration -app.config['SESSION_TYPE'] = os.environ.get('SESSION_TYPE', 'redis') -app.config['SESSION_PERMANENT'] = False -app.config['SESSION_USE_SIGNER'] = True +app.config['SESSION_TYPE'] = get('SESSION_TYPE') +app.config['SESSION_PERMANENT'] = get('SESSION_PERMANENT') +app.config['SESSION_USE_SIGNER'] = get('SESSION_USE_SIGNER') if app.config['SESSION_TYPE'] == 'redis': - app.config['SESSION_REDIS'] = redis.from_url(os.environ.get('REDIS_URL', 'redis://localhost:6379/0')) + app.config['SESSION_REDIS'] = get('SESSION_REDIS') elif app.config['SESSION_TYPE'] == 'filesystem': - app.config['SESSION_FILE_DIR'] = os.environ.get('SESSION_FILE_DIR', '/dev/shm/flask_session') - app.config['SESSION_FILE_THRESHOLD'] = os.environ.get('SESSION_FILE_THRESHOLD', 100) - app.config['SESSION_FILE_MODE'] = os.environ.get('SESSION_FILE_MODE', 384) -app.config['SESSION_KEY_PREFIX'] = 'anonchat_session:' - -# Scheduler configuration -app.config['SCHEDULER_API_ENABLED'] = False -app.config['SCHEDULER_TIMEZONE'] = 'UTC' + app.config['SESSION_FILE_DIR'] = get('SESSION_FILE_DIR', '/dev/shm/flask_session') + app.config['SESSION_FILE_THRESHOLD'] = get('SESSION_FILE_THRESHOLD', 100) + app.config['SESSION_FILE_MODE'] = get('SESSION_FILE_MODE', 384) +app.config['SESSION_KEY_PREFIX'] = get('SESSION_KEY_PREFIX') app.url_map.strict_slashes = False @@ -139,27 +162,25 @@ def run_migrations_command(): # Import migrations module here to avoid circular imports from .migrations import run_migrations - print("Running database migrations...") + app.logger.info("Running database migrations...") migrations_run = run_migrations(db) - print(f"Database migrations completed: {migrations_run} migrations applied.") + app.logger.info(f"Database migrations completed: {migrations_run} migrations applied.") -# Flask CLI command to initialize the database -@app.cli.command("init-db") -def init_db_command(): +def _init_db(): """Create database tables and initialize default data.""" # Explicitly import models here to ensure they are registered before create_all from .models import Inquiry, Message, Settings, Admin with app.app_context(): - print("Creating database tables...") + app.logger.info("Creating database tables...") db.create_all() # Should now create all tables including Inquiry # Run migrations after creating tables # This ensures any new columns added to existing tables are properly added - print("Running database migrations...") + app.logger.info("Running database migrations...") from .migrations import run_migrations run_migrations(db) - print("Initializing default settings...") + app.logger.info("Initializing default settings...") # Initialize settings if they don't exist # Note: Models already imported above if not Settings.query.first(): @@ -170,60 +191,46 @@ def init_db_command(): ) db.session.add(default_settings) db.session.commit() - print("Default settings initialized.") + app.logger.info("Default settings initialized.") else: - print("Settings already exist.") + app.logger.info("Settings already exist.") - print("Initializing admin user...") + app.logger.info("Initializing admin user...") # Initialize admin user if it doesn't exist # Note: Models already imported above - admin_user = Admin.query.filter_by(username=app.config['ADMIN_USERNAME']).first() - if not admin_user and app.config['ADMIN_PASSWORD'] == None: - print("Admin user not found and no password provided. Skipping admin user initialization.") - elif app.config['ADMIN_FORCE_RESET'] or not admin_user: - if admin_user: - admin_user.password_hash = Admin.hash_password(app.config['ADMIN_PASSWORD']) - print("Admin user password reset.") + admin_user = Admin.query.first() + + if not admin_user or os.environ.get('ADMIN_FORCE_RESET'): + admin_password = os.environ.get('ADMIN_PASSWORD') + if admin_password is not None: + if admin_user: + admin_user.password_hash = Admin.hash_password(admin_password) + app.logger.info("Admin user password reset.") + else: + admin_user = Admin( + username=os.environ.get('ADMIN_USERNAME'), + password_hash=Admin.hash_password(admin_password) + ) + db.session.add(admin_user) + db.session.commit() + app.logger.info("Admin user initialized.") else: - admin_user = Admin( - username=app.config['ADMIN_USERNAME'], - password_hash=Admin.hash_password(app.config['ADMIN_PASSWORD']) - ) - db.session.add(admin_user) - db.session.commit() - print("Admin user initialized.") + if os.environ.get('ADMIN_FORCE_RESET'): + app.logger.warning("Admin force reset is enabled, but no password was provided. Skipping admin user initialization.") + else: + app.logger.warning("Admin user not found and no password provided. Skipping admin user initialization.") else: - print("Admin user already exists.") - print("Database initialization complete.") + app.logger.info("No need to initialize admin user.") + + app.logger.info("Database initialization complete.") + +# Flask CLI command to initialize the database +@app.cli.command("init-db") +def init_db_command(): + _init_db() def run() -> None: - # Note: The database initialization is now handled by the 'init-db' command - # and should be run separately, e.g., via the entrypoint script. - # Keeping the original db.create_all() here might be redundant or could - # be useful for local development outside Docker. - # Consider if you still need the initialization logic within run(). - with app.app_context(): - # Explicitly import all models to ensure they're registered before db.create_all() - from .models import Inquiry, Message, Settings, Admin - db.create_all() - - # Initialize settings if they don't exist - if not Settings.query.first(): - default_settings = Settings( - webhook_enabled=app.config['WEBHOOK_ENABLED'], - webhook_url=app.config['WEBHOOK_URL'], - webhook_secret=app.config['WEBHOOK_SECRET'] - ) - db.session.add(default_settings) - db.session.commit() - - # Initialize admin user if it doesn't exist - if not Admin.query.filter_by(username=app.config['ADMIN_USERNAME']).first(): - admin_user = Admin( - username=app.config['ADMIN_USERNAME'], - password_hash=Admin.hash_password(app.config['ADMIN_PASSWORD']) - ) - db.session.add(admin_user) - db.session.commit() - + app.logger.setLevel(logging.DEBUG) + _init_db() + app.run(debug=True) diff --git a/src/anonchat/migrations/add_email_settings.py b/src/anonchat/migrations/add_email_settings.py index 27f6dee..8885196 100644 --- a/src/anonchat/migrations/add_email_settings.py +++ b/src/anonchat/migrations/add_email_settings.py @@ -26,7 +26,8 @@ def run_migration(db): 'smtp_username': 'VARCHAR(255)', 'smtp_password': 'VARCHAR(255)', 'recipient_email': 'VARCHAR(255)', - 'notification_show_message': 'BOOLEAN DEFAULT FALSE' + 'notification_show_message': 'BOOLEAN DEFAULT FALSE', + 'auto_delete_hours': 'INTEGER DEFAULT 48' } # Add each column if it doesn't exist diff --git a/src/anonchat/models/settings.py b/src/anonchat/models/settings.py index 5a35765..45df5fa 100644 --- a/src/anonchat/models/settings.py +++ b/src/anonchat/models/settings.py @@ -16,4 +16,6 @@ class Settings(db.Model): smtp_password = db.Column(db.String(255), nullable=True) # Consider storing this securely recipient_email = db.Column(db.String(255), nullable=True) - notification_show_message = db.Column(db.Boolean, default=False) \ No newline at end of file + notification_show_message = db.Column(db.Boolean, default=False) + + auto_delete_hours = db.Column(db.Integer, default=48) \ No newline at end of file diff --git a/src/anonchat/templates/admin/settings.html b/src/anonchat/templates/admin/settings.html index 2aff75c..c15eec6 100644 --- a/src/anonchat/templates/admin/settings.html +++ b/src/anonchat/templates/admin/settings.html @@ -30,6 +30,28 @@
+
+

Auto-Delete Settings

+ +
+

Configure how long inquiries are kept before being automatically deleted.

+
+ +
+ + +
+ + + Set to how many hours inquiries should be kept before automatic deletion. (1-8760 hours, 8760 = 1 year) +
+ + +
+
+ +
+

Notification Settings

@@ -150,15 +172,4 @@
- -
- -
-

System Configuration

-

The following settings are configured through environment variables:

-
    -
  • Auto Delete Delay: {{ config.AUTO_DELETE_HOURS }} hours (set with AUTO_DELETE_HOURS)
  • -
-

To change these values, update your environment variables or .env file and restart the application.

-
{% endblock %} \ No newline at end of file