File size: 4,456 Bytes
c33ecdb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Flask-based UI + proxy to the Android build-runner.

Configuration:
- BUILD_RUNNER_URL (env): base URL of your build-runner, e.g. https://runpod-host.example
- DEFAULT_API_KEY (env): optional default API key to send to build-runner
- PORT (env): port (default 8080)
"""
import os
from flask import Flask, send_from_directory, request, jsonify, Response, redirect
import requests

BUILD_RUNNER = os.environ.get("BUILD_RUNNER_URL", "").rstrip("/")
DEFAULT_API_KEY = os.environ.get("DEFAULT_API_KEY", "")
if not BUILD_RUNNER:
    # App will still run locally; advise user to set BUILD_RUNNER_URL
    print("WARNING: BUILD_RUNNER_URL not set. UI will not be able to trigger builds until set.")

app = Flask(__name__, static_folder="static", static_url_path="/static")


def forward_headers(incoming):
    headers = {}
    # Accept an API key from the UI (header or form) or fallback to DEFAULT_API_KEY
    api_key = incoming.headers.get("X-API-KEY") or incoming.args.get("api_key") or incoming.form.get("api_key") or DEFAULT_API_KEY
    if api_key:
        headers["X-API-KEY"] = api_key
    return headers


@app.route("/", methods=["GET"])
def index():
    return send_from_directory("static", "index.html")


# Proxy endpoints
@app.route("/build", methods=["POST"])
def build():
    if not BUILD_RUNNER:
        return jsonify({"error": "BUILD_RUNNER_URL not configured"}), 500
    headers = forward_headers(request)
    # If UI sends a file (multipart), forward as-stream
    if request.content_type and request.content_type.startswith("multipart/"):
        files = {}
        data = {}
        # forward known fields
        for k in request.form:
            data[k] = request.form[k]
        if 'file' in request.files:
            f = request.files['file']
            files['file'] = (f.filename, f.stream, f.mimetype)
        resp = requests.post(f"{BUILD_RUNNER}/build", headers=headers, data=data, files=files, timeout=300)
    else:
        # JSON body (repo_url etc)
        payload = request.get_json(force=False) or request.form.to_dict()
        resp = requests.post(f"{BUILD_RUNNER}/build", headers=headers, json=payload, timeout=300)
    return Response(resp.content, status=resp.status_code, content_type=resp.headers.get("content-type", "application/json"))


@app.route("/status/<job_id>", methods=["GET"])
def status(job_id):
    if not BUILD_RUNNER:
        return jsonify({"error": "BUILD_RUNNER_URL not configured"}), 500
    headers = forward_headers(request)
    resp = requests.get(f"{BUILD_RUNNER}/status/{job_id}", headers=headers, timeout=30)
    return Response(resp.content, status=resp.status_code, content_type=resp.headers.get("content-type", "application/json"))


@app.route("/logs/<job_id>", methods=["GET"])
def logs(job_id):
    if not BUILD_RUNNER:
        return jsonify({"error": "BUILD_RUNNER_URL not configured"}), 500
    headers = forward_headers(request)
    resp = requests.get(f"{BUILD_RUNNER}/logs/{job_id}", headers=headers, stream=True, timeout=300)
    def stream():
        for chunk in resp.iter_content(chunk_size=4096):
            if chunk:
                yield chunk
    return Response(stream(), status=resp.status_code, content_type=resp.headers.get("content-type", "text/plain"))


@app.route("/artifacts/<job_id>/<name>", methods=["GET"])
def artifact(job_id, name):
    if not BUILD_RUNNER:
        return jsonify({"error": "BUILD_RUNNER_URL not configured"}), 500
    headers = forward_headers(request)
    # The build-runner returns artifact URLs in status; we can redirect by trying to fetch the status and locating artifact URL.
    resp = requests.get(f"{BUILD_RUNNER}/status/{job_id}", headers=headers, timeout=10)
    if resp.status_code != 200:
        return Response(resp.content, status=resp.status_code, content_type=resp.headers.get("content-type", "application/json"))
    data = resp.json()
    for a in data.get("artifacts", []):
        if a.get("name") == name:
            url = a.get("url")
            # If url is a relative path on the runner, convert to full URL
            if url.startswith("/"):
                return redirect(BUILD_RUNNER + url)
            else:
                return redirect(url)
    return jsonify({"error": "artifact not found"}), 404


# Serve static assets under /static/* automatically
# (Flask static_folder handles it)


if __name__ == "__main__":
    port = int(os.environ.get("PORT", 8080))
    app.run(host="0.0.0.0", port=port)