mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-18 00:55:50 +00:00
786 lines
29 KiB
HTML
Executable file
786 lines
29 KiB
HTML
Executable file
{% extends "base.html" %}
|
|
{% block title %}Settings - Dispatcharr{% endblock %}
|
|
{% block page_header %}Settings{% endblock %}
|
|
{% block breadcrumb %}
|
|
<li class="breadcrumb-item"><a href="/core/dashboard/">Home</a></li>
|
|
<li class="breadcrumb-item active" aria-current="page">Settings</li>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title">Settings</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Nav Tabs -->
|
|
<ul class="nav nav-tabs" id="settingsTab" role="tablist">
|
|
<li class="nav-item">
|
|
<a class="nav-link active" id="stream-profiles-tab" data-bs-toggle="tab" href="#stream-profiles"
|
|
role="tab" aria-controls="stream-profiles" aria-selected="true">
|
|
Stream Profiles
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" id="user-agents-tab" data-bs-toggle="tab" href="#user-agents" role="tab"
|
|
aria-controls="user-agents" aria-selected="false">
|
|
User Agents
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" id="logo-caching-tab" data-bs-toggle="tab" href="#logo-caching" role="tab"
|
|
aria-controls="logo-caching" aria-selected="false">
|
|
Logo Caching
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" id="backup-restore-tab" data-bs-toggle="tab" href="#backup-restore" role="tab"
|
|
aria-controls="backup-restore" aria-selected="false">
|
|
Backup/Restore
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" id="hdhr-tab" data-bs-toggle="tab" href="#hdhr" role="tab"
|
|
aria-controls="hdhr" aria-selected="false">
|
|
HDHR Emulation
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" id="user-management-tab" data-bs-toggle="tab" href="#user-management"
|
|
role="tab" aria-controls="user-management" aria-selected="false">
|
|
Users & Groups
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Tab Content -->
|
|
<div class="tab-content mt-3" id="settingsTabContent">
|
|
|
|
<!-- STREAM PROFILES -->
|
|
<div class="tab-pane fade show active" id="stream-profiles" role="tabpanel"
|
|
aria-labelledby="stream-profiles-tab">
|
|
<button id="addStreamProfileBtn" class="btn btn-primary mb-2">
|
|
<i class="bi bi-plus"></i> Add Stream Profile
|
|
</button>
|
|
<div class="table-responsive">
|
|
<table id="streamProfilesTable" class="table table-striped nowrap">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Profile Name</th>
|
|
<th>Command</th>
|
|
<th>Parameters</th>
|
|
<th>Active</th>
|
|
<th>User Agent</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- USER AGENTS -->
|
|
<div class="tab-pane fade" id="user-agents" role="tabpanel" aria-labelledby="user-agents-tab">
|
|
<button id="addUserAgentBtn" class="btn btn-primary mb-2">
|
|
<i class="bi bi-plus"></i> Add User Agent
|
|
</button>
|
|
<div class="table-responsive">
|
|
<table id="userAgentsTable" class="table table-striped nowrap">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>User Agent Name</th>
|
|
<th>User Agent</th>
|
|
<th>Description</th>
|
|
<th>Active</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- LOGO CACHING -->
|
|
<div class="tab-pane fade" id="logo-caching" role="tabpanel" aria-labelledby="logo-caching-tab">
|
|
<form id="logoCachingForm" class="mt-3">
|
|
<div class="form-check form-switch mb-3">
|
|
<input type="checkbox" class="form-check-input" id="cacheLogosToggle" name="cache_logos">
|
|
<label class="form-check-label" for="cacheLogosToggle">
|
|
Cache Channel Logos Locally
|
|
</label>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Save Logo Caching Settings</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- BACKUP / RESTORE -->
|
|
<div class="tab-pane fade" id="backup-restore" role="tabpanel" aria-labelledby="backup-restore-tab">
|
|
<div class="mt-3">
|
|
<button id="createBackupBtn" class="btn btn-success mb-2">
|
|
<i class="bi bi-download"></i> Create Backup
|
|
</button>
|
|
<button id="restoreBackupBtn" class="btn btn-warning mb-2">
|
|
<i class="bi bi-upload"></i> Restore Backup
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- HDHR EMULATION -->
|
|
<div class="tab-pane fade" id="hdhr" role="tabpanel" aria-labelledby="hdhr-tab">
|
|
<form id="hdhrEmulationForm" class="mt-3">
|
|
<div class="form-check form-switch mb-3">
|
|
<input type="checkbox" class="form-check-input" id="hdhrEmulationToggle" name="hdhr_emulation">
|
|
<label class="form-check-label" for="hdhrEmulationToggle">Enable HDHR Emulation</label>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Save HDHR Settings</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- USERS & GROUPS -->
|
|
<div class="tab-pane fade" id="user-management" role="tabpanel" aria-labelledby="user-management-tab">
|
|
<div class="row mt-3">
|
|
<!-- USERS -->
|
|
<div class="col-md-6">
|
|
<h5>Users</h5>
|
|
<button id="addUserBtn" class="btn btn-primary mb-2">
|
|
<i class="bi bi-plus"></i> Add User
|
|
</button>
|
|
<div class="table-responsive">
|
|
<table id="usersTable" class="table table-striped nowrap">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Username</th>
|
|
<th>Email</th>
|
|
<th>Groups</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- GROUPS -->
|
|
<div class="col-md-6">
|
|
<h5>Groups</h5>
|
|
<button id="addGroupBtn" class="btn btn-primary mb-2">
|
|
<i class="bi bi-plus"></i> Add Group
|
|
</button>
|
|
<div class="table-responsive">
|
|
<table id="groupsTable" class="table table-striped nowrap">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Name</th>
|
|
<th>Permissions</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div><!-- end row -->
|
|
</div><!-- end user-management tab content -->
|
|
</div><!-- end tab-content -->
|
|
</div><!-- end card-body -->
|
|
</div><!-- end card -->
|
|
|
|
<!-- ============== MODALS ============== -->
|
|
|
|
<!-- STREAM PROFILE MODAL -->
|
|
<div class="modal fade" id="streamProfileModal" tabindex="-1" aria-labelledby="streamProfileModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<form id="streamProfileForm" enctype="multipart/form-data">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="streamProfileModalLabel">Stream Profile</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" id="streamProfileId" name="id">
|
|
<div class="mb-3">
|
|
<label for="profileName" class="form-label">Profile Name</label>
|
|
<input type="text" class="form-control" id="profileName" name="profile_name" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="command" class="form-label">Command</label>
|
|
<input type="text" class="form-control" id="command" name="command" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="parameters" class="form-label">Parameters</label>
|
|
<textarea class="form-control" id="parameters" name="parameters" required></textarea>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="profileActive" class="form-label">Active</label>
|
|
<select class="form-select" id="profileActive" name="is_active">
|
|
<option value="true">Yes</option>
|
|
<option value="false">No</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="profileUserAgent" class="form-label">User Agent (optional)</label>
|
|
<input type="text" class="form-control" id="profileUserAgent" name="user_agent">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="submit" class="btn btn-primary">Save Profile</button>
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- USER AGENT MODAL -->
|
|
<div class="modal fade" id="userAgentModal" tabindex="-1" aria-labelledby="userAgentModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<form id="userAgentForm">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="userAgentModalLabel">Add User Agent</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" id="userAgentId" name="id">
|
|
<div class="mb-3">
|
|
<label for="userAgentNameField" class="form-label">User Agent Name</label>
|
|
<input type="text" class="form-control" id="userAgentNameField" name="user_agent_name" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="userAgentStringField" class="form-label">User Agent String</label>
|
|
<input type="text" class="form-control" id="userAgentStringField" name="user_agent" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="userAgentDescription" class="form-label">Description</label>
|
|
<input type="text" class="form-control" id="userAgentDescription" name="description">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="userAgentActive" class="form-label">Active</label>
|
|
<select class="form-select" id="userAgentActive" name="is_active">
|
|
<option value="true">Yes</option>
|
|
<option value="false">No</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="submit" class="btn btn-primary">Save User Agent</button>
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- USER MODAL -->
|
|
<div class="modal fade" id="userModal" tabindex="-1" aria-labelledby="userModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<form id="userForm">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="userModalLabel">Add User</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" id="userId" name="id">
|
|
<div class="mb-3">
|
|
<label for="usernameField" class="form-label">Username</label>
|
|
<input type="text" class="form-control" id="usernameField" name="username" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="emailField" class="form-label">Email</label>
|
|
<input type="email" class="form-control" id="emailField" name="email">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="submit" class="btn btn-primary">Save User</button>
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- GROUP MODAL -->
|
|
<div class="modal fade" id="groupModal" tabindex="-1" aria-labelledby="groupModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<form id="groupForm">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="groupModalLabel">Add Group</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" id="groupId" name="id">
|
|
<div class="mb-3">
|
|
<label for="groupNameField" class="form-label">Group Name</label>
|
|
<input type="text" class="form-control" id="groupNameField" name="name" required>
|
|
</div>
|
|
<!-- If you want to pick permissions:
|
|
<div class="mb-3">
|
|
<label class="form-label">Permissions</label>
|
|
<select class="form-select" id="groupPermissionsField" name="permissions" multiple>
|
|
...
|
|
</select>
|
|
</div>
|
|
-->
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="submit" class="btn btn-primary">Save Group</button>
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<!-- DataTables CSS -->
|
|
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css">
|
|
<link rel="stylesheet" href="https://cdn.datatables.net/responsive/2.3.0/css/responsive.dataTables.min.css">
|
|
|
|
<!-- DataTables JS / Dependencies -->
|
|
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
|
|
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
|
<script src="https://cdn.datatables.net/responsive/2.3.0/js/dataTables.responsive.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
|
|
<script>
|
|
/* CSRF Helper */
|
|
function getCookie(name) {
|
|
let cookieValue = null;
|
|
if (document.cookie && document.cookie !== "") {
|
|
const cookies = document.cookie.split(";");
|
|
for (let i = 0; i < cookies.length; i++) {
|
|
const cookie = cookies[i].trim();
|
|
if (cookie.substring(0, name.length + 1) === (name + "=")) {
|
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return cookieValue;
|
|
}
|
|
const csrftoken = getCookie("csrftoken");
|
|
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
/* STREAM PROFILES TABLE */
|
|
const streamProfilesTable = new DataTable("#streamProfilesTable", {
|
|
ajax: { url: "/api/core/streamprofiles/", dataSrc: "" },
|
|
responsive: true,
|
|
columns: [
|
|
{ data: "id" },
|
|
{ data: "profile_name" },
|
|
{ data: "command" },
|
|
{ data: "parameters" },
|
|
{
|
|
data: "is_active",
|
|
render: data => (data ? "Yes" : "No")
|
|
},
|
|
{ data: "user_agent" },
|
|
{
|
|
data: null,
|
|
render: row => `
|
|
<button class="btn btn-sm btn-primary edit-stream-profile" data-id="${row.id}">Edit</button>
|
|
<button class="btn btn-sm btn-danger delete-stream-profile" data-id="${row.id}">Delete</button>
|
|
`
|
|
}
|
|
]
|
|
});
|
|
|
|
/* ADD STREAM PROFILE */
|
|
document.getElementById("addStreamProfileBtn").addEventListener("click", () => {
|
|
document.getElementById("streamProfileForm").reset();
|
|
document.getElementById("streamProfileId").value = "";
|
|
document.getElementById("streamProfileModalLabel").textContent = "Add Stream Profile";
|
|
new bootstrap.Modal(document.getElementById("streamProfileModal")).show();
|
|
});
|
|
|
|
/* SAVE STREAM PROFILE */
|
|
document.getElementById("streamProfileForm").addEventListener("submit", function(e) {
|
|
e.preventDefault();
|
|
const idVal = document.getElementById("streamProfileId").value;
|
|
const url = idVal ? `/api/core/streamprofiles/${idVal}/` : "/api/core/streamprofiles/";
|
|
const method = idVal ? "PUT" : "POST";
|
|
|
|
const formData = new FormData(this);
|
|
fetch(url, {
|
|
method: method,
|
|
body: formData,
|
|
headers: { "X-CSRFToken": csrftoken }
|
|
})
|
|
.then(resp => {
|
|
if(!resp.ok) throw new Error("Failed to save Stream Profile");
|
|
return resp.json();
|
|
})
|
|
.then(() => {
|
|
Swal.fire("Success", "Stream Profile saved.", "success");
|
|
streamProfilesTable.ajax.reload();
|
|
bootstrap.Modal.getInstance(document.getElementById("streamProfileModal")).hide();
|
|
})
|
|
.catch(err => {
|
|
Swal.fire("Error", err.message, "error");
|
|
});
|
|
});
|
|
|
|
/* EDIT / DELETE STREAM PROFILE */
|
|
$("#streamProfilesTable").on("click", ".edit-stream-profile", function(){
|
|
const rowData = streamProfilesTable.row($(this).closest("tr")).data();
|
|
document.getElementById("streamProfileModalLabel").textContent = "Edit Stream Profile";
|
|
document.getElementById("streamProfileId").value = rowData.id;
|
|
document.getElementById("profileName").value = rowData.profile_name;
|
|
document.getElementById("command").value = rowData.command;
|
|
document.getElementById("parameters").value = rowData.parameters;
|
|
document.getElementById("profileActive").value = rowData.is_active ? "true" : "false";
|
|
document.getElementById("profileUserAgent").value = rowData.user_agent || "";
|
|
new bootstrap.Modal(document.getElementById("streamProfileModal")).show();
|
|
});
|
|
$("#streamProfilesTable").on("click", ".delete-stream-profile", function(){
|
|
const rowData = streamProfilesTable.row($(this).closest("tr")).data();
|
|
Swal.fire({
|
|
title: "Delete this Stream Profile?",
|
|
text: `Are you sure you want to delete "${rowData.profile_name}"?`,
|
|
icon: "warning",
|
|
showCancelButton: true,
|
|
confirmButtonText: "Yes, delete it!"
|
|
}).then(result => {
|
|
if(result.isConfirmed) {
|
|
fetch(`/api/core/streamprofiles/${rowData.id}/`, {
|
|
method: "DELETE",
|
|
headers: { "X-CSRFToken": csrftoken }
|
|
})
|
|
.then(resp => {
|
|
if(!resp.ok) throw new Error("Failed to delete profile");
|
|
Swal.fire("Deleted!", "Stream Profile was removed.", "success");
|
|
streamProfilesTable.ajax.reload();
|
|
})
|
|
.catch(err => Swal.fire("Error", err.message, "error"));
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
/* USER AGENTS TABLE */
|
|
const userAgentsTable = new DataTable("#userAgentsTable", {
|
|
ajax: { url: "/api/core/useragents/", dataSrc: "" },
|
|
responsive: true,
|
|
columns: [
|
|
{ data: "id" },
|
|
{ data: "user_agent_name" },
|
|
{ data: "user_agent" },
|
|
{ data: "description" },
|
|
{
|
|
data: "is_active",
|
|
render: data => (data ? "Yes" : "No")
|
|
},
|
|
{
|
|
data: null,
|
|
render: row => `
|
|
<button class="btn btn-sm btn-primary edit-user-agent" data-id="${row.id}">Edit</button>
|
|
<button class="btn btn-sm btn-danger delete-user-agent" data-id="${row.id}">Delete</button>
|
|
`
|
|
}
|
|
]
|
|
});
|
|
|
|
/* ADD USER AGENT */
|
|
document.getElementById("addUserAgentBtn").addEventListener("click", () => {
|
|
document.getElementById("userAgentForm").reset();
|
|
document.getElementById("userAgentModalLabel").textContent = "Add User Agent";
|
|
document.getElementById("userAgentId").value = "";
|
|
new bootstrap.Modal(document.getElementById("userAgentModal")).show();
|
|
});
|
|
|
|
/* SAVE USER AGENT */
|
|
document.getElementById("userAgentForm").addEventListener("submit", function(e){
|
|
e.preventDefault();
|
|
const idVal = document.getElementById("userAgentId").value;
|
|
const url = idVal ? `/api/core/useragents/${idVal}/` : "/api/core/useragents/";
|
|
const method = idVal ? "PUT" : "POST";
|
|
|
|
const formData = new FormData(this);
|
|
fetch(url, {
|
|
method,
|
|
body: formData,
|
|
headers: { "X-CSRFToken": csrftoken }
|
|
})
|
|
.then(resp => {
|
|
if(!resp.ok) throw new Error("Failed to save user agent");
|
|
return resp.json();
|
|
})
|
|
.then(() => {
|
|
Swal.fire("Success", "User Agent saved.", "success");
|
|
userAgentsTable.ajax.reload();
|
|
bootstrap.Modal.getInstance(document.getElementById("userAgentModal")).hide();
|
|
})
|
|
.catch(err => Swal.fire("Error", err.message, "error"));
|
|
});
|
|
|
|
/* EDIT / DELETE USER AGENT */
|
|
$("#userAgentsTable").on("click", ".edit-user-agent", function(){
|
|
const rowData = userAgentsTable.row($(this).closest("tr")).data();
|
|
document.getElementById("userAgentModalLabel").textContent = "Edit User Agent";
|
|
document.getElementById("userAgentId").value = rowData.id;
|
|
document.getElementById("userAgentNameField").value = rowData.user_agent_name || "";
|
|
document.getElementById("userAgentStringField").value = rowData.user_agent || "";
|
|
document.getElementById("userAgentDescription").value = rowData.description || "";
|
|
document.getElementById("userAgentActive").value = rowData.is_active ? "true" : "false";
|
|
new bootstrap.Modal(document.getElementById("userAgentModal")).show();
|
|
});
|
|
$("#userAgentsTable").on("click", ".delete-user-agent", function(){
|
|
const rowData = userAgentsTable.row($(this).closest("tr")).data();
|
|
Swal.fire({
|
|
title: "Delete this User Agent?",
|
|
text: `Are you sure you want to delete "${rowData.user_agent_name}"?`,
|
|
icon: "warning",
|
|
showCancelButton: true,
|
|
confirmButtonText: "Yes, delete it!"
|
|
}).then(result => {
|
|
if(result.isConfirmed) {
|
|
fetch(`/api/core/useragents/${rowData.id}/`, {
|
|
method: "DELETE",
|
|
headers: { "X-CSRFToken": csrftoken }
|
|
})
|
|
.then(resp => {
|
|
if(!resp.ok) throw new Error("Failed to delete user agent");
|
|
Swal.fire("Deleted!", "User Agent was removed.", "success");
|
|
userAgentsTable.ajax.reload();
|
|
})
|
|
.catch(err => Swal.fire("Error", err.message, "error"));
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
/* LOGO CACHING SETTINGS */
|
|
document.getElementById("logoCachingForm").addEventListener("submit", function(e){
|
|
e.preventDefault();
|
|
const formData = new FormData(this);
|
|
fetch("/api/core/settings/1/", {
|
|
method: "PUT",
|
|
body: formData,
|
|
headers: { "X-CSRFToken": csrftoken }
|
|
})
|
|
.then(response => {
|
|
if(response.ok) Swal.fire("Success", "Logo caching settings saved.", "success");
|
|
else Swal.fire("Error", "Failed to save logo caching settings.", "error");
|
|
});
|
|
});
|
|
|
|
/* BACKUP / RESTORE */
|
|
document.getElementById("createBackupBtn").addEventListener("click", function(){
|
|
fetch("/api/channels/backup/", {
|
|
method: "POST",
|
|
headers: { "X-CSRFToken": csrftoken }
|
|
})
|
|
.then(resp => {
|
|
if(resp.ok) Swal.fire("Success", "Backup created.", "success");
|
|
else Swal.fire("Error", "Backup failed.", "error");
|
|
});
|
|
});
|
|
|
|
document.getElementById("restoreBackupBtn").addEventListener("click", function(){
|
|
fetch("/api/channels/restore/", {
|
|
method: "POST",
|
|
headers: { "X-CSRFToken": csrftoken }
|
|
})
|
|
.then(resp => {
|
|
if(resp.ok) Swal.fire("Success", "Restore complete.", "success");
|
|
else Swal.fire("Error", "Restore failed.", "error");
|
|
});
|
|
});
|
|
|
|
/* HDHR Emulation */
|
|
document.getElementById("hdhrEmulationForm").addEventListener("submit", function(e){
|
|
e.preventDefault();
|
|
const formData = new FormData(this);
|
|
fetch("/api/core/settings/1/", {
|
|
method: "PUT",
|
|
body: formData,
|
|
headers: { "X-CSRFToken": csrftoken }
|
|
})
|
|
.then(resp => {
|
|
if(resp.ok) Swal.fire("Success", "HDHR settings saved.", "success");
|
|
else Swal.fire("Error", "Failed to save HDHR settings.", "error");
|
|
});
|
|
});
|
|
|
|
/* USERS TABLE */
|
|
const usersTable = new DataTable("#usersTable", {
|
|
ajax: { url: "/api/accounts/users", dataSrc: "" },
|
|
responsive: true,
|
|
columns: [
|
|
{ data: "id" },
|
|
{ data: "username" },
|
|
{ data: "email" },
|
|
{ data: "groups" },
|
|
{
|
|
data: null,
|
|
render: row => `
|
|
<button class="btn btn-sm btn-primary edit-user" data-id="${row.id}">Edit</button>
|
|
<button class="btn btn-sm btn-danger delete-user" data-id="${row.id}">Delete</button>
|
|
`
|
|
}
|
|
]
|
|
});
|
|
|
|
/* ADD USER */
|
|
document.getElementById("addUserBtn").addEventListener("click", () => {
|
|
document.getElementById("userForm").reset();
|
|
document.getElementById("userModalLabel").textContent = "Add User";
|
|
document.getElementById("userId").value = "";
|
|
new bootstrap.Modal(document.getElementById("userModal")).show();
|
|
});
|
|
|
|
/* SAVE USER */
|
|
document.getElementById("userForm").addEventListener("submit", function(e){
|
|
e.preventDefault();
|
|
const idVal = document.getElementById("userId").value;
|
|
const url = idVal ? `/api/accounts/users/${idVal}/` : "/api/accounts/users/";
|
|
const method = idVal ? "PUT" : "POST";
|
|
|
|
const formData = new FormData(this);
|
|
fetch(url, {
|
|
method,
|
|
body: formData,
|
|
headers: { "X-CSRFToken": csrftoken }
|
|
})
|
|
.then(resp => {
|
|
if(!resp.ok) throw new Error("Failed to save user");
|
|
return resp.json();
|
|
})
|
|
.then(() => {
|
|
Swal.fire("Success", "User saved.", "success");
|
|
usersTable.ajax.reload();
|
|
bootstrap.Modal.getInstance(document.getElementById("userModal")).hide();
|
|
})
|
|
.catch(err => Swal.fire("Error", err.message, "error"));
|
|
});
|
|
|
|
/* EDIT / DELETE USER */
|
|
$("#usersTable").on("click", ".edit-user", function(){
|
|
const rowData = usersTable.row($(this).closest("tr")).data();
|
|
document.getElementById("userModalLabel").textContent = "Edit User";
|
|
document.getElementById("userId").value = rowData.id;
|
|
document.getElementById("usernameField").value = rowData.username || "";
|
|
document.getElementById("emailField").value = rowData.email || "";
|
|
new bootstrap.Modal(document.getElementById("userModal")).show();
|
|
});
|
|
$("#usersTable").on("click", ".delete-user", function(){
|
|
const rowData = usersTable.row($(this).closest("tr")).data();
|
|
Swal.fire({
|
|
title: "Delete this user?",
|
|
text: `Are you sure you want to delete "${rowData.username}"?`,
|
|
icon: "warning",
|
|
showCancelButton: true,
|
|
confirmButtonText: "Yes, delete!"
|
|
}).then(res => {
|
|
if(res.isConfirmed){
|
|
fetch(`/api/accounts/users/${rowData.id}/`, {
|
|
method: "DELETE",
|
|
headers: { "X-CSRFToken": csrftoken }
|
|
})
|
|
.then(resp => {
|
|
if(!resp.ok) throw new Error("Failed to delete user");
|
|
Swal.fire("Deleted!", "User was removed.", "success");
|
|
usersTable.ajax.reload();
|
|
})
|
|
.catch(err => Swal.fire("Error", err.message, "error"));
|
|
}
|
|
});
|
|
});
|
|
|
|
/* GROUPS TABLE */
|
|
const groupsTable = new DataTable("#groupsTable", {
|
|
ajax: { url: "/api/accounts/groups/", dataSrc: "" },
|
|
responsive: true,
|
|
columns: [
|
|
{ data: "id" },
|
|
{ data: "name" },
|
|
{
|
|
data: "permissions",
|
|
render: perms => {
|
|
if(!perms || !perms.length) return "";
|
|
return perms.join(", ");
|
|
}
|
|
},
|
|
{
|
|
data: null,
|
|
render: row => `
|
|
<button class="btn btn-sm btn-primary edit-group" data-id="${row.id}">Edit</button>
|
|
<button class="btn btn-sm btn-danger delete-group" data-id="${row.id}">Delete</button>
|
|
`
|
|
}
|
|
]
|
|
});
|
|
|
|
/* ADD GROUP */
|
|
document.getElementById("addGroupBtn").addEventListener("click", () => {
|
|
document.getElementById("groupForm").reset();
|
|
document.getElementById("groupModalLabel").textContent = "Add Group";
|
|
document.getElementById("groupId").value = "";
|
|
new bootstrap.Modal(document.getElementById("groupModal")).show();
|
|
});
|
|
|
|
/* SAVE GROUP */
|
|
document.getElementById("groupForm").addEventListener("submit", function(e){
|
|
e.preventDefault();
|
|
const idVal = document.getElementById("groupId").value;
|
|
const url = idVal ? `/api/accounts/groups/${idVal}/` : "/api/accounts/groups/";
|
|
const method = idVal ? "PUT" : "POST";
|
|
|
|
const formData = new FormData(this);
|
|
fetch(url, {
|
|
method,
|
|
body: formData,
|
|
headers: { "X-CSRFToken": csrftoken }
|
|
})
|
|
.then(resp => {
|
|
if(!resp.ok) throw new Error("Failed to save group");
|
|
return resp.json();
|
|
})
|
|
.then(() => {
|
|
Swal.fire("Success", "Group saved.", "success");
|
|
groupsTable.ajax.reload();
|
|
bootstrap.Modal.getInstance(document.getElementById("groupModal")).hide();
|
|
})
|
|
.catch(err => Swal.fire("Error", err.message, "error"));
|
|
});
|
|
|
|
/* EDIT / DELETE GROUP */
|
|
$("#groupsTable").on("click", ".edit-group", function(){
|
|
const rowData = groupsTable.row($(this).closest("tr")).data();
|
|
document.getElementById("groupModalLabel").textContent = "Edit Group";
|
|
document.getElementById("groupId").value = rowData.id;
|
|
document.getElementById("groupNameField").value = rowData.name || "";
|
|
new bootstrap.Modal(document.getElementById("groupModal")).show();
|
|
});
|
|
$("#groupsTable").on("click", ".delete-group", function(){
|
|
const rowData = groupsTable.row($(this).closest("tr")).data();
|
|
Swal.fire({
|
|
title: "Delete this group?",
|
|
text: `Are you sure you want to delete "${rowData.name}"?`,
|
|
icon: "warning",
|
|
showCancelButton: true,
|
|
confirmButtonText: "Yes, delete!"
|
|
}).then(res => {
|
|
if(res.isConfirmed){
|
|
fetch(`/api/accounts/groups/${rowData.id}/`, {
|
|
method: "DELETE",
|
|
headers: { "X-CSRFToken": csrftoken }
|
|
})
|
|
.then(resp => {
|
|
if(!resp.ok) throw new Error("Failed to delete group");
|
|
Swal.fire("Deleted!", "Group was removed.", "success");
|
|
groupsTable.ajax.reload();
|
|
})
|
|
.catch(err => Swal.fire("Error", err.message, "error"));
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|