organic: Add extended API

This commit is contained in:
2026-09-01 18:02:57 +02:00
parent 028c16be3d
commit 4572323a14
13 changed files with 978 additions and 266 deletions
View File
+346
View File
@@ -0,0 +1,346 @@
<?php
require "../.php/pdf.php";
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json; charset=UTF-8');
if ($_SERVER['REQUEST_METHOD'] !== 'GET' && $_SERVER['REQUEST_METHOD'] !== 'HEAD') {
header('Status: 405');
header('Content-Length: 56');
header('Allow: GET, HEAD');
echo "{\"error\":\"method not allowed\",\"allow\":[\"GET\",\"HEAD\"]}\n";
exit;
}
function jenc($data): string {
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
function jget($url): array | null {
$s = curl_init($url);
curl_setopt($s, CURLOPT_RETURNTRANSFER, true);
if (($json = curl_exec($s)) === false) {
return null;
}
return json_decode($json, true);
}
function pdf_fetch($url): array | null {
$s = curl_init("https://elwig.at/organic/pdf?format=json&url=" . urlencode($url));
curl_setopt($s, CURLOPT_RETURNTRANSFER, true);
if (($json = curl_exec($s)) === false) {
return null;
}
return json_decode($json, true);
}
function update_local(string $ooId, array $new): bool {
$existed = file_exists(".ids/$ooId.txt");
if (!($file = fopen(".ids/$ooId.txt", 'c+')))
return !$existed;
$data = [];
while (($line = fgets($file)) !== false) {
[$k,$v] = explode(':', trim($line), 2);
$data[$k] = $v;
}
foreach ($new as $k => $v) {
if ($v !== null)
$data[$k] = $v;
}
fseek($file, 0);
ftruncate($file, 0);
foreach ($data as $k => $v) {
fwrite($file, "$k:$v\n");
}
fclose($file);
return !$existed;
}
function find_local(string $anyId): string | null {
$found = [];
foreach (scandir('.ids/') as $file) {
if (!str_ends_with($file, '.txt')) continue;
$data = file_get_contents(".ids/$file");
if (str_contains($data, "\n$anyId\n"))
$found[] = substr($file, 0, strlen($file) - 4);
}
return sizeof($found) === 1 ? $found[0] : null;
}
function update_cache(string $country, string $postalCode): void {
$anyNew = false;
$traces = jget("https://webgate.ec.europa.eu/tracesnt/directory/publication/organic-operator/for/query?countryCode=$country&operatorPostalCode=$postalCode&sort=issuedOn");
foreach ($traces as $op) {
// traces
$ooId = $op['operatorIdentifier'];
$name = $op['operator']['name'];
$address = $op['operator']['address']['street']['value'];
$cityPostalCode = $op['operator']['address']['cityReference']['postalCode'];
$cityName = $op['operator']['address']['cityReference']['name'];
$new = update_local($ooId, ['plz' => $postalCode, 'name' => "$name|$address|$cityPostalCode $cityName"]);
$anyNew = $anyNew || $new;
}
if (!$anyNew) return;
$m = curl_multi_init();
$requests = [];
foreach (['easy-cert', 'bioc', 'bioqs', 'lkv'] as $api) {
$s = curl_init("https://elwig.at/organic/external/$api/operators?country=$country&postalCode=$postalCode");
$requests[] = $s;
curl_setopt($s, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($m, $s);
}
$running = 1;
do {
curl_multi_exec($m, $running);
if ($running) usleep(250_000);
} while ($running);
foreach ($requests as $r) {
curl_multi_remove_handle($m, $r);
}
curl_multi_close($m);
$data = [];
foreach ($requests as $s) {
if (($json = curl_multi_getcontent($s)) === null) {
header('Status: 500');
header('Content-Length: 34');
echo "{\"error\":\"internal server error\"}\n";
exit;
}
$data[] = json_decode($json, true)['data'];
}
foreach ($data[0] as $op) {
// easy-cert
$ooId = find_local("easy-cert:$op[db]:$op[id]");
$lfbisNr = null;
if ($ooId === null && ($opDetails = jget($op['url'])) !== null) {
foreach ($opDetails['certificates'] as $cert) {
if (preg_match("/\.([0-9]{3}-[0-9]{7})\./", $cert['nr'], $matches) === 1) {
$ooId = $matches[1];
}
}
if ($ooId === null || $lfbisNr === null) {
foreach ($opDetails['certificates'] as $cert) {
if ($cert['pdfUrl'] === null) continue;
$pdfDetails = pdf_fetch($cert['pdfUrl']);
if (isset($pdfDetails['operator']['lfbisNr'])) $lfbisNr = $pdfDetails['operator']['lfbisNr'];
if (!isset($pdfDetails['id'])) continue;
if (preg_match("/\.([0-9]{3}-[0-9]{7})\./", $pdfDetails['id'], $matches) === 1) {
$ooId = $matches[1];
}
}
}
}
if ($ooId === null)
continue;
update_local($ooId, ['plz' => $postalCode, 'lfbis' => $lfbisNr, 'easy-cert' => "$op[db]:$op[id]"]);
}
foreach ($data[1] as $op) {
// bioc
$ooId = find_local("bioc:$op[id]");
if ($ooId === null && ($opDetails = jget($op['url'])) !== null) {
foreach ($opDetails['certificates'] as $cert) {
if (preg_match("/\.([0-9]{3}-[0-9]{7})\./", $cert['nr'], $matches) === 1) {
$ooId = $matches[1];
}
}
if ($ooId === null) {
foreach ($opDetails['certificates'] as $cert) {
if ($cert['pdfUrl'] === null) continue;
$pdfDetails = pdf_fetch($cert['pdfUrl']);
if (isset($pdfDetails['operator']['lfbisNr'])) $op['lfbisNr'] = $pdfDetails['operator']['lfbisNr'];
if (!isset($pdfDetails['id'])) continue;
if (preg_match("/\.([0-9]{3}-[0-9]{7})\./", $pdfDetails['id'], $matches) === 1) {
$ooId = $matches[1];
}
}
}
}
if ($ooId === null)
continue;
update_local($ooId, ['plz' => $postalCode, 'bioc' => "$op[id]"]);
}
foreach ($data[2] as $op) {
// bioqs
$ooId = find_local("bioqs:$op[id]");
$lfbisNr = null;
if ($ooId === null || $lfbisNr === null) {
foreach ($op['certificates'] as $cert) {
if (preg_match("/\.([0-9]{3}-[0-9]{7})\./", $cert['nr'], $matches) === 1) {
$ooId = $matches[1];
}
}
if ($ooId === null || $lfbisNr === null) {
foreach ($op['certificates'] as $cert) {
if ($cert['pdfUrl'] === null) continue;
$pdfDetails = pdf_fetch($cert['pdfUrl']);
if (isset($pdfDetails['operator']['lfbisNr'])) $lfbisNr = $pdfDetails['operator']['lfbisNr'];
if (!isset($pdfDetails['id'])) continue;
if (preg_match("/\.([0-9]{3}-[0-9]{7})\./", $pdfDetails['id'], $matches) === 1) {
$ooId = $matches[1];
}
}
}
}
if ($ooId === null)
continue;
update_local($ooId, ['plz' => $postalCode, 'lfbis' => $lfbisNr, 'bioqs' => "$op[id]"]);
}
foreach ($data[3] as $op) {
// lkv
$ooId = find_local("lkv:$op[id]");
if ($ooId === null)
continue;
update_local($ooId, ['plz' => $postalCode, 'lfbis' => $op['lfbisNr'], 'lkv' => "$op[id]"]);
update_local($ooId, ['plz' => $postalCode, 'lfbis' => $op['lfbisNr'], 'lkv' => "$op[id]"]);
}
}
$info = substr($_SERVER['PATH_INFO'], 1);
if ($info === '') {
// search
$limit = $_GET['limit'] ?? null;
$offset = intval($_GET['offset'] ?? "0");
if ($limit === '') $limit = null;
if ($limit !== null) $limit = intval($limit);
$country = $_GET['country'] ?? null;
$postalCode = $_GET['postalCode'] ?? null;
if ($country === null || $postalCode === null) {
header('Status: 400');
header('Content-Length: 109');
echo "{\"error\":\"bad request\",\"message\":\"The 'country' and 'postalCode' request URL query parameters are required\"}\n";
exit;
}
update_cache($country, $postalCode);
echo "{\"data\":[";
if ($country === 'AT') {
$first = true;
foreach (scandir('.ids/') as $file) {
if (!str_ends_with($file, '.txt') || !str_starts_with($file, '040-')) continue;
$id = substr($file, 0, strlen($file) - 4);
$data = file_get_contents(".ids/$file");
if (str_starts_with($data, "plz:$postalCode\n")) {
preg_match("/^name:(.*?)\|(.*?)\|([0-9]{4}) (.*?)$/m", $data, $name);
preg_match("/^lfbis:(.*?)$/m", $data, $lfbis);
if (!$first) echo ",";
echo "\n ";
echo jenc([
'id' => $id,
'lfbisNr' => $lfbis[1],
'name' => $name[1],
'address' => $name[2],
'postalCode' => $name[3],
'city' => $name[4],
'country' => $country,
'url' => "https://elwig.at/organic/operators/$id",
]);
$first = false;
}
}
}
echo "\n]}\n";
exit;
}
if (preg_match("/^((easy-cert:[a-z]+|bioqs|bioc|lkv):[0-9a-zA-Z-_]+|lfbis:[0-9]+)$/", $info) === 1) {
$found = find_local($info);
if ($found !== null) {
header('Status: 303');
header('Location: /organic/operators/' . $found);
header('Content-Length: 0');
exit;
}
header('Status: 404');
header('Content-Length: 22');
echo "{\"error\":\"not found\"}\n";
exit;
} else if (!file_exists(".ids/$info.txt")) {
header('Status: 404');
header('Content-Length: 22');
echo "{\"error\":\"not found\"}\n";
exit;
}
$data = file_get_contents(".ids/$info.txt");
preg_match("/^name:(.*?)\|(.*?)\|([0-9]{4}) (.*?)$/m", $data, $name);
preg_match("/^lfbis:(.*?)$/m", $data, $lfbis);
preg_match("/^easy-cert:(.*?)$/m", $data, $easyCert);
preg_match("/^bioqs:(.*?)$/m", $data, $bioQs);
preg_match("/^bioc:(.*?)$/m", $data, $bioC);
if (sizeof($easyCert) > 1 && ($opDetails = jget("https://elwig.at/organic/external/easy-cert/operators/$easyCert[1]")) !== null) {
foreach ($opDetails['certificates'] as $cert) {
if ($cert['pdfUrl'] === null) continue;
pdf_fetch($cert['pdfUrl']);
}
}
if (sizeof($bioQs) > 1 && ($opDetails = jget("https://elwig.at/organic/external/bioqs/operators/$bioQs[1]")) !== null) {
foreach ($opDetails['certificates'] as $cert) {
if ($cert['pdfUrl'] === null) continue;
pdf_fetch($cert['pdfUrl']);
}
}
if (sizeof($bioC) > 1 && ($opDetails = jget("https://elwig.at/organic/external/bioc/operators/$bioC[1]")) !== null) {
foreach ($opDetails['certificates'] as $cert) {
if ($cert['pdfUrl'] === null) continue;
pdf_fetch($cert['pdfUrl']);
}
}
$certs = [];
$certIds = [];
foreach (scandir('../certificates/') as $file) {
if (!str_contains($file, ".$info."))
continue;
$certId = substr($file, 0, 31);
if (in_array($certId, $certIds))
continue;
$certIds[] = $certId;
$cert = parse_pdf("../certificates/$certId.pdf");
$appendix = parse_pdf("../certificates/$certId.appendix.pdf");
if ($cert === null && $appendix === null)
continue;
$parts = explode('.', $file);
$certs[] = [
'id' => $certId,
'nr' => $appendix['nr'] ?? null,
'validFrom' => $cert['validFrom'] ?? null,
'validTo' => $cert['validTo'] ?? null,
'authority' => ['code' => $parts[0]],
'operator' => ['id' => $parts[1]],
'url' => "https://elwig.at/organic/certificates/$certId",
'tracesPdfUrl' => $cert !== null ? "https://elwig.at/organic/certificates/$certId.pdf" : null,
'appendixPdfUrl' => $appendix !== null ? "https://elwig.at/organic/certificates/$certId.appendix.pdf" : null,
];
}
$details = [];
if (preg_match("/^easy-cert:(.*?)$/m", $data, $easyCert) === 1)
$details[] = "https://elwig.at/organic/external/easy-cert/operators/$easyCert[1]";
if (preg_match("/^bioc:(.*?)$/m", $data, $bioc) === 1)
$details[] = "https://elwig.at/organic/external/bioc/operators/$bioc[1]";
if (preg_match("/^bioqs:(.*?)$/m", $data, $bioqs) === 1)
$details[] = "https://elwig.at/organic/external/bioqs/operators/$bioqs[1]";
if (preg_match("/^lkv:(.*?)$/m", $data, $lkv) === 1)
$details[] = "https://elwig.at/organic/external/lkv/operators/$lkv[1]";
echo jenc([
'id' => $info,
'lfbisNr' => $lfbis[1],
'name' => $name[1],
'address' => $name[2],
'postalCode' => $name[3],
'city' => $name[4],
'country' => 'AT',
'url' => "https://elwig.at/organic/operators/$info",
'tracesUrl' => "https://webgate.ec.europa.eu/tracesnt/directory/publication/organic-operator/index#!?query=$info",
'originUrls' => $details,
'certificates' => $certs,
]);