feat: Удалить логику блокировок и обновить логику загрузки данных в менеджерах ASNs, Domains и IPRanges, улучшив фильтрацию и пагинацию для локального отображения
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m55s

This commit is contained in:
2025-08-27 17:39:04 +07:00
parent 0f3ad56f87
commit cd3f7a84e3
4 changed files with 37 additions and 177 deletions
+1 -84
View File
@@ -1,84 +1 @@
import { useEffect, useState } from 'react';
import api from '../lib/api.js';
function LockBanner({ resource, className = '' }) {
const [status, setStatus] = useState({ locked: false });
const [loading, setLoading] = useState(false);
const owner = (() => {
const existing = localStorage.getItem('uiOwner');
if (existing) return existing;
const gen = `ui-${Math.random().toString(36).slice(2, 8)}`;
localStorage.setItem('uiOwner', gen);
return gen;
})();
const fetchStatus = async () => {
try {
const res = await api.get(`/locks/${resource}`);
setStatus(res.data || { locked: false });
} catch {
// ignore
}
};
useEffect(() => {
fetchStatus();
const id = setInterval(fetchStatus, 30_000);
return () => clearInterval(id);
}, [resource]);
const acquireOrRefresh = async () => {
setLoading(true);
try {
const res = await api.post(`/locks/${resource}`, { owner, ttlSeconds: 180 });
setStatus(res.data || { locked: true, owner, expiresAt: Date.now() + 180_000 });
} catch {
// ignore
} finally {
setLoading(false);
}
};
const release = async () => {
setLoading(true);
try {
await api.delete(`/locks/${resource}`);
setStatus({ locked: false });
} catch {
// ignore
} finally {
setLoading(false);
}
};
const myLock = status.locked && status.owner === owner;
const until = status.expiresAt ? new Date(status.expiresAt).toLocaleTimeString() : null;
return (
<div className={`alert ${status.locked ? (myLock ? 'alert-success' : 'alert-warning') : 'alert-secondary'} d-flex align-items-center justify-content-between ${className}`} role="alert">
<div>
{status.locked ? (
myLock ? (
<>Вы удерживаете блокировку ресурса <code>{resource}</code>{until ? ` до ${until}` : ''}</>
) : (
<>Ресурс <code>{resource}</code> редактирует <strong>{status.owner || 'другой пользователь'}</strong>{until ? ` до ${until}` : ''}</>
)
) : (
<>Ресурс <code>{resource}</code> свободен</>
)}
</div>
<div className="btn-list m-0">
<button className="btn btn-outline-primary btn-sm" onClick={fetchStatus} disabled={loading}>Обновить</button>
<button className="btn btn-primary btn-sm" onClick={acquireOrRefresh} disabled={loading}>{myLock ? 'Продлить' : 'Захватить'}</button>
{myLock && (
<button className="btn btn-outline-danger btn-sm" onClick={release} disabled={loading}>Освободить</button>
)}
</div>
</div>
);
}
export default LockBanner;
export default function LockBanner() { return null }