Files
dealerhub/backend/apps/core/demo.py
T

72 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""演示账套与免注册体验(批次 C2)。
- `POST /api/v1/demo/enter/` 免注册进入演示账套(返回一次性 JWT,租户固定 demo)
- 演示租户默认只读:写操作被 `DemoReadOnlyMiddleware` 拦截(见 middleware.py)
安全:演示账号权限最小(非 staff/superuser),token 短时效;只读拦截按"租户 + 方法"
判定,不依赖前端自觉。
"""
from asgiref.sync import sync_to_async
from adrf.decorators import api_view
from rest_framework import status
from rest_framework.decorators import authentication_classes, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
DEMO_TENANT = "demo"
DEMO_USER = "demo"
@api_view(["POST"])
@authentication_classes([])
@permission_classes([AllowAny])
async def enter_demo(request):
"""免注册进入演示账套:签发一次性 JWT(2 小时)。
若演示租户尚未初始化,返回 503 并提示先跑 `manage.py seed_demo`。
"""
from django.conf import settings
from django.contrib.auth import get_user_model
from rest_framework_simplejwt.tokens import RefreshToken
from apps.core.models import Tenant, TenantMembership
def _prepare():
tenant = Tenant.objects.filter(code=DEMO_TENANT, is_active=True).first()
if tenant is None:
return None, None
user = get_user_model().objects.filter(username=DEMO_USER).first()
if user is None:
return tenant, None
# The token flow bypasses the normal login path, so make sure the
# membership exists even when the demo tenant was built by hand.
TenantMembership.objects.get_or_create(
user=user, tenant=tenant,
defaults={"role": "owner", "is_active": True},
)
return tenant, user
tenant, user = await sync_to_async(_prepare)()
if tenant is None or user is None:
return Response({
"code": "demo_not_ready",
"detail": "演示账套尚未初始化,请管理员执行:python manage.py seed_demo",
}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
def _token():
refresh = RefreshToken.for_user(user)
refresh.set_exp(lifetime=__import__("datetime").timedelta(hours=2))
return str(refresh.access_token)
access = await sync_to_async(_token)()
return Response({
"access": access,
"tenant": tenant.code,
"tenant_name": tenant.name,
"username": user.username,
"read_only": True,
"expires_in": 7200,
"message": "演示账套为只读模式:可以随意试用查询、报表、AI 功能,写入会被拒绝",
})