Initialize Go module
This commit is contained in:
parent
f9549a10bb
commit
e285d0fe5f
12 changed files with 2516 additions and 534 deletions
773
templates/status.html
Normal file
773
templates/status.html
Normal file
|
|
@ -0,0 +1,773 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
http-equiv="refresh"
|
||||
content="{{if .RefreshInterval}}{{.RefreshInterval}}{{else}}60{{end}}"
|
||||
/>
|
||||
<title>Server Status: {{.Server}}</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: {{.Color}};
|
||||
--text-color: white;
|
||||
--shadow-color: rgba(0, 0, 0, 0.3);
|
||||
--hover-color: rgba(255, 255, 255, 0.1);
|
||||
--card-bg: rgba(0, 0, 0, 0.15);
|
||||
--danger-color: #f44336;
|
||||
--success-color: #4caf50;
|
||||
--modal-bg: rgba(0, 0, 0, 0.85);
|
||||
--error-color: #ff6b6b;
|
||||
--info-color: #2196f3;
|
||||
--warning-color: #ff9800;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--primary-color);
|
||||
font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--text-color);
|
||||
padding: 20px;
|
||||
transition: background-color 0.5s ease;
|
||||
background-image: radial-gradient(circle at 10% 20%, rgba(255, 255, 255, 0.05) 0%, transparent 20%),
|
||||
radial-gradient(circle at 90% 80%, rgba(255, 255, 255, 0.05) 0%, transparent 20%);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--card-bg);
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(5px);
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.schedule-card {
|
||||
background-color: var(--card-bg);
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(5px);
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.schedule-header {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.schedule-status {
|
||||
padding: 12px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.schedule-status.active {
|
||||
background-color: rgba(76, 175, 80, 0.2);
|
||||
border: 1px solid rgba(76, 175, 80, 0.5);
|
||||
}
|
||||
|
||||
.schedule-status.inactive {
|
||||
background-color: rgba(244, 67, 54, 0.2);
|
||||
border: 1px solid rgba(244, 67, 54, 0.5);
|
||||
}
|
||||
|
||||
.schedule-info {
|
||||
margin-bottom: 25px;
|
||||
padding: 15px;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.schedule-info p {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.schedule-info p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.server-name {
|
||||
font-size: 1.2rem;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 15px 25px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: var(--text-color);
|
||||
background-color: var(--shadow-color);
|
||||
transition: transform 0.2s ease, background-color 0.3s ease, box-shadow 0.3s ease;
|
||||
min-width: 140px;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: var(--hover-color);
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 7px 14px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.button:active {
|
||||
transform: translateY(1px);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.button.danger {
|
||||
background-color: var(--danger-color);
|
||||
}
|
||||
|
||||
.button.success {
|
||||
background-color: var(--success-color);
|
||||
}
|
||||
|
||||
.button.submit {
|
||||
background-color: var(--success-color);
|
||||
}
|
||||
|
||||
.button::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.button.refresh::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 -960 960 960' width='24' fill='white'%3E%3Cpath d='M480-160q-133 0-226.5-93.5T160-480q0-133 93.5-226.5T480-800q85 0 149 34.5T740-671v-99q0-13 8.5-21.5T770-800q13 0 21.5 8.5T800-770v194q0 13-8.5 21.5T770-546H576q-13 0-21.5-8.5T546-576q0-13 8.5-21.5T576-606h138q-38-60-97-97t-137-37q-109 0-184.5 75.5T220-480q0 109 75.5 184.5T480-220q59 0 111-25t89-69q8-9 20.5-10t21.5 7q9 8 10 20t-7 22q-45 53-112 86.5T480-160Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.button.boot::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 -960 960 960' width='24' fill='white'%3E%3Cpath d='M480-120q-151 0-255.5-104.5T120-480q0-138 89-239t219-120q20-3 33.5 9.5T480-797q4 20-9 35.5T437-748q-103 12-170 87t-67 181q0 124 88 212t212 88q124 0 212-88t88-212q0-109-69.5-184.5T564-748q-21-3-31.5-19T525-798q3-20 19-30.5t35-6.5q136 19 228.5 122.5T900-480q0 150-104.5 255T480-120Zm0-170q-20 0-33.5-14T433-340v-286q0-21 14-34.5t33-13.5q20 0 33.5 13.5T527-626v286q0 22-14 36t-33 14Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.button.shutdown::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 -960 960 960' width='24' fill='white'%3E%3Cpath d='M480-120q-151 0-255.5-104.5T120-480q0-138 89-239t219-120q20-3 33.5 9.5T480-797q4 20-9 35.5T437-748q-103 12-170 87t-67 181q0 124 88 212t212 88q124 0 212-88t88-212q0-109-69.5-184.5T564-748q-21-3-31.5-19T525-798q3-20 19-30.5t35-6.5q136 19 228.5 122.5T900-480q0 150-104.5 255T480-120Zm0-360Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.button.schedule::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 -960 960 960' width='24' fill='white'%3E%3Cpath d='M480-120q-75 0-140.5-28.5t-114-77q-48.5-48.5-77-114T120-480q0-75 28.5-140.5t77-114q48.5-48.5 114-77T480-840q75 0 140.5 28.5t114 77q48.5 48.5 77 114T840-480q0 75-28.5 140.5t-77 114q-48.5 48.5-114 77T480-120Zm0-82q117 0 198.5-81.5T760-482q0-117-81.5-198.5T480-762q-117 0-198.5 81.5T200-482q0 117 81.5 198.5T480-202Zm0-280Zm-40 202-170-170 56-56 114 113 226-226 56 56-282 283Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.button.disable::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 -960 960 960' width='24' fill='white'%3E%3Cpath d='M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Zm-40 120-56-56 40-40-40-40 56-56 40 40 40-40 56 56-40 40 40 40-56 56-40-40-40 40Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.button.submit::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 -960 960 960' width='24' fill='white'%3E%3Cpath d='M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.test-panel {
|
||||
margin-top: 40px;
|
||||
padding: 20px;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 15px;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.test-note {
|
||||
color: white;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 40px;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.7;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Modal styles */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--modal-bg);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: #1e1e1e;
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
/* Form styles */
|
||||
.form-group {
|
||||
margin-bottom: 25px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
transition: border-color 0.3s, box-shadow 0.3s;
|
||||
}
|
||||
|
||||
input[type="datetime-local"] {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
outline: none;
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.checkbox-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.form-checkbox {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
accent-color: var(--success-color);
|
||||
}
|
||||
|
||||
.form-help {
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.7;
|
||||
margin-top: 5px;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.shutdown-warning {
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: rgba(255, 152, 0, 0.2);
|
||||
border: 1px solid rgba(255, 152, 0, 0.5);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: var(--error-color);
|
||||
font-size: 0.9rem;
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
border-radius: 50px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.badge.active {
|
||||
background-color: var(--success-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge.inactive {
|
||||
background-color: var(--danger-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 600px) {
|
||||
.card {
|
||||
padding: 30px 20px;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.controls {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.modal-actions .button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Status-specific icons */
|
||||
{{if eq .Status "Online"}}
|
||||
.status-icon::before {
|
||||
content: "✓";
|
||||
color: #4caf50;
|
||||
}
|
||||
{{else if eq .Status "Offline"}}
|
||||
.status-icon::before {
|
||||
content: "✗";
|
||||
color: #f44336;
|
||||
}
|
||||
{{else if eq .Status "Booting"}}
|
||||
.status-icon::before {
|
||||
content: "⟳";
|
||||
color: #ffeb3b;
|
||||
display: inline-block;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
{{else if eq .Status "Shutting down"}}
|
||||
.status-icon::before {
|
||||
content: "⏻";
|
||||
color: #ff9800;
|
||||
}
|
||||
{{end}}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
<div class="status-icon"></div>
|
||||
<h1 class="status-text">{{.Status}}</h1>
|
||||
<div class="server-name">Server: <strong>{{.Server}}</strong></div>
|
||||
|
||||
<div class="controls">
|
||||
<a href="/" class="button refresh">Refresh</a>
|
||||
<a href="/boot" class="button boot">Boot</a>
|
||||
<a href="/confirm-shutdown" class="button shutdown">Shutdown</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Scheduled Backup Window -->
|
||||
<div class="schedule-card">
|
||||
<h2 class="schedule-header">Scheduled Backup Window</h2>
|
||||
|
||||
{{if .Schedule.Enabled}}
|
||||
<div class="schedule-status active">
|
||||
Automatic scheduled backup is active
|
||||
</div>
|
||||
<div class="schedule-info">
|
||||
<p>
|
||||
<span>Start Time:</span>
|
||||
<span>{{.Schedule.StartTime}}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span>End Time:</span>
|
||||
<span>{{.Schedule.EndTime}}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span>Frequency:</span>
|
||||
<span>{{.Schedule.Frequency}}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span>Auto Shutdown:</span>
|
||||
<span
|
||||
>{{if .Schedule.AutoShutdown}}
|
||||
<span class="badge active">Enabled</span>
|
||||
{{else}}
|
||||
<span class="badge inactive">Disabled</span>
|
||||
{{end}}</span
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<span>Last Update:</span>
|
||||
<span>{{.LastUpdated}}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<button id="editSchedule" class="button schedule">
|
||||
Edit Schedule
|
||||
</button>
|
||||
<button id="disableSchedule" class="button disable">
|
||||
Disable Schedule
|
||||
</button>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="schedule-status inactive">No active backup schedule</div>
|
||||
<div class="controls">
|
||||
<button id="enableSchedule" class="button schedule">
|
||||
Configure Schedule
|
||||
</button>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if .IsTestMode}}
|
||||
<div class="test-panel">
|
||||
<div class="test-note">
|
||||
Running on macOS. Commands will be executed using the provided
|
||||
password.
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="footer">Wake-on-LAN Server Control Panel</div>
|
||||
</div>
|
||||
|
||||
<!-- Schedule Configuration Modal -->
|
||||
<div id="scheduleModal" class="modal-overlay" style="display: none">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">Configure Backup Schedule</div>
|
||||
<div class="modal-body">
|
||||
<form id="scheduleForm">
|
||||
<div class="form-group">
|
||||
<label for="startTime" class="form-label">Start Time:</label>
|
||||
<input
|
||||
type="time"
|
||||
id="startTime"
|
||||
name="startTime"
|
||||
class="form-input"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="endTime" class="form-label">End Time:</label>
|
||||
<input
|
||||
type="time"
|
||||
id="endTime"
|
||||
name="endTime"
|
||||
class="form-input"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="frequency" class="form-label">Frequency:</label>
|
||||
<select id="frequency" name="frequency" class="form-input">
|
||||
<option value="daily">Every day</option>
|
||||
<option value="every2days">Every 2 days</option>
|
||||
<option value="weekly">Once a week</option>
|
||||
<option value="monthly">Once a month</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox-wrapper">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="autoShutdown"
|
||||
name="autoShutdown"
|
||||
class="form-checkbox"
|
||||
/>
|
||||
<label for="autoShutdown"
|
||||
>Enable automatic shutdown at end time</label
|
||||
>
|
||||
</div>
|
||||
<div class="form-help">
|
||||
When enabled, the server will automatically shut down at the
|
||||
specified end time. Make sure SSH access is properly configured.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-help shutdown-warning">
|
||||
<strong>Note:</strong> To use automatic shutdown, add
|
||||
SHUTDOWN_PASSWORD to your .env file. The server will only be shut
|
||||
down automatically if it was started by the scheduler.
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="scheduleError"
|
||||
class="error-message"
|
||||
style="display: none"
|
||||
></div>
|
||||
|
||||
<div class="modal-actions">
|
||||
<button type="button" id="cancelSchedule" class="button">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="button submit">Save Schedule</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if .ConfirmShutdown}}
|
||||
<div class="modal-overlay">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">Confirm Shutdown</div>
|
||||
<div class="modal-body">
|
||||
Are you sure you want to shut down <strong>{{.Server}}</strong>?<br />
|
||||
This will immediately power off the server.
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<a href="/" class="button">Cancel</a>
|
||||
<form action="/shutdown" method="POST" style="display: inline">
|
||||
<button type="submit" class="button danger">Yes, Shutdown</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}} {{if .ErrorMessage}}
|
||||
<div class="modal-overlay">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">Error</div>
|
||||
<div class="modal-body">
|
||||
<div class="error-message">{{.ErrorMessage}}</div>
|
||||
|
||||
<div class="shutdown-warning" style="margin-top: 20px">
|
||||
<p>
|
||||
To use the shutdown feature, add SHUTDOWN_PASSWORD to your .env
|
||||
file.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<a href="/" class="button">Back to Home</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<script>
|
||||
// Schedule modal handling
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const scheduleModal = document.getElementById("scheduleModal");
|
||||
const enableScheduleBtn = document.getElementById("enableSchedule");
|
||||
const disableScheduleBtn = document.getElementById("disableSchedule");
|
||||
const cancelScheduleBtn = document.getElementById("cancelSchedule");
|
||||
const scheduleForm = document.getElementById("scheduleForm");
|
||||
const scheduleError = document.getElementById("scheduleError");
|
||||
|
||||
// Show schedule modal
|
||||
if (enableScheduleBtn) {
|
||||
enableScheduleBtn.addEventListener("click", function () {
|
||||
scheduleModal.style.display = "flex";
|
||||
});
|
||||
}
|
||||
|
||||
// Handle auto shutdown checkbox
|
||||
const autoShutdownCheckbox = document.getElementById("autoShutdown");
|
||||
|
||||
// Edit schedule button
|
||||
const editScheduleBtn = document.getElementById("editSchedule");
|
||||
if (editScheduleBtn) {
|
||||
editScheduleBtn.addEventListener("click", function() {
|
||||
// Pre-fill form with current values
|
||||
document.getElementById("startTime").value = "{{.Schedule.StartTime}}";
|
||||
document.getElementById("endTime").value = "{{.Schedule.EndTime}}";
|
||||
document.getElementById("frequency").value = "{{.Schedule.Frequency}}";
|
||||
document.getElementById("autoShutdown").checked = {{.Schedule.AutoShutdown}};
|
||||
|
||||
scheduleModal.style.display = "flex";
|
||||
});
|
||||
}
|
||||
|
||||
// Hide schedule modal
|
||||
if (cancelScheduleBtn) {
|
||||
cancelScheduleBtn.addEventListener("click", function () {
|
||||
scheduleModal.style.display = "none";
|
||||
});
|
||||
}
|
||||
|
||||
// Handle disable schedule
|
||||
if (disableScheduleBtn) {
|
||||
disableScheduleBtn.addEventListener("click", function () {
|
||||
// Disable schedule via API
|
||||
fetch("/api/schedule", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
enabled: false,
|
||||
startTime: "",
|
||||
endTime: "",
|
||||
frequency: "daily",
|
||||
autoShutdown: false,
|
||||
shutdownPassword: "",
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Refresh page to show updated status
|
||||
window.location.reload();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
alert("Failed to disable schedule. Please try again.");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Handle schedule form submission
|
||||
if (scheduleForm) {
|
||||
scheduleForm.addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
// We no longer use these variables - validation happens with startTime/endTime below
|
||||
|
||||
// Get form values
|
||||
const startTime = document.getElementById("startTime").value;
|
||||
const endTime = document.getElementById("endTime").value;
|
||||
const frequency = document.getElementById("frequency").value;
|
||||
|
||||
// Simple validation for times
|
||||
if (!startTime || !endTime) {
|
||||
scheduleError.textContent =
|
||||
"Please enter both start and end times";
|
||||
scheduleError.style.display = "block";
|
||||
return;
|
||||
}
|
||||
|
||||
// Get auto shutdown settings
|
||||
const autoShutdown =
|
||||
document.getElementById("autoShutdown").checked;
|
||||
|
||||
// Submit to API
|
||||
fetch("/api/schedule", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
enabled: true,
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
frequency: frequency,
|
||||
autoShutdown: autoShutdown,
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Refresh page to show updated status
|
||||
window.location.reload();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
scheduleError.textContent =
|
||||
"Failed to save schedule. Please try again.";
|
||||
scheduleError.style.display = "block";
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue