Files
TimeTracker/docs/AVATAR_STORAGE_MIGRATION.md
Dries Peeters 34946e1b80 feat: Make user profile pictures persistent across Docker updates
Store user avatars in persistent /data volume instead of application
directory to ensure profile pictures survive container rebuilds and
version updates.

Changes:
- Update avatar upload folder from app/static/uploads/avatars to
  /data/uploads/avatars using existing app_data volume mount
- Modify get_avatar_upload_folder() in auth routes to use persistent
  location with UPLOAD_FOLDER config
- Update User.get_avatar_path() to reference new storage location
- Add migration script to safely move existing avatars to new location
- Preserve backward compatibility - no database changes required

Benefits:
- Profile pictures now persist between Docker image updates
- Consistent with company logo storage pattern (/data/uploads)
- Better user experience - avatars not lost during upgrades
- Production-ready data/code separation
- All persistent uploads consolidated in app_data volume

Migration:
For existing installations with user avatars, run:
  docker-compose run --rm app python /app/docker/migrate-avatar-storage.py

New installations work automatically with no action required.

Documentation:
- docs/AVATAR_STORAGE_MIGRATION.md - Full migration guide
- docs/AVATAR_PERSISTENCE_SUMMARY.md - Quick reference
- docs/TEST_AVATAR_PERSISTENCE.md - Testing guide
- AVATAR_PERSISTENCE_CHANGELOG.md - Detailed changelog

Files modified:
- app/routes/auth.py
- app/models/user.py

Files added:
- docker/migrate-avatar-storage.py
- docs/AVATAR_STORAGE_MIGRATION.md
- docs/AVATAR_PERSISTENCE_SUMMARY.md
- docs/TEST_AVATAR_PERSISTENCE.md
- AVATAR_PERSISTENCE_CHANGELOG.md

Tested: ✓ No linter errors, backward compatible, volume mount verified
2025-10-22 11:12:11 +02:00

190 lines
5.5 KiB
Markdown

# User Avatar Storage Migration Guide
## Overview
As of this update, user profile pictures (avatars) are now stored in the persistent `/data` volume instead of the application directory. This ensures that **profile pictures persist between Docker container updates and rebuilds**.
## What Changed?
### Previous Behavior
- **Location:** `app/static/uploads/avatars/`
- **Problem:** This directory is inside the application container, so avatars were lost when updating or rebuilding the Docker image
- **Impact:** Users had to re-upload their profile pictures after each update
### New Behavior
- **Location:** `/data/uploads/avatars/`
- **Solution:** This directory is on the persistent `app_data` Docker volume
- **Benefit:** Profile pictures are preserved across all updates and rebuilds
## Migration Required?
**If you have existing user avatars**, you need to run the migration script to move them to the new location.
**If you're setting up a fresh installation**, no migration is needed - the new location will be used automatically.
## How to Migrate Existing Avatars
### Docker Environment
1. **Stop your TimeTracker containers:**
```bash
docker-compose down
```
2. **Run the migration script:**
```bash
docker-compose run --rm app python /app/docker/migrate-avatar-storage.py
```
3. **Start your containers:**
```bash
docker-compose up -d
```
4. **Verify avatars are working:**
- Log in to TimeTracker
- Check that user profile pictures are displayed correctly
- Try uploading a new avatar to confirm uploads work
5. **Optional - Cleanup old files:**
After confirming everything works, you can remove the old avatar directory:
```bash
docker-compose exec app rm -rf /app/static/uploads/avatars
```
### Bare Metal / Development Environment
1. **Navigate to your TimeTracker directory:**
```bash
cd /path/to/TimeTracker
```
2. **Ensure the new directory exists:**
```bash
mkdir -p /data/uploads/avatars
```
3. **Run the migration script:**
```bash
python docker/migrate-avatar-storage.py
```
4. **Restart your application:**
```bash
# Your normal restart command
systemctl restart timetracker
# or
./restart.sh
```
5. **Verify and cleanup:**
Follow steps 4-5 from the Docker instructions above.
## Technical Details
### Files Modified
1. **`app/routes/auth.py`**
- Updated `get_avatar_upload_folder()` to use `/data/uploads/avatars`
- Comment added explaining the persistence benefit
2. **`app/models/user.py`**
- Updated `get_avatar_path()` to use `/data/uploads/avatars`
- Added fallback for development environments
3. **`docker-compose.yml`**
- Already had `app_data:/data` volume mount (no changes needed)
### Configuration
The avatar location now respects the `UPLOAD_FOLDER` configuration:
- **Default:** `/data/uploads` (avatars go to `/data/uploads/avatars`)
- **Configurable:** Set `UPLOAD_FOLDER` in your environment to change the base path
### URL Structure
The public URL structure **remains unchanged**:
- **URL:** `/uploads/avatars/{filename}`
- **Route:** Handled by `auth.serve_uploaded_avatar()`
This means existing avatar URLs in the database continue to work without modification.
## Troubleshooting
### Avatars not displaying after migration
1. **Check file permissions:**
```bash
docker-compose exec app ls -la /data/uploads/avatars/
```
Files should be readable by the app user.
2. **Verify volume mount:**
```bash
docker inspect timetracker-app | grep -A 5 Mounts
```
Should show `/data` mounted from the `app_data` volume.
3. **Check migration log:**
Re-run the migration script to see if files were actually copied.
### New avatar uploads failing
1. **Check directory permissions:**
```bash
docker-compose exec app touch /data/uploads/avatars/.test
```
If this fails, fix permissions:
```bash
docker-compose exec app chown -R app:app /data/uploads/avatars
docker-compose exec app chmod -R 755 /data/uploads/avatars
```
2. **Check disk space:**
```bash
docker-compose exec app df -h /data
```
### Migration script can't find old directory
This is normal if:
- You're setting up a fresh installation (no avatars to migrate)
- Avatars were already migrated previously
- No users have uploaded avatars yet
The script will create the new directory structure automatically.
## Benefits of This Change
✅ **Persistent Storage:** Profile pictures survive Docker updates and rebuilds
✅ **Consistent with Logos:** Company logos already use `/data/uploads` (consistency)
✅ **Better UX:** Users don't lose their profile pictures during updates
✅ **Production Ready:** Proper separation of persistent data from application code
✅ **Backup Friendly:** All persistent uploads are in one volume (`app_data`)
## Backup Recommendations
Since avatars are now on the `app_data` volume, include this volume in your backup strategy:
```bash
# Backup the entire data volume
docker run --rm -v timetracker_app_data:/data -v $(pwd):/backup ubuntu tar czf /backup/app_data_backup.tar.gz -C /data .
# Restore the data volume
docker run --rm -v timetracker_app_data:/data -v $(pwd):/backup ubuntu tar xzf /backup/app_data_backup.tar.gz -C /data
```
## Questions?
If you encounter any issues with the avatar migration:
1. Check the [Troubleshooting](#troubleshooting) section above
2. Review the Docker logs: `docker-compose logs app`
3. Open an issue on GitHub with migration script output
---
**Last Updated:** October 2025
**Applies to:** TimeTracker v2.x and later