Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b5ad11185 | |||
| 2e1cd86e99 | |||
| 97a8c41fc0 | |||
| b2cc357dca | |||
| fc8fbc976f |
@@ -83,6 +83,7 @@ interface InboundConfigUI {
|
||||
name?: string;
|
||||
certificateFile?: string;
|
||||
keyFile?: string;
|
||||
routingProfile?: string;
|
||||
}
|
||||
|
||||
interface Domain {
|
||||
@@ -106,6 +107,7 @@ const CONNECTION_OPTIONS = [
|
||||
'shadowsocks-tcp',
|
||||
'trojan-tcp-reality',
|
||||
'custom',
|
||||
'happ-routing',
|
||||
];
|
||||
|
||||
const generateId = () => Math.random().toString(36).substring(7);
|
||||
@@ -221,6 +223,19 @@ export default function SubscriptionsPage() {
|
||||
|
||||
const createInbound = (type = 'vless-tcp-reality'): InboundConfigUI => {
|
||||
const nodeId = getDefaultNodeId();
|
||||
|
||||
// HAPP ROUTING - специальная обработка
|
||||
if (type === 'happ-routing') {
|
||||
return {
|
||||
id: generateId(),
|
||||
type,
|
||||
port: '0',
|
||||
sni: '',
|
||||
link: '',
|
||||
routingProfile: '',
|
||||
};
|
||||
}
|
||||
|
||||
const certDefaults = type === 'hysteria2-udp' ? getHysteriaCertDefaults(nodeId) : {};
|
||||
return {
|
||||
id: generateId(),
|
||||
@@ -280,6 +295,7 @@ export default function SubscriptionsPage() {
|
||||
relayServerId: item.relayServerId ? item.relayServerId.toString() : '',
|
||||
flag: item.flag || getNodeFlag(nodeId),
|
||||
name: item.name || '',
|
||||
routingProfile: item.routingProfile || '',
|
||||
certificateFile:
|
||||
item.type === 'hysteria2-udp'
|
||||
? item.certificateFile || certDefaults.certificateFile
|
||||
@@ -323,7 +339,7 @@ export default function SubscriptionsPage() {
|
||||
next.keyFile = next.keyFile || defaults.keyFile;
|
||||
}
|
||||
|
||||
if (field === 'type' && value !== 'custom' && !next.nodeId) {
|
||||
if (field === 'type' && value !== 'custom' && value !== 'happ-routing' && !next.nodeId) {
|
||||
next.nodeId = getDefaultNodeId();
|
||||
next.flag = getNodeFlag(next.nodeId);
|
||||
}
|
||||
@@ -363,7 +379,7 @@ export default function SubscriptionsPage() {
|
||||
|
||||
const handleSave = async () => {
|
||||
const nextPortErrors = inbounds.reduce<Record<string, string>>((acc, inbound) => {
|
||||
if (inbound.type !== 'custom' && !isValidPort(inbound.port)) {
|
||||
if (inbound.type !== 'custom' && inbound.type !== 'happ-routing' && !isValidPort(inbound.port)) {
|
||||
acc[inbound.id] = 'Порт: число 1-65535 или random';
|
||||
}
|
||||
return acc;
|
||||
@@ -385,10 +401,17 @@ export default function SubscriptionsPage() {
|
||||
|
||||
const payload = {
|
||||
name,
|
||||
inboundsConfig: inbounds.map((inbound) =>
|
||||
inbound.type === 'custom'
|
||||
? { type: inbound.type, link: inbound.link }
|
||||
: {
|
||||
inboundsConfig: inbounds.map((inbound) => {
|
||||
if (inbound.type === 'custom') {
|
||||
return { type: inbound.type, link: inbound.link };
|
||||
}
|
||||
if (inbound.type === 'happ-routing') {
|
||||
return {
|
||||
type: inbound.type,
|
||||
routingProfile: inbound.routingProfile,
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: inbound.type,
|
||||
port: inbound.port === 'random' ? 'random' : parseInt(inbound.port, 10),
|
||||
sni: inbound.type === 'hysteria2-udp' ? undefined : inbound.sni,
|
||||
@@ -406,8 +429,8 @@ export default function SubscriptionsPage() {
|
||||
inbound.type === 'hysteria2-udp'
|
||||
? inbound.keyFile?.trim() || undefined
|
||||
: undefined,
|
||||
},
|
||||
),
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -735,6 +758,18 @@ export default function SubscriptionsPage() {
|
||||
</FormControl>
|
||||
{inbound.type === 'custom' ? (
|
||||
<TextField size="small" label="Ссылка на подключение" placeholder="vless://..." value={inbound.link || ''} onChange={(e) => handleInboundChange(inbound.id, 'link', e.target.value)} sx={{ width: 460, flexShrink: 0 }} />
|
||||
) : inbound.type === 'happ-routing' ? (
|
||||
<TextField
|
||||
size="small"
|
||||
label="Routing профиль (JSON или base64)"
|
||||
placeholder='{"Name":"MyProfile","GlobalProxy":"true","DirectSites":["geosite:cn"]}'
|
||||
value={inbound.routingProfile || ''}
|
||||
onChange={(e) => handleInboundChange(inbound.id, 'routingProfile', e.target.value)}
|
||||
multiline
|
||||
rows={4}
|
||||
sx={{ width: 460, flexShrink: 0 }}
|
||||
helperText="Вставьте JSON профиль или готовую ссылку happ://routing/..."
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<FormControl size="small" sx={{ width: 170, flexShrink: 0 }}>
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@ set -euo pipefail
|
||||
# КОНФИГУРАЦИЯ И ПЕРЕМЕННЫЕ
|
||||
#################################
|
||||
PROJECT_DIR="/opt/3dp-manager"
|
||||
DOCKER_USER="denpiligrim"
|
||||
DOCKER_TAG="main"
|
||||
DOCKER_USER="zangetsuka"
|
||||
DOCKER_TAG="feature-happ-routing"
|
||||
IMAGE_SERVER="ghcr.io/${DOCKER_USER}/3dp-manager-server:${DOCKER_TAG}"
|
||||
IMAGE_CLIENT="ghcr.io/${DOCKER_USER}/3dp-manager-client:${DOCKER_TAG}"
|
||||
|
||||
|
||||
@@ -257,6 +257,7 @@ export class RotationService implements OnModuleInit {
|
||||
sni = config.sni === 'random' ? this.pickDomain(domains) : config.sni;
|
||||
}
|
||||
|
||||
|
||||
// === 2. Обработка Hysteria2 ===
|
||||
if (type === 'hysteria2-udp') {
|
||||
let port = 0;
|
||||
|
||||
@@ -82,6 +82,11 @@ export class InboundConfigDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
keyFile?: string;
|
||||
|
||||
// ⬇️ НОВОЕ ПОЛЕ ДЛЯ HAPP ROUTING ⬇️
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
routingProfile?: string;
|
||||
}
|
||||
|
||||
export class CreateSubscriptionDto {
|
||||
|
||||
@@ -40,6 +40,7 @@ export class Subscription {
|
||||
name?: string;
|
||||
certificateFile?: string;
|
||||
keyFile?: string;
|
||||
routingProfile?: string;
|
||||
}>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
|
||||
Reference in New Issue
Block a user