fix: Remove car_id property from adminAddVehicle call to fix TypeScript error
This commit is contained in:
322
temp_downloads_api.py
Normal file
322
temp_downloads_api.py
Normal file
@@ -0,0 +1,322 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Query, Request
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import desc
|
||||
from typing import List, Optional
|
||||
import uuid
|
||||
import os
|
||||
import csv
|
||||
import io
|
||||
from datetime import datetime
|
||||
|
||||
from ..database import get_db
|
||||
from ..models.download import Download, DownloadRequest
|
||||
from ..schemas.download import (
|
||||
DownloadResponse, DownloadRequestCreate, DownloadRequestResponse,
|
||||
DownloadAdminCreate, DownloadAdminUpdate, DownloadAdminResponse,
|
||||
DownloadRequestAdminResponse
|
||||
)
|
||||
from ..core.security import get_current_admin
|
||||
from ..core.config import settings
|
||||
|
||||
router = APIRouter(prefix="/downloads", tags=["downloads"])
|
||||
|
||||
UPLOAD_DIR = os.path.join(settings.UPLOAD_DIR, "downloads")
|
||||
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
||||
|
||||
ALLOWED_EXTENSIONS = {".exe", ".zip", ".msi", ".dmg", ".pkg", ".tar", ".gz", ".rar", ".7z", ".pdf"}
|
||||
ALLOWED_IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
|
||||
|
||||
|
||||
def get_localized_field(obj, field: str, lang: str) -> Optional[str]:
|
||||
localized = getattr(obj, f"{field}_{lang}", None)
|
||||
if localized:
|
||||
return localized
|
||||
return getattr(obj, f"{field}_ko", None)
|
||||
|
||||
|
||||
# Public Endpoints
|
||||
|
||||
@router.get("/", response_model=List[DownloadResponse])
|
||||
def get_downloads(lang: str = "ko", db: Session = Depends(get_db)):
|
||||
downloads = db.query(Download).filter(
|
||||
Download.is_active == True
|
||||
).order_by(Download.display_order).all()
|
||||
|
||||
result = []
|
||||
for d in downloads:
|
||||
result.append(DownloadResponse(
|
||||
id=d.id,
|
||||
title=get_localized_field(d, "title", lang) or "",
|
||||
description=get_localized_field(d, "description", lang),
|
||||
category=get_localized_field(d, "category", lang),
|
||||
file_name=d.file_name,
|
||||
file_size=d.file_size,
|
||||
version=d.version,
|
||||
thumbnail=d.thumbnail,
|
||||
download_count=d.download_count or 0
|
||||
))
|
||||
return result
|
||||
|
||||
|
||||
@router.get("/{download_id}", response_model=DownloadResponse)
|
||||
def get_download(download_id: int, lang: str = "ko", db: Session = Depends(get_db)):
|
||||
d = db.query(Download).filter(
|
||||
Download.id == download_id,
|
||||
Download.is_active == True
|
||||
).first()
|
||||
|
||||
if not d:
|
||||
raise HTTPException(status_code=404, detail="Download not found")
|
||||
|
||||
return DownloadResponse(
|
||||
id=d.id,
|
||||
title=get_localized_field(d, "title", lang) or "",
|
||||
description=get_localized_field(d, "description", lang),
|
||||
category=get_localized_field(d, "category", lang),
|
||||
file_name=d.file_name,
|
||||
file_size=d.file_size,
|
||||
version=d.version,
|
||||
thumbnail=d.thumbnail,
|
||||
download_count=d.download_count or 0
|
||||
)
|
||||
|
||||
|
||||
@router.post("/{download_id}/request", response_model=DownloadRequestResponse)
|
||||
async def request_download(
|
||||
download_id: int,
|
||||
request_data: DownloadRequestCreate,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
download = db.query(Download).filter(
|
||||
Download.id == download_id,
|
||||
Download.is_active == True
|
||||
).first()
|
||||
|
||||
if not download:
|
||||
raise HTTPException(status_code=404, detail="Download not found")
|
||||
|
||||
if not request_data.newsletter_agreed:
|
||||
raise HTTPException(status_code=400, detail="Newsletter consent is required")
|
||||
|
||||
client_ip = request.headers.get("X-Forwarded-For", request.client.host)
|
||||
if client_ip and "," in client_ip:
|
||||
client_ip = client_ip.split(",")[0].strip()
|
||||
|
||||
country = None
|
||||
country_code = None
|
||||
try:
|
||||
import httpx
|
||||
async with httpx.AsyncClient(timeout=2.0) as client:
|
||||
resp = await client.get(f"http://ip-api.com/json/{client_ip}?fields=country,countryCode")
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
country = data.get("country")
|
||||
country_code = data.get("countryCode")
|
||||
except:
|
||||
pass
|
||||
|
||||
download_request = DownloadRequest(
|
||||
download_id=download_id,
|
||||
email=request_data.email,
|
||||
newsletter_agreed=request_data.newsletter_agreed,
|
||||
ip_address=client_ip,
|
||||
country=country,
|
||||
country_code=country_code
|
||||
)
|
||||
db.add(download_request)
|
||||
download.download_count = (download.download_count or 0) + 1
|
||||
db.commit()
|
||||
|
||||
return DownloadRequestResponse(
|
||||
download_url=download.file_url,
|
||||
file_name=download.file_name or "download"
|
||||
)
|
||||
|
||||
|
||||
# Admin Endpoints
|
||||
|
||||
@router.get("/admin/list", response_model=List[DownloadAdminResponse])
|
||||
def admin_get_downloads(
|
||||
db: Session = Depends(get_db),
|
||||
admin = Depends(get_current_admin)
|
||||
):
|
||||
downloads = db.query(Download).order_by(Download.display_order).all()
|
||||
return downloads
|
||||
|
||||
|
||||
@router.get("/admin/{download_id}", response_model=DownloadAdminResponse)
|
||||
def admin_get_download(
|
||||
download_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
admin = Depends(get_current_admin)
|
||||
):
|
||||
download = db.query(Download).filter(Download.id == download_id).first()
|
||||
if not download:
|
||||
raise HTTPException(status_code=404, detail="Download not found")
|
||||
return download
|
||||
|
||||
|
||||
@router.post("/admin", response_model=DownloadAdminResponse)
|
||||
def admin_create_download(
|
||||
download_data: DownloadAdminCreate,
|
||||
db: Session = Depends(get_db),
|
||||
admin = Depends(get_current_admin)
|
||||
):
|
||||
download = Download(**download_data.model_dump())
|
||||
db.add(download)
|
||||
db.commit()
|
||||
db.refresh(download)
|
||||
return download
|
||||
|
||||
|
||||
@router.put("/admin/{download_id}", response_model=DownloadAdminResponse)
|
||||
def admin_update_download(
|
||||
download_id: int,
|
||||
download_data: DownloadAdminUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
admin = Depends(get_current_admin)
|
||||
):
|
||||
download = db.query(Download).filter(Download.id == download_id).first()
|
||||
if not download:
|
||||
raise HTTPException(status_code=404, detail="Download not found")
|
||||
|
||||
update_data = download_data.model_dump(exclude_unset=True)
|
||||
for key, value in update_data.items():
|
||||
setattr(download, key, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(download)
|
||||
return download
|
||||
|
||||
|
||||
@router.delete("/admin/{download_id}")
|
||||
def admin_delete_download(
|
||||
download_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
admin = Depends(get_current_admin)
|
||||
):
|
||||
download = db.query(Download).filter(Download.id == download_id).first()
|
||||
if not download:
|
||||
raise HTTPException(status_code=404, detail="Download not found")
|
||||
|
||||
db.delete(download)
|
||||
db.commit()
|
||||
return {"message": "Download deleted successfully"}
|
||||
|
||||
|
||||
@router.post("/admin/upload")
|
||||
async def admin_upload_file(
|
||||
file: UploadFile = File(...),
|
||||
file_type: str = Query("file", description="file or thumbnail"),
|
||||
admin = Depends(get_current_admin)
|
||||
):
|
||||
ext = os.path.splitext(file.filename)[1].lower()
|
||||
|
||||
if file_type == "thumbnail":
|
||||
if ext not in ALLOWED_IMAGE_EXTENSIONS:
|
||||
raise HTTPException(status_code=400, detail=f"Allowed image formats: {ALLOWED_IMAGE_EXTENSIONS}")
|
||||
else:
|
||||
if ext not in ALLOWED_EXTENSIONS and ext not in ALLOWED_IMAGE_EXTENSIONS:
|
||||
raise HTTPException(status_code=400, detail=f"Allowed formats: {ALLOWED_EXTENSIONS}")
|
||||
|
||||
unique_name = f"{uuid.uuid4()}{ext}"
|
||||
file_path = os.path.join(UPLOAD_DIR, unique_name)
|
||||
|
||||
content = await file.read()
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(content)
|
||||
|
||||
file_size = len(content)
|
||||
file_url = f"uploads/downloads/{unique_name}"
|
||||
|
||||
return {
|
||||
"file_url": file_url,
|
||||
"file_name": file.filename,
|
||||
"file_size": file_size
|
||||
}
|
||||
|
||||
|
||||
# Admin: Download Requests (Email List)
|
||||
|
||||
@router.get("/admin/requests", response_model=List[DownloadRequestAdminResponse])
|
||||
def admin_get_requests(
|
||||
newsletter_only: bool = False,
|
||||
limit: int = 100,
|
||||
offset: int = 0,
|
||||
db: Session = Depends(get_db),
|
||||
admin = Depends(get_current_admin)
|
||||
):
|
||||
query = db.query(DownloadRequest).join(Download)
|
||||
|
||||
if newsletter_only:
|
||||
query = query.filter(DownloadRequest.newsletter_agreed == True)
|
||||
|
||||
requests = query.order_by(desc(DownloadRequest.requested_at)).offset(offset).limit(limit).all()
|
||||
|
||||
result = []
|
||||
for r in requests:
|
||||
result.append(DownloadRequestAdminResponse(
|
||||
id=r.id,
|
||||
download_id=r.download_id,
|
||||
download_title=r.download.title_ko if r.download else None,
|
||||
email=r.email,
|
||||
newsletter_agreed=r.newsletter_agreed,
|
||||
ip_address=r.ip_address,
|
||||
country=r.country,
|
||||
country_code=r.country_code,
|
||||
requested_at=r.requested_at
|
||||
))
|
||||
return result
|
||||
|
||||
|
||||
@router.get("/admin/requests/export")
|
||||
def admin_export_requests(
|
||||
newsletter_only: bool = True,
|
||||
db: Session = Depends(get_db),
|
||||
admin = Depends(get_current_admin)
|
||||
):
|
||||
query = db.query(DownloadRequest).join(Download)
|
||||
|
||||
if newsletter_only:
|
||||
query = query.filter(DownloadRequest.newsletter_agreed == True)
|
||||
|
||||
requests = query.order_by(desc(DownloadRequest.requested_at)).all()
|
||||
|
||||
output = io.StringIO()
|
||||
writer = csv.writer(output)
|
||||
writer.writerow(["Email", "Download", "Newsletter", "Country", "Date"])
|
||||
|
||||
for r in requests:
|
||||
writer.writerow([
|
||||
r.email,
|
||||
r.download.title_ko if r.download else "",
|
||||
"Yes" if r.newsletter_agreed else "No",
|
||||
r.country or "",
|
||||
r.requested_at.strftime("%Y-%m-%d %H:%M") if r.requested_at else ""
|
||||
])
|
||||
|
||||
output.seek(0)
|
||||
|
||||
return StreamingResponse(
|
||||
io.BytesIO(output.getvalue().encode("utf-8-sig")),
|
||||
media_type="text/csv",
|
||||
headers={"Content-Disposition": f"attachment; filename=download_requests_{datetime.now().strftime('%Y%m%d')}.csv"}
|
||||
)
|
||||
|
||||
|
||||
@router.get("/admin/requests/stats")
|
||||
def admin_get_request_stats(
|
||||
db: Session = Depends(get_db),
|
||||
admin = Depends(get_current_admin)
|
||||
):
|
||||
total = db.query(DownloadRequest).count()
|
||||
newsletter = db.query(DownloadRequest).filter(DownloadRequest.newsletter_agreed == True).count()
|
||||
unique_emails = db.query(DownloadRequest.email).distinct().count()
|
||||
|
||||
return {
|
||||
"total_requests": total,
|
||||
"newsletter_subscribers": newsletter,
|
||||
"unique_emails": unique_emails
|
||||
}
|
||||
Reference in New Issue
Block a user