Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion backend/api/api_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,9 @@ async def get_profile(request: Request) -> UserInfo:
"""

if not settings.LDAP_ENABLED:
return UserInfo(username="anonymous", display_name="anonymous", email=None)
return UserInfo(
username="anonymous", display_name="anonymous", email=None, is_admin=True
)

user = get_current_user(request)
username = user.get("sub", user.get("username", ""))
Expand Down
79 changes: 79 additions & 0 deletions backend/api/api_collection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""
Author: Charm
Copyright (c) 2025, All Rights Reserved.
"""

from typing import Any, Dict, Optional

from fastapi import APIRouter, Query, Request

from model.collection import (
CollectionCreateRequest,
CollectionTaskAddRequest,
CollectionUpdateRequest,
)
from service.collection_service import (
add_task_to_collection_svc,
create_collection_svc,
delete_collection_svc,
get_collection_svc,
list_collection_tasks_svc,
list_collections_svc,
remove_task_from_collection_svc,
update_collection_svc,
)

router = APIRouter()


@router.post("", response_model=Dict[str, Any])
async def create_collection(
request: Request, collection_create: CollectionCreateRequest
):
return await create_collection_svc(request, collection_create)


@router.get("", response_model=Dict[str, Any])
async def list_collections(
request: Request,
page: int = Query(1, ge=1),
page_size: int = Query(10, ge=1, le=100),
search: Optional[str] = None,
):
return await list_collections_svc(request, page, page_size, search)


@router.get("/{collection_id}", response_model=Dict[str, Any])
async def get_collection(request: Request, collection_id: str):
return await get_collection_svc(request, collection_id)


@router.put("/{collection_id}", response_model=Dict[str, Any])
async def update_collection(
request: Request, collection_id: str, collection_update: CollectionUpdateRequest
):
return await update_collection_svc(request, collection_id, collection_update)


@router.delete("/{collection_id}", response_model=Dict[str, Any])
async def delete_collection(request: Request, collection_id: str):
return await delete_collection_svc(request, collection_id)


@router.post("/{collection_id}/tasks", response_model=Dict[str, Any])
async def add_task_to_collection(
request: Request, collection_id: str, task_req: CollectionTaskAddRequest
):
return await add_task_to_collection_svc(request, collection_id, task_req)


@router.delete("/{collection_id}/tasks/{task_id}", response_model=Dict[str, Any])
async def remove_task_from_collection(
request: Request, collection_id: str, task_id: str
):
return await remove_task_from_collection_svc(request, collection_id, task_id)


@router.get("/{collection_id}/tasks", response_model=Dict[str, Any])
async def list_collection_tasks(request: Request, collection_id: str):
return await list_collection_tasks_svc(request, collection_id)
2 changes: 2 additions & 0 deletions backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from api.api_analysis import router as analysis
from api.api_auth import router as auth
from api.api_collection import router as collection
from api.api_http_task import router as http_task
from api.api_llm_task import router as llm_task
from api.api_log import router as log
Expand Down Expand Up @@ -121,6 +122,7 @@ def read_root():
# add api routers
app.include_router(analysis, prefix="/api/analyze", tags=["analysis"])
app.include_router(auth, prefix="/api/auth", tags=["auth"])
app.include_router(collection, prefix="/api/collections", tags=["collections"])
app.include_router(system, prefix="/api/system", tags=["system"])
app.include_router(llm_task, prefix="/api/llm-tasks", tags=["llm-tasks"])
app.include_router(http_task, prefix="/api/http-tasks", tags=["http-tasks"])
Expand Down
107 changes: 107 additions & 0 deletions backend/model/collection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
"""
Author: Charm
Copyright (c) 2025, All Rights Reserved.
"""

from typing import List, Optional

from pydantic import BaseModel, Field
from sqlalchemy import Column, DateTime, Integer, String, Text, UniqueConstraint
from sqlalchemy.sql import func

from db.mysql import Base


class Collection(Base):
"""
SQLAlchemy model representing a collection in the 'collections' table.
"""

__tablename__ = "collections"

id = Column(String(40), primary_key=True, index=True)
name = Column(String(255), nullable=False)
description = Column(Text, nullable=True)
rich_content = Column(Text, nullable=True)
created_by = Column(String(100), nullable=True)
is_public = Column(Integer, nullable=False, default=1, server_default="1")
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())


class CollectionTask(Base):
"""
SQLAlchemy model representing the many-to-many relationship between collections and tasks.
"""

__tablename__ = "collection_tasks"

id = Column(Integer, primary_key=True, autoincrement=True)
collection_id = Column(String(40), nullable=False, index=True)
task_id = Column(String(40), nullable=False, index=True)
task_type = Column(String(16), nullable=False)
created_at = Column(DateTime, server_default=func.now())

__table_args__ = (
UniqueConstraint("collection_id", "task_id", name="uk_collection_task"),
)


# Pydantic models for API
class CollectionCreateRequest(BaseModel):
"""Payload for creating a collection."""

name: str = Field(..., description="Collection name")
description: Optional[str] = Field(None, description="Collection description")
rich_content: Optional[str] = Field(None, description="Rich text content")
is_public: Optional[bool] = Field(
True, description="Whether the collection is public"
)


class CollectionUpdateRequest(BaseModel):
"""Payload for partially updating a collection."""

name: Optional[str] = Field(None, description="Collection name")
description: Optional[str] = Field(None, description="Collection description")
rich_content: Optional[str] = Field(None, description="Rich text content")
is_public: Optional[bool] = Field(
None, description="Whether the collection is public"
)


class CollectionTaskAddRequest(BaseModel):
"""Payload for adding a task into a collection."""

task_id: str = Field(..., description="Task ID")
task_type: str = Field(..., description="Task type: http or llm")


class CollectionTaskRemoveRequest(BaseModel):
"""Payload for removing a task from a collection."""

task_id: str = Field(..., description="Task ID")


class CollectionTaskResponse(BaseModel):
"""Response schema for a collection-task relation row."""

id: int
collection_id: str
task_id: str
task_type: str
created_at: str


class CollectionResponse(BaseModel):
"""Response schema for collection detail/list items."""

id: str
name: str
description: Optional[str]
rich_content: Optional[str]
created_by: Optional[str]
is_public: bool
created_at: str
updated_at: str
task_count: int = 0
Loading