first commit

This commit is contained in:
2026-08-08 00:15:48 +03:00
parent ac2bc13da0
commit d98b4c1343
146 changed files with 122827 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
# Документы
Поместите ваш файл справочника с именем `handbook.docx` в эту папку.
Структура:
- `handbook.docx` - основной справочник
- Другие документы можно добавить по необходимости
+2
View File
@@ -0,0 +1,2 @@
COM_PORT=COM6
COM_SPEED=9600
+201
View File
@@ -0,0 +1,201 @@
const { app, BrowserWindow, protocol, ipcMain, globalShortcut } = require('electron');
const path = require('path');
require('dotenv').config({path: path.join(__dirname, 'config.env')})
const isDev = require('electron-is-dev');
const fs = require('fs');
const COM_PORT = process.env.COM_PORT;
const COM_SPEED = process.env.COM_SPEED;
const { SerialPort } = require('serialport');
const axios = require('axios');
const { logger } = require('../../backend/src/config/logger');
const config = require('../src/config.json')
protocol.registerSchemesAsPrivileged([
{
scheme: 'app',
privileges: {
secure: true,
standard: true,
supportFetchAPI: true,
corsEnabled: true
}
}
]);
if (!Promise.withResolvers) {
Promise.withResolvers = function () {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
}
let mainWindow;
let appConfig = {};
const configPath = isDev
? path.join(__dirname, '../src/config.json')
: path.join(process.resourcesPath, 'config.json');
try {
logger.info(`[Config] Trying to load config from: ${configPath}`);
if (fs.existsSync(configPath)) {
appConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
logger.info('[Config] External config loaded successfully:', appConfig);
} else {
logger.warn('[Config] External config file not found. Using default values.');
}
} catch (error) {
logger.error('[Config] Could not load external config file:', error);
}
global.allowClose = false;
function createWindow() {
const preloadPath = path.join(__dirname, 'preload.js');
try {
fs.accessSync(preloadPath);
logger.debug(`[DEBUG] Preload script found at: ${preloadPath}`);
} catch (e) {
logger.error(`[FATAL] Preload script not found at: ${preloadPath}`);
app.quit();
return;
}
mainWindow = new BrowserWindow({
kiosk: true,
titleBarStyle:'hidden',
alwaysOnTop: false,
webPreferences: {
preload: preloadPath,
contextIsolation: true,
nodeIntegration: false
},
});
mainWindow.loadURL(
isDev
? `${config.frontUrl}`
: `file://${path.join(__dirname, '../build/index.html')}`
);
mainWindow.on('close', (e) => {
if (!global.allowClose) {
e.preventDefault();
}
});
mainWindow.on('minimize', (e) => {
e.preventDefault();
});
}
app.whenReady().then(() => {
createWindow();
global.allowMinimize = false;
globalShortcut.register('Control+Alt+Backspace', () => {
global.allowMinimize = true;
if (mainWindow) mainWindow.minimize();
});
mainWindow.on('minimize', function (event) {
if (global.allowMinimize) {
global.allowMinimize = false;
return;
}
event.preventDefault();
setTimeout(() => {
if (mainWindow.isMinimized()) {
mainWindow.restore();
mainWindow.focus();
}
}, 10);
});
globalShortcut.register('Control+Alt+Enter', () => {
global.allowClose = true;
app.quit();
});
globalShortcut.register('F11', () => {
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});
app.on('will-quit', () => {
globalShortcut.unregisterAll();
});
//считыватель
SerialPort.list().then(ports => {
logger.info('Доступные порты:', ports.map(p => p.path));
}).catch(err => {
logger.error('Ошибка при получении списка портов:', err);
});
try {
const port = new SerialPort({ path: COM_PORT, baudRate: parseInt(COM_SPEED, 10)});
let buffer = '';
port.on('open', () => {
logger.info(' Serial port открыт');
});
port.on('data', (data) => {
logger.info('RAW DATA:', data);
buffer += data.toString('utf8');
if (buffer.includes('\n')) {
const lines = buffer.split('\n');
lines.forEach((line, idx) => {
if (idx < lines.length - 1) {
const uid = line.trim().replace(/\D/g, '');
if (uid) {
console.log(`UID карты: ${uid}`);
console.log('[DEBUG] Отправляю событие на admin-server:', {
event: 'CARD_PLACED',
uid: uid,
timestamp: Date.now()
});
axios.post(`${config.adminUrl}}/api/card-event`, {
event: 'CARD_PLACED',
uid: uid,
timestamp: Date.now()
}).catch(err => {
console.error('[ADMIN][CARD_LOG] Ошибка отправки:', err.message);
});
if (mainWindow && mainWindow.webContents) {
mainWindow.webContents.send('card-detected', uid);
console.log(` Отправка события 'card-detected' в рендерер с UID: ${uid}`);
} else {
console.warn('mainWindow или webContents не определены, событие не отправлено');
}
}
}
});
buffer = lines[lines.length - 1];
}
});
port.on('error', (err) => {
console.error('Ошибка serialport:', err);
});
} catch (err) {
console.error('!!! FATAL: FAILED TO INITIALIZE SERIALPORT !!!', err);
}
ipcMain.handle('get-app-config', async (event) => {
console.log('[IPC] Renderer requested app config. Sending:', appConfig);
return appConfig;
});
app.whenReady().then(() => {
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
+49
View File
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Инфокиоск</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<style>
.ql-align-center { text-align: center; }
.ql-align-right { text-align: right; }
.ql-align-justify { text-align: justify; }
</style>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

+25
View File
@@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
Binary file not shown.
Binary file not shown.
+75390
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+19
View File
@@ -0,0 +1,19 @@
const { logger } = require('../../backend/src/config/logger');
logger.info('[Preload] Script started.');
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld(
'electronAPI',
{
getAppConfig: () => ipcRenderer.invoke('get-app-config'),
onCardDetected: (callback) => {
ipcRenderer.on('card-detected', (event, uid) => callback(uid));
},
removeCardDetectedListeners: () => {
ipcRenderer.removeAllListeners('card-detected');
}
}
);
logger.log('[Preload] Script finished. electronAPI exposed.');
window.dispatchEvent(new Event('electronApiReady'));
+4
View File
@@ -0,0 +1,4 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow: