SunsetLog 0.0.2__tar.gz → 0.0.3__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.
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/.gitignore +1 -0
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/PKG-INFO +1 -1
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/SunsetLog/client.py +8 -1
- sunsetlog-0.0.3/examples/proxy_server.py +86 -0
- sunsetlog-0.0.3/examples/test_via_proxy.py +31 -0
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/pyproject.toml +1 -1
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/uv.lock +1 -1
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/.gitattributes +0 -0
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/.github/workflows/publish-pypi.yml +0 -0
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/.python-version +0 -0
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/README.md +0 -0
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/SunsetLog/__init__.py +0 -0
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/SunsetLog/models.py +0 -0
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/SunsetLog.code-workspace +0 -0
- {sunsetlog-0.0.2 → sunsetlog-0.0.3}/examples/get_channel_list.py +0 -0
|
@@ -108,17 +108,21 @@ class SunsetLogClient:
|
|
|
108
108
|
*,
|
|
109
109
|
channels: str | list[str] | None = None,
|
|
110
110
|
gang: int = 1,
|
|
111
|
+
job: int = 1,
|
|
111
112
|
q: str = "",
|
|
112
113
|
from_offset: int = 0,
|
|
113
114
|
mode: str = "exact",
|
|
114
115
|
operator: str = "and",
|
|
116
|
+
order: str = "desc",
|
|
117
|
+
hidden: str = "exclude",
|
|
115
118
|
) -> SearchResponse:
|
|
116
119
|
"""
|
|
117
120
|
Search logs. GET /search.
|
|
118
121
|
channels: single channel name or comma-separated list.
|
|
122
|
+
order: "desc" (newest first) or "asc" (oldest first).
|
|
119
123
|
"""
|
|
120
124
|
if channels is None:
|
|
121
|
-
channel_list = await self.get_channel_list(gang=gang)
|
|
125
|
+
channel_list = await self.get_channel_list(gang=gang, job=job)
|
|
122
126
|
indexes = [c["index"] for c in channel_list.get("channels", [])]
|
|
123
127
|
channels = ",".join(indexes) if indexes else "gang_glitch_locker1"
|
|
124
128
|
elif isinstance(channels, list):
|
|
@@ -130,8 +134,11 @@ class SunsetLogClient:
|
|
|
130
134
|
"from": from_offset,
|
|
131
135
|
"mode": mode,
|
|
132
136
|
"operator": operator,
|
|
137
|
+
"hidden": hidden,
|
|
133
138
|
"channels": channels,
|
|
139
|
+
"order": order,
|
|
134
140
|
"gang": gang,
|
|
141
|
+
"job": job,
|
|
135
142
|
}
|
|
136
143
|
r = await client.get("/search", params=params)
|
|
137
144
|
if r.status_code != 200:
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Reverse proxy for log-api.vmp.ir, meant to run ON A SERVER INSIDE IRAN
|
|
3
|
+
that already has direct access to the real API. Uses SunsetLogClient
|
|
4
|
+
(this library) internally instead of calling the API directly.
|
|
5
|
+
|
|
6
|
+
An external server (outside Iran, where log-api.vmp.ir is blocked) can then
|
|
7
|
+
point SunsetLogClient's base_url at THIS proxy instead of the real API.
|
|
8
|
+
No separate proxy key: the caller's own site token (the same one used
|
|
9
|
+
against the real API) is forwarded upstream as-is.
|
|
10
|
+
|
|
11
|
+
Run directly (host/port configurable via env vars, no uvicorn CLI needed):
|
|
12
|
+
pip install fastapi uvicorn
|
|
13
|
+
export PROXY_HOST=0.0.0.0 # your Iran server's bind address
|
|
14
|
+
export PROXY_PORT=8000
|
|
15
|
+
python proxy_server.py
|
|
16
|
+
|
|
17
|
+
In /docs, click the "Authorize" lock button at the top and paste just the
|
|
18
|
+
site token (no "Bearer " prefix needed there) — Swagger adds it for you.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
from typing import Annotated
|
|
23
|
+
|
|
24
|
+
import httpx
|
|
25
|
+
from fastapi import Depends, FastAPI, HTTPException, Query
|
|
26
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
27
|
+
|
|
28
|
+
UPSTREAM_BASE_URL = "https://log-api.vmp.ir"
|
|
29
|
+
|
|
30
|
+
app = FastAPI()
|
|
31
|
+
bearer_scheme = HTTPBearer()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def _forward(path: str, params: dict, token: str) -> dict:
|
|
35
|
+
async with httpx.AsyncClient(base_url=UPSTREAM_BASE_URL, timeout=30.0) as client:
|
|
36
|
+
r = await client.get(
|
|
37
|
+
path, params=params, headers={"Authorization": f"Bearer {token}"}
|
|
38
|
+
)
|
|
39
|
+
if r.status_code != 200:
|
|
40
|
+
raise HTTPException(status_code=r.status_code, detail=r.text)
|
|
41
|
+
return r.json()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@app.get("/user/getChannels")
|
|
45
|
+
async def get_channels(
|
|
46
|
+
creds: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)],
|
|
47
|
+
gang: int = 1,
|
|
48
|
+
job: int = 1,
|
|
49
|
+
):
|
|
50
|
+
return await _forward(
|
|
51
|
+
"/user/getChannels", {"gang": gang, "job": job}, creds.credentials
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@app.get("/search")
|
|
56
|
+
async def search(
|
|
57
|
+
creds: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)],
|
|
58
|
+
q: str = "",
|
|
59
|
+
from_offset: int = Query(0, alias="from"),
|
|
60
|
+
mode: str = "exact",
|
|
61
|
+
operator: str = "and",
|
|
62
|
+
channels: str = "",
|
|
63
|
+
gang: int = 1,
|
|
64
|
+
):
|
|
65
|
+
return await _forward(
|
|
66
|
+
"/search",
|
|
67
|
+
{
|
|
68
|
+
"q": q,
|
|
69
|
+
"from": from_offset,
|
|
70
|
+
"mode": mode,
|
|
71
|
+
"operator": operator,
|
|
72
|
+
"channels": channels,
|
|
73
|
+
"gang": gang,
|
|
74
|
+
},
|
|
75
|
+
creds.credentials,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
import uvicorn
|
|
81
|
+
|
|
82
|
+
uvicorn.run(
|
|
83
|
+
app,
|
|
84
|
+
host=os.environ.get("PROXY_HOST", "0.0.0.0"),
|
|
85
|
+
port=int(os.environ.get("PROXY_PORT", "8000")),
|
|
86
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Run this on the EXTERNAL server (outside Iran) to test that the Iran-side
|
|
3
|
+
proxy (see proxy_server.py) is reachable and working.
|
|
4
|
+
|
|
5
|
+
export PROXY_BASE_URL=http://your-iran-server-ip:8000
|
|
6
|
+
export PROXY_ACCESS_KEY=same_secret_set_on_the_proxy
|
|
7
|
+
python test_via_proxy.py
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
from SunsetLog import SunsetLogAPIError, SunsetLogClient
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def main() -> None:
|
|
17
|
+
base_url = os.environ["PROXY_BASE_URL"]
|
|
18
|
+
access_key = os.environ["PROXY_ACCESS_KEY"]
|
|
19
|
+
|
|
20
|
+
async with SunsetLogClient(access_key, base_url=base_url) as client:
|
|
21
|
+
try:
|
|
22
|
+
channels = await client.get_channel_list(gang=1, job=1)
|
|
23
|
+
print(f"OK - got {len(channels['channels'])} channels")
|
|
24
|
+
for c in channels["channels"][:5]:
|
|
25
|
+
print(f" {c['id']:>6} {c['index']}")
|
|
26
|
+
except SunsetLogAPIError as e:
|
|
27
|
+
print(f"FAILED: {e} (status={e.status_code}, body={e.body})")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
asyncio.run(main())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|