Improve UI security and deploy workflow
This commit is contained in:
1 parent
be7762e369
commit
db9c4054a5
13 files changed
+435
-129
No files matched your search
+11
-3
@@ -5,13 +5,21 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{% block title %}UPS Dashboard{% endblock %}</title>
|
||||
<meta name="csrf-token" content="{{ request.cookies.get('csrf_token', '') }}" />
|
||||
<link rel="stylesheet" href="/static/css/base.css?v=4" />
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=4" />
|
||||
<link rel="stylesheet" href="/static/css/base.css?v=5" />
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=5" />
|
||||
{% block head_extra %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<div class="brand"><a href="/">⚡ APC UPS Dashboard</a></div>
|
||||
<div class="brand">
|
||||
<a href="/">
|
||||
<span class="brand-mark" aria-hidden="true">APC</span>
|
||||
<span class="brand-copy">
|
||||
<span class="brand-title">UPS Dashboard</span>
|
||||
<span class="brand-subtitle">Power continuity</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<nav class="main-nav">
|
||||
<a href="/" class="{% if active_nav == 'dashboard' %}active{% endif %}">Dashboard</a>
|
||||
<a href="/events" class="{% if active_nav == 'events' %}active{% endif %}">Events</a>
|
||||
|
||||
@@ -4,6 +4,17 @@
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<section class="dashboard-header" aria-labelledby="dashboard-title">
|
||||
<div>
|
||||
<p class="eyebrow">Live fleet status</p>
|
||||
<h1 id="dashboard-title">Power continuity dashboard</h1>
|
||||
</div>
|
||||
<div class="dashboard-clock" aria-live="polite">
|
||||
<span class="pulse-dot" aria-hidden="true"></span>
|
||||
<span id="dashboard-sync-state">Waiting for UPS telemetry</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="fleet-overview" id="fleet-overview">
|
||||
<div class="fleet-card">
|
||||
<div class="fleet-stat">
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
<link rel="stylesheet" href="/static/css/login.css" />
|
||||
</head>
|
||||
<body class="auth-body">
|
||||
<div class="auth-card">
|
||||
<div class="auth-card" data-auth-card="login">
|
||||
<div class="auth-mark">APC</div>
|
||||
<h1>UPS Dashboard</h1>
|
||||
<p class="muted">Sign in to monitor runtime, load, transfers, and alerts.</p>
|
||||
<form id="login-form">
|
||||
<div class="form-field">
|
||||
<label for="username">Username</label>
|
||||
@@ -23,27 +25,6 @@
|
||||
<div id="login-error" class="login-error" hidden></div>
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById('login-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const errBox = document.getElementById('login-error');
|
||||
errBox.hidden = true;
|
||||
const resp = await fetch('/api/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
username: document.getElementById('username').value,
|
||||
password: document.getElementById('password').value,
|
||||
}),
|
||||
});
|
||||
if (resp.ok) {
|
||||
window.location.href = '/';
|
||||
} else {
|
||||
const err = await resp.json().catch(() => ({}));
|
||||
errBox.textContent = err.detail || 'Login failed';
|
||||
errBox.hidden = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script src="/static/js/auth.js?v=1"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -8,9 +8,10 @@
|
||||
<link rel="stylesheet" href="/static/css/login.css" />
|
||||
</head>
|
||||
<body class="auth-body">
|
||||
<div class="auth-card wide">
|
||||
<div class="auth-card wide" data-auth-card="setup">
|
||||
<div class="auth-mark">APC</div>
|
||||
<h1>First-run Setup</h1>
|
||||
<p class="muted">Create the admin account. Passwords must be at least 8 characters.</p>
|
||||
<p class="muted">Create the admin account before exposing UPS controls.</p>
|
||||
<form id="setup-form">
|
||||
<div class="form-field">
|
||||
<label for="username">Admin username</label>
|
||||
@@ -27,36 +28,8 @@
|
||||
<button type="submit" class="btn-primary btn-block">Create admin account</button>
|
||||
<div id="setup-error" class="login-error" hidden></div>
|
||||
</form>
|
||||
<p class="muted" style="margin-top:16px;">After setup you'll be redirected to the dashboard. Configure UPS devices and SMTP from the nav.</p>
|
||||
<p class="muted auth-note">After setup you will be redirected to the dashboard. Configure UPS devices and SMTP from the nav.</p>
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById('setup-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const errBox = document.getElementById('setup-error');
|
||||
errBox.hidden = true;
|
||||
const pw = document.getElementById('password').value;
|
||||
const pw2 = document.getElementById('password2').value;
|
||||
if (pw !== pw2) {
|
||||
errBox.textContent = 'Passwords do not match';
|
||||
errBox.hidden = false;
|
||||
return;
|
||||
}
|
||||
const resp = await fetch('/api/setup', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
username: document.getElementById('username').value,
|
||||
password: pw,
|
||||
}),
|
||||
});
|
||||
if (resp.ok) {
|
||||
window.location.href = '/';
|
||||
} else {
|
||||
const err = await resp.json().catch(() => ({}));
|
||||
errBox.textContent = err.detail || 'Setup failed';
|
||||
errBox.hidden = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script src="/static/js/auth.js?v=1"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user