reyserver 1.1.60__py3-none-any.whl → 1.1.62__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
@@ -11,10 +11,8 @@
11
11
 
12
12
  from typing import Any, TypedDict, NotRequired, Literal
13
13
  from datetime import datetime as Datetime
14
- from re import PatternError
15
14
  from fastapi import APIRouter, Request
16
- from fastapi.params import Depends
17
- from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
15
+ from fastapi.security import OAuth2PasswordBearer
18
16
  from reydb import rorm, DatabaseEngine, DatabaseEngineAsync
19
17
  from reykit.rdata import encode_jwt, decode_jwt, is_hash_bcrypt
20
18
  from reykit.rre import search_batch
@@ -29,35 +27,43 @@ __all__ = (
29
27
  'DatabaseORMTablePerm',
30
28
  'DatabaseORMTableUserRole',
31
29
  'DatabaseORMTableRolePerm',
32
- 'build_auth_db',
33
- 'auth_router'
30
+ 'build_db_auth',
31
+ 'router_auth'
34
32
  )
35
33
 
36
34
 
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',
35
+ UserData = TypedDict(
36
+ 'UserData',
37
+ {
38
+ 'create_time': float,
39
+ 'udpate_time': float,
40
+ 'user_id': int,
41
+ 'user_name': str,
42
+ 'role_names': list[str],
43
+ 'perm_names': list[str],
44
+ 'perm_apis': list[str],
45
+ 'email': str | None,
46
+ 'phone': str | None,
47
+ 'avatar': int | None,
48
+ 'password': NotRequired[str]
49
+ }
50
+ )
51
+ TokenData = TypedDict(
52
+ 'TokenData',
55
53
  {
56
54
  'sub': int,
57
55
  'iat': int,
58
56
  'nbf': int,
59
57
  'exp': int,
60
- 'user': UserInfo
58
+ 'user': UserData
59
+ }
60
+ )
61
+ Token = str
62
+ JSONToken = TypedDict(
63
+ 'JSONToken',
64
+ {
65
+ 'access_token': Token,
66
+ 'token_type': Literal['Bearer']
61
67
  }
62
68
  )
63
69
 
@@ -138,7 +144,7 @@ class DatabaseORMTableRolePerm(rorm.Table):
138
144
  perm_id: int = rorm.Field(rorm.types_mysql.SMALLINT(unsigned=True), key=True, comment='Permission ID.')
139
145
 
140
146
 
