Youngger9765 Claude commited on
Commit
3f45635
·
1 Parent(s): 78052ea

Add password protection system

Browse files

- Implement password protection page with session persistence
- Add API authentication middleware with password verification
- Create /api/verify-password endpoint
- Configure APP_PASSWORD environment variable (3030)
- Fix CORS preflight requests being blocked by auth middleware
- Update frontend to handle password verification flow
- Add X-Password-Verified header to authenticated API requests

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

backend/app/config/settings.py CHANGED
@@ -24,8 +24,14 @@ class Settings(BaseSettings):
24
  # Text Limits
25
  max_text_length: int = 10000
26
 
 
 
 
27
  class Config:
28
  env_file = ".env"
29
  case_sensitive = False
30
 
31
- settings = Settings()
 
 
 
 
24
  # Text Limits
25
  max_text_length: int = 10000
26
 
27
+ # Security
28
+ app_password: str = ""
29
+
30
  class Config:
31
  env_file = ".env"
32
  case_sensitive = False
33
 
34
+ settings = Settings()
35
+
36
+ def get_settings():
37
+ return settings
backend/app/models/grading.py CHANGED
@@ -54,4 +54,11 @@ class AgentInfo(BaseModel):
54
 
55
  class AgentsListResponse(BaseModel):
56
  version: int
57
- agents: List[AgentInfo]
 
 
 
 
 
 
 
 
54
 
55
  class AgentsListResponse(BaseModel):
56
  version: int
57
+ agents: List[AgentInfo]
58
+
59
+ class PasswordVerifyRequest(BaseModel):
60
+ password: str = Field(..., description="Password to verify")
61
+
62
+ class PasswordVerifyResponse(BaseModel):
63
+ success: bool = Field(..., description="Whether password is correct")
64
+ message: Optional[str] = None
backend/app/routers/grading.py CHANGED
@@ -5,10 +5,13 @@ from app.models.grading import (
5
  GradeRequest,
6
  GradeResponse,
7
  AgentsListResponse,
8
- AgentInfo
 
 
9
  )
10
  from app.config.agents import get_agents_config
11
  from app.services.openai_service import get_openai_service
 
12
 
13
  logger = logging.getLogger(__name__)
14
  router = APIRouter(prefix="/api", tags=["grading"])
@@ -98,6 +101,30 @@ async def grade_text(request: GradeRequest):
98
  }
99
  )
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  @router.get("/health")
102
  async def health_check():
103
  """Health check endpoint"""
 
5
  GradeRequest,
6
  GradeResponse,
7
  AgentsListResponse,
8
+ AgentInfo,
9
+ PasswordVerifyRequest,
10
+ PasswordVerifyResponse
11
  )
12
  from app.config.agents import get_agents_config
13
  from app.services.openai_service import get_openai_service
14
+ from app.config.settings import get_settings
15
 
16
  logger = logging.getLogger(__name__)
17
  router = APIRouter(prefix="/api", tags=["grading"])
 
101
  }
102
  )
103
 
104
+ @router.post("/verify-password", response_model=PasswordVerifyResponse)
105
+ async def verify_password(request: PasswordVerifyRequest):
106
+ """Verify password for platform access"""
107
+ try:
108
+ settings = get_settings()
109
+ correct_password = settings.app_password or "3030"
110
+
111
+ if request.password == correct_password:
112
+ return PasswordVerifyResponse(
113
+ success=True,
114
+ message="Password verified successfully"
115
+ )
116
+ else:
117
+ return PasswordVerifyResponse(
118
+ success=False,
119
+ message="Incorrect password"
120
+ )
121
+ except Exception as e:
122
+ logger.error(f"Error verifying password: {str(e)}")
123
+ return PasswordVerifyResponse(
124
+ success=False,
125
+ message="Password verification failed"
126
+ )
127
+
128
  @router.get("/health")
129
  async def health_check():
130
  """Health check endpoint"""
backend/main.py CHANGED
@@ -11,6 +11,7 @@ load_dotenv(".env")
11
 
12
  from app.config.settings import settings
13
  from app.routers import grading
 
14
 
15
  # Configure logging
