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 +55 -10
- reyserver/rfile.py +115 -59
- reyserver/rserver.py +2 -2
- {reyserver-1.1.45.dist-info → reyserver-1.1.46.dist-info}/METADATA +1 -1
- reyserver-1.1.46.dist-info/RECORD +10 -0
- reyserver-1.1.45.dist-info/RECORD +0 -10
- {reyserver-1.1.45.dist-info → reyserver-1.1.46.dist-info}/WHEEL +0 -0
- {reyserver-1.1.45.dist-info → reyserver-1.1.46.dist-info}/licenses/LICENSE +0 -0
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
|
129
|
+
def create_depend_db(database: str, mode: Literal['sess', 'conn']):
|
92
130
|
"""
|
93
|
-
Create dependencie function of asynchronous database
|
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
|
146
|
+
async def depend():
|
106
147
|
"""
|
107
|
-
Dependencie function of asynchronous database
|
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
|
-
|
115
|
-
|
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
|
163
|
+
return depend
|
reyserver/rfile.py
CHANGED
@@ -9,18 +9,28 @@
|
|
9
9
|
"""
|
10
10
|
|
11
11
|
|
12
|
-
from
|
13
|
-
|
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,
|
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
|
-
|
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'])
|
@@ -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,,
|
File without changes
|
File without changes
|