File size: 10,607 Bytes
52673ce
4272701
52673ce
4272701
52673ce
 
 
4272701
52673ce
 
 
 
4272701
 
52673ce
 
 
 
 
 
4272701
 
52673ce
 
 
 
 
 
 
4272701
 
52673ce
 
 
 
 
4272701
52673ce
 
4272701
52673ce
 
 
4272701
52673ce
 
 
 
 
4272701
52673ce
4272701
52673ce
 
 
 
4272701
 
52673ce
4272701
 
52673ce
 
 
 
 
 
 
4272701
 
52673ce
4272701
52673ce
 
 
4272701
52673ce
 
 
 
 
 
 
 
4272701
 
52673ce
4272701
52673ce
 
 
4272701
52673ce
 
 
4272701
52673ce
 
4272701
52673ce
 
 
4272701
52673ce
 
 
4272701
 
 
52673ce
 
 
 
 
 
 
 
4272701
 
52673ce
4272701
52673ce
 
 
 
4272701
52673ce
4272701
52673ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4272701
 
52673ce
 
 
 
 
4272701
 
52673ce
 
 
 
 
 
4272701
52673ce
 
 
 
 
 
 
 
 
 
 
4272701
52673ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4272701
52673ce
 
 
 
 
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
{% extends "base.html" %}
{% block title %}{{ _("Proxy") }} · {{ bot.name }}{% endblock %}
{% block crumbs %}
<a href="/bots" class="parent">{{ _("Bots") }}</a>
<span class="sep">/</span>
<a href="/bots/{{ bot.id }}" class="parent">{{ bot.name }}</a>
<span class="sep">/</span>
<h1>{{ _("Proxy") }}</h1>
{% endblock %}
{% block content %}
<div class="page-header">
    <div>
        <h1>{{ _("Host proxy") }}</h1>
        <div class="subtitle">{{ _("Every connection to this bot goes through the panel — one entry point, one audit trail.") }}</div>
    </div>
</div>

<div class="alert alert-info" style="margin-bottom: 18px;">
    <span class="ico">{{ panel_icon('shield')|safe }}</span>
    <div class="alert-body">
        <div class="alert-title">{{ _("Why a proxy?") }}</div>
        {{ _("The bot's HF Space can stay") }} <strong>{{ _("private") }}</strong>. {{ _("External callers (Telegram webhooks, integrations, scripts) hit") }} <code>/proxy/{{ bot.slug }}/…</code> {{ _("on this panel, and we forward to the bot. Every request is logged to the audit table.") }}
    </div>
</div>