16
  logging.basicConfig(
@@ -44,6 +45,9 @@ app.add_middleware(
44
  allow_headers=["*"],
45
  )
46
 
 
 
 
47
  # Include routers
48
  app.include_router(grading.router)
49
 
 
11
 
12
  from app.config.settings import settings
13
  from app.routers import grading
14
+ from app.middleware.password_auth import PasswordProtectionMiddleware
15
 
16
  # Configure logging
17
  logging.basicConfig(
 
45
  allow_headers=["*"],
46
  )
47
 
48
+ # Add password protection middleware
49
+ app.add_middleware(PasswordProtectionMiddleware)
50
+
51
  # Include routers
52
  app.include_router(grading.router)
53
 
frontend/src/App.tsx CHANGED
@@ -3,8 +3,10 @@ import './App.css';
3
  import type { Agent, GradeResult } from './types/index';
4
  import { gradingApi } from './api/grading';
5
  import AssistantGuide from './AssistantGuide';
 
6
 
7
  function App() {
 
8
  const [currentView, setCurrentView] = useState<'grading' | 'guide'>('grading');
9
  const [agents, setAgents] = useState<Agent[]>([]);
10
  const [selectedAgent, setSelectedAgent] = useState<string>('');
@@ -106,9 +108,19 @@ function App() {
106
  const currentLabels = labels[selectedLanguage as 'zh' | 'en'] || labels.zh;
107
 
108
  useEffect(() => {
109
- loadAgents();
 
 
 
 
 
110
  }, []);
111
 
 
 
 
 
 
112
  const loadAgents = async () => {
113
  try {
114
  const agentsList = await gradingApi.getAgents();
@@ -164,6 +176,11 @@ function App() {
164
  // 取得當前選中的 agent
165
  const currentAgent = agents.find(a => a.key === selectedAgent);
166
 
 
 
 
 
 
167
  return (
168
  <div className="App">
169
  <header className="app-header">
 
3
  import type { Agent, GradeResult } from './types/index';
4
  import { gradingApi } from './api/grading';
5
  import AssistantGuide from './AssistantGuide';
6
+ import PasswordProtection from './components/PasswordProtection';
7
 
8
  function App() {
9
+ const [isPasswordVerified, setIsPasswordVerified] = useState<boolean>(false);
10
  const [currentView, setCurrentView] = useState<'grading' | 'guide'>('grading');
11
  const [agents, setAgents] = useState<Agent[]>([]);
12
  const [selectedAgent, setSelectedAgent] = useState<string>('');
 
108
  const currentLabels = labels[selectedLanguage as 'zh' | 'en'] || labels.zh;
109
 
110
  useEffect(() => {
111
+ // Check if password is already verified
112
+ const savedPassword = sessionStorage.getItem('app_password_verified');
113
+ if (savedPassword === 'true') {
114
+ setIsPasswordVerified(true);
115
+ loadAgents();
116
+ }
117
  }, []);
118
 
119
+ const handlePasswordCorrect = () => {
120
+ setIsPasswordVerified(true);
121
+ loadAgents();
122
+ };
123
+
124
  const loadAgents = async () => {
125
  try {
126
  const agentsList = await gradingApi.getAgents();
 
176
  // 取得當前選中的 agent
177
  const currentAgent = agents.find(a => a.key === selectedAgent);
178
 
179
+ // Show password protection if not verified
180
+ if (!isPasswordVerified) {
181
+ return <PasswordProtection onPasswordCorrect={handlePasswordCorrect} />;
182
+ }
183
+
184
  return (
185
  <div className="App">
186
  <header className="app-header">
frontend/src/api/grading.ts CHANGED
@@ -10,6 +10,15 @@ const api = axios.create({
10
  },
11
  });
12
 
 
 
 
 
 
 
 
 
 
13
  export const gradingApi = {
14
  async getAgents(): Promise<Agent[]> {
15
  const response = await api.get<AgentsResponse>('/agents');
@@ -20,4 +29,9 @@ export const gradingApi = {
20
  const response = await api.post<GradeResponse>('/grade', request);
21
  return response.data;
22
  },
 
 
 
 
 
23
  };
 
10
  },
11
  });
12
 
13
+ // Add password verification interceptor
14
+ api.interceptors.request.use((config) => {
15
+ const passwordVerified = sessionStorage.getItem('app_password_verified');
16
+ if (passwordVerified === 'true') {
17
+ config.headers['X-Password-Verified'] = 'true';
18
+ }
19
+ return config;
20
+ });
21
+
22
  export const gradingApi = {
23
  async getAgents(): Promise<Agent[]> {
24
  const response = await api.get<AgentsResponse>('/agents');
 
29
  const response = await api.post<GradeResponse>('/grade', request);
30
  return response.data;
31
  },
32
+
33
+ async verifyPassword(password: string): Promise<{success: boolean, message?: string}> {
34
+ const response = await axios.post(`${API_BASE_URL}/verify-password`, { password });
35
+ return response.data;
36
+ },
37
  };