This commit is contained in:
moxitech 2024-09-28 05:46:42 +07:00
parent 19096a5d76
commit 6225ef638d
17 changed files with 552 additions and 193 deletions

4
.env
View File

@ -2,7 +2,9 @@ SERVER_BASE_ADDRESS=0.0.0.0:8080
MONGO_INITDB_ROOT_USERNAME=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_USER=moxitech

View File

@ -6,6 +6,9 @@ import Dashboard from './pages/Dashboard';
import UserAccount from './pages/UserAccount'; // Импортируем компонент UserAccount
import Login from './pages/Login'; // Импортируем страницу логина
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';
const App = () => {
@ -44,6 +47,8 @@ const App = () => {
{activeTab === 'account' && <UserAccount />} {/* Подключаем компонент UserAccount */}
{activeTab === 'groups' && <DeviceGroups />} {/* Группы устройств */}
{activeTab === 'devices' && <Devices />} {/* Устройства */}
{activeTab === 'prev_calc' && <PrevCalc />} {/* Устройства */}
{activeTab === 'docs' && <Docs />} {/* Устройства */}
</div>
</div>
</div>

View File

@ -22,9 +22,12 @@ const Sidebar = ({ onSelectTab, activeTab, onLogout }) => {
<li className={activeTab === 'connection' ? 'active' : ''} onClick={() => onSelectTab('connection')}>
Настройки
</li>
<li className={activeTab === 'connection' ? 'active' : ''} onClick={() => onSelectTab('connection')}>
<li className={activeTab === 'prev_calc' ? 'active' : ''} onClick={() => onSelectTab('prev_calc')}>
Проведенные вычисления
</li>
<li className={activeTab === 'docs' ? 'active' : ''} onClick={() => onSelectTab('docs')}>
Документация
</li>
<li className={activeTab === 'account' ? 'active' : ''} onClick={toggleSubMenu}>
Аккаунт пользователя
</li>

3
src/frontend/src/css/bulma.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View 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;
}
}

View File

@ -3,56 +3,64 @@ import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader';
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 mountRef = useRef(null);
const [heightData, setHeightData] = useState([
[1, 2, 3, 4, 5],
[2, 3, 4, 5, 6],
[3, 4, 5, 6, 7],
[4, 5, 6, 7, 8],
[5, 6, 7, 8, 9],
]);
const sceneRef = useRef(null);
const [heightData, setHeightData] = useState(RandomHeightPoint());
const [formData, setFormData] = useState({ x: '', y: '', height: '' });
const [drones, setDrones] = useState([])
const [mapSettings, setMapSettings] = useState({
maxHeight: 10,
maxHeight: 20,
coordX: 0,
coordY: 0,
});
const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0 });
useEffect(() => {
let localRef = null;
if (!mountRef.current) return;
let width = mountRef.current.clientWidth;
let height = mountRef.current.clientHeight;
// Сцена
const scene = new THREE.Scene();
// Камера
sceneRef.current = scene; // Сохраняем ссылку на сцену
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
camera.position.set(0, 20, 30);
// Рендерер
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
mountRef.current.appendChild(renderer.domElement);
// Плоскость для высотной карты
// Плоскость для высотной карты :: TODO :: W/H/Ws/Hs change
const geometry = new THREE.PlaneGeometry(20, 20, 4, 4);
const vertices = geometry.attributes.position.array;
const colors = [];
const color = new THREE.Color();
// Находим минимальные и максимальные значения высот
const minHeight = Math.min(...heightData.flat());
const maxHeight = Math.max(mapSettings.maxHeight, ...heightData.flat());
const minHeight = Math.min(...heightData.map((point) => point.height));
const maxHeight = Math.max(mapSettings.maxHeight, ...heightData.map((point) => point.height));
for (let i = 0; i < vertices.length; i += 3) {
const x = 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;
const normalizedHeight = (heightValue - minHeight) / (maxHeight - minHeight);
@ -72,68 +80,12 @@ const Dashboard = () => {
mesh.rotation.x = -Math.PI / 2;
scene.add(mesh);
// Оси координат X, Y и высота
const axesHelper = new THREE.AxesHelper(10);
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);
controls.enableDamping = true;
// Добавляем свет
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 50, 50).normalize();
scene.add(light);
@ -145,12 +97,38 @@ const Dashboard = () => {
};
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 () => {
mountRef.current.removeChild(renderer.domElement);
if (mountRef.current) {
mountRef.current.removeChild(renderer.domElement);
}
};
}, [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 { name, value } = event.target;
setFormData({
@ -164,14 +142,10 @@ const Dashboard = () => {
const { x, y, height } = formData;
if (x >= 0 && y >= 0 && x < heightData[0].length && y < heightData.length) {
const updatedData = [...heightData];
updatedData[y][x] = parseFloat(height);
setHeightData(updatedData);
setFormData({ x: '', y: '', height: '' });
} else {
alert('Координаты вне допустимого диапазона!');
}
const newPoint = new HeightPoint(parseInt(x), parseInt(y), parseFloat(height));
setHeightData((prevData) => [...prevData, newPoint]);
setFormData({ x: '', y: '', height: '' });
};
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 (
<div style={{ flex: 1 }}>
<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 Карта высот */}
<div ref={mountRef} style={{ width: '100%', height: '500px' }} />
<a class="navbar-item">
Документация
</a>
{/* Форма для добавления новых высот */}
<form onSubmit={handleAddHeight} style={{ marginTop: '20px' }}>
<div>
<label>Координата X:</label>
<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-item has-dropdown is-hoverable">
<a class="navbar-link">
Файл
</a>
{/* Форма для изменения настроек карты */}
<form style={{ marginTop: '20px' }}>
<div>
<label>Максимальная высота:</label>
<input
type="number"
name="maxHeight"
value={mapSettings.maxHeight}
onChange={handleSettingsChange}
required
/>
<div class="navbar-dropdown">
<a class="navbar-item">
Сохранить локально
</a>
<a class="navbar-item is-selected">
Сохранить на сервере
</a>
<a class="navbar-item">
Настройки клиента
</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>
<label>Координата X (камера):</label>
<input
type="number"
name="coordX"
value={mapSettings.coordX}
onChange={handleSettingsChange}
required
/>
</nav>
<div ref={mountRef} style={{ width: '100%', height: '500px', position: 'relative' }} />
{/* Контекстное меню */}
{contextMenu.visible && (
<ul
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>
<label>Координата Y (камера):</label>
<input
type="number"
name="coordY"
value={mapSettings.coordY}
onChange={handleSettingsChange}
required
/>
{/* Настройки карты */}
<div className='column'>
<div style={{ marginTop: '20px' }}>
<h2>Настройки карты</h2>
<div>
<label>Максимальная высота:</label>
<input
className="input"
type="number"
name="maxHeight"
value={mapSettings.maxHeight}
onChange={handleSettingsChange}
/>
</div>
</div>
</div>
</form>
</div>
</div>
);
};

View 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;

View File

@ -0,0 +1,11 @@
// Класс для хранения точки с координатами и высотой
class HeightPoint {
constructor(x, y, height) {
this.x = x;
this.y = y;
this.height = height;
}
}
export default HeightPoint;

View 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;

View File

@ -1,13 +1,18 @@
package main
import (
"moxitech/dns/internal/database"
"moxitech/dns/internal/server"
"os"
)
func main() {
D_EXPORT_VARS()
err := server.SpawnServer()
err := database.NewDBConnection()
if err != nil {
panic(err)
}
err = server.SpawnServer()
if err != nil {
panic(err)
}

View File

@ -1,7 +1,18 @@
package database
type SystemUser struct {
import "gorm.io/gorm"
type User struct {
gorm.Model
Username string `json:"username"`
FIO string `json:"fio"`
Password string `json:"password"`
IsAdmin bool `json:"is_admin"`
}
type Objects struct {
gorm.Model
ObjectName string `json:"name"`
Type int `json:"type"`
Params string `json:"params"`
}

View File

@ -2,11 +2,16 @@ module moxitech/dns
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 (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/fasthttp/websocket v1.5.10 // indirect
github.com/gofiber/contrib/websocket v1.3.2 // indirect
github.com/gofiber/fiber/v2 v2.52.5 // indirect
github.com/fasthttp/websocket v1.5.3 // indirect
github.com/gofiber/websocket/v2 v2.2.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // 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-runewidth v0.0.16 // 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/fasthttp v1.56.0 // indirect
github.com/valyala/tcplisten v1.0.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/sys v0.25.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
)

View File

@ -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/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
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/fasthttp/websocket v1.5.10/go.mod h1:BwHeuXGWzCW1/BIKUKD3+qfCl+cTdsHu/f243NcAI/Q=
github.com/gofiber/contrib/websocket v1.3.2 h1:AUq5PYeKwK50s0nQrnluuINYeep1c4nRCJ0NWsV3cvg=
github.com/gofiber/contrib/websocket v1.3.2/go.mod h1:07u6QGMsvX+sx7iGNCl5xhzuUVArWwLQ3tBIH24i+S8=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fasthttp/websocket v1.5.3 h1:TPpQuLwJYfd4LJPXvHDYPMFWbLjsT91n3GpWtCQtdek=
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/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gofiber/websocket/v2 v2.2.1 h1:C9cjxvloojayOp9AovmpQrk8VqvVnT8Oao3+IUygH7w=
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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
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/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
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/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/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.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
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-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
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/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.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/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/go.mod h1:sReBt3XZVnudxuLOx4J/fMrJVorWRiWY2koQKgABiVI=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
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.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
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/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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.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/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
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/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/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=

View File

@ -2,18 +2,23 @@ package database
import (
"fmt"
"moxitech/dns/entity/database"
"os"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
var (
DbDriver *GormDbInstance
)
type GormDbInstance struct {
DB *gorm.DB
}
// GiveMeConnectionString создает строку подключения к базе данных
func (db *GormDbInstance) GiveMeConnectionString() string {
// giveMeConnectionString создает строку подключения к базе данных
func giveMeConnectionString() string {
str := fmt.Sprintf("postgresql://%s:%s@%s:%s/%s",
os.Getenv("POSTGRES_USER"),
os.Getenv("POSTGRES_PASSWORD"),
@ -25,16 +30,51 @@ func (db *GormDbInstance) GiveMeConnectionString() string {
}
// NewDBConnection создает новое подключение к базе данных
func (db *GormDbInstance) NewDBConnection() (*gorm.DB, error) {
cs := db.GiveMeConnectionString()
func NewDBConnection() error {
cs := giveMeConnectionString()
dbInstance, err := gorm.Open(postgres.Open(cs), &gorm.Config{})
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 выполняет автоматическую миграцию
func (db *GormDbInstance) AutoMigrate(dbInstance *gorm.DB) error {
return dbInstance.AutoMigrate()
func (db *GormDbInstance) AutoMigrate() error {
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
}

View File

@ -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))
}

View File

@ -1,9 +1,12 @@
package server
import (
"fmt"
"moxitech/dns/internal/server/handlers/authorization"
"os"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/websocket/v2"
)
func SpawnServer() error {
@ -11,5 +14,57 @@ func SpawnServer() error {
app.Get("/", func(c *fiber.Ctx) error {
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"))
}
// 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()
}

View File

@ -1 +1,20 @@
package server
// CreateGroupRequest создает группу пользователей
// @UserDTO -> трансфер пользовательских данных {id}
func CreateGroupRequest() {
}
// FlushGroupRequest сохраняет результат вычислений группы
// @GroupEntity : WS entity -> сущность группы с данными и вычислениями
// @IsUserRequest : boolean -> если пользовательский запрос - сейвим с именем группы, иначе создаем произвольную запись с Username<UserId:Creator> + timestamp
func FlushGroupRequest() {
}
// ObserveGroupHandler проверяет массив групп пользователей и если в группе нет активных пользователей - закрываем соединение
// Запускать в горутине после старта сервера!
func ObserveGroupHandler() {
}