api docs, generator

This commit is contained in:
Mateusz Gruszczyński
2026-03-03 10:03:34 +01:00
parent b3a16303d2
commit 721ad44960
9 changed files with 1590 additions and 1004 deletions

194
static/js/generator.js Normal file
View File

@@ -0,0 +1,194 @@
const baseUrl = window.location.origin;
const variantsByApp = {
haproxy: [
{ value: "map", label: "map (recommended)" },
{ value: "acl", label: "acl" },
{ value: "lua", label: "lua" },
],
apache: [
{ value: "24", label: "24 (recommended)" },
{ value: "22", label: "22 (legacy)" },
],
nginx: [
{ value: "geo", label: "geo (recommended)" },
{ value: "deny", label: "deny (recommended)" },
{ value: "map", label: "map (not recommended)" },
],
};
function $(id) { return document.getElementById(id); }
function setBaseUrl() {
const el = $("baseUrl");
if (el) el.textContent = baseUrl;
}
function normalizeCountries(input) {
return String(input || "")
.split(",")
.map(s => s.trim().toUpperCase())
.filter(Boolean);
}
function updateModeUI() {
const mode = $("pyMode").value;
const rawOn = mode === "raw";
const genOn = mode === "generate";
$("pyRawFormatBox").style.display = rawOn ? "block" : "none";
$("pyAsJsBox").style.display = rawOn ? "block" : "none";
$("pyJsVarBox").style.display = rawOn ? "block" : "none";
$("pyAppTypeBox").style.display = genOn ? "block" : "none";
$("pyAppVariantBox").style.display = genOn ? "block" : "none";
if (genOn) {
updateVariantOptions();
} else {
updateRawJsFields();
}
}
function updateVariantOptions() {
const app = $("pyAppType").value;
const select = $("pyAppVariant");
const hint = $("variantHint");
select.innerHTML = "";
(variantsByApp[app] || []).forEach(v => {
const opt = document.createElement("option");
opt.value = v.value;
opt.textContent = v.label;
select.appendChild(opt);
});
if (app === "haproxy") hint.textContent = "Recommended: haproxy + map";
else if (app === "apache") hint.textContent = "Recommended: apache + 24";
else if (app === "nginx") hint.textContent = "Recommended: nginx + geo or deny (avoid map)";
else hint.textContent = "";
}
function updateRawJsFields() {
const fmt = $("pyRawFormat").value;
const asJs = $("pyAsJs").value === "true";
const allowJs = fmt === "raw-cidr_json";
$("pyAsJs").disabled = !allowJs;
$("pyJsVar").disabled = !allowJs || !asJs;
if (!allowJs) {
$("pyAsJs").value = "false";
}
}
function buildPythonScript() {
const mode = $("pyMode").value;
const countries = normalizeCountries($("pyCountries").value);
const aggregate = $("pyAggregate").value === "true";
const useCache = $("pyCache").value === "true";
let endpoint = "";
const payload = { countries, aggregate, use_cache: useCache };
if (mode === "raw") {
endpoint = "/api/generate/raw";
payload.app_type = $("pyRawFormat").value;
if (payload.app_type === "raw-cidr_json") {
const asJs = $("pyAsJs").value === "true";
payload.as_js = asJs;
if (asJs) payload.js_var = $("pyJsVar").value || "geoipBlocklist";
}
} else {
endpoint = "/api/generate";
payload.app_type = $("pyAppType").value;
payload.app_variant = $("pyAppVariant").value;
}
const script = `#!/usr/bin/env python3
import json
import re
import requests
BASE_URL = ${JSON.stringify(baseUrl)}
ENDPOINT = ${JSON.stringify(endpoint)}
payload = ${JSON.stringify(payload, null, 4)}
resp = requests.post(BASE_URL + ENDPOINT, json=payload, timeout=120)
print("Status:", resp.status_code)
print("X-From-Cache:", resp.headers.get("X-From-Cache"))
print("X-Cache-Type:", resp.headers.get("X-Cache-Type"))
print("X-Generated-At:", resp.headers.get("X-Generated-At"))
ct = (resp.headers.get("Content-Type") or "").lower()
if resp.status_code >= 400:
# try show JSON error, else text
try:
print(json.dumps(resp.json(), indent=2))
except Exception:
print(resp.text)
raise SystemExit(1)
if "application/json" in ct:
print(json.dumps(resp.json(), indent=2))
else:
filename = "output"
cd = resp.headers.get("Content-Disposition") or ""
m = re.search(r'filename="?([^"]+)"?', cd)
if m:
filename = m.group(1)
else:
# fallback extension
if "text/csv" in ct:
filename += ".csv"
elif "javascript" in ct:
filename += ".js"
elif "text/plain" in ct:
filename += ".txt"
else:
filename += ".bin"
with open(filename, "wb") as f:
f.write(resp.content)
print("Saved to:", filename)
`;
$("pythonScriptOutput").textContent = script;
}
async function copyPythonScript() {
const text = $("pythonScriptOutput").textContent || "";
await navigator.clipboard.writeText(text);
}
function bind() {
const topCopy = document.getElementById("btnCopyPyTop");
if (topCopy) topCopy.addEventListener("click", copyPythonScript);
$("pyMode").addEventListener("change", updateModeUI);
$("pyAppType").addEventListener("change", updateVariantOptions);
$("pyRawFormat").addEventListener("change", updateRawJsFields);
$("pyAsJs").addEventListener("change", updateRawJsFields);
$("btnGenPy").addEventListener("click", () => {
updateRawJsFields();
buildPythonScript();
});
$("btnCopyPy").addEventListener("click", copyPythonScript);
updateModeUI();
buildPythonScript();
}
document.addEventListener("DOMContentLoaded", () => {
setBaseUrl();
bind();
});