reyserver 1.1.59__py3-none-any.whl → 1.1.61__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.

Potentially problematic release.


This version of reyserver might be problematic. Click here for more details.

reyserver/__init__.py CHANGED
@@ -9,7 +9,6 @@
9
9
 
10
10
  Modules
11
11
  -------
12
- radmin : Admin methods.
13
12
  rall : All methods.
14
13
  rauth : Authentication methods.
15
14
  rbase : Base methods.
reyserver/rall.py CHANGED
@@ -9,7 +9,6 @@
9
9
  """
10
10
 
11
11
 
12
- from .radmin import *
13
12
  from .rauth import *
14
13
  from .rbase import *
15
14
  from .rclient import *
reyserver/rauth.py CHANGED
@@ -14,7 +14,7 @@ from datetime import datetime as Datetime
14
14
  from re import PatternError
15
15
  from fastapi import APIRouter, Request
16
16
  from fastapi.params import Depends
17
- from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
17
+ from fastapi.security import OAuth2PasswordBearer
18
18
  from reydb import rorm, DatabaseEngine, DatabaseEngineAsync
19
19
  from reykit.rdata import encode_jwt, decode_jwt, is_hash_bcrypt
20
20
  from reykit.rre import search_batch
@@ -29,29 +29,29 @@ __all__ = (
29
29
  'DatabaseORMTablePerm',
30
30
  'DatabaseORMTableUserRole',
31
31
  'DatabaseORMTableRolePerm',
32
- 'build_auth_db',
33
- 'auth_router'
32
+ 'build_db_auth',
33
+ 'router_auth'
34
34
  )
35
35
 
36
36
 
37
37
  UserInfo = TypedDict(
38
- 'UserInfo',
39
- {
40
- 'create_time': float,
41
- 'udpate_time': float,
42
- 'user_id': int,
43
- 'user_name': str,
44
- 'role_names': list[str],
45
- 'perm_names': list[str],
46
- 'perm_apis': list[str],
47
- 'email': str | None,
48
- 'phone': str | None,
49
- 'avatar': int | None,
50
- 'password': NotRequired[str]
51
- }
52
- )
53
- Token = TypedDict(
54
- 'Token',
38
+ 'UserInfo',
39
+ {
40
+ 'create_time': float,
41
+ 'udpate_time': float,
42
+ 'user_id': int,
43
+ 'user_name': str,
44
+ 'role_names': list[str],
45
+ 'perm_names': list[str],
46
+ 'perm_apis': list[str],
47
+ 'email': str | None,
48
+ 'phone': str | None,
49
+ 'avatar': int | None,
50
+ 'password': NotRequired[str]
51
+ }
52
+ )
53
+ TokenInfo = TypedDict(
54
+ 'TokenInfo',
55
55
  {
56
56
  'sub': int,
57
57
  'iat': int,
@@ -60,6 +60,14 @@ Token = TypedDict(
60
60
  'user': UserInfo
61
61
  }
62
62
  )
63
+ Token = str
64
+ ResponseToken = TypedDict(
65
+ 'ResponseToken',
66
+ {
67
+ 'access_token': Token,
68
+ 'token_type': Literal['Bearer']
69
+ }
70
+ )
63
71
 
64
72
 
65
73
  class DatabaseORMTableUser(rorm.Table):
@@ -138,7 +146,7 @@ class DatabaseORMTableRolePerm(rorm.Table):
138
146
  perm_id: int = rorm.Field(rorm.types_mysql.SMALLINT(unsigned=True), key=True, comment='Permission ID.')
139
147
 
140
148
 
141
- def build_auth_db(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
149
+ def build_db_auth(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
142
150
  """
143
151
  Check and build `auth` database tables.
144
152
 
