Files
vue-pult/server/AUTH_LOGOUT_DOCUMENTATION.md
sasha 3e90269b0b Initial commit: Vue.js тир управления система
- Клиентская часть Vue 3 + Vite
- Серверная часть Node.js + WebSocket
- Система авторизации и смен
- Управление игровыми портами
- Поддержка тем (светлая/темная)
- Адаптивный дизайн

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-19 12:24:22 +03:00

467 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Документация по системе авторизации и выхода пользователя
## Обзор системы авторизации
Система использует двухуровневую авторизацию:
1. **Локальная авторизация** - через WebSocket с пультом
2. **Серверная авторизация** - через внешний сервер ws.tirpriz.ru
---
## Процесс авторизации пользователя
### 1. Клиентская часть (Frontend)
#### Инициализация авторизации:
```javascript
// www/start.jsc
case "avt":
// Если авторизация успешна
if (postData.user != false) {
avt = postData.user;
sessionStorage.avt = JSON.stringify(avt);
showPage(page.run, true);
alert.add && alert.add({content: "Добро пожаловать "+(avt.fio ? avt.fio : avt.tel)+"!"});
} else {
showPage(page.avt, false);
alert.add && alert.add({content: "Неверный логин или пароль!"});
}
```
### 2. Серверная часть (Backend)
#### Обработка запроса авторизации:
```javascript
// ws.js:380-410
case "avt":
let user = false;
// Авторизация по паролю
if (postData.pass && postData.tel in avt && avt[postData.tel].pass == postData.pass) {
user = JSON.parse(JSON.stringify(avt[postData.tel]));
delete user.pass;
hash[postData.tel] = genHash(15);
user.hash = hash[postData.tel];
await fs.writeFile('./data/hash.ini', JSON.stringify(hash));
}
// Авторизация по токену (hash)
if (postData.hash && postData.tel in hash && postData.tel in avt && hash[postData.tel] == postData.hash) {
user = JSON.parse(JSON.stringify(avt[postData.tel]));
user.hash = hash[postData.tel];
delete user.pass;
}
wsClient.cfg.avt = user;
if (user != false) {
// Отправляем данные пользователя
wsClient.send(JSON.stringify({
do: "avt",
user,
synch_log: log.synch_log(),
VER,
games: game.getgame(),
info: game.getInfo(),
admins: avt,
pult: game.cfg.pult,
tir: game.cfg.tir,
esp_ping: game.cfg.pingStatus
}));
// Регистрируем вход
if (!(wsClient.cfg.avt._id in game.cfg.avt)) {
log.save({do: "login", adminId: wsClient.cfg.avt._id});
game.cfg.avt[wsClient.cfg.avt._id] = true;
}
}
```
---
## Процесс выхода из системы
### 1. Клиентская часть
#### Инициация выхода:
```javascript
// www/layout/menu/menu.jsc
<div class="logout" onclick="socket.send(JSON.stringify({do:'logout'}));"></div>
// www/page/run/run.jsc
<div onmousedown="socket.send(JSON.stringify({do:'logout'})); DIVconfirm.blur();">
<txt "Да">
</div>
```
#### Обработка ответа выхода:
```javascript
// www/start.jsc:166-170
case "logout":
avt = {};
delete sessionStorage.avt;
location.reload();
```
### 2. Серверная часть
#### Обработка запроса выхода:
```javascript
// ws.js:412-417
case "logout":
wsClient.send(JSON.stringify({do: "logout"}));
log.save({do: "logout", adminId: wsClient.cfg.avt._id});
wsClient.cfg.avt = false;
delete game.cfg.avt[wsClient.cfg.avt._id];
```
---
## Взаимодействие с внешним сервером
### 1. Подключение к серверу
```javascript
// ws-toserver.js:11
global.socket_to_server = new WebSocket("ws://ws.tirpriz.ru/ws");
```
### 2. Авторизация на сервере
```javascript
// ws-toserver.js:23-49
socket_to_server.onopen = async (e) => {
// Собираем информацию об администраторах
let admins = [];
wsServer.clients.forEach(client => {
if (client?.cfg?.avt) admins.push({
adminId: client.cfg.avt.id,
ip: client._socket.remoteAddress
});
});
// Формируем данные авторизации
const authData = {
do: "avt",
tip: "pult",
serialcpu,
admins,
VER,
network: os.networkInterfaces(),
disk,
freemem: os.freemem(),
totalmem: os.totalmem()
};
// Отправляем данные авторизации
socket_to_server.send(JSON.stringify(authData));
};
```
### 3. Синхронизация данных
```javascript
// ws-toserver.js:85-120
case "avt":
conn_to_server = true;
// Проверяем версии данных и обновляем если нужно
if (postData.tir && postData.tir.access.ver != ver.admin) {
let avt = await send_to_server({do: "admin/pult"});
if (avt.do = "admin/pult") {
let avt_file = {};
for (let i = 0; i < avt.admins.length; i++) {
avt_file[avt.admins[i].tel] = avt.admins[i];
}
await fs.writeFile('./data/avt.ini', JSON.stringify(avt_file));
ver.admin = avt.ver;
}
}
```
---
## Система токенов (hash)
### 1. Генерация токена
```javascript
// При успешной авторизации по паролю генерируется токен
hash[postData.tel] = genHash(15);
user.hash = hash[postData.tel];
await fs.writeFile('./data/hash.ini', JSON.stringify(hash));
```
### 2. Валидация токена
```javascript
// Проверка токена при последующих запросах
if (postData.hash && postData.tel in hash && postData.tel in avt && hash[postData.tel] == postData.hash) {
user = JSON.parse(JSON.stringify(avt[postData.tel]));
user.hash = hash[postData.tel];
}
```
---
## Роли и доступы
### 1. Проверка авторизации
```javascript
// ws.js:41
if (!wsClient.cfg.avt && postData.do != 'avt') {
// Блокируем доступ неавторизованным пользователям
return;
}
```
### 2. Типы ролей
- **Техник** - доступ к технической смене
- **Оператор** - доступ к обычной смене
- **Админ** - полный доступ
---
## Миграция на React
### 1. Рекомендуемая архитектура
#### Redux Store для авторизации:
```javascript
// store/authSlice.js
const authSlice = createSlice({
name: 'auth',
initialState: {
user: null,
isAuthenticated: false,
token: null,
loading: false,
error: null
},
reducers: {
loginStart: (state) => {
state.loading = true;
state.error = null;
},
loginSuccess: (state, action) => {
state.user = action.payload.user;
state.token = action.payload.hash;
state.isAuthenticated = true;
state.loading = false;
// Сохраняем в sessionStorage
sessionStorage.setItem('avt', JSON.stringify(action.payload.user));
},
loginFailure: (state, action) => {
state.error = action.payload;
state.loading = false;
},
logout: (state) => {
state.user = null;
state.token = null;
state.isAuthenticated = false;
sessionStorage.removeItem('avt');
},
restoreAuth: (state, action) => {
const savedAuth = sessionStorage.getItem('avt');
if (savedAuth) {
state.user = JSON.parse(savedAuth);
state.isAuthenticated = true;
}
}
}
});
```
#### WebSocket хук для авторизации:
```javascript
// hooks/useAuth.js
export const useAuth = () => {
const dispatch = useDispatch();
const { user, isAuthenticated, loading, error } = useSelector(state => state.auth);
const login = async (tel, password) => {
dispatch(loginStart());
try {
const response = await sendMessage({
do: "avt",
tel,
pass: password
});
if (response.user !== false) {
dispatch(loginSuccess(response));
return { success: true };
} else {
dispatch(loginFailure("Неверный логин или пароль"));
return { success: false, error: "Неверный логин или пароль" };
}
} catch (error) {
dispatch(loginFailure(error.message));
return { success: false, error: error.message };
}
};
const logout = () => {
sendMessage({ do: "logout" });
dispatch(logout());
};
const restoreAuth = () => {
dispatch(restoreAuth());
};
return { user, isAuthenticated, loading, error, login, logout, restoreAuth };
};
```
#### Компонент авторизации:
```javascript
// components/Auth/LoginForm.jsx
const LoginForm = () => {
const [tel, setTel] = useState('');
const [password, setPassword] = useState('');
const { login, loading, error } = useAuth();
const handleSubmit = async (e) => {
e.preventDefault();
const result = await login(tel, password);
if (result.success) {
// Переход на главную страницу
navigate('/dashboard');
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="tel"
value={tel}
onChange={(e) => setTel(e.target.value)}
placeholder="Телефон"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Пароль"
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Вход...' : 'Войти'}
</button>
{error && <div className="error">{error}</div>}
</form>
);
};
```
#### Защищенные маршруты:
```javascript
// components/ProtectedRoute.jsx
const ProtectedRoute = ({ children, requiredRole }) => {
const { isAuthenticated, user } = useAuth();
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
if (requiredRole && !hasAccess(user, requiredRole)) {
return <div>Недостаточно прав доступа</div>;
}
return children;
};
const hasAccess = (user, requiredRole) => {
switch (requiredRole) {
case 'tech':
return user.groupz?.access?.tools === true;
case 'operator':
return user.groupz?.access?.smena === true;
case 'admin':
return user.groupz?.access?.admin === true;
default:
return true;
}
};
```
### 2. WebSocket интеграция
#### WebSocket Context:
```javascript
// context/WebSocketContext.js
const WebSocketContext = createContext();
export const WebSocketProvider = ({ children }) => {
const [socket, setSocket] = useState(null);
const [connected, setConnected] = useState(false);
const dispatch = useDispatch();
useEffect(() => {
const ws = new WebSocket('ws://localhost:9000');
ws.onopen = () => {
setConnected(true);
setSocket(ws);
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.do) {
case 'avt':
if (data.user !== false) {
dispatch(loginSuccess(data));
} else {
dispatch(loginFailure("Авторизация не удалась"));
}
break;
case 'logout':
dispatch(logout());
break;
// ... другие случаи
}
};
ws.onclose = () => {
setConnected(false);
setSocket(null);
setTimeout(() => {
// Переподключение
}, 5000);
};
return () => ws.close();
}, []);
const sendMessage = (message) => {
if (socket && connected) {
socket.send(JSON.stringify(message));
}
};
return (
<WebSocketContext.Provider value={{ socket, connected, sendMessage }}>
{children}
</WebSocketContext.Provider>
);
};
```
---
## Ключевые особенности системы
1. **Двойная авторизация** - локальная и серверная
2. **Токенная система** - hash токены сохраняются в файл
3. **Роли доступа** - техник, оператор, админ
4. **Автоматическая синхронизация** - данные пользователей обновляются с сервера
5. **Логирование** - все входы/выходы записываются в лог
6. **Восстановление сессии** - через sessionStorage
7. **Реконнект** - автоматическое переподключение к серверу
## Важные файлы для миграции
- `ws.js` - основная логика авторизации
- `ws-toserver.js` - взаимодействие с внешним сервером
- `www/start.jsc` - клиентская обработка авторизации
- `data_start/avt.ini` - пользователи системы
- `data_start/hash.ini` - токены сессий