reyserver 1.1.45__py3-none-any.whl → 1.1.46__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,11 +9,11 @@
9
9
  """
10
10
 
11
11
 
12
- from typing import Sequence
12
+ from typing import Sequence, Literal
13
13
  from inspect import iscoroutinefunction
14
14
  from contextlib import asynccontextmanager
15
- from fastapi import FastAPI
16
- from reykit.rbase import CoroutineFunctionSimple, Base, ConfigMeta
15
+ from fastapi import FastAPI, HTTPException, status
16
+ from reykit.rbase import CoroutineFunctionSimple, Base, ConfigMeta, Exit
17
17
 
18
18
  from . import rserver
19
19
 
@@ -21,6 +21,9 @@ from . import rserver
21
21
  __all__ = (
22
22
  'ServerBase',
23
23
  'ServerConfig',
24
+ 'ServerExit',
25
+ 'ServerExitHTTP',
26
+ 'ServerExitHTTP404',
24
27
  'create_lifespan',
25
28
  'create_depend_sess'
26
29
  )
@@ -41,6 +44,41 @@ class ServerConfig(ServerBase, metaclass=ConfigMeta):
41
44
  'Server instance.'
42
45
 
43
46
 
47
+ class ServerExit(ServerBase, Exit):
48
+ """
49
+ Server exit type.
50
+ """
51
+
52
+
53
+ class ServerExitHTTP(ServerExit, HTTPException):
54
+ """
55
+ Server HTTP exit type.
56
+ """
57
+
58
+ status_code: int
59
+
60
+
61
+ def __init__(self, text: str | None = None):
62
+ """
63
+ Build instance attributes.
64
+
65
+ Parameters
66
+ ----------
67
+ text : Explain text.
68
+ """
69
+
70
+ # Super.
71
+ super().__init__(self.status_code, text)
72
+
73
+
74
+ class ServerExitHTTP404(ServerExitHTTP):
75
+ """
76
+ Server HTTP 404 exit type.
77
+ """
78
+
79
+ status_code = status.HTTP_404_NOT_FOUND
80
+
81
+
44
82
  def create_lifespan(
45
83
  before: CoroutineFunctionSimple | Sequence[CoroutineFunctionSimple] | None = None,
46
84
  after: CoroutineFunctionSimple | Sequence[CoroutineFunctionSimple] | None = None,
@@ -88,13 +126,16 @@ def create_lifespan(
88
126
  return lifespan
89
127
 
90
128
 
91
- def create_depend_sess(database: str):
129
+ def create_depend_db(database: str, mode: Literal['sess', 'conn']):
92
130
  """
93
- Create dependencie function of asynchronous database session.
131
+ Create dependencie function of asynchronous database.
94
132
 
95
133
  Parameters
96
134
  ----------
97
135
  database : Database name.
136
+ mode : Mode.
137
+ - `Literl['sess']`: Create ORM session instance.
138
+ - `Literl['conn']`: Create connection instance.
98
139
 
99
140
  Returns
100
141
  -------
@@ -102,17 +143,21 @@ def create_depend_sess(database: str):
102
143
  """
103
144
 
104
145
 
105
- async def depend_sess():
146
+ async def depend():
106
147
  """
107
- Dependencie function of asynchronous database session.
148
+ Dependencie function of asynchronous database.
108
149
  """
109
150
 
110
151
  # Parameter.
111
152
  engine = ServerConfig.server.db[database]
112
153
 
113
154
  # Context.
114
- async with engine.orm.session() as sess:
115
- yield sess
155
+ if mode == 'sess':
156
+ async with engine.orm.session() as sess:
157
+ yield sess
158
+ elif mode == 'conn':
159
+ async with engine.connect() as conn:
160
+ yield conn
116
161
 
117
162
 
118
- return depend_sess
163
+ return depend
reyserver/rfile.py CHANGED
@@ -9,18 +9,28 @@
9
9
  """
10
10
 
11
11
 
12
- from typing import Annotated
13
- from fastapi import APIRouter, Form, File, UploadFile, Depends
12
+ from fastapi import (
13
+ APIRouter,
14
+ Depends,
15
+ Path,
16
+ Form,
17
+ File,
18
+ UploadFile
19
+ )
20
+ from fastapi.responses import FileResponse
14
21
  from reydb import rorm
22
+ from reydb.rorm import DatabaseORMSessionAsync
23
+ from reydb.rconn import DatabaseConnectionAsync
15
24
  from reykit.ros import FileStore, get_md5
16
25
 
