svc-infra 0.1.616__py3-none-any.whl → 0.1.617__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.
Potentially problematic release.
This version of svc-infra might be problematic. Click here for more details.
- svc_infra/api/fastapi/middleware/ratelimit.py +8 -0
- svc_infra/cli/cmds/docs/docs_cmds.py +3 -71
- {svc_infra-0.1.616.dist-info → svc_infra-0.1.617.dist-info}/METADATA +1 -1
- {svc_infra-0.1.616.dist-info → svc_infra-0.1.617.dist-info}/RECORD +6 -6
- {svc_infra-0.1.616.dist-info → svc_infra-0.1.617.dist-info}/WHEEL +0 -0
- {svc_infra-0.1.616.dist-info → svc_infra-0.1.617.dist-info}/entry_points.txt +0 -0
|
@@ -27,6 +27,10 @@ class SimpleRateLimitMiddleware(BaseHTTPMiddleware):
|
|
|
27
27
|
limit_resolver=None,
|
|
28
28
|
# If True, automatically scopes the bucket key by tenant id when available
|
|
29
29
|
scope_by_tenant: bool = False,
|
|
30
|
+
# When True, allows unresolved tenant IDs to fall back to an "X-Tenant-Id" header value.
|
|
31
|
+
# Disabled by default to avoid trusting arbitrary client-provided headers which could
|
|
32
|
+
# otherwise be used to evade per-tenant limits when authentication fails.
|
|
33
|
+
allow_untrusted_tenant_header: bool = False,
|
|
30
34
|
store: RateLimitStore | None = None,
|
|
31
35
|
):
|
|
32
36
|
super().__init__(app)
|
|
@@ -34,6 +38,7 @@ class SimpleRateLimitMiddleware(BaseHTTPMiddleware):
|
|
|
34
38
|
self.key_fn = key_fn or (lambda r: r.headers.get("X-API-Key") or r.client.host)
|
|
35
39
|
self._limit_resolver = limit_resolver
|
|
36
40
|
self.scope_by_tenant = scope_by_tenant
|
|
41
|
+
self._allow_untrusted_tenant_header = allow_untrusted_tenant_header
|
|
37
42
|
self.store = store or InMemoryRateLimitStore(limit=limit)
|
|
38
43
|
|
|
39
44
|
async def dispatch(self, request, call_next):
|
|
@@ -45,6 +50,9 @@ class SimpleRateLimitMiddleware(BaseHTTPMiddleware):
|
|
|
45
50
|
tenant_id = await _resolve_tenant_id(request)
|
|
46
51
|
except Exception:
|
|
47
52
|
tenant_id = None
|
|
53
|
+
# Fallback: read from header only if explicitly trusted
|
|
54
|
+
if not tenant_id and self._allow_untrusted_tenant_header:
|
|
55
|
+
tenant_id = request.headers.get("X-Tenant-Id") or request.headers.get("X-Tenant-ID")
|
|
48
56
|
|
|
49
57
|
key = self.key_fn(request)
|
|
50
58
|
if self.scope_by_tenant and tenant_id:
|
|
@@ -21,17 +21,8 @@ def _discover_fs_topics(docs_dir: Path) -> Dict[str, Path]:
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def _discover_pkg_topics() -> Dict[str, object]:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
import importlib.resources as ir
|
|
27
|
-
|
|
28
|
-
pkg_docs = ir.files("svc_infra.bundled_docs")
|
|
29
|
-
for res in pkg_docs.iterdir():
|
|
30
|
-
if res.name.endswith(".md"):
|
|
31
|
-
topics[Path(res.name).stem.replace(" ", "-")] = res
|
|
32
|
-
except Exception:
|
|
33
|
-
pass
|
|
34
|
-
return topics
|
|
24
|
+
# No bundled fallback; docs are packaged from repo root 'docs/'
|
|
25
|
+
return {}
|
|
35
26
|
|
|
36
27
|
|
|
37
28
|
def _resolve_docs_dir(ctx: click.Context) -> Path | None:
|
|
@@ -65,9 +56,7 @@ class DocsGroup(TyperGroup):
|
|
|
65
56
|
names: List[str] = list(super().list_commands(ctx) or [])
|
|
66
57
|
dir_to_use = _resolve_docs_dir(ctx)
|
|
67
58
|
fs = _discover_fs_topics(dir_to_use) if dir_to_use else {}
|
|
68
|
-
pkg = _discover_pkg_topics() if not fs else {}
|
|
69
59
|
names.extend(fs.keys())
|
|
70
|
-
names.extend(pkg.keys())
|
|
71
60
|
# Deduplicate and sort
|
|
72
61
|
uniq = sorted({*names})
|
|
73
62
|
return uniq
|
|
@@ -90,27 +79,7 @@ class DocsGroup(TyperGroup):
|
|
|
90
79
|
|
|
91
80
|
return _show_fs
|
|
92
81
|
|
|
93
|
-
|
|
94
|
-
pkg = _discover_pkg_topics()
|
|
95
|
-
if name in pkg:
|
|
96
|
-
res = pkg[name]
|
|
97
|
-
|
|
98
|
-
@click.command(name=name)
|
|
99
|
-
def _show_pkg() -> None:
|
|
100
|
-
try:
|
|
101
|
-
import importlib.resources as ir
|
|
102
|
-
|
|
103
|
-
content = getattr(res, "read_text", None)
|
|
104
|
-
if callable(content):
|
|
105
|
-
text = content(encoding="utf-8", errors="replace")
|
|
106
|
-
else:
|
|
107
|
-
with ir.as_file(res) as p:
|
|
108
|
-
text = Path(p).read_text(encoding="utf-8", errors="replace")
|
|
109
|
-
click.echo(text)
|
|
110
|
-
except Exception as e: # pragma: no cover
|
|
111
|
-
raise click.ClickException(f"Failed to load bundled doc: {e}")
|
|
112
|
-
|
|
113
|
-
return _show_pkg
|
|
82
|
+
# No packaged fallback
|
|
114
83
|
|
|
115
84
|
return None
|
|
116
85
|
|
|
@@ -137,23 +106,6 @@ def register(app: typer.Typer) -> None:
|
|
|
137
106
|
if topic in fs:
|
|
138
107
|
typer.echo(fs[topic].read_text(encoding="utf-8", errors="replace"))
|
|
139
108
|
raise typer.Exit(code=0)
|
|
140
|
-
if not fs:
|
|
141
|
-
pkg = _discover_pkg_topics()
|
|
142
|
-
if topic in pkg:
|
|
143
|
-
try:
|
|
144
|
-
import importlib.resources as ir
|
|
145
|
-
|
|
146
|
-
res = pkg[topic]
|
|
147
|
-
content = getattr(res, "read_text", None)
|
|
148
|
-
if callable(content):
|
|
149
|
-
text = content(encoding="utf-8", errors="replace")
|
|
150
|
-
else:
|
|
151
|
-
with ir.as_file(res) as p:
|
|
152
|
-
text = Path(p).read_text(encoding="utf-8", errors="replace")
|
|
153
|
-
typer.echo(text)
|
|
154
|
-
raise typer.Exit(code=0)
|
|
155
|
-
except Exception as e: # pragma: no cover
|
|
156
|
-
raise typer.BadParameter(f"Failed to load bundled topic '{topic}': {e}")
|
|
157
109
|
raise typer.BadParameter(f"Unknown topic: {topic}")
|
|
158
110
|
|
|
159
111
|
@docs_app.command("list", help="List available documentation topics")
|
|
@@ -162,15 +114,12 @@ def register(app: typer.Typer) -> None:
|
|
|
162
114
|
root = resolve_project_root()
|
|
163
115
|
dir_to_use = _resolve_docs_dir(ctx)
|
|
164
116
|
fs = _discover_fs_topics(dir_to_use) if dir_to_use else {}
|
|
165
|
-
pkg = _discover_pkg_topics() if not fs else {}
|
|
166
117
|
for name, path in fs.items():
|
|
167
118
|
try:
|
|
168
119
|
rel = path.relative_to(root)
|
|
169
120
|
typer.echo(f"{name}\t{rel}")
|
|
170
121
|
except Exception:
|
|
171
122
|
typer.echo(f"{name}\t{path}")
|
|
172
|
-
for name in sorted(pkg.keys()):
|
|
173
|
-
typer.echo(f"{name}\t(bundled)")
|
|
174
123
|
|
|
175
124
|
# Also support a generic "show" command
|
|
176
125
|
@docs_app.command("show", help="Show docs for a topic (alternative to dynamic subcommand)")
|
|
@@ -181,23 +130,6 @@ def register(app: typer.Typer) -> None:
|
|
|
181
130
|
if topic in fs:
|
|
182
131
|
typer.echo(fs[topic].read_text(encoding="utf-8", errors="replace"))
|
|
183
132
|
return
|
|
184
|
-
if not fs:
|
|
185
|
-
pkg = _discover_pkg_topics()
|
|
186
|
-
if topic in pkg:
|
|
187
|
-
try:
|
|
188
|
-
import importlib.resources as ir
|
|
189
|
-
|
|
190
|
-
res = pkg[topic]
|
|
191
|
-
content = getattr(res, "read_text", None)
|
|
192
|
-
if callable(content):
|
|
193
|
-
text = content(encoding="utf-8", errors="replace")
|
|
194
|
-
else:
|
|
195
|
-
with ir.as_file(res) as p:
|
|
196
|
-
text = Path(p).read_text(encoding="utf-8", errors="replace")
|
|
197
|
-
typer.echo(text)
|
|
198
|
-
return
|
|
199
|
-
except Exception as e: # pragma: no cover
|
|
200
|
-
raise typer.BadParameter(f"Failed to load bundled topic '{topic}': {e}")
|
|
201
133
|
raise typer.BadParameter(f"Unknown topic: {topic}")
|
|
202
134
|
|
|
203
135
|
app.add_typer(docs_app, name="docs")
|
|
@@ -80,7 +80,7 @@ svc_infra/api/fastapi/middleware/errors/handlers.py,sha256=pQMVs5n627vcKkDFEaUzx
|
|
|
80
80
|
svc_infra/api/fastapi/middleware/idempotency.py,sha256=vnBQgMWzJVaF8oWgfw2ATjEKCyQifDeGPUc9z1N7ebE,5051
|
|
81
81
|
svc_infra/api/fastapi/middleware/idempotency_store.py,sha256=BQN_Cq_jf_cuZRhze4EF5v0lOMQXpUWoRo7CsSTprug,5528
|
|
82
82
|
svc_infra/api/fastapi/middleware/optimistic_lock.py,sha256=9lOMBI4VNIVndXnrMmgSq4qeR7xPjNR1H9d1F71M5S8,1271
|
|
83
|
-
svc_infra/api/fastapi/middleware/ratelimit.py,sha256=
|
|
83
|
+
svc_infra/api/fastapi/middleware/ratelimit.py,sha256=f-nvsh3wqLMpDEmwPsKvVnOrM3cfC9AqQyl8eUqrUQM,4496
|
|
84
84
|
svc_infra/api/fastapi/middleware/ratelimit_store.py,sha256=LmJR8-kkW42rzOjls9lG1SBtCKjVY7L2Y_bNKHNY3-A,2553
|
|
85
85
|
svc_infra/api/fastapi/middleware/request_id.py,sha256=Iru7ypTdK_n76lwziEGDWoVF4FKS0Ps1PMASYmzK8ek,768
|
|
86
86
|
svc_infra/api/fastapi/middleware/request_size_limit.py,sha256=AcGqaB-F7Tbhg-at7ViT4Bpifst34jFneDBlUBjgo5I,1248
|
|
@@ -142,7 +142,7 @@ svc_infra/cli/cmds/db/sql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
|
142
142
|
svc_infra/cli/cmds/db/sql/alembic_cmds.py,sha256=kkAu8sfBLWbb9ApMS95b7b_c6GifqvPaRsO7K8icMVI,9649
|
|
143
143
|
svc_infra/cli/cmds/db/sql/sql_export_cmds.py,sha256=6MxoQO-9upoXg0cl1RHIqz96yXFVGidiBYp_ewhB0E0,2700
|
|
144
144
|
svc_infra/cli/cmds/db/sql/sql_scaffold_cmds.py,sha256=eNTCqHXOxgl9H3WTbGVn9BHXYwCpjIEJsDqhEFdrYMM,4613
|
|
145
|
-
svc_infra/cli/cmds/docs/docs_cmds.py,sha256=
|
|
145
|
+
svc_infra/cli/cmds/docs/docs_cmds.py,sha256=A3LT__UffM8elPGTnaXmjNqsq8h4y2VVMMSzoqAeZeM,4668
|
|
146
146
|
svc_infra/cli/cmds/dx/__init__.py,sha256=wQtl3-kOgoESlpVkjl3YFtqkOnQSIvVsOdutiaZFejM,197
|
|
147
147
|
svc_infra/cli/cmds/dx/dx_cmds.py,sha256=XTKUJzS3UIYn6h3CHzDEWKYJaWn0TzGiUCq3OeW27E0,3326
|
|
148
148
|
svc_infra/cli/cmds/help.py,sha256=wGfZFMYaR2ZPwW2JwKDU7M3m4AtdCd8GRQ412AmEBUM,758
|
|
@@ -296,7 +296,7 @@ svc_infra/webhooks/fastapi.py,sha256=BCNvGNxukf6dC2a4i-6en-PrjBGV19YvCWOot5lXWsA
|
|
|
296
296
|
svc_infra/webhooks/router.py,sha256=6JvAVPMEth_xxHX-IsIOcyMgHX7g1H0OVxVXKLuMp9w,1596
|
|
297
297
|
svc_infra/webhooks/service.py,sha256=hWgiJRXKBwKunJOx91C7EcLUkotDtD3Xp0RT6vj2IC0,1797
|
|
298
298
|
svc_infra/webhooks/signing.py,sha256=NCwdZzmravUe7HVIK_uXK0qqf12FG-_MVsgPvOw6lsM,784
|
|
299
|
-
svc_infra-0.1.
|
|
300
|
-
svc_infra-0.1.
|
|
301
|
-
svc_infra-0.1.
|
|
302
|
-
svc_infra-0.1.
|
|
299
|
+
svc_infra-0.1.617.dist-info/METADATA,sha256=gFtkUXgTuAGcECWRsaWvXwoxnTEqxyl2yTYw9zzYhSk,8106
|
|
300
|
+
svc_infra-0.1.617.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
|
301
|
+
svc_infra-0.1.617.dist-info/entry_points.txt,sha256=6x_nZOsjvn6hRZsMgZLgTasaCSKCgAjsGhACe_CiP0U,48
|
|
302
|
+
svc_infra-0.1.617.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|