Skip to content

feat: 配置管理后台页面增加login功能#60

Open
chenhuping wants to merge 3 commits intomimo-x:mainfrom
chenhuping:feat/background-login
Open

feat: 配置管理后台页面增加login功能#60
chenhuping wants to merge 3 commits intomimo-x:mainfrom
chenhuping:feat/background-login

Conversation

@chenhuping
Copy link
Copy Markdown

@chenhuping chenhuping commented Jan 22, 2026

因服务需要和github或者gitlab交互,有部署到公网的可能性,因此在管理后台加上了登录认证,防止其他人恶意修改网站配置,和获取敏感信息
image
Uploading image.png…

Summary by CodeRabbit

  • New Features

    • Added a login page with username/password and visibility toggle.
    • Display current user in the header and a logout action with confirmation modal.
    • Routes now enforce authentication and redirect appropriately.
    • Server now supports cookie-based sessions with configurable expiry for persistent logins.
  • Bug Fixes

    • Session expiry redirects to login and shows a "login expired" notification.
    • API requests now send session cookies automatically.
  • Chores

    • Added configuration placeholders for default credentials and session expiry.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 22, 2026

📝 Walkthrough

Walkthrough

Adds a cookie-based authentication feature: frontend login UI, Pinia auth store, router guards, Axios cookie handling; backend Django auth endpoints using Redis-backed token storage and a Redis client; environment/example and settings updated to enable the flow.

Changes

Cohort / File(s) Summary
Env / Config
\.env.example
Added DEFAULT_USERNAME, DEFAULT_PASSWORD, and TOKEN_EXPIRE_SECONDS placeholders for backend admin login and token expiry.
Frontend — Auth UI & Store
frontend/src/views/Login.vue, frontend/src/layouts/MainLayout.vue, frontend/src/stores/auth.ts
New Login view and Pinia auth store (isAuthenticated, username, login(), logout(), checkAuth()); MainLayout displays username, adds logout button and confirmation modal, and imports LogOut and useAuthStore.
Frontend — Routing & HTTP
frontend/src/router/index.ts, frontend/src/utils/request.ts, frontend/src/api/index.ts
Added /login route and global auth guard; Axios set with withCredentials: true, 401 handling refined to ignore login endpoint and redirect to /login on expired sessions; added API methods login, logout, checkAuth.
Backend — Auth API
backend/apps/api_auth/views.py, backend/apps/api_auth/urls.py
New Django endpoints: login (validates env credentials, generates token, stores in Redis, sets HttpOnly cookie), logout (deletes token and clears cookie), check_auth (validates token). TOKEN_EXPIRE_SECONDS read from env (default 604800).
Backend — Redis Client & Integration
backend/utils/redis_client.py, backend/core/settings.py, backend/core/urls.py, backend/requirements.txt
Added RedisClient (singleton) and redis_client instance using REDIS_URL; client initializes Redis and exposes set/get/delete/refresh token methods (no in-memory fallback; connection errors raise). Added apps.api_auth to INSTALLED_APPS, registered api/auth/ URLs, and added redis==5.0.1 requirement.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Browser
    participant Router
    participant AuthStore as "Auth Store (Pinia)"
    participant API as "Backend /api/auth"
    participant Redis

    User->>Browser: Open /login, enter credentials
    Browser->>API: POST /api/auth/login (withCredentials)
    API->>Redis: set_token(token, username, expiry)
    Redis-->>API: OK
    API-->>Browser: 200 + Set-Cookie: auth_token (HttpOnly)
    Browser->>AuthStore: checkAuth() / set username, isAuthenticated

    rect rgba(100,150,240,0.5)
    Note over Browser,API: Subsequent protected request
    Browser->>API: Request (cookies auto-sent)
    API->>Redis: get_token(from cookie)
    alt token valid
        Redis-->>API: username
        API-->>Browser: resource response
    else token invalid/expired
        API-->>Browser: 401
        Browser->>Router: redirect /login, clear username
    end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped in, set a tiny key,
Cookies tucked where none can see.
I guard the gate and clear the trail,
Tokens saved in Redis vale.
Hop on—login, logout—safe and free. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding login functionality to the configuration management backend page. It accurately reflects the primary objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @.env.example:
- Around line 36-39: Remove the client-exposed credentials VITE_DEFAULT_USERNAME
and VITE_DEFAULT_PASSWORD from the front-end env (they are bundled into browser
JS) and instead implement server-side authentication: create a backend login API
endpoint that validates credentials stored securely on the server (use hashed
passwords in your user store), issue session tokens or JWTs on successful login,
and move any secret config to server-only env variables (no VITE_ prefix)
referenced by server code; update the front-end to call the login API rather
than relying on VITE_DEFAULT_USERNAME/PASSWORD.