17
- from .rbase import ServerConfig, create_depend_sess
26
+ from .rbase import ServerConfig, ServerExitHTTP404, create_depend_db
18
27
 
19
28
 
20
29
  __all__ = (
21
30
  'DatabaseORMTableInfo',
22
31
  'DatabaseORMTableData',
23
- 'build_file_db'
32
+ 'build_file_db',
33
+ 'file_router'
24
34
  )
25
35
 
26
36
 
@@ -50,61 +60,6 @@ class DatabaseORMTableData(rorm.Model, table=True):
50
60
  path: str = rorm.Field(rorm.types.VARCHAR(4095), not_null=True, comment='File disk storage path.')
51
61
 
52
62
 
53
- file_router = APIRouter()
54
- depend_sess_file = create_depend_sess('file')
55
-
56
-
57
- @file_router.post('/upload')
58
- async def upload(
59
- file: Annotated[UploadFile, File()],
60
- name: Annotated[str, Form()],
61
- note: Annotated[str, Form()],
62
- sess: Annotated[rorm.DatabaseORMSessionAsync, Depends(depend_sess_file)]
63
- ):
64
- """
65
- Upload file.
66
-
67
- Parameters
68
- ----------
69
- file : File instance.
70
- note : File note.
71
- sess : Asynchronous database session.
72
- """
73
-
74
- # Handle parameter.
75
- file_store = FileStore(ServerConfig.server.api_file_dir)
76
- file_bytes = await file.read()
77
- file_md5 = get_md5(file_bytes)
78
- file_size = len(file_bytes)
79
-
80
- # Upload.
81
- file_path = file_store.index(file_md5)
82
-
83
- ## Data.
84
- if file_path is None:
85
- file_path = file_store.store(file_bytes)
86
- table_data = DatabaseORMTableData(
87
- md5=file_md5,
88
- size=file_size,
89
- path=file_path
90
- )
91
- await sess.add(table_data)
92
-
93
- ## Information.
94
- table_info = DatabaseORMTableInfo(
95
- md5=file_md5,
96
- name=name,
97
- note=note
98
- )
99
- await sess.add(table_info)
100
-
101
- # Get ID.
102
- await sess.flush()
103
- file_id = table_info.file_id
104
-
105
- return {'file_id': file_id}
106
-
107
-
108
63
  def build_file_db() -> None:
