Nexom 1.0.7__py3-none-any.whl → 1.0.9__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.
nexom/app/auth.py CHANGED
@@ -133,8 +133,7 @@ class AuthService:
133
133
  def handler(self, environ: dict) -> JsonResponse:
134
134
  req = Request(environ)
135
135
  try:
136
- route = self.routing.get(req.path)
137
- return route.call_handler(req)
136
+ return self.routing.handle(req)
138
137
 
139
138
  except NexomError as e:
140
139
  # error code -> proper HTTP status
nexom/app/middleware.py CHANGED
@@ -48,4 +48,88 @@ class MiddlewareChain:
48
48
 
49
49
  return call_at(0, request, args)
50
50
 
51
- return wrapped
51
+ return wrapped
52
+
53
+
54
+ class CORSMiddleware:
55
+ def __init__(
56
+ self,
57
+ allowed_origins: list[str] | None = None, # None or ["*"] = allow all
58
+ allowed_methods: list[str] | None = None,
59
+ allowed_headers: list[str] | None = None,
60
+ access_control_allow_credentials: bool = False,
61
+ max_age: int | None = 600,
62
+ ) -> None:
63
+ self.allowed_origins = allowed_origins or ["*"]
64
+ self.allowed_methods = [m.upper() for m in (allowed_methods or ["GET", "POST", "PUT", "DELETE", "OPTIONS"])]
65
+ self.allowed_headers = allowed_headers or ["Content-Type", "Authorization"]
66
+ self.allow_credentials = access_control_allow_credentials
67
+ self.max_age = max_age
68
+
69
+ def _is_allowed_origin(self, origin: str) -> bool:
70
+ return "*" in self.allowed_origins or origin in self.allowed_origins
71
+
72
+ def _append_vary_origin(self, res: Response) -> None:
73
+ # append_header が単純 append なので、重複しないように軽くケア
74
+ for k, v in getattr(res, "headers", []):
75
+ if k.lower() == "vary":
76
+ # 既に Vary があるなら Origin が含まれてるかだけチェック
77
+ if "origin" in [p.strip().lower() for p in v.split(",")]:
78
+ return
79
+ res.append_header("Vary", v + ", Origin")
80
+ return
81
+ res.append_header("Vary", "Origin")
82
+
83
+ def __call__(self, request: Request, args: dict[str, str | None], next_: Handler) -> Response:
84
+ origin = request.headers.get("origin")
85
+ if not origin:
86
+ return next_(request, args)
87
+
88
+ if not self._is_allowed_origin(origin):
89
+ return next_(request, args)
90
+
91
+ # preflight 判定
92
+ acrm = request.headers.get("access-control-request-method")
93
+ is_preflight = request.method == "OPTIONS" and acrm is not None
94
+
95
+ if is_preflight:
96
+ # 204で十分(body無し)
97
+ res = Response(b"", status=204)
98
+ else:
99
+ res = next_(request, args)
100
+
101
+ # Allow-Origin(単一 or *)
102
+ if "*" in self.allowed_origins and not self.allow_credentials:
103
+ res.append_header("Access-Control-Allow-Origin", "*")
104
+ else:
105
+ # credentials=True なら必ず echo(*は禁止)
106
+ res.append_header("Access-Control-Allow-Origin", origin)
107
+ self._append_vary_origin(res)
108
+
109
+ # Allow-Credentials
110
+ if self.allow_credentials:
111
+ res.append_header("Access-Control-Allow-Credentials", "true")
112
+
113
+ # Allow-Methods
114
+ if self.allowed_methods == ["*"]:
115
+ req_method = request.headers.get("access-control-request-method")
116
+ res.append_header("Access-Control-Allow-Methods", req_method or "GET, POST, PUT, DELETE, OPTIONS")
117
+ else:
118
+ res.append_header("Access-Control-Allow-Methods", ", ".join(self.allowed_methods))
119
+
120
+ # Allow-Headers
121
+ if self.allowed_headers == ["*"]:
122
+ req_headers = request.headers.get("access-control-request-headers")
123
+ # ブラウザが要求してきたヘッダをそのまま許可
124
+ if req_headers:
125
+ res.append_header("Access-Control-Allow-Headers", req_headers)
126
+ else:
127
+ res.append_header("Access-Control-Allow-Headers", "Content-Type, Authorization")
128
+ else:
129
+ res.append_header("Access-Control-Allow-Headers", ", ".join(self.allowed_headers))
130
+
131
+
132
+ if self.max_age is not None:
133
+ res.append_header("Access-Control-Max-Age", str(self.max_age))
134
+
135
+ return res
nexom/app/path.py CHANGED
@@ -48,7 +48,7 @@ class Path:
48
48
  self.path_args[idx] = m.group(1)
49
49
 
50
50
  if detection_index == 0:
51
- detection_index = 0 if path == "" else len(path_segments)
51
+ detection_index = len(path_segments)
52
52
 
53
53
  self.path: str = "/".join(path_segments[:detection_index])
54
54
  self.detection_range: int = detection_index
