new drastic
parent
564f056b05
commit
ae6dcee956
22
app.py
22
app.py
|
|
@ -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"],
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue