mirror of
https://github.com/Findus23/HNReader.git
synced 2024-09-10 04:53:45 +02:00
79 lines
2.5 KiB
Python
79 lines
2.5 KiB
Python
from pathlib import Path
|
|
|
|
import maxminddb
|
|
from httpx import AsyncClient, Timeout
|
|
from redis.asyncio.client import StrictRedis
|
|
from starlette.applications import Starlette
|
|
from starlette.requests import Request
|
|
from starlette.responses import JSONResponse, Response
|
|
from starlette.routing import Route
|
|
|
|
from config import debug, user_agent, redis_socket, city
|
|
from hnapi import HNClient
|
|
from reader import Reader
|
|
|
|
geoip = maxminddb.open_database(Path(__file__).parent / 'dbip-city-lite-2022-11.mmdb')
|
|
|
|
session = AsyncClient(timeout=Timeout(timeout=15.0), headers={
|
|
"User-Agent": user_agent,
|
|
})
|
|
if redis_socket:
|
|
r = StrictRedis(unix_socket_path=redis_socket)
|
|
else:
|
|
r = StrictRedis()
|
|
reader = Reader()
|
|
|
|
api = HNClient(session, r)
|
|
|
|
|
|
async def is_from_city(ip: str) -> bool:
|
|
return geoip.get(ip)["city"]["names"]["en"] == city
|
|
|
|
|
|
async def item(request: Request):
|
|
item_id = request.path_params["item_id"]
|
|
data = await api.get_full_item(item_id)
|
|
return JSONResponse(data)
|
|
|
|
|
|
async def read(request: Request):
|
|
item_id = request.path_params["item_id"]
|
|
item = await api.get_item(item_id)
|
|
if "url" not in item:
|
|
return "Url not found", 404
|
|
if not debug and (
|
|
"x-forwarded-for" not in request.headers
|
|
or not await is_from_city(request.headers["x-forwarded-for"])
|
|
):
|
|
return JSONResponse({
|
|
"title": "Reader View not public",
|
|
"html": "For security reasons, this instance doesn't allow fetching a reader-friendly version of the website."
|
|
"If you want to use this feature, please set up you own instance "
|
|
"<a href='https://github.com/Findus23/HNReader/'>from the source code of this site</a> "
|
|
"or read the <a href='" + item["url"] + "'>original site</a>."
|
|
})
|
|
|
|
key = f"hnclient_read_{item_id}"
|
|
|
|
cache = await r.get(key)
|
|
if cache:
|
|
response = Response(cache)
|
|
else:
|
|
output = await reader.readable_html(item["url"])
|
|
await r.set(key, output, ex=60 * 60 * 24)
|
|
response = Response(output)
|
|
response.media_type = "application/json"
|
|
return response
|
|
|
|
|
|
async def topstories(request: Request):
|
|
offset = int(request.query_params.get("offset", 0))
|
|
data = await api.get_stories("topstories", offset=offset)
|
|
return JSONResponse(data)
|
|
|
|
|
|
app = Starlette(debug=debug, routes=[
|
|
Route('/api/item/{item_id:int}', item),
|
|
Route('/api/read/{item_id:int}', read),
|
|
Route('/api/topstories', topstories),
|
|
])
|