Add .gitignore, remove __pycache__ from tracking

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-12 12:16:04 +00:00
parent 8d688b459d
commit c8fad1cf36
181 changed files with 8 additions and 29855 deletions

View file

@ -1,118 +0,0 @@
"""
Public API endpoints - NO AUTHENTICATION REQUIRED.
These endpoints are designed for sharing with external parties (e.g., suppliers)
via hash-based URLs that don't require login.
"""
import os
from fastapi import APIRouter, HTTPException
from fastapi.responses import Response
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from fastapi import Depends
from database import get_db
from models.dispute import DisputeAttachment
from models.settings import KitchenSettings
from services.nextcloud_service import NextcloudService
router = APIRouter()
@router.get("/attachments/{public_hash}")
async def get_public_attachment(
public_hash: str,
db: AsyncSession = Depends(get_db)
):
"""
View a dispute attachment publicly via its hash.
This endpoint does NOT require authentication, allowing suppliers
to view attached images/documents via shareable links in emails.
"""
# Find attachment by public hash
result = await db.execute(
select(DisputeAttachment).where(DisputeAttachment.public_hash == public_hash)
)
attachment = result.scalar_one_or_none()
if not attachment:
raise HTTPException(status_code=404, detail="Attachment not found")
# Get file content
content = None
# Try local file first
if attachment.file_storage_location == "local" and attachment.file_path:
if os.path.exists(attachment.file_path):
with open(attachment.file_path, 'rb') as f:
content = f.read()
# Try Nextcloud if local not found
if content is None and attachment.file_storage_location == "nextcloud" and attachment.nextcloud_path:
# Get kitchen settings for Nextcloud credentials
settings_result = await db.execute(
select(KitchenSettings).where(KitchenSettings.kitchen_id == attachment.kitchen_id)
)
settings = settings_result.scalar_one_or_none()
if settings and settings.nextcloud_enabled:
nc = NextcloudService(
settings.nextcloud_host,
settings.nextcloud_username,
settings.nextcloud_password,
""
)
success, nc_content = await nc.download_file(attachment.nextcloud_path)
await nc.close()
if success:
content = nc_content
if content is None:
raise HTTPException(status_code=404, detail="File not found")
# Determine if browser should display inline or download
# Images and PDFs display inline, others download
inline_types = [
'image/jpeg', 'image/png', 'image/gif', 'image/webp',
'application/pdf'
]
disposition = "inline" if attachment.file_type in inline_types else "attachment"
return Response(
content=content,
media_type=attachment.file_type,
headers={
"Content-Disposition": f'{disposition}; filename="{attachment.file_name}"',
"Cache-Control": "private, max-age=3600" # Cache for 1 hour
}
)
@router.get("/attachments/{public_hash}/info")
async def get_public_attachment_info(
public_hash: str,
db: AsyncSession = Depends(get_db)
):
"""
Get attachment metadata without downloading the file.
Useful for email previews or link unfurling.
"""
result = await db.execute(
select(DisputeAttachment).where(DisputeAttachment.public_hash == public_hash)
)
attachment = result.scalar_one_or_none()
if not attachment:
raise HTTPException(status_code=404, detail="Attachment not found")
return {
"file_name": attachment.file_name,
"file_type": attachment.file_type,
"file_size_bytes": attachment.file_size_bytes,
"attachment_type": attachment.attachment_type,
"description": attachment.description,
"uploaded_at": attachment.uploaded_at.isoformat() if attachment.uploaded_at else None
}