reyserver 1.1.49__py3-none-any.whl → 1.1.50__py3-none-any.whl
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.
- reyserver/rauth.py +94 -72
- reyserver/rfile.py +35 -32
- reyserver/rserver.py +28 -7
- {reyserver-1.1.49.dist-info → reyserver-1.1.50.dist-info}/METADATA +1 -1
- reyserver-1.1.50.dist-info/RECORD +11 -0
- reyserver-1.1.49.dist-info/RECORD +0 -11
- {reyserver-1.1.49.dist-info → reyserver-1.1.50.dist-info}/WHEEL +0 -0
- {reyserver-1.1.49.dist-info → reyserver-1.1.50.dist-info}/licenses/LICENSE +0 -0
reyserver/rauth.py
CHANGED
@@ -10,16 +10,21 @@
|
|
10
10
|
|
11
11
|
|
12
12
|
from fastapi import APIRouter
|
13
|
-
from reydb import rorm
|
13
|
+
from reydb import rorm, DatabaseEngine, DatabaseEngineAsync
|
14
|
+
from reykit.rdata import encode_jwt, decode_jwt, hash_bcrypt, is_hash_bcrypt
|
15
|
+
from reykit.rtime import now
|
14
16
|
|
15
17
|
from .rbase import ServerConfig, Bind, exit_api
|
16
18
|
|
17
19
|
|
18
20
|
__all__ = (
|
19
|
-
'
|
20
|
-
'
|
21
|
-
'
|
22
|
-
'
|
21
|
+
'DatabaseORMTableUser',
|
22
|
+
'DatabaseORMTableRole',
|
23
|
+
'DatabaseORMTablePerm',
|
24
|
+
'DatabaseORMTableUserRole',
|
25
|
+
'DatabaseORMTableRolePerm',
|
26
|
+
'build_auth_db',
|
27
|
+
'auth_router'
|
23
28
|
)
|
24
29
|
|
25
30
|
|
@@ -34,10 +39,10 @@ class DatabaseORMTableUser(rorm.Model, table=True):
|
|
34
39
|
update_time: rorm.Datetime = rorm.Field(field_default=':update_time', not_null=True, index_n=True, comment='Record update time.')
|
35
40
|
user_id: int = rorm.Field(rorm.types_mysql.MEDIUMINT(unsigned=True), key_auto=True, comment='User ID.')
|
36
41
|
name: str = rorm.Field(rorm.types.VARCHAR(50), not_null=True, index_u=True, comment='User name.')
|
37
|
-
password: str
|
38
|
-
email: rorm.Email
|
39
|
-
phone:
|
40
|
-
|
42
|
+
password: str = rorm.Field(rorm.types.CHAR(60), not_null=True, comment='User password, encrypted with "bcrypt".')
|
43
|
+
email: rorm.Email = rorm.Field(rorm.types.VARCHAR(255), index_u=True, comment='User email.')
|
44
|
+
phone: str = rorm.Field(rorm.types.CHAR(11), index_u=True, comment='User phone.')
|
45
|
+
avatar: int = rorm.Field(rorm.types_mysql.MEDIUMINT(unsigned=True), comment='User avatar file ID.')
|
41
46
|
is_valid: bool = rorm.Field(rorm.types_mysql.TINYINT(unsigned=True), field_default='1', not_null=True, comment='Is the valid.')
|
42
47
|
|
43
48
|
|
@@ -61,13 +66,14 @@ class DatabaseORMTablePerm(rorm.Model, table=True):
|
|
61
66
|
"""
|
62
67
|
|
63
68
|
__name__ = 'perm'
|
64
|
-
__comment__ = '
|
69
|
+
__comment__ = 'API permission information table.'
|
65
70
|
create_time: rorm.Datetime = rorm.Field(field_default=':create_time', not_null=True, index_n=True, comment='Record create time.')
|
66
71
|
update_time: rorm.Datetime = rorm.Field(field_default=':update_time', not_null=True, index_n=True, comment='Record update time.')
|
67
72
|
perm_id: int = rorm.Field(rorm.types_mysql.SMALLINT(unsigned=True), key_auto=True, comment='Permission ID.')
|
68
73
|
name: str = rorm.Field(rorm.types.VARCHAR(50), not_null=True, index_u=True, comment='Permission name.')
|
69
74
|
desc: str = rorm.Field(rorm.types.VARCHAR(500), comment='Permission description.')
|
70
|
-
|
75
|
+
method: str = rorm.Field(rorm.types.VARCHAR(7), comment='API request method regular expression "match" pattern.')
|
76
|
+
path: str = rorm.Field(rorm.types.VARCHAR(1000), comment='API resource path regular expression "match" pattern.')
|
71
77
|
|
72
78
|
|
73
79
|
class DatabaseORMTableUserRole(rorm.Model, table=True):
|
@@ -96,13 +102,16 @@ class DatabaseORMTableRolePerm(rorm.Model, table=True):
|
|
96
102
|
perm_id: int = rorm.Field(rorm.types_mysql.SMALLINT(unsigned=True), key=True, comment='Permission ID.')
|
97
103
|
|
98
104
|
|
99
|
-
def
|
105
|
+
def build_auth_db(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
|
100
106
|
"""
|
101
|
-
Check and build `
|
107
|
+
Check and build `auth` database tables.
|
108
|
+
|
109
|
+
Parameters
|
110
|
+
----------
|
111
|
+
db : Database engine instance.
|
102
112
|
"""
|
103
113
|
|
104
114
|
# Set parameter.
|
105
|
-
engine = ServerConfig.server.db.file
|
106
115
|
database = engine.database
|
107
116
|
|
108
117
|
## Table.
|
@@ -120,110 +129,123 @@ def build_file_db() -> None:
|
|
120
129
|
'path': 'stats',
|
121
130
|
'items': [
|
122
131
|
{
|
123
|
-
'name': '
|
132
|
+
'name': 'user_count',
|
124
133
|
'select': (
|
125
134
|
'SELECT COUNT(1)\n'
|
126
|
-
f'FROM `{database}`.`
|
135
|
+
f'FROM `{database}`.`user`'
|
127
136
|
),
|
128
|
-
'comment': '
|
137
|
+
'comment': 'User information count.'
|
129
138
|
},
|
130
139
|
{
|
131
|
-
'name': '
|
140
|
+
'name': 'role_count',
|
132
141
|
'select': (
|
133
142
|
'SELECT COUNT(1)\n'
|
134
|
-
f'FROM `{database}`.`
|
135
|
-
'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) = 0'
|
143
|
+
f'FROM `{database}`.`role`'
|
136
144
|
),
|
137
|
-
'comment': '
|
145
|
+
'comment': 'Role information count.'
|
138
146
|
},
|
139
147
|
{
|
140
|
-
'name': '
|
148
|
+
'name': 'perm_count',
|
141
149
|
'select': (
|
142
150
|
'SELECT COUNT(1)\n'
|
143
|
-
f'FROM `{database}`.`
|
144
|
-
'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) <= 6'
|
151
|
+
f'FROM `{database}`.`perm`'
|
145
152
|
),
|
146
|
-
'comment': '
|
153
|
+
'comment': 'Permission information count.'
|
147
154
|
},
|
148
155
|
{
|
149
|
-
'name': '
|
156
|
+
'name': 'user_day_count',
|
150
157
|
'select': (
|
151
158
|
'SELECT COUNT(1)\n'
|
152
|
-
f'FROM `{database}`.`
|
153
|
-
'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW())
|
159
|
+
f'FROM `{database}`.`user`\n'
|
160
|
+
'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) = 0'
|
154
161
|
),
|
155
|
-
'comment': '
|
162
|
+
'comment': 'User information count in the past day.'
|
156
163
|
},
|
157
164
|
{
|
158
|
-
'name': '
|
165
|
+
'name': 'user_week_count',
|
159
166
|
'select': (
|
160
167
|
'SELECT COUNT(1)\n'
|
161
|
-
f'FROM `{database}`.`
|
162
|
-
|
163
|
-
'comment': 'File data unique count.'
|
164
|
-
},
|
165
|
-
{
|
166
|
-
'name': 'total_size',
|
167
|
-
'select': (
|
168
|
-
'SELECT FORMAT(SUM(`size`), 0)\n'
|
169
|
-
f'FROM `{database}`.`data`'
|
170
|
-
),
|
171
|
-
'comment': 'File total byte size.'
|
172
|
-
},
|
173
|
-
{
|
174
|
-
'name': 'avg_size',
|
175
|
-
'select': (
|
176
|
-
'SELECT FORMAT(AVG(`size`), 0)\n'
|
177
|
-
f'FROM `{database}`.`data`'
|
168
|
+
f'FROM `{database}`.`user`\n'
|
169
|
+
'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) <= 6'
|
178
170
|
),
|
179
|
-
'comment': '
|
171
|
+
'comment': 'User information count in the past week.'
|
180
172
|
},
|
181
173
|
{
|
182
|
-
'name': '
|
174
|
+
'name': 'user_month_count',
|
183
175
|
'select': (
|
184
|
-
'SELECT
|
185
|
-
f'FROM `{database}`.`
|
176
|
+
'SELECT COUNT(1)\n'
|
177
|
+
f'FROM `{database}`.`user`\n'
|
178
|
+
'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) <= 29'
|
186
179
|
),
|
187
|
-
'comment': '
|
180
|
+
'comment': 'User information count in the past month.'
|
188
181
|
},
|
189
182
|
{
|
190
|
-
'name': '
|
183
|
+
'name': 'user_last_time',
|
191
184
|
'select': (
|
192
185
|
'SELECT MAX(`create_time`)\n'
|
193
|
-
f'FROM `{database}`.`
|
186
|
+
f'FROM `{database}`.`user`'
|
194
187
|
),
|
195
|
-
'comment': '
|
188
|
+
'comment': 'User last record create time.'
|
196
189
|
}
|
197
190
|
]
|
198
191
|
}
|
199
192
|
]
|
200
193
|
|
201
194
|
# Build.
|
202
|
-
engine.sync_engine.build.build(tables=tables,
|
195
|
+
engine.sync_engine.build.build(tables=tables, views_stats=views_stats, skip=True)
|
203
196
|
|
204
197
|
|
205
|
-
|
206
|
-
|
207
|
-
|
198
|
+
auth_router = APIRouter()
|
199
|
+
depend_auth_sess = Bind.create_depend_db('auth', 'sess')
|
200
|
+
depend_auth_conn = Bind.create_depend_db('auth', 'conn')
|
208
201
|
|
209
202
|
|
210
|
-
@
|
211
|
-
async def
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
) -> DatabaseORMTableInfo:
|
203
|
+
@auth_router.post('/sessions')
|
204
|
+
async def create_sessions(
|
205
|
+
username: str = Bind.body,
|
206
|
+
password: str = Bind.body,
|
207
|
+
conn: Bind.Conn = depend_auth_conn
|
208
|
+
) -> dict:
|
217
209
|
"""
|
218
|
-
|
210
|
+
Create session.
|
219
211
|
|
220
212
|
Parameters
|
221
213
|
----------
|
222
|
-
|
223
|
-
|
224
|
-
note : File note.
|
214
|
+
username : User name.
|
215
|
+
password : User password.
|
225
216
|
|
226
217
|
Returns
|
227
218
|
-------
|
228
|
-
|
229
|
-
"""
|
219
|
+
JSON with `token`.
|
220
|
+
"""
|
221
|
+
|
222
|
+
# Parameter.
|
223
|
+
key = ServerConfig.server.api_auth_key
|
224
|
+
password_hash = hash_bcrypt(password)
|
225
|
+
|
226
|
+
# Check.
|
227
|
+
sql = (
|
228
|
+
''
|
229
|
+
)
|
230
|
+
result = await conn.execute(
|
231
|
+
sql,
|
232
|
+
username=username,
|
233
|
+
password_hash=password_hash
|
234
|
+
)
|
235
|
+
|
236
|
+
# Check.
|
237
|
+
if result.empty:
|
238
|
+
exit_api(401)
|
239
|
+
|
240
|
+
# Response.
|
241
|
+
json = {
|
242
|
+
'time': now('timestamp'),
|
243
|
+
'sub': username,
|
244
|
+
'iat': now('timestamp'),
|
245
|
+
'nbf': now('timestamp'),
|
246
|
+
'exp': now('timestamp') + 28800000
|
247
|
+
}
|
248
|
+
token = encode_jwt(json, key)
|
249
|
+
data = {'token': token}
|
250
|
+
|
251
|
+
return data
|
reyserver/rfile.py
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
|
12
12
|
from fastapi import APIRouter
|
13
13
|
from fastapi.responses import FileResponse
|
14
|
-
from reydb import rorm
|
14
|
+
from reydb import rorm, DatabaseEngine, DatabaseEngineAsync
|
15
15
|
from reykit.ros import FileStore, get_md5
|
16
16
|
|
17
17
|
from .rbase import ServerConfig, Bind, exit_api
|
@@ -51,13 +51,16 @@ class DatabaseORMTableData(rorm.Model, table=True):
|
|
51
51
|
path: str = rorm.Field(rorm.types.VARCHAR(4095), not_null=True, comment='File disk storage path.')
|
52
52
|
|
53
53
|
|
54
|
-
def build_file_db() -> None:
|
54
|
+
def build_file_db(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
|
55
55
|
"""
|
56
56
|
Check and build `file` database tables.
|
57
|
+
|
58
|
+
Parameters
|
59
|
+
----------
|
60
|
+
db : Database engine instance.
|
57
61
|
"""
|
58
62
|
|
59
63
|
# Set parameter.
|
60
|
-
engine = ServerConfig.server.db.file
|
61
64
|
database = engine.database
|
62
65
|
|
63
66
|
## Table.
|
@@ -179,7 +182,34 @@ depend_file_sess = Bind.create_depend_db('file', 'sess')
|
|
179
182
|
depend_file_conn = Bind.create_depend_db('file', 'conn')
|
180
183
|
|
181
184
|
|
182
|
-
@file_router.
|
185
|
+
@file_router.get('/files/{file_id}')
|
186
|
+
async def get_file_info(
|
187
|
+
file_id: int = Bind.path,
|
188
|
+
sess: Bind.Sess = depend_file_sess
|
189
|
+
) -> DatabaseORMTableInfo:
|
190
|
+
"""
|
191
|
+
Get file information.
|
192
|
+
|
193
|
+
Parameters
|
194
|
+
----------
|
195
|
+
file_id : File ID.
|
196
|
+
|
197
|
+
Returns
|
198
|
+
-------
|
199
|
+
File information.
|
200
|
+
"""
|
201
|
+
|
202
|
+
# Get.
|
203
|
+
table_info = await sess.get(DatabaseORMTableInfo, file_id)
|
204
|
+
|
205
|
+
# Check.
|
206
|
+
if table_info is None:
|
207
|
+
exit_api(404)
|
208
|
+
|
209
|
+
return table_info
|
210
|
+
|
211
|
+
|
212
|
+
@file_router.post('/files')
|
183
213
|
async def upload_file(
|
184
214
|
file: Bind.File = Bind.forms,
|
185
215
|
name: str = Bind.forms_n,
|
@@ -233,7 +263,7 @@ async def upload_file(
|
|
233
263
|
return table_info
|
234
264
|
|
235
265
|
|
236
|
-
@file_router.get('/{file_id}/download')
|
266
|
+
@file_router.get('/files/{file_id}/download')
|
237
267
|
async def download_file(
|
238
268
|
file_id: int = Bind.path,
|
239
269
|
conn: Bind.Conn = depend_file_conn
|
@@ -273,30 +303,3 @@ async def download_file(
|
|
273
303
|
response = FileResponse(file_path, filename=file_name)
|
274
304
|
|
275
305
|
return response
|
276
|
-
|
277
|
-
|
278
|
-
@file_router.get('/{file_id}')
|
279
|
-
async def get_file_info(
|
280
|
-
file_id: int = Bind.path,
|
281
|
-
sess: Bind.Sess = depend_file_sess
|
282
|
-
) -> DatabaseORMTableInfo:
|
283
|
-
"""
|
284
|
-
Get file information.
|
285
|
-
|
286
|
-
Parameters
|
287
|
-
----------
|
288
|
-
file_id : File ID.
|
289
|
-
|
290
|
-
Returns
|
291
|
-
-------
|
292
|
-
File information.
|
293
|
-
"""
|
294
|
-
|
295
|
-
# Get.
|
296
|
-
table_info = await sess.get(DatabaseORMTableInfo, file_id)
|
297
|
-
|
298
|
-
# Check.
|
299
|
-
if table_info is None:
|
300
|
-
exit_api(404)
|
301
|
-
|
302
|
-
return table_info
|
reyserver/rserver.py
CHANGED
@@ -19,7 +19,8 @@ from fastapi.staticfiles import StaticFiles
|
|
19
19
|
from fastapi.middleware.gzip import GZipMiddleware
|
20
20
|
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
|
21
21
|
from reydb import DatabaseAsync
|
22
|
-
from reykit.rbase import
|
22
|
+
from reykit.rbase import CoroutineFunctionSimple, Singleton, throw
|
23
|
+
from reykit.rrand import randchar
|
23
24
|
|
24
25
|
from .rbase import ServerBase, ServerConfig, Bind
|
25
26
|
|
@@ -102,6 +103,8 @@ class Server(ServerBase, Singleton):
|
|
102
103
|
self.__add_default_middleware()
|
103
104
|
|
104
105
|
# API.
|
106
|
+
self.api_auth_key: str
|
107
|
+
'Authentication API JWT encryption key.'
|
105
108
|
self.api_file_dir: str
|
106
109
|
'File API store directory path.'
|
107
110
|
|
@@ -187,13 +190,30 @@ class Server(ServerBase, Singleton):
|
|
187
190
|
setattr(self.app, key, value)
|
188
191
|
|
189
192
|
|
190
|
-
def add_api_auth(self):
|
193
|
+
def add_api_auth(self, key: str | None = None) -> None:
|
191
194
|
"""
|
192
|
-
Add
|
195
|
+
Add authentication API.
|
193
196
|
Note: must include database engine of `auth` name.
|
197
|
+
|
198
|
+
Parameters
|
199
|
+
----------
|
200
|
+
key : JWT encryption key.
|
201
|
+
- `None`: Random 32 length string.
|
194
202
|
"""
|
195
203
|
|
196
|
-
|
204
|
+
from .rauth import build_file_db, auth_router
|
205
|
+
|
206
|
+
# Parameter.
|
207
|
+
if key is None:
|
208
|
+
key = randchar(32)
|
209
|
+
|
210
|
+
# Build database.
|
211
|
+
engine = self.db.auth
|
212
|
+
build_file_db(engine)
|
213
|
+
|
214
|
+
# Add.
|
215
|
+
self.api_auth_key = key
|
216
|
+
self.app.include_router(auth_router, tags=['auth'])
|
197
217
|
|
198
218
|
|
199
219
|
def add_api_file(self, file_dir: str = 'file') -> None:
|
@@ -207,11 +227,12 @@ class Server(ServerBase, Singleton):
|
|
207
227
|
prefix : File API path prefix.
|
208
228
|
"""
|
209
229
|
|
210
|
-
from .rfile import
|
230
|
+
from .rfile import build_file_db, file_router
|
211
231
|
|
212
232
|
# Build database.
|
213
|
-
|
233
|
+
engine = self.db.file
|
234
|
+
build_file_db(engine)
|
214
235
|
|
215
236
|
# Add.
|
216
237
|
self.api_file_dir = file_dir
|
217
|
-
self.app.include_router(file_router,
|
238
|
+
self.app.include_router(file_router, tags=['file'])
|
@@ -0,0 +1,11 @@
|
|
1
|
+
reyserver/__init__.py,sha256=7GX64p7uI2eetJH9NJ-DTg-8iyQwOsGcviADFJCPxVA,373
|
2
|
+
reyserver/rall.py,sha256=riyDRTUsigco_Bee1H4aZFb8IgvjnxdX9qcnVb9i9mE,270
|
3
|
+
reyserver/rauth.py,sha256=xknFI0IEMJcDRTkmO8jb-4Pbl7QSnQOA8m87D7gU3MM,9228
|
4
|
+
reyserver/rbase.py,sha256=IUVkkNsLmQh-QRLX6qtbCjPZAbQAsxoe0goPLCxG9KA,5283
|
5
|
+
reyserver/rclient.py,sha256=pTJtn78jPKgFo5EoQwZRdM0cYHdCs7QUKqfl-jUBRgk,4220
|
6
|
+
reyserver/rfile.py,sha256=RpMeq0vRStop4RnyjKv4wjdXgJjKPOAKpwPAKpLt0hk,9000
|
7
|
+
reyserver/rserver.py,sha256=oOfAc0aM-C561k8mKhF8_gYDmF0Kn6hKyZ5ff6mzPCE,6498
|
8
|
+
reyserver-1.1.50.dist-info/METADATA,sha256=iVbZ7pE_k0IlyYw1bsuw5lJIziRJ2CvsRyM4ivSQRi8,1689
|
9
|
+
reyserver-1.1.50.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
10
|
+
reyserver-1.1.50.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
|
11
|
+
reyserver-1.1.50.dist-info/RECORD,,
|
@@ -1,11 +0,0 @@
|
|
1
|
-
reyserver/__init__.py,sha256=7GX64p7uI2eetJH9NJ-DTg-8iyQwOsGcviADFJCPxVA,373
|
2
|
-
reyserver/rall.py,sha256=riyDRTUsigco_Bee1H4aZFb8IgvjnxdX9qcnVb9i9mE,270
|
3
|
-
reyserver/rauth.py,sha256=VC6Gq2uoiQuON4pmSueZojucU4m2FTdPxLPhGgM4G0A,8390
|
4
|
-
reyserver/rbase.py,sha256=IUVkkNsLmQh-QRLX6qtbCjPZAbQAsxoe0goPLCxG9KA,5283
|
5
|
-
reyserver/rclient.py,sha256=pTJtn78jPKgFo5EoQwZRdM0cYHdCs7QUKqfl-jUBRgk,4220
|
6
|
-
reyserver/rfile.py,sha256=C_kuH9KQS6E5VFDmg_dvbEYfyWxa1hl64fjoh8BFiXQ,8874
|
7
|
-
reyserver/rserver.py,sha256=hqpemzJHO6xHy_7pO3cvvjnfy8Yfqy8HfyIq4sjk4Dc,5889
|
8
|
-
reyserver-1.1.49.dist-info/METADATA,sha256=LnFaa55uE9AkN95GWcvdVCPyIjQjw9S-xh3OE7QOLlo,1689
|
9
|
-
reyserver-1.1.49.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
10
|
-
reyserver-1.1.49.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
|
11
|
-
reyserver-1.1.49.dist-info/RECORD,,
|
File without changes
|
File without changes
|