reyserver 1.1.48__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
@@ -9,3 +9,243 @@
9
9
  """
10
10
 
11
11
 
12
+ from fastapi import APIRouter
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
16
+
17
+ from .rbase import ServerConfig, Bind, exit_api
18
+
19
+
20
+ __all__ = (
21
+ 'DatabaseORMTableUser',
22
+ 'DatabaseORMTableRole',
23
+ 'DatabaseORMTablePerm',
24
+ 'DatabaseORMTableUserRole',
25
+ 'DatabaseORMTableRolePerm',
26
+ 'build_auth_db',
27
+ 'auth_router'
28
+ )
29
+
30
+
31
+ class DatabaseORMTableUser(rorm.Model, table=True):
32
+ """
33
+ Database `user` table ORM model.
34
+ """
35
+
36
+ __name__ = 'user'
37
+ __comment__ = 'User information table.'
38
+ create_time: rorm.Datetime = rorm.Field(field_default=':create_time', not_null=True, index_n=True, comment='Record create time.')
39
+ update_time: rorm.Datetime = rorm.Field(field_default=':update_time', not_null=True, index_n=True, comment='Record update time.')
40
+ user_id: int = rorm.Field(rorm.types_mysql.MEDIUMINT(unsigned=True), key_auto=True, comment='User ID.')
41
+ name: str = rorm.Field(rorm.types.VARCHAR(50), not_null=True, index_u=True, comment='User name.')
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.')
46
+ is_valid: bool = rorm.Field(rorm.types_mysql.TINYINT(unsigned=True), field_default='1', not_null=True, comment='Is the valid.')
47
+
48
+
49
+ class DatabaseORMTableRole(rorm.Model, table=True):
50
+ """
51
+ Database `role` table ORM model.
52
+ """
53
+
54
+ __name__ = 'role'
55
+ __comment__ = 'Role information table.'
56
+ create_time: rorm.Datetime = rorm.Field(field_default=':create_time', not_null=True, index_n=True, comment='Record create time.')
57
+ update_time: rorm.Datetime = rorm.Field(field_default=':update_time', not_null=True, index_n=True, comment='Record update time.')
58
+ role_id: int = rorm.Field(rorm.types_mysql.SMALLINT(unsigned=True), key_auto=True, comment='Role ID.')
59
+ name: str = rorm.Field(rorm.types.VARCHAR(50), not_null=True, index_u=True, comment='Role name.')
60
+ desc: str = rorm.Field(rorm.types.VARCHAR(500), comment='Role description.')
61
+
62
+
63
+ class DatabaseORMTablePerm(rorm.Model, table=True):
64
+ """
65
+ Database `perm` table ORM model.
66
+ """
67
+
68
+ __name__ = 'perm'
69
+ __comment__ = 'API permission information table.'
70
+ create_time: rorm.Datetime = rorm.Field(field_default=':create_time', not_null=True, index_n=True, comment='Record create time.')
71
+ update_time: rorm.Datetime = rorm.Field(field_default=':update_time', not_null=True, index_n=True, comment='Record update time.')
72
+ perm_id: int = rorm.Field(rorm.types_mysql.SMALLINT(unsigned=True), key_auto=True, comment='Permission ID.')
73
+ name: str = rorm.Field(rorm.types.VARCHAR(50), not_null=True, index_u=True, comment='Permission name.')
74
+ 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
+
78
+
79
+ class DatabaseORMTableUserRole(rorm.Model, table=True):
80
+ """
81
+ Database `user_role` table ORM model.
82
+ """
83
+
84
+ __name__ = 'user_role'
85
+ __comment__ = 'User and role association table.'
86
+ create_time: rorm.Datetime = rorm.Field(field_default=':create_time', not_null=True, index_n=True, comment='Record create time.')
87
+ update_time: rorm.Datetime = rorm.Field(field_default=':update_time', not_null=True, index_n=True, comment='Record update time.')
88
+ user_id: int = rorm.Field(rorm.types_mysql.MEDIUMINT(unsigned=True), key=True, comment='User ID.')
89
+ role_id: int = rorm.Field(rorm.types_mysql.SMALLINT(unsigned=True), key=True, comment='Role ID.')
90
+
91
+
92
+ class DatabaseORMTableRolePerm(rorm.Model, table=True):
93
+ """
94
+ Database `role_perm` table ORM model.
95
+ """
96
+
97
+ __name__ = 'role_perm'
98
+ __comment__ = 'role and permission association table.'
99
+ create_time: rorm.Datetime = rorm.Field(field_default=':create_time', not_null=True, index_n=True, comment='Record create time.')
100
+ update_time: rorm.Datetime = rorm.Field(field_default=':update_time', not_null=True, index_n=True, comment='Record update time.')
101
+ role_id: int = rorm.Field(rorm.types_mysql.SMALLINT(unsigned=True), key=True, comment='Role ID.')
102
+ perm_id: int = rorm.Field(rorm.types_mysql.SMALLINT(unsigned=True), key=True, comment='Permission ID.')
103
+
104
+
105
+ def build_auth_db(engine: DatabaseEngine | DatabaseEngineAsync) -> None:
106
+ """
107
+ Check and build `auth` database tables.
108
+
109
+ Parameters
110
+ ----------
111
+ db : Database engine instance.
112
+ """
113
+
114
+ # Set parameter.
115
+ database = engine.database
116
+
117
+ ## Table.
118
+ tables = [
119
+ DatabaseORMTableUser,
120
+ DatabaseORMTableRole,
121
+ DatabaseORMTablePerm,
122
+ DatabaseORMTableUserRole,
123
+ DatabaseORMTableRolePerm
124
+ ]
125
+
126
+ ## View stats.
127
+ views_stats = [
128
+ {
129
+ 'path': 'stats',
130
+ 'items': [
131
+ {
132
+ 'name': 'user_count',
133
+ 'select': (
134
+ 'SELECT COUNT(1)\n'
135
+ f'FROM `{database}`.`user`'
136
+ ),
137
+ 'comment': 'User information count.'
138
+ },
139
+ {
140
+ 'name': 'role_count',
141
+ 'select': (
142
+ 'SELECT COUNT(1)\n'
143
+ f'FROM `{database}`.`role`'
144
+ ),
145
+ 'comment': 'Role information count.'
146
+ },
147
+ {
148
+ 'name': 'perm_count',
149
+ 'select': (
150
+ 'SELECT COUNT(1)\n'
151
+ f'FROM `{database}`.`perm`'
152
+ ),
153
+ 'comment': 'Permission information count.'
154
+ },
155
+ {
156
+ 'name': 'user_day_count',
157
+ 'select': (
158
+ 'SELECT COUNT(1)\n'
159
+ f'FROM `{database}`.`user`\n'
160
+ 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) = 0'
161
+ ),
162
+ 'comment': 'User information count in the past day.'
163
+ },
164
+ {
165
+ 'name': 'user_week_count',
166
+ 'select': (
167
+ 'SELECT COUNT(1)\n'
168
+ f'FROM `{database}`.`user`\n'
169
+ 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) <= 6'
170
+ ),
171
+ 'comment': 'User information count in the past week.'
172
+ },
173
+ {
174
+ 'name': 'user_month_count',
175
+ 'select': (
176
+ 'SELECT COUNT(1)\n'
177
+ f'FROM `{database}`.`user`\n'
178
+ 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) <= 29'
179
+ ),
180
+ 'comment': 'User information count in the past month.'
181
+ },
182
+ {
183
+ 'name': 'user_last_time',
184
+ 'select': (
185
+ 'SELECT MAX(`create_time`)\n'
186
+ f'FROM `{database}`.`user`'
187
+ ),
188
+ 'comment': 'User last record create time.'
189
+ }
190
+ ]
191
+ }
192
+ ]
193
+
194
+ # Build.
195
+ engine.sync_engine.build.build(tables=tables, views_stats=views_stats, skip=True)
196
+
197
+
198
+ auth_router = APIRouter()
199
+ depend_auth_sess = Bind.create_depend_db('auth', 'sess')
200
+ depend_auth_conn = Bind.create_depend_db('auth', 'conn')
201
+
202
+
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:
209
+ """
210
+ Create session.
211
+
212
+ Parameters
213
+ ----------
214
+ username : User name.
215
+ password : User password.
216
+
217
+ Returns
218
+ -------
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
@@ -33,7 +33,7 @@ class DatabaseORMTableInfo(rorm.Model, table=True):
33
33
  __name__ = 'info'
