Drones
This commit is contained in:
parent
19096a5d76
commit
6225ef638d
4
.env
4
.env
@ -2,7 +2,9 @@ SERVER_BASE_ADDRESS=0.0.0.0:8080
|
|||||||
|
|
||||||
MONGO_INITDB_ROOT_USERNAME=moxitech
|
MONGO_INITDB_ROOT_USERNAME=moxitech
|
||||||
MONGO_INITDB_ROOT_PASSWORD=moxitech
|
MONGO_INITDB_ROOT_PASSWORD=moxitech
|
||||||
MONGO_INITDB_DATABASE=moxitech
|
MONGO_INITDB_DATABASE=drone-network-simulator
|
||||||
|
MONGO_INITDB_SIM_COLLECTION=simulations
|
||||||
|
|
||||||
|
|
||||||
POSTGRES_DB=moxitech
|
POSTGRES_DB=moxitech
|
||||||
POSTGRES_USER=moxitech
|
POSTGRES_USER=moxitech
|
||||||
|
@ -6,6 +6,9 @@ import Dashboard from './pages/Dashboard';
|
|||||||
import UserAccount from './pages/UserAccount'; // Импортируем компонент UserAccount
|
import UserAccount from './pages/UserAccount'; // Импортируем компонент UserAccount
|
||||||
import Login from './pages/Login'; // Импортируем страницу логина
|
import Login from './pages/Login'; // Импортируем страницу логина
|
||||||
import Connections from './pages/Connections'; // Импортируем страницу подключений
|
import Connections from './pages/Connections'; // Импортируем страницу подключений
|
||||||
|
import PrevCalc from './pages/PrevCalc'; // Импортируем страницу подключений
|
||||||
|
import Docs from './pages/Docs'; // Импортируем страницу подключений
|
||||||
|
import "./css/bulma.min.css"
|
||||||
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
@ -44,6 +47,8 @@ const App = () => {
|
|||||||
{activeTab === 'account' && <UserAccount />} {/* Подключаем компонент UserAccount */}
|
{activeTab === 'account' && <UserAccount />} {/* Подключаем компонент UserAccount */}
|
||||||
{activeTab === 'groups' && <DeviceGroups />} {/* Группы устройств */}
|
{activeTab === 'groups' && <DeviceGroups />} {/* Группы устройств */}
|
||||||
{activeTab === 'devices' && <Devices />} {/* Устройства */}
|
{activeTab === 'devices' && <Devices />} {/* Устройства */}
|
||||||
|
{activeTab === 'prev_calc' && <PrevCalc />} {/* Устройства */}
|
||||||
|
{activeTab === 'docs' && <Docs />} {/* Устройства */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,9 +22,12 @@ const Sidebar = ({ onSelectTab, activeTab, onLogout }) => {
|
|||||||
<li className={activeTab === 'connection' ? 'active' : ''} onClick={() => onSelectTab('connection')}>
|
<li className={activeTab === 'connection' ? 'active' : ''} onClick={() => onSelectTab('connection')}>
|
||||||
Настройки
|
Настройки
|
||||||
</li>
|
</li>
|
||||||
<li className={activeTab === 'connection' ? 'active' : ''} onClick={() => onSelectTab('connection')}>
|
<li className={activeTab === 'prev_calc' ? 'active' : ''} onClick={() => onSelectTab('prev_calc')}>
|
||||||
Проведенные вычисления
|
Проведенные вычисления
|
||||||
</li>
|
</li>
|
||||||
|
<li className={activeTab === 'docs' ? 'active' : ''} onClick={() => onSelectTab('docs')}>
|
||||||
|
Документация
|
||||||
|
</li>
|
||||||
<li className={activeTab === 'account' ? 'active' : ''} onClick={toggleSubMenu}>
|
<li className={activeTab === 'account' ? 'active' : ''} onClick={toggleSubMenu}>
|
||||||
Аккаунт пользователя
|
Аккаунт пользователя
|
||||||
</li>
|
</li>
|
||||||
|
3
src/frontend/src/css/bulma.min.css
vendored
Normal file
3
src/frontend/src/css/bulma.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
41
src/frontend/src/pages/Components/Drone.js
Normal file
41
src/frontend/src/pages/Components/Drone.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Drone.js
|
||||||
|
import * as THREE from 'three';
|
||||||
|
|
||||||
|
export class Drone {
|
||||||
|
constructor() {
|
||||||
|
this.drone = new THREE.Object3D();
|
||||||
|
|
||||||
|
// Создаем основное тело дрона
|
||||||
|
const bodyGeometry = new THREE.BoxGeometry(1, 0.3, 1);
|
||||||
|
const bodyMaterial = new THREE.MeshBasicMaterial({ color: 0x0077ff });
|
||||||
|
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
|
||||||
|
this.drone.add(body);
|
||||||
|
|
||||||
|
// Добавляем четыре пропеллера
|
||||||
|
const propellerGeometry = new THREE.CylinderGeometry(0.1, 0.1, 0.02, 32);
|
||||||
|
const propellerMaterial = new THREE.MeshBasicMaterial({ color: 0x333333 });
|
||||||
|
const positions = [
|
||||||
|
[-0.5, 0.2, -0.5],
|
||||||
|
[-0.5, 0.2, 0.5],
|
||||||
|
[0.5, 0.2, -0.5],
|
||||||
|
[0.5, 0.2, 0.5],
|
||||||
|
];
|
||||||
|
|
||||||
|
positions.forEach((position) => {
|
||||||
|
const propeller = new THREE.Mesh(propellerGeometry, propellerMaterial);
|
||||||
|
propeller.rotation.x = Math.PI / 2;
|
||||||
|
propeller.position.set(...position);
|
||||||
|
this.drone.add(propeller);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Метод для установки позиции дрона
|
||||||
|
setPosition(x, y, z) {
|
||||||
|
this.drone.position.set(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем объект дрона для добавления в сцену
|
||||||
|
getObject() {
|
||||||
|
return this.drone;
|
||||||
|
}
|
||||||
|
}
|
@ -3,56 +3,64 @@ import * as THREE from 'three';
|
|||||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
|
||||||
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader';
|
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader';
|
||||||
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry';
|
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry';
|
||||||
|
import { Drone } from './Components/Drone';
|
||||||
|
import HeightPoint from './Model/HeightPoint';
|
||||||
|
|
||||||
|
|
||||||
|
const RandomHeightPoint = () => {
|
||||||
|
let result = [];
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
for (let j = 0; j < 20; j++) {
|
||||||
|
result.push(new HeightPoint(i, j, Math.random() * 10 + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
const mountRef = useRef(null);
|
const mountRef = useRef(null);
|
||||||
const [heightData, setHeightData] = useState([
|
const sceneRef = useRef(null);
|
||||||
[1, 2, 3, 4, 5],
|
const [heightData, setHeightData] = useState(RandomHeightPoint());
|
||||||
[2, 3, 4, 5, 6],
|
|
||||||
[3, 4, 5, 6, 7],
|
|
||||||
[4, 5, 6, 7, 8],
|
|
||||||
[5, 6, 7, 8, 9],
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [formData, setFormData] = useState({ x: '', y: '', height: '' });
|
const [formData, setFormData] = useState({ x: '', y: '', height: '' });
|
||||||
|
const [drones, setDrones] = useState([])
|
||||||
const [mapSettings, setMapSettings] = useState({
|
const [mapSettings, setMapSettings] = useState({
|
||||||
maxHeight: 10,
|
maxHeight: 20,
|
||||||
coordX: 0,
|
coordX: 0,
|
||||||
coordY: 0,
|
coordY: 0,
|
||||||
});
|
});
|
||||||
|
const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0 });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let localRef = null;
|
if (!mountRef.current) return;
|
||||||
let width = mountRef.current.clientWidth;
|
let width = mountRef.current.clientWidth;
|
||||||
let height = mountRef.current.clientHeight;
|
let height = mountRef.current.clientHeight;
|
||||||
|
|
||||||
// Сцена
|
|
||||||
const scene = new THREE.Scene();
|
const scene = new THREE.Scene();
|
||||||
|
sceneRef.current = scene; // Сохраняем ссылку на сцену
|
||||||
// Камера
|
|
||||||
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
|
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
|
||||||
camera.position.set(0, 20, 30);
|
camera.position.set(0, 20, 30);
|
||||||
|
|
||||||
// Рендерер
|
|
||||||
const renderer = new THREE.WebGLRenderer();
|
const renderer = new THREE.WebGLRenderer();
|
||||||
renderer.setSize(width, height);
|
renderer.setSize(width, height);
|
||||||
mountRef.current.appendChild(renderer.domElement);
|
mountRef.current.appendChild(renderer.domElement);
|
||||||
|
|
||||||
// Плоскость для высотной карты
|
// Плоскость для высотной карты :: TODO :: W/H/Ws/Hs change
|
||||||
const geometry = new THREE.PlaneGeometry(20, 20, 4, 4);
|
const geometry = new THREE.PlaneGeometry(20, 20, 4, 4);
|
||||||
|
|
||||||
const vertices = geometry.attributes.position.array;
|
const vertices = geometry.attributes.position.array;
|
||||||
const colors = [];
|
const colors = [];
|
||||||
const color = new THREE.Color();
|
const color = new THREE.Color();
|
||||||
|
|
||||||
// Находим минимальные и максимальные значения высот
|
const minHeight = Math.min(...heightData.map((point) => point.height));
|
||||||
const minHeight = Math.min(...heightData.flat());
|
const maxHeight = Math.max(mapSettings.maxHeight, ...heightData.map((point) => point.height));
|
||||||
const maxHeight = Math.max(mapSettings.maxHeight, ...heightData.flat());
|
|
||||||
|
|
||||||
for (let i = 0; i < vertices.length; i += 3) {
|
for (let i = 0; i < vertices.length; i += 3) {
|
||||||
const x = Math.floor(i / 3) % 5;
|
const x = Math.floor(i / 3) % 5;
|
||||||
const y = Math.floor(i / (3 * 5));
|
const y = Math.floor(i / (3 * 5));
|
||||||
const heightValue = heightData[y][x];
|
|
||||||
|
const heightPoint = heightData.find((point) => point.x === x && point.y === y);
|
||||||
|
const heightValue = heightPoint ? heightPoint.height : 0;
|
||||||
|
|
||||||
vertices[i + 2] = heightValue;
|
vertices[i + 2] = heightValue;
|
||||||
|
|
||||||
const normalizedHeight = (heightValue - minHeight) / (maxHeight - minHeight);
|
const normalizedHeight = (heightValue - minHeight) / (maxHeight - minHeight);
|
||||||
@ -72,68 +80,12 @@ const Dashboard = () => {
|
|||||||
mesh.rotation.x = -Math.PI / 2;
|
mesh.rotation.x = -Math.PI / 2;
|
||||||
scene.add(mesh);
|
scene.add(mesh);
|
||||||
|
|
||||||
// Оси координат X, Y и высота
|
|
||||||
const axesHelper = new THREE.AxesHelper(10);
|
const axesHelper = new THREE.AxesHelper(10);
|
||||||
scene.add(axesHelper);
|
scene.add(axesHelper);
|
||||||
|
|
||||||
// Линейка и числовые обозначения
|
|
||||||
const createRuler = (scene) => {
|
|
||||||
const lineMaterial = new THREE.LineBasicMaterial({ color: 0x000000 });
|
|
||||||
|
|
||||||
// Ось X
|
|
||||||
const xPoints = [];
|
|
||||||
xPoints.push(new THREE.Vector3(0, 0.1, 0)); // Левый нижний угол
|
|
||||||
xPoints.push(new THREE.Vector3(20, 0.1, 0)); // Правая сторона
|
|
||||||
const xGeometry = new THREE.BufferGeometry().setFromPoints(xPoints);
|
|
||||||
const xLine = new THREE.Line(xGeometry, lineMaterial);
|
|
||||||
scene.add(xLine);
|
|
||||||
|
|
||||||
// Ось Y
|
|
||||||
const yPoints = [];
|
|
||||||
yPoints.push(new THREE.Vector3(0, 0.1, 0)); // Левый нижний угол
|
|
||||||
yPoints.push(new THREE.Vector3(0, 0.1, 20)); // Верхняя сторона
|
|
||||||
const yGeometry = new THREE.BufferGeometry().setFromPoints(yPoints);
|
|
||||||
const yLine = new THREE.Line(yGeometry, lineMaterial);
|
|
||||||
scene.add(yLine);
|
|
||||||
|
|
||||||
// Числовые обозначения
|
|
||||||
const fontLoader = new FontLoader();
|
|
||||||
fontLoader.load('https://threejs.org/examples/fonts/helvetiker_regular.typeface.json', (font) => {
|
|
||||||
// Числа на оси X
|
|
||||||
for (let i = 0; i <= 20; i += 5) {
|
|
||||||
const textGeometry = new TextGeometry(i.toString(), {
|
|
||||||
font: font,
|
|
||||||
size: 0.5,
|
|
||||||
height: 0.1,
|
|
||||||
});
|
|
||||||
const textMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
|
|
||||||
const textMesh = new THREE.Mesh(textGeometry, textMaterial);
|
|
||||||
textMesh.position.set(i, 0.1, -0.5);
|
|
||||||
scene.add(textMesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Числа на оси Y
|
|
||||||
for (let i = 0; i <= 20; i += 5) {
|
|
||||||
const textGeometry = new TextGeometry(i.toString(), {
|
|
||||||
font: font,
|
|
||||||
size: 0.5,
|
|
||||||
height: 0.1,
|
|
||||||
});
|
|
||||||
const textMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
|
|
||||||
const textMesh = new THREE.Mesh(textGeometry, textMaterial);
|
|
||||||
textMesh.position.set(-0.5, 0.1, i);
|
|
||||||
scene.add(textMesh);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
createRuler(scene);
|
|
||||||
|
|
||||||
// Настройка OrbitControls для взаимодействия с картой
|
|
||||||
const controls = new OrbitControls(camera, renderer.domElement);
|
const controls = new OrbitControls(camera, renderer.domElement);
|
||||||
controls.enableDamping = true;
|
controls.enableDamping = true;
|
||||||
|
|
||||||
// Добавляем свет
|
|
||||||
const light = new THREE.DirectionalLight(0xffffff, 1);
|
const light = new THREE.DirectionalLight(0xffffff, 1);
|
||||||
light.position.set(0, 50, 50).normalize();
|
light.position.set(0, 50, 50).normalize();
|
||||||
scene.add(light);
|
scene.add(light);
|
||||||
@ -145,12 +97,38 @@ const Dashboard = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
animate();
|
animate();
|
||||||
if (mountRef.current) localRef = mountRef.current;
|
// Добавление сохранённых дронов в сцену при рендере
|
||||||
|
drones.forEach(droneData => {
|
||||||
|
// const drone = new Drone();
|
||||||
|
// drone.setPosition(droneData.x, droneData.y, droneData.z);
|
||||||
|
scene.add(droneData.getObject()); //drone.getObject()
|
||||||
|
});
|
||||||
|
// Обработка нажатия ПКМ
|
||||||
|
renderer.domElement.addEventListener('contextmenu', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setContextMenu({
|
||||||
|
visible: true,
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
mountRef.current.removeChild(renderer.domElement);
|
if (mountRef.current) {
|
||||||
|
mountRef.current.removeChild(renderer.domElement);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [heightData, mapSettings]);
|
}, [heightData, mapSettings]);
|
||||||
|
|
||||||
|
const handleAddDrone = () => {
|
||||||
|
if (sceneRef.current) {
|
||||||
|
const drone = new Drone();
|
||||||
|
drone.setPosition(Math.random() * 20 - 10, 0.5, Math.random() * 20 - 10);
|
||||||
|
setDrones((prevDrones) => [...prevDrones, drone]);
|
||||||
|
sceneRef.current.add(drone.getObject());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleInputChange = (event) => {
|
const handleInputChange = (event) => {
|
||||||
const { name, value } = event.target;
|
const { name, value } = event.target;
|
||||||
setFormData({
|
setFormData({
|
||||||
@ -164,14 +142,10 @@ const Dashboard = () => {
|
|||||||
|
|
||||||
const { x, y, height } = formData;
|
const { x, y, height } = formData;
|
||||||
|
|
||||||
if (x >= 0 && y >= 0 && x < heightData[0].length && y < heightData.length) {
|
const newPoint = new HeightPoint(parseInt(x), parseInt(y), parseFloat(height));
|
||||||
const updatedData = [...heightData];
|
|
||||||
updatedData[y][x] = parseFloat(height);
|
setHeightData((prevData) => [...prevData, newPoint]);
|
||||||
setHeightData(updatedData);
|
setFormData({ x: '', y: '', height: '' });
|
||||||
setFormData({ x: '', y: '', height: '' });
|
|
||||||
} else {
|
|
||||||
alert('Координаты вне допустимого диапазона!');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSettingsChange = (event) => {
|
const handleSettingsChange = (event) => {
|
||||||
@ -182,81 +156,146 @@ const Dashboard = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleContextMenuClick = (action) => {
|
||||||
|
if (action === 'add') {
|
||||||
|
handleAddDrone();
|
||||||
|
console.log('Добавить объект');
|
||||||
|
} else if (action === 'save') {
|
||||||
|
console.log('Сохранить промежуточный результат');
|
||||||
|
}
|
||||||
|
setContextMenu({ ...contextMenu, visible: false });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<h1>Drone Network Simulator - окно разработки</h1>
|
<h1>Drone Network Simulator - окно разработки</h1>
|
||||||
|
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||||
|
<div id="navbarBasicExample" class="navbar-menu">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item">
|
||||||
|
Объекты
|
||||||
|
</a>
|
||||||
|
|
||||||
{/* 3D Карта высот */}
|
<a class="navbar-item">
|
||||||
<div ref={mountRef} style={{ width: '100%', height: '500px' }} />
|
Документация
|
||||||
|
</a>
|
||||||
|
|
||||||
{/* Форма для добавления новых высот */}
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
<form onSubmit={handleAddHeight} style={{ marginTop: '20px' }}>
|
<a class="navbar-link">
|
||||||
<div>
|
Файл
|
||||||
<label>Координата X:</label>
|
</a>
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="x"
|
|
||||||
value={formData.x}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label>Координата Y:</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="y"
|
|
||||||
value={formData.y}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label>Высота:</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="height"
|
|
||||||
value={formData.height}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button type="submit">Добавить высоту</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{/* Форма для изменения настроек карты */}
|
<div class="navbar-dropdown">
|
||||||
<form style={{ marginTop: '20px' }}>
|
<a class="navbar-item">
|
||||||
<div>
|
Сохранить локально
|
||||||
<label>Максимальная высота:</label>
|
</a>
|
||||||
<input
|
<a class="navbar-item is-selected">
|
||||||
type="number"
|
Сохранить на сервере
|
||||||
name="maxHeight"
|
</a>
|
||||||
value={mapSettings.maxHeight}
|
<a class="navbar-item">
|
||||||
onChange={handleSettingsChange}
|
Настройки клиента
|
||||||
required
|
</a>
|
||||||
/>
|
<hr class="navbar-divider"/>
|
||||||
|
<a class="navbar-item">
|
||||||
|
Debug mode
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item">
|
||||||
|
<div class="tabs">
|
||||||
|
<ul>
|
||||||
|
<li class="is-active"><a>Моделирование</a></li>
|
||||||
|
<li><a>Симуляция</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</nav>
|
||||||
<label>Координата X (камера):</label>
|
|
||||||
<input
|
<div ref={mountRef} style={{ width: '100%', height: '500px', position: 'relative' }} />
|
||||||
type="number"
|
|
||||||
name="coordX"
|
{/* Контекстное меню */}
|
||||||
value={mapSettings.coordX}
|
{contextMenu.visible && (
|
||||||
onChange={handleSettingsChange}
|
<ul
|
||||||
required
|
style={{
|
||||||
/>
|
position: 'absolute',
|
||||||
|
top: `${contextMenu.y}px`,
|
||||||
|
left: `${contextMenu.x}px`,
|
||||||
|
backgroundColor: 'white',
|
||||||
|
listStyle: 'none',
|
||||||
|
padding: '10px',
|
||||||
|
boxShadow: '0px 0px 5px rgba(0,0,0,0.3)',
|
||||||
|
zIndex: 1000,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ flex: 1, flexFlow: "column" }}>
|
||||||
|
<li className={"button"} onClick={() => handleContextMenuClick('add')}>Добавить объект</li>
|
||||||
|
<li className={"button"} onClick={() => handleContextMenuClick('save')}>Сохранить промежуточный результат</li>
|
||||||
|
<li className={"button"} onClick={() => handleContextMenuClick('cancel')}>Отмена</li>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
<div className="columns">
|
||||||
|
{/* Форма для добавления новых высот */}
|
||||||
|
<div className='column'>
|
||||||
|
<form onSubmit={handleAddHeight} style={{ marginTop: '20px' }}>
|
||||||
|
<div>
|
||||||
|
<label>Координата X:</label>
|
||||||
|
<input
|
||||||
|
className="input is-primary"
|
||||||
|
type="number"
|
||||||
|
name="x"
|
||||||
|
value={formData.x}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Координата Y:</label>
|
||||||
|
<input
|
||||||
|
className="input is-primary"
|
||||||
|
type="number"
|
||||||
|
name="y"
|
||||||
|
value={formData.y}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Высота:</label>
|
||||||
|
<input
|
||||||
|
className="input is-primary"
|
||||||
|
type="number"
|
||||||
|
name="height"
|
||||||
|
value={formData.height}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button className='button' type="submit">Добавить точку высоты</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
{/* Настройки карты */}
|
||||||
<label>Координата Y (камера):</label>
|
<div className='column'>
|
||||||
<input
|
<div style={{ marginTop: '20px' }}>
|
||||||
type="number"
|
<h2>Настройки карты</h2>
|
||||||
name="coordY"
|
<div>
|
||||||
value={mapSettings.coordY}
|
<label>Максимальная высота:</label>
|
||||||
onChange={handleSettingsChange}
|
<input
|
||||||
required
|
className="input"
|
||||||
/>
|
type="number"
|
||||||
|
name="maxHeight"
|
||||||
|
value={mapSettings.maxHeight}
|
||||||
|
onChange={handleSettingsChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
19
src/frontend/src/pages/Docs.js
Normal file
19
src/frontend/src/pages/Docs.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const Docs = () => {
|
||||||
|
return (
|
||||||
|
<div className="block">
|
||||||
|
<div class="block">
|
||||||
|
Документация <strong>Drone Network Simulator</strong>.
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
created by <strong>@moxitech</strong>
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
Для начала работы необходимо войти в систему
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Docs;
|
11
src/frontend/src/pages/Model/HeightPoint.js
Normal file
11
src/frontend/src/pages/Model/HeightPoint.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Класс для хранения точки с координатами и высотой
|
||||||
|
class HeightPoint {
|
||||||
|
constructor(x, y, height) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default HeightPoint;
|
84
src/frontend/src/pages/PrevCalc.js
Normal file
84
src/frontend/src/pages/PrevCalc.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import '../UserAccount.scss';
|
||||||
|
|
||||||
|
const PrevCalc = () => {
|
||||||
|
const [userData, setUserData] = useState({
|
||||||
|
username: 'Мамут Рахал',
|
||||||
|
email: 'yatupoidayn@mail.ru',
|
||||||
|
phone: '+666',
|
||||||
|
});
|
||||||
|
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const [formData, setFormData] = useState(userData);
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
[name]: value,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
setUserData(formData);
|
||||||
|
setIsEditing(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setFormData(userData);
|
||||||
|
setIsEditing(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="user-account">
|
||||||
|
<h1>Данные</h1>
|
||||||
|
|
||||||
|
{isEditing ? (
|
||||||
|
<div className="edit-form">
|
||||||
|
<label>
|
||||||
|
Имя пользователя:
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="username"
|
||||||
|
value={formData.username}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Никнейм авторизации:
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="email"
|
||||||
|
value={formData.email}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Пароль:
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="phone"
|
||||||
|
value={formData.phone}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div className="buttons">
|
||||||
|
<button onClick={handleSave}>Сохранить</button>
|
||||||
|
<button onClick={handleCancel}>Отмена</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="user-info">
|
||||||
|
<p><strong>Имя:</strong> {userData.username}</p>
|
||||||
|
<p><strong>Email:</strong> {userData.email}</p>
|
||||||
|
<p><strong>Телефон:</strong> {userData.phone}</p>
|
||||||
|
<button className="edit-button" onClick={() => setIsEditing(true)}>
|
||||||
|
Редактировать
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PrevCalc;
|
@ -1,13 +1,18 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"moxitech/dns/internal/database"
|
||||||
"moxitech/dns/internal/server"
|
"moxitech/dns/internal/server"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
D_EXPORT_VARS()
|
D_EXPORT_VARS()
|
||||||
err := server.SpawnServer()
|
err := database.NewDBConnection()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = server.SpawnServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,18 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
type SystemUser struct {
|
import "gorm.io/gorm"
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
gorm.Model
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
|
FIO string `json:"fio"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
IsAdmin bool `json:"is_admin"`
|
IsAdmin bool `json:"is_admin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Objects struct {
|
||||||
|
gorm.Model
|
||||||
|
ObjectName string `json:"name"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
Params string `json:"params"`
|
||||||
|
}
|
||||||
|
@ -2,11 +2,16 @@ module moxitech/dns
|
|||||||
|
|
||||||
go 1.22.7
|
go 1.22.7
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gofiber/fiber/v2 v2.52.5
|
||||||
|
gorm.io/driver/postgres v1.5.9
|
||||||
|
gorm.io/gorm v1.25.12
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
github.com/fasthttp/websocket v1.5.10 // indirect
|
github.com/fasthttp/websocket v1.5.3 // indirect
|
||||||
github.com/gofiber/contrib/websocket v1.3.2 // indirect
|
github.com/gofiber/websocket/v2 v2.2.1 // indirect
|
||||||
github.com/gofiber/fiber/v2 v2.52.5 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
@ -19,15 +24,13 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
|
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
|
||||||
|
github.com/stretchr/testify v1.9.0 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.56.0 // indirect
|
github.com/valyala/fasthttp v1.56.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
golang.org/x/crypto v0.27.0 // indirect
|
golang.org/x/crypto v0.27.0 // indirect
|
||||||
golang.org/x/net v0.29.0 // indirect
|
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/sys v0.25.0 // indirect
|
golang.org/x/sys v0.25.0 // indirect
|
||||||
golang.org/x/text v0.18.0 // indirect
|
golang.org/x/text v0.18.0 // indirect
|
||||||
gorm.io/driver/postgres v1.5.9 // indirect
|
|
||||||
gorm.io/gorm v1.25.12 // indirect
|
|
||||||
)
|
)
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
|
||||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
|
||||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fasthttp/websocket v1.5.10 h1:bc7NIGyrg1L6sd5pRzCIbXpro54SZLEluZCu0rOpcN4=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/fasthttp/websocket v1.5.10/go.mod h1:BwHeuXGWzCW1/BIKUKD3+qfCl+cTdsHu/f243NcAI/Q=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gofiber/contrib/websocket v1.3.2 h1:AUq5PYeKwK50s0nQrnluuINYeep1c4nRCJ0NWsV3cvg=
|
github.com/fasthttp/websocket v1.5.3 h1:TPpQuLwJYfd4LJPXvHDYPMFWbLjsT91n3GpWtCQtdek=
|
||||||
github.com/gofiber/contrib/websocket v1.3.2/go.mod h1:07u6QGMsvX+sx7iGNCl5xhzuUVArWwLQ3tBIH24i+S8=
|
github.com/fasthttp/websocket v1.5.3/go.mod h1:46gg/UBmTU1kUaTcwQXpUxtRwG2PvIZYeA8oL6vF3Fs=
|
||||||
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
||||||
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
github.com/gofiber/websocket/v2 v2.2.1 h1:C9cjxvloojayOp9AovmpQrk8VqvVnT8Oao3+IUygH7w=
|
||||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/gofiber/websocket/v2 v2.2.1/go.mod h1:Ao/+nyNnX5u/hIFPuHl28a+NIkrqK7PRimyKaj4JxVU=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
@ -25,8 +23,6 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
|||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
|
||||||
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
|
||||||
github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
|
github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
|
||||||
github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
@ -34,45 +30,40 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
|
||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
|
||||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
|
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
|
||||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
|
|
||||||
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
|
||||||
github.com/valyala/fasthttp v1.56.0 h1:bEZdJev/6LCBlpdORfrLu/WOZXXxvrUQSiyniuaoW8U=
|
github.com/valyala/fasthttp v1.56.0 h1:bEZdJev/6LCBlpdORfrLu/WOZXXxvrUQSiyniuaoW8U=
|
||||||
github.com/valyala/fasthttp v1.56.0/go.mod h1:sReBt3XZVnudxuLOx4J/fMrJVorWRiWY2koQKgABiVI=
|
github.com/valyala/fasthttp v1.56.0/go.mod h1:sReBt3XZVnudxuLOx4J/fMrJVorWRiWY2koQKgABiVI=
|
||||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
|
||||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
|
||||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
|
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
|
||||||
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||||
|
@ -2,18 +2,23 @@ package database
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"moxitech/dns/entity/database"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DbDriver *GormDbInstance
|
||||||
|
)
|
||||||
|
|
||||||
type GormDbInstance struct {
|
type GormDbInstance struct {
|
||||||
DB *gorm.DB
|
DB *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// GiveMeConnectionString создает строку подключения к базе данных
|
// giveMeConnectionString создает строку подключения к базе данных
|
||||||
func (db *GormDbInstance) GiveMeConnectionString() string {
|
func giveMeConnectionString() string {
|
||||||
str := fmt.Sprintf("postgresql://%s:%s@%s:%s/%s",
|
str := fmt.Sprintf("postgresql://%s:%s@%s:%s/%s",
|
||||||
os.Getenv("POSTGRES_USER"),
|
os.Getenv("POSTGRES_USER"),
|
||||||
os.Getenv("POSTGRES_PASSWORD"),
|
os.Getenv("POSTGRES_PASSWORD"),
|
||||||
@ -25,16 +30,51 @@ func (db *GormDbInstance) GiveMeConnectionString() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewDBConnection создает новое подключение к базе данных
|
// NewDBConnection создает новое подключение к базе данных
|
||||||
func (db *GormDbInstance) NewDBConnection() (*gorm.DB, error) {
|
func NewDBConnection() error {
|
||||||
cs := db.GiveMeConnectionString()
|
cs := giveMeConnectionString()
|
||||||
dbInstance, err := gorm.Open(postgres.Open(cs), &gorm.Config{})
|
dbInstance, err := gorm.Open(postgres.Open(cs), &gorm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to open database connection: %w", err)
|
return fmt.Errorf("failed to open database connection: %w", err)
|
||||||
}
|
}
|
||||||
return dbInstance, nil
|
DbDriver = &GormDbInstance{DB: dbInstance}
|
||||||
|
// AutoMigration Block after restart application
|
||||||
|
DbDriver.AutoMigrate()
|
||||||
|
DbDriver.AutoMakeSuperUser()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutoMigrate выполняет автоматическую миграцию
|
// AutoMigrate выполняет автоматическую миграцию
|
||||||
func (db *GormDbInstance) AutoMigrate(dbInstance *gorm.DB) error {
|
func (db *GormDbInstance) AutoMigrate() error {
|
||||||
return dbInstance.AutoMigrate()
|
return DbDriver.DB.AutoMigrate(
|
||||||
|
database.User{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
func (db *GormDbInstance) AutoMakeSuperUser() error {
|
||||||
|
var user database.User
|
||||||
|
if db.DB != nil {
|
||||||
|
db.DB.Where("username = ?", "admin").First(&user)
|
||||||
|
if user.ID == 0 {
|
||||||
|
db.DB.Create(&database.User{Username: "admin", Password: "admin", IsAdmin: true})
|
||||||
|
fmt.Println("Superuser has updated! :: \n Login: admin \n Password: admin")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("DB is nil poiner")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *GormDbInstance) GetUserByName(name string) (*database.User, error) {
|
||||||
|
// Find user by name
|
||||||
|
var user database.User
|
||||||
|
if err := db.DB.Where("username = ?", name).First(&user).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to find user: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Found user: ID: %d, Name: %s, Email: %s\n", user.ID, user.Username, user.Password)
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
func CheckIsUserExists() {
|
||||||
|
|
||||||
|
}
|
||||||
|
func (db *GormDbInstance) GetUserSettings() error {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package authorization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"moxitech/dns/internal/database"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GET localhost:8080/auth/username?/password?
|
||||||
|
func AuthUser(c *fiber.Ctx) error {
|
||||||
|
username := c.Params("username")
|
||||||
|
password := c.Params("password")
|
||||||
|
if username == "" || password == "" {
|
||||||
|
return c.Status(501).SendString("username and password has required!")
|
||||||
|
}
|
||||||
|
user, err := database.DbDriver.GetUserByName(username)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(501).SendString(err.Error())
|
||||||
|
}
|
||||||
|
if user == nil {
|
||||||
|
return c.Status(403).SendString(fmt.Sprintf("User with username: %v not found", username))
|
||||||
|
}
|
||||||
|
if user.Password != password {
|
||||||
|
return c.Status(403).SendString("Bad password!")
|
||||||
|
}
|
||||||
|
return c.Status(200).SendString(fmt.Sprintf("Welcome to Drone Network Simulator server-side API! \n UserId: %v ", user.ID))
|
||||||
|
}
|
@ -1,9 +1,12 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"moxitech/dns/internal/server/handlers/authorization"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/websocket/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SpawnServer() error {
|
func SpawnServer() error {
|
||||||
@ -11,5 +14,57 @@ func SpawnServer() error {
|
|||||||
app.Get("/", func(c *fiber.Ctx) error {
|
app.Get("/", func(c *fiber.Ctx) error {
|
||||||
return c.SendString("Welcome to Drone Network Simulator server-side API")
|
return c.SendString("Welcome to Drone Network Simulator server-side API")
|
||||||
})
|
})
|
||||||
|
app.Get("/auth/:username/:password", authorization.AuthUser)
|
||||||
|
// WebSocket маршрут
|
||||||
|
app.Get("/ws/connect/", websocket.New(CreateSocket))
|
||||||
|
|
||||||
return app.Listen(os.Getenv("SERVER_BASE_ADDRESS"))
|
return app.Listen(os.Getenv("SERVER_BASE_ADDRESS"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WS :: ws://localhost:8080/ws/connect?userToken=?&groupId=?
|
||||||
|
func CreateSocket(c *websocket.Conn) {
|
||||||
|
// Получаем токен пользователя из query параметров
|
||||||
|
userToken := c.Query("userToken")
|
||||||
|
if userToken == "" {
|
||||||
|
c.WriteMessage(websocket.ClosePolicyViolation, []byte("userToken is required"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Получаем группу к которой коннектиться user из query параметров
|
||||||
|
groupId := c.Query("groupId")
|
||||||
|
if groupId == "" {
|
||||||
|
c.WriteMessage(websocket.ClosePolicyViolation, []byte("groupId is required"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Логика обработки подключения
|
||||||
|
fmt.Printf("User connected with token: %s\n", userToken)
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Читаем сообщения от клиента
|
||||||
|
messageType, msg, err := c.ReadMessage()
|
||||||
|
if messageType != websocket.TextMessage {
|
||||||
|
err = c.WriteMessage(websocket.BinaryMessage, []byte("Please use text message instead"))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error writing message: %v\n", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error reading message: %v\n", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fmt.Printf("Received message from user %s: %s\n", userToken, msg)
|
||||||
|
|
||||||
|
// Отправляем обратно сообщение клиенту
|
||||||
|
err = c.WriteMessage(websocket.BinaryMessage, msg)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error writing message: %v\n", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Закрываем соединение при выходе
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
@ -1 +1,20 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
|
// CreateGroupRequest создает группу пользователей
|
||||||
|
// @UserDTO -> трансфер пользовательских данных {id}
|
||||||
|
func CreateGroupRequest() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlushGroupRequest сохраняет результат вычислений группы
|
||||||
|
// @GroupEntity : WS entity -> сущность группы с данными и вычислениями
|
||||||
|
// @IsUserRequest : boolean -> если пользовательский запрос - сейвим с именем группы, иначе создаем произвольную запись с Username<UserId:Creator> + timestamp
|
||||||
|
func FlushGroupRequest() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObserveGroupHandler проверяет массив групп пользователей и если в группе нет активных пользователей - закрываем соединение
|
||||||
|
// Запускать в горутине после старта сервера!
|
||||||
|
func ObserveGroupHandler() {
|
||||||
|
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user