109
64
  """
110
65
  Check and build `file` database tables.
@@ -226,3 +181,104 @@ def build_file_db() -> None:
226
181
 
227
182
  # Build.
228
183
  engine.sync_engine.build.build(tables=tables, views=views, views_stats=views_stats, skip=True)
184
+
185
+
186
+ file_router = APIRouter()
187
+ depend_file_sess = create_depend_db('file', 'sess')
188
+ depend_file_conn = create_depend_db('file', 'conn')
189
+
190
+
191
+ @file_router.post('/')
192
+ async def upload_file(
193
+ file: UploadFile = File(),
194
+ name: str = Form(None),
195
+ note: str = Form(None),
196
+ sess: rorm.DatabaseORMSessionAsync = Depends(depend_file_sess)
197
+ ) -> dict:
198
+ """
199
+ Upload file.
200
+
201
+ Parameters
202
+ ----------
203
+ file : File instance.
204
+ note : File note.
205
+ """
206
+
207
+ # Handle parameter.
208
+ file_store = FileStore(ServerConfig.server.api_file_dir)
209
+ file_bytes = await file.read()
210
+ file_md5 = get_md5(file_bytes)
211
+ file_size = len(file_bytes)
212
+
213
+ # Upload.
214
+ file_path = file_store.index(file_md5)
215
+
216
+ ## Data.
217
+ if file_path is None:
218
+ file_path = file_store.store(file_bytes)
219
+ table_data = DatabaseORMTableData(
220
+ md5=file_md5,
221
+ size=file_size,
222
+ path=file_path
223
+ )
224
+ await sess.add(table_data)
225
+
226
+ ## Information.
227
+ table_info = DatabaseORMTableInfo(
228
+ md5=file_md5,
229
+ name=name,
230
+ note=note
231
+ )
232
+ await sess.add(table_info)
233
+
234
+ # Get ID.
235
+ await sess.flush()
236
+ file_id = table_info.file_id
237
+
238
+ return {'file_id': file_id}
239
+
240
+
241
+ @file_router.get('/{file_id}/download')
242
+ async def download_file(
243
+ file_id: int = Path(),
244
+ conn: DatabaseConnectionAsync = Depends(depend_file_conn)
245
+ ) -> FileResponse:
246
+ """
247
+ Download file.
248
+
249
+ Parameters
250
+ ----------
251
+ file_id : File ID.
252
+ """
253
+
254
+ # Search.
255
+ sql = (
256
+ 'SELECT `name`, (\n'
257
+ ' SELECT `path`\n'
258
+ f' FROM `{conn.engine.database}`.`data`\n'
259
+ f' WHERE `md5` = `info`.`md5`\n'
260
+ ' LIMIT 1\n'
261
+ ') AS `path`\n'
262
+ f'FROM `{conn.engine.database}`.`info`\n'
263
+ 'WHERE `file_id` = :file_id\n'
264
+ 'LIMIT 1'
265
+ )
266
+ result = await conn.execute(sql, file_id=file_id)
267
+
268
+ # Check.
269
+ if result.empty:
270
+ text = "file ID '%s' not exist" % file_id
271
+ raise ServerExitHTTP404(text)
272
+ file_name, file_path = result.first()
273
+
274
+ # Response.
275
+ response = FileResponse(file_path, filename=file_name)
276
+
277
+ return response
278
+
279
+
280
+ @file_router.get('/{file_id}')
281
+ async def get_file_info(
282
+ file_id: int = Path(),
283
+ sess: DatabaseConnectionAsync = Depends(depend_file_sess)
284
+ ) -> dict: ...
reyserver/rserver.py CHANGED
@@ -80,7 +80,7 @@ class Server(ServerBase, Singleton):
80
80
  self.app = FastAPI(
81
81
  dependencies=depend,
82
82
  lifespan=lifespan,
83
- # debug=True
83
+ debug=True
84
84
  )
85
85
 
86
86
  ## Static.
@@ -131,4 +131,4 @@ class Server(ServerBase, Singleton):
131
131
 
132
132
  # Add.
133
133
  self.api_file_dir = file_dir
134
- self.app.include_router(file_router, prefix='/file')
134
+ self.app.include_router(file_router, prefix='/files', tags=['file'])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reyserver
3
- Version: 1.1.45
3
+ Version: 1.1.46
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=JmDkcdP7oxmehE53rRRL_zhxmPinAbJRONpQp5qhsGI,340
2
+ reyserver/rall.py,sha256=6vqacf9erEfVfK5sgGJMDXD1h4ebt5gDN7NrPdtELms,248
3
+ reyserver/rbase.py,sha256=6mfTSuVzJGTK6Ch0HNlN0bmKIkPMLUlUAAE_EGt-1uo,3298
4
+ reyserver/rclient.py,sha256=OjY_OEH_USrJK8TuCevXG6jNPVbM0lN-xMBozB7kSM8,2195
5
+ reyserver/rfile.py,sha256=pA1LdHjBDJwj7S2k7cemwsHO19Oo0bHtkX_r79RBlJA,8770
6
+ reyserver/rserver.py,sha256=lsOr_fhiAukQhLHfqH1OfZByuUEzjFoJtqLI7BoNxvI,3401
7
+ reyserver-1.1.46.dist-info/METADATA,sha256=3E8xJWPt5S6j3piVBeuAbFSXzqbTHq6hhrEp5EJ0YLg,1658
8
+ reyserver-1.1.46.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
+ reyserver-1.1.46.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
10
+ reyserver-1.1.46.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- reyserver/__init__.py,sha256=JmDkcdP7oxmehE53rRRL_zhxmPinAbJRONpQp5qhsGI,340
2
- reyserver/rall.py,sha256=6vqacf9erEfVfK5sgGJMDXD1h4ebt5gDN7NrPdtELms,248
3
- reyserver/rbase.py,sha256=khBk6N0G33hstiisDWtz4Tdg1tvnFrGszOynBtnKxsI,2305
4
- reyserver/rclient.py,sha256=OjY_OEH_USrJK8TuCevXG6jNPVbM0lN-xMBozB7kSM8,2195
5
- reyserver/rfile.py,sha256=wf56tItty7Jbjcwvh1yMgcX2jlSSXAYbTDKnA5dI8Yw,7459
6
- reyserver/rserver.py,sha256=SfGs0W6StFtRtJ_UYI_avlL99euw7VvP55248mSOjLw,3387
7
- reyserver-1.1.45.dist-info/METADATA,sha256=N4nGN_EC1Czdd7Q5DIHr4mxM3NNiPQCgjDenIZJ25yg,1658
8
- reyserver-1.1.45.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
- reyserver-1.1.45.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
10
- reyserver-1.1.45.dist-info/RECORD,,