File size: 3,558 Bytes
8c290e6
 
 
bceb9b7
 
c2219a8
bceb9b7
 
8c290e6
bceb9b7
 
 
 
 
 
 
 
 
 
 
8c290e6
 
 
 
 
 
 
 
bceb9b7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26bf52a
 
 
 
 
 
 
 
c2219a8
 
 
 
26bf52a
 
 
 
c2219a8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bceb9b7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8c290e6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
from apscheduler.executors.asyncio import AsyncIOExecutor
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from contextlib import asynccontextmanager
from datetime import datetime
from fastapi import FastAPI, Request
from fastapi.responses import FileResponse, PlainTextResponse, Response
from typing import Any

import httpx
import json
import subprocess


class PrettyJSONResponse(Response):
    media_type = "application/json"

    def render(self, content: Any) -> bytes:
        return json.dumps(content, indent=2).encode("utf-8")


@asynccontextmanager
async def lifespan(app: FastAPI):
    scheduler.start()
    yield
    scheduler.shutdown()


app = FastAPI(lifespan=lifespan)


@app.middleware("http")
async def log_request(request: Request, call_next: Any):
    ts = datetime.now().strftime("%y%m%d%H%M%S%f")
    data = {
        "day": int(ts[:6]),
        "dt": int(ts[:-3]),
        "url": request.url,
        "query_params": request.query_params,
        "client": request.client.host,
        "method": request.method,
        "headers": dict(request.headers),
    }
    output = json.dumps(
        obj=data,
        default=str,
        indent=None,
        separators=(", ", ":"),
    )
    with open("a.json", "a") as f:
        separator = "\n" if f.tell() else ""
        f.write(separator + output)

    response = await call_next(request)
    return response


@app.get("/")
def read_root(request: Request):
    """Main URL returning an executable installer script.

    Query parameters can be used to install specific things, e.g.
    curl -fsSL "https://pup-py-fetch.hf.space?python=3.11&pixi=marimo&myenv=cowsay,duckdb"

    Package specs "duckdb>=1.1" are not supported.
    """

    query_params = dict(request.query_params)
    py_ver = query_params.pop("python", "3.12")
    if "Windows" in request.headers.get("user-agent"):
        pup_url = "https://raw.githubusercontent.com/liquidcarbon/puppy/main/pup.ps1"
        script = [
            f"$pup_ps1 = (iwr -useb {pup_url}).Content",
            f"& ([scriptblock]::Create($pup_ps1)) {py_ver}",
        ]
    else:
        pup_url = "https://raw.githubusercontent.com/liquidcarbon/puppy/main/pup.sh"
        script = [f"curl -fsSL {pup_url} | bash -s {py_ver}"]

    pixi_packages = query_params.pop("pixi", "")
    if pixi_packages:
        for pkg in pixi_packages.split(","):
            script.append(f"pixi add {pkg}")

    # remaining query params are venvs
    for venv, uv_packages in query_params.items():
        for pkg in uv_packages.split(","):
            script.append(f"pup add {venv} {pkg}")

    script.append("# script ends here\n")
    return PlainTextResponse("\n".join(script))


@app.get("/a", response_class=PrettyJSONResponse)
def get_analytics(n: int = 5):
    if n == 0:
        cmd = "cat a.json"
    else:
        cmd = f"tail -{n} a.json"
    json_lines = subprocess.run(cmd.split(), capture_output=True).stdout
    content = json.loads(f"[{json_lines.replace(b"\n", b",").decode()}]")
    return content


@app.api_route("/qa", response_class=FileResponse, methods=["GET", "HEAD"])
def query_analytics():
    return "a.json"


@app.get("/favicon.ico")
async def favicon():
    return {"message": "woof!"}


@app.get("/ping")
async def ping():
    return {"message": "woof!"}


async def self_ping():
    async with httpx.AsyncClient() as client:
        _ = await client.get("http://0.0.0.0:7860/ping")


scheduler = AsyncIOScheduler(executors={"default": AsyncIOExecutor()})
scheduler.add_job(self_ping, "interval", minutes=60)