7 Commits

Author SHA1 Message Date
Ryan Chen
e9803eff37 asdf 2025-08-15 07:49:05 -04:00
Ryan Chen
6c4acf438d docs: add Claude development guidelines for database migrations
- Create CLAUDE.md with comprehensive migration guidelines
- Emphasize MUST create migrations for all schema changes
- Provide complete migration template and workflow
- Include naming conventions and best practices
- Document current database schema reference
- Add integration examples for dev/Docker/production

This ensures future AI assistants and developers follow
proper migration practices to prevent schema inconsistencies.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-08 00:07:23 -04:00
ryan
ed896a2bdf Merge pull request 'feat: Update Docker setup for refactored Flask architecture' (#2) from docker-refactor-updates into main
Reviewed-on: #2
2025-08-08 00:00:03 -04:00
ryan
7deeb681c0 Merge branch 'main' into docker-refactor-updates 2025-08-07 23:59:57 -04:00
Ryan Chen
8814dd8994 perf: reduce Gunicorn workers to 1 for stability
- Change from 4 workers to 1 worker in both Dockerfile and docker-compose.yml
- Helps reduce resource usage and connection reset issues
- Better for single-user or low-traffic deployment scenarios

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-07 23:59:00 -04:00
Ryan Chen
02247340d4 fix: resolve image display issues after refactor
- Fix upload folder path to be relative to Flask app static folder
- Update app initialization to use proper static folder structure
- Change default development port to 5001 to avoid AirPlay conflicts
- Images now display correctly in both public and admin views

The refactored app now properly serves uploaded images from the
correct static file location.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-07 23:56:14 -04:00
ryan
f3c14d649c Merge pull request 'Updating Dockerfile and docker-compose following' (#1) from docker-refactor-updates into main
Reviewed-on: #1
2025-08-07 23:41:21 -04:00
7 changed files with 223 additions and 6 deletions

201
CLAUDE.md Normal file
View File

@@ -0,0 +1,201 @@
# Claude Development Guidelines
## Database Migrations
**⚠️ IMPORTANT: When making changes to database models or schema, you MUST create migration files.**
### When to Create Migrations
**ALWAYS create a migration when:**
- Adding new columns to existing tables
- Creating new tables
- Modifying column types or constraints
- Adding or removing indexes
- Changing default values
- Adding foreign keys or relationships
**Examples that require migrations:**
```python
# Adding a new field to PetPicture model
class PetPicture:
# ... existing fields ...
tags = models.TextField() # ❌ Requires migration!
```
```python
# Changing database schema in init_db()
def init_db():
db.execute("""
CREATE TABLE users ( -- ❌ New table requires migration!
id INTEGER PRIMARY KEY,
username TEXT NOT NULL
)
""")
```
### How to Create Migrations
1. **Navigate to migrations directory:**
```bash
cd migrations/
```
2. **Create new migration file:**
```bash
# Use next sequential number (002, 003, etc.)
touch XXX_descriptive_name.py
```
3. **Use the migration template:**
```python
#!/usr/bin/env python3
"""
Migration XXX: Brief description of changes
Date: YYYY-MM-DD
Description: Detailed explanation of what this migration does
"""
import sqlite3
import sys
from datetime import datetime
def check_column_exists(cursor, table_name, column_name):
"""Check if a column exists in a table"""
cursor.execute(f"PRAGMA table_info({table_name})")
columns = [column[1] for column in cursor.fetchall()]
return column_name in columns
def migrate_up(cursor):
"""Apply the migration"""
# Check if already applied
if check_column_exists(cursor, 'pet_pictures', 'new_column'):
print("Migration already applied, skipping...")
return False
# Apply the migration
print("Adding new_column to pet_pictures table...")
cursor.execute("ALTER TABLE pet_pictures ADD COLUMN new_column TEXT")
return True
def migrate_down(cursor):
"""Rollback the migration (optional)"""
print("Rollback not implemented for SQLite column drops")
return False
def main():
"""Run migration standalone"""
# Standard migration execution code
pass
if __name__ == "__main__":
sys.exit(main())
```
4. **Test the migration:**
```bash
# Test individual migration
python migrations/XXX_descriptive_name.py
# Or run all pending migrations
python migrations/migrate.py
```
5. **Commit the migration file:**
```bash
git add migrations/XXX_descriptive_name.py
git commit -m "feat: add migration for [description]"
```
### Migration Naming Convention
- **Sequential numbering**: `001_`, `002_`, `003_`, etc.
- **Descriptive names**: Explain what the migration does
- **Examples**:
- `002_add_user_table.py`
- `003_add_tags_column_to_pictures.py`
- `004_create_comments_table.py`
### DO NOT Modify Database Schema Without Migrations
**❌ NEVER do this:**
```python
# Directly modifying init_db() or model schema
def init_db():
db.execute("""
CREATE TABLE pet_pictures (
id INTEGER PRIMARY KEY,
filename TEXT NOT NULL,
new_field TEXT -- ❌ Added without migration!
)
""")
```
**✅ ALWAYS do this:**
1. Create migration file first
2. Run migration to update schema
3. Then update model code if needed
### Model Changes Workflow
1. **Identify needed schema change**
2. **Create migration file**
3. **Test migration locally**
4. **Update model/application code** (if needed)
5. **Test full application**
6. **Commit migration + code changes together**
### Migration Best Practices
- **One logical change per migration**
- **Make migrations idempotent** (safe to run multiple times)
- **Test on development data first**
- **Include rollback logic when possible**
- **Document breaking changes clearly**
- **Never edit applied migrations**
### Integration Points
**Development:**
```bash
# Apply migrations before starting app
python migrations/migrate.py && python main.py
```
**Docker:**
```bash
# Migrations run automatically via docker-compose
docker compose --profile migrate up migrate
```
**Production Deployment:**
```bash
# Run migrations as part of deployment
python migrations/migrate.py
systemctl restart pet-picture-queue
```
---
## Database Schema Reference
**Current Schema (after migrations):**
### `pet_pictures` table:
- `id` - INTEGER PRIMARY KEY AUTOINCREMENT
- `filename` - TEXT NOT NULL
- `subscriber_name` - TEXT NOT NULL
- `description` - TEXT NULL (added via migration 001)
- `uploaded_at` - TIMESTAMP NOT NULL
- `posted` - BOOLEAN DEFAULT 0
- `likes` - INTEGER DEFAULT 0
### `migrations` table (auto-created):
- `id` - INTEGER PRIMARY KEY AUTOINCREMENT
- `migration_name` - TEXT NOT NULL UNIQUE
- `applied_at` - TIMESTAMP NOT NULL
---
**Remember: Schema changes without migrations will cause deployment issues and data inconsistencies!**
Always follow the migration workflow for database changes.

View File

@@ -32,7 +32,7 @@ RUN uv venv && \
ENV FLASK_APP=main.py
ENV FLASK_ENV=production
ENV PATH="/app/.venv/bin:$PATH"
ENV GUNICORN_CMD_ARGS="--workers=4 --bind=0.0.0.0:5000 --timeout=120"
ENV GUNICORN_CMD_ARGS="--workers=1 --bind=0.0.0.0:5000 --timeout=120"
# Expose port
EXPOSE 5000

View File

@@ -15,7 +15,10 @@ def create_app():
app.config.from_object(Config)
# Ensure upload directory exists
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
upload_path = os.path.join(app.static_folder, 'uploads')
os.makedirs(upload_path, exist_ok=True)
# Update config to use absolute path for file operations
app.config['UPLOAD_FOLDER'] = upload_path
# Setup logging
from app.utils.logging_config import setup_logging

View File

@@ -12,7 +12,7 @@ class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or os.urandom(24)
# Upload settings
UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER') or 'app/static/uploads'
UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER') or 'static/uploads'
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB max file size
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}

View File

@@ -9,7 +9,7 @@ services:
environment:
- FLASK_APP=main.py
- FLASK_ENV=production
- GUNICORN_CMD_ARGS=--workers=4 --bind=0.0.0.0:5000 --timeout=120 --keep-alive=5 --worker-class=sync --worker-connections=1000 --max-requests=1000 --max-requests-jitter=50
- GUNICORN_CMD_ARGS=--workers=1 --bind=0.0.0.0:5000 --timeout=120 --keep-alive=5 --worker-class=sync --worker-connections=1000 --max-requests=1000 --max-requests-jitter=50
# Application configuration
- UPLOAD_FOLDER=app/static/uploads
- DATABASE_PATH=pet_pictures.db

View File

@@ -1 +1,13 @@
2025-08-07 18:12:42,230 INFO: Pets of Powerwashing startup [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/logging_config.py:29]
2025-08-07 23:46:18,045 INFO: Pets of Powerwashing startup [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/logging_config.py:29]
2025-08-07 23:46:25,378 INFO: Pets of Powerwashing startup [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/logging_config.py:29]
2025-08-07 23:46:48,136 INFO: Pets of Powerwashing startup [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/logging_config.py:29]
2025-08-07 23:46:56,323 INFO: Pets of Powerwashing startup [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/logging_config.py:29]
2025-08-07 23:47:05,316 INFO: Pets of Powerwashing startup [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/logging_config.py:29]
2025-08-07 23:47:20,820 INFO: Pets of Powerwashing startup [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/logging_config.py:29]
2025-08-07 23:47:44,082 INFO: Pets of Powerwashing startup [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/logging_config.py:29]
2025-08-07 23:47:44,243 INFO: Pets of Powerwashing startup [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/logging_config.py:29]
2025-08-07 23:48:09,264 ERROR: 404 error: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/error_handlers.py:13]
2025-08-15 07:36:43,456 INFO: Pets of Powerwashing startup [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/logging_config.py:29]
2025-08-15 07:36:47,429 ERROR: 404 error: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/error_handlers.py:13]
2025-08-15 07:37:31,681 INFO: Pets of Powerwashing startup [in /Users/ryanchen/Programs/pet-picture-queue/app/utils/logging_config.py:29]

View File

@@ -15,4 +15,5 @@ app = create_app()
if __name__ == "__main__":
# Development server configuration
debug_mode = os.environ.get('FLASK_ENV') == 'development'
app.run(debug=debug_mode, host='0.0.0.0', port=5000)
port = int(os.environ.get('PORT', 5001)) # Use port 5001 to avoid AirPlay conflict
app.run(debug=debug_mode, host='0.0.0.0', port=port)