reyserver 1.1.37__py3-none-any.whl → 1.1.39__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/rbase.py CHANGED
@@ -9,12 +9,17 @@
9
9
  """
10
10
 
11
11
 
12
- from reykit.rbase import Base
12
+ from typing import Sequence
13
+ from inspect import iscoroutinefunction
14
+ from contextlib import asynccontextmanager
15
+ from fastapi import FastAPI
16
+ from reykit.rbase import CoroutineFunctionSimple, Base, is_iterable
13
17
 
14
18
 
15
19
  __all__ = (
16
20
  'ServerBase',
17
- 'ServerAPI'
21
+ 'ServerAPI',
22
+ 'generate_lifespan'
18
23
  )
19
24
 
20
25
 
@@ -28,3 +33,50 @@ class ServerAPI(ServerBase):
28
33
  """
29
34
  Server API type.
30
35
  """
36
+
37
+
38
+ def generate_lifespan(
39
+ before: CoroutineFunctionSimple | Sequence[CoroutineFunctionSimple] | None = None,
40
+ after: CoroutineFunctionSimple | Sequence[CoroutineFunctionSimple] | None = None,
41
+ ):
42
+ """
43
+ Generate function of lifespan manager.
44
+
45
+ Parameters
46
+ ----------
47
+ before : Execute before server start.
48
+ after : Execute after server end.
49
+ """
50
+
51
+ # Parameter.
52
+ if before is None:
53
+ before = ()
54
+ elif iscoroutinefunction(before):
55
+ before = (before,)
56
+ if after is None:
57
+ after = ()
58
+ elif iscoroutinefunction(after):
59
+ after = (after,)
60
+
61
+ # Define.
62
+ @asynccontextmanager
63
+ async def lifespan(app: FastAPI):
64
+ """
65
+ Server lifespan manager.
66
+
67
+ Parameters
68
+ ----------
69
+ app : Server APP.
70
+ """
71
+
72
+ # Before.
73
+ for task in before:
74
+ await task()
75
+ yield
76
+
77
+ # After.
78
+ for task in after:
79
+ await after()
80
+
81
+
82
+ return lifespan
reyserver/rdb.py ADDED
@@ -0,0 +1,11 @@
1
+ # !/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ @Time : 2025-10-07 18:03:06
6
+ @Author : Rey
7
+ @Contact : reyxbo@163.com
8
+ @Explain : Database methods.
9
+ """
10
+
11
+
reyserver/rfile.py CHANGED
@@ -5,45 +5,32 @@
5
5
  @Time : 2025-10-06 18:23:51
6
6
  @Author : Rey
7
7
  @Contact : reyxbo@163.com
8
- @Explain : File methods.
8
+ @Explain : File methods. Can create database used `self.build_db` function.
9
9
  """
10
10
 
11
11
 
12
12
  from fastapi import APIRouter
13
13
  from reydb import rorm
14
+ from reydb.rdb import DatabaseAsync
14
15
 
15
16
  from . import rserver
16
17
  from .rbase import ServerAPI
17
18
 
18
19
 
19
20
  __all__ = (
20
- 'ServerAPIFile',
21
+ 'file_router',
22
+ 'DatabaseORMTableInfo',
23
+ 'DatabaseORMTableData',
24
+ 'build_file_db'
21
25
  )
22
26
 
23
27
 
24
- # class ServerAPIFile(ServerAPI):
25
- # """
26
- # Server File API type.
27
- # """
28
-
29
-
30
- # def __init__(self, server: 'rserver.Server') -> None:
31
- # """
32
- # Build instance attributes.
33
-
34
- # Parameters
35
- # ----------
36
- # server : Server instance.
37
- # """
38
-
39
- # # Build.
40
- # self.server = server
41
-
28
+ file_router = APIRouter()
42
29
 
43
30
 
44
31
  class DatabaseORMTableInfo(rorm.Model, table=True):
45
32
  """
46
- Database `info` table model.
33
+ Database `info` table ORM model.
47
34
  """
48
35
 
49
36
  __name__ = 'info'
@@ -57,7 +44,7 @@ class DatabaseORMTableInfo(rorm.Model, table=True):
57
44
 
58
45
  class DatabaseORMTableData(rorm.Model, table=True):
59
46
  """
60
- Database `data` table model.
47
+ Database `data` table ORM model.
61
48
  """
62
49
 
63
50
  __name__ = 'data'
@@ -67,13 +54,218 @@ class DatabaseORMTableData(rorm.Model, table=True):
67
54
  path: str = rorm.Field(rorm.types.VARCHAR(4095), not_null=True, comment='File disk storage path.')
