Files
MicroNAS/templates/index.html
T

341 lines
17 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MicroNAS</title>
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='img/favicon.svg') }}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/app.css') }}">
</head>
<body>
<div id="toastContainer"></div>
<!-- Password modal -->
<div class="modal fade" id="pwModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Change Password</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="text" id="pwUsername" class="form-control mb-3" readonly style="color:var(--muted)">
<input type="password" id="pwNew" class="form-control mb-2" placeholder="New password (min 8 chars)">
<input type="password" id="pwConfirm" class="form-control" placeholder="Confirm password">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="submitPassword()">Save</button>
</div>
</div>
</div>
</div>
<!-- Edit Share modal -->
<div class="modal fade" id="editModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editModalTitle">Edit Share</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body" id="editModalBody"></div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="submitEdit()">Save</button>
</div>
</div>
</div>
</div>
<!-- Delete confirm modal -->
<div class="modal fade" id="deleteModal" tabindex="-1">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title" style="color:var(--red)">Delete</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body pt-2">
<p id="deleteModalMsg" class="mb-0" style="color:var(--muted)"></p>
</div>
<div class="modal-footer border-0 pt-0">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-sm" id="deleteModalConfirm">Delete</button>
</div>
</div>
</div>
</div>
<!-- Admin settings modal -->
<div class="modal fade" id="adminModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Admin Credentials</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label" style="color:var(--muted); font-size:.85rem">Current Password</label>
<input type="password" id="adminCurrentPw" class="form-control" placeholder="Required to confirm changes">
</div>
<hr style="border-color:var(--border)">
<div class="mb-3">
<label class="form-label" style="color:var(--muted); font-size:.85rem">New Username <span style="font-weight:400">(leave blank to keep current)</span></label>
<input type="text" id="adminNewUser" class="form-control" placeholder="admin">
</div>
<div class="mb-3">
<label class="form-label" style="color:var(--muted); font-size:.85rem">New Password <span style="font-weight:400">(leave blank to keep current)</span></label>
<input type="password" id="adminNewPw" class="form-control" placeholder="min 8 characters">
</div>
<div class="mb-1">
<label class="form-label" style="color:var(--muted); font-size:.85rem">Confirm New Password</label>
<input type="password" id="adminNewPwConfirm" class="form-control">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="submitAdminSettings()">Save</button>
</div>
</div>
</div>
</div>
<!-- Header -->
<div class="topbar d-flex justify-content-between align-items-center">
<span class="d-flex align-items-center gap-2">
<img src="{{ url_for('static', filename='img/favicon.svg') }}" width="24" height="24" alt="">
<span class="fw-semibold fs-5">MicroNAS</span>
</span>
<div class="d-flex align-items-center gap-3">
<span id="lastRefresh" class="small" style="color:var(--muted)"></span>
<button type="button" class="btn btn-sm" style="border:1px solid var(--border); color:var(--muted)"
onclick="openAdminSettings()">&#9881; Settings</button>
<a href="/logout" class="btn btn-sm" style="border:1px solid var(--border); color:var(--muted)">Sign out</a>
</div>
</div>
<div class="container-lg py-4">
<ul class="nav nav-tabs mb-0" role="tablist">
<li class="nav-item">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#dashboard" type="button">Dashboard</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#smb" type="button">SMB Shares</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#nfs" type="button">NFS Exports</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#webdav" type="button">WebDAV</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#users" type="button">Users</button>
</li>
</ul>
<div class="tab-content" style="border:1px solid var(--border); border-top:none; border-radius:0 0 8px 8px; padding:1.5rem; background:var(--bg);">
<!-- DASHBOARD -->
<div class="tab-pane fade show active" id="dashboard">
<div class="row g-3 mb-4">
<div class="col-4 col-md-2">
<div class="card h-100">
<div class="card-body py-3 px-3">
<div class="card-label">smbd</div>
<div class="d-flex align-items-center mt-1" id="statusSmbd">
<span class="dot dot-red"></span><span class="small">…</span>
</div>
</div>
</div>
</div>
<div class="col-4 col-md-2">
<div class="card h-100">
<div class="card-body py-3 px-3">
<div class="card-label">nfsd</div>
<div class="d-flex align-items-center mt-1" id="statusNfsd">
<span class="dot dot-red"></span><span class="small">…</span>
</div>
</div>
</div>
</div>
<div class="col-4 col-md-2">
<div class="card h-100">
<div class="card-body py-3 px-3">
<div class="card-label">apache2</div>
<div class="d-flex align-items-center mt-1" id="statusApache2">
<span class="dot dot-red"></span><span class="small">…</span>
</div>
</div>
</div>
</div>
<div class="col-12 col-md-8">
<div class="card h-100">
<div class="card-body py-3 px-3">
<div class="d-flex justify-content-between align-items-baseline mb-2">
<div class="card-label">/srv disk</div>
<span id="diskLabel" class="small" style="color:var(--muted)">…</span>
</div>
<div class="progress" style="height:10px">
<div id="diskBar" class="progress-bar" style="width:0%; transition:width .6s ease"></div>
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-body py-2 px-3">
<span class="card-label me-3">Server IPs</span>
<span id="serverIps" style="font-size:.87em">…</span>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="fw-semibold">Shares</span>
<button class="btn btn-sm" style="color:var(--muted); border:1px solid var(--border);" onclick="loadDashboard()">&#x21bb; Refresh</button>
</div>
<div class="table-responsive mb-4">
<table class="table table-striped mb-0">
<thead><tr><th>Type</th><th>Name</th><th>Path</th><th>Access</th><th class="text-end">Size</th></tr></thead>
<tbody id="dashSharesBody">
<tr><td colspan="5" style="color:var(--muted)">Loading…</td></tr>
</tbody>
</table>
</div>
<div class="fw-semibold mb-2">Active SMB Connections</div>
<div class="table-responsive">
<table class="table table-striped mb-0">
<thead><tr><th>User</th><th>From</th></tr></thead>
<tbody id="activeConnectionsBody">
<tr><td colspan="2" style="color:var(--muted)">Loading…</td></tr>
</tbody>
</table>
</div>
</div>
<!-- SMB -->
<div class="tab-pane fade" id="smb">
<div class="fw-semibold mb-3">Add SMB Share</div>
<div class="row g-2 mb-4">
<div class="col-md-3"><input type="text" id="smbName" class="form-control" placeholder="Share name"></div>
<div class="col-md-4"><input type="text" id="smbPath" class="form-control" placeholder="/srv/samba/…"></div>
<div class="col-md-3"><input type="text" id="smbUsers" class="form-control" placeholder="user1,user2"></div>
<div class="col-md-2"><button type="button" class="btn btn-success w-100" onclick="addSMBShare()">Add</button></div>
</div>
<div class="fw-semibold mb-2">Shares</div>
<div class="table-responsive">
<table class="table table-striped mb-0">
<thead><tr><th>Name</th><th>Path</th><th>Valid Users</th><th></th><th>Actions</th></tr></thead>
<tbody id="smbTableBody">
<tr><td colspan="4" style="color:var(--muted)">Loading…</td></tr>
</tbody>
</table>
</div>
</div>
<!-- NFS -->
<div class="tab-pane fade" id="nfs">
<div class="fw-semibold mb-3">Add NFS Export</div>
<div class="row g-2 mb-4">
<div class="col-md-4"><input type="text" id="nfsPath" class="form-control" placeholder="/srv/data"></div>
<div class="col-md-3"><input type="text" id="nfsClients" class="form-control" placeholder="192.168.1.0/24 or *"></div>
<div class="col-md-2 d-flex align-items-center">
<div class="form-check ms-1">
<input type="checkbox" id="nfsReadOnly" class="form-check-input">
<label class="form-check-label" for="nfsReadOnly" style="color:var(--muted)">Read only</label>
</div>
</div>
<div class="col-md-3"><button type="button" class="btn btn-success w-100" onclick="addNFSShare()">Add</button></div>
</div>
<div class="fw-semibold mb-2">Exports</div>
<div class="table-responsive">
<table class="table table-striped mb-0">
<thead><tr><th>Path</th><th>Clients</th><th></th><th>Actions</th></tr></thead>
<tbody id="nfsTableBody">
<tr><td colspan="3" style="color:var(--muted)">Loading…</td></tr>
</tbody>
</table>
</div>
</div>
<!-- WEBDAV -->
<div class="tab-pane fade" id="webdav">
<div class="fw-semibold mb-3">Add WebDAV Share</div>
<div class="row g-2 mb-4">
<div class="col-md-2"><input type="text" id="webdavName" class="form-control" placeholder="Share name"></div>
<div class="col-md-4"><input type="text" id="webdavPath" class="form-control" placeholder="/srv/data"></div>
<div class="col-md-3"><input type="text" id="webdavUsers" class="form-control" placeholder="alice bob (empty = all)"></div>
<div class="col-md-3"><button type="button" class="btn btn-success w-100" onclick="addWebDAVShare()">Add</button></div>
</div>
<div class="fw-semibold mb-2">Shares</div>
<div class="table-responsive">
<table class="table table-striped mb-0">
<thead><tr><th>Name</th><th>Path</th><th>Users</th><th>URL</th><th>Actions</th></tr></thead>
<tbody id="webdavTableBody">
<tr><td colspan="5" style="color:var(--muted)">Loading…</td></tr>
</tbody>
</table>
</div>
<div class="mt-3" style="font-size:.82rem; color:var(--muted)">
Requires <code>apache2</code> + <code>apache2-utils</code> installed and
<code>mod_dav</code> / <code>mod_dav_fs</code> enabled.
Shares are served at <code>http://&lt;server-ip&gt;/dav/&lt;name&gt;</code>.
User passwords are synced automatically from the Users tab.
</div>
</div>
<!-- USERS -->
<div class="tab-pane fade" id="users">
<div class="fw-semibold mb-3">Create User</div>
<div class="row g-2 mb-4">
<div class="col-md-3"><input type="text" id="newUsername" class="form-control" placeholder="Username"></div>
<div class="col-md-4"><input type="password" id="newPassword" class="form-control" placeholder="Password (min 8 chars)"></div>
<div class="col-md-2"><button type="button" class="btn btn-success w-100" onclick="createUser()">Create</button></div>
</div>
<div class="fw-semibold mb-2">Users</div>
<div class="table-responsive mb-5">
<table class="table table-striped mb-0">
<thead><tr><th>Username</th><th>UID</th><th>SMB</th><th>Actions</th></tr></thead>
<tbody id="usersTableBody">
<tr><td colspan="4" style="color:var(--muted)">Loading…</td></tr>
</tbody>
</table>
</div>
<div class="fw-semibold mb-3">Create Group</div>
<div class="row g-2 mb-4">
<div class="col-md-3"><input type="text" id="newGroupName" class="form-control" placeholder="Group name"></div>
<div class="col-md-2"><button type="button" class="btn btn-success w-100" onclick="createGroup()">Create</button></div>
</div>
<div class="fw-semibold mb-2">Groups</div>
<div class="table-responsive">
<table class="table table-striped mb-0">
<thead><tr><th>Group</th><th>GID</th><th>Members</th><th>Actions</th></tr></thead>
<tbody id="groupsTableBody">
<tr><td colspan="4" style="color:var(--muted)">Loading…</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/js/bootstrap.bundle.min.js"></script>
<script src="{{ url_for('static', filename='js/utils.js') }}"></script>
<script src="{{ url_for('static', filename='js/dashboard.js') }}"></script>
<script src="{{ url_for('static', filename='js/shares.js') }}"></script>
<script src="{{ url_for('static', filename='js/users.js') }}"></script>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
</body>
</html>