Skip to content

Commit 7b5f5e9

Browse files
authored
feat: add recent files backend APIs (#119)
1 parent 7741c1f commit 7b5f5e9

6 files changed

Lines changed: 96 additions & 0 deletions

File tree

api/routers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
from domain.permission import api as permission
2020
from domain.user import api as user
2121
from domain.role import api as role
22+
from domain.recent_files import api as recent_files
2223

2324

2425
def include_routers(app: FastAPI):
2526
app.include_router(adapters.router)
2627
app.include_router(search_api.router)
2728
app.include_router(virtual_fs.router)
29+
app.include_router(recent_files.router)
2830
app.include_router(auth.router)
2931
app.include_router(config.router)
3032
app.include_router(processors.router)

domain/recent_files/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .api import router
2+
3+
__all__ = ["router"]

domain/recent_files/api.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from typing import Annotated
2+
3+
from fastapi import APIRouter, Depends, Query, Request
4+
5+
from api.response import success
6+
from domain.audit import AuditAction, audit
7+
from domain.auth import User, get_current_active_user
8+
9+
from .service import RecentFilesService
10+
from .types import RecordRecentFileRequest
11+
12+
router = APIRouter(prefix="/api/fs/recent", tags=["recent-files"])
13+
14+
15+
@router.get("/")
16+
@audit(action=AuditAction.READ, description="查看最近打开文件")
17+
async def list_recent_files(
18+
request: Request,
19+
current_user: Annotated[User, Depends(get_current_active_user)],
20+
limit: int = Query(20, ge=1, le=200, description="返回数量"),
21+
):
22+
data = await RecentFilesService.list_recent_files(current_user.id, limit)
23+
return success(data)
24+
25+
26+
@router.post("/")
27+
@audit(action=AuditAction.CREATE, description="记录最近打开文件", body_fields=["path"])
28+
async def record_recent_file(
29+
request: Request,
30+
body: RecordRecentFileRequest,
31+
current_user: Annotated[User, Depends(get_current_active_user)],
32+
):
33+
data = await RecentFilesService.record_opened_file(current_user.id, body.path)
34+
return success(data)
35+
36+
37+
@router.delete("/")
38+
@audit(action=AuditAction.DELETE, description="清空最近打开文件")
39+
async def clear_recent_files(
40+
request: Request,
41+
current_user: Annotated[User, Depends(get_current_active_user)],
42+
):
43+
data = await RecentFilesService.clear_recent_files(current_user.id)
44+
return success(data)

domain/recent_files/service.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from datetime import datetime, timezone
2+
3+
from models.database import RecentFile
4+
5+
6+
class RecentFilesService:
7+
@staticmethod
8+
async def record_opened_file(user_id: int, path: str) -> dict:
9+
item, created = await RecentFile.get_or_create(user_id=user_id, path=path)
10+
if not created:
11+
await RecentFile.filter(id=item.id).update(opened_at=datetime.now(timezone.utc))
12+
await item.fetch_from_db()
13+
return {"id": item.id, "path": item.path, "opened_at": item.opened_at.isoformat()}
14+
15+
@staticmethod
16+
async def list_recent_files(user_id: int, limit: int) -> list[dict]:
17+
items = await RecentFile.filter(user_id=user_id).order_by("-opened_at").limit(limit)
18+
return [{"id": i.id, "path": i.path, "opened_at": i.opened_at.isoformat()} for i in items]
19+
20+
@staticmethod
21+
async def clear_recent_files(user_id: int) -> dict:
22+
deleted = await RecentFile.filter(user_id=user_id).delete()
23+
return {"deleted": deleted}

domain/recent_files/types.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from pydantic import BaseModel, Field
2+
3+
4+
class RecordRecentFileRequest(BaseModel):
5+
path: str = Field(..., min_length=1, max_length=4096, description="文件完整路径")
6+
7+
8+
class RecentFileItem(BaseModel):
9+
id: int
10+
path: str
11+
opened_at: str

models/database.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,19 @@ class Meta:
234234
table = "share_links"
235235

236236

237+
class RecentFile(Model):
238+
id = fields.IntField(pk=True)
239+
user: fields.ForeignKeyRelation[UserAccount] = fields.ForeignKeyField(
240+
"models.UserAccount", related_name="recent_files", on_delete=fields.CASCADE
241+
)
242+
path = fields.CharField(max_length=4096)
243+
opened_at = fields.DatetimeField(auto_now=True)
244+
245+
class Meta:
246+
table = "recent_files"
247+
unique_together = (("user", "path"),)
248+
249+
237250
class Plugin(Model):
238251
id = fields.IntField(pk=True)
239252
key = fields.CharField(max_length=100, unique=True) # 插件唯一标识

0 commit comments

Comments
 (0)