Skip to content

Commit f887428

Browse files
authored
Create conversations.py
1 parent ce8954a commit f887428

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

‎app/controllers/conversations.py‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""Conversation business logic: ownership, CRUD, workspace teardown.
2+
3+
The routes above this module only wire HTTP to these calls; every rule
4+
about what a user may see or delete lives here.
5+
"""
6+
7+
from __future__ import annotations
8+
9+
import contextlib
10+
import shutil
11+
from pathlib import Path
12+
13+
from sqlalchemy.orm import Session
14+
15+
from ..infra.config import conversation_workspace, get_settings
16+
from ..models import Conversation, ConversationFile, User, new_id
17+
from ..models.db import commit_now
18+
from .errors import NotFound
19+
20+
21+
def owned(db: Session, user: User, conversation_id: str) -> Conversation:
22+
"""The user's conversation, or NotFound.
23+
24+
Someone else's conversation is reported missing rather than
25+
forbidden, so an id cannot be probed for existence.
26+
"""
27+
conversation = db.get(Conversation, conversation_id)
28+
if conversation is None or conversation.user_id != user.id:
29+
raise NotFound("conversation not found")
30+
return conversation
31+
32+
33+
def create(db: Session, user: User, title: str | None) -> Conversation:
34+
conversation = Conversation(id=new_id("cnv"), user_id=user.id, title=title)
35+
db.add(conversation)
36+
db.flush()
37+
commit_now(db)
38+
return conversation
39+
40+
41+
def list_for_user(db: Session, user: User) -> list[Conversation]:
42+
"""The user's conversations, most recently touched first."""
43+
return (
44+
db.query(Conversation)
45+
.filter(Conversation.user_id == user.id)
46+
.order_by(Conversation.updated_at.desc())
47+
.all()
48+
)
49+
50+
51+
def files_of(db: Session, conversation: Conversation) -> list[ConversationFile]:
52+
"""Uploads attached to a conversation, oldest first."""
53+
return (
54+
db.query(ConversationFile)
55+
.filter(ConversationFile.conversation_id == conversation.id)
56+
.order_by(ConversationFile.created_at)
57+
.all()
58+
)
59+
60+
61+
def delete(db: Session, user: User, conversation_id: str) -> None:
62+
"""Delete a conversation with its uploads and agent workspace."""
63+
conversation = owned(db, user, conversation_id)
64+
# Resolve the workspace BEFORE deleting: conversation_workspace()
65+
# mkdirs, and calling it after the delete would recreate the dir.
66+
workspace = None if get_settings().harness.cwd else conversation_workspace(conversation.id)
67+
for row in files_of(db, conversation):
68+
with contextlib.suppress(OSError):
69+
Path(row.path).unlink()
70+
db.delete(row)
71+
db.delete(conversation)
72+
commit_now(db)
73+
if workspace is not None:
74+
with contextlib.suppress(OSError):
75+
shutil.rmtree(workspace, ignore_errors=True)

0 commit comments

Comments
 (0)