new drastic

main
Satur@it-depot.ru 2025-03-06 14:59:31 +03:00
parent 564f056b05
commit ae6dcee956
2 changed files with 34 additions and 40 deletions

22
app.py
View File

@ -16,7 +16,7 @@ api_app = FastAPI(title="API Endpoints") # Для API (порт 8002)
conn = sqlite3.connect("/db/rustdesk.db", check_same_thread=False) conn = sqlite3.connect("/db/rustdesk.db", check_same_thread=False)
cursor = conn.cursor() cursor = conn.cursor()
# Проверяем и обновляем структуру таблицы installs (как в исходном коде) # Проверяем и обновляем структуру таблицы installs
cursor.execute("PRAGMA table_info(installs)") cursor.execute("PRAGMA table_info(installs)")
columns = [row[1] for row in cursor.fetchall()] columns = [row[1] for row in cursor.fetchall()]
if 'protocol' not in columns: if 'protocol' not in columns:
@ -26,7 +26,7 @@ if 'note' not in columns:
cursor.execute("ALTER TABLE installs ADD COLUMN note TEXT DEFAULT ''") cursor.execute("ALTER TABLE installs ADD COLUMN note TEXT DEFAULT ''")
conn.commit() conn.commit()
# Создаем таблицы (как в исходном коде) # Создаем таблицы
cursor.execute(""" cursor.execute("""
CREATE TABLE IF NOT EXISTS folders ( CREATE TABLE IF NOT EXISTS folders (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -49,7 +49,7 @@ CREATE TABLE IF NOT EXISTS installs (
""") """)
conn.commit() conn.commit()
# Проверяем/создаем папку "Несортированные" и получаем её ID (как в исходном коде) # Проверяем/создаем папку "Несортированные" и получаем её ID
cursor.execute("SELECT id FROM folders WHERE name = 'Несортированные'") cursor.execute("SELECT id FROM folders WHERE name = 'Несортированные'")
unsorted_folder = cursor.fetchone() unsorted_folder = cursor.fetchone()
if not unsorted_folder: if not unsorted_folder:
@ -59,7 +59,7 @@ if not unsorted_folder:
else: else:
unsorted_folder_id = unsorted_folder[0] unsorted_folder_id = unsorted_folder[0]
# Модели данных (как в исходном коде) # Модели данных
class Folder(BaseModel): class Folder(BaseModel):
name: str name: str
parent_id: int | None = None parent_id: int | None = None
@ -75,7 +75,7 @@ class InstallData(BaseModel):
protocol: str | None = 'rustdesk' protocol: str | None = 'rustdesk'
note: str | None = '' note: str | None = ''
# Функция форматирования времени (как в исходном коде) # Функция форматирования времени
def format_time(time_str): def format_time(time_str):
if not time_str: if not time_str:
return None return None
@ -89,16 +89,16 @@ def format_time(time_str):
except ValueError: except ValueError:
return time_str return time_str
# Монтируем папки templates и icons как статические файлы для веб-интерфейса # Монтируем папки templates и icons
web_app.mount("/templates", StaticFiles(directory="templates"), name="templates") web_app.mount("/templates", StaticFiles(directory="templates"), name="templates")
web_app.mount("/icons", StaticFiles(directory="templates/icons"), name="icons") web_app.mount("/icons", StaticFiles(directory="templates/icons"), name="icons")
# Веб-интерфейс (только GET-запросы и статические файлы) # Веб-интерфейс
@web_app.get("/") @web_app.get("/")
async def root(): async def root():
return FileResponse("templates/index.html") return FileResponse("templates/index.html")
# API-эндпоинты (POST, PUT, DELETE и т.д.) — только для порта 8002 # API-эндпоинты
@api_app.get("/api/folders") @api_app.get("/api/folders")
def get_folders(): def get_folders():
cursor.execute("SELECT * FROM folders") cursor.execute("SELECT * FROM folders")
@ -168,7 +168,7 @@ def update_install(install_id: int, data: InstallData):
new_computer_name = data.computer_name if data.computer_name is not None else current[1] new_computer_name = data.computer_name if data.computer_name is not None else current[1]
new_install_time = data.install_time if data.install_time is not None else current[2] new_install_time = data.install_time if data.install_time is not None else current[2]
new_folder_id = data.folder_id if data.folder_id is not None else current[3] new_folder_id = data.folder_id if data.folder_id is not None else current[3]
# Используем текущее значение protocol, если data.protocol не передано (None) # Сохраняем текущее значение protocol, если новое не передано
new_protocol = data.protocol if data.protocol is not None else current[4] new_protocol = data.protocol if data.protocol is not None else current[4]
new_note = data.note if data.note is not None else current[5] new_note = data.note if data.note is not None else current[5]
@ -275,7 +275,7 @@ async def import_csv(file: UploadFile = File(...), folder_id: int | None = Query
except Exception as e: except Exception as e:
raise HTTPException(status_code=400, detail=f"Ошибка импорта: {str(e)}") raise HTTPException(status_code=400, detail=f"Ошибка импорта: {str(e)}")
# CORS для API (можно настроить для конкретных источников) # CORS для API
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
api_app.add_middleware( api_app.add_middleware(
CORSMiddleware, CORSMiddleware,
@ -285,7 +285,7 @@ api_app.add_middleware(
allow_headers=["*"], allow_headers=["*"],
) )
# 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=["http://10.0.0.10:8001", "http://localhost:8001"],

View File

@ -5,7 +5,7 @@
<title>Органайзер АйТи-Депо</title> <title>Органайзер АйТи-Депо</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.12/jstree.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.12/jstree.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <!-- Библиотека для Markdown --> <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 { body {
@ -36,11 +36,11 @@
margin: 2px 0; margin: 2px 0;
background: #fff; background: #fff;
cursor: move; cursor: move;
color: #000; /* Цвет текста в светлой теме */ color: #000;
} }
.install-item.dark-theme { .install-item.dark-theme {
background: #2a2a2a; background: #2a2a2a;
color: #e0e0e0; /* Цвет текста в тёмной теме */ color: #e0e0e0;
} }
.install-item.selected { background-color: #e6f7ff; } .install-item.selected { background-color: #e6f7ff; }
.install-item.selected.dark-theme { background-color: #003a6d; } .install-item.selected.dark-theme { background-color: #003a6d; }
@ -110,11 +110,11 @@
border-collapse: collapse; border-collapse: collapse;
background: #f0f0f0; background: #f0f0f0;
cursor: pointer; cursor: pointer;
color: #000; /* Цвет текста в светлой теме */ color: #000;
} }
.header.dark-theme { .header.dark-theme {
background: #333; background: #333;
color: #e0e0e0; /* Цвет текста в тёмной теме */ color: #e0e0e0;
} }
.header > div { .header > div {
display: table-cell; display: table-cell;
@ -124,11 +124,11 @@
} }
.header > div:hover { .header > div:hover {
background: #e0e0e0; background: #e0e0e0;
color: #000; /* Тёмный текст на светлом фоне в светлой теме */ color: #000;
} }
.header.dark-theme > div:hover { .header.dark-theme > div:hover {
background: #444; background: #444;
color: #e0e0e0; /* Светлый текст на тёмном фоне в темной теме */ color: #e0e0e0;
} }
.sort-arrow { font-size: 12px; margin-left: 5px; } .sort-arrow { font-size: 12px; margin-left: 5px; }
.protocol-icon { .protocol-icon {
@ -300,7 +300,7 @@
</div> </div>
<div id="installs-container"> <div id="installs-container">
<h1 class="header-logo"> <h1 class="header-logo">
<img src="/icons/it-depo-logo.png" alt="АйТи-Депо логотип"> <!-- Логотип --> <img src="/icons/it-depo-logo.png" alt="АйТи-Депо логотип">
Органайзер АйТи-Депо Органайзер АйТи-Депо
<button class="theme-toggle" onclick="toggleTheme()">Темная тема</button> <button class="theme-toggle" onclick="toggleTheme()">Темная тема</button>
</h1> </h1>
@ -345,7 +345,6 @@
<div id="notes-content"></div> <div id="notes-content"></div>
</div> </div>
<!-- Модальное окно для редактирования заметок -->
<div id="modal-overlay"></div> <div id="modal-overlay"></div>
<div id="note-modal"> <div id="note-modal">
<h3>Редактировать заметку</h3> <h3>Редактировать заметку</h3>
@ -360,10 +359,10 @@
</div> </div>
<script> <script>
const API_URL = "/api"; // Замени <server-ip> на IP или домен сервера const API_URL = "/api";
let selectedFolderId = null; let selectedFolderId = null;
let allInstalls = []; let allInstalls = [];
let allFolders = []; // Добавляем для отслеживания папок let allFolders = [];
let selectedInstallId = null; let selectedInstallId = null;
let sortField = null; let sortField = null;
let sortDirection = 'asc'; let sortDirection = 'asc';
@ -386,7 +385,6 @@
'rdp': 'rdp://' 'rdp': 'rdp://'
}; };
// Проверка и применение сохранённой темы
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const savedTheme = localStorage.getItem('theme'); const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') { if (savedTheme === 'dark') {
@ -399,10 +397,8 @@
document.getElementById('import-label').classList.add('dark-theme'); document.getElementById('import-label').classList.add('dark-theme');
document.querySelector('.theme-toggle').classList.add('dark-theme'); document.querySelector('.theme-toggle').classList.add('dark-theme');
document.querySelector('.theme-toggle').textContent = 'Светлая тема'; document.querySelector('.theme-toggle').textContent = 'Светлая тема';
// Применяем темную тему к уже загруженным элементам таблицы
document.querySelectorAll('.install-item').forEach(item => item.classList.add('dark-theme')); document.querySelectorAll('.install-item').forEach(item => item.classList.add('dark-theme'));
document.querySelector('.header').classList.add('dark-theme'); document.querySelector('.header').classList.add('dark-theme');
// Добавляем класс для темной темы в jstree
$('#folder-tree').addClass('jstree-default-dark'); $('#folder-tree').addClass('jstree-default-dark');
} }
}); });
@ -452,18 +448,15 @@
} }
$(document).ready(function () { $(document).ready(function () {
// Загружаем все папки и записи при инициализации
loadFolders(); loadFolders();
loadInstalls(null); loadInstalls(null);
// Инициализация дерева папок с настройками для темной темы
$('#folder-tree').jstree({ $('#folder-tree').jstree({
'core': { 'core': {
'data': function (node, cb) { 'data': function (node, cb) {
$.getJSON(`${API_URL}/folders`, function (data) { $.getJSON(`${API_URL}/folders`, function (data) {
console.log("Loaded folders:", data); console.log("Loaded folders:", data);
allFolders = data; // Сохраняем все папки allFolders = data;
// Сортируем папки по имени
const sortedFolders = [...data].sort((a, b) => a.name.localeCompare(b.name)); const sortedFolders = [...data].sort((a, b) => a.name.localeCompare(b.name));
const treeData = [ const treeData = [
{ id: "root", text: "Корень", parent: "#" } { id: "root", text: "Корень", parent: "#" }
@ -476,7 +469,6 @@
}))); })));
cb(treeData); cb(treeData);
// Автоматически выбираем папку "Несортированные" после загрузки дерева
const unsortedFolder = sortedFolders.find(f => f.name === 'Несортированные'); const unsortedFolder = sortedFolders.find(f => f.name === 'Несортированные');
if (unsortedFolder) { if (unsortedFolder) {
setTimeout(() => { setTimeout(() => {
@ -491,7 +483,7 @@
}, },
'check_callback': true, 'check_callback': true,
'themes': { 'themes': {
'variant': 'default-dark' // Устанавливаем темную тему по умолчанию для jstree 'variant': 'default-dark'
} }
}, },
'plugins': ['dnd', 'html_data'], 'plugins': ['dnd', 'html_data'],
@ -508,7 +500,6 @@
saveFolderPosition(movedNode.id, newParentId); saveFolderPosition(movedNode.id, newParentId);
}); });
// Обработчики для поиска и выбора папки
$('#search-input').on('input', function () { $('#search-input').on('input', function () {
performSearch(); performSearch();
}); });
@ -517,7 +508,6 @@
performSearch(); performSearch();
}); });
// Drag-and-drop для записей
$('#installs-list').on('dragstart', '.install-item', function (e) { $('#installs-list').on('dragstart', '.install-item', function (e) {
console.log('Drag start:', $(this).data('id')); console.log('Drag start:', $(this).data('id'));
e.originalEvent.dataTransfer.setData('text/plain', $(this).data('id')); e.originalEvent.dataTransfer.setData('text/plain', $(this).data('id'));
@ -542,9 +532,8 @@
function loadFolders() { function loadFolders() {
$.getJSON(`${API_URL}/folders`, function (data) { $.getJSON(`${API_URL}/folders`, function (data) {
// Сортируем папки по имени перед сохранением
allFolders = [...data].sort((a, b) => a.name.localeCompare(b.name)); allFolders = [...data].sort((a, b) => a.name.localeCompare(b.name));
updateFolderSelect(); // Обновляем выпадающий список updateFolderSelect();
}).fail(function(jqxhr, textStatus, error) { }).fail(function(jqxhr, textStatus, error) {
console.error("Error loading folders:", textStatus, error); console.error("Error loading folders:", textStatus, error);
}); });
@ -618,7 +607,7 @@
function sortInstalls(field, filteredInstalls = null) { function sortInstalls(field, filteredInstalls = null) {
let installs = filteredInstalls || (selectedFolderId let installs = filteredInstalls || (selectedFolderId
? allInstalls.filter(i => i.folder_id == folderId && i.folder_id !== null) ? allInstalls.filter(i => i.folder_id == selectedFolderId && i.folder_id !== null)
: allInstalls); : allInstalls);
if (field) { if (field) {
@ -755,10 +744,9 @@
if (field === 'install_time') { if (field === 'install_time') {
data['folder_id'] = selectedFolderId; data['folder_id'] = selectedFolderId;
} }
// Добавляем текущее значение protocol
const install = allInstalls.find(i => i.id === installId); const install = allInstalls.find(i => i.id === installId);
if (install) { if (install) {
data['protocol'] = install.protocol; data['protocol'] = install.protocol; // Сохраняем текущий protocol
} }
$.ajax({ $.ajax({
url: `${API_URL}/install/${installId}`, url: `${API_URL}/install/${installId}`,
@ -793,12 +781,18 @@
function moveInstallToFolder(installId, folderId) { function moveInstallToFolder(installId, folderId) {
console.log('Moving installId:', installId, 'to folderId:', folderId); console.log('Moving installId:', installId, 'to folderId:', folderId);
const install = allInstalls.find(i => i.id === parseInt(installId));
if (!install) {
console.error('Install not found:', installId);
return;
}
$.ajax({ $.ajax({
url: `${API_URL}/install/${installId}`, url: `${API_URL}/install/${installId}`,
type: 'PUT', type: 'PUT',
contentType: 'application/json', contentType: 'application/json',
data: JSON.stringify({ data: JSON.stringify({
folder_id: folderId folder_id: folderId,
protocol: install.protocol // Добавляем текущее значение protocol
}), }),
success: function () { success: function () {
console.log('Move successful'); console.log('Move successful');
@ -863,7 +857,7 @@
contentType: 'application/json', contentType: 'application/json',
data: JSON.stringify({ data: JSON.stringify({
note: note, note: note,
protocol: install.protocol protocol: install.protocol // Сохраняем текущий protocol
}), }),
success: function () { success: function () {
loadInstalls(selectedFolderId); loadInstalls(selectedFolderId);