proxylens-server 0.1.0__tar.gz

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.
Files changed (63) hide show
  1. proxylens_server-0.1.0/.gitignore +1 -0
  2. proxylens_server-0.1.0/PKG-INFO +38 -0
  3. proxylens_server-0.1.0/README.md +20 -0
  4. proxylens_server-0.1.0/docs/architecture.md +196 -0
  5. proxylens_server-0.1.0/docs/spec.md +1139 -0
  6. proxylens_server-0.1.0/pyproject.toml +55 -0
  7. proxylens_server-0.1.0/src/proxylens_server/__init__.py +5 -0
  8. proxylens_server-0.1.0/src/proxylens_server/__main__.py +5 -0
  9. proxylens_server-0.1.0/src/proxylens_server/_version.py +1 -0
  10. proxylens_server-0.1.0/src/proxylens_server/app.py +58 -0
  11. proxylens_server-0.1.0/src/proxylens_server/bootstrap.py +172 -0
  12. proxylens_server-0.1.0/src/proxylens_server/cli.py +154 -0
  13. proxylens_server-0.1.0/src/proxylens_server/cli_test.py +132 -0
  14. proxylens_server-0.1.0/src/proxylens_server/common/__init__.py +1 -0
  15. proxylens_server-0.1.0/src/proxylens_server/common/http.py +13 -0
  16. proxylens_server-0.1.0/src/proxylens_server/common/identity.py +30 -0
  17. proxylens_server-0.1.0/src/proxylens_server/common/identity_test.py +20 -0
  18. proxylens_server-0.1.0/src/proxylens_server/common/json.py +8 -0
  19. proxylens_server-0.1.0/src/proxylens_server/common/time.py +19 -0
  20. proxylens_server-0.1.0/src/proxylens_server/config.py +12 -0
  21. proxylens_server-0.1.0/src/proxylens_server/domain/__init__.py +12 -0
  22. proxylens_server-0.1.0/src/proxylens_server/domain/errors.py +10 -0
  23. proxylens_server-0.1.0/src/proxylens_server/domain/event.py +33 -0
  24. proxylens_server-0.1.0/src/proxylens_server/domain/request.py +23 -0
  25. proxylens_server-0.1.0/src/proxylens_server/infra/__init__.py +1 -0
  26. proxylens_server-0.1.0/src/proxylens_server/infra/filters/__init__.py +1 -0
  27. proxylens_server-0.1.0/src/proxylens_server/infra/filters/script_runner.py +42 -0
  28. proxylens_server-0.1.0/src/proxylens_server/infra/filters/script_runner_test.py +35 -0
  29. proxylens_server-0.1.0/src/proxylens_server/infra/persistence/__init__.py +1 -0
  30. proxylens_server-0.1.0/src/proxylens_server/infra/persistence/repositories/__init__.py +1 -0
  31. proxylens_server-0.1.0/src/proxylens_server/infra/persistence/repositories/blobs.py +115 -0
  32. proxylens_server-0.1.0/src/proxylens_server/infra/persistence/repositories/deferred_events.py +89 -0
  33. proxylens_server-0.1.0/src/proxylens_server/infra/persistence/repositories/events.py +168 -0
  34. proxylens_server-0.1.0/src/proxylens_server/infra/persistence/repositories/requests.py +308 -0
  35. proxylens_server-0.1.0/src/proxylens_server/infra/persistence/repositories/tombstones.py +64 -0
  36. proxylens_server-0.1.0/src/proxylens_server/infra/persistence/sqlite.py +195 -0
  37. proxylens_server-0.1.0/src/proxylens_server/infra/routes/__init__.py +1 -0
  38. proxylens_server-0.1.0/src/proxylens_server/infra/routes/blobs/dtos.py +16 -0
  39. proxylens_server-0.1.0/src/proxylens_server/infra/routes/blobs/router.py +42 -0
  40. proxylens_server-0.1.0/src/proxylens_server/infra/routes/docs/router.py +24 -0
  41. proxylens_server-0.1.0/src/proxylens_server/infra/routes/events/dtos.py +35 -0
  42. proxylens_server-0.1.0/src/proxylens_server/infra/routes/events/router.py +43 -0
  43. proxylens_server-0.1.0/src/proxylens_server/infra/routes/requests/dtos.py +155 -0
  44. proxylens_server-0.1.0/src/proxylens_server/infra/routes/requests/router.py +269 -0
  45. proxylens_server-0.1.0/src/proxylens_server/use_cases/__init__.py +1 -0
  46. proxylens_server-0.1.0/src/proxylens_server/use_cases/clear_all.py +57 -0
  47. proxylens_server-0.1.0/src/proxylens_server/use_cases/clear_tombstones.py +40 -0
  48. proxylens_server-0.1.0/src/proxylens_server/use_cases/delete_request.py +88 -0
  49. proxylens_server-0.1.0/src/proxylens_server/use_cases/delete_requests.py +63 -0
  50. proxylens_server-0.1.0/src/proxylens_server/use_cases/get_request.py +159 -0
  51. proxylens_server-0.1.0/src/proxylens_server/use_cases/get_request_body.py +53 -0
  52. proxylens_server-0.1.0/src/proxylens_server/use_cases/get_request_events.py +52 -0
  53. proxylens_server-0.1.0/src/proxylens_server/use_cases/get_response_body.py +53 -0
  54. proxylens_server-0.1.0/src/proxylens_server/use_cases/get_response_detail.py +63 -0
  55. proxylens_server-0.1.0/src/proxylens_server/use_cases/ingest_events.py +598 -0
  56. proxylens_server-0.1.0/src/proxylens_server/use_cases/ingest_events_test.py +19 -0
  57. proxylens_server-0.1.0/src/proxylens_server/use_cases/list_requests.py +153 -0
  58. proxylens_server-0.1.0/src/proxylens_server/use_cases/request_histogram.py +147 -0
  59. proxylens_server-0.1.0/src/proxylens_server/use_cases/upload_blob.py +33 -0
  60. proxylens_server-0.1.0/src/proxylens_server/use_cases/vacuum.py +73 -0
  61. proxylens_server-0.1.0/tests/test_container_integration.py +272 -0
  62. proxylens_server-0.1.0/tests/test_server_integration.py +132 -0
  63. proxylens_server-0.1.0/tests/test_time_precision.py +43 -0
