Files
hyprcrd/relay/ui/layout.php
T

187 lines
9.3 KiB
PHP

<?php
if (!defined('RELAY_BOOTSTRAPPED')) { http_response_code(404); exit; }
?><!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#1e293b">
<title>hyprcrd relay</title>
<link rel="stylesheet" href="fonts/phosphor.css">
<link rel="stylesheet" href="ui/style.css">
</head>
<body>
<header>
<span class="dot"></span>
<h1>hyprcrd relay</h1>
<span id="header-status" class="pixel" style="font-size:9px;color:var(--dim);"></span>
<span class="spacer"></span>
<div id="nav-buttons" class="hidden" style="display:flex;gap:6px;align-items:center;">
<span id="nav-user" class="user"></span>
<button id="nav-compose" class="nav-btn active" onclick="showView('compose')"><i class="ph ph-pencil-line"></i> compose</button>
<button id="nav-account" class="nav-btn" onclick="showView('account')"><i class="ph ph-user-circle"></i> account</button>
<button id="nav-admin" class="nav-btn hidden" onclick="showView('admin')"><i class="ph ph-users"></i> users</button>
<button class="nav-btn danger" onclick="doLogout()"><i class="ph ph-sign-out"></i> logout</button>
</div>
</header>
<main>
<!-- ── Setup view (first install) ── -->
<div id="view-setup" class="auth-form hidden">
<h2>welcome to relay</h2>
<p>This is the first time the relay is being set up. The user you create here will become the <strong>admin</strong>. You'll also set the <strong>super-shared secret</strong> — a relay-wide key used to route messages between tenants.</p>
<div class="field">
<label>admin username</label>
<input type="text" id="setup-username" placeholder="alice" autocomplete="username">
</div>
<div class="field">
<label>admin password</label>
<input type="password" id="setup-password" placeholder="min 4 chars" autocomplete="new-password">
</div>
<div class="field">
<label>super-shared secret</label>
<input type="password" id="setup-super-secret" placeholder="min 12 chars" autocomplete="new-password">
<p style="font-size:9px;color:var(--dim);margin-top:4px;line-height:1.4;">Used by the relay to encrypt messages at rest and route them between tenants. Set once, can't be changed without re-setup.</p>
</div>
<button class="btn btn-primary" onclick="doSetup()"><i class="ph ph-user-circle-plus"></i> create admin + activate relay</button>
<div id="setup-err" class="err"></div>
</div>
<!-- ── Login view ── -->
<div id="view-login" class="auth-form hidden">
<h2>login</h2>
<div class="field">
<label>username</label>
<input type="text" id="login-username" autocomplete="username">
</div>
<div class="field">
<label>password</label>
<input type="password" id="login-password" autocomplete="current-password">
</div>
<button class="btn btn-primary" onclick="doLogin()"><i class="ph ph-sign-in"></i> unlock</button>
<div id="login-err" class="err"></div>
</div>
<!-- ── Compose view ── -->
<div id="view-compose" class="hidden">
<div class="compose-card">
<h2><i class="ph ph-paper-plane-tilt"></i> compose</h2>
<div class="field" id="compose-recipient-field">
<label>recipient</label>
<select id="compose-recipient"></select>
</div>
<div class="field">
<label>title (optional)</label>
<input type="text" id="compose-title" autocomplete="off">
</div>
<div class="field">
<label>body</label>
<textarea id="compose-body" placeholder="write something..."></textarea>
</div>
<div class="actions">
<button class="btn btn-secondary" onclick="clearCompose()"><i class="ph ph-x"></i> clear</button>
<span class="spacer"></span>
<span id="compose-status" class="status"></span>
<button class="btn btn-primary" onclick="sendCompose()"><i class="ph ph-paper-plane-tilt"></i> send</button>
</div>
</div>
<div id="inbox-section" class="section">
<h2><i class="ph ph-tray"></i> your inbox <span class="count" id="inbox-count">0</span></h2>
<div id="inbox-list"></div>
</div>
</div>
<!-- ── Forced password change (first login) ── -->
<div id="view-change-password" class="auth-form hidden">
<h2>change your password</h2>
<p>This is your first login. Please choose a new password before you can use the relay.</p>
<div class="field">
<label>current password</label>
<input type="password" id="cp-current" placeholder="the one the admin gave you" autocomplete="current-password">
</div>
<div class="field">
<label>new password</label>
<input type="password" id="cp-new" placeholder="min 4 chars" autocomplete="new-password">
</div>
<div class="field">
<label>confirm new password</label>
<input type="password" id="cp-new2" placeholder="repeat" autocomplete="new-password">
</div>
<button class="btn btn-primary" onclick="doChangePassword()"><i class="ph ph-key"></i> change password</button>
<div id="cp-err" class="err"></div>
</div>
<!-- ── Account view ── -->
<div id="view-account" class="hidden">
<div class="section">
<h2><i class="ph ph-user-circle"></i> your client secret</h2>
<p style="font-size:11px;color:var(--muted);line-height:1.5;margin-bottom:14px;">
Your hyprcrd clients use this secret to encrypt messages before sending them to the relay.
All your devices share the same secret. Other tenants never see it. The relay stores it
encrypted with the master key so it can route your messages.
</p>
<div id="account-no-secret" class="hidden notice warn" style="margin-bottom:14px;">
⚠ no secret set yet — your hyprcrd clients can't connect until you generate or set one below
</div>
<div id="account-current" class="hidden" style="margin-bottom:14px;">
<label class="field-label" style="display:block;margin-bottom:4px;">current secret</label>
<div style="display:flex;gap:8px;align-items:stretch;">
<input id="account-secret-display" type="password" readonly class="w-full" style="flex:1;background:var(--bg);border:1px solid var(--border);color:var(--bright);padding:9px 12px;font-size:13px;font-family:'JetBrains Mono',monospace;border-radius:3px;">
<button class="btn btn-secondary" onclick="toggleAccountSecret()" id="account-secret-toggle"><i class="ph ph-eye"></i> show</button>
<button class="btn btn-secondary" onclick="copyAccountSecret()"><i class="ph ph-copy"></i> copy</button>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr;gap:14px;">
<div>
<label class="field-label">set your own secret</label>
<div style="display:flex;gap:8px;">
<input id="account-secret-input" type="text" placeholder="min 8 chars" style="flex:1;background:var(--bg);border:1px solid var(--border);color:var(--bright);padding:9px 12px;font-size:13px;font-family:'JetBrains Mono',monospace;border-radius:3px;">
<button class="btn btn-primary" onclick="saveAccountSecret()"><i class="ph ph-floppy-disk"></i> save</button>
</div>
</div>
<div style="text-align:center;color:var(--dim);font-family:var(--pixel);font-size:9px;">— or —</div>
<button class="btn btn-secondary" onclick="generateAccountSecret()"><i class="ph ph-dice-five"></i> generate random secret</button>
</div>
<div id="account-status" class="status" style="margin-top:12px;"></div>
<div style="margin-top:24px;padding-top:14px;border-top:1px solid var(--border);">
<button class="btn btn-danger" onclick="clearAccountSecret()"><i class="ph ph-x-circle"></i> clear secret (disconnect all clients)</button>
</div>
</div>
</div>
<!-- ── Admin view ── -->
<div id="view-admin" class="hidden">
<div class="section">
<h2><i class="ph ph-users-three"></i> users <span class="count" id="users-count">0</span></h2>
<div id="users-list"></div>
<div class="create-user-form">
<div>
<label for="new-username" class="field-label">username</label>
<input type="text" id="new-username" placeholder="alice" autocomplete="off">
</div>
<div>
<label for="new-password" class="field-label">password</label>
<input type="password" id="new-password" placeholder="min 4 chars" autocomplete="new-password">
</div>
<label class="checkbox">
<input type="checkbox" id="new-is-admin"> grant admin privileges
</label>
<p style="font-size:9px;color:var(--dim);line-height:1.4;margin-top:2px;">The new user sets their own client secret on first login.</p>
<div class="actions">
<button class="btn btn-primary" onclick="createUser()"><i class="ph ph-user-plus"></i> create user</button>
<span id="create-user-status" class="status"></span>
</div>
</div>
</div>
</div>
</main>
<div class="toast" id="toast"></div>
<script src="ui/app.js"></script>
</body>
</html>