@@ -192,4 +192,10 @@ class Router(list[Path]):
192
192
 
193
193
  if self.raise_if_not_exist:
194
194
  raise PathNotFoundError(request_path)
195
- return None
195
+ return None
196
+
197
+ def handle(self, request: Request) -> Response:
198
+ path = self.get(request.path, method=request.method)
199
+ if path is None:
200
+ raise PathNotFoundError(request.path)
201
+ return path.call_handler(request, tuple(self.middlewares))
nexom/app/response.py CHANGED
@@ -62,6 +62,9 @@ class Response:
62
62
  """
63
63
  yield self.body
64
64
 
65
+ def append_header(self, key: str, value: str) -> None:
66
+ self.headers.append((key, value))
67
+
65
68
  class HtmlResponse(Response):
66
69
  def __init__(
67
70
  self,
@@ -2,7 +2,7 @@
2
2
  <Insert main>
3
3
  <div class="thumbnail-container">
4
4
  <div class="message-card">
5
- <h1>Welcome Nexom v1.0.7 Deeeeeeeev</h1>
5
+ <h1>Welcome Nexom v1.0.9 Deeeeeeeev</h1>
6
6
  <p>How are you? I'm fine! Thank you!</p>
7
7
  </div>
8
8
  <div class="accounts-card">
nexom/assets/app/wsgi.py CHANGED
@@ -39,8 +39,7 @@ def app(environ: dict, start_response: Callable) -> Iterable[bytes]:
39
39
  path = req.path
40
40
  method = req.method
41
41
 
42
- p = routing.get(path, method=method)
43
- res = p.call_handler(req)
42
+ res = routing.handle(req)
44
43
 
45
44
  except PathNotFoundError as e:
46
45
  logger.warn(str(e))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Nexom
3
- Version: 1.0.7
3
+ Version: 1.0.9
4
4
  Summary: Lightweight Python Web Framework (WSGI)
5
5
  Author: TouriAida
6
6
  License: MIT License
@@ -1,21 +1,21 @@
1
1
  nexom/__init__.py,sha256=wMpCghSUTLo67kGxKbaK2QnU7DpfICyfp3-XbDObLSA,367
2
2
  nexom/__main__.py,sha256=VKT7WSe6552_mzwLwJqa55tfag5Olr28eYVhVnf9kWY,6196
3
3
  nexom/app/__init__.py,sha256=DgKTx8GtuRJkiRwBKmLhCLlf3l19zTWwXEmeRPznzlM,1089
4
- nexom/app/auth.py,sha256=u_srAJPVKNKr7W0yuQdY18xXSRUuxmymKaBZysdU8vI,13790
4
+ nexom/app/auth.py,sha256=ahLgrzV7v1-QFjoLu5bk69_VjQhto5jiibc3Dq8zh1I,13744
5
5
  nexom/app/cookie.py,sha256=VCau9i8z6QlkaE_s8gKZXjcFtrV2bX1T4xssNtpJ5A0,2026
6
6
  nexom/app/db.py,sha256=fl3SrNPOltTT3d5PqjISdS4u7SXEMRqLPotbJE0FXsY,3320
7
7
  nexom/app/http_status_codes.py,sha256=R4ka3n4rijqvfahF5n5kS-Qrg8bZSsrvF8lGnpKWAgY,1934
8
- nexom/app/middleware.py,sha256=7bHGtEem92QhgULOvTFDFlZ4K719CQgu9RKj1g7XOW8,1429
9
- nexom/app/path.py,sha256=LdlJg7QRh7Zwgw5TsHzPF9ZYiIhlcNjOOEX5LEaud-o,6006
8
+ nexom/app/middleware.py,sha256=PO3V76pdnPsKwVqxM9Lx02vqsTQeLEDU-bLJpZqUhvs,5017
9
+ nexom/app/path.py,sha256=ELSqfuyZyTTZqo1m0rpg8IydZ8e3FMDy1drbNqU2tmk,6241
10
10
  nexom/app/request.py,sha256=4dvcfrrS9gKzVFtoizRw5eFvP7eHRXsavh21zEHXX2U,8867
11
- nexom/app/response.py,sha256=a5297XDlDkoArymss-oxLTJ1a0I-MNjFUvFel0CQnN8,4483
11
+ nexom/app/response.py,sha256=1HznRpImlLTdnKb8r0Pd-5DAMkOK7HzoaL7qjbQ68Xo,4585
12
12
  nexom/app/template.py,sha256=sGaO39EPrPs-K_4gUxdhtEOyjzYyEPTUzpBzJqFP-_Y,3650
13
13
  nexom/app/user.py,sha256=6KLLsb08Ma39CnLBbq5y290o33tfxtHFWu9lu1kT0NM,623
14
14
  nexom/assets/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  nexom/assets/app/config.py,sha256=Ltpj40PE1--s0eRPTHiJNFqG8pFA7Lb6x02C39kwoYA,765
16
16
  nexom/assets/app/gunicorn.conf.py,sha256=0kAQ7ddfpnPpqLAZxIXqYPF6EuUzkjIC9aOTqfxn6rc,149
17
17
  nexom/assets/app/router.py,sha256=Rnm_H6k6fIAhNaP62IP7lcSsZ0O2q_TfSyYQWd9tdh4,412
18
- nexom/assets/app/wsgi.py,sha256=o-GtCPPHKiavTOmr8qxqCY95iUFU_IvRmwLqLK3ecwU,1743
18
+ nexom/assets/app/wsgi.py,sha256=5wk74UzxVE5lnWwtXFcbjOW7Fz9nz9NWG9lq8Q6bzbw,1698
19
19
  nexom/assets/app/__pycache__/__init__.cpython-313.pyc,sha256=5qiNgiTI26RZcyxr09EKigB5YYYBSqRJxyq8LCVWR14,195
20
20
  nexom/assets/app/pages/__init__.py,sha256=DlX9iwM39JaMKfzK4mbgjE2uVMX3ax0wd2mCVnpg0b4,61
21
21
  nexom/assets/app/pages/_templates.py,sha256=1c8KUwfMHkoo7S4neFbYjs5ixK44evn-uFVAFR9Y834,213
@@ -26,7 +26,7 @@ nexom/assets/app/static/dog.jpeg,sha256=qGEhcg48FxRLRISzbmXvw1qvfbdluC_ukM8ZrHg7
26
26
  nexom/assets/app/static/github.png,sha256=DUwjX--e_sVBdKfABfwP4M4tY9NcIYmKtRSFhxETl6k,6223
27
27
  nexom/assets/app/static/style.css,sha256=9LxUcxaQ8ppnHEncJPuO-Mt_DYCUFv56wzlHq5ed264,12609
28
28
  nexom/assets/app/templates/base.html,sha256=RVW_63gA1sr7pqNkfB6Djk5an_VFpowahDIHGjfV-Wk,349
29
- nexom/assets/app/templates/default.html,sha256=iugOU0K39oLzgak_DtszWM_1dptscQlXVDldVg5-Qxw,617
29
+ nexom/assets/app/templates/default.html,sha256=FGBT6ilK0uiFlAYibgYgEqoYe-EfLx_13m42C48R9FM,617
30
30
  nexom/assets/app/templates/document.html,sha256=XzKxibNf_i5DXBN2MHwLu2_Y5KNr9hSgIYxlY5l3gT8,5694
31
31
  nexom/assets/app/templates/footer.html,sha256=mh0mm4f1nHIGV3cjDFFi8gpYwEpgk5Jqpbjokh9DPbA,94
32
32
  nexom/assets/app/templates/header.html,sha256=ZqtFQtL7hP8Rf5naU8jMSfE6Ce1ktA9KRb2g0PbF8Kc,341
@@ -49,9 +49,9 @@ nexom/core/log.py,sha256=q0FYz1-kkTUHvNkEBW5V1zvUv1phMYchOqwAfUXGHQI,3016
49
49
  nexom/core/object_html_render.py,sha256=4yT3Aihia4oG62gr1rqZd757wEXsu78mTCCpjpVE7JM,7027
50
50
  nexom/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
51
  nexom/templates/auth.py,sha256=q-kNIpi2M1UH8fmEy7lmcnM1hCTpj9r78wHXgnW-Aak,4617
52
- nexom-1.0.7.dist-info/licenses/LICENSE,sha256=YbcHyxYLJ5jePeMS_NXpR9yiZjP5skhn32LvcKeknJw,1066
53
- nexom-1.0.7.dist-info/METADATA,sha256=HmkdqKobbJLQEjaQ4K3KpRDbrQVMjQUFls8Ld9UDyGs,5896
54
- nexom-1.0.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
55
- nexom-1.0.7.dist-info/entry_points.txt,sha256=0r1egKZmF1MUo25AKPt-2d55sk5Q_EIBOQwD3JC8RgI,46
56
- nexom-1.0.7.dist-info/top_level.txt,sha256=mTyUruscL3rqXvYJRo8KGwDM6PWXRzgf4CamLr8LSX4,6
57
- nexom-1.0.7.dist-info/RECORD,,
52
+ nexom-1.0.9.dist-info/licenses/LICENSE,sha256=YbcHyxYLJ5jePeMS_NXpR9yiZjP5skhn32LvcKeknJw,1066
53
+ nexom-1.0.9.dist-info/METADATA,sha256=aaGusVrxjOLjB7gLbh-Xd_R2SdAiFKf0Brf-6R6UiGk,5896
54
+ nexom-1.0.9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
55
+ nexom-1.0.9.dist-info/entry_points.txt,sha256=0r1egKZmF1MUo25AKPt-2d55sk5Q_EIBOQwD3JC8RgI,46
56
+ nexom-1.0.9.dist-info/top_level.txt,sha256=mTyUruscL3rqXvYJRo8KGwDM6PWXRzgf4CamLr8LSX4,6
57
+ nexom-1.0.9.dist-info/RECORD,,
File without changes