2025-11-23 09:08:01 +00:00
|
|
|
from datetime import datetime
|
|
|
|
|
from typing import List, Optional
|
2025-11-19 21:16:32 +00:00
|
|
|
|
2025-11-23 09:08:01 +00:00
|
|
|
from sqlmodel import Field, Relationship, SQLModel # type: ignore
|
2025-11-19 21:16:32 +00:00
|
|
|
|
|
|
|
|
|
2025-12-15 21:33:00 +00:00
|
|
|
class User(SQLModel, table=True): # type: ignore
|
2025-12-08 22:08:30 +00:00
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
|
|
|
username: str = Field(unique=True, index=True)
|
|
|
|
|
email: str = Field(unique=True, index=True)
|
|
|
|
|
hashed_password: str
|
|
|
|
|
salt: str
|
|
|
|
|
wrapped_master_key: str
|
2025-12-15 21:33:00 +00:00
|
|
|
created_at: datetime = Field(default_factory=datetime.now)
|
2025-12-08 22:08:30 +00:00
|
|
|
|
|
|
|
|
# Add relationships to existing models
|
|
|
|
|
notes: List["Note"] = Relationship(back_populates="user")
|
|
|
|
|
folders: List["Folder"] = Relationship(back_populates="user")
|
|
|
|
|
sessions: List["Session"] = Relationship(back_populates="user")
|
2025-12-15 21:33:00 +00:00
|
|
|
tags: List["Tag"] = Relationship(back_populates="user")
|
2025-12-08 22:08:30 +00:00
|
|
|
|
|
|
|
|
|
2025-12-15 21:33:00 +00:00
|
|
|
class Session(SQLModel, table=True): # type: ignore
|
2025-12-08 22:08:30 +00:00
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
|
|
|
session_id: str = Field(unique=True, index=True)
|
|
|
|
|
user_id: int = Field(foreign_key="user.id")
|
2025-12-15 21:33:00 +00:00
|
|
|
created_at: datetime = Field(default_factory=datetime.now)
|
2025-12-08 22:08:30 +00:00
|
|
|
expires_at: datetime
|
|
|
|
|
ip_address: Optional[str] = None
|
|
|
|
|
user_agent: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
user: User = Relationship(back_populates="sessions")
|
|
|
|
|
|
|
|
|
|
|
2025-11-23 09:08:01 +00:00
|
|
|
class Folder(SQLModel, table=True): # type: ignore
|
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
|
|
|
name: str = Field(max_length=255)
|
|
|
|
|
parent_id: Optional[int] = Field(default=None, foreign_key="folder.id")
|
2025-12-15 21:33:00 +00:00
|
|
|
created_at: datetime = Field(default_factory=datetime.now)
|
2025-12-08 22:08:30 +00:00
|
|
|
user_id: int = Field(foreign_key="user.id")
|
2025-11-19 21:16:32 +00:00
|
|
|
|
2025-11-23 09:08:01 +00:00
|
|
|
# Relationships
|
|
|
|
|
parent: Optional["Folder"] = Relationship(
|
|
|
|
|
back_populates="children", sa_relationship_kwargs={"remote_side": "Folder.id"}
|
|
|
|
|
)
|
|
|
|
|
children: List["Folder"] = Relationship(back_populates="parent")
|
|
|
|
|
notes: List["Note"] = Relationship(back_populates="folder")
|
2025-12-08 22:08:30 +00:00
|
|
|
user: User = Relationship(back_populates="folders")
|
2025-11-19 21:16:32 +00:00
|
|
|
|
2025-11-23 09:08:01 +00:00
|
|
|
|
2025-12-15 21:33:00 +00:00
|
|
|
class NoteTag(SQLModel, table=True): #type: ignore
|
|
|
|
|
note_id: int = Field(foreign_key="note.id", primary_key=True)
|
|
|
|
|
tag_id: int = Field(foreign_key="tag.id", primary_key=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Tag(SQLModel, table=True): # type: ignore
|
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
|
|
|
name: str = Field(max_length=255)
|
|
|
|
|
parent_id: Optional[int] = Field(default=None, foreign_key="tag.id")
|
|
|
|
|
user_id: int = Field(foreign_key="user.id")
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.now)
|
|
|
|
|
|
|
|
|
|
# Relationships
|
|
|
|
|
user: User = Relationship(back_populates="tags")
|
|
|
|
|
parent: Optional["Tag"] = Relationship(
|
|
|
|
|
back_populates="children",
|
|
|
|
|
sa_relationship_kwargs={"remote_side": "Tag.id"}
|
|
|
|
|
)
|
|
|
|
|
children: List["Tag"] = Relationship(back_populates="parent")
|
|
|
|
|
notes: List["Note"] = Relationship(back_populates="tags", link_model=NoteTag)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-11-23 09:08:01 +00:00
|
|
|
class Note(SQLModel, table=True): # type: ignore
|
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
|
|
|
title: str = Field(max_length=255)
|
|
|
|
|
content: str
|
|
|
|
|
folder_id: Optional[int] = Field(default=None, foreign_key="folder.id")
|
2025-12-15 21:33:00 +00:00
|
|
|
created_at: datetime = Field(default_factory=datetime.now)
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.now)
|
2025-12-08 22:08:30 +00:00
|
|
|
user_id: int = Field(foreign_key="user.id")
|
2025-11-23 09:08:01 +00:00
|
|
|
|
2025-12-15 21:33:00 +00:00
|
|
|
#Relationships
|
2025-11-23 09:08:01 +00:00
|
|
|
folder: Optional[Folder] = Relationship(back_populates="notes")
|
2025-12-08 22:08:30 +00:00
|
|
|
user: User = Relationship(back_populates="notes")
|
2025-12-15 21:33:00 +00:00
|
|
|
tags: List[Tag] = Relationship(back_populates="notes", link_model=NoteTag)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# API Response models
|
|
|
|
|
class TagRead(SQLModel):
|
|
|
|
|
id: int
|
|
|
|
|
name: str
|
|
|
|
|
parent_id: Optional[int] = None
|
|
|
|
|
created_at: datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TagCreate(SQLModel):
|
|
|
|
|
name: str
|
|
|
|
|
parent_id: Optional[int] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TagUpdate(SQLModel):
|
|
|
|
|
name: Optional[str] = None
|
|
|
|
|
parent_id: Optional[int] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TagTreeNode(SQLModel):
|
|
|
|
|
id: int
|
|
|
|
|
name: str
|
2025-12-22 15:23:40 +00:00
|
|
|
parent_id: Optional[int] = None
|
|
|
|
|
created_at: datetime
|
2025-12-15 21:33:00 +00:00
|
|
|
children: List["TagTreeNode"] = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TagTreeResponse(SQLModel):
|
|
|
|
|
tags: List[TagTreeNode]
|
2025-11-23 09:08:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class NoteRead(SQLModel):
|
|
|
|
|
id: int
|
|
|
|
|
title: str
|
|
|
|
|
content: str
|
|
|
|
|
folder_id: Optional[int] = None
|
|
|
|
|
created_at: datetime
|
|
|
|
|
updated_at: datetime
|
2025-12-15 21:33:00 +00:00
|
|
|
tags: List[TagRead] = []
|
2025-11-23 09:08:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class FolderTreeNode(SQLModel):
|
|
|
|
|
id: int
|
|
|
|
|
name: str
|
|
|
|
|
notes: List[NoteRead] = []
|
|
|
|
|
children: List["FolderTreeNode"] = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FolderTreeResponse(SQLModel):
|
|
|
|
|
folders: List[FolderTreeNode]
|
|
|
|
|
orphaned_notes: List[NoteRead]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Create/Update models
|
|
|
|
|
class NoteCreate(SQLModel):
|
|
|
|
|
title: str
|
|
|
|
|
content: str
|
|
|
|
|
folder_id: Optional[int] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class NoteUpdate(SQLModel):
|
|
|
|
|
title: Optional[str] = None
|
|
|
|
|
content: Optional[str] = None
|
|
|
|
|
folder_id: Optional[int] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FolderCreate(SQLModel):
|
|
|
|
|
name: str
|
|
|
|
|
parent_id: Optional[int] = None
|
2025-11-30 19:40:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class FolderUpdate(SQLModel):
|
|
|
|
|
name: Optional[str] = None
|
|
|
|
|
parent_id: Optional[int] = None
|