68
55
 
69
56
 
70
- router = APIRouter()
57
+ def build_file_db(db: DatabaseAsync) -> None:
58
+ """
59
+ Check and build all standard databases and tables, by `self.db_names`.
60
+
61
+ Parameters
62
+ db : Asynchronous database instance.
63
+ """
64
+
65
+ # Set parameter.
66
+ database = db.database
67
+
68
+ ## Table.
69
+ tables = [DatabaseORMTableInfo, DatabaseORMTableData]
70
+
71
+ ## View.
72
+ views = [
73
+ {
74
+ 'path': 'data_info',
75
+ 'select': (
76
+ 'SELECT `b`.`last_time`, `a`.`md5`, `a`.`size`, `b`.`names`, `b`.`notes`\n'
77
+ f'FROM `{database}`.`data` AS `a`\n'
78
+ 'LEFT JOIN (\n'
79
+ ' SELECT\n'
80
+ ' `md5`,\n'
81
+ " GROUP_CONCAT(DISTINCT(`name`) ORDER BY `create_time` DESC SEPARATOR ' | ') AS `names`,\n"
82
+ " GROUP_CONCAT(DISTINCT(`note`) ORDER BY `create_time` DESC SEPARATOR ' | ') AS `notes`,\n"
83
+ ' MAX(`create_time`) as `last_time`\n'
84
+ f' FROM `{database}`.`info`\n'
85
+ ' GROUP BY `md5`\n'
86
+ ' ORDER BY `last_time` DESC\n'
87
+ ') AS `b`\n'
88
+ 'ON `a`.`md5` = `b`.`md5`\n'
89
+ 'ORDER BY `last_time` DESC'
90
+ )
91
+ }
92
+ ]
93
+
94
+ ## View stats.
95
+ views_stats = [
96
+ {
97
+ 'path': 'stats',
98
+ 'items': [
99
+ {
100
+ 'name': 'count',
101
+ 'select': (
102
+ 'SELECT COUNT(1)\n'
103
+ f'FROM `{database}`.`info`'
104
+ ),
105
+ 'comment': 'File information count.'
106
+ },
107
+ {
108
+ 'name': 'past_day_count',
109
+ 'select': (
110
+ 'SELECT COUNT(1)\n'
111
+ f'FROM `{database}`.`info`\n'
112
+ 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) = 0'
113
+ ),
114
+ 'comment': 'File information count in the past day.'
115
+ },
116
+ {
117
+ 'name': 'past_week_count',
118
+ 'select': (
119
+ 'SELECT COUNT(1)\n'
120
+ f'FROM `{database}`.`info`\n'
121
+ 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) <= 6'
122
+ ),
123
+ 'comment': 'File information count in the past week.'
124
+ },
125
+ {
126
+ 'name': 'past_month_count',
127
+ 'select': (
128
+ 'SELECT COUNT(1)\n'
129
+ f'FROM `{database}`.`info`\n'
130
+ 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) <= 29'
131
+ ),
132
+ 'comment': 'File information count in the past month.'
133
+ },
134
+ {
135
+ 'name': 'data_count',
136
+ 'select': (
137
+ 'SELECT COUNT(1)\n'
138
+ f'FROM `{database}`.`data`'
139
+ ),
140
+ 'comment': 'File data unique count.'
141
+ },
142
+ {
143
+ 'name': 'total_size',
144
+ 'select': (
145
+ 'SELECT FORMAT(SUM(`size`), 0)\n'
146
+ f'FROM `{database}`.`data`'
147
+ ),
148
+ 'comment': 'File total byte size.'
149
+ },
150
+ {
151
+ 'name': 'avg_size',
152
+ 'select': (
153
+ 'SELECT FORMAT(AVG(`size`), 0)\n'
154
+ f'FROM `{database}`.`data`'
155
+ ),
156
+ 'comment': 'File average byte size.'
157
+ },
158
+ {
159
+ 'name': 'max_size',
160
+ 'select': (
161
+ 'SELECT FORMAT(MAX(`size`), 0)\n'
162
+ f'FROM `{database}`.`data`'
163
+ ),
164
+ 'comment': 'File maximum byte size.'
165
+ },
166
+ {
167
+ 'name': 'last_time',
168
+ 'select': (
169
+ 'SELECT MAX(`create_time`)\n'
170
+ f'FROM `{database}`.`info`'
171
+ ),
172
+ 'comment': 'File last record create time.'
173
+ }
174
+ ]
175
+ }
176
+ ]
177
+
178
+ # Build.
179
+ db.build.build(tables=tables, views=views, views_stats=views_stats)
180
+
181
+
182
+ @file_router.post('/upload')
183
+ def upload(
184
+ source: bytes,
185
+ name: str | None = None,
186
+ note: str | None = None
187
+ ) -> int:
188
+ """
189
+ Upload file.
190
+
191
+ Parameters
192
+ ----------
193
+ source : File path or file bytes.
194
+ name : File name.
195
+ - `None`: Automatic set.
196
+ `parameter 'file' is 'str'`: Use path file name.
197
+ `parameter 'file' is 'bytes'`: No name.
198
+ - `str`: Use this name.
199
+ note : File note.
200
+
201
+ Returns
202
+ -------
203
+ File ID.
204
+ """
205
+
206
+ # Handle parameter.
207
+ conn = self.db.connect()
208
+ match source:
209
+
210
+ ## File path.
211
+ case str():
212
+ file = File(source)
213
+ file_bytes = file.bytes
214
+ file_md5 = get_md5(file_bytes)
215
+ file_name = file.name_suffix
216
+
217
+ ## File bytes.
218
+ case bytes() | bytearray():
219
+ if type(source) == bytearray:
220
+ source = bytes(source)
221
+ file_bytes = source
222
+ file_md5 = get_md5(file_bytes)
223
+ file_name = None
224
+
225
+ ## File name.
226
+ if name is not None:
227
+ file_name = name
228
+
229
+ ## File size.
230
+ file_size = len(file_bytes)
231
+
232
+ # Exist.
233
+ exist = conn.execute.exist(
234
+ (self.db_names['file'], self.db_names['file.data']),
235
+ '`md5` = :file_md5',
236
+ file_md5=file_md5
237
+ )
238
+
239
+ # Upload.
71
240
 