In `@frontend/src/stores/auth.ts`:
- Around line 18-29: The login function currently performs insecure client-side
validation using DEFAULT_USERNAME/DEFAULT_PASSWORD and a predictable token
(btoa(...)); replace this by delegating authentication to a backend API: remove
any hardcoded credential checks in login, call a secure /api/login endpoint with
the provided user/pass, handle the server response (store only a server-issued
opaque token or HttpOnly cookie, not a base64 string), set isAuthenticated.value
and username.value based on the successful response, and remove reliance on
DEFAULT_USERNAME/DEFAULT_PASSWORD and client-generated auth_token; ensure logout
clears client state and the server session/token as appropriate.
- Around line 45-54: checkAuth() currently trusts any token in localStorage;
update it to actually validate the token before setting
isAuthenticated/username: retrieve the token via
localStorage.getItem('auth_token') and call your backend token verification
endpoint (or a JWT verification utility) to confirm signature and expiry, then
use the verified payload (or backend response) to set username.value and
isAuthenticated.value; on verification failure clear the stored token and return
false. Ensure checkAuth() returns true only after a successful verification
response and reference the existing symbols checkAuth, isAuthenticated,
username, and localStorage.getItem('auth_token').
🧹 Nitpick comments (5)
frontend/src/utils/request.ts (1)

47-51: Duplicated auth cleanup logic; use auth store instead.

Lines 48-49 duplicate the cleanup logic from authStore.logout(). This creates a maintenance burden and potential inconsistency if the logout logic changes.

♻️ Suggested refactor
+import { useAuthStore } from '@/stores/auth'
+
 // In the 401 handler:
         case 401:
-          // 未授权,清除登录状态并跳转到登录页
-          localStorage.removeItem('auth_token')
-          localStorage.removeItem('username')
-          router.push('/login')
           alert('登录已过期,请重新登录')
+          const authStore = useAuthStore()
+          authStore.logout()
+          router.push('/login')
           break

Also, consider showing the alert before navigation to avoid potential timing issues where the alert might interfere with the route transition.

frontend/src/router/index.ts (1)

68-73: Consider showing a proper 404 page instead of silent redirect.

Redirecting all unknown paths to /projects silently may confuse users who mistype URLs or follow stale links. A dedicated 404 page with a "go home" option provides better UX and helps identify broken links.

frontend/src/views/Login.vue (3)

53-54: Password visibility toggle icons are semantically inverted.

Currently, EyeOff is shown when the password is hidden (!showPassword), and Eye when visible. Convention is the opposite: Eye icon indicates "click to show password" (when hidden), and EyeOff indicates "click to hide password" (when visible).

♻️ Suggested fix
-              <EyeOff v-if="!showPassword" class="w-5 h-5" />
-              <Eye v-else class="w-5 h-5" />
+              <Eye v-if="!showPassword" class="w-5 h-5" />
+              <EyeOff v-else class="w-5 h-5" />

75-80: Empty hint section can be removed.

This section contains only a comment and renders an empty box. Either populate it with useful information or remove it entirely.

♻️ Suggested removal
-        <!-- Default Account Hint -->
-        <div class="mt-6 p-4 bg-apple-gray-50 rounded-lg">
-          <p class="text-xs text-apple-gray-600 text-center">
-            <!-- 默认账号密码在 .env 文件中配置 -->
-          </p>
-        </div>

115-117: Artificial delay is unnecessary for client-side validation.

