Normalize base address in Node class to ensure consistent URI formatting
- Implemented `NormalizeBaseAddress` method to add a trailing slash to the `BaseAddress` when necessary, preventing issues with relative path merging in 3x-ui deployments. - Updated `Register` and `UpdateAddress` methods to utilize the normalization logic, ensuring that both methods handle base address input consistently. - Added unit tests to verify the normalization behavior for various input scenarios, enhancing the reliability of node address management. - Updated domain model documentation to reflect the new normalization behavior for `BaseAddress`.
This commit is contained in:
@@ -34,7 +34,7 @@ public sealed class Node : Entity
|
|||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Name = name,
|
Name = name,
|
||||||
BaseAddress = baseAddress,
|
BaseAddress = NormalizeBaseAddress(baseAddress),
|
||||||
Credentials = credentials,
|
Credentials = credentials,
|
||||||
Location = location,
|
Location = location,
|
||||||
Status = NodeStatus.Unknown,
|
Status = NodeStatus.Unknown,
|
||||||
@@ -54,9 +54,18 @@ public sealed class Node : Entity
|
|||||||
if (!baseAddress.IsAbsoluteUri)
|
if (!baseAddress.IsAbsoluteUri)
|
||||||
throw new DomainException("Адрес ноды должен быть абсолютным URI.");
|
throw new DomainException("Адрес ноды должен быть абсолютным URI.");
|
||||||
|
|
||||||
BaseAddress = baseAddress;
|
BaseAddress = NormalizeBaseAddress(baseAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>3x-ui часто разворачивается за нестандартным base path (`webBasePath`, напр.
|
||||||
|
/// `/benis`) — без завершающего слэша относительное объединение пути (RFC 3986 merge) отбрасывает
|
||||||
|
/// последний сегмент базы вместо добавления к нему, роняя `/benis` из итогового URL. Нормализуем
|
||||||
|
/// один раз при сохранении, чтобы админ мог вводить адрес и с завершающим слэшем, и без него.</summary>
|
||||||
|
private static Uri NormalizeBaseAddress(Uri baseAddress) =>
|
||||||
|
baseAddress.AbsolutePath.EndsWith('/')
|
||||||
|
? baseAddress
|
||||||
|
: new UriBuilder(baseAddress) { Path = baseAddress.AbsolutePath + "/" }.Uri;
|
||||||
|
|
||||||
public void UpdateCredentials(NodeCredentials credentials) => Credentials = credentials;
|
public void UpdateCredentials(NodeCredentials credentials) => Credentials = credentials;
|
||||||
|
|
||||||
public void Enable() => IsEnabled = true;
|
public void Enable() => IsEnabled = true;
|
||||||
|
|||||||
@@ -35,6 +35,29 @@ public class NodeTests
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("https://host.example.com/benis", "https://host.example.com/benis/")]
|
||||||
|
[InlineData("https://host.example.com/benis/", "https://host.example.com/benis/")]
|
||||||
|
[InlineData("https://host.example.com", "https://host.example.com/")]
|
||||||
|
public void Register_NormalizesBaseAddressToTrailingSlash(string input, string expected)
|
||||||
|
{
|
||||||
|
var node = Node.Register("Node", new Uri(input), Credentials, null);
|
||||||
|
|
||||||
|
Assert.Equal(expected, node.BaseAddress.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("https://host.example.com/benis", "https://host.example.com/benis/")]
|
||||||
|
[InlineData("https://host.example.com/benis/", "https://host.example.com/benis/")]
|
||||||
|
public void UpdateAddress_NormalizesBaseAddressToTrailingSlash(string input, string expected)
|
||||||
|
{
|
||||||
|
var node = Node.Register("Node", new Uri("https://old.example.com"), Credentials, null);
|
||||||
|
|
||||||
|
node.UpdateAddress(new Uri(input));
|
||||||
|
|
||||||
|
Assert.Equal(expected, node.BaseAddress.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UpdateDetails_ChangesNameAndLocation()
|
public void UpdateDetails_ChangesNameAndLocation()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -55,6 +55,13 @@ AppUser
|
|||||||
конфиги на ноде запрещены, но **существующие не трогаем** (клиенты остаются в 3x-ui). Статус ноды
|
конфиги на ноде запрещены, но **существующие не трогаем** (клиенты остаются в 3x-ui). Статус ноды
|
||||||
показываем пользователю как индикатор «состояние сервера».
|
показываем пользователю как индикатор «состояние сервера».
|
||||||
|
|
||||||
|
`BaseAddress` **нормализуется к завершающему `/`** (`Register`/`UpdateAddress`) — 3x-ui часто стоит за
|
||||||
|
нестандартным base path (`webBasePath`, напр. `https://host/benis`, панель тогда доступна по
|
||||||
|
`/benis/panel/...`); без завершающего слэша относительное объединение пути (RFC 3986 merge) отбрасывает
|
||||||
|
последний сегмент базы вместо добавления к нему, роняя `/benis` из итогового URL при запросах к панели.
|
||||||
|
Нормализация — единственная защита от этого на уровне PnvPanel; админ может вводить адрес и со слэшем,
|
||||||
|
и без него — результат одинаковый.
|
||||||
|
|
||||||
### Inbound — прокси-inbound на ноде
|
### Inbound — прокси-inbound на ноде
|
||||||
Проекция inbound из 3x-ui; определяет протокол и параметры подключения.
|
Проекция inbound из 3x-ui; определяет протокол и параметры подключения.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user