241
+ ## Data.
242
+ if not exist:
243
+ data = {
244
+ 'md5': file_md5,
245
+ 'size': file_size,
246
+ 'bytes': file_bytes
247
+ }
248
+ conn.execute.insert(
249
+ (self.db_names['file'], self.db_names['file.data']),
250
+ data,
251
+ 'ignore'
252
+ )
72
253
 
254
+ ## Information.
255
+ data = {
256
+ 'md5': file_md5,
257
+ 'name': file_name,
258
+ 'note': note
259
+ }
260
+ conn.execute.insert(
261
+ (self.db_names['file'], self.db_names['file.information']),
262
+ data
263
+ )
73
264
 
74
- @router.get('/{file_id}')
75
- def get_file_info(): ...
265
+ # Get ID.
266
+ file_id = conn.insert_id
76
267
 
77
- @router.get('/{}/bytes')
78
- def get_file_bytes(): ...
268
+ # Commit.
269
+ conn.commit()
79
270
 
271
+ return file_id
reyserver/rserver.py CHANGED
@@ -10,15 +10,16 @@
10
10
 
11
11
 
12
12
  from typing import Literal
13
+ from inspect import iscoroutinefunction
13
14
  from collections.abc import Sequence, Callable, Coroutine
14
- from fastapi import FastAPI
15
+ from fastapi import FastAPI, Depends
15
16
  from fastapi.middleware.gzip import GZipMiddleware
16
17
  from fastapi.staticfiles import StaticFiles
17
18
  from uvicorn import run as uvicorn_run
19
+ from contextlib import asynccontextmanager
18
20
  from reykit.rbase import CoroutineFunctionSimple
19
21
 
20
- from .rbase import ServerBase
21
- from .rfile import ServerAPIFile
22
+ from .rbase import ServerBase, generate_lifespan
22
23
 
23
24
 
