forked from erp-dev/erp
166 lines
5.8 KiB
Python
166 lines
5.8 KiB
Python
"""
|
||
URL configuration for flower project.
|
||
|
||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||
https://docs.djangoproject.com/en/5.2/topics/http/urls/
|
||
Examples:
|
||
Function views
|
||
1. Add an import: from my_app import views
|
||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||
Class-based views
|
||
1. Add an import: from other_app.views import Home
|
||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||
Including another URLconf
|
||
1. Import the include() function: from django.urls import include, path
|
||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||
"""
|
||
from django.contrib import admin
|
||
from django.contrib.auth import get_user_model
|
||
from django.contrib.auth.models import update_last_login
|
||
from django.core.exceptions import ObjectDoesNotExist
|
||
from django.urls import path, include
|
||
from rest_framework import status
|
||
from rest_framework.response import Response
|
||
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
|
||
from sse.views import create_sse_event, push_test_event, get_sse_status, shutdown_sse
|
||
from rest_framework_simplejwt.settings import api_settings
|
||
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
|
||
from rest_framework_simplejwt.views import (
|
||
TokenObtainPairView,
|
||
TokenRefreshView,
|
||
)
|
||
|
||
from basic_info.models import EmployeeStatusEnum
|
||
from flower.app_version import AppVersionView
|
||
from flower.error_code import AuthErrorCode
|
||
from flower.error_code_views import ErrorCodeListView
|
||
|
||
# 自定义后台站点标题(simpleui 也会读取)
|
||
admin.site.site_header = "宇问科技"
|
||
admin.site.site_title = "宇问科技"
|
||
admin.site.index_title = "管理后台"
|
||
|
||
def login_error_response(error_code: AuthErrorCode, code: str, message: str, http_status: int):
|
||
return Response(
|
||
{
|
||
'error_code': int(error_code),
|
||
'code': code,
|
||
'message': message,
|
||
'detail': message,
|
||
},
|
||
status=http_status,
|
||
)
|
||
|
||
|
||
class CustomTokenObtainPairView(TokenObtainPairView):
|
||
"""自定义登录视图"""
|
||
|
||
def post(self, request, *args, **kwargs):
|
||
data = request.data or {}
|
||
username = (data.get('username') or '').strip()
|
||
password = data.get('password')
|
||
|
||
if not username and not password:
|
||
return login_error_response(
|
||
AuthErrorCode.MISSING_CREDENTIALS,
|
||
'missing_credentials',
|
||
'请输入用户名和密码',
|
||
status.HTTP_400_BAD_REQUEST,
|
||
)
|
||
if not username:
|
||
return login_error_response(
|
||
AuthErrorCode.MISSING_USERNAME,
|
||
'missing_username',
|
||
'请输入用户名',
|
||
status.HTTP_400_BAD_REQUEST,
|
||
)
|
||
if not password:
|
||
return login_error_response(
|
||
AuthErrorCode.MISSING_PASSWORD,
|
||
'missing_password',
|
||
'请输入密码',
|
||
status.HTTP_400_BAD_REQUEST,
|
||
)
|
||
|
||
UserModel = get_user_model()
|
||
try:
|
||
user = UserModel._default_manager.get_by_natural_key(username)
|
||
except UserModel.DoesNotExist:
|
||
return login_error_response(
|
||
AuthErrorCode.USER_NOT_FOUND,
|
||
'user_not_found',
|
||
'用户不存在',
|
||
status.HTTP_401_UNAUTHORIZED,
|
||
)
|
||
|
||
if not user.check_password(password):
|
||
return login_error_response(
|
||
AuthErrorCode.INVALID_PASSWORD,
|
||
'invalid_password',
|
||
'密码错误',
|
||
status.HTTP_401_UNAUTHORIZED,
|
||
)
|
||
|
||
if not user.is_active:
|
||
return login_error_response(
|
||
AuthErrorCode.USER_INACTIVE,
|
||
'user_inactive',
|
||
'该用户已被禁用',
|
||
status.HTTP_403_FORBIDDEN,
|
||
)
|
||
|
||
try:
|
||
employee = user.employee
|
||
except ObjectDoesNotExist:
|
||
return login_error_response(
|
||
AuthErrorCode.EMPLOYEE_NOT_BOUND,
|
||
'employee_not_bound',
|
||
'该用户未绑定员工身份',
|
||
status.HTTP_403_FORBIDDEN,
|
||
)
|
||
|
||
if employee.status != EmployeeStatusEnum.ACTIVE:
|
||
return login_error_response(
|
||
AuthErrorCode.EMPLOYEE_INACTIVE,
|
||
'employee_inactive',
|
||
'该员工已离职或停用',
|
||
status.HTTP_403_FORBIDDEN,
|
||
)
|
||
|
||
refresh = TokenObtainPairSerializer.get_token(user)
|
||
if api_settings.UPDATE_LAST_LOGIN:
|
||
update_last_login(None, user)
|
||
|
||
return Response(
|
||
{
|
||
'refresh': str(refresh),
|
||
'access': str(refresh.access_token),
|
||
},
|
||
status=status.HTTP_200_OK,
|
||
)
|
||
|
||
|
||
urlpatterns = [
|
||
# JWT 登录
|
||
path('api/auth/login/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
|
||
path('api/auth/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
|
||
path('api/error-codes/', ErrorCodeListView.as_view(), name='error_code_list'),
|
||
path('api/app-version/', AppVersionView.as_view(), name='app_version'),
|
||
|
||
# API 文档
|
||
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
|
||
path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
|
||
|
||
path('admin/', admin.site.urls),
|
||
path('api/v1/', include('api_v1.urls')),
|
||
path('api/v2/', include('api_v2.urls')),
|
||
path('api/core/', include('api_core.urls')),
|
||
path('api/backend/', include('api_man.urls')),
|
||
|
||
# sse 相关端点
|
||
path('sse/', create_sse_event, name='sse_event'),
|
||
path('sse/push/', push_test_event, name='push_sse_event'),
|
||
path('sse/status/', get_sse_status, name='sse_status'),
|
||
path('sse/shutdown/', shutdown_sse, name='shutdown_sse'),
|
||
]
|