fix errors
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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,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,
|
||||
) {}
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user