This commit is contained in:
2025-11-07 09:47:03 +01:00
parent 646e574059
commit 55620c52d4
22 changed files with 1835 additions and 2 deletions

200
web/setup.php Normal file
View File

@@ -0,0 +1,200 @@
<?php
require __DIR__ . '/lib/app.php';
require __DIR__ . '/lib/torctl.php';
if (function_exists('app_is_installed') && app_is_installed()) {
header('Location: /'); exit;
}
$err = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$u = trim($_POST['u'] ?? '');
$p = $_POST['p'] ?? '';
$p2 = $_POST['p2'] ?? '';
$nickname = trim($_POST['nickname'] ?? 'RPI-Relay');
$contact = trim($_POST['contact'] ?? 'contact@admin.com');
$orport = (int)($_POST['orport'] ?? 9001);
$rate_mbps = (int)($_POST['rate_mbps'] ?? 5);
$burst_mbps = (int)($_POST['burst_mbps']?? max(10, $rate_mbps*2));
$cap_gb = (int)($_POST['cap_gb'] ?? 100);
$acc_day = (int)($_POST['acc_day'] ?? 1);
if ($u === '' || $p === '') {
$err = 'Username and password are required.';
} elseif ($p !== $p2) {
$err = 'Passwords do not match.';
} else {
$app_cfg = [
'admin_user' => $u,
'admin_pass' => password_hash($p, PASSWORD_DEFAULT),
'created_at' => date('c'),
];
if (function_exists('app_save_config')) {
app_save_config($app_cfg);
} else {
$dir = '/etc/torpanel';
@mkdir($dir, 0770, true);
@file_put_contents("$dir/app.json", json_encode($app_cfg, JSON_PRETTY_PRINT), LOCK_EX);
@chgrp($dir, 'www-data'); @chmod($dir, 0770);
@chgrp("$dir/app.json", 'www-data'); @chmod("$dir/app.json", 0660);
}
config_apply([
'nickname' => $nickname,
'contact' => $contact,
'orport' => $orport,
'rate_mbps' => $rate_mbps,
'burst_mbps' => $burst_mbps,
'cap_gb' => $cap_gb,
'acc_day' => $acc_day,
]);
header('Location: /login.php?ok=1'); exit;
}
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>First-time Setup · Tor Relay Panel</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="/assets/panel.css" rel="stylesheet">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="alternate icon" href="/favicon.svg">
</head>
<body>
<nav class="navbar navbar-dark" style="background:linear-gradient(90deg,#0d6efd,#4dabf7);">
<div class="container-fluid">
<span class="navbar-brand fw-bold">
<img src="/favicon.svg" alt="" style="width:20px;height:20px;margin-right:.5rem;vertical-align:-3px;">
Tor Relay Panel
</span>
<div class="ms-auto">
<button id="btnTheme" type="button" class="btn btn-sm btn-outline-light">Theme</button>
</div>
</div>
</nav>
<div class="page-wrap">
<div class="container maxw-960">
<div class="row justify-content-center">
<div class="col-lg-10">
<div class="card p-4 shadow-lg">
<h1 class="h4 mb-3">First-time Setup</h1>
<?php if ($err): ?>
<div class="alert alert-danger py-2 mb-3" role="alert"><?= htmlspecialchars($err) ?></div>
<?php endif; ?>
<form method="post" action="/setup.php" autocomplete="on">
<div class="mb-2"><div class="small text-muted">Step 1</div><div class="h5 mb-2">Admin account</div></div>
<div class="row g-3">
<div class="col-md-4">
<label class="form-label" for="u">Username</label>
<input class="form-control" id="u" name="u" required autofocus value="<?= htmlspecialchars($_POST['u'] ?? '') ?>">
</div>
<div class="col-md-4">
<label class="form-label" for="p">Password</label>
<input class="form-control" id="p" name="p" type="password" required autocomplete="new-password">
</div>
<div class="col-md-4">
<label class="form-label" for="p2">Confirm password</label>
<input class="form-control" id="p2" name="p2" type="password" required autocomplete="new-password">
</div>
</div>
<div class="mt-4 mb-2"><div class="small text-muted">Step 2</div><div class="h5 mb-2">Relay basics</div></div>
<div class="row g-3">
<div class="col-md-3">
<label class="form-label" for="nickname">Nickname</label>
<input class="form-control" id="nickname" name="nickname" placeholder="RPI-Relay"
value="<?= htmlspecialchars($_POST['nickname'] ?? '') ?>">
</div>
<div class="col-md-4">
<label class="form-label" for="contact">Contact</label>
<input class="form-control" id="contact" name="contact" placeholder="admin@example.com"
value="<?= htmlspecialchars($_POST['contact'] ?? '') ?>">
</div>
<div class="col-md-2">
<label class="form-label" for="orport">ORPort</label>
<input class="form-control" id="orport" name="orport" type="number" min="1" max="65535" placeholder="9001"
value="<?= htmlspecialchars($_POST['orport'] ?? '9001') ?>">
</div>
<div class="col-md-2">
<label class="form-label" for="rate_mbps">Bandwidth</label>
<div class="input-group">
<input class="form-control" id="rate_mbps" name="rate_mbps" type="number" min="1" step="1" placeholder="5"
value="<?= htmlspecialchars($_POST['rate_mbps'] ?? '5') ?>">
<span class="input-group-text">Mbps</span>
</div>
</div>
</div>
<div class="row g-3 mt-1">
<div class="col-md-3">
<label class="form-label" for="cap_gb">Monthly cap</label>
<div class="input-group">
<input class="form-control" id="cap_gb" name="cap_gb" type="number" min="1" step="1" placeholder="100"
value="<?= htmlspecialchars($_POST['cap_gb'] ?? '100') ?>">
<span class="input-group-text">GB</span>
</div>
</div>
<div class="col-md-3">
<label class="form-label" for="acc_day">Accounting day</label>
<select id="acc_day" name="acc_day" class="form-select">
<?php
$sel = (int)($_POST['acc_day'] ?? 1);
for($d=1;$d<=28;$d++){
$s = ($sel===$d)?' selected':'';
echo "<option$s>$d</option>";
}
?>
</select>
</div>
<div class="col-md-3">
<label class="form-label" for="burst_mbps">Burst</label>
<div class="input-group">
<input class="form-control" id="burst_mbps" name="burst_mbps" type="number" min="1" step="1" placeholder="10"
value="<?= htmlspecialchars($_POST['burst_mbps'] ?? '10') ?>">
<span class="input-group-text">Mbps</span>
</div>
</div>
</div>
<div class="d-flex gap-2 mt-4">
<button class="btn btn-primary" type="submit">Save & Finish</button>
</div>
<div class="alert alert-warning mt-3 py-2">
<div class="fw-semibold">Reminder</div>
<ul class="mb-0">
<li>Forward <span class="mono">TCP 9001</span> (or your ORPort) from your router to this device.</li>
<li>It can take a while to get the <b>Running</b> flag after changes.</li>
</ul>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
const THEME_KEY='torpanel:theme';
const mql=window.matchMedia('(prefers-color-scheme: dark)');
const btn=document.getElementById('btnTheme');
function preferredTheme(){const s=localStorage.getItem(THEME_KEY);return (s==='dark'||s==='light')?s:(mql.matches?'dark':'light');}
function setBtnLabel(t){ if(btn) btn.textContent=(t==='dark')?'🌞 Light':'🌙 Dark'; }
function applyTheme(t){ document.documentElement.setAttribute('data-theme', t); localStorage.setItem(THEME_KEY,t); setBtnLabel(t); }
mql.addEventListener('change',e=>{ if(!localStorage.getItem(THEME_KEY)) applyTheme(e.matches?'dark':'light'); });
btn?.addEventListener('click',()=>applyTheme(document.documentElement.getAttribute('data-theme')==='dark'?'light':'dark'));
applyTheme(preferredTheme());
</script>
</body>
</html>