feat: show source directories in library stats and settings UI

Add active source root directories to the library stats endpoint and
display them in the settings page. Hardcode container PHOTO_DIRS to
/photos since the volume mount handles host path mapping. Add .env to
.gitignore to prevent committing secrets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-04-13 08:09:08 +02:00
parent b7aa2aed3d
commit e974ffbfd2
5 changed files with 26 additions and 4 deletions

1
.gitignore vendored
View File

@@ -34,6 +34,7 @@ dist-ssr/
.DS_Store
# Environment
.env
.env.local
.env.*.local

View File

@@ -18,6 +18,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.models import Photo
from app.models.folders import SourceRoot
from app.models.user import User
from app.dependencies import get_current_user
@@ -105,6 +106,13 @@ async def get_library_stats(
size = (await db.execute(select(func.sum(Photo.file_size)).where(owner))).scalar() or 0
# Source root directories (active ones only).
roots = (
await db.execute(
select(SourceRoot.path).where(SourceRoot.is_active.is_(True)).order_by(SourceRoot.path)
)
).scalars().all()
return {
"all_photos": all_photos_count,
"rated": rated_count,
@@ -116,6 +124,7 @@ async def get_library_stats(
"total_videos": video_count,
"total_size": size,
"total_size_gb": round(size / (1024**3), 2) if size else 0,
"source_dirs": roots,
}
@router.post("/scan")

View File

@@ -48,7 +48,7 @@ services:
- REDIS_URL=redis://redis:6379
- CELERY_BROKER_URL=redis://redis:6379
- CELERY_RESULT_BACKEND=redis://redis:6379
- PHOTO_DIRS=${PHOTO_DIRS:-/photos}
- PHOTO_DIRS=/photos
- ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-*}
- SECRET_KEY=${SECRET_KEY:-mulita-dev-secret-change-me}
- ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES:-60}
@@ -104,7 +104,7 @@ services:
- REDIS_URL=redis://redis:6379
- CELERY_BROKER_URL=redis://redis:6379
- CELERY_RESULT_BACKEND=redis://redis:6379
- PHOTO_DIRS=${PHOTO_DIRS:-/photos}
- PHOTO_DIRS=/photos
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- TZ=${TZ:-UTC}
# NullPool — see app/database.py for rationale.
@@ -138,7 +138,7 @@ services:
- REDIS_URL=redis://redis:6379
- CELERY_BROKER_URL=redis://redis:6379
- CELERY_RESULT_BACKEND=redis://redis:6379
- PHOTO_DIRS=${PHOTO_DIRS:-/photos}
- PHOTO_DIRS=/photos
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- TZ=${TZ:-UTC}
- MULITA_CELERY_WORKER=1
@@ -170,7 +170,7 @@ services:
- REDIS_URL=redis://redis:6379
- CELERY_BROKER_URL=redis://redis:6379
- CELERY_RESULT_BACKEND=redis://redis:6379
- PHOTO_DIRS=${PHOTO_DIRS:-/photos}
- PHOTO_DIRS=/photos
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- TZ=${TZ:-UTC}
- MULITA_CELERY_WORKER=1

View File

@@ -258,6 +258,17 @@ export function SettingsPage() {
/>
</div>
{(libStats?.source_dirs?.length ?? 0) > 0 && (
<div className="mt-3 text-xs text-text-muted">
<span className="font-medium text-text">Source folders</span>
<div className="mt-1 space-y-0.5">
{libStats!.source_dirs.map((dir: string) => (
<div key={dir} className="truncate font-mono" title={dir}>{dir}</div>
))}
</div>
</div>
)}
{/* Inline scan progress. Rendered as a full-width sub-card
when a scan is active so the user sees the same info
they'd get from the floating widget without leaving

View File

@@ -625,6 +625,7 @@ export interface LibraryStats {
total_videos: number
total_size: number
total_size_gb: number
source_dirs: string[]
}
// Heaps API