162 lines
5.5 KiB
Python
162 lines
5.5 KiB
Python
"""
|
|
sharing.py — Forenklet deling af playlister.
|
|
Kun ejeren kan redigere. Delte brugere får read-only via sync.
|
|
"""
|
|
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
|
|
from sqlalchemy.orm import Session
|
|
from pydantic import BaseModel, EmailStr
|
|
|
|
from app.core.database import get_db
|
|
from app.core.security import get_current_user
|
|
from app.models import User, Project, PlaylistShare
|
|
|
|
router = APIRouter(prefix="/sharing", tags=["sharing"])
|
|
|
|
|
|
class ShareRequest(BaseModel):
|
|
email: EmailStr
|
|
|
|
|
|
# ── Del med bruger ────────────────────────────────────────────────────────────
|
|
|
|
@router.post("/playlists/{project_id}/share", status_code=201)
|
|
async def share_playlist(
|
|
project_id: str,
|
|
data: ShareRequest,
|
|
background_tasks: BackgroundTasks,
|
|
db: Session = Depends(get_db),
|
|
me: User = Depends(get_current_user),
|
|
):
|
|
"""Del en playliste med en bruger — de får listen ved næste sync."""
|
|
project = db.query(Project).filter_by(id=project_id, owner_id=me.id).first()
|
|
if not project:
|
|
raise HTTPException(404, "Playliste ikke fundet eller du er ikke ejer")
|
|
|
|
target = db.query(User).filter_by(email=data.email).first()
|
|
|
|
existing = db.query(PlaylistShare).filter_by(
|
|
project_id=project_id, invited_email=data.email
|
|
).first()
|
|
if existing:
|
|
return {"detail": "Allerede delt med denne bruger"}
|
|
|
|
share = PlaylistShare(
|
|
project_id=project_id,
|
|
shared_with_id=target.id if target else None,
|
|
invited_email=data.email,
|
|
permission="view",
|
|
)
|
|
db.add(share)
|
|
db.commit()
|
|
|
|
# Send invitation-mail
|
|
try:
|
|
from app.core.mail import send_share_invitation
|
|
from app.core.config import settings
|
|
background_tasks.add_task(
|
|
send_share_invitation,
|
|
email=data.email,
|
|
owner_name=me.username,
|
|
playlist_name=project.name,
|
|
permission="view",
|
|
accept_url=f"{settings.BASE_URL}/sharing/playlists/{project_id}",
|
|
)
|
|
except Exception:
|
|
pass
|
|
|
|
return {"detail": f"Delt med {data.email}"}
|
|
|
|
|
|
@router.delete("/playlists/{project_id}/share/{share_id}", status_code=204)
|
|
def remove_share(
|
|
project_id: str,
|
|
share_id: str,
|
|
db: Session = Depends(get_db),
|
|
me: User = Depends(get_current_user),
|
|
):
|
|
project = db.query(Project).filter_by(id=project_id, owner_id=me.id).first()
|
|
if not project:
|
|
raise HTTPException(404, "Playliste ikke fundet")
|
|
share = db.query(PlaylistShare).filter_by(id=share_id, project_id=project_id).first()
|
|
if not share:
|
|
raise HTTPException(404, "Deling ikke fundet")
|
|
db.delete(share)
|
|
db.commit()
|
|
|
|
|
|
@router.get("/playlists/{project_id}/shares")
|
|
def list_shares(
|
|
project_id: str,
|
|
db: Session = Depends(get_db),
|
|
me: User = Depends(get_current_user),
|
|
):
|
|
project = db.query(Project).filter_by(id=project_id, owner_id=me.id).first()
|
|
if not project:
|
|
raise HTTPException(404, "Playliste ikke fundet")
|
|
shares = db.query(PlaylistShare).filter_by(project_id=project_id).all()
|
|
return [{"id": s.id, "email": s.invited_email} for s in shares]
|
|
|
|
|
|
# ── Visibility ────────────────────────────────────────────────────────────────
|
|
|
|
@router.patch("/playlists/{project_id}/visibility")
|
|
def set_visibility(
|
|
project_id: str,
|
|
visibility: str,
|
|
db: Session = Depends(get_db),
|
|
me: User = Depends(get_current_user),
|
|
):
|
|
if visibility not in ("private", "shared", "public"):
|
|
raise HTTPException(400, "Brug private, shared eller public")
|
|
project = db.query(Project).filter_by(id=project_id, owner_id=me.id).first()
|
|
if not project:
|
|
raise HTTPException(404, "Playliste ikke fundet")
|
|
project.visibility = visibility
|
|
db.commit()
|
|
return {"detail": f"Synlighed: {visibility}"}
|
|
|
|
|
|
# ── Hent playliste-indhold ────────────────────────────────────────────────────
|
|
|
|
@router.get("/playlists/{project_id}")
|
|
def get_shared_playlist(
|
|
project_id: str,
|
|
db: Session = Depends(get_db),
|
|
me: User = Depends(get_current_user),
|
|
):
|
|
p = db.query(Project).filter_by(id=project_id).first()
|
|
if not p:
|
|
raise HTTPException(404, "Playliste ikke fundet")
|
|
if p.owner_id != me.id:
|
|
if p.visibility != "public":
|
|
share = db.query(PlaylistShare).filter(
|
|
PlaylistShare.project_id == project_id,
|
|
(PlaylistShare.shared_with_id == me.id) |
|
|
(PlaylistShare.invited_email == me.email)
|
|
).first()
|
|
if not share:
|
|
raise HTTPException(403, "Ingen adgang")
|
|
|
|
from app.models import Song
|
|
songs = []
|
|
for ps in p.project_songs:
|
|
song = db.query(Song).filter_by(id=ps.song_id).first()
|
|
if not song:
|
|
continue
|
|
songs.append({
|
|
"title": song.title,
|
|
"artist": song.artist,
|
|
"position": ps.position,
|
|
"status": ps.status,
|
|
"is_workshop": ps.is_workshop,
|
|
"dance_override": ps.dance_override or "",
|
|
})
|
|
|
|
return {
|
|
"id": p.id,
|
|
"name": p.name,
|
|
"description": p.description or "",
|
|
"visibility": p.visibility,
|
|
"songs": sorted(songs, key=lambda x: x["position"]),
|
|
}
|