perf: consolidate initial navigation loading
ci / docker (push) Successful in 3m46s

This commit is contained in:
2026-09-05 20:29:47 +08:00
parent 05ab768a3b
commit 27432fa2e2
4 changed files with 79 additions and 1 deletions
+25
View File
@@ -173,6 +173,31 @@ async def me(user: User = Depends(current_user)):
return user
@app.get("/api/v1/bootstrap")
async def bootstrap_data(user: User = Depends(current_user), db: AsyncSession = Depends(get_db)):
folders = list((await db.scalars(
select(Folder)
.where(Folder.user_id == user.id, Folder.deleted_at.is_(None))
.order_by(Folder.position, Folder.created_at)
)).all())
lists = list((await db.scalars(
select(TaskList)
.where(TaskList.user_id == user.id, TaskList.deleted_at.is_(None))
.order_by(TaskList.is_inbox.desc(), TaskList.position, TaskList.created_at)
)).all())
tags = list((await db.scalars(
select(Tag).where(Tag.user_id == user.id).order_by(Tag.name, Tag.id)
)).all())
inbox = next((item for item in lists if item.is_inbox), None)
return {
"user": UserOut.model_validate(user),
"folders": [FolderOut.model_validate(item) for item in folders],
"lists": [ListOut.model_validate(item) for item in lists],
"tags": [TagOut.model_validate(item) for item in tags],
"inbox_id": inbox.id if inbox else None,
}
@app.post("/api/v1/auth/logout", status_code=204)
async def logout(
response: Response,
+14 -1
View File
@@ -107,8 +107,14 @@ async function bootstrap() {
const status = await api('/setup/status')
initialized.value = status.initialized
if (status.initialized) {
await api('/me')
const data = await api('/bootstrap')
authenticated.value = true
folders.value = data.folders ?? []
lists.value = data.lists ?? []
tags.value = data.tags ?? []
navigationLoaded.value = true
activeList.value = data.inbox_id || lists.value.find((item: TaskList) => item.is_inbox)?.id || lists.value[0]?.id || ''
expandedFolders.value = new Set(folders.value.map((folder) => folder.id))
await loadAll()
}
} catch { authenticated.value = false; initialized.value ??= true }
@@ -119,6 +125,13 @@ async function submitAuth() {
const path = initialized.value ? '/auth/login' : '/setup/initialize'
await api(path, { method: 'POST', body: JSON.stringify({ username: username.value, password: password.value }) })
initialized.value = true; authenticated.value = true
const data = await api('/bootstrap')
folders.value = data.folders ?? []
lists.value = data.lists ?? []
tags.value = data.tags ?? []
navigationLoaded.value = true
activeList.value = data.inbox_id || lists.value.find((item: TaskList) => item.is_inbox)?.id || lists.value[0]?.id || ''
expandedFolders.value = new Set(folders.value.map((folder) => folder.id))
await loadAll()
} catch (reason) { fail(reason) }
}
@@ -0,0 +1,23 @@
"""remove duplicate habit log index
Revision ID: 0006
Revises: 0005
"""
from alembic import op
revision = "0006"
down_revision = "0005"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.drop_index("ix_habit_logs_habit_day", table_name="habit_logs")
def downgrade() -> None:
op.create_index(
"ix_habit_logs_habit_day",
"habit_logs",
["habit_id", "day"],
)
+17
View File
@@ -12,6 +12,23 @@ def boot(client):
return client.get("/api/v1/lists").json()[0]
def test_bootstrap_returns_navigation_and_current_user(client):
inbox = boot(client)
client.post("/api/v1/folders", json={"name": "工作"})
client.post("/api/v1/tags", json={"name": "重要", "color": "#ff0000"})
client.post("/api/v1/tasks", json={"title": "首屏任务", "list_id": inbox["id"]})
response = client.get("/api/v1/bootstrap")
assert response.status_code == 200
data = response.json()
assert data["user"]["username"] == "owner"
assert any(row["id"] == inbox["id"] for row in data["lists"])
assert [row["name"] for row in data["folders"]] == ["工作"]
assert [row["name"] for row in data["tags"]] == ["重要"]
assert data["inbox_id"] == inbox["id"]
def test_recurring_calendar_exceptions_and_scopes(client):
inbox = boot(client)
task = client.post(