24
25
  __all__ = (
@@ -49,31 +50,43 @@ class Server(ServerBase):
49
50
  ----------
50
51
  public : Public directory.
51
52
  depend : Global api dependencies.
52
- before : Before
53
+ before : Execute before server start.
54
+ after : Execute after server end.
53
55
  """
54
56
 
55
57
  # Parameter.
56
58
  if type(ssl_cert) != type(ssl_key):
57
59
  raise
60
+ lifespan = generate_lifespan(before, after)
61
+ if iscoroutinefunction(depend):
62
+ depend = (depend,)
63
+ dependencies = [
64
+ Depends(depend)
65
+ for task in depend
66
+ ]
58
67
 
59
68
  # Build.
60
- self.app = FastAPI()
61
- # self.index = Folder(public) + 'index.html'
62
69
  self.ssl_cert = ssl_cert
63
70
  self.ssl_key = ssl_key
64
71
 
65
- ## Middleware.
66
- self.app.add_middleware(GZipMiddleware)
67
- # self.app.add_middleware(TrustedHostMiddleware)
68
- # self.app.add_middleware(HTTPSRedirectMiddleware)
72
+ ## App.
73
+ self.app = FastAPI(
74
+ dependencies=dependencies,
75
+ lifespan=lifespan
76
+ )
69
77
 
70
78
  ## Static.
71
79
  if public is not None:
72
80
  subapp = StaticFiles(directory=public, html=True)
73
81
  self.app.mount('/', subapp)
74
82
 
83
+ ## Middleware.
84
+ self.app.add_middleware(GZipMiddleware)
85
+ # self.app.add_middleware(TrustedHostMiddleware)
86
+ # self.app.add_middleware(HTTPSRedirectMiddleware)
87
+
75
88
 
76
- def run(self):
89
+ def run(self) -> None:
77
90
  """
78
91
  Run.
79
92
  """
@@ -86,23 +99,26 @@ class Server(ServerBase):
86
99
  )
87
100
 
88
101
 
89
- def add_api_all(self):
102
+ # def add_api_all(self) -> None:
103
+ # """
104
+ # Add all API.
105
+ # """
90
106
 
91
- self.add_api_all()
107
+ # self.add_api_all()
92
108
 
93
109
 
94
- def add_api_base(self):
110
+ # def add_api_base(self):
95
111
 
96
- # @self.app.get('/')
97
- # def index():
98
- # file_bytes = File(self.index).bytes
99
- # response = HTMLResponse(file_bytes)
100
- # return response
112
+ # # @self.app.get('/')
113
+ # # def index():
114
+ # # file_bytes = File(self.index).bytes
115
+ # # response = HTMLResponse(file_bytes)
116
+ # # return response
101
117
 
102
118
 
103
- @self.app.get('/test')
104
- def test():
105
- return {'message': 'test'}
119
+ # @self.app.get('/test')
120
+ # def test():
121
+ # return {'message': 'test'}
106
122
 
107
123
 
108
124
  def add_api_file(self): ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reyserver
3
- Version: 1.1.37
3
+ Version: 1.1.39
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,10 @@
1
+ reyserver/__init__.py,sha256=ZNzM6wBvBSXe1LgV40ZJ1WIAbejWNYGl8x22dmt8C60,289
2
+ reyserver/rall.py,sha256=F-vUJIf5DgP0FXI0pdTrEeEiMtN-g81_yTo4ibUL9xk,233
3
+ reyserver/rbase.py,sha256=EVLCkX2xdPNdPwkRnZ2ZSjM7kctnMoQ_FSlSNHSZt2E,1630
4
+ reyserver/rdb.py,sha256=dfM3alvIII_eDgSCYhGfmQH7kh6eYsDmz4DaprYy9c8,170
5
+ reyserver/rfile.py,sha256=P3i1wa2JYsgNFrm0pDCsDN_zFo_Xpv4XigePotVzn8s,8213
6
+ reyserver/rserver.py,sha256=e-AFB75511HTOf7XjWerCNq86-QEm_2C81psMrldxls,3085
7
+ reyserver-1.1.39.dist-info/METADATA,sha256=EgXGwkne3cVZIovlCdQEjTngnnpWIjWVY35AO89_TTA,1549
8
+ reyserver-1.1.39.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
+ reyserver-1.1.39.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
10
+ reyserver-1.1.39.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- reyserver/__init__.py,sha256=ZNzM6wBvBSXe1LgV40ZJ1WIAbejWNYGl8x22dmt8C60,289
2
- reyserver/rall.py,sha256=F-vUJIf5DgP0FXI0pdTrEeEiMtN-g81_yTo4ibUL9xk,233
3
- reyserver/rbase.py,sha256=TtCmqjofwKkE0vdOIc5WhWxbgAUd8pFgx26-VON11yA,397
4
- reyserver/rfile.py,sha256=j4-X1WJPhiYXLvojMUBVEitlPTlSTKWgIb4uJSeMK6k,1989
5
- reyserver/rserver.py,sha256=PopXz0gu1c36FRgrpvT13BWFCOVwctb-JAlcFmocdjI,2580
6
- reyserver-1.1.37.dist-info/METADATA,sha256=DO6G-zowwi8w6vI74KWig-jtL6Zsz3hsrjZQ-j5ioQM,1549
7
- reyserver-1.1.37.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
- reyserver-1.1.37.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
9
- reyserver-1.1.37.dist-info/RECORD,,