The 500ms delay simulates network latency but serves no purpose when authentication is purely client-side. It only degrades UX. If/when authentication moves to a real backend API, this delay should be removed entirely.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@backend/apps/api_auth/views.py`:
- Around line 31-42: The code defaults credentials to insecure values
(default_username/default_password) and ignores failures from
redis_client.set_token; update the auth view to require explicit credential
configuration in non-development environments (do not accept 'admin'/'admin123'
when ENV != 'development' or when a REQUIRED_CREDS flag is set) and validate
that DEFAULT_USERNAME and DEFAULT_PASSWORD are present before authenticating,
and after generating token call redis_client.set_token(token, username,
TOKEN_EXPIRE_SECONDS) and check its return/exception; if storage fails, return
an appropriate error response (500/503) instead of success and log the failure.
Ensure you update checks around default_username/default_password and the token
storage handling in the same view where token generation occurs.
- Around line 17-61: The login view currently ignores redis_client.set_token's
return value and uses insecure cookie/CSRF defaults; update the login function
to check the boolean result of redis_client.set_token(token, username,
TOKEN_EXPIRE_SECONDS) and if it returns False, return a JsonResponse error (do
not set the cookie or report success). Import settings from django.conf and set
the cookie secure flag to secure=not settings.DEBUG in response.set_cookie, and
change samesite to 'Strict' (or remove the `@csrf_exempt` decorator and re-enable
CSRF middleware) so cookie-based auth is protected; ensure any early error
responses do not set the auth_token cookie.

In `@backend/requirements.txt`:
- Around line 37-39: The requirements pin currently fixes redis to 5.0.1 which
is outdated; update the redis package line in requirements.txt (replace
"redis==5.0.1") to a newer compatible version (e.g., "redis==7.1.0" or at least
"redis>=5.1,<8") and then run the test suite and any Redis integration checks to
validate compatibility; if your project must support Python 3.7, choose the
highest 5.x release that supports 3.7 and document the reason in the
requirements or a comment.

In `@backend/utils/redis_client.py`:
- Around line 20-38: The Redis client currently falls back to a per-process
memory store silently in __init__ when self._client connection fails; change
this so the fallback is only used if an explicit environment flag (e.g.,
ALLOW_INMEMORY_TOKEN_STORE) is set to a truthy value—otherwise raise an
exception to fail fast during startup; always initialize self._memory_store
(e.g., to {} ) in the class regardless of connection outcome to avoid attribute
errors; update refresh_token (and any token read/write paths) to return/behave
correctly in memory mode (return False or perform the actual in-memory refresh)
instead of silently returning True; ensure this behavior is enforced at
import-time instantiation of the client by checking the env flag inside __init__
before assigning self._client = None and before falling back to memory.
🧹 Nitpick comments (4)
.env.example (1)

16-20: Optional: reorder keys to satisfy dotenv-linter.

The linter expects DEFAULT_PASSWORD before DEFAULT_USERNAME.

♻️ Proposed reorder
-DEFAULT_USERNAME=your-username-here
-DEFAULT_PASSWORD=your-password-here
+DEFAULT_PASSWORD=your-password-here
+DEFAULT_USERNAME=your-username-here
frontend/src/utils/request.ts (1)

43-55: Consider more precise login endpoint detection and alert timing.

Two minor observations:

  1. The URL check config.url?.includes('/auth/login') could match unintended URLs (e.g., /auth/login-history). A more precise check like config.url?.endsWith('/auth/login') would be safer.

  2. The alert() on line 55 blocks execution and appears after router.push() is called. Consider reordering or using a non-blocking notification.

♻️ Suggested improvement
-      const isLoginRequest = config.url?.includes('/auth/login')
+      const isLoginRequest = config.url?.endsWith('/auth/login')
          // 其他接口返回 401,说明 token 过期,清除用户名并跳转到登录页
          localStorage.removeItem('username')
-          router.push('/login')
          alert('登录已过期,请重新登录')
+          router.push('/login')
          break
frontend/src/layouts/MainLayout.vue (1)

274-278: Add error feedback for logout failures.

The confirmLogout function doesn't handle potential errors from authStore.logout(). While the store's logout uses a finally block to clear local state regardless of API success, users won't be informed if the server-side logout failed (e.g., network issue).

♻️ Optional: Add user feedback on error
 const confirmLogout = async () => {
-  await authStore.logout()
-  showLogoutModal.value = false
-  router.push('/login')
+  try {
+    await authStore.logout()
+  } catch (error) {
+    console.error('Logout error:', error)
+  } finally {
+    showLogoutModal.value = false
+    router.push('/login')
+  }
 }
frontend/src/stores/auth.ts (1)

60-85: Potential edge case: cookie exists but localStorage username is cleared.

The checkAuth function returns false early if localStorage.getItem('username') is null (lines 62-65), skipping the backend validation. This could cause issues if:

  • The HttpOnly cookie still exists (valid session)
  • But localStorage was cleared (e.g., by browser cleanup, user clearing site data partially, or the 401 handler in request.ts)

In this scenario, the user would be forced to re-login even though their session is valid.

♻️ Consider validating with backend regardless of localStorage
   const checkAuth = async (): Promise<boolean> => {
     try {
-      const savedUsername = localStorage.getItem('username')
-      if (!savedUsername) {
-        return false
-      }
-
       // 调用后端接口验证 Cookie 中的 token
       const response: any = await api.checkAuth()

       if (response.success && response.data) {
         isAuthenticated.value = true
         username.value = response.data.username
         // 更新本地存储的用户名
         localStorage.setItem('username', response.data.username)
         return true
       }
+      // Clear any stale localStorage data
+      localStorage.removeItem('username')
       return false
     } catch (error) {
       // Token 无效或已过期
       localStorage.removeItem('username')
       isAuthenticated.value = false
       username.value = ''
       return false
     }
   }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant