reyserver 1.1.50__py3-none-any.whl → 1.1.52__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 CHANGED
@@ -9,10 +9,12 @@
9
9
  """
10
10
 
11
11
 
12
+ from typing import Any, Literal
13
+ from datetime import datetime as Datetime
12
14
  from fastapi import APIRouter
13
15
  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
16
+ from reykit.rdata import encode_jwt, is_hash_bcrypt
17
+ from reykit.rtime import now, time_to
16
18
 
17
19
  from .rbase import ServerConfig, Bind, exit_api
18
20
 
@@ -72,8 +74,7 @@ class DatabaseORMTablePerm(rorm.Model, table=True):
72
74
  perm_id: int = rorm.Field(rorm.types_mysql.SMALLINT(unsigned=True), key_auto=True, comment='Permission ID.')
73
75
  name: str = rorm.Field(rorm.types.VARCHAR(50), not_null=True, index_u=True, comment='Permission name.')
74
76
  desc: str = rorm.Field(rorm.types.VARCHAR(500), comment='Permission description.')
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.')
77
+ api: str = rorm.Field(rorm.types.VARCHAR(1000), comment='API resource path regular expression "match" pattern.')
77
78
 
78
79
 
79
80
  class DatabaseORMTableUserRole(rorm.Model, table=True):
@@ -202,8 +203,9 @@ depend_auth_conn = Bind.create_depend_db('auth', 'conn')
202
203
 
203
204
  @auth_router.post('/sessions')
204
205
  async def create_sessions(
205
- username: str = Bind.body,
206
- password: str = Bind.body,
206
+ account: str = Bind.i.body,
207
+ password: str = Bind.i.body,
208
+ account_type: Literal['name', 'email', 'phone'] = Bind.Body('name'),
207
209
  conn: Bind.Conn = depend_auth_conn
208
210
  ) -> dict:
209
211
  """
