From 5048de4fa1f978bf1c3de93eae898f8cace7a1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A5=80=E6=A2=A6?= <3501646051@qq.com> Date: Mon, 18 May 2026 00:02:18 +0800 Subject: [PATCH] feat: add data backup/import, goal step ordering, and PostgreSQL migration - Add GET /api/backup/export and POST /api/backup/import endpoints for full data backup - Add drag-and-drop reorder for goal steps with PUT /api/goals/{id}/steps/reorder - Auto-assign sort_order on step creation (preserves creation order) - Fix duplicate milestone rendering in goal detail page - Add category management button in goal dialog - Migrate database default from SQLite to PostgreSQL - Fix router guard redirect loop for logged-in users on setup/login pages - Fix ALTER TABLE ADD COLUMN crash on callable defaults (uuid.uuid4) - Add auth status rate limiter and token version caching - Update CLAUDE.md to reflect current architecture Co-Authored-By: Claude Opus 4.7 (1M context) --- Dockerfile | 4 +- WebUI/src/api/backup.ts | 13 ++ WebUI/src/api/goals.ts | 4 + WebUI/src/components/GoalDialog.vue | 34 +++- WebUI/src/router/index.ts | 12 ++ WebUI/src/views/GoalDetailPage.vue | 128 ++++++++++++++- WebUI/src/views/LoginView.vue | 2 +- WebUI/src/views/SettingsView.vue | 231 +++++++--------------------- WebUI/src/views/SetupView.vue | 1 + api/app/config.py | 7 +- api/app/database.py | 6 +- api/app/routers/__init__.py | 3 +- api/app/routers/auth.py | 33 +++- api/app/routers/backup.py | 161 +++++++++++++++++++ api/app/routers/goals.py | 40 ++++- api/app/schemas/backup.py | 14 ++ api/app/schemas/goal.py | 11 ++ api/app/utils/auth.py | 12 ++ api/webui/index.html | 6 +- docker-compose.yml | 44 ++++-- requirements.txt | 2 +- 21 files changed, 543 insertions(+), 225 deletions(-) create mode 100644 WebUI/src/api/backup.ts create mode 100644 api/app/routers/backup.py create mode 100644 api/app/schemas/backup.py diff --git a/Dockerfile b/Dockerfile index 40d8114..2c423ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,8 +15,8 @@ COPY api/ ./api/ # 拷贝编译后的前端产物 COPY api/webui/ ./api/webui/ -# 创建数据和日志目录 -RUN mkdir -p api/data api/logs +# 创建日志目录 +RUN mkdir -p api/logs EXPOSE 23994 diff --git a/WebUI/src/api/backup.ts b/WebUI/src/api/backup.ts new file mode 100644 index 0000000..54cfcda --- /dev/null +++ b/WebUI/src/api/backup.ts @@ -0,0 +1,13 @@ +import request from './request' + +export function exportBackup(): Promise { + return request.get('/backup/export', { responseType: 'blob' }) +} + +export function importBackup(file: File): Promise<{ message: string; count: number }> { + const form = new FormData() + form.append('file', file) + return request.post('/backup/import', form, { + headers: { 'Content-Type': 'multipart/form-data' }, + }) +} diff --git a/WebUI/src/api/goals.ts b/WebUI/src/api/goals.ts index ac863b1..7d2ec6b 100644 --- a/WebUI/src/api/goals.ts +++ b/WebUI/src/api/goals.ts @@ -50,6 +50,10 @@ export function toggleStep(goalId: number, stepId: number): Promise { return patch(`/goals/${goalId}/steps/${stepId}/toggle`) } +export function reorderSteps(goalId: number, items: { id: number; sort_order: number }[]): Promise<{ message: string }> { + return put<{ message: string }>(`/goals/${goalId}/steps/reorder`, { items }) +} + // ============ Reviews ============ export function createReview(goalId: number, data: GoalReviewFormData): Promise { diff --git a/WebUI/src/components/GoalDialog.vue b/WebUI/src/components/GoalDialog.vue index 9f04855..2bc6c87 100644 --- a/WebUI/src/components/GoalDialog.vue +++ b/WebUI/src/components/GoalDialog.vue @@ -2,6 +2,7 @@ import { ref, watch } from 'vue' import { useGoalStore } from '@/stores/useGoalStore' import { useCategoryStore } from '@/stores/useCategoryStore' +import { useUIStore } from '@/stores/useUIStore' import type { Goal, GoalFormData } from '@/api/types' const props = defineProps<{ @@ -16,6 +17,7 @@ const emit = defineEmits<{ const goalStore = useGoalStore() const categoryStore = useCategoryStore() +const uiStore = useUIStore() const form = ref({ title: '', @@ -119,14 +121,19 @@ const iconOptions = ['flag', 'star', 'trophy', 'aim', 'medal', 'magic', 'sunny', - + - - - +
+ + + + + + +
- +