feat: silent wechat login, marketing tab optimization, admin page foundation
- Add silent WeChat login for MP/browser environments - Fix Python 3.6 compatibility (remove typing.Annotated usage) - Marketing page: tab-based content generation with category support - Translate page: add auto-detect language default - Homepage: add TTS playback, announcement ticker, remove redundant quick-actions - Fix FAB button overlap with custom tabbar on customers/quotation pages - Make openai/anthropic imports lazy for Python 3.6 compat
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated
|
||||
from app.database import get_db
|
||||
from app.services.admin import AdminService
|
||||
from app.api.v1.deps import get_current_user
|
||||
@@ -17,7 +16,7 @@ async def require_admin(current_user: dict = Depends(get_current_user)) -> dict:
|
||||
@router.get("/dashboard")
|
||||
async def get_dashboard(
|
||||
_: dict = Depends(require_admin),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = AdminService(db)
|
||||
return await service.get_dashboard()
|
||||
@@ -28,7 +27,7 @@ async def list_users(
|
||||
page: int = Query(1, ge=1),
|
||||
size: int = Query(20, ge=1, le=100),
|
||||
_: dict = Depends(require_admin),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = AdminService(db)
|
||||
return await service.list_users(page, size)
|
||||
@@ -39,7 +38,7 @@ async def update_user_tier(
|
||||
target_user_id: str,
|
||||
data: dict,
|
||||
_: dict = Depends(require_admin),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = AdminService(db)
|
||||
tier = data.get("tier")
|
||||
@@ -55,7 +54,7 @@ async def update_user_tier(
|
||||
async def toggle_user_active(
|
||||
target_user_id: str,
|
||||
_: dict = Depends(require_admin),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = AdminService(db)
|
||||
success = await service.toggle_user_active(target_user_id)
|
||||
@@ -66,7 +65,7 @@ async def toggle_user_active(
|
||||
|
||||
@router.get("/health")
|
||||
async def system_health(
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = AdminService(db)
|
||||
return await service.get_system_health()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated
|
||||
from app.database import get_db
|
||||
from app.services.analytics import AnalyticsService
|
||||
from app.api.v1.deps import get_current_user_id
|
||||
@@ -11,7 +10,7 @@ router = APIRouter()
|
||||
@router.get("/customers")
|
||||
async def customer_analytics(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = AnalyticsService(db)
|
||||
return await service.get_customer_stats(user_id)
|
||||
@@ -20,7 +19,7 @@ async def customer_analytics(
|
||||
@router.get("/translations")
|
||||
async def translation_analytics(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = AnalyticsService(db)
|
||||
return await service.get_translation_stats(user_id)
|
||||
@@ -29,7 +28,7 @@ async def translation_analytics(
|
||||
@router.get("/quotations")
|
||||
async def quotation_analytics(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = AnalyticsService(db)
|
||||
return await service.get_quotation_stats(user_id)
|
||||
@@ -38,7 +37,7 @@ async def quotation_analytics(
|
||||
@router.get("/messages")
|
||||
async def message_analytics(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = AnalyticsService(db)
|
||||
return await service.get_message_stats(user_id)
|
||||
@@ -47,7 +46,7 @@ async def message_analytics(
|
||||
@router.get("/overview")
|
||||
async def overview(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = AnalyticsService(db)
|
||||
customers = await service.get_customer_stats(user_id)
|
||||
@@ -67,7 +66,7 @@ async def overview(
|
||||
@router.get("/marketing")
|
||||
async def marketing_analytics(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = AnalyticsService(db)
|
||||
return await service.get_marketing_stats(user_id)
|
||||
|
||||
@@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, HTTPException, status, Header
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from typing import Annotated, Optional
|
||||
from typing import Optional
|
||||
import uuid
|
||||
from app.database import get_db
|
||||
from app.models.user import User
|
||||
@@ -31,7 +31,7 @@ class RefreshRequest(BaseModel):
|
||||
|
||||
|
||||
@router.post("/register")
|
||||
async def register(data: RegisterRequest, db: Annotated[AsyncSession, Depends(get_db)] = None):
|
||||
async def register(data: RegisterRequest, db: AsyncSession = Depends(get_db)):
|
||||
existing = await db.execute(select(User).where(User.phone == data.phone))
|
||||
if existing.scalar_one_or_none():
|
||||
raise HTTPException(status_code=400, detail="Phone already registered")
|
||||
@@ -56,8 +56,8 @@ async def register(data: RegisterRequest, db: Annotated[AsyncSession, Depends(ge
|
||||
|
||||
@router.post("/login", response_model=LoginResponse)
|
||||
async def login(
|
||||
form: Annotated[OAuth2PasswordRequestForm, Depends()],
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
form: OAuth2PasswordRequestForm = Depends(),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
result = await db.execute(select(User).where(User.phone == form.username))
|
||||
user = result.scalar_one_or_none()
|
||||
@@ -128,7 +128,7 @@ async def refresh(data: RefreshRequest):
|
||||
@router.get("/me")
|
||||
async def get_me(
|
||||
authorization: Optional[str] = Header(None, alias="Authorization"),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
if not authorization or not authorization.startswith("Bearer "):
|
||||
raise HTTPException(status_code=401, detail="Missing token")
|
||||
@@ -178,8 +178,17 @@ class WeChatLoginRequest(BaseModel):
|
||||
iv: str = ""
|
||||
|
||||
|
||||
@router.get("/wechat/config")
|
||||
async def wechat_config():
|
||||
from app.config import settings
|
||||
return {
|
||||
"available": bool(settings.WECHAT_APP_ID and settings.WECHAT_APP_SECRET),
|
||||
"app_id": settings.WECHAT_APP_ID or "",
|
||||
}
|
||||
|
||||
|
||||
@router.post("/wechat-login")
|
||||
async def wechat_login(data: WeChatLoginRequest, db: Annotated[AsyncSession, Depends(get_db)] = None):
|
||||
async def wechat_login(data: WeChatLoginRequest, db: AsyncSession = Depends(get_db)):
|
||||
from app.services.wechat import wechat_service
|
||||
|
||||
session = await wechat_service.code2session(data.code)
|
||||
@@ -216,7 +225,7 @@ async def wechat_login(data: WeChatLoginRequest, db: Annotated[AsyncSession, Dep
|
||||
async def update_settings(
|
||||
data: SettingsUpdate,
|
||||
authorization: Optional[str] = Header(None, alias="Authorization"),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
if not authorization or not authorization.startswith("Bearer "):
|
||||
raise HTTPException(status_code=401, detail="Missing token")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, UploadFile, File, Response
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated, Optional, List
|
||||
from typing import Optional, List
|
||||
from app.database import get_db
|
||||
from app.services.customer import CustomerService
|
||||
from app.services.customer_health import CustomerHealthService
|
||||
@@ -18,7 +18,7 @@ async def list_customers(
|
||||
page: int = Query(1, ge=1),
|
||||
size: int = Query(20, ge=1, le=100),
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = CustomerService(db)
|
||||
return await service.list_customers(user_id, status, page, size)
|
||||
@@ -28,7 +28,7 @@ async def list_customers(
|
||||
async def get_silent(
|
||||
days: int = Query(3, ge=1),
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = CustomerService(db)
|
||||
customers = await service.get_silent_customers(user_id, days)
|
||||
@@ -43,7 +43,7 @@ async def get_silent(
|
||||
async def get_customer(
|
||||
customer_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = CustomerService(db)
|
||||
customer = await service.get_customer(user_id, customer_id)
|
||||
@@ -56,7 +56,7 @@ async def get_customer(
|
||||
async def create_customer(
|
||||
data: dict,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = CustomerService(db)
|
||||
customer = await service.create_customer(user_id, data)
|
||||
@@ -68,7 +68,7 @@ async def update_customer(
|
||||
customer_id: str,
|
||||
data: dict,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = CustomerService(db)
|
||||
customer = await service.update_customer(user_id, customer_id, data)
|
||||
@@ -81,7 +81,7 @@ async def update_customer(
|
||||
async def delete_customer(
|
||||
customer_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = CustomerService(db)
|
||||
deleted = await service.delete_customer(user_id, customer_id)
|
||||
@@ -94,7 +94,7 @@ async def delete_customer(
|
||||
async def import_customers(
|
||||
file: UploadFile = File(...),
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
from app.workers.tasks import process_customer_import
|
||||
|
||||
@@ -135,7 +135,7 @@ async def import_customers(
|
||||
async def export_customers(
|
||||
status: Optional[str] = None,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = CustomerService(db)
|
||||
result = await service.list_customers(user_id, status, 1, 9999)
|
||||
@@ -151,7 +151,7 @@ async def export_customers(
|
||||
@router.get("/health-overview")
|
||||
async def get_health_overview(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = CustomerHealthService(db)
|
||||
return await service.get_health_overview(user_id)
|
||||
@@ -160,7 +160,7 @@ async def get_health_overview(
|
||||
@router.get("/health-scores")
|
||||
async def get_all_health_scores(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = CustomerHealthService(db)
|
||||
return {"items": await service.get_all_health_scores(user_id)}
|
||||
@@ -170,7 +170,7 @@ async def get_all_health_scores(
|
||||
async def get_customer_health(
|
||||
customer_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = CustomerHealthService(db)
|
||||
health = await service.get_customer_health(user_id, customer_id)
|
||||
@@ -185,7 +185,7 @@ async def get_conversation(
|
||||
page: int = Query(1, ge=1),
|
||||
size: int = Query(50, ge=1, le=200),
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = CustomerService(db)
|
||||
return await service.get_conversation(user_id, customer_id, page, size)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated
|
||||
from pydantic import BaseModel
|
||||
from app.database import get_db
|
||||
from app.models.feedback import Feedback
|
||||
@@ -19,7 +18,7 @@ class FeedbackRequest(BaseModel):
|
||||
async def submit_feedback(
|
||||
data: FeedbackRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
if not data.content.strip():
|
||||
raise HTTPException(status_code=400, detail="Content is required")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated, Optional
|
||||
from typing import Optional
|
||||
from app.database import get_db
|
||||
from app.services.followup_engine import FollowupEngine
|
||||
from app.api.v1.deps import get_current_user_id
|
||||
@@ -11,7 +11,7 @@ router = APIRouter()
|
||||
@router.get("/strategies")
|
||||
async def list_strategies(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
engine = FollowupEngine(db)
|
||||
await engine.ensure_default_strategies()
|
||||
@@ -23,7 +23,7 @@ async def get_pending_followups(
|
||||
page: int = Query(1, ge=1),
|
||||
size: int = Query(20, ge=1, le=100),
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
engine = FollowupEngine(db)
|
||||
return await engine.get_pending_followups(user_id, page, size)
|
||||
@@ -34,7 +34,7 @@ async def get_followup_logs(
|
||||
page: int = Query(1, ge=1),
|
||||
size: int = Query(20, ge=1, le=100),
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
engine = FollowupEngine(db)
|
||||
return await engine.get_followup_logs(user_id, page, size)
|
||||
@@ -44,7 +44,7 @@ async def get_followup_logs(
|
||||
async def mark_followup_sent(
|
||||
log_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
engine = FollowupEngine(db)
|
||||
success = await engine.mark_sent(user_id, log_id)
|
||||
@@ -58,7 +58,7 @@ async def edit_and_send_followup(
|
||||
log_id: str,
|
||||
body: dict,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
edited_text = body.get("edited_text", "")
|
||||
if not edited_text:
|
||||
@@ -73,7 +73,7 @@ async def edit_and_send_followup(
|
||||
@router.get("/stats")
|
||||
async def get_followup_stats(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
engine = FollowupEngine(db)
|
||||
return await engine.get_stats(user_id)
|
||||
@@ -82,7 +82,7 @@ async def get_followup_stats(
|
||||
@router.post("/scan")
|
||||
async def trigger_followup_scan(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
engine = FollowupEngine(db)
|
||||
result = await engine.scan_and_followup()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated
|
||||
from app.database import get_db
|
||||
from app.services.preference import UserPreferenceService
|
||||
from app.services.marketing_effect import MarketingEffectService
|
||||
@@ -13,7 +12,7 @@ router = APIRouter()
|
||||
async def record_selection(
|
||||
data: dict,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
message_id = data.get("message_id")
|
||||
selected_index = data.get("selected_index")
|
||||
@@ -30,7 +29,7 @@ async def record_selection(
|
||||
async def record_edit(
|
||||
data: dict,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
message_id = data.get("message_id")
|
||||
edited_text = data.get("edited_text")
|
||||
@@ -46,7 +45,7 @@ async def record_edit(
|
||||
@router.post("/analyze")
|
||||
async def analyze_preferences(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = UserPreferenceService(db)
|
||||
preferences = await service.analyze_preferences(user_id)
|
||||
@@ -56,7 +55,7 @@ async def analyze_preferences(
|
||||
@router.get("/preferences")
|
||||
async def get_preferences(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = UserPreferenceService(db)
|
||||
return await service.get_analysis(user_id)
|
||||
@@ -66,7 +65,7 @@ async def get_preferences(
|
||||
async def track_marketing_effect(
|
||||
data: dict,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = MarketingEffectService(db)
|
||||
result = await service.track_event(
|
||||
@@ -87,7 +86,7 @@ async def get_marketing_effects(
|
||||
page: int = 1,
|
||||
size: int = 20,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = MarketingEffectService(db)
|
||||
return await service.get_effects(user_id, page, size)
|
||||
@@ -96,7 +95,7 @@ async def get_marketing_effects(
|
||||
@router.get("/marketing-effects/stats")
|
||||
async def get_marketing_effect_stats(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = MarketingEffectService(db)
|
||||
return await service.get_stats(user_id)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from typing import Optional, Annotated
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.database import get_db
|
||||
@@ -43,7 +43,7 @@ class CompetitorRequest(BaseModel):
|
||||
async def generate_marketing(
|
||||
data: MarketingRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = MarketingService()
|
||||
pref_service = UserPreferenceService(db)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated, Optional
|
||||
from typing import Optional
|
||||
from app.database import get_db
|
||||
from app.services.notification import NotificationService
|
||||
from app.api.v1.deps import get_current_user_id
|
||||
@@ -14,7 +14,7 @@ async def list_notifications(
|
||||
size: int = Query(20, ge=1, le=100),
|
||||
unread_only: bool = Query(False),
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = NotificationService(db)
|
||||
return await service.list_notifications(user_id, page, size, unread_only)
|
||||
@@ -23,7 +23,7 @@ async def list_notifications(
|
||||
@router.get("/unread-count")
|
||||
async def unread_count(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = NotificationService(db)
|
||||
count = await service.get_unread_count(user_id)
|
||||
@@ -34,7 +34,7 @@ async def unread_count(
|
||||
async def mark_read(
|
||||
notification_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = NotificationService(db)
|
||||
success = await service.mark_read(user_id, notification_id)
|
||||
@@ -46,7 +46,7 @@ async def mark_read(
|
||||
@router.post("/read-all")
|
||||
async def mark_all_read(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = NotificationService(db)
|
||||
count = await service.mark_all_read(user_id)
|
||||
@@ -57,7 +57,7 @@ async def mark_all_read(
|
||||
async def delete_notification(
|
||||
notification_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = NotificationService(db)
|
||||
success = await service.delete_notification(user_id, notification_id)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated
|
||||
from pydantic import BaseModel
|
||||
from app.database import get_db
|
||||
from app.services.onboarding import OnboardingService
|
||||
@@ -19,7 +18,7 @@ class OnboardingRequest(BaseModel):
|
||||
@router.get("/status")
|
||||
async def get_status(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = OnboardingService(db)
|
||||
return await service.check_status(user_id)
|
||||
@@ -29,7 +28,7 @@ async def get_status(
|
||||
async def create_first_product(
|
||||
data: OnboardingRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
if not data.name.strip():
|
||||
raise HTTPException(status_code=400, detail="Product name is required")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated
|
||||
from pydantic import BaseModel
|
||||
from app.database import get_db
|
||||
from app.services.payment import PaymentService
|
||||
@@ -27,7 +26,7 @@ async def get_plans():
|
||||
@router.get("/subscription")
|
||||
async def get_subscription(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
svc = PaymentService(db)
|
||||
return await svc.get_current_subscription(user_id)
|
||||
@@ -37,7 +36,7 @@ async def get_subscription(
|
||||
async def create_order(
|
||||
data: CreateOrderRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
svc = PaymentService(db)
|
||||
try:
|
||||
@@ -49,7 +48,7 @@ async def create_order(
|
||||
@router.post("/callback")
|
||||
async def payment_callback(
|
||||
data: PaymentCallbackRequest,
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
svc = PaymentService(db)
|
||||
success = await svc.handle_payment_callback(data.payment_id, data.success)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated, Optional
|
||||
from typing import Optional
|
||||
from app.database import get_db
|
||||
from app.services.product import ProductService
|
||||
from app.api.v1.deps import get_current_user_id
|
||||
@@ -44,7 +44,7 @@ async def list_products(
|
||||
page: int = Query(1, ge=1),
|
||||
size: int = Query(20, ge=1, le=100),
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = ProductService(db)
|
||||
return await service.list_products(user_id, category, page, size)
|
||||
@@ -54,7 +54,7 @@ async def list_products(
|
||||
async def get_product(
|
||||
product_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = ProductService(db)
|
||||
product = await service.get_product(user_id, product_id)
|
||||
@@ -67,7 +67,7 @@ async def get_product(
|
||||
async def create_product(
|
||||
data: ProductCreate,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = ProductService(db)
|
||||
product = await service.create_product(user_id, data.dict())
|
||||
@@ -79,7 +79,7 @@ async def update_product(
|
||||
product_id: str,
|
||||
data: ProductUpdate,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = ProductService(db)
|
||||
product = await service.update_product(user_id, product_id, data.dict(exclude_unset=True))
|
||||
@@ -92,7 +92,7 @@ async def update_product(
|
||||
async def delete_product(
|
||||
product_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = ProductService(db)
|
||||
deleted = await service.delete_product(user_id, product_id)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated, Optional
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
from app.database import get_db
|
||||
from app.services.push import PushService
|
||||
@@ -20,7 +20,7 @@ class DeviceRegisterRequest(BaseModel):
|
||||
async def register_device(
|
||||
data: DeviceRegisterRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = PushService(db)
|
||||
device = await service.register_device(
|
||||
@@ -41,7 +41,7 @@ async def register_device(
|
||||
async def unregister_device(
|
||||
data: dict,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
client_id = data.get("client_id")
|
||||
if not client_id:
|
||||
@@ -56,7 +56,7 @@ async def unregister_device(
|
||||
@router.get("/devices")
|
||||
async def list_devices(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = PushService(db)
|
||||
devices = await service.get_user_devices(user_id)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated, Optional
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
from app.database import get_db
|
||||
from app.services.quotation import QuotationService
|
||||
@@ -23,7 +23,7 @@ class InquiryRequest(BaseModel):
|
||||
async def generate_from_inquiry(
|
||||
data: InquiryRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = QuotationService(db)
|
||||
result = await service.generate_from_inquiry(
|
||||
@@ -38,7 +38,7 @@ async def generate_from_inquiry(
|
||||
async def create_quotation(
|
||||
data: dict,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = QuotationService(db)
|
||||
try:
|
||||
@@ -53,7 +53,7 @@ async def list_quotations(
|
||||
page: int = Query(1, ge=1),
|
||||
size: int = Query(20, ge=1, le=100),
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = QuotationService(db)
|
||||
return await service.list_quotations(user_id, page, size)
|
||||
@@ -63,7 +63,7 @@ async def list_quotations(
|
||||
async def get_quotation(
|
||||
quotation_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = QuotationService(db)
|
||||
quotation = await service.get_quotation(user_id, quotation_id)
|
||||
@@ -77,7 +77,7 @@ async def update_quotation_status(
|
||||
quotation_id: str,
|
||||
data: dict,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = QuotationService(db)
|
||||
quotation = await service.update_status(user_id, quotation_id, data.get("status", "draft"))
|
||||
@@ -89,7 +89,7 @@ async def update_quotation_status(
|
||||
@router.get("/export/csv")
|
||||
async def export_quotations(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = QuotationService(db)
|
||||
result = await service.list_quotations(user_id, 1, 9999)
|
||||
@@ -106,7 +106,7 @@ async def export_quotations(
|
||||
async def export_quotation_pdf(
|
||||
quotation_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = QuotationService(db)
|
||||
quotation = await service.get_quotation(user_id, quotation_id)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated
|
||||
from app.database import get_db
|
||||
from app.services.silent_pattern import SilentPatternService
|
||||
from app.api.v1.deps import get_current_user_id
|
||||
@@ -11,7 +10,7 @@ router = APIRouter()
|
||||
@router.get("/risk-analysis")
|
||||
async def get_silent_risk_analysis(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = SilentPatternService(db)
|
||||
risks = await service.analyze_silent_risk(user_id)
|
||||
@@ -27,7 +26,7 @@ async def get_silent_risk_analysis(
|
||||
async def get_followup_suggestions(
|
||||
customer_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = SilentPatternService(db)
|
||||
suggestions = await service.get_suggestions(user_id, customer_id)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated, Optional
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
from app.database import get_db
|
||||
from app.services.team import TeamService
|
||||
@@ -26,7 +26,7 @@ class UpdateRoleRequest(BaseModel):
|
||||
async def create_team(
|
||||
data: CreateTeamRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = TeamService(db)
|
||||
try:
|
||||
@@ -39,7 +39,7 @@ async def create_team(
|
||||
@router.get("")
|
||||
async def list_teams(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = TeamService(db)
|
||||
return {"teams": await service.list_user_teams(user_id)}
|
||||
@@ -49,7 +49,7 @@ async def list_teams(
|
||||
async def get_team(
|
||||
team_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = TeamService(db)
|
||||
team = await service.get_team(team_id, user_id)
|
||||
@@ -63,7 +63,7 @@ async def invite_member(
|
||||
team_id: str,
|
||||
data: InviteRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = TeamService(db)
|
||||
try:
|
||||
@@ -78,7 +78,7 @@ async def remove_member(
|
||||
team_id: str,
|
||||
member_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = TeamService(db)
|
||||
success = await service.remove_member(team_id, user_id, member_id)
|
||||
@@ -91,7 +91,7 @@ async def remove_member(
|
||||
async def leave_team(
|
||||
team_id: str,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = TeamService(db)
|
||||
success = await service.leave_team(team_id, user_id)
|
||||
@@ -106,7 +106,7 @@ async def update_member_role(
|
||||
member_id: str,
|
||||
data: UpdateRoleRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = TeamService(db)
|
||||
if data.role not in ("admin", "member", "viewer"):
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Annotated
|
||||
from app.database import get_db
|
||||
from app.services.corpus_trainer import CorpusTrainer
|
||||
from app.api.v1.deps import get_current_user_id
|
||||
@@ -10,7 +9,7 @@ router = APIRouter()
|
||||
|
||||
@router.post("/corpus/run")
|
||||
async def run_corpus_training(
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
trainer = CorpusTrainer(db)
|
||||
result = await trainer.run_pipeline()
|
||||
@@ -20,7 +19,7 @@ async def run_corpus_training(
|
||||
@router.post("/corpus/embeddings")
|
||||
async def compute_embeddings(
|
||||
batch_size: int = 50,
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
trainer = CorpusTrainer(db)
|
||||
result = await trainer.compute_embeddings(batch_size)
|
||||
@@ -29,7 +28,7 @@ async def compute_embeddings(
|
||||
|
||||
@router.get("/corpus/stats")
|
||||
async def corpus_stats(
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
trainer = CorpusTrainer(db)
|
||||
return await trainer.get_stats()
|
||||
@@ -37,7 +36,7 @@ async def corpus_stats(
|
||||
|
||||
@router.post("/corpus/deduplicate")
|
||||
async def deduplicate_corpus(
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
trainer = CorpusTrainer(db)
|
||||
result = await trainer.deduplicate()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import APIRouter, HTTPException, Response, Depends
|
||||
from typing import Optional, Dict, Any, Annotated
|
||||
from typing import Optional, Dict, Any
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.database import get_db
|
||||
@@ -51,7 +51,7 @@ async def translate_text(
|
||||
async def generate_reply(
|
||||
data: ReplyRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
pref_service = UserPreferenceService(db)
|
||||
pref_context = await pref_service.get_preference_context(user_id, "reply")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from fastapi import APIRouter, Request, HTTPException, Depends, Header
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, and_
|
||||
from typing import Annotated, Optional
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
from app.database import get_db
|
||||
from app.services.whatsapp import WhatsAppService
|
||||
@@ -30,7 +30,7 @@ async def verify_webhook(
|
||||
async def handle_webhook(
|
||||
request: Request,
|
||||
x_hub_signature_256: Optional[str] = Header(None),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
svc = WhatsAppService()
|
||||
body = await request.body()
|
||||
@@ -80,7 +80,7 @@ class SendMessageRequest(BaseModel):
|
||||
async def send_message(
|
||||
data: SendMessageRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: Annotated[AsyncSession, Depends(get_db)] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
svc = WhatsAppService()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user