<div class="grid-2">
    <div class="card">
        <div class="card-header">
            <div>
                <h2>{{ _("Public URL") }}</h2>
                <div class="desc">{{ _("What external services call.") }}</div>
            </div>
        </div>
        <div class="card-body">
            <div class="detail-list">
                <div class="detail-row">
                    <span class="label">{{ _("Telegram webhook") }}</span>
                    <span class="value mono" style="font-size: 12.5px;">
                        <span data-copy="/proxy/{{ bot.slug }}/telegram/webhook">{{ request.url.scheme }}://{{ request.url.netloc }}/proxy/{{ bot.slug }}/telegram/webhook</span>
                        <button type="button" class="icon-btn" style="display:inline-grid;width:24px;height:24px;margin-left:4px;" data-copy="/proxy/{{ bot.slug }}/telegram/webhook" title="{{ _("Copy") }}">{{ panel_icon('copy')|safe }}</button>
                    </span>
                </div>
                <div class="detail-row">
                    <span class="label">{{ _("Health endpoint") }}</span>
                    <span class="value mono" style="font-size: 12.5px;">
                        <a href="/proxy/{{ bot.slug }}/health" target="_blank">/proxy/{{ bot.slug }}/health</a>
                    </span>
                </div>
                <div class="detail-row">
                    <span class="label">{{ _("Catch-all") }}</span>
                    <span class="value mono" style="font-size: 12.5px;">
                        /proxy/{{ bot.slug }}/&lt;{{ _("any-path") }}&gt;
                    </span>
                </div>
            </div>
            <hr>
            <h3 class="form-section-title"><span class="ico">{{ panel_icon('key')|safe }}</span> {{ _("Authentication") }}</h3>
            <p class="muted" style="font-size: 13px;">{{ _("Two ways to authenticate against the proxy:") }}</p>
            <ul style="margin: 0; padding-left: 18px; line-height: 1.7; color: var(--text-muted); font-size: 13px;">
                <li><strong>{{ _("Session cookie") }}</strong> — {{ _("for browser access. Admin or bot owner only.") }}</li>
                <li><strong>{{ _("Bearer token") }}</strong> — <code>Authorization: Bearer &lt;llm_proxy_token&gt;</code> {{ _("for machine-to-machine traffic.") }}</li>
            </ul>
        </div>
    </div>

    <div class="card">
        <div class="card-header">
            <div>
                <h2>{{ _("Health probe") }}</h2>
                <div class="desc">{{ _("Round-trip through the proxy to the bot's runtime.") }}</div>
            </div>
            <button type="button" class="btn btn-sm btn-outline" data-proxy-test>{{ panel_icon('refresh')|safe }} {{ _("Probe") }}</button>
        </div>
        <div class="card-body">
            <div data-proxy-test-result>
                <p class="muted">{{ _("Click") }} <strong>{{ _("Probe") }}</strong> {{ _("to send a request through") }} <code>/proxy/{{ bot.slug }}/health</code>.</p>
            </div>
        </div>
    </div>
</div>

<div class="card">
    <div class="card-header">
        <div>
            <h2>{{ _("Upstream (Render) — legacy") }}</h2>
            <div class="desc">{{ _("If your bot uses a separate Render proxy for LLM traffic, its status is shown here.") }}</div>
        </div>
        <a href="/bots/{{ bot.id }}?edit=1" class="btn btn-sm btn-outline">{{ panel_icon('edit')|safe }} {{ _("Edit URL") }}</a>
    </div>
    <div class="card-body">
        {% if not bot.render_url %}
        <p class="muted" style="margin: 0;">{{ _("No Render proxy URL configured. The bot either calls LLMs directly or uses the host's own proxy above.") }}</p>
        {% else %}
        <div class="detail-list">
            <div class="detail-row">
                <span class="label">{{ _("Status") }}</span>
                <span class="value">
                    {% if health.ok %}
                    <span class="health-pill healthy"><span class="ico"></span> {{ _("Healthy") }} · {{ health.latency_ms }}ms · HTTP {{ health.status }}</span>
                    {% elif health.status %}
                    <span class="health-pill warn"><span class="ico"></span> HTTP {{ health.status }}</span>
                    {% else %}
                    <span class="health-pill down"><span class="ico"></span> {{ _("Unreachable") }}</span>
                    {% endif %}
                </span>
            </div>
            <div class="detail-row"><span class="label">{{ _("URL") }}</span><span class="value mono" style="font-size: 12.5px;"><a href="{{ bot.render_url }}" target="_blank" rel="noopener">{{ bot.render_url }}</a></span></div>
            <div class="detail-row"><span class="label">{{ _("Path probed") }}</span><span class="value mono" style="font-size: 12.5px;">{{ health.path or '—' }}</span></div>
            {% if health.error %}<div class="detail-row"><span class="label">{{ _("Error") }}</span><span class="value muted" style="font-size: 12px;">{{ health.error }}</span></div>{% endif %}
        </div>
        {% endif %}
    </div>
</div>

