Initial AppFactory webhook service
This commit is contained in:
+16
@@ -0,0 +1,16 @@
|
|||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends docker.io git curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY app ./app
|
||||||
|
|
||||||
|
EXPOSE 9000
|
||||||
|
|
||||||
|
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "9000"]
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
import subprocess
|
||||||
|
import threading
|
||||||
|
from fastapi import FastAPI, Request, Header, HTTPException
|
||||||
|
|
||||||
|
WEBHOOK_SECRET = "change-me-webhook-secret"
|
||||||
|
DEPLOY_SCRIPT = "/tools/deploy-app.sh"
|
||||||
|
|
||||||
|
app = FastAPI(title="AppFactory Webhook")
|
||||||
|
|
||||||
|
def verify_signature(body: bytes, signature: str | None):
|
||||||
|
if not signature:
|
||||||
|
raise HTTPException(status_code=401, detail="Missing signature")
|
||||||
|
|
||||||
|
expected = hmac.new(
|
||||||
|
WEBHOOK_SECRET.encode("utf-8"),
|
||||||
|
body,
|
||||||
|
hashlib.sha256
|
||||||
|
).hexdigest()
|
||||||
|
|
||||||
|
valid_signatures = [
|
||||||
|
expected,
|
||||||
|
f"sha256={expected}",
|
||||||
|
]
|
||||||
|
|
||||||
|
if not any(hmac.compare_digest(s, signature) for s in valid_signatures):
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid signature")
|
||||||
|
|
||||||
|
def run_deploy(app_id: str):
|
||||||
|
subprocess.run(
|
||||||
|
[DEPLOY_SCRIPT, app_id],
|
||||||
|
capture_output=True,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.get("/health")
|
||||||
|
def health():
|
||||||
|
return {"status": "ok"}
|
||||||
|
|
||||||
|
@app.post("/gitea")
|
||||||
|
async def gitea_webhook(
|
||||||
|
request: Request,
|
||||||
|
x_gitea_signature: str | None = Header(default=None)
|
||||||
|
):
|
||||||
|
body = await request.body()
|
||||||
|
verify_signature(body, x_gitea_signature)
|
||||||
|
|
||||||
|
payload = await request.json()
|
||||||
|
|
||||||
|
repo_name = payload.get("repository", {}).get("name")
|
||||||
|
ref = payload.get("ref", "")
|
||||||
|
|
||||||
|
if not repo_name:
|
||||||
|
raise HTTPException(status_code=400, detail="Missing repo name")
|
||||||
|
|
||||||
|
if ref != "refs/heads/main":
|
||||||
|
return {
|
||||||
|
"status": "ignored",
|
||||||
|
"reason": "not main branch",
|
||||||
|
"ref": ref
|
||||||
|
}
|
||||||
|
|
||||||
|
threading.Thread(target=run_deploy, args=(repo_name,), daemon=True).start()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "accepted",
|
||||||
|
"repo": repo_name
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fastapi
|
||||||
|
uvicorn[standard]
|
||||||
Reference in New Issue
Block a user