@@ -0,0 +1 @@
1
+ .proxylens-server-data/
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: proxylens-server
3
+ Version: 0.1.0
4
+ Summary: ProxyLens Server
5
+ Keywords: api,fastapi,http,mitmproxy,proxy,proxylens
6
+ Classifier: Intended Audience :: Developers
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.14
9
+ Classifier: Topic :: Internet :: Proxy Servers
10
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
11
+ Requires-Python: >=3.14
12
+ Requires-Dist: fastapi==0.135.1
13
+ Requires-Dist: pydantic<3,>=2.11
14
+ Requires-Dist: pyyaml<7,>=6.0.2
15
+ Requires-Dist: scalar-fastapi==1.8.1
16
+ Requires-Dist: uvicorn[standard]<0.39,>=0.35
17
+ Description-Content-Type: text/markdown
18
+
19
+ # ProxyLens Server
20
+
21
+ Implementation of the ProxyLens Server spec.
22
+
23
+ ## Running the server
24
+
25
+ Use the package entry point:
26
+
27
+ ```bash
28
+ uv run proxylens-server --bind 127.0.0.1:8000 --log-level info
29
+ ```
30
+
31
+ Useful options:
32
+
33
+ - `--host` / `--port` to set the bind address without `--bind`
34
+ - `--reload` for development
35
+ - `--workers` to run multiple uvicorn worker processes
36
+ - `--data-dir` to choose the server data directory
37
+ - `--filter-script` to load an event filter script
38
+ - `--no-access-log` to disable access logging
@@ -0,0 +1,20 @@
1
+ # ProxyLens Server
2
+
3
+ Implementation of the ProxyLens Server spec.
4
+
5
+ ## Running the server
6
+
7
+ Use the package entry point:
8
+
9
+ ```bash
10
+ uv run proxylens-server --bind 127.0.0.1:8000 --log-level info
11
+ ```
12
+
13
+ Useful options:
14
+
15
+ - `--host` / `--port` to set the bind address without `--bind`
16
+ - `--reload` for development
17
+ - `--workers` to run multiple uvicorn worker processes
18
+ - `--data-dir` to choose the server data directory
19
+ - `--filter-script` to load an event filter script
20
+ - `--no-access-log` to disable access logging
@@ -0,0 +1,196 @@
1
+ # Architecture spec
2
+
3
+ ## Goal
4
+
5
+ This document defines the architectural rules for the server implementation in `server/`.
6
+
7
+ The target style is:
8
+
9
+ - simple hexagonal architecture
10
+ - light DDD where it helps clarify ownership
11
+ - no unnecessary framework-shaped abstraction layers
12
+ - no generic shared model buckets
13
+
14
+ The point is to keep the codebase easy to navigate and hard to muddle.
15
+
16
+ ## Core principles
17
+
18
+ - Keep the architecture simple. Prefer clear ownership over clever indirection.
19
+ - Put code where it naturally belongs instead of introducing shared "utility" files that become dumping grounds.
20
+ - The direction of dependency should stay obvious:
21
+ - routes depend on use cases
22
+ - use cases depend on repository interfaces and other use cases when necessary
23
+ - persistence depends on SQLite/filesystem details
24
+ - domain stays isolated from infrastructure concerns
25
+ - If a concept belongs to one layer, define it in that layer rather than in a cross-cutting shared file.
26
+
27
+ ## Project structure
28
+
29
+ The intended structure under `server/src/proxylens_server/` is:
30
+
31
+ - `domain/`
32
+ - `use_cases/`
33
+ - `infra/routes/`
34
+ - `infra/persistence/`
35
+ - `infra/filters/`
36
+ - `app.py`
37
+ - `bootstrap.py`
38
+
39
+ ## Domain rules
40
+
41
+ The domain layer should stay small and plain.
42
+
43
+ - Domain entities should represent the real concepts in this server.
44
+ - The important entities are:
45
+ - `Request`
46
+ - `Event`
47
+ - The relationship between them is:
48
+ - a request is uniquely identified by `request_id`
49
+ - an event is uniquely identified by request-local identity plus event identity
50
+ - a request can have multiple events
51
+ - the request-event relationship includes ordering within that request
52
+
53
+ Domain code must not contain infrastructure concerns.
54
+
55
+ - No SQLite details
56
+ - No HTTP details
57
+ - No Pydantic DTOs
58
+ - No JSON serialization helpers
59
+ - No generic filtering/query objects
60
+ - No external-dependency-heavy helpers unless there is a very strong reason
61
+
62
+ In practice, the domain should be plain Python objects and small domain errors only.
63
+
64
+ ## Use case rules
65
+
66
+ Use cases are the application layer.
67
+
68
+ - Put use cases under `use_cases/`
69
+ - Use one file per use case
70
+ - Examples:
71
+ - `upload_blob.py`
72
+ - `ingest_events.py`
73
+ - `list_requests.py`
74
+ - `get_request.py`
75
+ - `delete_request.py`
76
+
77
+ Each use case file should own:
78
+
79
+ - its input contract
80
+ - its output contract
81
+ - its `execute(...)` implementation
82
+
83
+ Use case contract rules:
84
+
85
+ - If an input model belongs to one use case, define it in that use case file.
86
+ - If an output model belongs to one use case, define it in that use case file.
87
+ - Do not move use-case-specific contracts into generic shared files like `models.py`.
88
+
89
+ Use case dependency rules:
90
+
91
+ - A use case may depend on repository interfaces.
92
+ - A use case may depend on another use case when it is genuinely application orchestration.
93
+ - A use case must not depend on route DTOs.
94
+ - A use case must not depend on persistence-specific row models unless it is explicitly mapping from repository returns.
95
+
96
+ Filtering rule:
97
+
98
+ - Query/filter parsing should not become a domain concept.
99
+ - If a route accepts filter parameters, it should pass the relevant values into the use case input contract.
100
+
101
+ ## Route rules
102
+
103
+ - HTTP endpoints live under `infra/routes/`
104
+ - Use one resource directory per resource area
105
+ - Each resource directory should contain:
106
+ - `router.py`
107
+ - `dtos.py`
108
+
109
+ Route dependency rules:
110
+
111
+ - `router.py` may depend on use cases
112
+ - `dtos.py` should define HTTP request/response DTOs only
113
+ - Route DTOs are responsible for HTTP boundary validation and serialization
114
+ - Route DTOs should not become generic shared schemas for the rest of the app
115
+
116
+ ## Persistence rules
117
+
118
+ Persistence code lives under `infra/persistence/`.
119
+
120
+ - Repository implementations belong under `infra/persistence/repositories/`
121
+ - Use one repository per file
122
+ - If a repository interface or protocol is needed, define it in the same file as that repository
123
+
124
+ Persistence ownership rules:
125
+
126
+ - SQLite row mapping belongs in persistence
127
+ - filesystem/blob storage concerns belong in persistence
128
+ - persistence-local records belong in persistence
129
+ - persistence serialization and deserialization belong in persistence
130
+
131
+ Persistence code must not push its internal shapes upward as generic app-wide models.
132
+
133
+ - Repositories may return persistence-local records
134
+ - Use cases should map those records into use-case outputs when needed
135
+ - Route DTOs should map from use-case outputs, not from persistence models
136
+
137
+ ## Filter rules
138
+
139
+ Ingestion-time filtering belongs to infrastructure.
140
+
141
+ - Filter loading and execution belong in `infra/filters/`
142
+
143
+ The current filter contract shape is conceptually:
144
+
145
+ ```python
146
+ def filter_event(app_container, event, request):
147
+ ...
148
+ ```
149
+
150
+ ## Composition root rules
151
+
152
+ - `bootstrap.py` is the composition root
153
+ - `AppContainer` holds the wired repositories, filter runner, and use cases
154
+ - `app.py` creates the container and wires the HTTP app to it
155
+
156
+ ## Shared-model anti-pattern
157
+
158
+ Do not create a generic shared schema file like `models.py` for unrelated concepts.
159
+
160
+ A model should live next to the layer that owns it:
161
+
162
+ - domain entity -> `domain/`
163
+ - use-case input/output -> that use case file
164
+ - route request/response DTO -> route `dtos.py`
165
+ - persistence record -> persistence repository file
166
+
167
+ The same rule applies to enums.
168
+
169
+ - If an enum belongs to an ingestion contract, keep it in that ingestion use case module.
170
+ - If an enum belongs to histogram behavior, keep it in the histogram use case module.
171
+ - Do not put unrelated enums into a generic shared enum bucket.
172
+
173
+ ## Testing rules
174
+
175
+ Tests should follow ownership.
176
+
177
+ - Unit tests live next to the source file they cover
178
+ - Integration tests live under `server/tests/`
179
+
180
+ ## Simplicity rules
181
+
182
+ When in doubt:
183
+
184
+ - prefer directness over indirection
185
+ - prefer colocated models over shared buckets
186
+ - prefer explicit names over generic names
187
+ - prefer one clear abstraction over two overlapping ones
188
+
189
+ The architecture should make it easy to answer:
190
+
191
+ - what layer owns this code?
192
+ - what data shape owns this model?
193
+ - what is the dependency direction?
194
+ - where should a new feature be added?
195
+
196
+ If the answer is fuzzy, the code is probably in the wrong place.