fix errors

This commit is contained in:
Den Piligrim
2026-02-03 10:34:24 +03:00
parent 10208930ff
commit 007afd3b83
8 changed files with 113 additions and 17 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ export default function Footer() {
<IconButton
component="a"
href="https://github.com/denpiligrim"
href="https://github.com/denpiligrim/3dp-manager"
target="_blank"
aria-label="GitHub"
color="inherit"
+69 -7
View File
@@ -22,7 +22,8 @@ export default function SettingsPage() {
});
const [msg, setMsg] = useState({ open: false, type: 'success' as 'success' | 'error', text: '' });
const [intervalError, setIntervalError] = useState('');
const [intervalError, setIntervalError] = useState<string>('');
const [loadingRotate, setLoadingRotate] = useState<boolean>(false);
useEffect(() => {
loadSettings();
@@ -37,6 +38,42 @@ export default function SettingsPage() {
}
}, [settings.rotation_interval]);
const cleanData = () => {
const cleaned = { ...settings };
if (cleaned.xui_url) {
cleaned.xui_url = cleaned.xui_url.replace(/\/+$/, '');
}
if (cleaned.xui_login) cleaned.xui_login = cleaned.xui_login.trim();
if (cleaned.xui_password) cleaned.xui_password = cleaned.xui_password.trim();
setSettings(prev => ({ ...prev, ...cleaned }));
return cleaned;
};
const handleCheckConnection = async () => {
const data = cleanData(); // Сначала чистим
try {
setMsg({ open: true, type: 'success', text: 'Проверка...' });
const res = await api.post('/settings/check', {
xui_url: data.xui_url,
xui_login: data.xui_login,
xui_password: data.xui_password
});
if (res.data.success) {
setMsg({ open: true, type: 'success', text: 'Подключение успешно!' });
} else {
setMsg({ open: true, type: 'error', text: 'Ошибка: Неверные данные или нет доступа' });
}
} catch (e) {
setMsg({ open: true, type: 'error', text: 'Ошибка сети при проверке' });
}
};
const loadSettings = async () => {
try {
const { data } = await api.get('/settings');
@@ -64,8 +101,10 @@ export default function SettingsPage() {
return;
}
const data = cleanData();
try {
await api.post('/settings', settings);
await api.post('/settings', data);
setMsg({ open: true, type: 'success', text: 'Настройки сохранены!' });
} catch (e) {
setMsg({ open: true, type: 'error', text: 'Ошибка сохранения' });
@@ -86,13 +125,25 @@ export default function SettingsPage() {
}
};
const handleForceRotate = async () => {
const handleForceRotate = async () => {
if (confirm('ВНИМАНИЕ: Это немедленно обновит конфиги в подписках.\n\nИнтервал автоматической ротации НЕ будет сброшен.\n\nПродолжить?')) {
try {
await api.post('/rotation/rotate-all');
setMsg({ open: true, type: 'success', text: 'Ротация успешно выполнена!' });
setLoadingRotate(true);
const res = await api.post('/rotation/rotate-all');
setLoadingRotate(false);
if (res.data && res.data.success) {
setMsg({ open: true, type: 'success', text: res.data.message || 'Ротация успешно выполнена!' });
} else {
setMsg({
open: true,
type: 'error',
text: res.data?.message || 'Ошибка выполнения ротации'
});
}
} catch (e) {
setMsg({ open: true, type: 'error', text: 'Ошибка при запуске ротации' });
setLoadingRotate(false);
setMsg({ open: true, type: 'error', text: 'Ошибка сети или сервера' });
}
}
};
@@ -111,7 +162,7 @@ export default function SettingsPage() {
<TextField
fullWidth margin="normal" label="URL панели"
value={settings.xui_url} onChange={handleSettingChange('xui_url')}
helperText="Например: https://my-vpn.com:2053/panel_path"
helperText="Например: https://my-vpn.com:2053/wfgpoVHaOF"
/>
<TextField
fullWidth margin="normal" label="Логин 3x-ui"
@@ -125,6 +176,16 @@ export default function SettingsPage() {
<Button variant="contained" sx={{ mt: 2 }} onClick={handleSaveSettings}>
Сохранить подключение
</Button>
{settings.xui_url && settings.xui_login && settings.xui_password && (
<Button
variant="outlined"
color="info"
sx={{ mt: 2, ml: 2 }}
onClick={handleCheckConnection}
>
Проверить
</Button>
)}
</Paper>
</Grid>
@@ -162,6 +223,7 @@ export default function SettingsPage() {
</Button>
<Button
variant="outlined"
loading={loadingRotate}
color="warning"
onClick={handleForceRotate}
sx={{ mt: 2, ml: 2 }}
+4 -4
View File
@@ -251,7 +251,7 @@ server {
}
location /api/ {
proxy_pass http://backend:3000/api/;
proxy_set_header Host \$host;
proxy_set_header Host \$http_host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
}
@@ -266,7 +266,7 @@ server {
location / {
proxy_pass http://backend:3000/;
proxy_set_header Host \$host;
proxy_set_header Host \$http_host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
@@ -356,7 +356,7 @@ server {
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_set_header Host \$http_host;
proxy_cache_bypass \$http_upgrade;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
@@ -367,7 +367,7 @@ server {
server_name localhost;
location / {
proxy_pass http://backend:3000/;
proxy_set_header Host \$host;
proxy_set_header Host \$http_host;
}
}
EOF
+6 -3
View File
@@ -58,16 +58,18 @@ export class RotationService {
const isLoginSuccess = await this.xuiService.login();
if (!isLoginSuccess) {
this.logger.error('Отмена ротации: Не удалось войти в панель 3x-ui');
return;
return { success: false, message: 'Не удалось войти в панель 3x-ui' };
}
const subscriptions = await this.subRepo.find({ where: { isEnabled: true }, relations: ['inbounds'] });
if (subscriptions.length === 0) return;
if (subscriptions.length === 0) {
return { success: false, message: 'Нет активных подписок для ротации' };
}
const domains = await this.domainRepo.find({ where: { isEnabled: true } });
if (domains.length === 0) {
this.logger.warn('Список доменов пуст! Ротация невозможна.');
return;
return { success: false, message: 'Список доменов пуст!' };
}
for (const sub of subscriptions) {
@@ -75,6 +77,7 @@ export class RotationService {
}
this.logger.log('Ротация завершена.');
return { success: true, message: 'Ротация успешно выполнена' };
}
private async rotateSubscription(sub: Subscription, domains: Domain[]) {
@@ -4,12 +4,14 @@ import { Repository } from 'typeorm';
import { Setting } from './entities/setting.entity';
import * as dns from 'dns/promises';
import { COUNTRIES } from './countries';
import { XuiService } from 'src/xui/xui.service';
@Controller('settings')
export class SettingsController {
constructor(
@InjectRepository(Setting)
private settingsRepo: Repository<Setting>,
private xuiService: XuiService
) {}
@Get()
@@ -18,6 +20,12 @@ export class SettingsController {
return settings.reduce((acc, curr) => ({ ...acc, [curr.key]: curr.value }), {});
}
@Post('check')
async checkConnection(@Body() body: { xui_url: string; xui_login: string; xui_password: string }) {
const success = await this.xuiService.checkConnection(body.xui_url, body.xui_login, body.xui_password);
return { success };
}
@Post()
async update(@Body() settings: Record<string, string>) {
if (settings.xui_url) {
+2 -1
View File
@@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { SettingsController } from './settings.controller';
import { Setting } from './entities/setting.entity';
import { XuiModule } from 'src/xui/xui.module';
@Module({
imports: [TypeOrmModule.forFeature([Setting])],
imports: [TypeOrmModule.forFeature([Setting]), XuiModule],
controllers: [SettingsController],
})
export class SettingsModule {}
@@ -11,7 +11,6 @@ export class SubscriptionsService {
constructor(
@InjectRepository(Subscription)
private subRepo: Repository<Subscription>,
@InjectRepository(Inbound)
private xuiService: XuiService,
) {}
+23
View File
@@ -119,6 +119,29 @@ export class XuiService {
}
}
async checkConnection(url: string, username: string, pass: string): Promise<boolean> {
try {
const tempApi = axios.create({
baseURL: url,
timeout: 5000,
httpsAgent: new https.Agent({ rejectUnauthorized: false }),
withCredentials: true
});
const res = await tempApi.post('/login', {
username: username,
password: pass,
});
if (res.headers['set-cookie'] && res.data?.success) {
return true;
}
} catch (e) {
this.logger.warn(`Ошибка авторизации: ${e.message}`);
}
return false;
}
async getNewX25519Cert() {
try {
const res = await this.api.get('/panel/api/server/getNewX25519Cert');