Enhance VpnConfigDto to include ClientEmail and update related components
- Added ClientEmail property to VpnConfigDto for better client identification. - Updated ConfigCard to display the ClientEmail in the UI for improved user information. - Modified API schema and types to accommodate the new ClientEmail field. - Enhanced localization for displaying the ClientEmail in both Russian and English.
This commit is contained in:
@@ -5,13 +5,15 @@ namespace PnvPanel.Application.Configs;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Пользователю показываем только DisplayName + протокол инбаунда — адрес/хост ноды и прочие
|
/// Пользователю показываем только DisplayName + протокол инбаунда — адрес/хост ноды и прочие
|
||||||
/// детали 3x-ui в этот DTO не попадают (см. domain-model.md).
|
/// детали 3x-ui в этот DTO не попадают (см. domain-model.md). Исключение — ClientEmail: это не
|
||||||
|
/// секрет и не адрес ноды, а просто ключ клиента в 3x-ui, полезный владельцу для диагностики
|
||||||
|
/// (сверка конфига с записью в панели у админа).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed record VpnConfigDto(
|
public sealed record VpnConfigDto(
|
||||||
Guid Id, string? Label, VpnProtocol Protocol, string Location,
|
Guid Id, string? Label, string ClientEmail, VpnProtocol Protocol, string Location,
|
||||||
long UsedUpBytes, long UsedDownBytes, DateTimeOffset? ExpiresAt, ConfigStatus Status, DateTimeOffset CreatedAt)
|
long UsedUpBytes, long UsedDownBytes, DateTimeOffset? ExpiresAt, ConfigStatus Status, DateTimeOffset CreatedAt)
|
||||||
{
|
{
|
||||||
public static VpnConfigDto FromDomain(VpnConfig config, Inbound inbound) => new(
|
public static VpnConfigDto FromDomain(VpnConfig config, Inbound inbound) => new(
|
||||||
config.Id, config.Label, config.Protocol, inbound.DisplayName ?? inbound.Remark,
|
config.Id, config.Label, config.ClientEmail, config.Protocol, inbound.DisplayName ?? inbound.Remark,
|
||||||
config.UsedUpBytes, config.UsedDownBytes, config.ExpiresAt, config.Status, config.CreatedAt);
|
config.UsedUpBytes, config.UsedDownBytes, config.ExpiresAt, config.Status, config.CreatedAt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ export function ConfigCard({ config }: { config: VpnConfigDto }) {
|
|||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
↑ {formatBytes(config.usedUpBytes)} ↓ {formatBytes(config.usedDownBytes)}
|
↑ {formatBytes(config.usedUpBytes)} ↓ {formatBytes(config.usedDownBytes)}
|
||||||
</div>
|
</div>
|
||||||
|
<p className="truncate font-mono text-xs text-muted-foreground/60">
|
||||||
|
{t('configs.panelEmail', { email: config.clientEmail })}
|
||||||
|
</p>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
<Button size="sm" variant="outline" onClick={() => setDetailsOpen(true)}>
|
<Button size="sm" variant="outline" onClick={() => setDetailsOpen(true)}>
|
||||||
<QrCode className="h-4 w-4" />
|
<QrCode className="h-4 w-4" />
|
||||||
|
|||||||
@@ -2071,6 +2071,7 @@ export interface components {
|
|||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
id: string;
|
id: string;
|
||||||
label: null | string;
|
label: null | string;
|
||||||
|
clientEmail: string;
|
||||||
protocol: components["schemas"]["VpnProtocol"];
|
protocol: components["schemas"]["VpnProtocol"];
|
||||||
location: string;
|
location: string;
|
||||||
/** Format: int64 */
|
/** Format: int64 */
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export type ActivationStatusDto = {
|
|||||||
export type VpnConfigDto = {
|
export type VpnConfigDto = {
|
||||||
id: string
|
id: string
|
||||||
label: string | null
|
label: string | null
|
||||||
|
clientEmail: string
|
||||||
protocol: VpnProtocol
|
protocol: VpnProtocol
|
||||||
location: string
|
location: string
|
||||||
usedUpBytes: number
|
usedUpBytes: number
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ const resources = {
|
|||||||
copyLink: 'Скопировать ссылку',
|
copyLink: 'Скопировать ссылку',
|
||||||
loadingLink: 'Загрузка ссылки…',
|
loadingLink: 'Загрузка ссылки…',
|
||||||
subscriptionLink: 'Ссылка-подписка (для клиента):',
|
subscriptionLink: 'Ссылка-подписка (для клиента):',
|
||||||
|
panelEmail: 'Email в панели: {{email}}',
|
||||||
aggregatedSubscription: 'Общая подписка',
|
aggregatedSubscription: 'Общая подписка',
|
||||||
aggregatedSubscriptionHint: 'Одна ссылка/QR со всеми активными конфигами — удобно добавить один раз в клиент.',
|
aggregatedSubscriptionHint: 'Одна ссылка/QR со всеми активными конфигами — удобно добавить один раз в клиент.',
|
||||||
status: {
|
status: {
|
||||||
@@ -379,6 +380,7 @@ const resources = {
|
|||||||
copyLink: 'Copy link',
|
copyLink: 'Copy link',
|
||||||
loadingLink: 'Loading link…',
|
loadingLink: 'Loading link…',
|
||||||
subscriptionLink: 'Subscription link (for the client app):',
|
subscriptionLink: 'Subscription link (for the client app):',
|
||||||
|
panelEmail: 'Panel email: {{email}}',
|
||||||
aggregatedSubscription: 'Aggregated subscription',
|
aggregatedSubscription: 'Aggregated subscription',
|
||||||
aggregatedSubscriptionHint: 'One link/QR with all active configs — add it once to your client.',
|
aggregatedSubscriptionHint: 'One link/QR with all active configs — add it once to your client.',
|
||||||
status: {
|
status: {
|
||||||
|
|||||||
Reference in New Issue
Block a user