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 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
- 'DatabaseORMTableInfo',
20
- 'DatabaseORMTableData',
21
- 'build_file_db',
22
- 'file_router'
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: int
40
- head: int
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__ = 'Permission information table.'
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
- code: str
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 build_file_db() -> None:
105
+ def build_auth_db(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
100
106
  """
101
- Check and build `file` database tables.
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': 'count',
132
+ 'name': 'user_count',
124
133
  'select': (
125
134
  'SELECT COUNT(1)\n'
126
- f'FROM `{database}`.`info`'
135
+ f'FROM `{database}`.`user`'
127
136
  ),
128
- 'comment': 'File information count.'
137
+ 'comment': 'User information count.'
129
138
  },
130
139
  {
131
- 'name': 'past_day_count',
140
+ 'name': 'role_count',
132
141
  'select': (
133
142
  'SELECT COUNT(1)\n'
134
- f'FROM `{database}`.`info`\n'
135
- 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) = 0'
143
+ f'FROM `{database}`.`role`'
136
144
  ),
137
- 'comment': 'File information count in the past day.'
145
+ 'comment': 'Role information count.'
138
146
  },
139
147
  {
140
- 'name': 'past_week_count',
148
+ 'name': 'perm_count',
141
149
  'select': (
142
150
  'SELECT COUNT(1)\n'
143
- f'FROM `{database}`.`info`\n'
144
- 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) <= 6'
151
+ f'FROM `{database}`.`perm`'
145
152
  ),
146
- 'comment': 'File information count in the past week.'
153
+ 'comment': 'Permission information count.'
147
154
  },
148
155
  {
149
- 'name': 'past_month_count',
156
+ 'name': 'user_day_count',
150
157
  'select': (
151
158
  'SELECT COUNT(1)\n'
152
- f'FROM `{database}`.`info`\n'
153
- 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) <= 29'
159
+ f'FROM `{database}`.`user`\n'
160
+ 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) = 0'
154
161
  ),
155
- 'comment': 'File information count in the past month.'
162
+ 'comment': 'User information count in the past day.'
156
163
  },
157
164
  {
158
- 'name': 'data_count',
165
+ 'name': 'user_week_count',
159
166
  'select': (
160
167
  'SELECT COUNT(1)\n'
161
- f'FROM `{database}`.`data`'
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': 'File average byte size.'
171
+ 'comment': 'User information count in the past week.'
180
172
  },
181
173
  {
182
- 'name': 'max_size',
174
+ 'name': 'user_month_count',
183
175
  'select': (
184
- 'SELECT FORMAT(MAX(`size`), 0)\n'
185
- f'FROM `{database}`.`data`'
176
+ 'SELECT COUNT(1)\n'
177
+ f'FROM `{database}`.`user`\n'
178
+ 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) <= 29'
186
179
  ),
187
- 'comment': 'File maximum byte size.'
180
+ 'comment': 'User information count in the past month.'
188
181
  },
189
182
  {
190
- 'name': 'last_time',
183
+ 'name': 'user_last_time',
191
184
  'select': (
192
185
  'SELECT MAX(`create_time`)\n'
193
- f'FROM `{database}`.`info`'
186
+ f'FROM `{database}`.`user`'
194
187
  ),
195
- 'comment': 'File last record create time.'
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, views=views, views_stats=views_stats, skip=True)
195
+ engine.sync_engine.build.build(tables=tables, views_stats=views_stats, skip=True)
203
196
 
204
197
 
205
- file_router = APIRouter()
206
- depend_file_sess = Bind.create_depend_db('file', 'sess')
207
- depend_file_conn = Bind.create_depend_db('file', 'conn')
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
- @file_router.post('/')
211
- async def upload_file(
212
- file: Bind.File = Bind.forms,
213
- name: str = Bind.forms_n,
214
- note: str = Bind.forms_n,
215
- sess: Bind.Sess = depend_file_sess
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
- Upload file.
210
+ Create session.
219
211
 
220
212
  Parameters
221
213
  ----------
222
- file : File instance.
223
- name : File name.
224
- note : File note.
214
+ username : User name.
215
+ password : User password.
225
216
 
226
217
  Returns
227
218
  -------
228
- File information.
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.post('/')
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 CallableT, CoroutineFunctionSimple, Singleton, throw
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 Authentication API.
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 file_router, build_file_db
230
+ from .rfile import build_file_db, file_router
211
231
 
212
232
  # Build database.
213
- build_file_db()
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, prefix='/files', tags=['file'])
238
+ self.app.include_router(file_router, tags=['file'])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reyserver
3
- Version: 1.1.49
3
+ Version: 1.1.50
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=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,,