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.
- proxylens_server-0.1.0/.gitignore +1 -0
- proxylens_server-0.1.0/PKG-INFO +38 -0
- proxylens_server-0.1.0/README.md +20 -0
- proxylens_server-0.1.0/docs/architecture.md +196 -0
- proxylens_server-0.1.0/docs/spec.md +1139 -0
- proxylens_server-0.1.0/pyproject.toml +55 -0
- proxylens_server-0.1.0/src/proxylens_server/__init__.py +5 -0
- proxylens_server-0.1.0/src/proxylens_server/__main__.py +5 -0
- proxylens_server-0.1.0/src/proxylens_server/_version.py +1 -0
- proxylens_server-0.1.0/src/proxylens_server/app.py +58 -0
- proxylens_server-0.1.0/src/proxylens_server/bootstrap.py +172 -0
- proxylens_server-0.1.0/src/proxylens_server/cli.py +154 -0
- proxylens_server-0.1.0/src/proxylens_server/cli_test.py +132 -0
- proxylens_server-0.1.0/src/proxylens_server/common/__init__.py +1 -0
- proxylens_server-0.1.0/src/proxylens_server/common/http.py +13 -0
- proxylens_server-0.1.0/src/proxylens_server/common/identity.py +30 -0
- proxylens_server-0.1.0/src/proxylens_server/common/identity_test.py +20 -0
- proxylens_server-0.1.0/src/proxylens_server/common/json.py +8 -0
- proxylens_server-0.1.0/src/proxylens_server/common/time.py +19 -0
- proxylens_server-0.1.0/src/proxylens_server/config.py +12 -0
- proxylens_server-0.1.0/src/proxylens_server/domain/__init__.py +12 -0
- proxylens_server-0.1.0/src/proxylens_server/domain/errors.py +10 -0
- proxylens_server-0.1.0/src/proxylens_server/domain/event.py +33 -0
- proxylens_server-0.1.0/src/proxylens_server/domain/request.py +23 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/__init__.py +1 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/filters/__init__.py +1 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/filters/script_runner.py +42 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/filters/script_runner_test.py +35 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/persistence/__init__.py +1 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/persistence/repositories/__init__.py +1 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/persistence/repositories/blobs.py +115 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/persistence/repositories/deferred_events.py +89 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/persistence/repositories/events.py +168 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/persistence/repositories/requests.py +308 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/persistence/repositories/tombstones.py +64 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/persistence/sqlite.py +195 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/routes/__init__.py +1 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/routes/blobs/dtos.py +16 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/routes/blobs/router.py +42 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/routes/docs/router.py +24 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/routes/events/dtos.py +35 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/routes/events/router.py +43 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/routes/requests/dtos.py +155 -0
- proxylens_server-0.1.0/src/proxylens_server/infra/routes/requests/router.py +269 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/__init__.py +1 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/clear_all.py +57 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/clear_tombstones.py +40 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/delete_request.py +88 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/delete_requests.py +63 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/get_request.py +159 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/get_request_body.py +53 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/get_request_events.py +52 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/get_response_body.py +53 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/get_response_detail.py +63 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/ingest_events.py +598 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/ingest_events_test.py +19 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/list_requests.py +153 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/request_histogram.py +147 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/upload_blob.py +33 -0
- proxylens_server-0.1.0/src/proxylens_server/use_cases/vacuum.py +73 -0
- proxylens_server-0.1.0/tests/test_container_integration.py +272 -0
- proxylens_server-0.1.0/tests/test_server_integration.py +132 -0
- 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.
|