mirror of
https://github.com/jumpserver/jumpserver.git
synced 2026-03-18 19:12:07 +00:00
perf: support generate webui_schema.json such as form, serializer
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
from django.core.cache import cache
|
||||
from django.utils.translation import gettext as _
|
||||
from rest_framework.serializers import Serializer
|
||||
from rest_framework.exceptions import NotFound
|
||||
from rest_framework.generics import CreateAPIView, RetrieveAPIView
|
||||
from rest_framework.permissions import AllowAny
|
||||
@@ -104,6 +105,7 @@ class FaceCallbackApi(AuthMixin, CreateAPIView):
|
||||
class FaceContextApi(AuthMixin, RetrieveAPIView, CreateAPIView):
|
||||
permission_classes = (AllowAny,)
|
||||
face_token_session_key = FACE_SESSION_KEY
|
||||
serializer_class = Serializer
|
||||
|
||||
@staticmethod
|
||||
def get_face_cache_key(token):
|
||||
|
||||
@@ -6,6 +6,7 @@ from django.contrib.auth import logout
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from rest_framework import generics
|
||||
from rest_framework import status
|
||||
from rest_framework.serializers import Serializer
|
||||
from rest_framework.response import Response
|
||||
|
||||
from common.sessions.cache import user_session_manager
|
||||
@@ -52,6 +53,7 @@ class UserSessionManager:
|
||||
|
||||
class UserSessionApi(generics.RetrieveDestroyAPIView):
|
||||
permission_classes = ()
|
||||
serializer_class = Serializer
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
if isinstance(request.user, AnonymousUser):
|
||||
|
||||
@@ -81,3 +81,13 @@ def get_user_login_form_cls(*, captcha=False):
|
||||
bases.append(CaptchaMixin)
|
||||
bases.append(UserLoginForm)
|
||||
return type('UserLoginForm', tuple(bases), {})
|
||||
|
||||
|
||||
def get_comprehensive_user_login_form_cls():
|
||||
bases = [
|
||||
ChallengeMixin,
|
||||
UserCheckOtpCodeForm,
|
||||
CaptchaMixin,
|
||||
UserLoginForm
|
||||
]
|
||||
return type('UserLoginForm', tuple(bases), {})
|
||||
|
||||
@@ -30,7 +30,7 @@ from users.utils import (
|
||||
)
|
||||
from .. import mixins, errors
|
||||
from ..const import RSA_PRIVATE_KEY, RSA_PUBLIC_KEY, USER_LOGIN_GUARD_VIEW_REDIRECT_FIELD
|
||||
from ..forms import get_user_login_form_cls
|
||||
from ..forms import get_user_login_form_cls, get_comprehensive_user_login_form_cls
|
||||
from ..utils import get_auth_methods
|
||||
|
||||
__all__ = [
|
||||
@@ -253,6 +253,9 @@ class UserLoginView(mixins.AuthMixin, UserLoginContextMixin, FormView):
|
||||
return get_user_login_form_cls(captcha=True)
|
||||
else:
|
||||
return get_user_login_form_cls()
|
||||
|
||||
def get_comprehensive_form_class(self):
|
||||
return get_comprehensive_user_login_form_cls()
|
||||
|
||||
def clear_rsa_key(self):
|
||||
self.request.session[RSA_PRIVATE_KEY] = None
|
||||
|
||||
@@ -3,6 +3,7 @@ import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils._os import safe_join
|
||||
from rest_framework.serializers import Serializer
|
||||
from rest_framework.generics import RetrieveAPIView
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.response import Response
|
||||
@@ -14,6 +15,7 @@ class ComponentI18nApi(RetrieveAPIView):
|
||||
base_path = 'locale'
|
||||
permission_classes = [AllowAny]
|
||||
lang_data = {}
|
||||
serializer_class = Serializer
|
||||
|
||||
def get_component_translations(self, name):
|
||||
if not settings.DEBUG and name in self.lang_data:
|
||||
|
||||
537
utils/view_schema/generate.py
Normal file
537
utils/view_schema/generate.py
Normal file
@@ -0,0 +1,537 @@
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from turtle import up
|
||||
|
||||
import django
|
||||
from django.urls import get_resolver
|
||||
from django.urls.resolvers import URLPattern, URLResolver
|
||||
|
||||
# 获取项目根目录(jumpserver 目录)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
APP_DIR = os.path.join(BASE_DIR, 'apps')
|
||||
|
||||
# 不改变工作目录,直接加入 sys.path
|
||||
sys.path.insert(0, APP_DIR)
|
||||
sys.path.insert(0, BASE_DIR)
|
||||
|
||||
# 设置 Django 环境
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jumpserver.settings")
|
||||
django.setup()
|
||||
|
||||
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
OUTPUT_FILE_DIR = os.path.join(CURRENT_DIR, 'output')
|
||||
os.makedirs(OUTPUT_FILE_DIR, exist_ok=True)
|
||||
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.permissions import OperandHolder, AND, OR, NOT
|
||||
from django.views.generic.base import View
|
||||
from django.views.generic.edit import FormView
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.handlers.asgi import ASGIRequest
|
||||
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
from rbac.permissions import RBACPermission
|
||||
from common.utils import lazyproperty
|
||||
# 尝试使用模拟请求
|
||||
scope = {
|
||||
'type': 'http',
|
||||
'method': 'GET',
|
||||
'path': '/',
|
||||
'query_string': b'',
|
||||
'headers': [],
|
||||
}
|
||||
|
||||
async def receive():
|
||||
return {'type': 'http.request', 'body': b''}
|
||||
|
||||
request = ASGIRequest(scope, receive)
|
||||
|
||||
|
||||
def log(message):
|
||||
print(message)
|
||||
|
||||
|
||||
class FieldsGenerator:
|
||||
|
||||
def write_fields_schema(self):
|
||||
return {}
|
||||
|
||||
def required_fields(self):
|
||||
return []
|
||||
|
||||
|
||||
class FormFieldsGenerator(FieldsGenerator):
|
||||
def __init__(self, raw_class, view):
|
||||
self.raw_class = raw_class
|
||||
self.view = view
|
||||
|
||||
|
||||
@lazyproperty
|
||||
def fields(self):
|
||||
return self.raw_class().fields
|
||||
|
||||
def write_fields_schema(self):
|
||||
schema = self.get_fields_schema(self.fields)
|
||||
return schema
|
||||
|
||||
def required_fields(self):
|
||||
fields = [name for name, field in self.fields.items() if field.required]
|
||||
return fields
|
||||
|
||||
def get_fields_schema(self, fields):
|
||||
schemas = {}
|
||||
for name, field in fields.items():
|
||||
schema = {
|
||||
'type': self.get_field_type(field),
|
||||
}
|
||||
description = getattr(field, 'help_text', '')
|
||||
if description:
|
||||
schema['description'] = str(description)
|
||||
schemas[name] = schema
|
||||
return schemas
|
||||
|
||||
def get_field_type(self, field):
|
||||
"""将 Django Form Field 类型映射到 JSON Schema 类型"""
|
||||
from django import forms
|
||||
|
||||
type_mapping = {
|
||||
forms.CharField: 'string',
|
||||
forms.EmailField: 'string',
|
||||
forms.URLField: 'string',
|
||||
forms.SlugField: 'string',
|
||||
forms.UUIDField: 'string',
|
||||
forms.RegexField: 'string',
|
||||
forms.FileField: 'string',
|
||||
forms.ImageField: 'string',
|
||||
forms.FilePathField: 'string',
|
||||
forms.GenericIPAddressField: 'string',
|
||||
forms.IntegerField: 'integer',
|
||||
forms.FloatField: 'number',
|
||||
forms.DecimalField: 'number',
|
||||
forms.BooleanField: 'boolean',
|
||||
forms.NullBooleanField: 'boolean',
|
||||
forms.DateField: 'string',
|
||||
forms.TimeField: 'string',
|
||||
forms.DateTimeField: 'string',
|
||||
forms.DurationField: 'string',
|
||||
forms.MultipleChoiceField: 'array',
|
||||
forms.TypedMultipleChoiceField: 'array',
|
||||
forms.ModelMultipleChoiceField: 'array',
|
||||
forms.ChoiceField: 'string',
|
||||
forms.TypedChoiceField: 'string',
|
||||
forms.ModelChoiceField: 'string',
|
||||
forms.JSONField: 'object',
|
||||
}
|
||||
|
||||
for field_type, json_type in type_mapping.items():
|
||||
if isinstance(field, field_type):
|
||||
return json_type
|
||||
|
||||
return 'string'
|
||||
|
||||
|
||||
class SerializerFieldsGenerator(FieldsGenerator):
|
||||
|
||||
def __init__(self, raw_class, view):
|
||||
self.raw_class = raw_class
|
||||
self.view = view
|
||||
|
||||
@lazyproperty
|
||||
def fields(self):
|
||||
fields = {}
|
||||
try:
|
||||
fields = self.raw_class().fields
|
||||
except Exception as e:
|
||||
if hasattr(self.raw_class, '_declared_fields'):
|
||||
fields = self.raw_class._declared_fields
|
||||
return fields
|
||||
|
||||
@lazyproperty
|
||||
def write_fields(self):
|
||||
fields = {}
|
||||
for name, field in self.fields.items():
|
||||
if field.read_only:
|
||||
continue
|
||||
fields[name] = field
|
||||
return fields
|
||||
|
||||
def get_fields_schema(self, fields):
|
||||
schemas = {}
|
||||
if not hasattr(fields, 'items'):
|
||||
return {}
|
||||
for name, field in fields.items():
|
||||
schema = self.get_field_schema(field)
|
||||
if hasattr(field, 'child'):
|
||||
_fields = field.child
|
||||
_fields_schema = self.get_fields_schema(_fields)
|
||||
schema['properties'] = _fields_schema
|
||||
schemas[name] = schema
|
||||
return schemas
|
||||
|
||||
def get_field_schema(self, field):
|
||||
schema = {
|
||||
'type': self.get_field_type(field),
|
||||
}
|
||||
if description := getattr(field, 'help_text', ''):
|
||||
schema['description'] = str(description)
|
||||
extra_schema = self.get_field_extra_schema(field)
|
||||
schema.update(extra_schema)
|
||||
return schema
|
||||
|
||||
def field_is_nested(self, field):
|
||||
from rest_framework import serializers
|
||||
nested_field_types = (
|
||||
serializers.Serializer,
|
||||
serializers.ModelSerializer,
|
||||
)
|
||||
return isinstance(field, nested_field_types)
|
||||
|
||||
def get_field_extra_schema(self, field):
|
||||
"""获取字段的正则表达式模式"""
|
||||
patterns = {}
|
||||
# 检查 validators 中是否有正则验证器
|
||||
if hasattr(field, 'validators'):
|
||||
for validator in field.validators:
|
||||
if hasattr(validator, 'regex'):
|
||||
patterns['pattern'] = str(validator.regex.pattern)
|
||||
break
|
||||
# 针对特定字段类型添加模式
|
||||
from rest_framework import serializers
|
||||
if isinstance(field, serializers.EmailField):
|
||||
patterns['format'] = 'email'
|
||||
elif isinstance(field, serializers.URLField):
|
||||
patterns['format'] = 'uri'
|
||||
elif isinstance(field, (serializers.DateTimeField, serializers.DateField, serializers.TimeField)):
|
||||
patterns['format'] = 'date-time'
|
||||
|
||||
# 添加长度限制
|
||||
if hasattr(field, 'max_length') and field.max_length:
|
||||
patterns['maxLength'] = field.max_length
|
||||
if hasattr(field, 'min_length') and field.min_length:
|
||||
patterns['minLength'] = field.min_length
|
||||
|
||||
# 添加数值范围
|
||||
if hasattr(field, 'max_value') and field.max_value is not None:
|
||||
patterns['maximum'] = field.max_value
|
||||
if hasattr(field, 'min_value') and field.min_value is not None:
|
||||
patterns['minimum'] = field.min_value
|
||||
|
||||
if choices := self.get_field_choices(field):
|
||||
patterns['enum'] = choices
|
||||
return patterns
|
||||
|
||||
def get_field_choices(self, field):
|
||||
from rest_framework import serializers
|
||||
choices = []
|
||||
field_need_query_db = isinstance(field, (
|
||||
serializers.PrimaryKeyRelatedField, # 会查询数据库
|
||||
serializers.StringRelatedField, # 会查询数据库
|
||||
serializers.SlugRelatedField, # 会查询数据库
|
||||
serializers.HyperlinkedRelatedField, # 会查询数据库
|
||||
serializers.HyperlinkedIdentityField,# 会查询数据库
|
||||
serializers.RelatedField, # 基类,会查询数据库
|
||||
serializers.ManyRelatedField, # 会查询数据库
|
||||
serializers.ListSerializer # 可能会查询数据库
|
||||
))
|
||||
if field_need_query_db:
|
||||
return choices # 不返回需要查询数据库的字段选项
|
||||
|
||||
choices = getattr(field, 'choices', [])
|
||||
if not choices:
|
||||
return choices
|
||||
|
||||
if isinstance(choices, dict):
|
||||
# choices 可能是字典、列表或元组
|
||||
choices = list(choices.keys())
|
||||
elif isinstance(choices, (list, tuple)):
|
||||
# choices 可能是 [(value, label), ...] 或 [value, ...]
|
||||
for choice in choices:
|
||||
if isinstance(choice, (list, tuple)) and len(choice) == 2:
|
||||
choices.append(choice[0])
|
||||
else:
|
||||
choices.append(choice)
|
||||
return choices
|
||||
|
||||
def get_field_type(self, field):
|
||||
"""将 Python 字段类型映射到 JSON Schema 类型"""
|
||||
from rest_framework import serializers
|
||||
|
||||
if self.field_is_nested(field):
|
||||
return 'object'
|
||||
|
||||
type_mapping = {
|
||||
serializers.CharField: 'string',
|
||||
serializers.EmailField: 'string',
|
||||
serializers.URLField: 'string',
|
||||
serializers.UUIDField: 'string',
|
||||
serializers.SlugField: 'string',
|
||||
serializers.ChoiceField: 'string',
|
||||
serializers.IntegerField: 'integer',
|
||||
serializers.FloatField: 'number',
|
||||
serializers.DecimalField: 'number',
|
||||
serializers.BooleanField: 'boolean',
|
||||
serializers.DateTimeField: 'string',
|
||||
serializers.DateField: 'string',
|
||||
serializers.TimeField: 'string',
|
||||
serializers.ListField: 'array',
|
||||
serializers.DictField: 'object',
|
||||
serializers.JSONField: 'object',
|
||||
}
|
||||
|
||||
for field_type, json_type in type_mapping.items():
|
||||
if isinstance(field, field_type):
|
||||
return json_type
|
||||
|
||||
return 'string' # 默认类型
|
||||
|
||||
def write_fields_schema(self):
|
||||
fields = self.write_fields
|
||||
if not fields:
|
||||
return {}
|
||||
schema = self.get_fields_schema(fields)
|
||||
return schema
|
||||
|
||||
def required_fields(self):
|
||||
required = []
|
||||
for name, field in self.write_fields.items():
|
||||
if field.required:
|
||||
required.append(name)
|
||||
return required
|
||||
|
||||
|
||||
class CustomView:
|
||||
|
||||
def __init__(self, view_func):
|
||||
self.view_func = view_func
|
||||
|
||||
@property
|
||||
def view_class(self):
|
||||
cls = getattr(self.view_func, 'view_class', None)
|
||||
if not cls:
|
||||
cls = getattr(self.view_func, 'cls', None)
|
||||
return cls
|
||||
|
||||
@property
|
||||
def view_path(self):
|
||||
if self.view_class:
|
||||
v = self.view_class
|
||||
else:
|
||||
v = self.view_func
|
||||
return f'{v.__module__}.{v.__name__}'
|
||||
|
||||
@property
|
||||
def view_type(self):
|
||||
if self.view_class:
|
||||
return 'class'
|
||||
else:
|
||||
return 'function'
|
||||
|
||||
@lazyproperty
|
||||
def fields_generator(self):
|
||||
generator = None
|
||||
if self.view_class:
|
||||
if issubclass(self.view_class, FormView):
|
||||
generator = self.get_form_fields_generator()
|
||||
if issubclass(self.view_class, APIView):
|
||||
generator= self.get_serializer_fields_generator()
|
||||
if not generator:
|
||||
generator = FieldsGenerator()
|
||||
return generator
|
||||
|
||||
@property
|
||||
def write_fields_schema(self):
|
||||
return self.fields_generator.write_fields_schema()
|
||||
|
||||
@property
|
||||
def required_fields(self):
|
||||
return self.fields_generator.required_fields()
|
||||
|
||||
def get_form_fields_generator(self):
|
||||
if hasattr(self.view_class, 'get_comprehensive_form_class'):
|
||||
view_instance = self.view_class(request=request)
|
||||
form_class = view_instance.get_comprehensive_form_class()
|
||||
else:
|
||||
form_class = getattr(self.view_class, 'form_class', None)
|
||||
if not form_class:
|
||||
if hasattr(self.view_class, 'get_form_class'):
|
||||
# TODO: 实例化 view 类需要传入 request 参数
|
||||
view_instance = self.view_class(request=request)
|
||||
form_class = view_instance.get_form_class()
|
||||
if form_class:
|
||||
return FormFieldsGenerator(raw_class=form_class, view=self)
|
||||
|
||||
def get_serializer_fields_generator(self):
|
||||
serializer_class = getattr(self.view_class, 'serializer_class', None)
|
||||
if not serializer_class:
|
||||
if hasattr(self.view_class, 'get_serializer_class'):
|
||||
# TODO: 实例化 view 类需要传入 request 参数
|
||||
view_instance = self.view_class(request=request)
|
||||
serializer_class = view_instance.get_serializer_class()
|
||||
if serializer_class:
|
||||
return SerializerFieldsGenerator(raw_class=serializer_class, view=self)
|
||||
|
||||
@property
|
||||
def query_fields_schema(self):
|
||||
return {}
|
||||
|
||||
@lazyproperty
|
||||
def requires_auth(self):
|
||||
if self.view_class:
|
||||
return self.check_view_class_requires_auth()
|
||||
else:
|
||||
return self.check_view_func_requires_auth()
|
||||
|
||||
def check_view_class_requires_auth(self):
|
||||
if issubclass(self.view_class, LoginRequiredMixin):
|
||||
return True
|
||||
|
||||
permission_classes = getattr(self.view_class, 'permission_classes', [])
|
||||
if not permission_classes:
|
||||
return False
|
||||
|
||||
return self.check_permission_classes_requires_auth(permission_classes)
|
||||
|
||||
def check_permission_classes_requires_auth(self, permission_classes, operator=AND):
|
||||
if operator == AND:
|
||||
for pc in permission_classes:
|
||||
if self.check_permission_class_requires_auth(pc):
|
||||
return True
|
||||
return False
|
||||
elif operator == OR:
|
||||
for pc in permission_classes:
|
||||
if not self.check_permission_class_requires_auth(pc):
|
||||
return False
|
||||
return True
|
||||
elif operator == NOT:
|
||||
raise ValueError('NOT operator is not supported in permission_classes')
|
||||
else:
|
||||
return False
|
||||
|
||||
def check_permission_class_requires_auth(self, permission_class):
|
||||
if isinstance(permission_class, OperandHolder):
|
||||
operator = permission_class.operator_class
|
||||
op1_class = permission_class.op1_class
|
||||
op2_class = permission_class.op2_class
|
||||
permission_classes = [op1_class, op2_class]
|
||||
return self.check_permission_classes_requires_auth(permission_classes, operator)
|
||||
else:
|
||||
if issubclass(permission_class, (IsAuthenticated, RBACPermission)):
|
||||
return True
|
||||
if issubclass(permission_class, (AllowAny, )):
|
||||
return False
|
||||
if hasattr(permission_class, '__name__'):
|
||||
if 'Authenticated' in permission_class.__name__:
|
||||
return True
|
||||
if permission_class.__name__.startswith('UserConfirmation'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_view_func_requires_auth(self):
|
||||
if hasattr(self.view_func, '__wrapped__'):
|
||||
if hasattr(self.view_func, '__name__'):
|
||||
if 'login_required' in str(self.view_func):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class CustomURLPattern:
|
||||
def __init__(self, raw, prefix='/'):
|
||||
self.raw = raw
|
||||
self.prefix = prefix
|
||||
self.full_path = f'{self.prefix}{self.raw.pattern}'
|
||||
self.view = CustomView(view_func=self.raw.callback)
|
||||
|
||||
def __str__(self):
|
||||
s = f'{self.full_path} -> {self.view.view_path}'
|
||||
return s
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
|
||||
class ViewSchemaGenerator:
|
||||
|
||||
def __init__(self):
|
||||
self.resolver = get_resolver()
|
||||
self.url_patterns = self.get_url_patterns()
|
||||
|
||||
def get_url_patterns(self):
|
||||
return self._extract_url_patterns(self.resolver.url_patterns)
|
||||
|
||||
def _extract_url_patterns(self, url_patterns, prefix='/'):
|
||||
url_pattern_objects = []
|
||||
for pattern in url_patterns:
|
||||
if isinstance(pattern, URLResolver):
|
||||
resolver = pattern
|
||||
_prefix = f'{prefix}{resolver.pattern}'
|
||||
patterns = self._extract_url_patterns(resolver.url_patterns, prefix=_prefix)
|
||||
url_pattern_objects.extend(patterns)
|
||||
continue
|
||||
elif isinstance(pattern, URLPattern):
|
||||
p = CustomURLPattern(raw=pattern, prefix=prefix)
|
||||
url_pattern_objects.append(p)
|
||||
else:
|
||||
log(f'Unknown pattern type: {type(pattern)}')
|
||||
return url_pattern_objects
|
||||
|
||||
def generate(self):
|
||||
self.write_url_patterns()
|
||||
self.write_webui_schema()
|
||||
|
||||
def write_webui_schema(self):
|
||||
data = {
|
||||
'GET': {},
|
||||
'POST': {}
|
||||
}
|
||||
post_schema = {}
|
||||
for pattern in self.url_patterns:
|
||||
if pattern.view.requires_auth:
|
||||
continue
|
||||
url = pattern.full_path
|
||||
item = {
|
||||
'allowIf': 'prelogin',
|
||||
'query': {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": [],
|
||||
"additionalProperties": False
|
||||
|
||||
},
|
||||
'body': {
|
||||
'type': 'object',
|
||||
'properties': pattern.view.write_fields_schema,
|
||||
'required': pattern.view.required_fields,
|
||||
'additionalProperties': False
|
||||
}
|
||||
}
|
||||
post_schema[url] = item
|
||||
data['POST'] = post_schema
|
||||
self.write_to_file(data, 'webui_schema.json')
|
||||
|
||||
def write_url_patterns(self):
|
||||
data = []
|
||||
for pattern in self.url_patterns:
|
||||
if pattern.view.requires_auth:
|
||||
continue
|
||||
if pattern.view.view_type == 'function':
|
||||
continue
|
||||
item = {
|
||||
'url': pattern.full_path,
|
||||
'view_path': pattern.view.view_path,
|
||||
'view_requires_auth': pattern.view.requires_auth,
|
||||
'view_type': pattern.view.view_type,
|
||||
}
|
||||
view_write_fields_schema = pattern.view.write_fields_schema
|
||||
if view_write_fields_schema:
|
||||
item['view_write_fields_schema'] = view_write_fields_schema
|
||||
data.append(item)
|
||||
self.write_to_file(data, 'all_url_patterns.json')
|
||||
|
||||
def write_to_file(self, data, filename):
|
||||
file_path = os.path.join(OUTPUT_FILE_DIR, filename)
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=4, ensure_ascii=False)
|
||||
|
||||
if __name__ == '__main__':
|
||||
ViewSchemaGenerator().generate()
|
||||
799
utils/view_schema/output/all_url_patterns.json
Normal file
799
utils/view_schema/output/all_url_patterns.json
Normal file
@@ -0,0 +1,799 @@
|
||||
[
|
||||
{
|
||||
"url": "/api/v1/users/^service-account-registrations/$",
|
||||
"view_path": "users.api.service.ServiceAccountRegistrationViewSet",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
"comment": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/users/^service-account-registrations\\.(?P<format>[a-z0-9]+)/?$",
|
||||
"view_path": "users.api.service.ServiceAccountRegistrationViewSet",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
"comment": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/users/^service-account-registrations/(?P<pk>[^/.]+)/$",
|
||||
"view_path": "users.api.service.ServiceAccountRegistrationViewSet",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
"comment": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/users/^service-account-registrations/(?P<pk>[^/.]+)\\.(?P<format>[a-z0-9]+)/?$",
|
||||
"view_path": "users.api.service.ServiceAccountRegistrationViewSet",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
"comment": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/terminal/terminal-registrations/",
|
||||
"view_path": "terminal.api.component.terminal.TerminalRegistrationApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"maxLength": 1024
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"koko",
|
||||
"guacamole",
|
||||
"omnidb",
|
||||
"xrdp",
|
||||
"lion",
|
||||
"core",
|
||||
"celery",
|
||||
"magnus",
|
||||
"razor",
|
||||
"tinker",
|
||||
"video_worker",
|
||||
"chen",
|
||||
"kael",
|
||||
"panda",
|
||||
"nec",
|
||||
"facelive"
|
||||
]
|
||||
},
|
||||
"comment": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/terminal/registration/",
|
||||
"view_path": "terminal.api.component.terminal.TerminalRegistrationApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"maxLength": 1024
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"koko",
|
||||
"guacamole",
|
||||
"omnidb",
|
||||
"xrdp",
|
||||
"lion",
|
||||
"core",
|
||||
"celery",
|
||||
"magnus",
|
||||
"razor",
|
||||
"tinker",
|
||||
"video_worker",
|
||||
"chen",
|
||||
"kael",
|
||||
"panda",
|
||||
"nec",
|
||||
"facelive"
|
||||
]
|
||||
},
|
||||
"comment": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/settings/logo/",
|
||||
"view_path": "settings.api.settings.SettingsLogoApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/settings/public/open/",
|
||||
"view_path": "settings.api.public.OpenPublicSettingApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"XPACK_ENABLED": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"INTERFACE": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"LANGUAGES": {
|
||||
"type": "array",
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/settings/i18n/<str:name>/",
|
||||
"view_path": "settings.api.i18n.ComponentI18nApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/settings/client/versions/",
|
||||
"view_path": "settings.api.settings.ClientVersionView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/authentication/face/context/",
|
||||
"view_path": "authentication.api.face.FaceContextApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/authentication/auth/",
|
||||
"view_path": "authentication.api.token.TokenCreateApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"public_key": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/authentication/tokens/",
|
||||
"view_path": "authentication.api.token.TokenCreateApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"public_key": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/authentication/mfa/verify/",
|
||||
"view_path": "authentication.api.mfa.MFAChallengeVerifyApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"code": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/authentication/mfa/challenge/",
|
||||
"view_path": "authentication.api.mfa.MFAChallengeVerifyApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"code": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/authentication/mfa/select/",
|
||||
"view_path": "authentication.api.mfa.MFASendCodeApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/authentication/mfa/send-code/",
|
||||
"view_path": "authentication.api.mfa.MFASendCodeApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/authentication/password/reset-code/",
|
||||
"view_path": "authentication.api.password.UserResetPasswordSendCodeApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"form_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"sms",
|
||||
"email"
|
||||
]
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"sms": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/authentication/login-confirm-ticket/status/",
|
||||
"view_path": "authentication.api.login_confirm.TicketStatusApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/authentication/user-session/",
|
||||
"view_path": "authentication.api.session.UserSessionApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/prometheus/metrics/",
|
||||
"view_path": "jumpserver.api.health.PrometheusMetricsApi",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/api/health/",
|
||||
"view_path": "jumpserver.api.health.HealthCheckView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/api/v1/health/",
|
||||
"view_path": "jumpserver.api.health.HealthCheckView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/login/",
|
||||
"view_path": "authentication.views.login.UserLoginView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"auto_login": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"captcha": {
|
||||
"type": "string"
|
||||
},
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"mfa_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"challenge": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/login/mfa/",
|
||||
"view_path": "authentication.views.mfa.UserLoginMFAView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"mfa_type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/login/wait-confirm/",
|
||||
"view_path": "authentication.views.login.UserLoginWaitConfirmView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/login/mfa/face/capture/",
|
||||
"view_path": "authentication.views.mfa.UserLoginMFAFaceView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/login/guard/",
|
||||
"view_path": "authentication.views.login.UserLoginGuardView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/logout/",
|
||||
"view_path": "authentication.views.login.UserLogoutView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/password/forget/previewing/",
|
||||
"view_path": "users.views.profile.reset.UserForgotPasswordPreviewingView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"captcha": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/password/forgot/",
|
||||
"view_path": "users.views.profile.reset.UserForgotPasswordView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"country_code": {
|
||||
"type": "string"
|
||||
},
|
||||
"sms": {
|
||||
"type": "string",
|
||||
"description": "The phone number must contain an area code, for example, +86"
|
||||
},
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"form_type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/password/reset/",
|
||||
"view_path": "users.views.profile.reset.UserResetPasswordView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"new_password": {
|
||||
"type": "string"
|
||||
},
|
||||
"confirm_password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/password/verify/",
|
||||
"view_path": "users.views.profile.password.UserVerifyPasswordView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/wecom/bind/start/",
|
||||
"view_path": "authentication.views.wecom.WeComEnableStartView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/wecom/qr/login/",
|
||||
"view_path": "authentication.views.wecom.WeComQRLoginView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/wecom/qr/login/callback/",
|
||||
"view_path": "authentication.views.wecom.WeComQRLoginCallbackView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/wecom/oauth/login/",
|
||||
"view_path": "authentication.views.wecom.WeComOAuthLoginView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/wecom/oauth/login/callback/",
|
||||
"view_path": "authentication.views.wecom.WeComOAuthLoginCallbackView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/dingtalk/bind/start/",
|
||||
"view_path": "authentication.views.dingtalk.DingTalkEnableStartView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/dingtalk/qr/login/",
|
||||
"view_path": "authentication.views.dingtalk.DingTalkQRLoginView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/dingtalk/qr/login/callback/",
|
||||
"view_path": "authentication.views.dingtalk.DingTalkQRLoginCallbackView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/dingtalk/oauth/login/",
|
||||
"view_path": "authentication.views.dingtalk.DingTalkOAuthLoginView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/dingtalk/oauth/login/callback/",
|
||||
"view_path": "authentication.views.dingtalk.DingTalkOAuthLoginCallbackView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/feishu/bind/start/",
|
||||
"view_path": "authentication.views.feishu.FeiShuEnableStartView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/feishu/qr/login/",
|
||||
"view_path": "authentication.views.feishu.FeiShuQRLoginView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/feishu/qr/login/callback/",
|
||||
"view_path": "authentication.views.feishu.FeiShuQRLoginCallbackView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/lark/bind/start/",
|
||||
"view_path": "authentication.views.lark.LarkEnableStartView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/lark/qr/login/",
|
||||
"view_path": "authentication.views.lark.LarkQRLoginView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/lark/qr/login/callback/",
|
||||
"view_path": "authentication.views.lark.LarkQRLoginCallbackView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/slack/bind/start/",
|
||||
"view_path": "authentication.views.slack.SlackEnableStartView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/slack/qr/login/",
|
||||
"view_path": "authentication.views.slack.SlackQRLoginView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/slack/qr/login/callback/",
|
||||
"view_path": "authentication.views.slack.SlackQRLoginCallbackView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/profile/otp/enable/start/",
|
||||
"view_path": "users.views.profile.otp.UserOtpEnableStartView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/profile/otp/enable/install-app/",
|
||||
"view_path": "users.views.profile.otp.UserOtpEnableInstallAppView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/profile/otp/enable/bind/",
|
||||
"view_path": "users.views.profile.otp.UserOtpEnableBindView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"otp_code": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/profile/face/enable/",
|
||||
"view_path": "users.views.profile.face.UserFaceEnableView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/profile/face/disable/",
|
||||
"view_path": "users.views.profile.face.UserFaceDisableView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class",
|
||||
"view_write_fields_schema": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/cas/login/",
|
||||
"view_path": "authentication.backends.cas.views.CASLoginView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/cas/logout/",
|
||||
"view_path": "django_cas_ng.views.LogoutView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/cas/callback/",
|
||||
"view_path": "django_cas_ng.views.CallbackView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/openid/login/",
|
||||
"view_path": "authentication.backends.oidc.views.OIDCAuthRequestView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/openid/callback/",
|
||||
"view_path": "authentication.backends.oidc.views.OIDCAuthCallbackView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/openid/logout/",
|
||||
"view_path": "authentication.backends.oidc.views.OIDCEndSessionView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/saml2/login/",
|
||||
"view_path": "authentication.backends.saml2.views.Saml2AuthRequestView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/saml2/logout/",
|
||||
"view_path": "authentication.backends.saml2.views.Saml2EndSessionView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/saml2/callback/",
|
||||
"view_path": "authentication.backends.saml2.views.Saml2AuthCallbackView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/saml2/metadata/",
|
||||
"view_path": "authentication.backends.saml2.views.Saml2AuthMetadataView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/oauth2/login/",
|
||||
"view_path": "authentication.backends.oauth2.views.OAuth2AuthRequestView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/oauth2/callback/",
|
||||
"view_path": "authentication.backends.oauth2.views.OAuth2AuthCallbackView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/oauth2/logout/",
|
||||
"view_path": "authentication.backends.oauth2.views.OAuth2EndSessionView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/oauth2-provider/token/",
|
||||
"view_path": "oauth2_provider.views.base.TokenView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/oauth2-provider/revoke/",
|
||||
"view_path": "oauth2_provider.views.base.RevokeTokenView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/auth/oauth2-provider/.well-known/oauth-authorization-server",
|
||||
"view_path": "authentication.backends.oauth2_provider.views.OAuthAuthorizationServerView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/reports/export-pdf/",
|
||||
"view_path": "reports.views.ExportPdfView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/reports/send-mail/",
|
||||
"view_path": "reports.views.SendMailView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/tickets/direct-approve/<str:token>/",
|
||||
"view_path": "tickets.views.approve.TicketDirectApproveView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/common/flash-message/",
|
||||
"view_path": "common.views.msg.FlashMessageMsgView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/download/",
|
||||
"view_path": "jumpserver.views.other.ResourceDownload",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/redirect/confirm/",
|
||||
"view_path": "jumpserver.views.other.RedirectConfirm",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/i18n/<str:lang>/",
|
||||
"view_path": "jumpserver.views.other.I18NView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/media/^(?P<path>.*)$",
|
||||
"view_path": "private_storage.views.PrivateStorageView",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
},
|
||||
{
|
||||
"url": "/core/jsi18n/",
|
||||
"view_path": "django.views.i18n.JavaScriptCatalog",
|
||||
"view_requires_auth": false,
|
||||
"view_type": "class"
|
||||
}
|
||||
]
|
||||
1702
utils/view_schema/output/webui_schema.json
Normal file
1702
utils/view_schema/output/webui_schema.json
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user