7
7
from starlette .staticfiles import StaticFiles
8
8
from starlette .routing import Route , Mount
9
9
from starlette .templating import Jinja2Templates
10
+ from starlette .responses import JSONResponse , RedirectResponse
11
+ from starlette .datastructures import URL
12
+ from starlette .types import ASGIApp , Receive , Scope , Send
13
+ repeated_quotes = re .compile (r'//+' ) # handling multiple // in url
10
14
import sys
11
15
import uvicorn
12
16
import valkey
22
26
valkey_host = config ['DEFAULT' ]['valkey_host' ]
23
27
valkey_port = config ['DEFAULT' ]['valkey_port' ]
24
28
cache = valkey .Valkey (host = valkey_host , port = valkey_port , db = 0 )
29
+ # API definition
30
+ from apiman .starlette import Apiman
31
+ apiman = Apiman (template = "openapi.yml" )
25
32
26
33
if not cache .ping ():
27
34
sys .exit ("Valkey server not reachable" )
28
35
36
+
37
+ class HttpUrlRedirectMiddleware :
38
+ """
39
+ This http middleware redirects urls with repeated slashes to the cleaned up
40
+ versions of the urls - from GitHub issue from Starlette project
41
+ """
42
+
43
+ def __init__ (self , app : ASGIApp ) -> None :
44
+ self .app = app
45
+
46
+ async def __call__ (self , scope : Scope , receive : Receive , send : Send ) -> None :
47
+
48
+ if scope ["type" ] == "http" and repeated_quotes .search (URL (scope = scope ).path ):
49
+ url = URL (scope = scope )
50
+ url = url .replace (path = repeated_quotes .sub ('/' , url .path ))
51
+ response = RedirectResponse (url , status_code = 307 )
52
+ await response (scope , receive , send )
53
+ else :
54
+ await self .app (scope , receive , send )
55
+
29
56
def check_onion (onion = None ):
30
57
# We only support onion_v3 and automatically append dot onion if missing
31
58
if onion is None or onion == '' :
@@ -46,9 +73,8 @@ def query_onion(onion=None):
46
73
r = requests .get (f'{ ail_url } /api/v1/lookup/onion/{ onion } ' , headers = headers , verify = False )
47
74
if r .status_code != 200 :
48
75
return False
49
- print (r .status_code )
50
- print (r .json ())
51
76
cache .set (keycache , r .text , ex = 3600 )
77
+ return json .loads (cache .get (keycache ))
52
78
53
79
async def homepage (request ):
54
80
template = "index.html"
@@ -65,6 +91,32 @@ async def homepage(request):
65
91
66
92
return templates .TemplateResponse (template , context )
67
93
94
+ @apiman .from_yaml (
95
+ """
96
+ summary: lookup api
97
+ tags:
98
+ - lookup
99
+ parameters:
100
+ - name: onion
101
+ in: path
102
+ required: True
103
+ responses:
104
+ "200":
105
+ description: OK
106
+ """ )
107
+ async def lookup (request ):
108
+ onion = check_onion (onion = request .path_params ['onion' ].lower ())
109
+ keycache = f'onion-lookup:{ onion } '
110
+ if onion :
111
+ onion_response = check_onion (onion = onion )
112
+ if onion_response is not False :
113
+ onion_meta = query_onion (onion = onion )
114
+ if onion_meta is not False :
115
+ return JSONResponse (onion_meta )
116
+ else :
117
+ return JSONResponse ({"error" : "Incorrect onion format" })
118
+
119
+ return JSONResponse ({})
68
120
69
121
async def error (request ):
70
122
"""
@@ -92,8 +144,10 @@ async def server_error(request: Request, exc: HTTPException):
92
144
93
145
routes = [
94
146
Route ('/' , homepage ),
147
+ Route ('/api/lookup/{onion}' , lookup , methods = ['GET' ]),
95
148
Route ('/error' , error ),
96
149
Mount ('/static' , app = StaticFiles (directory = 'statics' ), name = 'static' )
150
+
97
151
]
98
152
99
153
exception_handlers = {
@@ -102,7 +156,8 @@ async def server_error(request: Request, exc: HTTPException):
102
156
}
103
157
104
158
app = Starlette (debug = True , routes = routes , exception_handlers = exception_handlers )
105
-
159
+ app .add_middleware (HttpUrlRedirectMiddleware )
160
+ apiman .init_app (app )
106
161
107
162
if __name__ == "__main__" :
108
163
uvicorn .run (app , host = '0.0.0.0' , port = 8000 )
0 commit comments