liuq 3 månader sedan
förälder
incheckning
762a105557
4 ändrade filer med 69 tillägg och 16 borttagningar
  1. 11 1
      backend/app/main.py
  2. 29 7
      docker-compose.yml
  3. 4 6
      frontend/src/views/Dashboard.vue
  4. 25 2
      frontend/src/views/ResetPassword.vue

+ 11 - 1
backend/app/main.py

@@ -11,7 +11,17 @@ from app.core.database import Base, engine
 from app.core.cache import redis_client
 
 # Configure Logging
-logging.basicConfig(level=logging.INFO)
+log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+logging.basicConfig(level=logging.INFO, format=log_format)
+
+# Update uvicorn loggers to include timestamp
+# Note: This overrides uvicorn's default color formatter
+uvicorn_logger_names = ["uvicorn", "uvicorn.access", "uvicorn.error"]
+for logger_name in uvicorn_logger_names:
+    logging_logger = logging.getLogger(logger_name)
+    for handler in logging_logger.handlers:
+        handler.setFormatter(logging.Formatter(log_format))
+
 logger = logging.getLogger(__name__)
 
 # Retry Logic Configuration

+ 29 - 7
docker-compose.yml

@@ -18,7 +18,7 @@ services:
     environment:
       # Browser needs to access backend directly for CORS requests in Dev mode
       - VITE_API_BASE_URL=http://localhost:8000/api/v1
-    restart: on-failure
+    restart: always
 
   # ==========================================
   # Backend (FastAPI)
@@ -41,12 +41,15 @@ services:
       # Also explictly allow 127.0.0.1 and localhost with port 5173
       - BACKEND_CORS_ORIGINS=["http://localhost:5173", "http://127.0.0.1:5173", "http://frontend:5173"]
     depends_on:
-      - db
-      - redis
-      - hydra
+      db:
+        condition: service_healthy
+      redis:
+        condition: service_healthy
+      hydra:
+        condition: service_started
     volumes:
       - ./backend:/app # Hot Reload for Backend too
-    restart: on-failure
+    restart: always
 
   # ==========================================
   # Database (MySQL)
@@ -64,6 +67,12 @@ services:
       - "3308:3306"
     volumes:
       - db_data:/var/lib/mysql
+    healthcheck:
+      test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "-u", "uap_user", "-p$$MYSQL_PASSWORD"]
+      interval: 10s
+      timeout: 5s
+      retries: 10
+      start_period: 10s
 
   # ==========================================
   # Redis
@@ -73,6 +82,11 @@ services:
     ports:
       - "6379:6379"
     restart: always
+    healthcheck:
+      test: ["CMD", "redis-cli", "ping"]
+      interval: 10s
+      timeout: 5s
+      retries: 5
 
   # ==========================================
   # Ory Hydra Services
@@ -83,13 +97,15 @@ services:
       - DSN=postgres://hydra:secret@postgresd:5432/hydra?sslmode=disable&max_conns=20&max_idle_conns=4
     command: migrate sql -e --yes
     depends_on:
-      - postgresd
+      postgresd:
+        condition: service_healthy
     restart: on-failure
 
   hydra:
     image: oryd/hydra:v2.2.0
     depends_on:
-      - hydra-migrate
+      hydra-migrate:
+        condition: service_completed_successfully
     ports:
       - "4444:4444"
       - "4445:4445"
@@ -121,6 +137,12 @@ services:
       - POSTGRES_DB=hydra
     volumes:
       - postgres_data:/var/lib/postgresql/data
+    healthcheck:
+      test: ["CMD-SHELL", "pg_isready -U hydra"]
+      interval: 10s
+      timeout: 5s
+      retries: 5
+      start_period: 10s
 
 volumes:
   db_data:

+ 4 - 6
frontend/src/views/Dashboard.vue

@@ -29,12 +29,10 @@
             <span>用户管理</span>
           </el-menu-item>
 
-          <el-menu-item index="/dashboard/changelog">
-            <el-icon><List /></el-icon>
-            <span>更新日志</span>
-          </el-menu-item>
-
-          <el-menu-item index="/dashboard/help">
+          <el-menu-item 
+            v-if="user && (user.role === 'SUPER_ADMIN' || user.role === 'DEVELOPER')"
+            index="/dashboard/help"
+          >
             <el-icon><QuestionFilled /></el-icon>
             <span>使用帮助</span>
           </el-menu-item>

+ 25 - 2
frontend/src/views/ResetPassword.vue

@@ -40,14 +40,29 @@
         </el-form-item>
         <el-form-item>
           <div style="display: flex; width: 100%; gap: 10px;">
-            <el-input v-model="form.sms_code" placeholder="收到的短信验证码" prefix-icon="Message" style="flex: 1" />
+            <el-input 
+              v-model="form.sms_code" 
+              placeholder="收到的短信验证码" 
+              prefix-icon="Message" 
+              style="flex: 1"
+              :name="dynamicFields.sms"
+              autocomplete="off"
+            />
             <el-button type="primary" plain :disabled="countdown > 0" @click="openCaptchaDialog">
               {{ countdown > 0 ? `${countdown}s` : '重新发送' }}
             </el-button>
           </div>
         </el-form-item>
         <el-form-item>
-          <el-input v-model="form.new_password" type="password" placeholder="新密码" prefix-icon="Lock" show-password />
+          <el-input 
+            v-model="form.new_password" 
+            type="password" 
+            placeholder="新密码" 
+            prefix-icon="Lock" 
+            show-password 
+            :name="dynamicFields.password"
+            autocomplete="new-password"
+          />
         </el-form-item>
         <el-form-item>
           <el-button type="primary" style="width: 100%" @click="handleReset" :loading="loading">
@@ -105,6 +120,11 @@ const captchaForm = reactive({
   code: ''
 })
 
+const dynamicFields = reactive({
+  sms: 'sms_code',
+  password: 'new_password'
+})
+
 const form = reactive({
   mobile: '',
   captcha_id: '',
@@ -218,6 +238,9 @@ const handleReset = async () => {
 
 onMounted(() => {
   fetchCaptcha()
+  const suffix = Math.random().toString(36).slice(2, 8)
+  dynamicFields.sms = `sms_${suffix}`
+  dynamicFields.password = `pwd_${suffix}`
 })
 
 onUnmounted(() => {