fix bug editor block
This commit is contained in:
@@ -30,6 +30,10 @@
|
|||||||
- [x] 5.5 Удалить react-scripts
|
- [x] 5.5 Удалить react-scripts
|
||||||
|
|
||||||
## Этап 6: Проверка
|
## Этап 6: Проверка
|
||||||
- [ ] 6.1 npm install для backend и frontend
|
- [x] 6.1 npm install для backend и frontend
|
||||||
- [ ] 6.2 npm run build для frontend
|
- [x] 6.2 npm run build для frontend (Vite 8, 503ms)
|
||||||
- [ ] 6.3 Запуск backend
|
- [x] 6.3 Запуск backend
|
||||||
|
|
||||||
|
## Этап 7: Синхронизация версий Vite (после реструктуризации)
|
||||||
|
- [x] 7.1 frontend обновлён до Vite 8.2.1 + plugin-react 6.0.5 (совпадение с admin-frontend)
|
||||||
|
- [x] 7.2 Сборка frontend подтверждена (621 kB, gzip 187 kB, 503ms)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="stylesheet" href="https://cdn.quilljs.com/1.3.6/quill.snow.css" />
|
||||||
<title>Admin Frontend</title>
|
<title>Admin Frontend</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
|
import 'quill/dist/quill.snow.css';
|
||||||
import '../App.css';
|
import '../App.css';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ function Editor() {
|
|||||||
const [editContent, setEditContent] = useState('');
|
const [editContent, setEditContent] = useState('');
|
||||||
const [editPreviewContent, setEditPreviewContent] = useState('');
|
const [editPreviewContent, setEditPreviewContent] = useState('');
|
||||||
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
||||||
const [csrfToken, setCsrfToken] = useState('');
|
//const [csrfToken, setCsrfToken] = useState('');
|
||||||
const [notify, setNotify] = useState({ message: '', type: '', show: false });
|
const [notify, setNotify] = useState({ message: '', type: '', show: false });
|
||||||
|
|
||||||
const editorRef = useRef(null);
|
const editorRef = useRef(null);
|
||||||
@@ -30,14 +31,14 @@ function Editor() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Fetch CSRF token
|
// Fetch CSRF token
|
||||||
useEffect(() => {
|
//useEffect(() => {
|
||||||
fetch('/moders/csrf-token')
|
// fetch('/moders/csrf-token')
|
||||||
.then(res => res.json())
|
// .then(res => res.json())
|
||||||
.then(data => {
|
// .then(data => {
|
||||||
setCsrfToken(data.csrfToken);
|
// setCsrfToken(data.csrfToken);
|
||||||
})
|
//})
|
||||||
.catch(() => {});
|
//.catch(() => {});
|
||||||
}, []);
|
//}, []);
|
||||||
|
|
||||||
// Check session on mount
|
// Check session on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -54,12 +55,8 @@ function Editor() {
|
|||||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
const authenticatedFetch = useCallback((url, options = {}) => {
|
const authenticatedFetch = useCallback((url, options = {}) => {
|
||||||
if (!options.headers) options.headers = {};
|
|
||||||
if (options.method && ['POST', 'PUT', 'DELETE'].includes(options.method.toUpperCase())) {
|
|
||||||
options.headers['x-csrf-token'] = csrfToken;
|
|
||||||
}
|
|
||||||
return fetch(url, options);
|
return fetch(url, options);
|
||||||
}, [csrfToken]);
|
}, []);
|
||||||
|
|
||||||
const loadBlocks = useCallback(async (tab) => {
|
const loadBlocks = useCallback(async (tab) => {
|
||||||
try {
|
try {
|
||||||
@@ -144,25 +141,34 @@ function Editor() {
|
|||||||
const content = DOMPurify.sanitize(editContent.trim());
|
const content = DOMPurify.sanitize(editContent.trim());
|
||||||
if (!content || content === '<p><br></p>') return;
|
if (!content || content === '<p><br></p>') return;
|
||||||
|
|
||||||
|
const url = editingBlockId
|
||||||
|
? `/moders/content/${activeTab}/${editingBlockId}`
|
||||||
|
: `/moders/content/${activeTab}`;
|
||||||
|
|
||||||
|
const method = editingBlockId ? 'PUT' : 'POST';
|
||||||
|
|
||||||
|
console.log('➡️ Отправка:', method, url, { type: 'text', content });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (editingBlockId) {
|
const response = await fetch(url, {
|
||||||
await authenticatedFetch(`/moders/content/${activeTab}/${editingBlockId}`, {
|
method,
|
||||||
method: 'PUT',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ type: 'text', content })
|
body: JSON.stringify({ type: 'text', content })
|
||||||
});
|
});
|
||||||
showNotify('Блок обновлён', 'success');
|
|
||||||
} else {
|
const data = await response.json();
|
||||||
await authenticatedFetch(`/moders/content/${activeTab}`, {
|
|
||||||
method: 'POST',
|
console.log('⬅️ Ответ сервера:', response.status, data);
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ type: 'text', content })
|
if (response.ok) {
|
||||||
});
|
showNotify(editingBlockId ? 'Блок обновлён' : 'Блок добавлен', 'success');
|
||||||
showNotify('Блок добавлен', 'success');
|
|
||||||
}
|
|
||||||
setShowEditModal(false);
|
setShowEditModal(false);
|
||||||
loadBlocks(activeTab);
|
loadBlocks(activeTab);
|
||||||
|
} else {
|
||||||
|
showNotify(`Ошибка: ${data.message || response.status}`, 'error');
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.error('❌ Ошибка при сохранении:', e);
|
||||||
showNotify('Ошибка сохранения', 'error');
|
showNotify('Ошибка сохранения', 'error');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ import '../App.css';
|
|||||||
|
|
||||||
// CSRF token management
|
// CSRF token management
|
||||||
let csrfToken = null;
|
let csrfToken = null;
|
||||||
async function fetchCsrfToken() {
|
//async function fetchCsrfToken() {
|
||||||
try {
|
// try {
|
||||||
const res = await fetch('/moders/csrf-token', { credentials: 'include' });
|
// const res = await fetch('/moders/csrf-token', { credentials: 'include' });
|
||||||
if (res.ok) {
|
// if (res.ok) {
|
||||||
const data = await res.json();
|
// const data = await res.json();
|
||||||
csrfToken = data.csrfToken;
|
// csrfToken = data.csrfToken;
|
||||||
}
|
// }
|
||||||
} catch (e) { csrfToken = null; }
|
//} catch (e) { csrfToken = null; }
|
||||||
}
|
//}
|
||||||
|
|
||||||
async function apiFetch(url, options = {}) {
|
async function apiFetch(url, options = {}) {
|
||||||
if (!options.headers) options.headers = {};
|
if (!options.headers) options.headers = {};
|
||||||
|
|||||||
@@ -1588,3 +1588,53 @@
|
|||||||
[2026-08-09T17:57:08.445Z] [INFO] [LOGIN] Результат сравнения пароля: true
|
[2026-08-09T17:57:08.445Z] [INFO] [LOGIN] Результат сравнения пароля: true
|
||||||
[2026-08-09T17:57:08.446Z] [INFO] [LOGIN] Сессия установлена: {"isModerator":true,"userId":4,"username":"vova"}
|
[2026-08-09T17:57:08.446Z] [INFO] [LOGIN] Сессия установлена: {"isModerator":true,"userId":4,"username":"vova"}
|
||||||
[2026-08-09T17:57:08.447Z] [INFO] [LOGIN] Ответ отправлен
|
[2026-08-09T17:57:08.447Z] [INFO] [LOGIN] Ответ отправлен
|
||||||
|
[2026-08-09T19:05:03.523Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:05:03.527Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:05:12.014Z] [INFO] [DB CHECK] Проверка подключения к MySQL: localhost:3306/rasch_content
|
||||||
|
[2026-08-09T19:05:12.048Z] [INFO] [DB CHECK] ✅ Успешное подключение к MySQL
|
||||||
|
[2026-08-09T19:05:12.093Z] [ERROR] [DB CHECK] ❌ Ошибка связи с бэкендом: connect ECONNREFUSED 127.0.0.1:3001; connect ECONNREFUSED ::1:3001
|
||||||
|
[2026-08-09T19:10:18.966Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:10:18.970Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:11:58.219Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:11:58.223Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:12:24.042Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:12:24.045Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:13:05.537Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:13:05.540Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:14:14.837Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:14:14.842Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:19:29.874Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:19:29.880Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:20:26.539Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:20:26.543Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:21:13.450Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:21:13.456Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:24:43.343Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:24:43.347Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:24:47.081Z] [ERROR] Error: ENOENT: no such file or directory, stat 'D:\rasch\admin-frontend\build\index.html'
|
||||||
|
[2026-08-09T19:24:47.308Z] [ERROR] Error: ENOENT: no such file or directory, stat 'D:\rasch\admin-frontend\build\index.html'
|
||||||
|
[2026-08-09T19:25:15.162Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:25:15.167Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:25:51.739Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:25:51.743Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:26:23.916Z] [INFO] [DB CHECK] Проверка подключения к MySQL: localhost:3306/rasch_content
|
||||||
|
[2026-08-09T19:26:23.932Z] [INFO] [DB CHECK] ✅ Успешное подключение к MySQL
|
||||||
|
[2026-08-09T19:26:36.976Z] [INFO] [LOGIN] Попытка входа: vova
|
||||||
|
[2026-08-09T19:26:36.980Z] [INFO] [LOGIN] Проверка подключения к базе...
|
||||||
|
[2026-08-09T19:26:36.991Z] [INFO] [LOGIN] Результат поиска пользователя: найден
|
||||||
|
[2026-08-09T19:26:36.993Z] [INFO] [LOGIN] Сравнение пароля через bcrypt...
|
||||||
|
[2026-08-09T19:26:37.333Z] [INFO] [LOGIN] Результат сравнения пароля: true
|
||||||
|
[2026-08-09T19:26:37.335Z] [INFO] [LOGIN] Сессия установлена: {"isModerator":true,"userId":4,"username":"vova"}
|
||||||
|
[2026-08-09T19:26:37.338Z] [INFO] [LOGIN] Ответ отправлен
|
||||||
|
[2026-08-09T19:29:13.173Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:29:13.178Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:30:13.482Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:30:13.486Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:32:50.665Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:32:50.668Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:34:06.656Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:34:06.660Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:34:39.788Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:34:39.791Z] [INFO] Card logs initialized successfully
|
||||||
|
[2026-08-09T19:36:51.312Z] [INFO] Server running on http://localhost:3005
|
||||||
|
[2026-08-09T19:36:51.318Z] [INFO] Card logs initialized successfully
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ app.use('/api/health', healthRouter);
|
|||||||
app.use('/api/monitoring', monitoringRouter);
|
app.use('/api/monitoring', monitoringRouter);
|
||||||
app.use('/moders', moderAuthRouter);
|
app.use('/moders', moderAuthRouter);
|
||||||
app.use('/moders/content', moderContentRouter);
|
app.use('/moders/content', moderContentRouter);
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
|
||||||
|
next();
|
||||||
|
});
|
||||||
// Initialize card logs
|
// Initialize card logs
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Generated
+586
-1056
File diff suppressed because it is too large
Load Diff
@@ -69,12 +69,12 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^26.2.0",
|
"@types/node": "^26.2.0",
|
||||||
"@vitejs/plugin-react": "^4.7.0",
|
"@vitejs/plugin-react":"^6.0.5",
|
||||||
"concurrently":"^7.5.0",
|
"concurrently":"^7.5.0",
|
||||||
"electron":"^37.0.0",
|
"electron":"^37.0.0",
|
||||||
"electron-builder":"^26.15.3",
|
"electron-builder":"^26.15.3",
|
||||||
"electron-rebuild":"^3.2.9",
|
"electron-rebuild":"^3.2.9",
|
||||||
"vite": "^6.0.0",
|
"vite":"^8.2.1",
|
||||||
"vitest":"^2.1.8",
|
"vitest":"^2.1.8",
|
||||||
"wait-on":"^6.0.1"
|
"wait-on":"^6.0.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
{"root":["./src/app.test.tsx","./src/app.tsx","./src/config.js","./src/global.d.ts","./src/index.tsx","./src/r5usb.js","./src/r5usb_example.js","./src/react-app-env.d.ts","./src/reportwebvitals.ts","./src/setuptests.ts","./src/components/employeeinfo.tsx","./src/components/guestmode.tsx","./src/components/loginwindow.tsx","./src/components/payslipviewer.tsx","./vite.config.js"],"version":"5.9.3"}
|
||||||
Reference in New Issue
Block a user