Online_fix2
parent
0acdb5f1b8
commit
73cb28284d
10
app.py
10
app.py
|
|
@ -119,8 +119,12 @@ async def root():
|
||||||
class ConnectionManager:
|
class ConnectionManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.active_connections: list[WebSocket] = []
|
self.active_connections: list[WebSocket] = []
|
||||||
|
self.max_connections = 100 # Ограничение числа подключений
|
||||||
|
|
||||||
async def connect(self, websocket: WebSocket):
|
async def connect(self, websocket: WebSocket):
|
||||||
|
if len(self.active_connections) >= self.max_connections:
|
||||||
|
await websocket.close(code=1008, reason="Too many connections")
|
||||||
|
return
|
||||||
await websocket.accept()
|
await websocket.accept()
|
||||||
self.active_connections.append(websocket)
|
self.active_connections.append(websocket)
|
||||||
|
|
||||||
|
|
@ -128,7 +132,7 @@ class ConnectionManager:
|
||||||
self.active_connections.remove(websocket)
|
self.active_connections.remove(websocket)
|
||||||
|
|
||||||
async def broadcast(self, message: str):
|
async def broadcast(self, message: str):
|
||||||
for connection in self.active_connections:
|
for connection in self.active_connections[:]: # Копируем список для безопасного удаления
|
||||||
await connection.send_text(message)
|
await connection.send_text(message)
|
||||||
|
|
||||||
manager = ConnectionManager()
|
manager = ConnectionManager()
|
||||||
|
|
@ -359,7 +363,7 @@ async def upload_image(install_id: int = Form(...), file: UploadFile = File(...)
|
||||||
# CORS для API
|
# CORS для API
|
||||||
api_app.add_middleware(
|
api_app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["http://10.0.0.10:8001", "http://localhost:8001"],
|
allow_origins=["https://rd.it-depot.ru"], # Обновлено для HTTPS
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
|
|
@ -368,7 +372,7 @@ api_app.add_middleware(
|
||||||
# CORS для веб-интерфейса
|
# CORS для веб-интерфейса
|
||||||
web_app.add_middleware(
|
web_app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["http://10.0.0.10:8001", "http://localhost:8001"],
|
allow_origins=["https://rd.it-depot.ru"], # Обновлено для HTTPS
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,252 @@
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.12/themes/default/style.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.12/themes/default/style.min.css">
|
||||||
<style>
|
<style>
|
||||||
/* Существующие стили остаются без изменений */
|
body {
|
||||||
.status-online { background-color: green; width: 10px; height: 10px; display: inline-block; border-radius: 50%; margin-left: 5px; }
|
font-family: Arial, sans-serif;
|
||||||
.status-offline { background-color: red; width: 10px; height: 10px; display: inline-block; border-radius: 50%; margin-left: 5px; }
|
margin: 0;
|
||||||
/* Остальные стили остаются без изменений */
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
body.dark-theme {
|
||||||
|
background-color: #222;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
#tree-container, #installs-container, #notes-panel {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
#tree-container {
|
||||||
|
width: 250px;
|
||||||
|
min-width: 200px;
|
||||||
|
border-right: 1px solid #ccc;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
#tree-container.dark-theme {
|
||||||
|
border-right-color: #444;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
#installs-container {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
#installs-container.dark-theme {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
#notes-panel {
|
||||||
|
width: 300px;
|
||||||
|
min-width: 200px;
|
||||||
|
border-left: 1px solid #ccc;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
#notes-panel.dark-theme {
|
||||||
|
border-left-color: #444;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
#splitter {
|
||||||
|
width: 5px;
|
||||||
|
background-color: #ccc;
|
||||||
|
cursor: col-resize;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
#splitter.dark-theme {
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
.header-logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.header-logo img {
|
||||||
|
height: 40px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.theme-toggle, #add-record-button {
|
||||||
|
padding: 5px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.theme-toggle.dark-theme, #add-record-button.dark-theme {
|
||||||
|
background-color: #555;
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid #777;
|
||||||
|
}
|
||||||
|
.export-import-container {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
#search-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
#search-container.dark-theme input, #search-container.dark-theme select {
|
||||||
|
background-color: #444;
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid #666;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 5px;
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.header.dark-theme {
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
.header div {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.sort-arrow {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.install-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 5px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
cursor: pointer;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.install-item.dark-theme {
|
||||||
|
border-bottom-color: #555;
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
.install-item:hover {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
.install-item.dark-theme:hover {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
.install-item.selected {
|
||||||
|
background-color: #d0d0d0;
|
||||||
|
}
|
||||||
|
.install-item.dark-theme.selected {
|
||||||
|
background-color: #666;
|
||||||
|
}
|
||||||
|
.computer-name, .connection-id, .install-time, .actions {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.protocol-icon {
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
.connection-link {
|
||||||
|
color: #007bff;
|
||||||
|
text-decoration: none;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.connection-link.dark-theme {
|
||||||
|
color: #66b0ff;
|
||||||
|
}
|
||||||
|
.action-buttons button {
|
||||||
|
margin: 0 2px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
}
|
||||||
|
.action-buttons.dark-theme button {
|
||||||
|
background-color: #555;
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid #777;
|
||||||
|
}
|
||||||
|
#notes-content {
|
||||||
|
padding: 10px;
|
||||||
|
min-height: 200px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
#notes-content.dark-theme {
|
||||||
|
border-color: #444;
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
#modal-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
#add-modal, #note-modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
z-index: 1001;
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
#add-modal.dark-theme, #note-modal.dark-theme {
|
||||||
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
#add-modal input, #add-modal select, #add-modal textarea,
|
||||||
|
#note-modal textarea {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
#add-modal.dark-theme input, #add-modal.dark-theme select, #add-modal.dark-theme textarea,
|
||||||
|
#note-modal.dark-theme textarea {
|
||||||
|
background-color: #444;
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid #666;
|
||||||
|
}
|
||||||
|
.markdown-buttons, .image-upload {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.time-hint {
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.time-hint.dark-theme {
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
.status-online {
|
||||||
|
background-color: green;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 50%;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.status-offline {
|
||||||
|
background-color: red;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 50%;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.highlight {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
}
|
||||||
|
.highlight.dark-theme {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -157,7 +399,7 @@
|
||||||
|
|
||||||
const containerRect = document.body.getBoundingClientRect();
|
const containerRect = document.body.getBoundingClientRect();
|
||||||
let newWidth = e.clientX - containerRect.left;
|
let newWidth = e.clientX - containerRect.left;
|
||||||
const minWidth = parseInt(getComputedStyle(treeContainer).minWidth, 10);
|
const minWidth = 200;
|
||||||
const maxWidth = containerRect.width * 0.5;
|
const maxWidth = containerRect.width * 0.5;
|
||||||
|
|
||||||
if (newWidth < minWidth) newWidth = minWidth;
|
if (newWidth < minWidth) newWidth = minWidth;
|
||||||
|
|
@ -194,7 +436,6 @@
|
||||||
$(items[currentIndex])[0].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
$(items[currentIndex])[0].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Подключение к WebSocket
|
|
||||||
connectWebSocket();
|
connectWebSocket();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -360,7 +601,8 @@
|
||||||
if (ws) {
|
if (ws) {
|
||||||
ws.close();
|
ws.close();
|
||||||
}
|
}
|
||||||
ws = new WebSocket(`ws://${window.location.host}/ws`);
|
// Используем wss для соответствия HTTPS
|
||||||
|
ws = new WebSocket(`wss://${window.location.host}/ws`);
|
||||||
ws.onmessage = function(event) {
|
ws.onmessage = function(event) {
|
||||||
allInstalls = JSON.parse(event.data);
|
allInstalls = JSON.parse(event.data);
|
||||||
displayInstalls(allInstalls.filter(i => !selectedFolderId || i.folder_id == selectedFolderId));
|
displayInstalls(allInstalls.filter(i => !selectedFolderId || i.folder_id == selectedFolderId));
|
||||||
|
|
@ -438,7 +680,7 @@
|
||||||
return `
|
return `
|
||||||
<div class="install-item ${isDarkTheme ? 'dark-theme' : ''}" data-id="${item.id}" draggable="true">
|
<div class="install-item ${isDarkTheme ? 'dark-theme' : ''}" data-id="${item.id}" draggable="true">
|
||||||
<div class="computer-name">${item.computer_name} <button class="edit-button ${isDarkTheme ? 'dark-theme' : ''}" onclick="editField(${item.id}, 'computer_name', '${item.computer_name}')">✏️</button></div>
|
<div class="computer-name">${item.computer_name} <button class="edit-button ${isDarkTheme ? 'dark-theme' : ''}" onclick="editField(${item.id}, 'computer_name', '${item.computer_name}')">✏️</button></div>
|
||||||
<div class="connection-id"><span class="protocol-icon">${protocolIcons[item.protocol]}</span><a href="${protocolLinks[item.protocol]}${item.rust_id}" class="connection-link ${isDarkTheme ? 'dark-theme' : ''}" target="_blank">${item.rust_id}</a> <span class="${statusClass}"></span> <button class="copy-button ${isDarkTheme ? 'dark-theme' : ''}" onclick="copyToClipboard('${item.rust_id}')">📋</button> <button class="edit-button ${isDarkTheme ? 'dark-theme' : ''}" onclick="editField(${item.id}, 'rust_id', '${item.rust_id}')">✏️</button></div>
|
<div class="connection-id"><span class="protocol-icon">${protocolIcons[item.protocol]}</span><a href="${protocolLinks[item.protocol]}${item.rust_id}" class="connection-link ${isDarkTheme ? 'dark-theme' : ''}" target="_blank">${item.rust_id}</a><span class="${statusClass}"></span> <button class="copy-button ${isDarkTheme ? 'dark-theme' : ''}" onclick="copyToClipboard('${item.rust_id}')">📋</button> <button class="edit-button ${isDarkTheme ? 'dark-theme' : ''}" onclick="editField(${item.id}, 'rust_id', '${item.rust_id}')">✏️</button></div>
|
||||||
<div class="install-time">${item.install_time} <button class="edit-button ${isDarkTheme ? 'dark-theme' : ''}" onclick="editField(${item.id}, 'install_time', '${item.install_time}')">✏️</button></div>
|
<div class="install-time">${item.install_time} <button class="edit-button ${isDarkTheme ? 'dark-theme' : ''}" onclick="editField(${item.id}, 'install_time', '${item.install_time}')">✏️</button></div>
|
||||||
<div class="actions"><span class="action-buttons"><button onclick="deleteInstall(${item.id})">Удалить</button> <button class="note-button">Заметка</button></span></div>
|
<div class="actions"><span class="action-buttons"><button onclick="deleteInstall(${item.id})">Удалить</button> <button class="note-button">Заметка</button></span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -865,7 +1107,7 @@
|
||||||
function formatItalic() {
|
function formatItalic() {
|
||||||
const textarea = $('#note-textarea');
|
const textarea = $('#note-textarea');
|
||||||
const start = textarea[0].selectionStart;
|
const start = textarea[0].selectionStart;
|
||||||
const end = textarea[0].selectionEnd;
|
end = textarea[0].selectionEnd;
|
||||||
const text = textarea.val();
|
const text = textarea.val();
|
||||||
const selected = text.substring(start, end);
|
const selected = text.substring(start, end);
|
||||||
const newText = `*${selected}*`;
|
const newText = `*${selected}*`;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue