diff --git a/README.md b/README.md
index 97edac9..7e65106 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@

- [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
# 3DP-MANAGER
@@ -11,23 +11,23 @@
**Поддержать проект**
- Банковским переводом:
- - Карта МИР: `2204320436318077`
- - Карта MasterCard: `5395452209474530`
+ - :credit_card: Карта МИР: `2204320436318077`
+ - :credit_card: Карта MasterCard: `5395452209474530`
- На электронный кошелек:
- - ЮМоney: `4100116897060652`
- - PayPal: `vasiljevdenisx@gmail.com`
+ - :moneybag: ЮМоney: `4100116897060652`
+ - :moneybag: PayPal: `vasiljevdenisx@gmail.com`
- Криптовалютой:
- - USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
- - USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
- - Bitcoin: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
- - TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
- - Bybit ID: `165292278`
+ - :coin: USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
+ - :coin: USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
+ - :coin: Bitcoin: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
+ - :coin: TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
+ - :coin: Bybit ID: `165292278`
## Описание
Главная цель утилиты — сделать так, чтобы ваш трафик не выглядел одинаковым. Бот генерирует по заданному интервалу 10 подключений с разными параметрами:
-- протоколы: `vless`, `vmess`, `shadowsocks`, `trojan`;
+- протоколы: `vless`, `vmess`, `shadowsocks`, `hysteria2`, `trojan`;
- порты: `443`, `8443` (фиксированные) и случайные из диапазона `10000-60000`;
- транспорт: `tcp`, `websocket`, `grpc`, `xhttp`;
- SNI берутся из белого списка доменов (whitelist); можно использовать свой список.
diff --git a/README_CN.md b/README_CN.md
index c30994e..8f708cb 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -2,7 +2,7 @@

- [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
# 3DP-MANAGER
diff --git a/README_EN.md b/README_EN.md
index c51f854..7694c6a 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -2,7 +2,7 @@

- [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
# 3DP-MANAGER
diff --git a/README_IR.md b/README_IR.md
index b11341b..0eb951a 100644
--- a/README_IR.md
+++ b/README_IR.md
@@ -2,7 +2,7 @@

- [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
# 3DP-MANAGER
diff --git a/README_TK.md b/README_TK.md
index e4d2dce..0685712 100644
--- a/README_TK.md
+++ b/README_TK.md
@@ -2,7 +2,7 @@

- [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
# 3DP-MANAGER
diff --git a/client/src/components/Footer.tsx b/client/src/components/Footer.tsx
index 0b2fc2f..9d6c805 100644
--- a/client/src/components/Footer.tsx
+++ b/client/src/components/Footer.tsx
@@ -1,7 +1,11 @@
import { Box, Container, Grid, IconButton, Link, Stack } from '@mui/material';
import { GitHub, YouTube, Telegram } from '@mui/icons-material';
-export default function Footer() {
+interface FooterProps {
+ isMobile?: boolean;
+}
+
+export default function Footer({ isMobile }: FooterProps) {
return (
-
-
+
+
-
+
-
+
void;
+ isMobile?: boolean;
+}
+
+export default function Header({ onMenuClick, isMobile }: HeaderProps) {
const { mode, toggleColorMode } = useThemeContext();
const { logout } = useAuth();
const navigate = useNavigate();
@@ -48,12 +54,17 @@ export default function Header() {
sx={{ zIndex: (theme) => theme.zIndex.drawer + 1 }}
>
+ {isMobile && (
+
+
+
+ )}
-
+
3DP-MANAGER
-
+
setHelpOpen(true)}>
@@ -121,7 +132,7 @@ export default function Header() {
- Версия: 2.0.1
+ Версия: 2.0.2
Разработчик: DenPiligrim
diff --git a/client/src/components/Layout.tsx b/client/src/components/Layout.tsx
index c60aa77..8ce644a 100644
--- a/client/src/components/Layout.tsx
+++ b/client/src/components/Layout.tsx
@@ -1,9 +1,10 @@
import {
Toolbar, Drawer, List, ListItem,
- ListItemButton, ListItemIcon, ListItemText, Box
+ ListItemButton, ListItemIcon, ListItemText, Box, useMediaQuery, useTheme
} from '@mui/material';
import { People, Settings, Dns, SwapHoriz } from '@mui/icons-material';
import { useNavigate, useLocation, Outlet } from 'react-router-dom';
+import { useState } from 'react';
import Header from './Header';
import Footer from './Footer';
@@ -13,6 +14,13 @@ const drawerWidth = 240;
export default function Layout() {
const navigate = useNavigate();
const location = useLocation();
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'));
+ const [mobileOpen, setMobileOpen] = useState(false);
+
+ const handleDrawerToggle = () => {
+ setMobileOpen(!mobileOpen);
+ };
const menuItems = [
{ text: 'Подписки', icon: , path: '/' },
@@ -21,35 +29,44 @@ export default function Layout() {
{ text: 'Настройки', icon: , path: '/settings' },
];
+ const drawerContent = (
+
+
+
+ {menuItems.map((item) => (
+
+ {
+ navigate(item.path);
+ if (isMobile) setMobileOpen(false);
+ }}
+ >
+ {item.icon}
+
+
+
+ ))}
+
+
+ );
+
return (
-
-
+ {/* Передаем функцию открытия в Header */}
+
-
-
-
- {menuItems.map((item) => (
-
- navigate(item.path)}
- >
- {item.icon}
-
-
-
- ))}
-
-
+ {drawerContent}
-
-
+
-
-
+
);
diff --git a/client/src/index.css b/client/src/index.css
index e69de29..108ad08 100644
--- a/client/src/index.css
+++ b/client/src/index.css
@@ -0,0 +1,35 @@
+.animated-container {
+ background: linear-gradient(-45deg, #111827, #15122c, #24243e, #0B0F19);
+ background-size: 400% 400%;
+ animation: gradientBG 15s ease infinite;
+}
+
+@keyframes gradientBG {
+ 0% {
+ background-position: 0% 50%;
+ }
+
+ 50% {
+ background-position: 100% 50%;
+ }
+
+ 100% {
+ background-position: 0% 50%;
+ }
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+input:autofill {
+ background: transparent;
+}
\ No newline at end of file
diff --git a/client/src/pages/DomainsPage.tsx b/client/src/pages/DomainsPage.tsx
index 7c5e456..30cd5bf 100644
--- a/client/src/pages/DomainsPage.tsx
+++ b/client/src/pages/DomainsPage.tsx
@@ -1,5 +1,5 @@
import React, { useEffect, useRef, useState } from 'react';
-import { Box, TextField, Button, Typography, List, ListItem, ListItemText, IconButton, Paper, TablePagination } from '@mui/material';
+import { Box, TextField, Button, Typography, List, ListItem, ListItemText, IconButton, Paper, TablePagination, useTheme, useMediaQuery } from '@mui/material';
import { Delete, Add, UploadFile, Remove } from '@mui/icons-material';
import api from '../api';
@@ -10,6 +10,8 @@ export default function DomainsPage() {
const [newDomain, setNewDomain] = useState('');
const fileInputRef = useRef(null);
const [totalCount, setTotalCount] = useState(0);
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
@@ -52,12 +54,10 @@ export default function DomainsPage() {
const handleDeleteAll = async () => {
if (confirm('ВНИМАНИЕ! Вы действительно хотите удалить ВСЕ домены из белого списка?')) {
- if (confirm('Это действие необратимо. Точно удалить?')) {
- try {
- await api.delete('/domains/all');
- loadDomains();
- } catch (_e) { alert('Ошибка удаления'); }
- }
+ try {
+ await api.delete('/domains/all');
+ loadDomains();
+ } catch (_e) { alert('Ошибка удаления'); }
}
};
@@ -87,21 +87,31 @@ export default function DomainsPage() {
return (
- Белый список доменов (SNI)
+ Белый список доменов (SNI)
setNewDomain(e.target.value)}
/>
- }
- sx={{ width: '170px' }}
- onClick={() => fileInputRef.current?.click()}
- >
- Из файла
-
+ {isMobile ? (
+ <>
+ fileInputRef.current?.click()}>
+
+ >
+ ) : (
+ <>
+ }
+ sx={{ width: isMobile ? 'auto' : '170px' }}
+ onClick={() => fileInputRef.current?.click()}
+ >
+ {isMobile ? '' : 'Из файла'}
+
+ } onClick={handleAdd}>Добавить
+ >
+ )}
- } onClick={handleAdd}>Добавить
{domains.length > 0 && (
@@ -124,10 +133,9 @@ export default function DomainsPage() {
Удалить все
-
)}
-
+ 0 ? 0 : 3 }}>
{domains.map((d) => (
))}
- {domains.length === 0 && Список пуст}
+ {domains.length === 0 && Нет доменов}
-
- Вход в 3DP-MANAGER
+
+ Вход в 3DP-MANAGER
+
{error && {error}}
@@ -40,7 +49,7 @@ export default function LoginPage() {
fullWidth margin="normal" label="Пароль" type="password"
value={creds.password} onChange={(e) => setCreds({ ...creds, password: e.target.value })}
/>
-
- } onClick={() => setOpen(true)}>Создать
+ } onClick={handleOpenCreate}>Создать
-
-
+
@@ -164,53 +307,187 @@ export default function SubscriptionsPage() {
{sub.uuid}
{sub.inbounds?.length || 0}
- navigator.clipboard.writeText(selectedServer === 'main' ? `${location.protocol}//${location.hostname}:3000/bus/${sub.uuid}` : `${location.protocol}//${location.hostname}:3000/bus/${sub.uuid}/${selectedServer}`)}
- title="Копировать ссылку"
- >
-
-
- window.open(selectedServer === 'main' ? `${location.protocol}//${location.hostname}:3000/bus/${sub.uuid}` : `${location.protocol}//${location.hostname}:3000/bus/${sub.uuid}/${selectedServer}`, '_blank')}
- title="Открыть подписку"
- >
-
-
- showLinks(sub)} title="Показать конфиги">
-
-
- handleDelete(sub.id)} title="Удалить">
-
+ {!isMobile && (
+ <>
+ navigator.clipboard.writeText(selectedServer === 'main' ? `${location.protocol}//${location.hostname}:3000/bus/${sub.uuid}` : `${location.protocol}//${location.hostname}:3000/bus/${sub.uuid}/${selectedServer}`)}
+ title="Копировать ссылку"
+ >
+
+
+ window.open(selectedServer === 'main' ? `${location.protocol}//${location.hostname}:3000/bus/${sub.uuid}` : `${location.protocol}//${location.hostname}:3000/bus/${sub.uuid}/${selectedServer}`, '_blank')}
+ title="Открыть подписку"
+ >
+
+
+ >
+ )}
+
+ {/* Кнопка "Три точки" для вызова меню действий */}
+ handleActionMenuClick(e, sub)}>
+
))}
+ {subs.length === 0 && Нет подписок}
-