This commit is contained in:
@@ -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
@@ -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"],
|
||||
)
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user