@@ -211,8 +213,9 @@ async def create_sessions(
211
213
 
212
214
  Parameters
213
215
  ----------
214
- username : User name.
216
+ account : User account, name or email or phone.
215
217
  password : User password.
218
+ account_type : User account type.
216
219
 
217
220
  Returns
218
221
  -------
@@ -221,30 +224,78 @@ async def create_sessions(
221
224
 
222
225
  # Parameter.
223
226
  key = ServerConfig.server.api_auth_key
224
- password_hash = hash_bcrypt(password)
227
+ sess_seconds = ServerConfig.server.api_auth_sess_seconds
225
228
 
226
229
  # Check.
227
230
  sql = (
228
- ''
231
+ 'SELECT ANY_VALUE(`create_time`) AS `create_time`,\n'
232
+ ' ANY_VALUE(`update_time`) AS `update_time`,\n'
233
+ ' ANY_VALUE(`user`.`user_id`) AS `user_id`,\n'
234
+ ' ANY_VALUE(`user`.`name`) AS `user_name`,\n'
235
+ ' ANY_VALUE(`password`) AS `password`,\n'
236
+ ' ANY_VALUE(`email`) AS `email`,\n'
237
+ ' ANY_VALUE(`phone`) AS `phone`,\n'
238
+ ' ANY_VALUE(`avatar`) AS `avatar`,\n'
239
+ " GROUP_CONCAT(DISTINCT `role`.`name` SEPARATOR ';') AS `role_names`,\n"
240
+ " GROUP_CONCAT(DISTINCT `perm`.`name` SEPARATOR ';') AS `perm_names`,\n"
241
+ " GROUP_CONCAT(DISTINCT `perm`.`api` SEPARATOR ';') AS `perm_apis`\n"
242
+ 'FROM (\n'
243
+ ' SELECT `create_time`, `update_time`, `user_id`, `password`, `name`, `email`, `phone`, `avatar`\n'
244
+ ' FROM `test`.`user`\n'
245
+ f' WHERE `{account_type}` = :account\n'
246
+ ' LIMIT 1\n'
247
+ ') as `user`\n'
248
+ 'LEFT JOIN (\n'
249
+ ' SELECT `user_id`, `role_id`\n'
250
+ ' FROM `test`.`user_role`\n'
251
+ ') as `user_role`\n'
252
+ 'ON `user_role`.`user_id` = `user`.`user_id`\n'
253
+ 'LEFT JOIN (\n'
254
+ ' SELECT `role_id`, `name`\n'
255
+ ' FROM `test`.`role`\n'
256
+ ') AS `role`\n'
257
+ 'ON `user_role`.`role_id` = `role`.`role_id`\n'
258
+ 'LEFT JOIN (\n'
259
+ ' SELECT `role_id`, `perm_id`\n'
260
+ ' FROM `test`.`role_perm`\n'
261
+ ') as `role_perm`\n'
262
+ 'ON `role_perm`.`role_id` = `role`.`role_id`\n'
263
+ 'LEFT JOIN (\n'
264
+ " SELECT `perm_id`, `name`, CONCAT(`method`, ':', `path`) as `api`\n"
265
+ ' FROM `test`.`perm`\n'
266
+ ') AS `perm`\n'
267
+ 'ON `role_perm`.`perm_id` = `perm`.`perm_id`\n'
268
+ 'GROUP BY `user_id`'
229
269
  )
230
270
  result = await conn.execute(
231
271
  sql,
232
- username=username,
233
- password_hash=password_hash
272
+ account=account
234
273
  )
235
274
 
236
275
  # Check.
237
276
  if result.empty:
238
277
  exit_api(401)
278
+ json: dict[str, Datetime | Any] = result.to_row()
279
+ if not is_hash_bcrypt(password, json['password']):
280
+ exit_api(401)
239
281
 
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
- }
282
+ # JWT.
283
+ now_timestamp_s = now('timestamp_s')
284
+ json['sub'] = json.pop('user_id')
285
+ json['iat'] = now_timestamp_s
286
+ json['nbf'] = now_timestamp_s
287
+ json['exp'] = now_timestamp_s + sess_seconds
288
+ json['role_names'] = json['role_names'].split(';')
289
+ json['perm_names'] = json['perm_names'].split(';')
290
+ perm_apis: list[str] = json['perm_apis'].split(';')
291
+ perm_api_dict = {}
292
+ for perm_api in perm_apis:
293
+ method, path = perm_api.split(':', 1)
294
+ paths: list = perm_api_dict.setdefault(method, [])
295
+ paths.append(path)
296
+ json['perm_apis'] = perm_api_dict
297
+ json['create_time'] = json['create_time'].timestamp()
298
+ json['update_time'] = json['update_time'].timestamp()
248
299
  token = encode_jwt(json, key)
249
300
  data = {'token': token}
250
301
 
reyserver/rbase.py CHANGED
@@ -26,8 +26,7 @@ from fastapi.params import (
26
26
  )
27
27
  from reydb.rconn import DatabaseConnectionAsync
28
28
  from reydb.rorm import DatabaseORMModel, DatabaseORMSessionAsync
29
- from reykit.rwrap import wrap_cache
30
- from reykit.rbase import CoroutineFunctionSimple, Base, Exit, StaticMeta, ConfigMeta, throw
29
+ from reykit.rbase import CoroutineFunctionSimple, Base, Exit, StaticMeta, ConfigMeta, Singleton, throw
31
30
 
32
31
  from . import rserver
33
32
 
@@ -38,7 +37,8 @@ __all__ = (
38
37
  'ServerExit',
39
38
  'ServerExitAPI',
40
39
  'exit_api',
41
- 'ServerBind'
40
+ 'ServerBind',
41
+ 'Bind'
42
42
  )
43
43
 
44
44
 
@@ -91,6 +91,168 @@ def exit_api(code: int = 400, text: str | None = None) -> NoReturn:
91
91
  raise ServerExitAPI(code, text)
92
92
 
93
93
 
94
+ class ServerBindInstance(ServerBase, Singleton):
95
+ """
96
+ Server API bind parameter build instance type.
97
+ """
98
+
99
+
100
+ @property
101
+ def path(self) -> Path:
102
+ """
103
+ Path instance.
104
+ """
105
+
106
+ # Build.
107
+ path = Path()
108
+
109
+ return path
110
+
111
+
112
+ @property
113
+ def query(self) -> Query:
114
+ """
115
+ Query instance.
116
+ """
117
+
118
+ # Build.
119
+ query = Query()
120
+
121
+ return query
122
+
123
+
124
+ @property
125
+ def header(self) -> Header:
126
+ """
127
+ Header instance.
128
+ """
129
+
130
+ # Build.
131
+ header = Header()
132
+
133
+ return header
134
+
135
+
136
+ @property
137
+ def cookie(self) -> Cookie:
138
+ """
139
+ Cookie instance.
140
+ """
141
+
142
+ # Build.
143
+ cookie = Cookie()
144
+
145
+ return cookie
146
+
147
+
148
+ @property
149
+ def body(self) -> Body:
150
+ """
151
+ Body instance.
152
+ """
153
+
154
+ # Build.
155
+ body = Body()
156
+
157
+ return body
158
+
159
+
160
+ @property
161
+ def form(self) -> Form:
162
+ """
163
+ Form instance.
164
+ """
165
+
166
+ # Build.
167
+ form = Form()
168
+
169
+ return form
170
+
171
+
172
+ @property
173
+ def forms(self) -> Forms:
174
+ """
175
+ Forms instance.
176
+ """
177
+
178
+ # Build.
179
+ forms = Forms()
180
+
181
+ return forms
182
+
183
+
184
+ @property
185
+ def query_n(self) -> Query:
186
+ """
187
+ Query instance, default `None`.
188
+ """
189
+
190
+ # Build.
191
+ query = Query(None)
192
+
193
+ return query
194
+
195
+
196
+ @property
197
+ def header_n(self) -> Header:
198
+ """
199
+ Header instance, default `None`.
200
+ """
201
+
202
+ # Build.
203
+ header = Header(None)
204
+
205
+ return header
206
+
207
+
208
+ @property
209
+ def cookie_n(self) -> Cookie:
210
+ """
211
+ Cookie instance, default `None`.
212
+ """
213
+
214
+ # Build.
215
+ cookie = Cookie(None)
216
+
217
+ return cookie
218
+
219
+
220
+ @property
221
+ def body_n(self) -> Body:
222
+ """
223
+ Body instance, default `None`.
224
+ """
225
+
226
+ # Build.
227
+ body = Body(None)
228
+
229
+ return body
230
+
231
+
232
+ @property
233
+ def form_n(self) -> Form:
234
+ """
235
+ Form instance, default `None`.
236
+ """
237
+
238
+ # Build.
239
+ form = Form(None)
240
+
241
+ return form
242
+
243
+
244
+ @property
245
+ def forms_n(self) -> Forms:
246
+ """
247
+ Forms instance, default `None`.
248
+ """
249
+
250
+ # Build.
251
+ forms = Forms(None)
252
+
253
+ return forms
254
+
255
+
94
256
  class ServerBind(ServerBase, metaclass=StaticMeta):
95
257
  """
96
258
  Server API bind parameter type.
@@ -148,7 +310,6 @@ class ServerBind(ServerBase, metaclass=StaticMeta):
148
310
  return lifespan
149
311
 
150
312
 
151
- @wrap_cache
152
313
  def create_depend_db(database: str, mode: Literal['sess', 'conn']) -> Depends:
153
314
  """
154
315
  Create dependencie type of asynchronous database.
@@ -201,32 +362,7 @@ class ServerBind(ServerBase, metaclass=StaticMeta):
201
362
  JSON = DatabaseORMModel
202
363
  Conn = DatabaseConnectionAsync
203
364
  Sess = DatabaseORMSessionAsync
204
- path = Path()
205
- 'Path instance.'
206
- query = Query()
207
- 'Query instance.'
208
- header = Header()
209
- 'Header instance.'
210
- cookie = Cookie()
211
- 'Cookie instance.'
212
- body = Body()
213
- 'Body instance.'
214
- form = Form()
215
- 'Form instance.'
216
- forms = Forms()
217
- 'Forms instance.'
218
- query_n = Query(None)
219
- 'Query instance, default `None`.'
220
- header_n = Header(None)
221
- 'Header instance, default `None`.'
222
- cookie_n = Cookie(None)
223
- 'Cookie instance, default `None`.'
224
- body_n = Body(None)
225
- 'Body instance, default `None`.'
226
- form_n = Form(None)
227
- 'Form instance, default `None`.'
228
- forms_n = Forms(None)
229
- 'Forms instance, default `None`.'
365
+ i = ServerBindInstance()
230
366
 
231
367
 
232
368
  Bind = ServerBind
reyserver/rclient.py CHANGED
@@ -9,7 +9,7 @@
9
9
  """
10
10
 
11
11
 
12
- from typing import TypedDict
12
+ from typing import TypedDict, Literal
13
13
  from datetime import datetime as Datetime
14
14
  from reykit.ros import File, Folder, overload
15
15
  from reykit.rnet import join_url, request, get_response_file_name
@@ -44,6 +44,43 @@ class ServerClient(ServerBase):
44
44
  self.url = url
45
45
 
46
46
 
47
+ def create_session(
48
+ self,
49
+ account: str,
50
+ password: str,
51
+ account_type: Literal['name', 'email', 'phone'] = 'name'
52
+ ) -> str:
53
+ """
54
+ Create session.
55
+
56
+ Parameters
57
+ ----------
58
+ account : User account, name or email or phone.
59
+ password : User password.
60
+ account_type : User account type.
61
+
62
+ Returns
63
+ -------
64
+ Token.
65
+ """
66
+
67
+ # Parameter.
68
+ url = join_url(self.url, 'sessions')
69
+ json = {
70
+ 'account': account,
71
+ 'password': password,
72
+ 'account_type': account_type
73
+ }
74
+
75
+ # Request.
76
+ response = request(url, json=json, check=True)
77
+ response_dict = response.json()
78
+ print(response_dict)
79
+ token = response_dict['token']
80
+
81
+ return token
82
+
83
+
47
84
  def upload_file(
48
85
  self,
49
86
  source: str | bytes,
reyserver/rfile.py CHANGED
@@ -184,7 +184,7 @@ depend_file_conn = Bind.create_depend_db('file', 'conn')
184
184
 
185
185
  @file_router.get('/files/{file_id}')
186
186
  async def get_file_info(
187
- file_id: int = Bind.path,
187
+ file_id: int = Bind.i.path,
188
188
  sess: Bind.Sess = depend_file_sess
189
189
  ) -> DatabaseORMTableInfo:
190
190
  """
@@ -211,9 +211,9 @@ async def get_file_info(
211
211
 
212
212
  @file_router.post('/files')
213
213
  async def upload_file(
214
- file: Bind.File = Bind.forms,
215
- name: str = Bind.forms_n,
216
- note: str = Bind.forms_n,
214
+ file: Bind.File = Bind.i.forms,
215
+ name: str = Bind.i.forms_n,
216
+ note: str = Bind.i.forms_n,
217
217
  sess: Bind.Sess = depend_file_sess
218
218
  ) -> DatabaseORMTableInfo:
219
219
  """
@@ -265,7 +265,7 @@ async def upload_file(
265
265
 
266
266
  @file_router.get('/files/{file_id}/download')
267
267
  async def download_file(
268
- file_id: int = Bind.path,
268
+ file_id: int = Bind.i.path,
269
269
  conn: Bind.Conn = depend_file_conn
270
270
  ) -> FileResponse:
271
271
  """
reyserver/rserver.py CHANGED
@@ -105,6 +105,8 @@ class Server(ServerBase, Singleton):
105
105
  # API.
106
106
  self.api_auth_key: str
107
107
  'Authentication API JWT encryption key.'
108
+ self.api_auth_sess_seconds: int
109
+ 'Authentication API session valid seconds.'
108
110
  self.api_file_dir: str
109
111
  'File API store directory path.'
110
112
 
@@ -136,7 +138,10 @@ class Server(ServerBase, Singleton):
136
138
  response = await call_next(request)
137
139
 
138
140
  # After.
139
- if request.method == 'POST':
141
+ if (
142
+ response.status_code == 200
143
+ and request.method == 'POST'
144
+ ):
140
145
  response.status_code = 201
141
146
 
142
147
  return response
@@ -190,7 +195,7 @@ class Server(ServerBase, Singleton):
190
195
  setattr(self.app, key, value)
191
196
 
192
197
 
193
- def add_api_auth(self, key: str | None = None) -> None:
198
+ def add_api_auth(self, key: str | None = None, sess_seconds: int = 28800) -> None:
194
199
  """
195
200
  Add authentication API.
196
201
  Note: must include database engine of `auth` name.
@@ -199,9 +204,10 @@ class Server(ServerBase, Singleton):
199
204
  ----------
200
205
  key : JWT encryption key.
201
206
  - `None`: Random 32 length string.
207
+ sess_seconds : Session valid seconds.
202
208
  """
203
209
 
204
- from .rauth import build_file_db, auth_router
210
+ from .rauth import build_auth_db, auth_router
205
211
 
206
212
  # Parameter.
207
213
  if key is None:
@@ -209,10 +215,11 @@ class Server(ServerBase, Singleton):
209
215
 
210
216
  # Build database.
211
217
  engine = self.db.auth
212
- build_file_db(engine)
218
+ build_auth_db(engine)
213
219
 
214
220
  # Add.
215
221
  self.api_auth_key = key
222
+ self.api_auth_sess_seconds = sess_seconds
216
223
  self.app.include_router(auth_router, tags=['auth'])
217
224
 
218
225
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reyserver
3
- Version: 1.1.50
3
+ Version: 1.1.52
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=oa6Iwuutcj9eO6RH7DaT-rPn_YqYwKuyHYaBd3lZoZE,11728
4
+ reyserver/rbase.py,sha256=xgdLP_O77e-pSrRWm9GVSziPSqEOh2w20cWkF4HBeWo,7042
5
+ reyserver/rclient.py,sha256=IWZ3smyIP0_YJrfSrM8JFCr0FCtN02AyT3hp8YuSsDQ,5103
6
+ reyserver/rfile.py,sha256=bvuXOYO3UDM1jMiyNzQDz56_0ekZUEIRcfNFAhGgdUY,9010
7
+ reyserver/rserver.py,sha256=bfakJGhcHF0CIiAmRyMVOURLbPNCIALGyrnKpf_Yi80,6800
8
+ reyserver-1.1.52.dist-info/METADATA,sha256=ujWE6YB95IdJf4nCYtq7FZW2sw0s_TGSeyuEwAjfHW8,1689
9
+ reyserver-1.1.52.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ reyserver-1.1.52.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
11
+ reyserver-1.1.52.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=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,,