34
34
  __comment__ = 'File information table.'
35
35
  create_time: rorm.Datetime = rorm.Field(field_default=':create_time', not_null=True, index_n=True, comment='Record create time.')
36
- file_id: int = rorm.Field(rorm.types_mysql.MEDIUMINT(unsigned=True), key_auto=True, comment='File self increase ID.')
36
+ file_id: int = rorm.Field(rorm.types_mysql.MEDIUMINT(unsigned=True), key_auto=True, comment='File ID.')
37
37
  md5: str = rorm.Field(rorm.types.CHAR(32), not_null=True, index_n=True, comment='File MD5.')
38
38
  name: str = rorm.Field(rorm.types.VARCHAR(260), index_n=True, comment='File name.')
39
39
  note: str = rorm.Field(rorm.types.VARCHAR(500), comment='File note.')
@@ -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.48
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=QyY4gZ0ulpH9Kxvux_jnZBhzfWZZEuOWB1oYU9uzCnY,167
4
- reyserver/rbase.py,sha256=IUVkkNsLmQh-QRLX6qtbCjPZAbQAsxoe0goPLCxG9KA,5283
5
- reyserver/rclient.py,sha256=pTJtn78jPKgFo5EoQwZRdM0cYHdCs7QUKqfl-jUBRgk,4220
6
- reyserver/rfile.py,sha256=6Dwq8_X1kiY1n-9RhbLL3hvdhTAnsAUHyXivcviYcoA,8888
7
- reyserver/rserver.py,sha256=hqpemzJHO6xHy_7pO3cvvjnfy8Yfqy8HfyIq4sjk4Dc,5889
8
- reyserver-1.1.48.dist-info/METADATA,sha256=T6Q2BrTheAiAziG15T9seCxh_Yi9IRcplL35YTa-wGo,1689
9
- reyserver-1.1.48.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- reyserver-1.1.48.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
11
- reyserver-1.1.48.dist-info/RECORD,,