633c1bbe97
- Add hidden class to quota, create-user, and reset-password modal divs - Override opacity:0 from modals.css with opacity:1 in admin.css - Add crown and ban SVG icons to icon registry for toggle-role and deactivate buttons
610 lines
28 KiB
HTML
Executable File
610 lines
28 KiB
HTML
Executable File
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>OxiCloud — Admin Panel</title>
|
||
<script>
|
||
if (localStorage.getItem("oxicloud_theme") === "dark")
|
||
document.documentElement.setAttribute("data-theme", "dark");
|
||
</script>
|
||
<script src="/js/core/icons.js" defer></script>
|
||
<script src="/js/core/formatters.js" defer></script>
|
||
<script src="/js/core/csrf.js" defer></script>
|
||
<link rel="stylesheet" href="/css/main.css" />
|
||
<link rel="stylesheet" href="/css/views/admin.css" />
|
||
</head>
|
||
<body>
|
||
<div class="admin-header">
|
||
<div class="admin-header-left">
|
||
<a href="/" class="admin-logo-link link-reset-flex">
|
||
<div class="admin-logo">
|
||
<svg viewBox="0 0 500 500">
|
||
<path
|
||
d="M345 310c32 0 58-26 58-58s-26-58-58-58c-6.2 0-12 0.9-17.5 2.7C318 166 289 143 255 143c-34.3 0-63.1 22.6-73 53.7C176.9 195.7 171 195 165 195c-32 0-58 26-58 58s26 58 58 58h180z"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
<span class="admin-title-text">OxiCloud</span>
|
||
</a>
|
||
<span class="admin-title-separator">· Admin</span>
|
||
</div>
|
||
<div class="admin-header-right">
|
||
<a href="/"
|
||
><i class="fas fa-arrow-left"></i> Back to OxiCloud</a
|
||
>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="admin-container">
|
||
<div id="loading"><i class="fas fa-circle-notch"></i> Loading…</div>
|
||
<div id="access-denied">
|
||
<div class="access-icon"><i class="fas fa-lock"></i></div>
|
||
<h2>Access Denied</h2>
|
||
<p>Administrator privileges required to access this panel.</p>
|
||
<a href="/login"><i class="fas fa-sign-in-alt"></i> Sign in</a>
|
||
</div>
|
||
|
||
<div id="main-content">
|
||
<div class="admin-tabs">
|
||
<button
|
||
class="admin-tab active"
|
||
onclick="switchTab('dashboard',this)"
|
||
>
|
||
<i class="fas fa-chart-pie"></i> Dashboard
|
||
</button>
|
||
<button class="admin-tab" onclick="switchTab('users',this)">
|
||
<i class="fas fa-users"></i> Users
|
||
</button>
|
||
<button class="admin-tab" onclick="switchTab('oidc',this)">
|
||
<i class="fas fa-key"></i> SSO / OIDC
|
||
</button>
|
||
</div>
|
||
|
||
<div id="tab-dashboard" class="tab-content active">
|
||
<div class="stats-grid">
|
||
<div class="stat-card">
|
||
<div
|
||
class="stat-value text-blue"
|
||
id="ds-total-users"
|
||
>
|
||
—
|
||
</div>
|
||
<div class="stat-label">Total Users</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div
|
||
class="stat-value text-green"
|
||
id="ds-active-users"
|
||
>
|
||
—
|
||
</div>
|
||
<div class="stat-label">Active Users</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div
|
||
class="stat-value text-blue"
|
||
id="ds-admin-users"
|
||
>
|
||
—
|
||
</div>
|
||
<div class="stat-label">Admins</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="ds-version">—</div>
|
||
<div class="stat-label">Version</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="admin-card">
|
||
<h2><i class="fas fa-hdd"></i> Storage Overview</h2>
|
||
<div class="stats-grid">
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="ds-used">—</div>
|
||
<div class="stat-label">Used</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="ds-quota">—</div>
|
||
<div class="stat-label">Total Quota</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="ds-usage-pct">
|
||
—
|
||
</div>
|
||
<div class="stat-label">Usage %</div>
|
||
</div>
|
||
</div>
|
||
<div class="progress-bar">
|
||
<div
|
||
class="progress-fill green width-zero"
|
||
id="ds-bar"
|
||
></div>
|
||
</div>
|
||
<div class="mt-14">
|
||
<div class="stats-grid">
|
||
<div class="stat-card warn" id="ds-warn-card">
|
||
<div
|
||
class="stat-value text-orange"
|
||
id="ds-over80"
|
||
>
|
||
0
|
||
</div>
|
||
<div class="stat-label">
|
||
Users >80% quota
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="stat-card danger"
|
||
id="ds-danger-card"
|
||
>
|
||
<div
|
||
class="stat-value text-red"
|
||
id="ds-overquota"
|
||
>
|
||
0
|
||
</div>
|
||
<div class="stat-label">
|
||
Users over quota
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="admin-card">
|
||
<h2><i class="fas fa-server"></i> System</h2>
|
||
<div class="stats-grid">
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="ds-auth">—</div>
|
||
<div class="stat-label">Auth</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="ds-oidc">—</div>
|
||
<div class="stat-label">OIDC</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="ds-quotas-flag">
|
||
—
|
||
</div>
|
||
<div class="stat-label">Quotas</div>
|
||
</div>
|
||
</div>
|
||
<div class="toggle-row toggle-row-strong">
|
||
<label
|
||
><i
|
||
class="fas fa-user-plus icon-muted-right"
|
||
></i>
|
||
Allow public self-registration</label
|
||
>
|
||
<label class="switch"
|
||
><input
|
||
type="checkbox"
|
||
id="ds-registration"
|
||
checked
|
||
onchange="toggleRegistration(this.checked)" /><span
|
||
class="slider"
|
||
></span
|
||
></label>
|
||
</div>
|
||
<div class="warning" id="registration-warning">
|
||
<i class="fas fa-exclamation-triangle"></i> Public
|
||
registration is disabled. Only admins can create new
|
||
users.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tab-users" class="tab-content">
|
||
<div class="admin-card">
|
||
<h2 class="h2-space-between">
|
||
<span
|
||
><i class="fas fa-users-cog"></i> User
|
||
Management</span
|
||
><button
|
||
class="btn btn-primary"
|
||
onclick="openCreateUserModal()"
|
||
>
|
||
<i class="fas fa-user-plus"></i> Create User
|
||
</button>
|
||
</h2>
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>User</th>
|
||
<th>Role</th>
|
||
<th>Auth</th>
|
||
<th>Status</th>
|
||
<th>Storage</th>
|
||
<th>Last Login</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="users-tbody">
|
||
<tr>
|
||
<td
|
||
colspan="7"
|
||
class="table-loading-cell"
|
||
>
|
||
<i
|
||
class="fas fa-spinner fa-spin"
|
||
></i>
|
||
Loading users…
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="pagination">
|
||
<span id="users-info">—</span>
|
||
<div class="flex-gap-6">
|
||
<button
|
||
class="btn btn-sm btn-secondary"
|
||
id="prev-btn"
|
||
onclick="prevPage()"
|
||
disabled
|
||
>
|
||
<i class="fas fa-chevron-left"></i> Prev
|
||
</button>
|
||
<button
|
||
class="btn btn-sm btn-secondary"
|
||
id="next-btn"
|
||
onclick="nextPage()"
|
||
>
|
||
Next <i class="fas fa-chevron-right"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tab-oidc" class="tab-content">
|
||
<div class="admin-card">
|
||
<h2>
|
||
<i class="fas fa-shield-alt"></i> Single Sign-On
|
||
(OIDC / SSO)
|
||
</h2>
|
||
<div class="toggle-row">
|
||
<label>Enable SSO Authentication</label>
|
||
<label class="switch"
|
||
><input
|
||
type="checkbox"
|
||
id="oidc-enabled" /><span
|
||
class="slider"
|
||
></span
|
||
></label>
|
||
</div>
|
||
<div id="oidc-form">
|
||
<div class="form-group">
|
||
<label
|
||
>Provider Name
|
||
<span id="badge-provider_name"></span
|
||
></label>
|
||
<input
|
||
type="text"
|
||
id="provider-name"
|
||
placeholder="e.g., Authentik, Keycloak"
|
||
/>
|
||
</div>
|
||
<div class="form-group">
|
||
<label
|
||
>Issuer URL
|
||
<span id="badge-issuer_url"></span
|
||
></label>
|
||
<input
|
||
type="url"
|
||
id="issuer-url"
|
||
placeholder="https://auth.example.com/application/o/oxicloud/"
|
||
/>
|
||
<small
|
||
>OpenID Connect issuer URL of your identity
|
||
provider</small
|
||
>
|
||
</div>
|
||
<div class="oidc-discover-wrap">
|
||
<button
|
||
class="btn btn-secondary btn-sm"
|
||
id="discover-btn"
|
||
onclick="testConnection()"
|
||
>
|
||
<i class="fas fa-search"></i> Auto-discover
|
||
</button>
|
||
</div>
|
||
<div id="discovery-result"></div>
|
||
<div class="form-group">
|
||
<label
|
||
>Client ID <span id="badge-client_id"></span
|
||
></label>
|
||
<input
|
||
type="text"
|
||
id="client-id"
|
||
placeholder="oxicloud"
|
||
/>
|
||
</div>
|
||
<div class="form-group">
|
||
<label
|
||
>Client Secret
|
||
<span id="badge-client_secret"></span
|
||
></label>
|
||
<input
|
||
type="password"
|
||
id="client-secret"
|
||
placeholder="Leave empty to keep current value"
|
||
/>
|
||
<small id="secret-hint"
|
||
><i
|
||
class="fas fa-check-circle secret-icon"
|
||
></i>
|
||
A client secret is already configured</small
|
||
>
|
||
</div>
|
||
<div class="form-group">
|
||
<label
|
||
>Callback URL
|
||
<small class="small-muted"
|
||
>(register in your IdP)</small
|
||
></label
|
||
>
|
||
<div class="readonly-field">
|
||
<span id="callback-url">—</span
|
||
><button
|
||
onclick="copyCallback()"
|
||
title="Copy"
|
||
>
|
||
<i class="fas fa-copy"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<details>
|
||
<summary>
|
||
<i
|
||
class="fas fa-sliders-h summary-icon-right"
|
||
></i>
|
||
Advanced Settings
|
||
</summary>
|
||
<div class="form-group">
|
||
<label
|
||
>Scopes <span id="badge-scopes"></span
|
||
></label>
|
||
<input
|
||
type="text"
|
||
id="scopes"
|
||
placeholder="openid profile email"
|
||
/>
|
||
</div>
|
||
<div class="toggle-row">
|
||
<label
|
||
>Auto-provision users on first
|
||
login</label
|
||
>
|
||
<label class="switch"
|
||
><input
|
||
type="checkbox"
|
||
id="auto-provision"
|
||
checked /><span
|
||
class="slider"
|
||
></span
|
||
></label>
|
||
</div>
|
||
<div class="form-group">
|
||
<label
|
||
>Admin Groups
|
||
<span id="badge-admin_groups"></span
|
||
></label>
|
||
<input
|
||
type="text"
|
||
id="admin-groups"
|
||
placeholder="e.g., oxicloud-admins"
|
||
/>
|
||
<small
|
||
>Comma-separated OIDC group names that
|
||
map to admin role</small
|
||
>
|
||
</div>
|
||
<div class="toggle-row">
|
||
<label
|
||
>Disable password login (OIDC
|
||
only)</label
|
||
>
|
||
<label class="switch"
|
||
><input
|
||
type="checkbox"
|
||
id="disable-password" /><span
|
||
class="slider"
|
||
></span
|
||
></label>
|
||
</div>
|
||
<div class="warning" id="password-warning">
|
||
<i class="fas fa-exclamation-triangle"></i>
|
||
This will prevent ALL password-based logins!
|
||
</div>
|
||
</details>
|
||
<div class="oidc-actions">
|
||
<button
|
||
class="btn btn-secondary"
|
||
onclick="testConnection()"
|
||
>
|
||
<i class="fas fa-vial"></i> Test
|
||
</button>
|
||
<button
|
||
class="btn btn-primary"
|
||
id="save-btn"
|
||
onclick="saveOidcSettings()"
|
||
>
|
||
<i class="fas fa-save"></i> Save
|
||
</button>
|
||
</div>
|
||
<div id="oidc-status" class="alert"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="quota-modal" class="modal-overlay hidden">
|
||
<div class="modal">
|
||
<h3>
|
||
<i class="fas fa-box modal-title-icon"></i> Update Storage
|
||
Quota
|
||
</h3>
|
||
<div class="form-group">
|
||
<label>User: <strong id="qm-username"></strong></label>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>New Quota</label>
|
||
<div class="quota-input-row">
|
||
<input
|
||
type="number"
|
||
id="qm-value"
|
||
min="0"
|
||
step="1"
|
||
class="flex-2"
|
||
/>
|
||
<select id="qm-unit" class="select-qm-unit">
|
||
<option value="1073741824">GB</option>
|
||
<option value="1048576">MB</option>
|
||
<option value="1099511627776">TB</option>
|
||
</select>
|
||
</div>
|
||
<small>Set to 0 for unlimited</small>
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button
|
||
class="btn btn-secondary"
|
||
onclick="closeQuotaModal()"
|
||
>
|
||
Cancel
|
||
</button>
|
||
<button class="btn btn-primary" onclick="saveQuota()">
|
||
<i class="fas fa-save"></i> Save
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="create-user-modal" class="modal-overlay hidden">
|
||
<div class="modal">
|
||
<h3>
|
||
<i class="fas fa-user-plus modal-title-icon"></i> Create New
|
||
User
|
||
</h3>
|
||
<div class="form-group">
|
||
<label>Username *</label>
|
||
<input
|
||
type="text"
|
||
id="cu-username"
|
||
placeholder="johndoe"
|
||
minlength="3"
|
||
maxlength="32"
|
||
/>
|
||
<small>3–32 characters</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Password *</label>
|
||
<input
|
||
type="password"
|
||
id="cu-password"
|
||
placeholder="Min 8 characters"
|
||
minlength="8"
|
||
/>
|
||
</div>
|
||
<div class="form-group">
|
||
<label
|
||
>Email
|
||
<small class="small-muted">(optional)</small></label
|
||
>
|
||
<input
|
||
type="text"
|
||
id="cu-email"
|
||
placeholder="user@example.com (auto-generated if empty)"
|
||
/>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group flex-1">
|
||
<label>Role</label>
|
||
<select id="cu-role" class="select-cu-role">
|
||
<option value="user">User</option>
|
||
<option value="admin">Admin</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group flex-1">
|
||
<label>Quota</label>
|
||
<div class="quota-row">
|
||
<input
|
||
type="number"
|
||
id="cu-quota-value"
|
||
min="0"
|
||
step="1"
|
||
value="1"
|
||
class="flex-1"
|
||
/>
|
||
<select
|
||
id="cu-quota-unit"
|
||
class="select-cu-quota-unit"
|
||
>
|
||
<option value="1073741824">GB</option>
|
||
<option value="1048576">MB</option>
|
||
<option value="1099511627776">TB</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="cu-error" class="alert alert-no-margin"></div>
|
||
<div class="modal-actions">
|
||
<button
|
||
class="btn btn-secondary"
|
||
onclick="closeCreateUserModal()"
|
||
>
|
||
Cancel
|
||
</button>
|
||
<button
|
||
class="btn btn-primary"
|
||
id="cu-submit"
|
||
onclick="submitCreateUser()"
|
||
>
|
||
<i class="fas fa-user-plus"></i> Create
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="reset-pw-modal" class="modal-overlay hidden">
|
||
<div class="modal">
|
||
<h3>
|
||
<i class="fas fa-key modal-title-icon"></i> Reset Password
|
||
</h3>
|
||
<div class="form-group">
|
||
<label>User: <strong id="rp-username"></strong></label>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>New Password</label>
|
||
<input
|
||
type="password"
|
||
id="rp-password"
|
||
placeholder="Min 8 characters"
|
||
minlength="8"
|
||
/>
|
||
</div>
|
||
<div id="rp-error" class="alert alert-no-margin"></div>
|
||
<div class="modal-actions">
|
||
<button
|
||
class="btn btn-secondary"
|
||
onclick="closeResetPasswordModal()"
|
||
>
|
||
Cancel
|
||
</button>
|
||
<button
|
||
class="btn btn-primary"
|
||
id="rp-submit"
|
||
onclick="submitResetPassword()"
|
||
>
|
||
<i class="fas fa-save"></i> Reset
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/js/views/admin/admin.js" defer></script>
|
||
</body>
|
||
</html>
|