diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7d895b6 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +DB_TYPE=sqlite +SQLITE_PATH=databases/chatbot.db +FLASK_DEBUG=true +FLASK_SECRET_KEY=change-this-in-productio \ No newline at end of file diff --git a/.gitignore b/.gitignore index d4e940c..e5616be 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ /.idea/ testing.py /databases/ +.venv .env \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3448bda --- /dev/null +++ b/Dockerfile @@ -0,0 +1,64 @@ +# Use Python 3.11 slim image for smaller size +FROM python:3.11-slim + +# Set maintainer +LABEL maintainer="fuh@fuhlig.de" +LABEL description="PY_ChatBot - Modern Flask-based Chatbot Application" + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + FLASK_APP=main.py \ + FLASK_ENV=production \ + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 + +# Set work directory +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + gcc \ + sqlite3 \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Create non-root user for security +RUN addgroup --system --gid 1001 chatbot && \ + adduser --system --uid 1001 --gid 1001 --no-create-home --disabled-password chatbot + +# Create necessary directories with proper permissions +RUN mkdir -p /app/data /app/logs /app/static && \ + chown -R chatbot:chatbot /app + +# Copy requirements first for better Docker layer caching +COPY requirements.txt . +RUN chown chatbot:chatbot requirements.txt + +# Install Python dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY . . +RUN chown -R chatbot:chatbot . + +# Copy entrypoint script +COPY docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh && \ + chown chatbot:chatbot /usr/local/bin/docker-entrypoint.sh + +# Switch to non-root user +USER chatbot + +# Expose port +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ + CMD curl -f http://localhost:8080/ || exit 1 + +# Set entrypoint +ENTRYPOINT ["docker-entrypoint.sh"] + +# Default command +CMD ["python", "main.py"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..32b6707 --- /dev/null +++ b/README.md @@ -0,0 +1,157 @@ +# đ¤ PY_ChatBot + +A modular, secure, and extensible web-based chatbot platform built with Flask. Features include user authentication, session management, multi-database support, and a modern responsive UI. + +--- + +## đ Table of Contents + +- [Features](#features) +- [Demo](#demo) +- [Installation](#installation) +- [Configuration](#configuration) +- [Usage](#usage) +- [Project Structure](#project-structure) +- [Docker Setup](#docker-setup) +- [Contributing](#contributing) +- [Troubleshooting](#troubleshooting) +- [License](#license) + +--- + +## ⨠Features + +- **User Authentication** + - Registration, login, logout, and password change + - Email validation and strong password requirements + - Secure session cookies with configurable lifetime + +- **Multi-Database Support** + - Abstracted database layer + - Thread-safe SQLite implementation + - Ready for PostgreSQL/MySQL integration + +- **Modern UI/UX** + - Responsive templates: login, register, dashboard, profile, change password, 404 + - Gradient backgrounds, animations, and hover effects + - Flash messages for real-time feedback + +- **Security** + - SHA-512 password hashing + - Decorators for route protection (`@login_required`, `@logout_required`) + - CSRF-ready and input validation utilities + +- **Developer-Friendly** + - Well-organized code: `config`, `database`, `frontend`, `models`, `services`, `utils` + - Comprehensive logging and error handlers + - Environment-based configuration + +--- + +## đĨ Demo + +Screenshots and live demo links can be added here. + +--- + +## đ Installation + +### Prerequisites + +- Python 3.8+ +- Git + +### Local Setup + +1. **Clone the repo** + + ```bash + git clone https://github.com/florianuhlig/PY_ChatBot.git + cd PY_ChatBot + git checkout Development + ``` + +2. **Create virtual environment** + + ```bash + python -m venv .venv + source .venv/bin/activate # macOS/Linux + .venv\Scripts\activate # Windows + ``` + +3. **Install dependencies** + + ```bash + pip install -r requirements.txt + ``` + +4. **Configure environment** + + Copy `.env.example` to `.env` and adjust values: + + ```bash + cp .env.example .env + ``` + +5. **Run the app** + + ```bash + python main.py + ``` + + Open your browser to `http://localhost:8080`. + +--- + +## âī¸ Configuration + +Edit `.env` for: + +- `DB_TYPE` (e.g., `sqlite`, `postgresql`, `mysql`) +- `SQLITE_PATH` (path to SQLite file) +- `FLASK_SECRET_KEY` +- `FLASK_DEBUG`, `FLASK_HOST`, `FLASK_PORT` +- Session and password policy variables + +--- + +## đ Usage + +- **Register**: `/register` +- **Login**: `/login` +- **Dashboard**: `/dashboard` (protected) +- **Profile**: `/profile` (protected) +- **Change Password**: `/change-password` (protected) +- **Logout**: `/logout` + +--- + +## đ Project Structure + +``` +PY_ChatBot/ +âââ config/ # App configuration loader +â âââ database.py +âââ database/ # DB abstraction and implementations +â âââ interface.py +â âââ sqlite_db.py +â âââ flask_integration.py +âââ frontend/ # Flask application and templates +â âââ app.py +â âââ templates/ +â âââ login.html +â âââ register.html +â âââ dashboard.html +â âââ profile.html +â âââ change_password.html +â âââ 404.html +âââ models/ # Data models (if any) +âââ services/ # Business logic: user and auth services +âââ utils/ # Utility functions and decorators +âââ .env.example # Environment variable template +âââ Dockerfile # Container setup +âââ docker-compose.yml # Orchestration +âââ docker-entrypoint.sh # Init script +âââ main.py # Entry point +âââ requirements.txt # Python dependencies +``` \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6c11a2b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,66 @@ +services: + chatbot: + build: + context: . + dockerfile: Dockerfile + container_name: py_chatbot + restart: unless-stopped + ports: + - "8080:8080" + environment: + # Database Configuration + - DB_TYPE=sqlite + - SQLITE_PATH=/app/data/chatbot.db + + # Flask Configuration + - FLASK_HOST=0.0.0.0 + - FLASK_PORT=8080 + - FLASK_DEBUG=false + - FLASK_SECRET_KEY=your-super-secret-key-change-this-in-production + + # Session Configuration + - SESSION_LIFETIME_HOURS=24 + - SESSION_COOKIE_SECURE=false + - SESSION_COOKIE_HTTPONLY=true + volumes: + # Persist database and logs + - chatbot_data:/app/data + - chatbot_logs:/app/logs + networks: + - chatbot_network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + + postgres: + image: postgres:15-alpine + container_name: py_chatbot_postgres + restart: unless-stopped + environment: + - POSTGRES_DB=chatbot + - POSTGRES_USER=chatbot_user + - POSTGRES_PASSWORD=secure_password_change_this + volumes: + - postgres_data:/var/lib/postgresql/data + - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro + networks: + - chatbot_network + profiles: + - postgres + +# Named Volumes +volumes: + chatbot_data: + driver: local + chatbot_logs: + driver: local + postgres_data: + driver: local + +# Networks +networks: + chatbot_network: + driver: bridge diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..d695393 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,53 @@ +#!/bin/bash +set -e + +echo "đ¤ Starting PY_ChatBot..." +# Create directory for database dynamically based on SQLITE_PATH + +DB_DIR=$(dirname "$SQLITE_PATH") +mkdir -p "$DB_DIR" /app/logs + +# Set default environment variables if not provided +export DB_TYPE=${DB_TYPE:-sqlite} +export SQLITE_PATH=${SQLITE_PATH:-databases/chatbot.db} +export FLASK_HOST=${FLASK_HOST:-0.0.0.0} +export FLASK_PORT=${FLASK_PORT:-8080} +export FLASK_DEBUG=${FLASK_DEBUG:-false} + +# Generate a random secret key if not provided +if [ -z "$FLASK_SECRET_KEY" ]; then + export FLASK_SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(32))") + echo "â ī¸ Generated random secret key. Set FLASK_SECRET_KEY environment variable for production!" +fi + +# Initialize database if it doesn't exist +if [ "$DB_TYPE" = "sqlite" ] && [ ! -f "$SQLITE_PATH" ]; then + echo "đĻ Initializing SQLite database at $SQLITE_PATH..." + python -c " +import sys +sys.path.append('/app') +from config.database import DatabaseConfig +from database import get_database + +try: + db_config = DatabaseConfig() + db_type, config = db_config.get_database_config() + database = get_database(db_type, config) + print('â Database initialized successfully!') + database.disconnect() +except Exception as e: + print(f'â Database initialization failed: {e}') + sys.exit(1) +" +fi + +# Print configuration info +echo "đ Configuration:" +echo " - Database Type: $DB_TYPE" +echo " - Database Path: $SQLITE_PATH" +echo " - Flask Host: $FLASK_HOST" +echo " - Flask Port: $FLASK_PORT" +echo " - Debug Mode: $FLASK_DEBUG" + +# Execute the command +exec "$@" diff --git a/frontend/templates/404.html b/frontend/templates/404.html new file mode 100644 index 0000000..2d2c506 --- /dev/null +++ b/frontend/templates/404.html @@ -0,0 +1,471 @@ + + +
+ + ++ The requested URL was not found on this server. This could be due to a + mistyped URL, a moved page, or a link that's no longer valid. +
++ You're currently using our ChatBot platform. If you were trying to access + a specific chat or feature, please navigate through the main menu or + contact our support team for assistance. +
+Update your account password
+For your security, we require your current password to make changes. Your new password will be encrypted and stored securely.
+Status: Active
-Created: {{ user.created_at[:19] if user.created_at else 'Unknown' }}
-Last Updated: {{ user.updated_at[:19] if user.updated_at else 'Unknown' }}
+Created: {{ user.created_at.strftime('%Y-%m-%d %H:%M:%S') if user.created_at else 'Unknown' }}
+Last Updated: {{ user.updated_at.strftime('%Y-%m-%d %H:%M:%S') if user.updated_at else 'Unknown' }}