<div class="card">
    <div class="card-header">
        <div>
            <h2>{{ _("Recent proxy traffic") }}</h2>
            <div class="desc">{{ _("Last requests routed through") }} <code>/proxy/{{ bot.slug }}/…</code>.</div>
        </div>
        <a href="/admin/audit?target_type=bot&target_id={{ bot.id }}" class="btn btn-sm btn-ghost">{{ _("Open audit log") }}</a>
    </div>
    <div class="card-body" style="padding: 0;">
        <div class="table-wrap" style="border: 0; border-radius: 0;">
        <table class="table">
            <thead><tr><th>{{ _("When") }}</th><th>{{ _("Method") }}</th><th>{{ _("Path") }}</th><th>{{ _("Status") }}</th><th>{{ _("Latency") }}</th><th>{{ _("User") }}</th></tr></thead>
            <tbody data-proxy-audit-body>
                <tr><td colspan="6" class="muted" style="text-align:center; padding: 18px;">{{ _("Loading…") }}</td></tr>
            </tbody>
        </table>
        </div>
    </div>
</div>
{% endblock %}

{% block scripts %}
<script>
(async function () {
    const btn = document.querySelector("[data-proxy-test]");
    const out = document.querySelector("[data-proxy-test-result]");
    if (btn && out) {
        btn.addEventListener("click", async () => {
            btn.disabled = true;
            btn.innerHTML = `<span class="spinner"></span> {{ _("Probing") }}`;
            out.innerHTML = `<p class="muted">{{ _("Probing through host proxy…") }}</p>`;
            try {
                const resp = await fetch("/proxy/{{ bot.slug }}/health", { credentials: "include" });
                const text = await resp.text();
                out.innerHTML = `
                    <div class="detail-list">
                        <div class="detail-row"><span class="label">{{ _("Status") }}</span><span class="value"><span class="badge status-${resp.ok ? 'success' : 'error'}">HTTP ${resp.status}</span></span></div>
                        <div class="detail-row"><span class="label">{{ _("Content-Type") }}</span><span class="value mono" style="font-size:12.5px;">${escapeHtml(resp.headers.get('content-type') || '—')}</span></div>
                    </div>
                    <pre class="mono" style="margin-top:10px;max-height:240px;overflow:auto;font-size:12px;">${escapeHtml(text.slice(0, 4096))}${text.length > 4096 ? '\n…' : ''}</pre>`;
            } catch (e) {
                out.innerHTML = `<div class="alert alert-error"><span class="ico">⚠️</span><div class="alert-body">${escapeHtml(String(e.message || e))}</div></div>`;
            } finally {
                btn.disabled = false;
                btn.innerHTML = `{{ panel_icon('refresh')|safe }} {{ _("Probe") }}`;
            }
        });
    }

    const tbody = document.querySelector("[data-proxy-audit-body]");
    if (tbody) {
        try {
            const resp = await fetch("/api/proxy/recent?bot_id={{ bot.id }}&limit=15", { credentials: "include" });
            if (resp.ok) {
                const data = await resp.json();
                if (!data.events || !data.events.length) {
                    tbody.innerHTML = `<tr><td colspan="6" class="muted" style="text-align:center; padding: 18px;">{{ _("No proxy traffic yet.") }}</td></tr>`;
                } else {
                    tbody.innerHTML = data.events.map(ev => {
                        const when = ev.created_at ? new Date(ev.created_at).toLocaleString() : "—";
                        const tone = ev.status >= 500 ? "error" : ev.status >= 400 ? "warn" : "success";
                        return `<tr>
                            <td class="muted" style="font-size: 12px;">${escapeHtml(when)}</td>
                            <td><code>${escapeHtml(ev.method || '')}</code></td>
                            <td class="mono" style="font-size: 12px;">${escapeHtml(ev.path || '')}</td>
                            <td><span class="badge status-${tone}">${ev.status}</span></td>
                            <td class="muted">${ev.latency_ms ?? '—'}ms</td>
                            <td>${escapeHtml(ev.user || '—')}</td>
                        </tr>`;
                    }).join("");
                }
            }
        } catch (e) {
            tbody.innerHTML = `<tr><td colspan="6" class="muted" style="text-align:center;">{{ _("Could not load recent traffic.") }}</td></tr>`;
        }
    }
})();
</script>
{% endblock %}