GNServer 0.0.0.0.6__py3-none-any.whl → 0.0.0.0.8__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.
- GNServer/__init__.py +1 -1
- GNServer/_app.py +79 -37
- {gnserver-0.0.0.0.6.dist-info → gnserver-0.0.0.0.8.dist-info}/METADATA +1 -1
- gnserver-0.0.0.0.8.dist-info/RECORD +7 -0
- GNServer/models.py +0 -25
- gnserver-0.0.0.0.6.dist-info/RECORD +0 -8
- {gnserver-0.0.0.0.6.dist-info → gnserver-0.0.0.0.8.dist-info}/WHEEL +0 -0
- {gnserver-0.0.0.0.6.dist-info → gnserver-0.0.0.0.8.dist-info}/licenses/LICENSE +0 -0
- {gnserver-0.0.0.0.6.dist-info → gnserver-0.0.0.0.8.dist-info}/top_level.txt +0 -0
GNServer/__init__.py
CHANGED
GNServer/_app.py
CHANGED
@@ -110,6 +110,59 @@ def guess_type(filename: str) -> str:
|
|
110
110
|
return mime_map.get(ext, "application/octet-stream")
|
111
111
|
|
112
112
|
|
113
|
+
import re
|
114
|
+
from typing import List
|
115
|
+
|
116
|
+
# regex для !{var}, поддерживает вложенность через точку
|
117
|
+
TPL_VAR_RE = re.compile(r'(?<!\\)!\{([A-Za-z_][A-Za-z0-9_\.]*)\}')
|
118
|
+
|
119
|
+
# список mime, которые считаем текстовыми
|
120
|
+
TEXTUAL_MIME_PREFIXES = [
|
121
|
+
"text/", # text/html, text/css, text/plain
|
122
|
+
]
|
123
|
+
TEXTUAL_MIME_EXACT = {
|
124
|
+
"application/javascript",
|
125
|
+
"application/json",
|
126
|
+
"application/xml",
|
127
|
+
"application/xhtml+xml"
|
128
|
+
}
|
129
|
+
TEXTUAL_MIME_SUFFIXES = (
|
130
|
+
"+xml", # например application/rss+xml
|
131
|
+
"+json", # application/ld+json
|
132
|
+
)
|
133
|
+
|
134
|
+
def extract_template_vars(filedata: bytes, mime: str) -> List[str]:
|
135
|
+
"""
|
136
|
+
Ищет все !{var} в тексте, если MIME относится к текстовым.
|
137
|
+
"""
|
138
|
+
mime = (mime or "").lower().strip()
|
139
|
+
|
140
|
+
# определяем, текстовый ли mime
|
141
|
+
is_textual = (
|
142
|
+
mime.startswith(tuple(TEXTUAL_MIME_PREFIXES))
|
143
|
+
or mime in TEXTUAL_MIME_EXACT
|
144
|
+
or mime.endswith(TEXTUAL_MIME_SUFFIXES)
|
145
|
+
or "javascript" in mime
|
146
|
+
or "json" in mime
|
147
|
+
or "xml" in mime
|
148
|
+
)
|
149
|
+
|
150
|
+
if not is_textual:
|
151
|
+
return []
|
152
|
+
|
153
|
+
try:
|
154
|
+
text = filedata.decode("utf-8", errors="ignore")
|
155
|
+
except Exception:
|
156
|
+
return []
|
157
|
+
|
158
|
+
return list(set(m.group(1) for m in TPL_VAR_RE.finditer(text)))
|
159
|
+
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
|
113
166
|
@dataclass
|
114
167
|
class Route:
|
115
168
|
method: str
|
@@ -230,6 +283,7 @@ class App:
|
|
230
283
|
|
231
284
|
def delete(self, path: str, *, cors: Optional[gn.CORSObject] = None):
|
232
285
|
return self.route("DELETE", path, cors)
|
286
|
+
|
233
287
|
|
234
288
|
def setRouteCors(self, cors: Optional[gn.CORSObject] = None):
|
235
289
|
self._cors = cors
|
@@ -287,7 +341,23 @@ class App:
|
|
287
341
|
return gn.GNResponse("gn:backend:404", {'error': 'Not Found'})
|
288
342
|
|
289
343
|
|
290
|
-
def
|
344
|
+
def fastFile(self, path: str, file_path: str, cors: Optional[gn.CORSObject] = None):
|
345
|
+
@self.get(path)
|
346
|
+
async def r_static():
|
347
|
+
nonlocal file_path
|
348
|
+
if file_path.endswith('/'):
|
349
|
+
file_path = file_path[:-1]
|
350
|
+
|
351
|
+
if not os.path.isfile(file_path):
|
352
|
+
return gn.GNResponse('gn:backend:404', cors=cors)
|
353
|
+
|
354
|
+
mime_type = guess_type(file_path.split('/')[-1])
|
355
|
+
async with aiofiles.open(file_path, "rb") as f:
|
356
|
+
data = await f.read()
|
357
|
+
|
358
|
+
return gn.GNResponse('ok', {'files': [{'mime-type': mime_type, 'data': data}]}, cors=cors)
|
359
|
+
|
360
|
+
def static(self, path: str, dir_path: str, cors: Optional[gn.CORSObject] = None):
|
291
361
|
@self.get(f"{path}/{{_path:path}}")
|
292
362
|
async def r_static(_path: str):
|
293
363
|
file_path = os.path.join(dir_path, _path)
|
@@ -296,13 +366,13 @@ class App:
|
|
296
366
|
file_path = file_path[:-1]
|
297
367
|
|
298
368
|
if not os.path.isfile(file_path):
|
299
|
-
return gn.GNResponse('gn:backend:404')
|
369
|
+
return gn.GNResponse('gn:backend:404', cors=cors)
|
300
370
|
|
301
371
|
mime_type = guess_type(file_path.split('/')[-1])
|
302
372
|
async with aiofiles.open(file_path, "rb") as f:
|
303
373
|
data = await f.read()
|
304
374
|
|
305
|
-
return gn.GNResponse('ok', {'files': [{'mime-type': mime_type, 'data': data}]})
|
375
|
+
return gn.GNResponse('ok', {'files': [{'mime-type': mime_type, 'data': data}]}, cors=cors)
|
306
376
|
|
307
377
|
|
308
378
|
|
@@ -412,14 +482,10 @@ class App:
|
|
412
482
|
|
413
483
|
response = await self._api.dispatch(request)
|
414
484
|
|
415
|
-
|
416
|
-
response = await self.resolve_extra_response(response)
|
417
|
-
|
418
|
-
|
419
485
|
if inspect.isasyncgen(response):
|
420
486
|
async for chunk in response: # type: ignore[misc]
|
421
487
|
chunk._stream = True
|
422
|
-
chunk = self.resolve_response(chunk)
|
488
|
+
chunk = await self.resolve_response(chunk)
|
423
489
|
self._quic.send_stream_data(request.stream_id, chunk.serialize(3), end_stream=False)
|
424
490
|
self.transmit()
|
425
491
|
|
@@ -431,14 +497,14 @@ class App:
|
|
431
497
|
return
|
432
498
|
|
433
499
|
|
434
|
-
response = self.resolve_response(response)
|
500
|
+
response = await self.resolve_response(response)
|
435
501
|
self._quic.send_stream_data(request.stream_id, response.serialize(3), end_stream=True)
|
436
502
|
logger.debug(f'Отправлен на сервер ответ -> {response.command} {response.payload if response.payload and len((response.payload)) < 200 else ''}')
|
437
503
|
self.transmit()
|
438
504
|
except Exception as e:
|
439
505
|
logger.error('GNServer: error\n' + traceback.format_exc())
|
440
506
|
|
441
|
-
response = gn.GNResponse('gn:backend:500
|
507
|
+
response = gn.GNResponse('gn:backend:500')
|
442
508
|
self._quic.send_stream_data(request.stream_id, response.serialize(3), end_stream=True)
|
443
509
|
self.transmit()
|
444
510
|
|
@@ -446,36 +512,12 @@ class App:
|
|
446
512
|
if response._cors is None:
|
447
513
|
response._cors = self._api._cors
|
448
514
|
|
515
|
+
await response.assembly()
|
516
|
+
|
449
517
|
return response
|
450
518
|
|
451
519
|
|
452
|
-
|
453
|
-
|
454
|
-
file_types = (
|
455
|
-
'html',
|
456
|
-
'css',
|
457
|
-
'js',
|
458
|
-
'svg'
|
459
|
-
)
|
460
|
-
|
461
|
-
if isinstance(response, gn.GNResponse):
|
462
|
-
payload = response.payload
|
463
|
-
if payload is not None:
|
464
|
-
for ext_file in file_types:
|
465
|
-
ext_file_ = payload.get(ext_file)
|
466
|
-
if ext_file_ is not None:
|
467
|
-
if isinstance(ext_file_, str):
|
468
|
-
if ext_file_.startswith('/') or ext_file_.startswith('./'):
|
469
|
-
try:
|
470
|
-
async with await anyio.open_file(ext_file_, mode="rb") as file:
|
471
|
-
data = await file.read()
|
472
|
-
|
473
|
-
payload.pop(ext_file)
|
474
|
-
payload['files'] = [{'mime-type': guess_type(ext_file_.split('/')[-1]), 'data': data}]
|
475
|
-
except Exception as e:
|
476
|
-
payload[ext_file] = f'GNServer error: {e}'
|
477
|
-
logger.debug(f'error resolving extra response -> {traceback.format_exc()}')
|
478
|
-
return response
|
520
|
+
|
479
521
|
|
480
522
|
|
481
523
|
|
@@ -0,0 +1,7 @@
|
|
1
|
+
GNServer/__init__.py,sha256=l9B6gZZ2gTkWvNdla--J15_Bj0BtP8RyPsrMI6amgDo,1376
|
2
|
+
GNServer/_app.py,sha256=WM7gJqSzgUl7fHNPLhzGeyfJOSC4yj4dUZxowSSbWms,18340
|
3
|
+
gnserver-0.0.0.0.8.dist-info/licenses/LICENSE,sha256=WH_t7dKZyWJ5Ld07eYIkUG4Tv6zZWXtAdsUqYAUesn0,1084
|
4
|
+
gnserver-0.0.0.0.8.dist-info/METADATA,sha256=WCL7mc5UxeFicgVe2tLAw0oyCqKlgpGJ0uPg-bygirU,804
|
5
|
+
gnserver-0.0.0.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
6
|
+
gnserver-0.0.0.0.8.dist-info/top_level.txt,sha256=-UOUBuD4u7Qkb1o5PdcwyA3kx8xCH2lwy0tJHi26Wb4,9
|
7
|
+
gnserver-0.0.0.0.8.dist-info/RECORD,,
|
GNServer/models.py
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
GNServer/__init__.py,sha256=T6kT6WoJpmIXashMSHfuafb9DSePUIzw192h27j9a4M,1364
|
2
|
-
GNServer/_app.py,sha256=8JcVzozjtdLtQjPThi-wR2OaAYNvYfaIqDReCmmFmrg,17801
|
3
|
-
GNServer/models.py,sha256=3HTbPgXMcltK3hh3RWii0z6X2qYitXmPWzud8pOc4Rk,50
|
4
|
-
gnserver-0.0.0.0.6.dist-info/licenses/LICENSE,sha256=WH_t7dKZyWJ5Ld07eYIkUG4Tv6zZWXtAdsUqYAUesn0,1084
|
5
|
-
gnserver-0.0.0.0.6.dist-info/METADATA,sha256=S5wZWPijiIAS9Kd8BkMeKs7PJtY0CMeMVvp149dQyhI,804
|
6
|
-
gnserver-0.0.0.0.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
-
gnserver-0.0.0.0.6.dist-info/top_level.txt,sha256=-UOUBuD4u7Qkb1o5PdcwyA3kx8xCH2lwy0tJHi26Wb4,9
|
8
|
-
gnserver-0.0.0.0.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|