feat: Migrate to MySQL and add unified environment configuration

## Database Migration (SQLite → MySQL)
- Add Alembic migration framework
- Add 'tr_' prefix to all tables to avoid conflicts in shared database
- Remove SQLite support, use MySQL exclusively
- Add pymysql driver dependency
- Change ad_token column to Text type for long JWT tokens

## Unified Environment Configuration
- Centralize all hardcoded settings to environment variables
- Backend: Extend Settings class in app/core/config.py
- Frontend: Use Vite environment variables (import.meta.env)
- Docker: Move credentials to environment variables
- Update .env.example files with comprehensive documentation

## Test Organization
- Move root-level test files to tests/ directory:
  - test_chat_room.py → tests/test_chat_room.py
  - test_websocket.py → tests/test_websocket.py
  - test_realtime_implementation.py → tests/test_realtime_implementation.py
- Fix path references in test_realtime_implementation.py

Breaking Changes:
- CORS now requires explicit origins (no more wildcard)
- All database tables renamed with 'tr_' prefix
- SQLite no longer supported

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
egg
2025-12-07 14:15:11 +08:00
parent 1d5d4d447d
commit 92834dbe0e
39 changed files with 1558 additions and 136 deletions

View File

@@ -1,11 +1,56 @@
# Frontend Environment Variables
# =============================================================================
# Task Reporter - Frontend Environment Configuration
# =============================================================================
# Copy this file to .env and customize as needed.
# All variables are optional and have sensible defaults for development.
# =============================================================================
# -----------------------------------------------------------------------------
# API Configuration
# -----------------------------------------------------------------------------
# API Base URL (optional)
# - For local development: leave empty or don't set (will use /api)
# - For local development: leave empty or don't set (will use /api with proxy)
# - For production with separate backend: set to full URL
# Example: https://api.yourdomain.com/api
VITE_API_BASE_URL=
# Note: When set, this URL is also used for WebSocket connections
# http:// will be converted to ws://
# https:// will be converted to wss://
# API request timeout in milliseconds (default: 30000 = 30 seconds)
VITE_API_TIMEOUT_MS=30000
# -----------------------------------------------------------------------------
# Development Server Configuration
# -----------------------------------------------------------------------------
# Frontend development server port (default: 3000)
VITE_PORT=3000
# Backend API URL for development proxy (default: http://localhost:8000)
# This is used by Vite's proxy configuration during development
VITE_BACKEND_URL=http://localhost:8000
# -----------------------------------------------------------------------------
# WebSocket Configuration
# -----------------------------------------------------------------------------
# Maximum WebSocket reconnection delay in milliseconds (default: 30000 = 30 seconds)
# The reconnection uses exponential backoff, capped at this value
VITE_MAX_RECONNECT_DELAY_MS=30000
# -----------------------------------------------------------------------------
# Query/Cache Configuration
# -----------------------------------------------------------------------------
# Messages refetch interval in milliseconds (default: 30000 = 30 seconds)
# Used for polling online users status
VITE_MESSAGES_REFETCH_INTERVAL_MS=30000
# Reports stale time in milliseconds (default: 30000 = 30 seconds)
# Time before cached report data is considered stale
VITE_REPORTS_STALE_TIME_MS=30000
# =============================================================================
# Notes
# =============================================================================
# - All VITE_ prefixed variables are exposed to the browser
# - Never put sensitive data (API keys, secrets) in frontend environment variables
# - When VITE_API_BASE_URL is set:
# - http:// URLs will be converted to ws:// for WebSocket connections
# - https:// URLs will be converted to wss:// for WebSocket connections
# =============================================================================

View File

@@ -61,12 +61,18 @@ export function useCreateMessage(roomId: string) {
})
}
// Configurable refetch interval for online users (default 30 seconds)
const MESSAGES_REFETCH_INTERVAL = parseInt(
import.meta.env.VITE_MESSAGES_REFETCH_INTERVAL_MS || '30000',
10
)
export function useOnlineUsers(roomId: string) {
return useQuery({
queryKey: messageKeys.online(roomId),
queryFn: () => messagesService.getOnlineUsers(roomId),
enabled: !!roomId,
refetchInterval: 30000, // Refresh every 30 seconds
refetchInterval: MESSAGES_REFETCH_INTERVAL,
})
}

View File

@@ -14,6 +14,12 @@ const reportKeys = {
[...reportKeys.all, 'detail', roomId, reportId] as const,
}
// Configurable stale time for reports (default 30 seconds)
const REPORTS_STALE_TIME = parseInt(
import.meta.env.VITE_REPORTS_STALE_TIME_MS || '30000',
10
)
/**
* Hook to list reports for a room
*/
@@ -22,7 +28,7 @@ export function useReports(roomId: string) {
queryKey: reportKeys.list(roomId),
queryFn: () => reportsService.listReports(roomId),
enabled: !!roomId,
staleTime: 30000, // 30 seconds
staleTime: REPORTS_STALE_TIME,
})
}

View File

@@ -12,7 +12,10 @@ import type {
} from '../types'
const RECONNECT_DELAY = 1000
const MAX_RECONNECT_DELAY = 30000
const MAX_RECONNECT_DELAY = parseInt(
import.meta.env.VITE_MAX_RECONNECT_DELAY_MS || '30000',
10
)
const RECONNECT_MULTIPLIER = 2
interface UseWebSocketOptions {

View File

@@ -3,10 +3,13 @@ import axios, { type AxiosError, type InternalAxiosRequestConfig } from 'axios'
// API Base URL: use environment variable or default to relative path
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api'
// API Timeout: configurable via environment variable (default 30 seconds)
const API_TIMEOUT = parseInt(import.meta.env.VITE_API_TIMEOUT_MS || '30000', 10)
// Create axios instance
const api = axios.create({
baseURL: API_BASE_URL,
timeout: 30000,
timeout: API_TIMEOUT,
headers: {
'Content-Type': 'application/json',
},

View File

@@ -1,28 +1,36 @@
/// <reference types="vitest/config" />
import { defineConfig } from 'vite'
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [
tailwindcss(),
react(),
],
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
ws: true, // Enable WebSocket proxying
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
// Configuration from environment variables with defaults
const PORT = parseInt(env.VITE_PORT || '3000', 10)
const BACKEND_URL = env.VITE_BACKEND_URL || 'http://localhost:8000'
return {
plugins: [
tailwindcss(),
react(),
],
server: {
port: PORT,
proxy: {
'/api': {
target: BACKEND_URL,
changeOrigin: true,
ws: true, // Enable WebSocket proxying
},
},
},
},
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
},
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
},
}
})