Fix JS fetch credentials and CSV export from web UI
This commit is contained in:
@@ -12,7 +12,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (logout) {
|
if (logout) {
|
||||||
logout.addEventListener('click', async (e) => {
|
logout.addEventListener('click', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await fetch('/api/auth/logout', { method: 'POST' });
|
await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' });
|
||||||
window.location.href = '/login';
|
window.location.href = '/login';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ document.getElementById('settings-form').addEventListener('submit', async (e) =>
|
|||||||
const res = await fetch('/api/settings', {
|
const res = await fetch('/api/settings', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
credentials: 'include',
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ document.getElementById('create-user-form').addEventListener('submit', async (e)
|
|||||||
const res = await fetch('/api/auth/users', {
|
const res = await fetch('/api/auth/users', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
credentials: 'include',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
username: document.getElementById('new-username').value,
|
username: document.getElementById('new-username').value,
|
||||||
password: document.getElementById('new-password').value,
|
password: document.getElementById('new-password').value,
|
||||||
@@ -91,6 +92,7 @@ document.querySelectorAll('.change-role').forEach(btn => {
|
|||||||
const res = await fetch(`/api/auth/users/${btn.dataset.id}`, {
|
const res = await fetch(`/api/auth/users/${btn.dataset.id}`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
credentials: 'include',
|
||||||
body: JSON.stringify({role: newRole}),
|
body: JSON.stringify({role: newRole}),
|
||||||
});
|
});
|
||||||
if (res.ok) location.reload();
|
if (res.ok) location.reload();
|
||||||
@@ -105,6 +107,7 @@ document.querySelectorAll('.reset-password').forEach(btn => {
|
|||||||
const res = await fetch(`/api/auth/users/${btn.dataset.id}`, {
|
const res = await fetch(`/api/auth/users/${btn.dataset.id}`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
credentials: 'include',
|
||||||
body: JSON.stringify({password}),
|
body: JSON.stringify({password}),
|
||||||
});
|
});
|
||||||
if (res.ok) alert('Пароль изменён');
|
if (res.ok) alert('Пароль изменён');
|
||||||
@@ -115,7 +118,7 @@ document.querySelectorAll('.reset-password').forEach(btn => {
|
|||||||
document.querySelectorAll('.delete-user').forEach(btn => {
|
document.querySelectorAll('.delete-user').forEach(btn => {
|
||||||
btn.addEventListener('click', async () => {
|
btn.addEventListener('click', async () => {
|
||||||
if (!confirm(`Удалить пользователя ${btn.dataset.username}?`)) return;
|
if (!confirm(`Удалить пользователя ${btn.dataset.username}?`)) return;
|
||||||
const res = await fetch(`/api/auth/users/${btn.dataset.id}`, {method: 'DELETE'});
|
const res = await fetch(`/api/auth/users/${btn.dataset.id}`, {method: 'DELETE', credentials: 'include'});
|
||||||
if (res.ok) location.reload();
|
if (res.ok) location.reload();
|
||||||
else alert('Ошибка удаления');
|
else alert('Ошибка удаления');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h2>Компьютеры</h2>
|
<h2>Компьютеры</h2>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<a href="/api/computers/export/csv?{{ request.query_params }}" class="btn btn-outline-success btn-sm" target="_blank">
|
<button type="button" class="btn btn-outline-success btn-sm" id="export-csv-btn">
|
||||||
<i class="bi bi-download"></i> Экспорт CSV
|
<i class="bi bi-download"></i> Экспорт CSV
|
||||||
</a>
|
</button>
|
||||||
<a href="/notifications" class="btn btn-outline-info btn-sm">
|
<a href="/notifications" class="btn btn-outline-info btn-sm">
|
||||||
<i class="bi bi-bell"></i> Уведомления {% if unread_count %}<span class="badge bg-danger">{{ unread_count }}</span>{% endif %}
|
<i class="bi bi-bell"></i> Уведомления {% if unread_count %}<span class="badge bg-danger">{{ unread_count }}</span>{% endif %}
|
||||||
</a>
|
</a>
|
||||||
@@ -107,18 +107,45 @@ function currentQuery() {
|
|||||||
|
|
||||||
async function deleteComputer(id, hostname) {
|
async function deleteComputer(id, hostname) {
|
||||||
if (!confirm(`Удалить ПК ${hostname}?`)) return;
|
if (!confirm(`Удалить ПК ${hostname}?`)) return;
|
||||||
const res = await fetch(`/api/computers/${id}`, {method: 'DELETE'});
|
const res = await fetch(`/api/computers/${id}`, {method: 'DELETE', credentials: 'include'});
|
||||||
if (res.ok) location.reload();
|
if (res.ok) location.reload();
|
||||||
|
else if (res.status === 401) location.href = '/login';
|
||||||
else alert('Ошибка удаления');
|
else alert('Ошибка удаления');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function exportCsv() {
|
||||||
|
const res = await fetch('/api/computers/export/csv?' + currentQuery(), {credentials: 'include'});
|
||||||
|
if (!res.ok) {
|
||||||
|
if (res.status === 401) location.href = '/login';
|
||||||
|
else alert('Ошибка экспорта');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
const disposition = res.headers.get('content-disposition');
|
||||||
|
let filename = 'computers.csv';
|
||||||
|
if (disposition) {
|
||||||
|
const match = disposition.match(/filename="?([^";]+)"?/);
|
||||||
|
if (match) filename = match[1];
|
||||||
|
}
|
||||||
|
a.download = filename;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('export-csv-btn').addEventListener('click', exportCsv);
|
||||||
|
|
||||||
document.querySelectorAll('.delete-pc').forEach(btn => {
|
document.querySelectorAll('.delete-pc').forEach(btn => {
|
||||||
btn.addEventListener('click', () => deleteComputer(btn.dataset.id, btn.dataset.hostname));
|
btn.addEventListener('click', () => deleteComputer(btn.dataset.id, btn.dataset.hostname));
|
||||||
});
|
});
|
||||||
|
|
||||||
async function loadDashboard() {
|
async function loadDashboard() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/computers?' + currentQuery());
|
const res = await fetch('/api/computers?' + currentQuery(), {credentials: 'include'});
|
||||||
if (!res.ok) return location.reload();
|
if (!res.ok) return location.reload();
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const tbody = document.querySelector('#computers-table tbody');
|
const tbody = document.querySelector('#computers-table tbody');
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ document.getElementById('login-form').addEventListener('submit', async (e) => {
|
|||||||
form.append('username', document.getElementById('username').value);
|
form.append('username', document.getElementById('username').value);
|
||||||
form.append('password', document.getElementById('password').value);
|
form.append('password', document.getElementById('password').value);
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/auth/login', { method: 'POST', body: form });
|
const res = await fetch('/api/auth/login', { method: 'POST', body: form, credentials: 'include' });
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
} else {
|
} else {
|
||||||
@@ -79,6 +79,7 @@ document.getElementById('register-form').addEventListener('submit', async (e) =>
|
|||||||
const res = await fetch('/api/auth/register', {
|
const res = await fetch('/api/auth/register', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
credentials: 'include',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
username: document.getElementById('reg-username').value,
|
username: document.getElementById('reg-username').value,
|
||||||
password: document.getElementById('reg-password').value,
|
password: document.getElementById('reg-password').value,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script>
|
<script>
|
||||||
async function markRead(id) {
|
async function markRead(id) {
|
||||||
const res = await fetch(`/api/notifications/${id}/read`, { method: 'POST' });
|
const res = await fetch(`/api/notifications/${id}/read`, { method: 'POST', credentials: 'include' });
|
||||||
if (res.ok) location.reload();
|
if (res.ok) location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ document.querySelectorAll('.mark-read').forEach(btn => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('mark-all-read')?.addEventListener('click', async () => {
|
document.getElementById('mark-all-read')?.addEventListener('click', async () => {
|
||||||
const res = await fetch('/api/notifications/read-all', { method: 'POST' });
|
const res = await fetch('/api/notifications/read-all', { method: 'POST', credentials: 'include' });
|
||||||
if (res.ok) location.reload();
|
if (res.ok) location.reload();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user