@@ -231,7 +239,69 @@ def build_auth_db(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
231
239
  engine.sync_engine.build.build(tables=tables, views_stats=views_stats, skip=True)
232
240
 
233
241
 
234
- auth_router = APIRouter()
242
+ bearer = OAuth2PasswordBearer(
243
+ tokenUrl='/token',
244
+ scheme_name='RBACBearer',
245
+ description='Global authentication of based on RBAC model and Bearer framework.',
246
+ auto_error=False
247
+ )
248
+
249
+
250
+ async def depend_auth(
251
+ request: Request,
252
+ server: Bind.Server = Bind.server,
253
+ token: Token | None = Depends(bearer)
254
+ ) -> UserInfo:
255
+ """
256
+ Dependencie function of authentication.
257
+ If the verification fails, then response status code is 401 or 403.
258
+
259
+ Parameters
260
+ ----------
261
+ request : Request.
262
+ server : Server.
263
+ token : Authentication token.
264
+
265
+ Returns
266
+ -------
267
+ User information
268
+ """
269
+
270
+ # Check.
271
+ if not server.is_started_auth:
272
+ return
273
+ if bearer is None:
274
+ exit_api(401)
275
+
276
+ # Parameter.
277
+ key = server.api_auth_key
278
+ api_path = f'{request.method} {request.url.path}'
279
+
280
+ # Cache.
281
+ user: UserInfo | None = getattr(request.state, 'user', None)
282
+
283
+ # Decode.
284
+ if user is None:
285
+ json: TokenInfo | None = decode_jwt(token, key)
286
+ if json is None:
287
+ exit_api(401)
288
+ user = json['user']
289
+ request.state.user = user
290
+
291
+ # Authentication.
292
+ perm_apis = json['user']['perm_apis']
293
+ perm_apis = [
294
+ f'^{pattern}$'
295
+ for pattern in perm_apis
296
+ ]
297
+ result = search_batch(api_path, *perm_apis)
298
+ if result is None:
299
+ exit_api(403)
300
+
301
+ return user
302
+
303
+
304
+ router_auth = APIRouter()
235
305
 
236
306
 
237
307
  async def get_user_info(
@@ -336,25 +406,20 @@ async def get_user_info(
336
406
  return info
337
407
 
338
408
 
339
- @auth_router.post('/sessions')
409
+ @router_auth.post('/token')
340
410
  async def create_sessions(
341
- account: str = Bind.i.body,
342
- password: str = Bind.i.body,
343
- account_type: Literal['name', 'email', 'phone'] = Bind.Body('name'),
411
+ username: str = Bind.i.form,
412
+ password: str = Bind.i.form,
344
413
  conn: Bind.Conn = Bind.conn.auth,
345
414
  server: Bind.Server = Bind.server
346
- ) -> dict:
415
+ ) -> ResponseToken:
347
416
  """
348
417
  Create session.
349
418
 
350
419
  Parameters
351
420
  ----------
352
- account : User account.
421
+ username : User name.
353
422
  password : User password.
354
- account_type : User account type.
355
- - `Literal['name']`: User name.
356
- - `Literal['email']`: User Email.
357
- - `Literal['phone']`: User phone mumber.
358
423
 
359
424
  Returns
360
425
  -------
@@ -366,7 +431,7 @@ async def create_sessions(
366
431
  sess_seconds = server.api_auth_sess_seconds
367
432
 
368
433
  # User information.
369
- info = await get_user_info(conn, account, account_type)
434
+ info = await get_user_info(conn, username)
370
435
 
371
436
  # Check.
372
437
  if info is None:
@@ -375,10 +440,10 @@ async def create_sessions(
375
440
  if not is_hash_bcrypt(password, password_hash):
376
441
  exit_api(401)
377
442
 
378
- # JWT.
443
+ # Response.
379
444
  now_timestamp_s = now('timestamp_s')
380
445
  user_id = info.pop('user_id')
381
- data: Token = {
446
+ data: TokenInfo = {
382
447
  'sub': str(user_id),
383
448
  'iat': now_timestamp_s,
384
449
  'nbf': now_timestamp_s,
@@ -386,64 +451,9 @@ async def create_sessions(
386
451
  'user': info
387
452
  }
388
453
  token = encode_jwt(data, key)
389
- response = {'token': token}
454
+ response = {
455
+ 'access_token': token,
456
+ 'token_type': 'Bearer'
457
+ }
390
458
 
391
459
  return response
392
-
393
-
394
- bearer = HTTPBearer(
395
- scheme_name='RBACBearer',
396
- description='Global authentication of based on RBAC model and Bearer framework.',
397
- bearerFormat='JWT standard.',
398
- auto_error=False
399
- )
400
-
401
-
402
- async def depend_auth(
403
- request: Request,
404
- server: Bind.Server = Bind.server,
405
- auth: HTTPAuthorizationCredentials | None = Depends(bearer)
406
- ) -> None:
407
- """
408
- Dependencie function of authentication.
409
-
410
- Parameters
411
- ----------
412
- request : Request.
413
- server : Server.
414
- auth : Authentication.
415
- """
416
-
417
- # Check.
418
- api_path = f'{request.method} {request.url.path}'
419
- if (
420
- not server.is_started_auth
421
- or api_path == 'POST /sessions'
422
- ):
423
- return
424
- if auth is None:
425
- exit_api(401)
426
-
427
- # Parameter.
428
- key = server.api_auth_key
429
- token = auth.credentials
430
-
431
- # Decode.
432
- token = decode_jwt(token, key)
433
- if token is None:
434
- exit_api(401)
435
- token: Token
436
- request.state.token = token
437
-
438
- # Check.
439
- perm_apis = token['user']['perm_apis']
440
- perm_apis = [
441
- f'^{pattern}$'
442
- for pattern in perm_apis
443
- ]
444
- try:
445
- result = search_batch(api_path, *perm_apis)
446
- except PatternError:
447
- exit_api(403)
448
- if result is None:
449
- exit_api(403)
reyserver/rclient.py CHANGED
@@ -9,8 +9,10 @@
9
9
  """
10
10
 
11
11
 
12
- from typing import TypedDict, Literal
12
+ from typing import TypedDict
13
13
  from datetime import datetime as Datetime
14
+ from requests import Response
15
+ from reykit.rbase import copy_type_hints
14
16
  from reykit.ros import File, Folder, overload
15
17
  from reykit.rnet import join_url, request, get_response_file_name
16
18
 
@@ -31,33 +33,41 @@ class ServerClient(ServerBase):
31
33
  """
32
34
 
33
35
 
34
- def __init__(self, url: str) -> None:
36
+ def __init__(
37
+ self,
38
+ username: str,
39
+ password: str,
40
+ url: str = 'http://127.0.0.1:8000',
41
+ ) -> None:
35
42
  """
36
43
  Build instance attributes.
37
44
 
38
45
  Parameters
39
46
  ----------
47
+ username: User name.
48
+ password: User password.
40
49
  url : Server url.
41
50
  """
42
51
 
43
52
  # Build.
53
+ self.username = username
54
+ self.password = password
44
55
  self.url = url
56
+ self.token = self.get_token(username, password)
45
57
 
46
58
 
47
- def create_session(
59
+ def get_token(
48
60
  self,
49
- account: str,
50
- password: str,
51
- account_type: Literal['name', 'email', 'phone'] = 'name'
61
+ username: str,
62
+ password: str
52
63
  ) -> str:
53
64
  """
54
- Create session.
65
+ Get token.
55
66
 
56
67
  Parameters
57
68
  ----------
58
- account : User account, name or email or phone.
69
+ username : User name.
59
70
  password : User password.
60
- account_type : User account type.
61
71
 
62
72
  Returns
63
73
  -------
@@ -65,21 +75,59 @@ class ServerClient(ServerBase):
65
75
  """
66
76
 
67
77
  # Parameter.
68
- url = join_url(self.url, 'sessions')
69
- json = {
70
- 'account': account,
71
- 'password': password,
72
- 'account_type': account_type
78
+ url = join_url(self.url, 'token')
79
+ data = {
80
+ 'username': username,
81
+ 'password': password
73
82
  }
74
83
 
75
84
  # Request.
76
- response = request(url, json=json, check=True)
85
+ response = request(url, data=data, check=True)
77
86
  response_dict = response.json()
78
87
  token = response_dict['token']
79
88
 
80
89
  return token
81
90
 
82
91
 
92
+ def _request(self, *args, **kwargs) -> Response:
93
+ """
94
+ Send request.
95
+
96
+ Parameters
97
+ ----------
98
+ args : Position arguments.
99
+ kwargs : Keyword arguments.
100
+
101
+ Returns
102
+ -------
103
+ Response.
104
+ """
105
+
106
+ # Parameter.
107
+ headers = kwargs.setdefault('headers', {})
108
+ headers['Authorization'] = f'Bearer {self.token}'
109
+ kwargs['check'] = list(range(200, 400))
110
+ kwargs['check'].append(401)
111
+
112
+ # Request.
113
+ response = request(*args, **kwargs)
114
+
115
+ # Check.
116
+ if response.status_code != 401:
117
+ return response
118
+
119
+ # Try request.
120
+ self.token = self.create_session(self.username, self.password)
121
+ headers['Authorization'] = f'Bearer {self.token}'
122
+ kwargs['check'] = True
123
+ response = request(*args, **kwargs)
124
+
125
+ return response
126
+
127
+
128
+ request = copy_type_hints(_request, request)
129
+
130
+
83
131
  def upload_file(
84
132
  self,
85
133
  source: str | bytes,
@@ -128,7 +176,7 @@ class ServerClient(ServerBase):
128
176
  # Request.
129
177
  data = {'name': file_name, 'note': note}
130
178
  files = {'file': file_bytes}
131
- response = request(url, data=data, files=files, check=True)
179
+ response = self.request(url, data=data, files=files, check=True)
132
180
 
133
181
  ## Extract.
134
182
  response_json = response.json()
@@ -177,7 +225,7 @@ class ServerClient(ServerBase):
177
225
  url = join_url(self.url, 'files', file_id, 'download')
178
226
 
179
227
  # Request.
180
- response = request(url, check=True)
228
+ response = self.request(url, check=True)
181
229
  file_bytes = response.content
182
230
 
183
231
  # Not save.
@@ -215,7 +263,7 @@ class ServerClient(ServerBase):
215
263
  url = join_url(self.url, 'files', file_id)
216
264
 
217
265
  # Request.
218
- response = request(url, check=True)
266
+ response = self.request(url, check=True)
219
267
  response_dict = response.json()
220
268
 
221
269
  return response_dict
reyserver/rfile.py CHANGED
@@ -20,8 +20,8 @@ from .rbase import Bind, exit_api
20
20
  __all__ = (
21
21
  'DatabaseORMTableInfo',
22
22
  'DatabaseORMTableData',
23
- 'build_file_db',
24
- 'file_router'
23
+ 'build_db_file',
24
+ 'router_file'
25
25
  )
26
26
 
27
27
 
@@ -51,7 +51,7 @@ class DatabaseORMTableData(rorm.Table):
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(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
54
+ def build_db_file(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
55
55
  """
56
56
  Check and build `file` database tables.
57
57
 
@@ -177,10 +177,10 @@ def build_file_db(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
177
177
  engine.sync_engine.build.build(tables=tables, views=views, views_stats=views_stats, skip=True)
178
178
 
179
179
 
180
- file_router = APIRouter()
180
+ router_file = APIRouter()
181
181
 
182
182
 
183
- @file_router.get('/files/{file_id}')
183
+ @router_file.get('/files/{file_id}')
184
184
  async def get_file_info(
185
185
  file_id: int = Bind.i.path,
186
186
  sess: Bind.Sess = Bind.sess.file
@@ -207,7 +207,7 @@ async def get_file_info(
207
207
  return table_info
208
208
 
209
209
 
210
- @file_router.post('/files')
210
+ @router_file.post('/files')
211
211
  async def upload_file(
212
212
  file: Bind.File = Bind.i.forms,
213
213
  name: str = Bind.i.forms_n,
@@ -262,7 +262,7 @@ async def upload_file(
262
262
  return table_info
263
263
 
264
264
 
265
- @file_router.get('/files/{file_id}/download')
265
+ @router_file.get('/files/{file_id}/download')
266
266
  async def download_file(
267
267
  file_id: int = Bind.i.path,
268
268
  conn: Bind.Conn = Bind.conn.file
reyserver/rserver.py CHANGED
@@ -16,7 +16,6 @@ from contextlib import asynccontextmanager, _AsyncGeneratorContextManager
16
16
  from uvicorn import run as uvicorn_run
17
17
  from starlette.middleware.base import _StreamingResponse
18
18
  from fastapi import FastAPI, Request
19
- from fastapi.params import Depends
20
19
  from fastapi.staticfiles import StaticFiles
21
20
  from fastapi.middleware.gzip import GZipMiddleware
22
21
  from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
@@ -25,7 +24,6 @@ from reykit.rbase import CoroutineFunctionSimple, Singleton, throw
25
24
  from reykit.rrand import randchar
26
25
 
27
26
  from .rbase import ServerBase, Bind
28
- from . import radmin
29
27
 
30
28
 
31
29
  __all__ = (
@@ -69,8 +67,6 @@ class Server(ServerBase, Singleton):
69
67
  debug : Whether use development mode debug server.
70
68
  """
71
69
 
72
- from .rauth import depend_auth
73
-
74
70
  # Parameter.
75
71
  if type(ssl_cert) != type(ssl_key):
76
72
  throw(AssertionError, ssl_cert, ssl_key)
@@ -79,8 +75,6 @@ class Server(ServerBase, Singleton):
79
75
  elif iscoroutinefunction(depend):
80
76
  depend = (depend,)
81
77
  depend = [
82
- Bind.Depend(depend_auth)
83
- ] + [
84
78
  Bind.Depend(task)
85
79
  for task in depend
86
80
  ]
@@ -90,8 +84,6 @@ class Server(ServerBase, Singleton):
90
84
  self.db = db
91
85
  self.ssl_cert = ssl_cert
92
86
  self.ssl_key = ssl_key
93
-
94
- ## App.
95
87
  self.app = FastAPI(
96
88
  dependencies=depend,
97
89
  lifespan=lifespan,
@@ -99,17 +91,18 @@ class Server(ServerBase, Singleton):
99
91
  server=self
100
92
  )
101
93
 
94
+ # Public file.
102
95
  if public is not None:
103
96
  subapp = StaticFiles(directory=public, html=True)
104
97
  self.app.mount('/', subapp)
105
- self.wrap_middleware = self.app.middleware('http')
106
- 'Decorator, add middleware to APP.'
107
98
 
108
99
  # Middleware
100
+ self.wrap_middleware = self.app.middleware('http')
101
+ 'Decorator, add middleware to APP.'
109
102
  self.app.add_middleware(GZipMiddleware)
110
103
  if not debug:
111
104
  self.app.add_middleware(HTTPSRedirectMiddleware)
112
- self.__add_default_middleware()
105
+ self.__add_base_middleware()
113
106
 
114
107
  # API.
115
108
  self.is_started_auth: bool = False
@@ -185,19 +178,19 @@ class Server(ServerBase, Singleton):
185
178
  return lifespan
186
179
 
187
180
 
188
- def __add_default_middleware(self) -> None:
181
+ def __add_base_middleware(self) -> None:
189
182
  """
190
- Add default handle middleware.
183
+ Add base middleware.
191
184
  """
192
185
 
193
186
  # Add.
194
187
  @self.wrap_middleware
195
- async def middleware(
188
+ async def base_middleware(
196
189
  request: Request,
197
190
  call_next: Callable[[Request], Coroutine[None, None, _StreamingResponse]]
198
191
  ) -> _StreamingResponse:
199
192
  """
200
- Default handle middleware.
193
+ Base middleware.
201
194
 
202
195
  Parameters
203
196
  ----------
@@ -217,6 +210,8 @@ class Server(ServerBase, Singleton):
217
210
  and request.method == 'POST'
218
211
  ):
219
212
  response.status_code = 201
213
+ elif response.status_code == 401:
214
+ response.headers.setdefault('WWW-Authenticate', 'Bearer')
220
215
 
221
216
  return response
222
217
 
@@ -234,6 +229,9 @@ class Server(ServerBase, Singleton):
234
229
  )
235
230
 
236
231
 
232
+ __call__ = run
233
+
234
+
237
235
  def set_doc(
238
236
  self,
239
237
  version: str | None = None,
@@ -273,50 +271,30 @@ class Server(ServerBase, Singleton):
273
271
  """
274
272
  Add base API.
275
273
  """
276
- from fastapi import Request
277
- @self.app.get('/test')
278
- async def test(request: Request) -> str:
279
- return 'test'
280
274
 
275
+ from fastapi import Depends, Form
276
+ from time import time
281
277
 
282
- def add_api_admin(self) -> None:
283
- """
284
- Add admin API.
285
- """
286
-
287
- # Add.
288
- self.admin = radmin.ServerAdmin(self)
289
-
290
-
291
- def add_admin_model(
292
- self,
293
- model: Type['radmin.ServerAdminModel'] | type[rorm.Model],
294
- engine: DatabaseEngineAsync | str = None,
295
- label: str | None = None,
296
- name: str | None = None,
297
- column: str | Sequence[str] | None = None,
298
- **attrs: Any
299
- ) -> None:
300
- """
301
- Add admin model type.
302
-
303
- Parameters
304
- ----------
305
- model : Model type.
306
- - `type[rorm.Model]`: Define as `ServerAdminModel`.
307
- engine : Database engine or name.
308
- label : Admin model class label.
309
- name : Admin model name.
310
- column : Admin model display column names.
311
- attrs : Other admin model attributes.
312
- """
313
-
314
- # Check.
315
- if not hasattr(self, 'admin'):
316
- return
317
-
318
- # Add.
319
- self.admin.add_model(model, engine, label, name, column, **attrs)
278
+ form = Form()
279
+ def func():
280
+ return int(time())
281
+ depend = Depends(func)
282
+ @self.app.get('/test')
283
+ async def test(a: int = form, b: int = form) -> str:
284
+ print(a, b)
285
+ return 'test'
286
+ @self.app.get('/test1')
287
+ async def test1(a: int = depend, b: int = depend) -> str:
288
+ print(a, b)
289
+ return 'test'
290
+ @self.app.get('/test2')
291
+ async def test2(a: int = form, b: int = depend) -> str:
292
+ print(a, b)
293
+ return 'test'
294
+ @self.app.get('/test3')
295
+ async def test3(a: int = form, b: int = depend) -> str:
296
+ print(a, b)
297
+ return 'test'
320
298
 
321
299
 
322
300
  def add_api_auth(self, key: str | None = None, sess_seconds: int = 28800) -> None:
@@ -331,16 +309,7 @@ class Server(ServerBase, Singleton):
331
309
  sess_seconds : Session valid seconds.
332
310
  """
333
311
 
334
- from .rauth import (
335
- DatabaseORMTableUser,
336
- DatabaseORMTableRole,
337
- DatabaseORMTablePerm,
338
- DatabaseORMTableUserRole,
339
- DatabaseORMTableRolePerm,
340
- build_auth_db,
341
- auth_router,
342
- depend_auth
343
- )
312
+ from .rauth import build_db_auth, router_auth
344
313
 
345
314
  # Parameter.
346
315
  if key is None:
@@ -350,21 +319,14 @@ class Server(ServerBase, Singleton):
350
319
  if 'auth' not in self.db:
351
320
  throw(AssertionError, self.db)
352
321
  engine = self.db.auth
353
- build_auth_db(engine)
322
+ build_db_auth(engine)
354
323
 
355
324
  # Add.
356
325
  self.api_auth_key = key
357
326
  self.api_auth_sess_seconds = sess_seconds
358
- self.app.include_router(auth_router, tags=['auth'])
327
+ self.app.include_router(router_auth, tags=['auth'])
359
328
  self.is_started_auth = True
360
329
 
361
- ## Admin.
362
- self.add_admin_model(DatabaseORMTableUser, engine, category='auth', name='User', column_list=['user_id', 'name'])
363
- self.add_admin_model(DatabaseORMTableRole, engine, category='auth', name='Role', column_list=['role_id', 'name'])
364
- self.add_admin_model(DatabaseORMTablePerm, engine, category='auth', name='Perm', column_list=['perm_id', 'name'])
365
- self.add_admin_model(DatabaseORMTableUserRole, engine, category='auth', name='User Role')
366
- self.add_admin_model(DatabaseORMTableRolePerm, engine, category='auth', name='Role Perm')
367
-
368
330
 
369
331
  def add_api_file(self, file_dir: str = 'file') -> None:
370
332
  """
@@ -374,26 +336,17 @@ class Server(ServerBase, Singleton):
374
336
  Parameters
375
337
  ----------
376
338
  file_dir : File API store directory path.
377
- prefix : File API path prefix.
378
339
  """
379
340
 
380
- from .rfile import (
381
- build_file_db,
382
- file_router,
383
- DatabaseORMTableInfo,
384
- DatabaseORMTableData
385
- )
341
+ from .rauth import depend_auth
342
+ from .rfile import build_db_file, router_file
386
343
 
387
344
  # Database.
388
345
  if 'file' not in self.db:
389
346
  throw(AssertionError, self.db)
390
347
  engine = self.db.file
391
- build_file_db(engine)
348
+ build_db_file(engine)
392
349
 
393
350
  # Add.
394
351
  self.api_file_dir = file_dir
395
- self.app.include_router(file_router, tags=['file'])
396
-
397
- ## Admin.
398
- self.add_admin_model(DatabaseORMTableInfo, engine, category='file', name='Info', column_list=['file_id', 'name'])
399
- self.add_admin_model(DatabaseORMTableData, engine, category='file', name='Data')
352
+ self.app.include_router(router_file, tags=['file'])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reyserver
3
- Version: 1.1.59
3
+ Version: 1.1.61
4
4
  Summary: Backend server method set.
5
5
  Project-URL: homepage, https://github.com/reyxbo/reyserver/
6
6
  Author-email: Rey <reyxbo@163.com>
@@ -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=hXI21VrZ_sDrFZzd982ENymFJfPWi6U1XHmGmiglez0,15103
4
+ reyserver/rbase.py,sha256=kzSbo6yoPlWquR6XiseTKi5hEbllaG3qVmmwnnKKac0,6898
5
+ reyserver/rclient.py,sha256=a07zHDUH4Y3f-WdAXMX7Wq9PocwvlrDfdIl2s-M4IWo,6222
6
+ reyserver/rfile.py,sha256=6H5_7B9aiA3F56VToQDI9Trarkrl9gcIuFqYyCVCiCU,8877
7
+ reyserver/rserver.py,sha256=mrtsYu8wsxJmctmh2qWfwD9l70gOZq57UrKcDJ9EAVM,9856
8
+ reyserver-1.1.61.dist-info/METADATA,sha256=IghNj6ce-pWACkbexwa3jvtkMx0lc8hto2XGWhWtWyo,1666
9
+ reyserver-1.1.61.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ reyserver-1.1.61.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
11
+ reyserver-1.1.61.dist-info/RECORD,,
reyserver/radmin.py DELETED
@@ -1,128 +0,0 @@
1
- # !/usr/bin/env python
2
- # -*- coding: utf-8 -*-
3
-
4
- """
5
- @Time : 2025-10-14
6
- @Author : Rey
7
- @Contact : reyxbo@163.com
8
- @Explain : Admin methods.
9
- """
10
-
11
-
12
- from typing import Any, Type
13
- from collections.abc import Sequence
14
- from sqlalchemy.ext.asyncio import async_sessionmaker
15
- from sqladmin import Admin, ModelView
16
- from reydb import DatabaseEngineAsync, rorm
17
- from reykit.rbase import Singleton
18
-
19
- from .rbase import ServerBase
20
- from . import rserver
21
-
22
-
23
- __all__ = (
24
- 'ServerAdminModel',
25
- 'ServerAdminModelView',
26
- 'ServerAdminModelViewStats'
27
- )
28
-
29
-
30
- class ServerAdminModel(ModelView):
31
- """
32
- Server admin model type.
33
- """
34
-
35
-
36
- class ServerAdminModelView(ServerAdminModel):
37
- """
38
- Server admin view model type.
39
- """
40
-
41
- category = 'View'
42
- can_view_details = can_edit = can_create = can_delete = False
43
-
44
-
45
- class ServerAdminModelViewStats(ServerAdminModelView):
46
- """
47
- Server admin stats view model type.
48
- """
49
-
50
- column_list = ['item', 'value', 'comment']
51
-
52
-
53
- class ServerAdmin(ServerBase, Singleton):
54
- """
55
- Server admin type, singleton mode.
56
- """
57
-
58
-
59
- def __init__(self, server: 'rserver.Server') -> None:
60
- """
61
- Build instance attributes.
62
-
63
- Parameters
64
- ----------
65
- server : Server.
66
- """
67
-
68
- # Build.
69
- self.server = server
70
-
71
- ## Admin.
72
- self.api_admin_binds: dict[ServerAdminModel, DatabaseEngineAsync] = {}
73
- 'Admin API model bind database engine dictionary.'
74
- Session = async_sessionmaker()
75
- Session.configure(binds=self.api_admin_binds)
76
- self.admin = Admin(self.server.app, session_maker=Session)
77
-
78
-
79
- def add_model(
80
- self,
81
- model: Type[ServerAdminModel] | type[rorm.Model],
82
- engine: DatabaseEngineAsync | str = None,
83
- label: str | None = None,
84
- name: str | None = None,
85
- column: str | Sequence[str] | None = None,
86
- **attrs: Any
87
- ) -> None:
88
- """
89
- Add admin model type.
90
-
91
- Parameters
92
- ----------
93
- model : Model type.
94
- - `type[rorm.Model]`: Define as `ServerAdminModel`.
95
- engine : Database engine or name.
96
- label : Admin model class label.
97
- name : Admin model name.
98
- column : Admin model display column names.
99
- attrs : Other admin model attributes.
100
- """
101
-
102
- # Parameter.
103
- if issubclass(model, rorm.Model):
104
- class ServerAdminModel_(ServerAdminModel, model=model): ...
105
- model = ServerAdminModel_
106
- if type(engine) == str:
107
- engine = self.server.db[engine]
108
- if label is not None:
109
- model.category = label
110
- if name is not None:
111
- model.name = model.name_plural = name
112
- if type(column) == str:
113
- column = [column]
114
- if column is not None:
115
- model.column_list = list(column)
116
- for key, value in attrs.items():
117
- setattr(model, key, value)
118
-
119
- # Add.
120
- self.api_admin_binds[model.model] = engine.engine
121
- self.admin.add_view(model)
122
-
123
-
124
- # Simple path.
125
-
126
- ## Server admin model type.
127
- View = ServerAdminModelView
128
- ViewStats = ServerAdminModelViewStats
@@ -1,12 +0,0 @@
1
- reyserver/__init__.py,sha256=Oq-lOcQInzhgKt1_4OB2jNx0OO2d9qZg9iZqUx6YRr8,398
2
- reyserver/radmin.py,sha256=Hwy8QsiQOyK2YP7abcS22IbRKB7sgcDGPHQ2-mHtf-8,3269
3
- reyserver/rall.py,sha256=MI1NnqUpq22CK9w3XPPBS0K8lNuf0BnbI0pn4MGMx38,293
4
- reyserver/rauth.py,sha256=irnwf1_OSOjB9fB7VBWY5RvpetPX2ydivMt_teXmDVs,15079
5
- reyserver/rbase.py,sha256=kzSbo6yoPlWquR6XiseTKi5hEbllaG3qVmmwnnKKac0,6898
6
- reyserver/rclient.py,sha256=Ffm66YuWupzEtQ6RqEm2KCc6jSF8JeMB7Xq6pzRFZ6M,5073
7
- reyserver/rfile.py,sha256=0wS-ohsAOGcSCg07U66wpqJlxnCLWN6gkEQJK4qavWQ,8877
8
- reyserver/rserver.py,sha256=mjc-SeE2RZZwB5Z1nUSMo_VMign3VcftIK1Kv7XIne8,11484
9
- reyserver-1.1.59.dist-info/METADATA,sha256=oJNACNqqErBtx9jcDkkGRQoTyDaFT-T5tnfbEkVm-6E,1666
10
- reyserver-1.1.59.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- reyserver-1.1.59.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
12
- reyserver-1.1.59.dist-info/RECORD,,