PHP
384 lines · 19,658 bytes
<?php
$cfg = require dirname(__DIR__) . '/config.php';
require_once dirname(__DIR__) . '/core/Brand.php';
require_once dirname(__DIR__) . '/core/Seo.php';
require_once dirname(__DIR__) . '/core/PagePerf.php';
require_once dirname(__DIR__) . '/core/Db.php';
require_once dirname(__DIR__) . '/core/SignKeys.php';
require_once dirname(__DIR__) . '/docs/sdk-registry.php';
$base = __DIR__;
$site = rtrim($cfg['site']['url'] ?? '', '/');
$apiUrl = $site . '/api/' . ($cfg['api']['version'] ?? '1.3') . '/';
$telegram = Brand::TELEGRAM;
$devUrl = Brand::DEV_URL;
$devName = Brand::DEV_NAME;
$signPub = '';
$sodiumOk = extension_loaded('sodium');
try {
Db::Connect($cfg['db']);
$signPub = SignKeys::PublicHex($cfg);
} catch (Throwable $e) {
}
function h($s) { return htmlspecialchars((string) $s, ENT_QUOTES, 'UTF-8'); }
function sdkResolve($base, $rel)
{
$rel = str_replace('\\', '/', $rel);
$rel = ltrim($rel, '/');
if ($rel === '' || strpos($rel, '..') !== false) return null;
$file = realpath($base . '/' . $rel);
if (!$file || strpos($file, realpath($base)) !== 0 || !is_file($file)) return null;
return $file;
}
function sdkLang($path)
{
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
$map = [
'php' => ['lang' => 'php', 'label' => 'PHP', 'icon' => 'php'],
'js' => ['lang' => 'javascript', 'label' => 'JavaScript', 'icon' => 'js'],
'py' => ['lang' => 'python', 'label' => 'Python', 'icon' => 'py'],
'cs' => ['lang' => 'csharp', 'label' => 'C#', 'icon' => 'cs'],
'lua' => ['lang' => 'lua', 'label' => 'Lua', 'icon' => 'lua'],
'rs' => ['lang' => 'rust', 'label' => 'Rust', 'icon' => 'rs'],
'go' => ['lang' => 'go', 'label' => 'Go', 'icon' => 'go'],
'java' => ['lang' => 'java', 'label' => 'Java', 'icon' => 'java'],
'ts' => ['lang' => 'typescript', 'label' => 'TypeScript', 'icon' => 'ts'],
'rb' => ['lang' => 'ruby', 'label' => 'Ruby', 'icon' => 'rb'],
'pl' => ['lang' => 'perl', 'label' => 'Perl', 'icon' => 'pl'],
'md' => ['lang' => 'markdown', 'label' => 'Markdown', 'icon' => 'cpp'],
];
return $map[$ext] ?? ['lang' => 'plaintext', 'label' => strtoupper($ext), 'icon' => 'cpp'];
}
$files = [
['lang' => 'PHP', 'path' => 'php/LibreAuth.php', 'note' => 'LibreAuth PHP client', 'icon' => 'php'],
['lang' => 'PHP', 'path' => 'php/example.php', 'note' => 'Login / license example', 'icon' => 'php'],
['lang' => 'JavaScript', 'path' => 'js/libreauth.js', 'note' => 'Browser & Node.js', 'icon' => 'js'],
['lang' => 'Python', 'path' => 'python/libreauth.py', 'note' => 'Python client', 'icon' => 'py'],
['lang' => 'C#', 'path' => 'csharp/LibreAuth.cs', 'note' => '.NET / Unity', 'icon' => 'cs'],
['lang' => 'Java', 'path' => 'java/LibreAuth.java', 'note' => 'Java client', 'icon' => 'java'],
['lang' => 'TypeScript', 'path' => 'typescript/LibreAuth.ts', 'note' => 'TypeScript client', 'icon' => 'ts'],
['lang' => 'Go', 'path' => 'go/libreauth.go', 'note' => 'Go client', 'icon' => 'go'],
['lang' => 'Ruby', 'path' => 'ruby/libreauth.rb', 'note' => 'Ruby client', 'icon' => 'rb'],
['lang' => 'Perl', 'path' => 'perl/libreauth.pl', 'note' => 'Perl client', 'icon' => 'pl'],
['lang' => 'Lua', 'path' => 'lua/libreauth.lua', 'note' => 'FiveM / RedM', 'icon' => 'lua'],
['lang' => 'Rust', 'path' => 'rust/src/lib.rs', 'note' => 'Rust client', 'icon' => 'rs'],
['lang' => 'C++', 'path' => 'cpp/README.md', 'note' => 'C++ setup guide', 'icon' => 'cpp'],
];
foreach (sdk_registry_all() as $id => $sdk) {
$local = $sdk['local'] ?? '';
if (!$local || $local === 'sdk/cpp/README.md') continue;
$rel = preg_replace('#^sdk/#', '', $local);
if (!is_file($base . '/' . $rel)) continue;
$exists = false;
foreach ($files as $f) {
if ($f['path'] === $rel) { $exists = true; break; }
}
if ($exists) continue;
if (substr($rel, -9) === 'README.md') {
$files[] = ['lang' => $sdk['name'], 'path' => $rel, 'note' => $sdk['repo'] . ' guide', 'icon' => $sdk['icon'] ?? 'cpp'];
}
}
$grouped = [];
foreach ($files as $f) {
$grouped[$f['lang']][] = $f;
}
if (isset($_GET['dl'])) {
$file = sdkResolve($base, $_GET['dl']);
if (!$file) { http_response_code(404); exit('Not found'); }
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
}
$viewPath = $_GET['view'] ?? $_GET['raw'] ?? null;
if ($viewPath) {
$file = sdkResolve($base, $viewPath);
if (!$file) { http_response_code(404); exit('Not found'); }
$rel = str_replace('\\', '/', ltrim(str_replace($base, '', $file), '/\\'));
$meta = sdkLang($rel);
$code = file_get_contents($file);
$lines = substr_count($code, "\n") + 1;
$current = $rel;
$viewTitle = basename($rel) . ' — LibreAuth SDK';
$viewDesc = 'View and download ' . ($meta['label'] ?? 'SDK') . ' source for LibreAuth client integration.';
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= h($viewTitle) ?></title>
<?php Seo::head([
'title' => $viewTitle,
'raw_title' => true,
'description' => $viewDesc,
'path' => 'sdk/?view=' . rawurlencode($rel),
'robots' => 'noindex, follow',
]); ?>
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<?php PagePerf::fonts(); ?>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/tokyo-night-dark.min.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/tokyo-night-dark.min.css"></noscript>
<?php PagePerf::css('sdk/assets/sdk.css'); ?>
<?php PagePerf::css('assets/brand.css'); ?>
</head>
<body>
<header class="topbar">
<div class="topbar-left">
<a href="<?= h($site) ?>/sdk/" class="brand">
<span class="brand-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg></span>
LibreAuth SDK
</a>
<a href="<?= h($site) ?>/docs/" class="nav-link">Docs</a>
</div>
<div class="topbar-right">
<a href="<?= h($devUrl) ?>" class="nav-link" target="_blank" rel="noopener"><?= h($devName) ?></a>
<a href="<?= h($telegram) ?>" class="icon-btn" target="_blank" rel="noopener" title="Telegram"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M11.944 0A12 12 0 000 12a12 12 0 0012 12 12 12 0 0012-12A12 12 0 0012 0a12 12 0 00-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 01.171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg></a>
</div>
</header>
<div class="view-layout" style="padding-top:0">
<div class="view-bar">
<div class="breadcrumb">
<a href="<?= h($site) ?>/sdk/">SDK</a><span class="sep">/</span>
<span class="current"><?= h($rel) ?></span>
</div>
<div class="view-actions">
<button type="button" class="copy-btn" id="copyCode"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg> Copy</button>
<a href="<?= h($site) ?>/sdk/?dl=<?= urlencode($rel) ?>" class="btn btn-sm btn-ghost">Download</a>
<a href="<?= h($site) ?>/sdk/" class="btn btn-sm btn-primary">← Back</a>
</div>
</div>
<div class="view-body">
<aside class="view-sidebar">
<?php foreach ($files as $f): ?>
<a href="<?= h($site) ?>/sdk/?view=<?= urlencode($f['path']) ?>" class="<?= $f['path'] === $current ? 'active' : '' ?>"><?= h(basename($f['path'])) ?></a>
<?php endforeach; ?>
</aside>
<div class="view-main">
<div class="code-wrap">
<div class="code-toolbar">
<span class="lang-tag"><?= h($meta['label']) ?></span>
<span><?= (int) $lines ?> lines · <?= number_format(strlen($code)) ?> bytes</span>
</div>
<pre><code class="language-<?= h($meta['lang']) ?>" id="sourceCode"><?= h($code) ?></code></pre>
</div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/php.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/javascript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/csharp.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/lua.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/rust.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/markdown.min.js"></script>
<script>
hljs.highlightAll();
document.getElementById('copyCode')?.addEventListener('click', () => {
const text = document.getElementById('sourceCode')?.textContent || '';
navigator.clipboard.writeText(text).then(() => {
const btn = document.getElementById('copyCode');
btn.classList.add('copied');
btn.innerHTML = '✓ Copied';
setTimeout(() => { btn.classList.remove('copied'); btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg> Copy'; }, 2000);
});
});
</script>
<?= Brand::footHtml() ?>
</body>
</html>
<?php
exit;
}
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LibreAuth SDK — Client Libraries</title>
<?php Seo::head([
'title' => 'Client SDK Libraries',
'description' => 'Download LibreAuth SDK source for C++, C#, Python, Go, Rust, Java, Lua, JavaScript, TypeScript and 17 languages — LibreAuth API v1.3 clients.',
'path' => 'sdk/',
'keywords' => 'LibreAuth SDK, C++ auth client, C# license, Python auth, Lua FiveM, game auth SDK',
'json_ld' => [
[
'@context' => 'https://schema.org',
'@type' => 'CollectionPage',
'name' => 'LibreAuth SDK Libraries',
'description' => 'Official client libraries for LibreAuth API v1.3',
'url' => Seo::url('sdk/'),
],
],
]); ?>
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<?php PagePerf::fonts(800); ?>
<?php PagePerf::css('sdk/assets/sdk.css'); ?>
<?php PagePerf::css('assets/brand.css'); ?>
</head>
<body>
<header class="topbar">
<div class="topbar-left">
<a href="<?= h($site) ?>/sdk/" class="brand">
<span class="brand-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg></span>
LibreAuth SDK
</a>
<a href="<?= h($site) ?>/docs/" class="nav-link">Docs</a>
<a href="<?= h($site) ?>/panel/" class="nav-link">Panel</a>
</div>
<div class="topbar-right">
<a href="<?= h($devUrl) ?>" class="nav-link" target="_blank" rel="noopener"><?= h($devName) ?></a>
<a href="<?= h($telegram) ?>" class="icon-btn" target="_blank" rel="noopener" title="Telegram"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M11.944 0A12 12 0 000 12a12 12 0 0012 12 12 12 0 0012-12A12 12 0 0012 0a12 12 0 00-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 01.171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg></a>
</div>
</header>
<div class="page">
<div class="hero">
<div class="hero-badge">LibreAuth Native SDKs</div>
<h1>Client SDK Libraries</h1>
<p>Download or view source code instantly — 17 built-in languages</p>
<div class="hero-actions">
<a href="<?= h($site) ?>/docs/?p=sdk" class="btn btn-primary">All 17 SDKs</a>
<a href="<?= h($site) ?>/docs/?p=api-live" class="btn btn-ghost">Live API Docs</a>
<a href="<?= h($site) ?>/sdk/?view=php/LibreAuth.php" class="btn btn-ghost">LibreAuth.php</a>
</div>
</div>
<div class="grid-2">
<div class="panel">
<div class="panel-head"><h2><span class="dot"></span> API Endpoint</h2></div>
<div class="endpoint"><?= h($apiUrl) ?></div>
<p class="hint">POST · params: <code style="background:var(--bg3);padding:2px 6px;border-radius:4px;font-size:11px">type</code> <code style="background:var(--bg3);padding:2px 6px;border-radius:4px;font-size:11px">name</code> <code style="background:var(--bg3);padding:2px 6px;border-radius:4px;font-size:11px">ownerid</code></p>
</div>
<div class="panel">
<div class="panel-head">
<h2><span class="dot" style="background:var(--accent2)"></span> Ed25519 Public Key</h2>
<?php if ($signPub !== ''): ?>
<button type="button" class="btn btn-sm btn-ghost" id="copyPub">Copy</button>
<?php endif; ?>
</div>
<?php if (!$sodiumOk): ?>
<p class="status-bad">PHP sodium disabled — server will not send signature headers</p>
<?php elseif ($signPub === ''): ?>
<p class="status-warn">No key yet — the first API init call will auto-generate one</p>
<?php else: ?>
<div class="pubkey-wrap"><div class="pubkey" id="pubkey"><?= h($signPub) ?></div></div>
<p class="status-ok">Use in C++ <code>get_public_key_hex()</code> from /health</p>
<?php endif; ?>
</div>
</div>
<div class="panel" style="margin-top:24px">
<div class="panel-head"><h2>All Client Languages</h2></div>
<div class="lang-all-grid">
<?php foreach (sdk_registry_all() as $id => $sdk): ?>
<a href="<?= h($site) ?>/docs/?p=sdk-detail&lang=<?= h($id) ?>" class="lang-all-item">
<span class="file-icon <?= h($sdk['icon']) ?>"><?= h(strtoupper(substr($sdk['name'], 0, 2))) ?></span>
<span><?= h($sdk['name']) ?></span>
</a>
<?php endforeach; ?>
</div>
</div>
<div class="search-bar">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
<input type="text" id="fileSearch" placeholder="Search SDKs — PHP, Lua, Python...">
</div>
<div id="fileSections">
<?php foreach ($grouped as $langName => $items): ?>
<section class="lang-section" data-lang="<?= h(strtolower($langName)) ?>">
<div class="lang-label"><?= h($langName) ?></div>
<div class="file-grid">
<?php foreach ($items as $f): ?>
<div class="file-card" data-name="<?= h(strtolower($f['path'] . ' ' . $f['note'])) ?>">
<div class="file-card-top">
<div class="file-icon <?= h($f['icon']) ?>"><?= h(strtoupper(substr($f['icon'], 0, 2))) ?></div>
</div>
<div class="file-name"><?= h(basename($f['path'])) ?></div>
<div class="file-desc"><?= h($f['note']) ?></div>
<div class="file-path" style="font-size:11px;color:var(--muted2);font-family:'JetBrains Mono',monospace"><?= h($f['path']) ?></div>
<div class="file-actions">
<a href="<?= h($site) ?>/sdk/?view=<?= urlencode($f['path']) ?>" class="primary">View Code</a>
<a href="<?= h($site) ?>/sdk/?dl=<?= urlencode($f['path']) ?>">Download</a>
</div>
</div>
<?php endforeach; ?>
</div>
</section>
<?php endforeach; ?>
</div>
<div class="panel" style="margin-top:8px">
<div class="panel-head"><h2>Quick Start</h2></div>
<div class="quick-tabs">
<button type="button" class="quick-tab active" data-tab="php">PHP</button>
<button type="button" class="quick-tab" data-tab="js">JavaScript</button>
<button type="button" class="quick-tab" data-tab="cpp">C++</button>
</div>
<div class="quick-code active" id="tab-php"><pre>require 'LibreAuth.php';
$LibreAuthPublicKey = '<?= h($signPub) ?>';
$app = new LibreAuth('AppName', 'OWNER_ID_10', '1.0', '<?= h($apiUrl) ?>');
$app->init();
$info = $app->login('user', 'pass', 'HWID');</pre></div>
<div class="quick-code" id="tab-js"><pre>const LibreAuth = require('./libreauth.js');
const app = new LibreAuth('AppName', 'OWNER_ID_10', '1.0', '<?= h($apiUrl) ?>');
await app.Init();
await app.Login('user', 'pass', 'HWID');</pre></div>
<div class="quick-code" id="tab-cpp"><pre>// auth.cpp
static std::string get_public_key_hex() {
return "<?= h($signPub) ?>";
}
// URL: <?= h($apiUrl) ?></pre></div>
</div>
</div>
<script>
document.getElementById('fileSearch')?.addEventListener('input', (e) => {
const q = e.target.value.toLowerCase();
document.querySelectorAll('.file-card').forEach(card => {
card.style.display = !q || card.dataset.name.includes(q) ? '' : 'none';
});
document.querySelectorAll('.lang-section').forEach(sec => {
const visible = [...sec.querySelectorAll('.file-card')].some(c => c.style.display !== 'none');
sec.style.display = visible ? '' : 'none';
});
});
document.querySelectorAll('.quick-tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.quick-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.quick-code').forEach(c => c.classList.remove('active'));
tab.classList.add('active');
document.getElementById('tab-' + tab.dataset.tab)?.classList.add('active');
});
});
document.getElementById('copyPub')?.addEventListener('click', () => {
const t = document.getElementById('pubkey')?.textContent || '';
navigator.clipboard.writeText(t).then(() => {
const btn = document.getElementById('copyPub');
btn.textContent = 'Copied!';
setTimeout(() => btn.textContent = 'Copy', 2000);
});
});
</script>
<?= Brand::footHtml() ?>
</body>
</html>