valar 1.2.5__tar.gz → 1.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of valar might be problematic. Click here for more details.
- {valar-1.2.5/src/valar.egg-info → valar-1.3.1}/PKG-INFO +26 -1
- {valar-1.2.5 → valar-1.3.1}/README.md +24 -0
- {valar-1.2.5 → valar-1.3.1}/setup.py +2 -2
- {valar-1.2.5 → valar-1.3.1}/src/valar/apps.py +10 -4
- valar-1.3.1/src/valar/auth/Authentication.py +32 -0
- valar-1.3.1/src/valar/auth/Middleware.py +28 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/channels/sender.py +4 -1
- valar-1.3.1/src/valar/classes/valar_response.py +9 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/abstract.py +6 -3
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/defaults/field_keys_default.py +10 -1
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/engine.py +9 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/orm_field.py +4 -6
- valar-1.3.1/src/valar/migrations/0001_initial.py +285 -0
- valar-1.3.1/src/valar/models/__init__.py +1 -0
- valar-1.3.1/src/valar/models/auth.py +47 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/urls.py +10 -4
- valar-1.3.1/src/valar/views/__init__.py +0 -0
- valar-1.3.1/src/valar/views/auth.py +110 -0
- valar-1.3.1/src/valar/views/password.py +60 -0
- {valar-1.2.5 → valar-1.3.1/src/valar.egg-info}/PKG-INFO +26 -1
- {valar-1.2.5 → valar-1.3.1}/src/valar.egg-info/SOURCES.txt +7 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar.egg-info/requires.txt +1 -0
- valar-1.2.5/src/valar/classes/valar_response.py +0 -8
- valar-1.2.5/src/valar/models/__init__.py +0 -1
- {valar-1.2.5 → valar-1.3.1}/LICENSE +0 -0
- {valar-1.2.5 → valar-1.3.1}/setup.cfg +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/__init__.py +0 -0
- {valar-1.2.5/src/valar/channels → valar-1.3.1/src/valar/auth}/__init__.py +0 -0
- {valar-1.2.5/src/valar/classes → valar-1.3.1/src/valar/channels}/__init__.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/channels/consumer.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/channels/counter.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/channels/executer.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/channels/mapping.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/channels/views.py +0 -0
- {valar-1.2.5/src/valar/classes/app_mixins → valar-1.3.1/src/valar/classes}/__init__.py +0 -0
- {valar-1.2.5/src/valar/dao/defaults → valar-1.3.1/src/valar/classes/app_mixins}/__init__.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/classes/app_mixins/auto_migration_mixin.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/classes/app_mixins/auto_urlpatterns_mixin.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/classes/singleton_meta.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/classes/valar_minio.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/__init__.py +0 -0
- {valar-1.2.5/src/valar/migrations → valar-1.3.1/src/valar/dao/defaults}/__init__.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/defaults/field_values_default.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/defaults/view_defaults.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/frame.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/meta.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/mon_dao.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/mon_field.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/orm_dao.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/dao/query.py +0 -0
- {valar-1.2.5/src/valar/views → valar-1.3.1/src/valar/migrations}/__init__.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/models/core.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/models/frame.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/models/meta.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/models/test.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/views/file.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/views/handler.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/views/meta.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar/views/rest.py +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar.egg-info/dependency_links.txt +0 -0
- {valar-1.2.5 → valar-1.3.1}/src/valar.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: valar
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Summary: valar for morghulis
|
|
5
5
|
Home-page: https://gitee.com/GRIFFIN120/valar_dev
|
|
6
6
|
Author: LYP
|
|
@@ -16,6 +16,7 @@ Requires-Dist: pandas==2.2.3
|
|
|
16
16
|
Requires-Dist: openpyxl==3.1.5
|
|
17
17
|
Requires-Dist: deepmerge~=2.0
|
|
18
18
|
Requires-Dist: minio==7.2.2
|
|
19
|
+
Requires-Dist: PyJWT~=2.10.1
|
|
19
20
|
Dynamic: author
|
|
20
21
|
Dynamic: author-email
|
|
21
22
|
Dynamic: description
|
|
@@ -106,6 +107,15 @@ MONGO_URI = 'mongodb://username:password@host:27017'
|
|
|
106
107
|
MINIO_URL = "s3://username:password@host:9000"
|
|
107
108
|
MINIO_ROOT = "https://host:9001"
|
|
108
109
|
|
|
110
|
+
""" Email Options """
|
|
111
|
+
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
|
112
|
+
EMAIL_HOST = 'xxx.xx.xx.xx'
|
|
113
|
+
EMAIL_PORT = 465
|
|
114
|
+
EMAIL_DOMAIN = 'http://xx.xx.xx'
|
|
115
|
+
EMAIL_HOST_USER = "xxx@xx.xx.xx"
|
|
116
|
+
EMAIL_HOST_PASSWORD = '*******'
|
|
117
|
+
EMAIL_USE_TLS = False
|
|
118
|
+
EMAIL_USE_SSL = True
|
|
109
119
|
```
|
|
110
120
|
|
|
111
121
|
## 2.2 asgi.py
|
|
@@ -199,3 +209,18 @@ channel_mapping = {
|
|
|
199
209
|
}
|
|
200
210
|
|
|
201
211
|
```
|
|
212
|
+
|
|
213
|
+
# 5. create an orm model extends the AbstractUser class, enable the authentication of Valar
|
|
214
|
+
|
|
215
|
+
```pycon
|
|
216
|
+
|
|
217
|
+
class User(AbstractUser):
|
|
218
|
+
"""
|
|
219
|
+
any fields you want
|
|
220
|
+
"""
|
|
221
|
+
description = models.TextField(null=True, verbose_name='备注')
|
|
222
|
+
|
|
223
|
+
class Meta:
|
|
224
|
+
verbose_name = 'User'
|
|
225
|
+
|
|
226
|
+
```
|
|
@@ -78,6 +78,15 @@ MONGO_URI = 'mongodb://username:password@host:27017'
|
|
|
78
78
|
MINIO_URL = "s3://username:password@host:9000"
|
|
79
79
|
MINIO_ROOT = "https://host:9001"
|
|
80
80
|
|
|
81
|
+
""" Email Options """
|
|
82
|
+
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
|
83
|
+
EMAIL_HOST = 'xxx.xx.xx.xx'
|
|
84
|
+
EMAIL_PORT = 465
|
|
85
|
+
EMAIL_DOMAIN = 'http://xx.xx.xx'
|
|
86
|
+
EMAIL_HOST_USER = "xxx@xx.xx.xx"
|
|
87
|
+
EMAIL_HOST_PASSWORD = '*******'
|
|
88
|
+
EMAIL_USE_TLS = False
|
|
89
|
+
EMAIL_USE_SSL = True
|
|
81
90
|
```
|
|
82
91
|
|
|
83
92
|
## 2.2 asgi.py
|
|
@@ -170,4 +179,19 @@ channel_mapping = {
|
|
|
170
179
|
'test_handler': valar_test_handler,
|
|
171
180
|
}
|
|
172
181
|
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
# 5. create an orm model extends the AbstractUser class, enable the authentication of Valar
|
|
185
|
+
|
|
186
|
+
```pycon
|
|
187
|
+
|
|
188
|
+
class User(AbstractUser):
|
|
189
|
+
"""
|
|
190
|
+
any fields you want
|
|
191
|
+
"""
|
|
192
|
+
description = models.TextField(null=True, verbose_name='备注')
|
|
193
|
+
|
|
194
|
+
class Meta:
|
|
195
|
+
verbose_name = 'User'
|
|
196
|
+
|
|
173
197
|
```
|
|
@@ -12,11 +12,11 @@ requires = [
|
|
|
12
12
|
'openpyxl==3.1.5',
|
|
13
13
|
'deepmerge~=2.0',
|
|
14
14
|
'minio==7.2.2',
|
|
15
|
+
'PyJWT~=2.10.1'
|
|
15
16
|
]
|
|
16
|
-
|
|
17
17
|
setup(
|
|
18
18
|
name="valar", # 包名
|
|
19
|
-
version="1.
|
|
19
|
+
version="1.3.1", # 版本号
|
|
20
20
|
author="LYP", # 作者
|
|
21
21
|
author_email="liuyinpeng@buaa.edu.cn", # 邮箱
|
|
22
22
|
description="valar for morghulis", # 简短描述
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import traceback
|
|
3
|
+
|
|
2
4
|
from django.apps import AppConfig
|
|
3
5
|
|
|
4
6
|
from .classes.app_mixins.auto_migration_mixin import AutoMigrationMixin
|
|
@@ -11,7 +13,11 @@ class ValarConfig(AutoMigrationMixin, AutoUrlPatternsMixin, AppConfig):
|
|
|
11
13
|
|
|
12
14
|
def ready(self):
|
|
13
15
|
if os.environ.get('RUN_MAIN') == 'true':
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
try:
|
|
17
|
+
from .dao.frame import MetaFrame
|
|
18
|
+
getattr(super(), 'set_url', None)()
|
|
19
|
+
getattr(super(), 'auto_migrate', None)()
|
|
20
|
+
MetaFrame()
|
|
21
|
+
except Exception as e:
|
|
22
|
+
traceback.print_exc()
|
|
23
|
+
print('ERROR', e)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import jwt
|
|
2
|
+
from django.conf import settings
|
|
3
|
+
|
|
4
|
+
from ..classes.valar_response import ValarResponse
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def auth_required(view_func):
|
|
8
|
+
def wrapper(request, *args, **kwargs):
|
|
9
|
+
payload, response = validate(request)
|
|
10
|
+
if payload:
|
|
11
|
+
request.user_id = payload["user_id"]
|
|
12
|
+
return view_func(request, *args, **kwargs)
|
|
13
|
+
else:
|
|
14
|
+
return response
|
|
15
|
+
|
|
16
|
+
return wrapper
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate(request):
|
|
20
|
+
auth_header = request.headers.get("Authorization")
|
|
21
|
+
if not auth_header or not auth_header.startswith("Bearer "):
|
|
22
|
+
return None, ValarResponse(False, '请登录系统', 'info', status=401)
|
|
23
|
+
token = auth_header.split(" ")[1]
|
|
24
|
+
if not token:
|
|
25
|
+
return None, ValarResponse(False, status=401)
|
|
26
|
+
try:
|
|
27
|
+
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
|
|
28
|
+
return payload, ValarResponse(False)
|
|
29
|
+
except jwt.ExpiredSignatureError:
|
|
30
|
+
return None, ValarResponse(False, '状态已过期,请重新登录', 'warning', status=401)
|
|
31
|
+
except jwt.InvalidTokenError:
|
|
32
|
+
return None, ValarResponse(False, '错误状态,请重新登录', 'error', status=401)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from django.http import HttpRequest
|
|
5
|
+
from django.utils.deprecation import MiddlewareMixin
|
|
6
|
+
|
|
7
|
+
from ..auth.Authentication import validate
|
|
8
|
+
from ..classes.valar_response import ValarResponse
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ValarMiddleware(MiddlewareMixin):
|
|
12
|
+
@staticmethod
|
|
13
|
+
def process_response(request: HttpRequest, response: ValarResponse):
|
|
14
|
+
auth = request.headers.get("Auth")
|
|
15
|
+
if auth:
|
|
16
|
+
payload, _ = validate(request)
|
|
17
|
+
if not payload:
|
|
18
|
+
return ValarResponse(False, '无效权限', 'error', status=403)
|
|
19
|
+
if type(response) == ValarResponse:
|
|
20
|
+
valar_message, valar_code = response.valar_message, response.valar_code
|
|
21
|
+
data = {
|
|
22
|
+
'payload': json.loads(response.content),
|
|
23
|
+
'message': valar_message,
|
|
24
|
+
'code': valar_code
|
|
25
|
+
}
|
|
26
|
+
response.content = json.dumps(data, ensure_ascii=False).encode("utf-8")
|
|
27
|
+
response["Content-Type"] = "application/json; charset=utf-8"
|
|
28
|
+
return response
|
|
@@ -7,6 +7,7 @@ from channels.layers import get_channel_layer
|
|
|
7
7
|
from django.http import HttpRequest
|
|
8
8
|
import threading
|
|
9
9
|
from .consumer import VALAR_CHANNEL_GROUP
|
|
10
|
+
from ..auth.Authentication import validate
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class Channel:
|
|
@@ -54,7 +55,9 @@ class ValarChannelSender(Sender):
|
|
|
54
55
|
self.__lock__ = threading.Lock()
|
|
55
56
|
self.__interval__ = interval
|
|
56
57
|
if self.__channel__.auth and not self.uid:
|
|
57
|
-
|
|
58
|
+
payload, _ = validate(request)
|
|
59
|
+
if payload is None:
|
|
60
|
+
raise Exception('Unauthorized!')
|
|
58
61
|
|
|
59
62
|
def _run(self):
|
|
60
63
|
while self.__loading__:
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from django.http import JsonResponse
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ValarResponse(JsonResponse):
|
|
5
|
+
def __init__(self, data=True, message='', code='info', status=200):
|
|
6
|
+
self.valar_message = message
|
|
7
|
+
self.valar_code = code
|
|
8
|
+
self.status_code = status
|
|
9
|
+
super(ValarResponse, self).__init__(data, safe=False)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
|
|
3
3
|
from bson import ObjectId
|
|
4
|
+
from django.db.models import QuerySet
|
|
4
5
|
|
|
5
6
|
from ..dao.engine import ValarEngine
|
|
7
|
+
from ..models.core import VModel
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class AbstractField(ABC):
|
|
@@ -12,6 +14,7 @@ class AbstractField(ABC):
|
|
|
12
14
|
label = None
|
|
13
15
|
domain = None
|
|
14
16
|
refer = None
|
|
17
|
+
model_field = None
|
|
15
18
|
|
|
16
19
|
@abstractmethod
|
|
17
20
|
def to_dict(self):
|
|
@@ -51,7 +54,7 @@ class AbstractDao(ABC):
|
|
|
51
54
|
pass
|
|
52
55
|
|
|
53
56
|
@abstractmethod
|
|
54
|
-
def values(self, conditions, props):
|
|
57
|
+
def values(self, conditions, props) -> list:
|
|
55
58
|
pass
|
|
56
59
|
|
|
57
60
|
@abstractmethod
|
|
@@ -59,11 +62,11 @@ class AbstractDao(ABC):
|
|
|
59
62
|
pass
|
|
60
63
|
|
|
61
64
|
@abstractmethod
|
|
62
|
-
def find_one(self, _id):
|
|
65
|
+
def find_one(self, _id) -> VModel:
|
|
63
66
|
pass
|
|
64
67
|
|
|
65
68
|
@abstractmethod
|
|
66
|
-
def find(self, conditions=None, orders=None, size=0, page=1):
|
|
69
|
+
def find(self, conditions=None, orders=None, size=0, page=1) -> (QuerySet, int):
|
|
67
70
|
pass
|
|
68
71
|
|
|
69
72
|
@abstractmethod
|
|
@@ -39,10 +39,19 @@ meta_field_key_defaults = {
|
|
|
39
39
|
]
|
|
40
40
|
),
|
|
41
41
|
},
|
|
42
|
-
'valar.
|
|
42
|
+
'valar.Vala': {
|
|
43
43
|
'simple': ('pick', ['id', 'name', 'text_field', 'boolean_field', 'integer_field', 'float_field']),
|
|
44
44
|
'date': ('pick', ['id', 'name', 'date_field', 'datetime_field', 'time_field']),
|
|
45
45
|
'special': ('pick', ['id', 'name', 'text_field', 'json_field', 'file', 'm2m']),
|
|
46
46
|
'ref': ('pick', ['id', 'name', 'm2o', 'm2m', 'o2o_id']),
|
|
47
47
|
},
|
|
48
|
+
'valar.Account': {
|
|
49
|
+
'auth': ('pick', ['user_id', 'username', 'email', 'is_active', 'is_admin', 'roles']),
|
|
50
|
+
},
|
|
51
|
+
'valar.Role': {
|
|
52
|
+
'auth': ('pick', ['name', 'duty', 'menu', 'account']),
|
|
53
|
+
},
|
|
54
|
+
'valar.Menu': {
|
|
55
|
+
'auth': ('pick', ['icon', 'path', 'name', 'roles', 'is_admin', 'scope']),
|
|
56
|
+
},
|
|
48
57
|
}
|
|
@@ -2,6 +2,8 @@ import pymongo
|
|
|
2
2
|
from django.apps import apps
|
|
3
3
|
from django.conf import settings
|
|
4
4
|
from urllib.parse import urlparse
|
|
5
|
+
|
|
6
|
+
from django.core.mail import EmailMessage
|
|
5
7
|
from minio import Minio
|
|
6
8
|
from pymongo.synchronous.collection import Collection
|
|
7
9
|
|
|
@@ -55,6 +57,13 @@ class ValarEngine(metaclass=SingletonMeta):
|
|
|
55
57
|
else:
|
|
56
58
|
self.minio_engine = None
|
|
57
59
|
|
|
60
|
+
self.from_email = settings.EMAIL_HOST_USER
|
|
61
|
+
|
|
62
|
+
def send_email(self, title, content, email):
|
|
63
|
+
e = EmailMessage(title, content, self.from_email, [email])
|
|
64
|
+
e.content_subtype = 'html'
|
|
65
|
+
e.send()
|
|
66
|
+
|
|
58
67
|
def get_orm_model(self, entity) -> VModel:
|
|
59
68
|
return self.orm_engine[entity]
|
|
60
69
|
|
|
@@ -131,12 +131,10 @@ class OrmField(AbstractField):
|
|
|
131
131
|
|
|
132
132
|
|
|
133
133
|
def column_width(domain, tool):
|
|
134
|
-
if domain in ['BooleanField', '
|
|
135
|
-
return 80
|
|
136
|
-
elif domain in ['DateField', 'DateTimeField', 'TimeField']:
|
|
134
|
+
if domain in ['BooleanField', 'DateField', 'DateTimeField', 'TimeField']:
|
|
137
135
|
return 120
|
|
138
|
-
elif domain in ['ManyToManyRel', '
|
|
139
|
-
return
|
|
140
|
-
elif domain in ['TextField']:
|
|
136
|
+
elif domain in ['ManyToManyRel', 'ManyToManyField', 'ManyToOneRel']:
|
|
137
|
+
return 120
|
|
138
|
+
elif domain in ['TextField', 'FileField', 'JSONField']:
|
|
141
139
|
return 80 if tool == 'rich' else 0
|
|
142
140
|
return 0
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Generated by Django 4.2.23 on 2025-09-06 15:12
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
import django.db.models.deletion
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
initial = True
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.CreateModel(
|
|
16
|
+
name='Meta',
|
|
17
|
+
fields=[
|
|
18
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
19
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
20
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
21
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
22
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
23
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
24
|
+
('db', models.CharField(max_length=100, null=True, verbose_name='数据库')),
|
|
25
|
+
('entity', models.CharField(max_length=100, null=True, verbose_name='数据源')),
|
|
26
|
+
('name', models.CharField(max_length=50, null=True, verbose_name='实体别名')),
|
|
27
|
+
('tree', models.BooleanField(default=False, verbose_name='是否树形')),
|
|
28
|
+
],
|
|
29
|
+
options={
|
|
30
|
+
'verbose_name': '数据实体',
|
|
31
|
+
'unique_together': {('db', 'entity')},
|
|
32
|
+
},
|
|
33
|
+
),
|
|
34
|
+
migrations.CreateModel(
|
|
35
|
+
name='MetaFieldTool',
|
|
36
|
+
fields=[
|
|
37
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
38
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
39
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
40
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
41
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
42
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
43
|
+
('pid', models.IntegerField(default=0, verbose_name='父节点')),
|
|
44
|
+
('isLeaf', models.BooleanField(default=False, verbose_name='叶子节点')),
|
|
45
|
+
('icon', models.CharField(max_length=255, null=True, verbose_name='图标')),
|
|
46
|
+
('name', models.CharField(max_length=255, null=True, verbose_name='名称')),
|
|
47
|
+
('code', models.CharField(max_length=100, null=True, unique=True, verbose_name='代码')),
|
|
48
|
+
('align', models.CharField(max_length=10, null=True, verbose_name='对齐方式')),
|
|
49
|
+
],
|
|
50
|
+
options={
|
|
51
|
+
'verbose_name': '元数据字段工具',
|
|
52
|
+
},
|
|
53
|
+
),
|
|
54
|
+
migrations.CreateModel(
|
|
55
|
+
name='Role',
|
|
56
|
+
fields=[
|
|
57
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
58
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
59
|
+
('name', models.CharField(max_length=50, null=True)),
|
|
60
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
61
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
62
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
63
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
64
|
+
('duty', models.TextField(null=True, verbose_name='职责描述')),
|
|
65
|
+
],
|
|
66
|
+
options={
|
|
67
|
+
'verbose_name': '角色',
|
|
68
|
+
},
|
|
69
|
+
),
|
|
70
|
+
migrations.CreateModel(
|
|
71
|
+
name='Valar',
|
|
72
|
+
fields=[
|
|
73
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
74
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
75
|
+
('name', models.CharField(max_length=50, null=True)),
|
|
76
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
77
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
78
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
79
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
80
|
+
('text_field', models.TextField(null=True, verbose_name='Text Field')),
|
|
81
|
+
('boolean_field', models.BooleanField(null=True, verbose_name='Boolean Field')),
|
|
82
|
+
('integer_field', models.IntegerField(null=True, verbose_name='Integer Field')),
|
|
83
|
+
('float_field', models.FloatField(null=True, verbose_name='Float Field')),
|
|
84
|
+
('date_field', models.DateField(null=True, verbose_name='Date Field')),
|
|
85
|
+
('datetime_field', models.DateTimeField(null=True, verbose_name='Datetime Field')),
|
|
86
|
+
('time_field', models.TimeField(null=True, verbose_name='Time Field')),
|
|
87
|
+
('json_field', models.JSONField(null=True, verbose_name='Json Field')),
|
|
88
|
+
('file', models.FileField(null=True, upload_to='', verbose_name='File Field')),
|
|
89
|
+
],
|
|
90
|
+
options={
|
|
91
|
+
'abstract': False,
|
|
92
|
+
},
|
|
93
|
+
),
|
|
94
|
+
migrations.CreateModel(
|
|
95
|
+
name='Voo',
|
|
96
|
+
fields=[
|
|
97
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
98
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
99
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
100
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
101
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
102
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
103
|
+
('name', models.CharField(max_length=100, null=True, verbose_name='Name')),
|
|
104
|
+
('valar', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='valar.valar', verbose_name='Valar')),
|
|
105
|
+
],
|
|
106
|
+
options={
|
|
107
|
+
'abstract': False,
|
|
108
|
+
},
|
|
109
|
+
),
|
|
110
|
+
migrations.CreateModel(
|
|
111
|
+
name='Vmo',
|
|
112
|
+
fields=[
|
|
113
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
114
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
115
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
116
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
117
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
118
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
119
|
+
('name', models.CharField(max_length=100, null=True, verbose_name='Name')),
|
|
120
|
+
('valar', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='valar.valar', verbose_name='Valar')),
|
|
121
|
+
],
|
|
122
|
+
options={
|
|
123
|
+
'abstract': False,
|
|
124
|
+
},
|
|
125
|
+
),
|
|
126
|
+
migrations.CreateModel(
|
|
127
|
+
name='Vmm',
|
|
128
|
+
fields=[
|
|
129
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
130
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
131
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
132
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
133
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
134
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
135
|
+
('pid', models.IntegerField(default=0, verbose_name='父节点')),
|
|
136
|
+
('isLeaf', models.BooleanField(default=False, verbose_name='叶子节点')),
|
|
137
|
+
('icon', models.CharField(max_length=255, null=True, verbose_name='图标')),
|
|
138
|
+
('name', models.CharField(max_length=100, null=True, verbose_name='name')),
|
|
139
|
+
('valars', models.ManyToManyField(to='valar.valar', verbose_name='valars')),
|
|
140
|
+
],
|
|
141
|
+
options={
|
|
142
|
+
'abstract': False,
|
|
143
|
+
},
|
|
144
|
+
),
|
|
145
|
+
migrations.CreateModel(
|
|
146
|
+
name='MetaView',
|
|
147
|
+
fields=[
|
|
148
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
149
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
150
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
151
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
152
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
153
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
154
|
+
('property', models.JSONField(default=dict, verbose_name='属性')),
|
|
155
|
+
('code', models.CharField(default='default ', max_length=50, verbose_name='类视图')),
|
|
156
|
+
('name', models.CharField(max_length=50, null=True, verbose_name='视图名称')),
|
|
157
|
+
('lock', models.BooleanField(default=False, verbose_name='锁定元数据')),
|
|
158
|
+
('enable', models.BooleanField(default=True, verbose_name='是否启用')),
|
|
159
|
+
('form_width', models.IntegerField(null=True, verbose_name='表单宽度')),
|
|
160
|
+
('form_height', models.IntegerField(null=True, verbose_name='表单高度')),
|
|
161
|
+
('table_width', models.IntegerField(null=True, verbose_name='表格宽度')),
|
|
162
|
+
('table_height', models.IntegerField(null=True, verbose_name='表格高度')),
|
|
163
|
+
('allow_search', models.BooleanField(default=True, verbose_name='检索功能')),
|
|
164
|
+
('allow_order', models.BooleanField(default=True, verbose_name='排序功能')),
|
|
165
|
+
('allow_insert', models.BooleanField(default=True, verbose_name='新增功能')),
|
|
166
|
+
('allow_edit', models.BooleanField(default=True, verbose_name='编辑功能')),
|
|
167
|
+
('allow_edit_on_form', models.BooleanField(default=True, verbose_name='表单编辑')),
|
|
168
|
+
('allow_edit_on_cell', models.BooleanField(default=True, verbose_name='表内编辑')),
|
|
169
|
+
('allow_edit_on_sort', models.BooleanField(default=True, verbose_name='移动功能')),
|
|
170
|
+
('allow_remove', models.BooleanField(default=True, verbose_name='删除功能')),
|
|
171
|
+
('allow_download', models.BooleanField(default=True, verbose_name='下载功能')),
|
|
172
|
+
('allow_upload', models.BooleanField(default=True, verbose_name='上传功能')),
|
|
173
|
+
('meta', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='valar.meta', verbose_name='元数据')),
|
|
174
|
+
],
|
|
175
|
+
options={
|
|
176
|
+
'verbose_name': '数据视图',
|
|
177
|
+
'unique_together': {('meta', 'code')},
|
|
178
|
+
},
|
|
179
|
+
),
|
|
180
|
+
migrations.CreateModel(
|
|
181
|
+
name='MetaFieldDomain',
|
|
182
|
+
fields=[
|
|
183
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
184
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
185
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
186
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
187
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
188
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
189
|
+
('name', models.CharField(max_length=255, null=True, unique=True, verbose_name='名称')),
|
|
190
|
+
('default', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='valar.metafieldtool', verbose_name='默认工具')),
|
|
191
|
+
('tools', models.ManyToManyField(to='valar.metafieldtool', verbose_name='工具集')),
|
|
192
|
+
],
|
|
193
|
+
options={
|
|
194
|
+
'verbose_name': '元数据字段类型',
|
|
195
|
+
},
|
|
196
|
+
),
|
|
197
|
+
migrations.CreateModel(
|
|
198
|
+
name='MetaField',
|
|
199
|
+
fields=[
|
|
200
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
201
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
202
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
203
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
204
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
205
|
+
('prop', models.CharField(max_length=100, verbose_name='字段名称')),
|
|
206
|
+
('label', models.CharField(max_length=100, verbose_name='字段标签')),
|
|
207
|
+
('name', models.CharField(max_length=100, verbose_name='字段别名')),
|
|
208
|
+
('domain', models.CharField(max_length=100, verbose_name='字段类型')),
|
|
209
|
+
('tool', models.CharField(default='default', max_length=100, verbose_name='工具组件')),
|
|
210
|
+
('refer', models.JSONField(default=dict, verbose_name='索引')),
|
|
211
|
+
('format', models.JSONField(default=dict, verbose_name='格式')),
|
|
212
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
213
|
+
('not_null', models.BooleanField(default=False, verbose_name='不为空')),
|
|
214
|
+
('allow_edit', models.BooleanField(default=True, verbose_name='可编辑')),
|
|
215
|
+
('allow_order', models.BooleanField(default=True, verbose_name='可排序')),
|
|
216
|
+
('allow_search', models.BooleanField(default=True, verbose_name='可搜索')),
|
|
217
|
+
('allow_download', models.BooleanField(default=True, verbose_name='可下载')),
|
|
218
|
+
('allow_upload', models.BooleanField(default=True, verbose_name='可上传')),
|
|
219
|
+
('allow_update', models.BooleanField(default=True, verbose_name='可更新')),
|
|
220
|
+
('unit', models.CharField(max_length=55, null=True, verbose_name='单位符')),
|
|
221
|
+
('column_width', models.FloatField(default=0, verbose_name='表头宽度')),
|
|
222
|
+
('align', models.CharField(max_length=55, null=True, verbose_name='对齐方式')),
|
|
223
|
+
('fixed', models.CharField(max_length=100, null=True, verbose_name='固定位置')),
|
|
224
|
+
('header_color', models.CharField(max_length=55, null=True, verbose_name='表头颜色')),
|
|
225
|
+
('cell_color', models.CharField(max_length=55, null=True, verbose_name='单元颜色')),
|
|
226
|
+
('edit_on_table', models.BooleanField(default=True, verbose_name='表格编辑')),
|
|
227
|
+
('hide_on_table', models.BooleanField(default=False, verbose_name='表内隐藏')),
|
|
228
|
+
('span', models.IntegerField(default=0, verbose_name='表单占位')),
|
|
229
|
+
('hide_on_form', models.BooleanField(default=False, verbose_name='表单隐藏')),
|
|
230
|
+
('hide_on_form_edit', models.BooleanField(default=False, verbose_name='编辑隐藏')),
|
|
231
|
+
('hide_on_form_insert', models.BooleanField(default=False, verbose_name='新增隐藏')),
|
|
232
|
+
('hide_on_form_branch', models.BooleanField(default=False, verbose_name='分支隐藏')),
|
|
233
|
+
('hide_on_form_leaf', models.BooleanField(default=False, verbose_name='叶子隐藏')),
|
|
234
|
+
('view', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='valar.metaview', verbose_name='数据视图')),
|
|
235
|
+
],
|
|
236
|
+
options={
|
|
237
|
+
'verbose_name': '视图字段',
|
|
238
|
+
},
|
|
239
|
+
),
|
|
240
|
+
migrations.CreateModel(
|
|
241
|
+
name='Account',
|
|
242
|
+
fields=[
|
|
243
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
244
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
245
|
+
('name', models.CharField(max_length=50, null=True)),
|
|
246
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
247
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
248
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
249
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
250
|
+
('username', models.CharField(max_length=255, null=True, unique=True, verbose_name='账号')),
|
|
251
|
+
('password', models.CharField(max_length=255, verbose_name='密码')),
|
|
252
|
+
('email', models.CharField(max_length=255, null=True, unique=True, verbose_name='邮箱')),
|
|
253
|
+
('is_admin', models.BooleanField(default=False, verbose_name='超级管理员')),
|
|
254
|
+
('is_active', models.BooleanField(default=False, verbose_name='激活状态')),
|
|
255
|
+
('token', models.CharField(max_length=255, null=True, verbose_name='Token')),
|
|
256
|
+
('roles', models.ManyToManyField(to='valar.role')),
|
|
257
|
+
],
|
|
258
|
+
options={
|
|
259
|
+
'verbose_name': '账户信息',
|
|
260
|
+
},
|
|
261
|
+
),
|
|
262
|
+
migrations.CreateModel(
|
|
263
|
+
name='Menu',
|
|
264
|
+
fields=[
|
|
265
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
266
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
267
|
+
('name', models.CharField(max_length=50, null=True)),
|
|
268
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
269
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
270
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
271
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
272
|
+
('pid', models.IntegerField(default=0, verbose_name='父节点')),
|
|
273
|
+
('isLeaf', models.BooleanField(default=False, verbose_name='叶子节点')),
|
|
274
|
+
('icon', models.CharField(max_length=255, null=True, verbose_name='图标')),
|
|
275
|
+
('scope', models.CharField(max_length=100, null=True, verbose_name='域')),
|
|
276
|
+
('path', models.CharField(max_length=255, null=True, verbose_name='地址')),
|
|
277
|
+
('is_admin', models.BooleanField(default=False, null=True, verbose_name='超管权限')),
|
|
278
|
+
('roles', models.ManyToManyField(to='valar.role')),
|
|
279
|
+
],
|
|
280
|
+
options={
|
|
281
|
+
'verbose_name': '菜单',
|
|
282
|
+
'unique_together': {('scope', 'path')},
|
|
283
|
+
},
|
|
284
|
+
),
|
|
285
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import core, frame, meta, auth, test
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from .core import VModel, VTree
|
|
3
|
+
from django.contrib.auth.hashers import check_password
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Role(VModel):
|
|
7
|
+
duty = models.TextField(null=True, verbose_name='职责描述')
|
|
8
|
+
|
|
9
|
+
class Meta:
|
|
10
|
+
verbose_name = '角色'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Account(VModel):
|
|
14
|
+
"""账户核心信息"""
|
|
15
|
+
username = models.CharField(max_length=255, null=True, unique=True, verbose_name='账号')
|
|
16
|
+
password = models.CharField(max_length=255, verbose_name='密码')
|
|
17
|
+
email = models.CharField(max_length=255, null=True, unique=True, verbose_name='邮箱')
|
|
18
|
+
"""权限"""
|
|
19
|
+
is_admin = models.BooleanField(default=False, verbose_name='超级管理员')
|
|
20
|
+
roles = models.ManyToManyField(Role)
|
|
21
|
+
"""密码找回"""
|
|
22
|
+
is_active = models.BooleanField(default=False, verbose_name='激活状态')
|
|
23
|
+
token = models.CharField(max_length=255, null=True, verbose_name='Token')
|
|
24
|
+
|
|
25
|
+
def is_auth(self, password):
|
|
26
|
+
return password == self.token or check_password(password, self.password)
|
|
27
|
+
|
|
28
|
+
class Meta:
|
|
29
|
+
verbose_name = '账户信息'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Menu(VTree):
|
|
33
|
+
scope = models.CharField(max_length=100, null=True, verbose_name='域')
|
|
34
|
+
path = models.CharField(max_length=255, null=True, verbose_name='地址')
|
|
35
|
+
roles = models.ManyToManyField(Role)
|
|
36
|
+
is_admin = models.BooleanField(null=True, default=False, verbose_name='超管权限')
|
|
37
|
+
|
|
38
|
+
class Meta:
|
|
39
|
+
verbose_name = '菜单'
|
|
40
|
+
unique_together = ('scope', 'path')
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AbstractUser(VModel):
|
|
44
|
+
account = models.OneToOneField(Account, null=True, on_delete=models.SET_NULL)
|
|
45
|
+
|
|
46
|
+
class Meta:
|
|
47
|
+
abstract = True
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
from django.urls import path
|
|
2
|
-
|
|
3
1
|
from .channels.views import handel_channel
|
|
4
|
-
from .views import rest, meta, file
|
|
2
|
+
from .views import rest, meta, file, auth, password
|
|
3
|
+
from django.urls import path
|
|
5
4
|
|
|
6
5
|
urlpatterns = [
|
|
7
6
|
path('socket/<str:handler>', handel_channel),
|
|
@@ -13,7 +12,6 @@ urlpatterns = [
|
|
|
13
12
|
path('<str:db>/<str:entity>/update', rest.update),
|
|
14
13
|
path('<str:db>/<str:entity>/search', rest.search),
|
|
15
14
|
path('<str:db>/<str:entity>/values', rest.values),
|
|
16
|
-
# path('<str:db>/<str:entity>/tree', rest.tree),
|
|
17
15
|
|
|
18
16
|
path('<str:db>/<str:entity>/meta_view', meta.meta_view),
|
|
19
17
|
path('metas', meta.metas),
|
|
@@ -26,4 +24,12 @@ urlpatterns = [
|
|
|
26
24
|
path('<str:db>/<str:entity>/save_file', file.save_file),
|
|
27
25
|
path('<str:db>/<str:entity>/remove_file', file.remove_file),
|
|
28
26
|
|
|
27
|
+
path('sign_in', auth.sign_in),
|
|
28
|
+
path("user_profile", auth.user_profile),
|
|
29
|
+
path("free_menus", auth.free_menus),
|
|
30
|
+
|
|
31
|
+
path("change_password", password.change_password),
|
|
32
|
+
path("retrieve_password", password.retrieve_password),
|
|
33
|
+
path("send_password", password.send_password),
|
|
34
|
+
|
|
29
35
|
]
|
|
File without changes
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
import jwt
|
|
5
|
+
from django.conf import settings
|
|
6
|
+
from django.db.models import OneToOneRel
|
|
7
|
+
from django.contrib.auth.hashers import make_password
|
|
8
|
+
|
|
9
|
+
from ..auth.Authentication import auth_required
|
|
10
|
+
from ..classes.valar_response import ValarResponse
|
|
11
|
+
from ..dao import Dao
|
|
12
|
+
from ..models.auth import Account, AbstractUser, Menu
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def sign_in(request):
|
|
16
|
+
body = json.loads(request.body)
|
|
17
|
+
username = body.get("username")
|
|
18
|
+
email = body.get("email")
|
|
19
|
+
password = body.get("password")
|
|
20
|
+
signin = body.get("signin")
|
|
21
|
+
dao = Dao('valar.Account')
|
|
22
|
+
account = dao.search({"username": username}).first()
|
|
23
|
+
if account is not None:
|
|
24
|
+
if signin:
|
|
25
|
+
if not account.is_auth(password):
|
|
26
|
+
return ValarResponse(False, '密码错误', 'warning', status=400)
|
|
27
|
+
else:
|
|
28
|
+
return ValarResponse(False, f"{username}已被占用", 'warning', status=400)
|
|
29
|
+
else:
|
|
30
|
+
if signin:
|
|
31
|
+
return ValarResponse(False, f"{username}不存在", 'warning', status=400)
|
|
32
|
+
else:
|
|
33
|
+
if username == 'admin' and password != settings.SECRET_KEY:
|
|
34
|
+
return ValarResponse(False, "请输入正确的admin密码", 'warning', status=400)
|
|
35
|
+
else:
|
|
36
|
+
account = dao.save_one({
|
|
37
|
+
"username": username,
|
|
38
|
+
"email": email,
|
|
39
|
+
"password": make_password(password),
|
|
40
|
+
"is_admin": username == 'admin'
|
|
41
|
+
})
|
|
42
|
+
return ValarResponse(jwt.encode({
|
|
43
|
+
"user_id": account.id,
|
|
44
|
+
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30)
|
|
45
|
+
}, settings.SECRET_KEY, algorithm="HS256"))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def free_menus(request):
|
|
49
|
+
body = json.loads(request.body)
|
|
50
|
+
scope = body.get("scope", 'admin')
|
|
51
|
+
menus = Menu.objects.filter(isLeaf=True, scope=scope, is_admin=False, path__isnull=False,
|
|
52
|
+
roles__isnull=True).values('path')
|
|
53
|
+
payload = {
|
|
54
|
+
"permissions": [m['path'] for m in menus]
|
|
55
|
+
}
|
|
56
|
+
token = jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")
|
|
57
|
+
return ValarResponse(token)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@auth_required
|
|
61
|
+
def user_profile(request):
|
|
62
|
+
body = json.loads(request.body)
|
|
63
|
+
scope = body.get("scope", 'admin')
|
|
64
|
+
account_id = request.user_id
|
|
65
|
+
account = Account.objects.filter(id=account_id).first()
|
|
66
|
+
if account is None:
|
|
67
|
+
return ValarResponse(False, f"账户已不存在", 'warning', status=401)
|
|
68
|
+
user_admin = account.is_admin
|
|
69
|
+
user_roles = list(account.roles.values('id', 'name'))
|
|
70
|
+
user_role_keys = [r['id'] for r in user_roles]
|
|
71
|
+
permissions = []
|
|
72
|
+
for m in Menu.objects.filter(isLeaf=True, scope=scope):
|
|
73
|
+
_id = m.path
|
|
74
|
+
if _id is None:
|
|
75
|
+
continue
|
|
76
|
+
if user_admin:
|
|
77
|
+
permissions.append(_id)
|
|
78
|
+
else:
|
|
79
|
+
if not m.is_admin:
|
|
80
|
+
menu_role_keys = [r['id'] for r in m.roles.all().values('id')]
|
|
81
|
+
if len(menu_role_keys):
|
|
82
|
+
if bool(set(user_role_keys) & set(menu_role_keys)):
|
|
83
|
+
permissions.append(_id)
|
|
84
|
+
else:
|
|
85
|
+
permissions.append(_id)
|
|
86
|
+
payload = {
|
|
87
|
+
"username": account.username,
|
|
88
|
+
"roles": user_roles,
|
|
89
|
+
"is_admin": user_admin,
|
|
90
|
+
"email": account.email,
|
|
91
|
+
"is_active": account.is_active,
|
|
92
|
+
"temporary": account.token is not None,
|
|
93
|
+
"permissions": permissions
|
|
94
|
+
}
|
|
95
|
+
print(permissions)
|
|
96
|
+
field = (
|
|
97
|
+
next((field for field in account.get_meta().get_fields()
|
|
98
|
+
if type(field) == OneToOneRel
|
|
99
|
+
and issubclass(field.related_model, AbstractUser)), None))
|
|
100
|
+
if field:
|
|
101
|
+
accessor = field.get_accessor_name()
|
|
102
|
+
model = field.related_model
|
|
103
|
+
entity = f'{model._meta.app_label}.{model.__name__}'
|
|
104
|
+
payload.update({'user_entity': entity, 'user_accessor': accessor})
|
|
105
|
+
if hasattr(account, accessor):
|
|
106
|
+
user = getattr(account, accessor)
|
|
107
|
+
name = getattr(user, 'name')
|
|
108
|
+
payload.update({'name': name})
|
|
109
|
+
token = jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")
|
|
110
|
+
return ValarResponse(token)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import secrets
|
|
3
|
+
from django.contrib.auth.hashers import check_password, make_password
|
|
4
|
+
|
|
5
|
+
from ..auth.Authentication import auth_required
|
|
6
|
+
from ..classes.valar_response import ValarResponse
|
|
7
|
+
|
|
8
|
+
from ..dao.engine import ValarEngine
|
|
9
|
+
from ..models.auth import Account, AbstractUser
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def send_password(request):
|
|
13
|
+
body = json.loads(request.body)
|
|
14
|
+
account_id = body.get('account_id')
|
|
15
|
+
account = Account.objects.filter(id=account_id).first()
|
|
16
|
+
return __send__(account)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def retrieve_password(request):
|
|
20
|
+
includes = json.loads(request.body)
|
|
21
|
+
account = Account.objects.filter(**includes).first()
|
|
22
|
+
account.token = account.token or secrets.token_hex(16)
|
|
23
|
+
account.save()
|
|
24
|
+
if account:
|
|
25
|
+
return __send__(account)
|
|
26
|
+
else:
|
|
27
|
+
return ValarResponse(False, '该邮箱未在系统中注册', code='error')
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@auth_required
|
|
31
|
+
def change_password(request):
|
|
32
|
+
body = json.loads(request.body)
|
|
33
|
+
old_password = body.get('old_password')
|
|
34
|
+
new_password = body.get('new_password')
|
|
35
|
+
account_id = request.user_id
|
|
36
|
+
account = Account.objects.get(id=account_id)
|
|
37
|
+
if not account:
|
|
38
|
+
return ValarResponse(False, '账户不存在', code='error')
|
|
39
|
+
if not account.is_auth(old_password):
|
|
40
|
+
return ValarResponse(False, '当前密码错误', code='error')
|
|
41
|
+
if old_password == new_password:
|
|
42
|
+
return ValarResponse(False, '新密码不能与当前密码一致', code='error')
|
|
43
|
+
account.password = make_password(new_password)
|
|
44
|
+
account.token = None
|
|
45
|
+
account.save()
|
|
46
|
+
return ValarResponse(True)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def __send__(account):
|
|
50
|
+
if not account:
|
|
51
|
+
return ValarResponse(False, '账户不存在', code='error')
|
|
52
|
+
token = account.token
|
|
53
|
+
if not token:
|
|
54
|
+
return ValarResponse(False, '未生成临时密码', code='error')
|
|
55
|
+
email = account.email
|
|
56
|
+
if not email:
|
|
57
|
+
return ValarResponse(False, '该账户未登记邮箱信息', code='error')
|
|
58
|
+
content = f'Your temporary password is {token}, please change it as soon as possible.'
|
|
59
|
+
ValarEngine().send_email('Retrieve Password', content, email)
|
|
60
|
+
return ValarResponse(True, '临时密码发送成功', 'success')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: valar
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Summary: valar for morghulis
|
|
5
5
|
Home-page: https://gitee.com/GRIFFIN120/valar_dev
|
|
6
6
|
Author: LYP
|
|
@@ -16,6 +16,7 @@ Requires-Dist: pandas==2.2.3
|
|
|
16
16
|
Requires-Dist: openpyxl==3.1.5
|
|
17
17
|
Requires-Dist: deepmerge~=2.0
|
|
18
18
|
Requires-Dist: minio==7.2.2
|
|
19
|
+
Requires-Dist: PyJWT~=2.10.1
|
|
19
20
|
Dynamic: author
|
|
20
21
|
Dynamic: author-email
|
|
21
22
|
Dynamic: description
|
|
@@ -106,6 +107,15 @@ MONGO_URI = 'mongodb://username:password@host:27017'
|
|
|
106
107
|
MINIO_URL = "s3://username:password@host:9000"
|
|
107
108
|
MINIO_ROOT = "https://host:9001"
|
|
108
109
|
|
|
110
|
+
""" Email Options """
|
|
111
|
+
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
|
112
|
+
EMAIL_HOST = 'xxx.xx.xx.xx'
|
|
113
|
+
EMAIL_PORT = 465
|
|
114
|
+
EMAIL_DOMAIN = 'http://xx.xx.xx'
|
|
115
|
+
EMAIL_HOST_USER = "xxx@xx.xx.xx"
|
|
116
|
+
EMAIL_HOST_PASSWORD = '*******'
|
|
117
|
+
EMAIL_USE_TLS = False
|
|
118
|
+
EMAIL_USE_SSL = True
|
|
109
119
|
```
|
|
110
120
|
|
|
111
121
|
## 2.2 asgi.py
|
|
@@ -199,3 +209,18 @@ channel_mapping = {
|
|
|
199
209
|
}
|
|
200
210
|
|
|
201
211
|
```
|
|
212
|
+
|
|
213
|
+
# 5. create an orm model extends the AbstractUser class, enable the authentication of Valar
|
|
214
|
+
|
|
215
|
+
```pycon
|
|
216
|
+
|
|
217
|
+
class User(AbstractUser):
|
|
218
|
+
"""
|
|
219
|
+
any fields you want
|
|
220
|
+
"""
|
|
221
|
+
description = models.TextField(null=True, verbose_name='备注')
|
|
222
|
+
|
|
223
|
+
class Meta:
|
|
224
|
+
verbose_name = 'User'
|
|
225
|
+
|
|
226
|
+
```
|
|
@@ -9,6 +9,9 @@ src/valar.egg-info/SOURCES.txt
|
|
|
9
9
|
src/valar.egg-info/dependency_links.txt
|
|
10
10
|
src/valar.egg-info/requires.txt
|
|
11
11
|
src/valar.egg-info/top_level.txt
|
|
12
|
+
src/valar/auth/Authentication.py
|
|
13
|
+
src/valar/auth/Middleware.py
|
|
14
|
+
src/valar/auth/__init__.py
|
|
12
15
|
src/valar/channels/__init__.py
|
|
13
16
|
src/valar/channels/consumer.py
|
|
14
17
|
src/valar/channels/counter.py
|
|
@@ -37,14 +40,18 @@ src/valar/dao/defaults/__init__.py
|
|
|
37
40
|
src/valar/dao/defaults/field_keys_default.py
|
|
38
41
|
src/valar/dao/defaults/field_values_default.py
|
|
39
42
|
src/valar/dao/defaults/view_defaults.py
|
|
43
|
+
src/valar/migrations/0001_initial.py
|
|
40
44
|
src/valar/migrations/__init__.py
|
|
41
45
|
src/valar/models/__init__.py
|
|
46
|
+
src/valar/models/auth.py
|
|
42
47
|
src/valar/models/core.py
|
|
43
48
|
src/valar/models/frame.py
|
|
44
49
|
src/valar/models/meta.py
|
|
45
50
|
src/valar/models/test.py
|
|
46
51
|
src/valar/views/__init__.py
|
|
52
|
+
src/valar/views/auth.py
|
|
47
53
|
src/valar/views/file.py
|
|
48
54
|
src/valar/views/handler.py
|
|
49
55
|
src/valar/views/meta.py
|
|
56
|
+
src/valar/views/password.py
|
|
50
57
|
src/valar/views/rest.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from . import core, frame, meta, test
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|