Private
Public Access
The 8MB image/file/avatar cap is now a site setting (UploadSettings, single-row table like SmtpSettings) editable from the Admin Settings tab, instead of a hardcoded constant. All three upload endpoints read the live value and interpolate it into their 413 messages. A new GET /api/uploads/limit endpoint (open to any authenticated user, unlike the admin-only settings endpoints) lets the composer reject an oversized file client-side before it ever hits the network, though the server still enforces the same cap independently. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
34 lines
1.3 KiB
Python
34 lines
1.3 KiB
Python
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.models import UploadSettings
|
|
from app.storage import DEFAULT_MAX_UPLOAD_BYTES
|
|
|
|
|
|
async def get_upload_settings(db: AsyncSession) -> UploadSettings:
|
|
"""Unlike SmtpSettings (where "no row yet" means "unconfigured, skip"),
|
|
an upload cap must always resolve to a usable value -- so this creates
|
|
the row with the default on first read instead of returning None."""
|
|
result = await db.execute(select(UploadSettings).limit(1))
|
|
settings_row = result.scalar_one_or_none()
|
|
if settings_row is None:
|
|
settings_row = UploadSettings(max_upload_bytes=DEFAULT_MAX_UPLOAD_BYTES)
|
|
db.add(settings_row)
|
|
await db.commit()
|
|
await db.refresh(settings_row)
|
|
return settings_row
|
|
|
|
|
|
async def update_upload_settings(db: AsyncSession, *, max_upload_bytes: int) -> UploadSettings:
|
|
settings_row = await get_upload_settings(db)
|
|
settings_row.max_upload_bytes = max_upload_bytes
|
|
await db.commit()
|
|
await db.refresh(settings_row)
|
|
return settings_row
|
|
|
|
|
|
def format_mb(num_bytes: int) -> str:
|
|
"""Used in 413 error messages, which need to reflect the live
|
|
admin-configured cap rather than a hardcoded "8 MB"."""
|
|
return f"{num_bytes / (1024 * 1024):g} MB"
|