141
- def build_auth_db(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
147
+ def build_db_auth(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
142
148
  """
143
149
  Check and build `auth` database tables.
144
150
 
@@ -231,17 +237,80 @@ def build_auth_db(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
231
237
  engine.sync_engine.build.build(tables=tables, views_stats=views_stats, skip=True)
232
238
 
233
239
 
234
- auth_router = APIRouter()
240
+ bearer = OAuth2PasswordBearer(
241
+ tokenUrl='/token',
242
+ scheme_name='OAuth2Password',
243
+ description='Authentication of OAuth2 password model.',
244
+ auto_error=False
245
+ )
246
+
247
+
248
+ async def depend_token(
249
+ request: Request,
250
+ server: Bind.Server = Bind.server,
251
+ token: Token | None = Bind.Depend(bearer)
252
+ ) -> TokenData:
253
+ """
254
+ Dependencie function of authentication token.
255
+ If the verification fails, then response status code is 401 or 403.
256
+
257
+ Parameters
258
+ ----------
259
+ request : Request.
260
+ server : Server.
261
+ token : Authentication token.
262
+
263
+ Returns
264
+ -------
265
+ Token data.
266
+ """
267
+
268
+ # Check.
269
+ if not server.is_started_auth:
270
+ return
271
+ if bearer is None:
272
+ exit_api(401)
273
+
274
+ # Parameter.
275
+ key = server.api_auth_key
276
+ api_path = f'{request.method} {request.url.path}'
277
+
278
+ # Cache.
279
+ token_data: UserData | None = getattr(request.state, 'token_data', None)
280
+
281
+ # Decode.
282
+ if token_data is None:
283
+ token_data: TokenData | None = decode_jwt(token, key)
284
+ if token_data is None:
285
+ exit_api(401)
286
+ request.state.token_data = token_data
287
+
288
+ # Authentication.
289
+ perm_apis = token_data['user']['perm_apis']
290
+ perm_apis = [
291
+ f'^{pattern}$'
292
+ for pattern in perm_apis
293
+ ]
294
+ result = search_batch(api_path, *perm_apis)
295
+ if result is None:
296
+ exit_api(403)
297
+
298
+ return token_data
299
+
235
300
 
301
+ Bind.token = Bind.Depend(depend_token)
236
302
 
237
- async def get_user_info(
303
+ router_auth = APIRouter()
304
+
305
+
306
+ async def get_user_data(
238
307
  conn: Bind.Conn,
239
308
  account: str,
240
- account_type: Literal['name', 'email', 'phone'],
309
+ account_type: Literal['user_id', 'name', 'email', 'phone'] = 'name',
241
310
  filter_invalid: bool = True
242
- ) -> UserInfo | None:
311
+ ) -> UserData | None:
243
312
  """
244
- Get user information.
313
+ Get user data.
245
314
 
246
315
  Parameters
247
316
  ----------
@@ -255,7 +324,7 @@ async def get_user_info(
255
324
 
256
325
  Returns
257
326
  -------
258
- User information or null.
327
+ User data or null.
259
328
  """
260
329
 
261
330
  # Parameters.
@@ -286,6 +355,7 @@ async def get_user_info(
286
355
  ' SELECT `create_time`, `update_time`, `user_id`, `password`, `name`, `email`, `phone`, `avatar`\n'
287
356
  ' FROM `test`.`user`\n'
288
357
  f'{sql_where}'
358
+ ' LIMIT 1\n'
289
359
  ') as `user`\n'
290
360
  'LEFT JOIN (\n'
291
361
  ' SELECT `user_id`, `role_id`\n'
@@ -319,7 +389,7 @@ async def get_user_info(
319
389
  info = None
320
390
  else:
321
391
  row: dict[str, Datetime | Any] = result.to_row()
322
- info: UserInfo = {
392
+ info: UserData = {
323
393
  'create_time': row['create_time'].timestamp(),
324
394
  'udpate_time': row['update_time'].timestamp(),
325
395
  'user_id': row['user_id'],
@@ -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
+ ) -> JSONToken:
347
416
  """
348
- Create session.
417
+ Create token.
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
  -------
@@ -365,86 +430,30 @@ async def create_sessions(
365
430
  key = server.api_auth_key
366
431
  sess_seconds = server.api_auth_sess_seconds
367
432
 
368
- # User information.
369
- info = await get_user_info(conn, account, account_type)
433
+ # User data.
434
+ user_data = await get_user_data(conn, username)
370
435
 
371
436
  # Check.
372
- if info is None:
437
+ if user_data is None:
373
438
  exit_api(401)
374
- password_hash = info.pop('password')
439
+ password_hash = user_data.pop('password')
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
- user_id = info.pop('user_id')
381
- data: Token = {
445
+ user_id = user_data.pop('user_id')
446
+ data: TokenData = {
382
447
  'sub': str(user_id),
383
448
  'iat': now_timestamp_s,
384
449
  'nbf': now_timestamp_s,
385
450
  'exp': now_timestamp_s + sess_seconds,
386
- 'user': info
451
+ 'user': user_data
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
- print(11111111111111111)
419
- api_path = f'{request.method} {request.url.path}'
420
- if (
421
- not server.is_started_auth
422
- or api_path == 'POST /sessions'
423
- ):
424
- return
425
- if auth is None:
426
- exit_api(401)
427
-
428
- # Parameter.
429
- key = server.api_auth_key
430
- token = auth.credentials
431
-
432
- # Decode.
433
- json = decode_jwt(token, key)
434
- if json is None:
435
- exit_api(401)
436
- json: Token
437
- request.state.user = json['user']
438
-
439
- # Check.
440
- perm_apis = json['user']['perm_apis']
441
- perm_apis = [
442
- f'^{pattern}$'
443
- for pattern in perm_apis
444
- ]
445
- try:
446
- result = search_batch(api_path, *perm_apis)
447
- except PatternError:
448
- exit_api(403)
449
- if result is None:
450
- exit_api(403)
reyserver/rbase.py CHANGED
@@ -9,9 +9,9 @@
9
9
  """
10
10
 
11
11
 
12
- from typing import Type, NoReturn, overload
12
+ from typing import Literal, Type, NoReturn, overload
13
13
  from http import HTTPStatus
14
- from fastapi import FastAPI
14
+ from fastapi import APIRouter
15
15
  from fastapi import HTTPException, Request, UploadFile as File
16
16
  from fastapi.params import (
17
17
  Depends,
@@ -24,7 +24,7 @@ from fastapi.params import (
24
24
  File as Forms
25
25
  )
26
26
  from reydb.rconn import DatabaseConnectionAsync
27
- from reydb.rorm import DatabaseORMModel, DatabaseORMSessionAsync
27
+ from reydb.rorm import DatabaseORMSessionAsync
28
28
  from reykit.rbase import Base, Exit, StaticMeta, Singleton, throw
29
29
 
30
30
  from . import rserver
@@ -311,49 +311,82 @@ class ServerBindInstance(ServerBase, Singleton):
311
311
  return forms
312
312
 
313
313
 
314
- async def depend_server(request: Request) -> 'rserver.Server':
315
- """
316
- Dependencie function of now Server instance.
317
-
318
- Parameters
319
- ----------
320
- request : Request.
321
-
322
- Returns
323
- -------
324
- Server.
325
- """
326
-
327
- # Get.
328
- app: FastAPI = request.app
329
- server = app.extra['server']
330
-
331
- return server
332
-
333
-
334
314
  class ServerBind(ServerBase, metaclass=StaticMeta):
335
315
  """
336
316
  Server API bind parameter type.
337
317
  """
338
318
 
339
319
  Request = Request
320
+ 'Reqeust instance dependency type.'
340
321
  Path = Path
322
+ 'URL source path dependency type.'
341
323
  Query = Query
324
+ 'URL query parameter dependency type.'
342
325
  Header = Header
326
+ 'Request header parameter dependency type.'
343
327
  Cookie = Cookie
328
+ 'Request header cookie parameter dependency type.'
344
329
  Body = Body
330
+ 'Request body JSON parameter dependency type.'
345
331
  Form = Form
332
+ 'Request body form parameter dependency type.'
346
333
  Forms = Forms
334
+ 'Request body multiple forms parameter dependency type.'
347
335
  File = File
336
+ 'Verify file type.'
348
337
  Depend = Depends
349
- JSON = DatabaseORMModel
338
+ 'Dependency type.'
350
339
  Conn = DatabaseConnectionAsync
351
340
  Sess = DatabaseORMSessionAsync
352
341
  Server = Type['rserver.Server']
353
- server = Depends(depend_server)
342
+ 'Server type.'
343
+ server: Depend
344
+ 'Server instance dependency type.'
354
345
  i = ServerBindInstance()
346
+ 'Server API bind parameter build instance.'
355
347
  conn = ServerBindInstanceDatabaseConnection()
348
+ 'Server API bind parameter asynchronous database connection.'
356
349
  sess = ServerBindInstanceDatabaseSession()
350
+ 'Server API bind parameter asynchronous database session.'
351
+ token: Depend
352
+ 'Server authentication token dependency type.'
357
353
 
358
354
 
359
355
  Bind = ServerBind
356
+
357
+ router_base = APIRouter()
358
+
359
+
360
+ @router_base.get('/test')
361
+ def test() -> Literal['test']:
362
+ """
363
+ Test.
364
+
365
+ Returns
366
+ -------
367
+ Text `test`.
368
+ """
369
+
370
+ # Resposne.
371
+ response = 'test'
372
+
373
+ return response
374
+
375
+
376
+ @router_base.post('/test/echo')
377
+ def echo(data: dict = Bind.i.body) -> dict:
378
+ """
379
+ Echo test.
380
+
381
+ Paremeters
382
+ ----------
383
+ data : Echo data.
384
+
385
+ Returns
386
+ -------
387
+ Echo data.
388
+ """
389
+
390
+ # Resposne.
391
+
392
+ return data
reyserver/rclient.py CHANGED
@@ -53,22 +53,21 @@ class ServerClient(ServerBase):
53
53
  self.username = username
54
54
  self.password = password
55
55
  self.url = url
56
- self.token = self.create_session(username, password)
56
+ self.token = self.get_token(username, password)
57
57
 
58
58
 
59
- def create_session(
59
+ def get_token(
60
60
  self,
61
61
  username: str,
62
62
  password: str
63
63
  ) -> str:
64
64
  """
65
- Create session.
65
+ Get token.
66
66
 
67
67
  Parameters
68
68
  ----------
69
- account : User name.
69
+ username : User name.
70
70
  password : User password.
71
- account_type : User account type.
72
71
 
73
72
  Returns
74
73
  -------
@@ -76,16 +75,16 @@ class ServerClient(ServerBase):
76
75
  """
77
76
 
78
77
  # Parameter.
79
- url = join_url(self.url, 'sessions')
80
- json = {
81
- 'account': username,
78
+ url = join_url(self.url, 'token')
79
+ data = {
80
+ 'username': username,
82
81
  'password': password
83
82
  }
84
83
 
85
84
  # Request.
86
- response = request(url, json=json, check=True)
85
+ response = request(url, data=data, check=True)
87
86
  response_dict = response.json()
88
- token = response_dict['token']
87
+ token = response_dict['access_token']
89
88
 
90
89
  return token
91
90
 
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
@@ -9,23 +9,21 @@
9
9
  """
10
10
 
11
11
 
12
- from typing import Any, Literal, Type
12
+ from typing import Literal
13
13
  from collections.abc import Sequence, Callable, Coroutine
14
14
  from inspect import iscoroutinefunction
15
15
  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
23
- from reydb import rorm, DatabaseAsync, DatabaseEngineAsync
22
+ from reydb import DatabaseAsync
24
23
  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,11 @@ 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
-
281
274
 
282
- def add_api_admin(self) -> None:
283
- """
284
- Add admin API.
285
- """
275
+ from .rbase import router_base
286
276
 
287
277
  # 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
+ self.app.include_router(router_base, tags=['test'])
320
279
 
321
280
 
322
281
  def add_api_auth(self, key: str | None = None, sess_seconds: int = 28800) -> None:
@@ -331,16 +290,7 @@ class Server(ServerBase, Singleton):
331
290
  sess_seconds : Session valid seconds.
332
291
  """
333
292
 
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
- )
293
+ from .rauth import build_db_auth, router_auth
344
294
 
345
295
  # Parameter.
346
296
  if key is None:
@@ -350,21 +300,14 @@ class Server(ServerBase, Singleton):
350
300
  if 'auth' not in self.db:
351
301
  throw(AssertionError, self.db)
352
302
  engine = self.db.auth
353
- build_auth_db(engine)
303
+ build_db_auth(engine)
354
304
 
355
305
  # Add.
356
306
  self.api_auth_key = key
357
307
  self.api_auth_sess_seconds = sess_seconds
358
- self.app.include_router(auth_router, tags=['auth'])
308
+ self.app.include_router(router_auth, tags=['auth'])
359
309
  self.is_started_auth = True
360
310
 
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
311
 
369
312
  def add_api_file(self, file_dir: str = 'file') -> None:
370
313
  """
@@ -374,26 +317,40 @@ class Server(ServerBase, Singleton):
374
317
  Parameters
375
318
  ----------
376
319
  file_dir : File API store directory path.
377
- prefix : File API path prefix.
378
320
  """
379
321
 
380
- from .rfile import (
381
- build_file_db,
382
- file_router,
383
- DatabaseORMTableInfo,
384
- DatabaseORMTableData
385
- )
322
+ from .rfile import build_db_file, router_file
386
323
 
387
324
  # Database.
388
325
  if 'file' not in self.db:
389
326
  throw(AssertionError, self.db)
390
327
  engine = self.db.file
391
- build_file_db(engine)
328
+ build_db_file(engine)
392
329
 
393
330
  # Add.
394
331
  self.api_file_dir = file_dir
395
- self.app.include_router(file_router, tags=['file'])
332
+ self.app.include_router(router_file, tags=['file'], dependencies=(Bind.token,))
333
+
334
+
335
+ async def depend_server(request: Request) -> Server:
336
+ """
337
+ Dependencie function of now Server instance.
338
+
339
+ Parameters
340
+ ----------
341
+ request : Request.
342
+
343
+ Returns
344
+ -------
345
+ Server.
346
+ """
347
+
348
+ # Get.
349
+ app: FastAPI = request.app
350
+ server = app.extra['server']
351
+
352
+ return server
353
+
396
354
 
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')
355
+ Bind.Server = Server
356
+ Bind.server = Bind.Depend(depend_server)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reyserver
3
- Version: 1.1.60
3
+ Version: 1.1.62
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=JGo5lLTRGoP1SFPyQA-Q5EE-O3OU_86Mb3pvwhvFu-w,15125
4
+ reyserver/rbase.py,sha256=0ECJ1zuUpJhwaNc0u5GvR5VyGxIAnKQep-EdotwnLoE,7752
5
+ reyserver/rclient.py,sha256=og5YuWm-PODkFn9njBwYQpGlk0j1BfqFuEarlCFSQYI,6229
6
+ reyserver/rfile.py,sha256=6H5_7B9aiA3F56VToQDI9Trarkrl9gcIuFqYyCVCiCU,8877
7
+ reyserver/rserver.py,sha256=UQXzYKS-xdH1zRfLQIiZn253kqONWhBzZ4QGLu6GEoQ,9537
8
+ reyserver-1.1.62.dist-info/METADATA,sha256=RaK-segYQBiE3SqVbECLENKHd-1TOQFeYarUhtt6huI,1666
9
+ reyserver-1.1.62.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ reyserver-1.1.62.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
11
+ reyserver-1.1.62.dist-info/RECORD,,
reyserver/radmin.py DELETED
@@ -1,171 +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 fastapi import Request
16
- from sqladmin import Admin, ModelView
17
- from sqladmin.authentication import AuthenticationBackend
18
- from reydb import DatabaseEngineAsync, rorm
19
- from reykit.rbase import Singleton
20
-
21
- from .rbase import ServerBase
22
- from . import rserver
23
-
24
-
25
- __all__ = (
26
- 'ServerAdminModel',
27
- 'ServerAdminModelView',
28
- 'ServerAdminModelViewStats',
29
- 'ServerAdmin'
30
- )
31
-
32
-
33
- class ServerAdminModel(ServerBase, ModelView):
34
- """
35
- Server admin model type.
36
- """
37
-
38
-
39
- class ServerAdminModelView(ServerAdminModel):
40
- """
41
- Server admin view model type.
42
- """
43
-
44
- category = 'View'
45
- can_view_details = can_edit = can_create = can_delete = False
46
-
47
-
48
- class ServerAdminModelViewStats(ServerAdminModelView):
49
- """
50
- Server admin stats view model type.
51
- """
52
-
53
- column_list = ['item', 'value', 'comment']
54
-
55
-
56
- # class ServerAdminAuthentication(ServerAdmin, AuthenticationBackend):
57
- # """
58
- # Server admin authentication type.
59
- # """
60
-
61
-
62
- # async def authenticate(self, request: Request) -> bool:
63
- # """
64
- # Authenticate request.
65
-
66
- # Parameters
67
- # ----------
68
- # request : Request.
69
- # """
70
-
71
- # #
72
- # ...
73
- # request.session['token']
74
- # ...
75
-
76
-
77
- # async def login(self, request: Request) -> bool:
78
- # form = await request.form()
79
- # form['username']
80
- # form['password']
81
- # ...
82
- # request.session['token'] = ...
83
- # ...
84
-
85
-
86
- # async def logout(self, request: Request) -> None:
87
- # request.session.clear()
88
-
89
-
90
- class ServerAdmin(ServerBase, Singleton):
91
- """
92
- Server admin type, singleton mode.
93
- """
94
-
95
-
96
- def __init__(self, server: 'rserver.Server') -> None:
97
- """
98
- Build instance attributes.
99
-
100
- Parameters
101
- ----------
102
- server : Server.
103
- """
104
-
105
- # Build.
106
- self.server = server
107
-
108
- ## Admin.
109
- self.api_admin_binds: dict[ServerAdminModel, DatabaseEngineAsync] = {}
110
- 'Admin API model bind database engine dictionary.'
111
- Session = async_sessionmaker()
112
- Session.configure(binds=self.api_admin_binds)
113
- auth = self.__create_auth()
114
- self.admin = Admin(
115
- self.server.app,
116
- session_maker=Session,
117
- authentication_backend=auth
118
- )
119
-
120
-
121
-
122
- def add_model(
123
- self,
124
- model: Type[ServerAdminModel] | type[rorm.Model],
125
- engine: DatabaseEngineAsync | str = None,
126
- label: str | None = None,
127
- name: str | None = None,
128
- column: str | Sequence[str] | None = None,
129
- **attrs: Any
130
- ) -> None:
131
- """
132
- Add admin model type.
133
-
134
- Parameters
135
- ----------
136
- model : Model type.
137
- - `type[rorm.Model]`: Define as `ServerAdminModel`.
138
- engine : Database engine or name.
139
- label : Admin model class label.
140
- name : Admin model name.
141
- column : Admin model display column names.
142
- attrs : Other admin model attributes.
143
- """
144
-
145
- # Parameter.
146
- if issubclass(model, rorm.Model):
147
- class ServerAdminModel_(ServerAdminModel, model=model): ...
148
- model = ServerAdminModel_
149
- if type(engine) == str:
150
- engine = self.server.db[engine]
151
- if label is not None:
152
- model.category = label
153
- if name is not None:
154
- model.name = model.name_plural = name
155
- if type(column) == str:
156
- column = [column]
157
- if column is not None:
158
- model.column_list = list(column)
159
- for key, value in attrs.items():
160
- setattr(model, key, value)
161
-
162
- # Add.
163
- self.api_admin_binds[model.model] = engine.engine
164
- self.admin.add_view(model)
165
-
166
-
167
- # Simple path.
168
-
169
- ## Server admin model type.
170
- View = ServerAdminModelView
171
- ViewStats = ServerAdminModelViewStats
@@ -1,12 +0,0 @@
1
- reyserver/__init__.py,sha256=Oq-lOcQInzhgKt1_4OB2jNx0OO2d9qZg9iZqUx6YRr8,398
2
- reyserver/radmin.py,sha256=0nOEBd1vFDn0annjiGXi3TN9C6Vofpb8W_UfXj5TuhM,4257
3
- reyserver/rall.py,sha256=MI1NnqUpq22CK9w3XPPBS0K8lNuf0BnbI0pn4MGMx38,293
4
- reyserver/rauth.py,sha256=KgHWuRmqmxMoeNFCvCpdxb0S5uv7VH75M0vuMrZTOt8,15111
5
- reyserver/rbase.py,sha256=kzSbo6yoPlWquR6XiseTKi5hEbllaG3qVmmwnnKKac0,6898
6
- reyserver/rclient.py,sha256=VDUqQMVUTGzmZ8kjiMet2_zEt0_Siw0gyQkqHRIZ594,6281
7
- reyserver/rfile.py,sha256=0wS-ohsAOGcSCg07U66wpqJlxnCLWN6gkEQJK4qavWQ,8877
8
- reyserver/rserver.py,sha256=mjc-SeE2RZZwB5Z1nUSMo_VMign3VcftIK1Kv7XIne8,11484
9
- reyserver-1.1.60.dist-info/METADATA,sha256=JuZnt-TPamZk_dtV07w4aEm6cT5fZa3Js146QUa6wNI,1666
10
- reyserver-1.1.60.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- reyserver-1.1.60.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
12
- reyserver-1.1.60.dist-info/RECORD,,