v 0.9.1
parent
7e08bcae2d
commit
dec66770f3
|
@ -0,0 +1,37 @@
|
|||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from app.models.models import LoginRequest, LoginResponse
|
||||
from app.core.database import get_db_connection
|
||||
import hashlib
|
||||
import datetime
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
return hashlib.sha256(password.encode()).hexdigest()
|
||||
|
||||
@router.post("/auth/login", response_model=LoginResponse)
|
||||
def login(request: LoginRequest):
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
query = "SELECT user_id, username, caption, email, password_hash FROM users WHERE username = %s"
|
||||
cursor.execute(query, (request.username,))
|
||||
user = cursor.fetchone()
|
||||
|
||||
if not user:
|
||||
raise HTTPException(status_code=401, detail="用户名或密码错误")
|
||||
|
||||
hashed_input = hash_password(request.password)
|
||||
if user['password_hash'] != hashed_input and user['password_hash'] != request.password:
|
||||
raise HTTPException(status_code=401, detail="用户名或密码错误")
|
||||
|
||||
token = f"token_{user['user_id']}_{hash_password(str(datetime.datetime.now()))[:16]}"
|
||||
|
||||
return LoginResponse(
|
||||
user_id=user['user_id'],
|
||||
username=user['username'],
|
||||
caption=user['caption'],
|
||||
email=user['email'],
|
||||
token=token
|
||||
)
|
|
@ -0,0 +1,394 @@
|
|||
|
||||
from fastapi import APIRouter, HTTPException, UploadFile, File, Form
|
||||
from app.models.models import Meeting, TranscriptSegment, CreateMeetingRequest, UpdateMeetingRequest
|
||||
from app.core.database import get_db_connection
|
||||
from app.core.config import UPLOAD_DIR, AUDIO_DIR, ALLOWED_EXTENSIONS, MAX_FILE_SIZE
|
||||
from typing import Optional
|
||||
import os
|
||||
import uuid
|
||||
import shutil
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/meetings", response_model=list[Meeting])
|
||||
def get_meetings(user_id: Optional[int] = None):
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
base_query = '''
|
||||
SELECT
|
||||
m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at,
|
||||
m.user_id as creator_id, u.caption as creator_username
|
||||
FROM meetings m
|
||||
JOIN users u ON m.user_id = u.user_id
|
||||
'''
|
||||
|
||||
if user_id:
|
||||
query = f'''
|
||||
{base_query}
|
||||
LEFT JOIN attendees a ON m.meeting_id = a.meeting_id
|
||||
WHERE m.user_id = %s OR a.user_id = %s
|
||||
GROUP BY m.meeting_id
|
||||
ORDER BY m.meeting_time DESC, m.created_at DESC
|
||||
'''
|
||||
cursor.execute(query, (user_id, user_id))
|
||||
else:
|
||||
query = f" {base_query} ORDER BY m.meeting_time DESC, m.created_at DESC"
|
||||
cursor.execute(query)
|
||||
|
||||
meetings = cursor.fetchall()
|
||||
|
||||
meeting_list = []
|
||||
for meeting in meetings:
|
||||
attendees_query = '''
|
||||
SELECT u.user_id, u.caption
|
||||
FROM attendees a
|
||||
JOIN users u ON a.user_id = u.user_id
|
||||
WHERE a.meeting_id = %s
|
||||
'''
|
||||
cursor.execute(attendees_query, (meeting['meeting_id'],))
|
||||
attendees_data = cursor.fetchall()
|
||||
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
||||
|
||||
meeting_list.append(Meeting(
|
||||
meeting_id=meeting['meeting_id'],
|
||||
title=meeting['title'],
|
||||
meeting_time=meeting['meeting_time'],
|
||||
summary=meeting['summary'],
|
||||
created_at=meeting['created_at'],
|
||||
attendees=attendees,
|
||||
creator_id=meeting['creator_id'],
|
||||
creator_username=meeting['creator_username']
|
||||
))
|
||||
|
||||
return meeting_list
|
||||
|
||||
@router.get("/meetings/{meeting_id}", response_model=Meeting)
|
||||
def get_meeting_details(meeting_id: int):
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
query = '''
|
||||
SELECT
|
||||
m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at,
|
||||
m.user_id as creator_id, u.caption as creator_username,
|
||||
af.file_path as audio_file_path
|
||||
FROM meetings m
|
||||
JOIN users u ON m.user_id = u.user_id
|
||||
LEFT JOIN audio_files af ON m.meeting_id = af.meeting_id
|
||||
WHERE m.meeting_id = %s
|
||||
'''
|
||||
cursor.execute(query, (meeting_id,))
|
||||
meeting = cursor.fetchone()
|
||||
|
||||
if not meeting:
|
||||
raise HTTPException(status_code=404, detail="Meeting not found")
|
||||
|
||||
attendees_query = '''
|
||||
SELECT u.user_id, u.caption
|
||||
FROM attendees a
|
||||
JOIN users u ON a.user_id = u.user_id
|
||||
WHERE a.meeting_id = %s
|
||||
'''
|
||||
cursor.execute(attendees_query, (meeting['meeting_id'],))
|
||||
attendees_data = cursor.fetchall()
|
||||
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
||||
|
||||
meeting_data = Meeting(
|
||||
meeting_id=meeting['meeting_id'],
|
||||
title=meeting['title'],
|
||||
meeting_time=meeting['meeting_time'],
|
||||
summary=meeting['summary'],
|
||||
created_at=meeting['created_at'],
|
||||
attendees=attendees,
|
||||
creator_id=meeting['creator_id'],
|
||||
creator_username=meeting['creator_username']
|
||||
)
|
||||
|
||||
# Add audio file path if exists
|
||||
if meeting['audio_file_path']:
|
||||
meeting_data.audio_file_path = meeting['audio_file_path']
|
||||
|
||||
return meeting_data
|
||||
|
||||
@router.get("/meetings/{meeting_id}/transcript", response_model=list[TranscriptSegment])
|
||||
def get_meeting_transcript(meeting_id: int):
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
# First check if meeting exists
|
||||
meeting_query = "SELECT meeting_id FROM meetings WHERE meeting_id = %s"
|
||||
cursor.execute(meeting_query, (meeting_id,))
|
||||
if not cursor.fetchone():
|
||||
raise HTTPException(status_code=404, detail="Meeting not found")
|
||||
|
||||
# Get transcript segments
|
||||
transcript_query = '''
|
||||
SELECT segment_id, meeting_id, speaker_tag, start_time_ms, end_time_ms, text_content
|
||||
FROM transcript_segments
|
||||
WHERE meeting_id = %s
|
||||
ORDER BY start_time_ms ASC
|
||||
'''
|
||||
cursor.execute(transcript_query, (meeting_id,))
|
||||
segments = cursor.fetchall()
|
||||
|
||||
return [TranscriptSegment(**segment) for segment in segments]
|
||||
|
||||
@router.post("/meetings")
|
||||
def create_meeting(meeting_request: CreateMeetingRequest):
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
# Create meeting
|
||||
meeting_query = '''
|
||||
INSERT INTO meetings (user_id, title, meeting_time, summary)
|
||||
VALUES (%s, %s, %s, %s)
|
||||
'''
|
||||
# Note: You'll need to pass user_id, for now using hardcoded value
|
||||
cursor.execute(meeting_query, (1, meeting_request.title, meeting_request.meeting_time, None))
|
||||
meeting_id = cursor.lastrowid
|
||||
|
||||
# Add attendees
|
||||
for attendee_id in meeting_request.attendee_ids:
|
||||
attendee_query = '''
|
||||
INSERT INTO attendees (meeting_id, user_id)
|
||||
VALUES (%s, %s)
|
||||
ON DUPLICATE KEY UPDATE meeting_id = meeting_id
|
||||
'''
|
||||
cursor.execute(attendee_query, (meeting_id, attendee_id))
|
||||
|
||||
connection.commit()
|
||||
return {"meeting_id": meeting_id, "message": "Meeting created successfully"}
|
||||
|
||||
@router.put("/meetings/{meeting_id}")
|
||||
def update_meeting(meeting_id: int, meeting_request: UpdateMeetingRequest):
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
# Check if meeting exists
|
||||
cursor.execute("SELECT meeting_id FROM meetings WHERE meeting_id = %s", (meeting_id,))
|
||||
if not cursor.fetchone():
|
||||
raise HTTPException(status_code=404, detail="Meeting not found")
|
||||
|
||||
# Update meeting
|
||||
update_query = '''
|
||||
UPDATE meetings
|
||||
SET title = %s, meeting_time = %s, summary = %s
|
||||
WHERE meeting_id = %s
|
||||
'''
|
||||
cursor.execute(update_query, (
|
||||
meeting_request.title,
|
||||
meeting_request.meeting_time,
|
||||
meeting_request.summary,
|
||||
meeting_id
|
||||
))
|
||||
|
||||
# Update attendees - remove existing ones and add new ones
|
||||
cursor.execute("DELETE FROM attendees WHERE meeting_id = %s", (meeting_id,))
|
||||
|
||||
for attendee_id in meeting_request.attendee_ids:
|
||||
attendee_query = '''
|
||||
INSERT INTO attendees (meeting_id, user_id)
|
||||
VALUES (%s, %s)
|
||||
'''
|
||||
cursor.execute(attendee_query, (meeting_id, attendee_id))
|
||||
|
||||
connection.commit()
|
||||
return {"message": "Meeting updated successfully"}
|
||||
|
||||
@router.delete("/meetings/{meeting_id}")
|
||||
def delete_meeting(meeting_id: int):
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
# Check if meeting exists
|
||||
cursor.execute("SELECT meeting_id FROM meetings WHERE meeting_id = %s", (meeting_id,))
|
||||
if not cursor.fetchone():
|
||||
raise HTTPException(status_code=404, detail="Meeting not found")
|
||||
|
||||
# Delete related records first (foreign key constraints)
|
||||
cursor.execute("DELETE FROM transcript_segments WHERE meeting_id = %s", (meeting_id,))
|
||||
cursor.execute("DELETE FROM audio_files WHERE meeting_id = %s", (meeting_id,))
|
||||
cursor.execute("DELETE FROM attachments WHERE meeting_id = %s", (meeting_id,))
|
||||
cursor.execute("DELETE FROM attendees WHERE meeting_id = %s", (meeting_id,))
|
||||
|
||||
# Delete meeting
|
||||
cursor.execute("DELETE FROM meetings WHERE meeting_id = %s", (meeting_id,))
|
||||
|
||||
connection.commit()
|
||||
return {"message": "Meeting deleted successfully"}
|
||||
|
||||
@router.post("/meetings/{meeting_id}/regenerate-summary")
|
||||
def regenerate_summary(meeting_id: int):
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
# Check if meeting exists
|
||||
cursor.execute("SELECT meeting_id FROM meetings WHERE meeting_id = %s", (meeting_id,))
|
||||
if not cursor.fetchone():
|
||||
raise HTTPException(status_code=404, detail="Meeting not found")
|
||||
|
||||
# For now, return a mock summary
|
||||
# In a real implementation, this would call an AI service
|
||||
mock_summary = """# AI 生成摘要
|
||||
|
||||
## 主要议题
|
||||
- 项目进度回顾
|
||||
- 技术方案讨论
|
||||
- 下阶段规划
|
||||
|
||||
## 关键决策
|
||||
- 采用新的技术架构
|
||||
- 调整项目时间节点
|
||||
- 分配任务责任
|
||||
|
||||
## 后续行动
|
||||
- [ ] 完成技术方案文档
|
||||
- [ ] 安排下次会议时间
|
||||
- [ ] 跟进项目进度"""
|
||||
|
||||
# Update meeting summary
|
||||
cursor.execute(
|
||||
"UPDATE meetings SET summary = %s WHERE meeting_id = %s",
|
||||
(mock_summary, meeting_id)
|
||||
)
|
||||
connection.commit()
|
||||
|
||||
return {"summary": mock_summary}
|
||||
|
||||
@router.get("/meetings/{meeting_id}/edit", response_model=Meeting)
|
||||
def get_meeting_for_edit(meeting_id: int):
|
||||
"""Get meeting details with full attendee information for editing"""
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
query = '''
|
||||
SELECT
|
||||
m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at,
|
||||
m.user_id as creator_id, u.caption as creator_username,
|
||||
af.file_path as audio_file_path
|
||||
FROM meetings m
|
||||
JOIN users u ON m.user_id = u.user_id
|
||||
LEFT JOIN audio_files af ON m.meeting_id = af.meeting_id
|
||||
WHERE m.meeting_id = %s
|
||||
'''
|
||||
cursor.execute(query, (meeting_id,))
|
||||
meeting = cursor.fetchone()
|
||||
|
||||
if not meeting:
|
||||
raise HTTPException(status_code=404, detail="Meeting not found")
|
||||
|
||||
# Get attendees with full info for editing
|
||||
attendees_query = '''
|
||||
SELECT u.user_id, u.caption
|
||||
FROM attendees a
|
||||
JOIN users u ON a.user_id = u.user_id
|
||||
WHERE a.meeting_id = %s
|
||||
'''
|
||||
cursor.execute(attendees_query, (meeting['meeting_id'],))
|
||||
attendees_data = cursor.fetchall()
|
||||
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
||||
|
||||
meeting_data = Meeting(
|
||||
meeting_id=meeting['meeting_id'],
|
||||
title=meeting['title'],
|
||||
meeting_time=meeting['meeting_time'],
|
||||
summary=meeting['summary'],
|
||||
created_at=meeting['created_at'],
|
||||
attendees=attendees,
|
||||
creator_id=meeting['creator_id'],
|
||||
creator_username=meeting['creator_username']
|
||||
)
|
||||
|
||||
# Add audio file path if exists
|
||||
if meeting['audio_file_path']:
|
||||
meeting_data.audio_file_path = meeting['audio_file_path']
|
||||
|
||||
return meeting_data
|
||||
|
||||
@router.post("/meetings/upload-audio")
|
||||
async def upload_audio(
|
||||
audio_file: UploadFile = File(...),
|
||||
meeting_id: int = Form(...)
|
||||
):
|
||||
# Validate file extension
|
||||
file_extension = os.path.splitext(audio_file.filename)[1].lower()
|
||||
if file_extension not in ALLOWED_EXTENSIONS:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Unsupported file type. Allowed types: {', '.join(ALLOWED_EXTENSIONS)}"
|
||||
)
|
||||
|
||||
# Check file size
|
||||
if audio_file.size > MAX_FILE_SIZE:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="File size exceeds 100MB limit"
|
||||
)
|
||||
|
||||
# Generate unique filename
|
||||
unique_filename = f"{uuid.uuid4()}{file_extension}"
|
||||
file_path = AUDIO_DIR / unique_filename
|
||||
|
||||
# Store only relative path for database (audio/filename)
|
||||
relative_path = f"audio/{unique_filename}"
|
||||
|
||||
# Save file
|
||||
try:
|
||||
with open(file_path, "wb") as buffer:
|
||||
shutil.copyfileobj(audio_file.file, buffer)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to save file: {str(e)}")
|
||||
|
||||
# Save file info to database
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
# Check if meeting exists
|
||||
cursor.execute("SELECT meeting_id FROM meetings WHERE meeting_id = %s", (meeting_id,))
|
||||
if not cursor.fetchone():
|
||||
# Clean up uploaded file if meeting doesn't exist
|
||||
os.remove(file_path)
|
||||
raise HTTPException(status_code=404, detail="Meeting not found")
|
||||
|
||||
# Insert audio file record
|
||||
insert_query = '''
|
||||
INSERT INTO audio_files (meeting_id, file_name, file_path, file_size, upload_time)
|
||||
VALUES (%s, %s, %s, %s, NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
file_name = VALUES(file_name),
|
||||
file_path = VALUES(file_path),
|
||||
file_size = VALUES(file_size),
|
||||
upload_time = VALUES(upload_time)
|
||||
'''
|
||||
cursor.execute(insert_query, (meeting_id, audio_file.filename, relative_path, audio_file.size))
|
||||
connection.commit()
|
||||
|
||||
return {
|
||||
"message": "Audio file uploaded successfully",
|
||||
"file_name": audio_file.filename,
|
||||
"file_path": relative_path
|
||||
}
|
||||
|
||||
@router.get("/meetings/{meeting_id}/audio")
|
||||
def get_audio_file(meeting_id: int):
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
query = '''
|
||||
SELECT file_name, file_path, file_size, upload_time
|
||||
FROM audio_files
|
||||
WHERE meeting_id = %s
|
||||
'''
|
||||
cursor.execute(query, (meeting_id,))
|
||||
audio_file = cursor.fetchone()
|
||||
|
||||
if not audio_file:
|
||||
raise HTTPException(status_code=404, detail="Audio file not found for this meeting")
|
||||
|
||||
return {
|
||||
"file_name": audio_file['file_name'],
|
||||
"file_path": audio_file['file_path'],
|
||||
"file_size": audio_file['file_size'],
|
||||
"upload_time": audio_file['upload_time']
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from app.models.models import UserInfo
|
||||
from app.core.database import get_db_connection
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/users", response_model=list[UserInfo])
|
||||
def get_all_users():
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
query = '''
|
||||
SELECT
|
||||
user_id, username, caption, email, created_at,
|
||||
(SELECT COUNT(*) FROM meetings WHERE user_id = u.user_id) as meetings_created,
|
||||
(SELECT COUNT(*) FROM attendees WHERE user_id = u.user_id) as meetings_attended
|
||||
FROM users u
|
||||
ORDER BY caption ASC
|
||||
'''
|
||||
cursor.execute(query)
|
||||
users = cursor.fetchall()
|
||||
|
||||
return [UserInfo(**user) for user in users]
|
||||
|
||||
@router.get("/users/{user_id}", response_model=UserInfo)
|
||||
def get_user_info(user_id: int):
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
user_query = "SELECT user_id, username, caption, email, created_at FROM users WHERE user_id = %s"
|
||||
cursor.execute(user_query, (user_id,))
|
||||
user = cursor.fetchone()
|
||||
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="用户不存在")
|
||||
|
||||
created_query = "SELECT COUNT(*) as count FROM meetings WHERE user_id = %s"
|
||||
cursor.execute(created_query, (user_id,))
|
||||
meetings_created = cursor.fetchone()['count']
|
||||
|
||||
attended_query = "SELECT COUNT(*) as count FROM attendees WHERE user_id = %s"
|
||||
cursor.execute(attended_query, (user_id,))
|
||||
meetings_attended = cursor.fetchone()['count']
|
||||
|
||||
return UserInfo(
|
||||
user_id=user['user_id'],
|
||||
username=user['username'],
|
||||
caption=user['caption'],
|
||||
email=user['email'],
|
||||
created_at=user['created_at'],
|
||||
meetings_created=meetings_created,
|
||||
meetings_attended=meetings_attended
|
||||
)
|
|
@ -0,0 +1,32 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# 基础路径配置
|
||||
BASE_DIR = Path(__file__).parent.parent.parent
|
||||
UPLOAD_DIR = BASE_DIR / "uploads"
|
||||
AUDIO_DIR = UPLOAD_DIR / "audio"
|
||||
|
||||
# 文件上传配置
|
||||
ALLOWED_EXTENSIONS = {".mp3", ".wav", ".m4a", ".mpeg", ".mp4"}
|
||||
MAX_FILE_SIZE = 100 * 1024 * 1024 # 100MB
|
||||
|
||||
# 确保上传目录存在
|
||||
UPLOAD_DIR.mkdir(exist_ok=True)
|
||||
AUDIO_DIR.mkdir(exist_ok=True)
|
||||
|
||||
# 数据库配置
|
||||
DATABASE_CONFIG = {
|
||||
'host': os.getenv('DB_HOST', 'localhost'),
|
||||
'user': os.getenv('DB_USER', 'root'),
|
||||
'password': os.getenv('DB_PASSWORD', ''),
|
||||
'database': os.getenv('DB_NAME', 'imeeting'),
|
||||
'port': int(os.getenv('DB_PORT', '3306')),
|
||||
'charset': 'utf8mb4'
|
||||
}
|
||||
|
||||
# API配置
|
||||
API_CONFIG = {
|
||||
'host': os.getenv('API_HOST', '0.0.0.0'),
|
||||
'port': int(os.getenv('API_PORT', '8000')),
|
||||
'cors_origins': os.getenv('CORS_ORIGINS', 'http://localhost:5173').split(',')
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
from fastapi import HTTPException
|
||||
import mysql.connector
|
||||
from mysql.connector import Error
|
||||
from contextlib import contextmanager
|
||||
|
||||
DB_CONFIG = {
|
||||
'host': 'localhost',
|
||||
'database': 'imeeting',
|
||||
'user': 'root',
|
||||
'password': 'sagacity',
|
||||
'port': 3306,
|
||||
'charset': 'utf8mb4'
|
||||
}
|
||||
|
||||
@contextmanager
|
||||
def get_db_connection():
|
||||
connection = None
|
||||
try:
|
||||
connection = mysql.connector.connect(**DB_CONFIG)
|
||||
yield connection
|
||||
except Error as e:
|
||||
print(f"数据库连接错误: {e}")
|
||||
raise HTTPException(status_code=500, detail="数据库连接失败")
|
||||
finally:
|
||||
if connection and connection.is_connected():
|
||||
connection.close()
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
from pydantic import BaseModel, EmailStr
|
||||
from typing import Optional, Union, List
|
||||
import datetime
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
class LoginResponse(BaseModel):
|
||||
user_id: int
|
||||
username: str
|
||||
caption: str
|
||||
email: EmailStr
|
||||
token: str
|
||||
|
||||
class UserInfo(BaseModel):
|
||||
user_id: int
|
||||
username: str
|
||||
caption: str
|
||||
email: EmailStr
|
||||
created_at: datetime.datetime
|
||||
meetings_created: int
|
||||
meetings_attended: int
|
||||
|
||||
class AttendeeInfo(BaseModel):
|
||||
user_id: int
|
||||
caption: str
|
||||
|
||||
class Meeting(BaseModel):
|
||||
meeting_id: int
|
||||
title: str
|
||||
meeting_time: Optional[datetime.datetime]
|
||||
summary: Optional[str]
|
||||
created_at: datetime.datetime
|
||||
attendees: Union[List[str], List[AttendeeInfo]] # Support both formats
|
||||
creator_id: int
|
||||
creator_username: str
|
||||
audio_file_path: Optional[str] = None
|
||||
|
||||
class TranscriptSegment(BaseModel):
|
||||
segment_id: int
|
||||
meeting_id: int
|
||||
speaker_tag: str
|
||||
start_time_ms: int
|
||||
end_time_ms: int
|
||||
text_content: str
|
||||
|
||||
class CreateMeetingRequest(BaseModel):
|
||||
title: str
|
||||
meeting_time: Optional[datetime.datetime]
|
||||
attendee_ids: list[int]
|
||||
|
||||
class UpdateMeetingRequest(BaseModel):
|
||||
title: str
|
||||
meeting_time: Optional[datetime.datetime]
|
||||
summary: Optional[str]
|
||||
attendee_ids: list[int]
|
|
@ -0,0 +1,38 @@
|
|||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from app.api.endpoints import auth, users, meetings
|
||||
from app.core.config import UPLOAD_DIR, API_CONFIG
|
||||
import os
|
||||
|
||||
app = FastAPI(
|
||||
title="iMeeting API",
|
||||
description="智慧会议系统API",
|
||||
version="1.0.0"
|
||||
)
|
||||
|
||||
# 添加CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=API_CONFIG['cors_origins'],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 静态文件服务 - 提供音频文件下载
|
||||
if UPLOAD_DIR.exists():
|
||||
app.mount("/uploads", StaticFiles(directory=str(UPLOAD_DIR)), name="uploads")
|
||||
|
||||
# 包含API路由
|
||||
app.include_router(auth.router, prefix="/api", tags=["Authentication"])
|
||||
app.include_router(users.router, prefix="/api", tags=["Users"])
|
||||
app.include_router(meetings.router, prefix="/api", tags=["Meetings"])
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"message": "Welcome to iMeeting API"}
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run(app, host=API_CONFIG['host'], port=API_CONFIG['port'])
|
|
@ -0,0 +1,6 @@
|
|||
fastapi
|
||||
mysql-connector-python
|
||||
uvicorn[standard]
|
||||
python-multipart
|
||||
pydantic[email]
|
||||
passlib[bcrypt]
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue