feat: Extract hardcoded configs to environment variables
- Add environment variable configuration for backend and frontend - Backend: DB_POOL_SIZE, JWT_EXPIRE_HOURS, timeout configs, directory paths - Frontend: VITE_API_BASE_URL, VITE_UPLOAD_TIMEOUT, Whisper configs - Create deployment script (scripts/deploy-backend.sh) - Create 1Panel deployment guide (docs/1panel-deployment.md) - Update DEPLOYMENT.md with env var documentation - Create README.md with project overview - Remove obsolete PRD.md, SDD.md, TDD.md (replaced by OpenSpec) - Keep CORS allow_origins=["*"] for Electron EXE distribution 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,10 +13,6 @@ from ..config import settings
|
||||
from ..models import SummarizeRequest, SummarizeResponse, ActionItemCreate, TokenPayload
|
||||
from .auth import get_current_user
|
||||
|
||||
# Supported audio formats
|
||||
SUPPORTED_AUDIO_FORMATS = {".mp3", ".wav", ".m4a", ".webm", ".ogg", ".flac", ".aac"}
|
||||
MAX_FILE_SIZE = 500 * 1024 * 1024 # 500MB
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -45,7 +41,7 @@ async def summarize_transcript(
|
||||
"response_mode": "blocking",
|
||||
"user": current_user.email,
|
||||
},
|
||||
timeout=120.0, # Long timeout for LLM processing
|
||||
timeout=settings.llm_timeout_seconds,
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
@@ -135,10 +131,10 @@ async def transcribe_audio(
|
||||
|
||||
# Validate file extension
|
||||
file_ext = os.path.splitext(file.filename or "")[1].lower()
|
||||
if file_ext not in SUPPORTED_AUDIO_FORMATS:
|
||||
if file_ext not in settings.supported_audio_formats_set:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Unsupported audio format. Supported: {', '.join(SUPPORTED_AUDIO_FORMATS)}"
|
||||
detail=f"Unsupported audio format. Supported: {settings.SUPPORTED_AUDIO_FORMATS}"
|
||||
)
|
||||
|
||||
# Create temp directory for processing
|
||||
@@ -151,10 +147,10 @@ async def transcribe_audio(
|
||||
with open(temp_file_path, "wb") as f:
|
||||
while chunk := await file.read(1024 * 1024): # 1MB chunks
|
||||
file_size += len(chunk)
|
||||
if file_size > MAX_FILE_SIZE:
|
||||
if file_size > settings.MAX_FILE_SIZE:
|
||||
raise HTTPException(
|
||||
status_code=413,
|
||||
detail=f"File too large. Maximum size: {MAX_FILE_SIZE // (1024*1024)}MB"
|
||||
detail=f"File too large. Maximum size: {settings.MAX_FILE_SIZE // (1024*1024)}MB"
|
||||
)
|
||||
f.write(chunk)
|
||||
|
||||
@@ -245,18 +241,18 @@ async def transcribe_audio_stream(
|
||||
|
||||
# Validate file extension
|
||||
file_ext = os.path.splitext(file.filename or "")[1].lower()
|
||||
if file_ext not in SUPPORTED_AUDIO_FORMATS:
|
||||
if file_ext not in settings.supported_audio_formats_set:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Unsupported audio format. Supported: {', '.join(SUPPORTED_AUDIO_FORMATS)}"
|
||||
detail=f"Unsupported audio format. Supported: {settings.SUPPORTED_AUDIO_FORMATS}"
|
||||
)
|
||||
|
||||
# Read file into memory for streaming
|
||||
file_content = await file.read()
|
||||
if len(file_content) > MAX_FILE_SIZE:
|
||||
if len(file_content) > settings.MAX_FILE_SIZE:
|
||||
raise HTTPException(
|
||||
status_code=413,
|
||||
detail=f"File too large. Maximum size: {MAX_FILE_SIZE // (1024*1024)}MB"
|
||||
detail=f"File too large. Maximum size: {settings.MAX_FILE_SIZE // (1024*1024)}MB"
|
||||
)
|
||||
|
||||
async def generate_progress() -> AsyncGenerator[str, None]:
|
||||
@@ -366,7 +362,7 @@ async def segment_audio_with_sidecar(audio_path: str, output_dir: str) -> dict:
|
||||
# Send command and wait for response
|
||||
stdout, stderr = await asyncio.wait_for(
|
||||
process.communicate(input=f"{cmd_input}\n{{\"action\": \"quit\"}}\n".encode()),
|
||||
timeout=600 # 10 minutes timeout for large files
|
||||
timeout=settings.upload_timeout_seconds
|
||||
)
|
||||
|
||||
# Parse response (skip status messages, find the segment result)
|
||||
@@ -490,7 +486,7 @@ async def transcribe_chunk_with_dify(
|
||||
}
|
||||
]
|
||||
},
|
||||
timeout=300.0, # 5 minutes per chunk (increased for longer segments)
|
||||
timeout=settings.dify_stt_timeout_seconds,
|
||||
)
|
||||
|
||||
print(f"[Dify] Chat response: {response.status_code}")
|
||||
|
||||
@@ -17,7 +17,7 @@ def create_token(email: str, role: str) -> str:
|
||||
payload = {
|
||||
"email": email,
|
||||
"role": role,
|
||||
"exp": datetime.utcnow() + timedelta(hours=24),
|
||||
"exp": datetime.utcnow() + timedelta(hours=settings.JWT_EXPIRE_HOURS),
|
||||
}
|
||||
return jwt.encode(payload, settings.JWT_SECRET, algorithm="HS256")
|
||||
|
||||
@@ -67,7 +67,7 @@ async def login(request: LoginRequest):
|
||||
response = await client.post(
|
||||
settings.AUTH_API_URL,
|
||||
json={"username": request.email, "password": request.password},
|
||||
timeout=30.0,
|
||||
timeout=settings.auth_timeout_seconds,
|
||||
)
|
||||
|
||||
if response.status_code == 401:
|
||||
|
||||
@@ -6,14 +6,14 @@ import io
|
||||
import os
|
||||
|
||||
from ..database import get_db_cursor
|
||||
from ..config import settings
|
||||
from ..models import TokenPayload
|
||||
from .auth import get_current_user, is_admin
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# Directory paths
|
||||
TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "template")
|
||||
RECORD_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "record")
|
||||
# Base directory for resolving relative paths
|
||||
BASE_DIR = os.path.join(os.path.dirname(__file__), "..", "..")
|
||||
|
||||
|
||||
def fill_template_workbook(
|
||||
@@ -186,8 +186,12 @@ async def export_meeting(
|
||||
)
|
||||
actions = cursor.fetchall()
|
||||
|
||||
# Get directory paths from config
|
||||
template_dir = settings.get_template_dir(BASE_DIR)
|
||||
record_dir = settings.get_record_dir(BASE_DIR)
|
||||
|
||||
# Check for template file
|
||||
template_path = os.path.join(TEMPLATE_DIR, "meeting_template.xlsx")
|
||||
template_path = os.path.join(template_dir, "meeting_template.xlsx")
|
||||
if os.path.exists(template_path):
|
||||
# Load and fill template
|
||||
wb = load_workbook(template_path)
|
||||
@@ -204,10 +208,10 @@ async def export_meeting(
|
||||
filename = f"meeting_{meeting.get('uuid', meeting_id)}.xlsx"
|
||||
|
||||
# Ensure record directory exists
|
||||
os.makedirs(RECORD_DIR, exist_ok=True)
|
||||
os.makedirs(record_dir, exist_ok=True)
|
||||
|
||||
# Save to record directory
|
||||
record_path = os.path.join(RECORD_DIR, filename)
|
||||
record_path = os.path.join(record_dir, filename)
|
||||
wb.save(record_path)
|
||||
|
||||
# Save to bytes buffer for download
|
||||
|
||||
Reference in New Issue
Block a user