feat(group): add node group management UI
Add node group management interface with automatic user assignment and traffic-based grouping. Features: - Node group CRUD with traffic range configuration - Three grouping modes: average, subscription-based, and traffic-based - Group recalculation with preview and history tracking - Subscribe-to-group mapping management - User subscription group locking - Group calculation history with detailed reports - Multi-language support (en-US, zh-CN) - Enhanced node and subscription forms with group selection
This commit is contained in:
+278
-5
@@ -103,6 +103,7 @@ declare namespace API {
|
||||
longitude: string;
|
||||
created_at: number;
|
||||
download: number;
|
||||
port: number;
|
||||
};
|
||||
|
||||
type AuthConfig = {
|
||||
@@ -534,7 +535,7 @@ declare namespace API {
|
||||
};
|
||||
|
||||
type DeleteUserSubscribeRequest = {
|
||||
user_subscribe_id: number;
|
||||
user_subscribe_id: string;
|
||||
};
|
||||
|
||||
type DeviceAuthticateConfig = {
|
||||
@@ -702,12 +703,14 @@ declare namespace API {
|
||||
page: number;
|
||||
size: number;
|
||||
search?: string;
|
||||
node_group_id?: number;
|
||||
};
|
||||
|
||||
type FilterNodeListRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
search?: string;
|
||||
node_group_id?: number;
|
||||
};
|
||||
|
||||
type FilterNodeListResponse = {
|
||||
@@ -1147,6 +1150,7 @@ declare namespace API {
|
||||
size: number;
|
||||
language?: string;
|
||||
search?: string;
|
||||
node_group_id?: number;
|
||||
};
|
||||
|
||||
type GetSubscribeListRequest = {
|
||||
@@ -1154,6 +1158,7 @@ declare namespace API {
|
||||
size: number;
|
||||
language?: string;
|
||||
search?: string;
|
||||
node_group_id?: number;
|
||||
};
|
||||
|
||||
type GetSubscribeListResponse = {
|
||||
@@ -1210,6 +1215,7 @@ declare namespace API {
|
||||
unscoped?: boolean;
|
||||
subscribe_id?: number;
|
||||
user_subscribe_id?: number;
|
||||
user_group_id?: number;
|
||||
};
|
||||
|
||||
type GetUserListRequest = {
|
||||
@@ -1440,6 +1446,8 @@ declare namespace API {
|
||||
protocol: string;
|
||||
enabled: boolean;
|
||||
sort?: number;
|
||||
node_group_id?: number;
|
||||
node_group_ids?: number[];
|
||||
created_at: number;
|
||||
updated_at: number;
|
||||
};
|
||||
@@ -1921,11 +1929,11 @@ declare namespace API {
|
||||
};
|
||||
|
||||
type ResetUserSubscribeTokenRequest = {
|
||||
user_subscribe_id: number;
|
||||
user_subscribe_id: any;
|
||||
};
|
||||
|
||||
type ResetUserSubscribeTrafficRequest = {
|
||||
user_subscribe_id: number;
|
||||
user_subscribe_id: string;
|
||||
};
|
||||
|
||||
type Response = {
|
||||
@@ -2106,6 +2114,8 @@ declare namespace API {
|
||||
quota: number;
|
||||
nodes: number[];
|
||||
node_tags: string[];
|
||||
node_group_ids?: number[];
|
||||
node_group_id?: number;
|
||||
show: boolean;
|
||||
sell: boolean;
|
||||
sort: number;
|
||||
@@ -2171,6 +2181,8 @@ declare namespace API {
|
||||
quota?: number;
|
||||
nodes?: number[];
|
||||
node_tags?: string[];
|
||||
node_group_ids?: number[];
|
||||
node_group_id?: number;
|
||||
show?: boolean;
|
||||
sell?: boolean;
|
||||
sort?: number;
|
||||
@@ -2244,7 +2256,7 @@ declare namespace API {
|
||||
};
|
||||
|
||||
type ToggleUserSubscribeStatusRequest = {
|
||||
user_subscribe_id: number;
|
||||
user_subscribe_id: any;
|
||||
};
|
||||
|
||||
type TosConfig = {
|
||||
@@ -2473,7 +2485,7 @@ declare namespace API {
|
||||
};
|
||||
|
||||
type UpdateUserSubscribeRequest = {
|
||||
user_subscribe_id: number;
|
||||
user_subscribe_id: string;
|
||||
subscribe_id: number;
|
||||
traffic: number;
|
||||
expired_at: number;
|
||||
@@ -2498,6 +2510,8 @@ declare namespace API {
|
||||
enable_login_notify: boolean;
|
||||
enable_subscribe_notify: boolean;
|
||||
enable_trade_notify: boolean;
|
||||
user_group_id: string;
|
||||
group_locked: boolean;
|
||||
auth_methods: UserAuthMethod[];
|
||||
user_devices: UserDevice[];
|
||||
rules: string[];
|
||||
@@ -2659,4 +2673,263 @@ declare namespace API {
|
||||
security: string;
|
||||
security_config: SecurityConfig;
|
||||
};
|
||||
|
||||
// ===== Group Management Types =====
|
||||
|
||||
type UserGroup = {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
sort: number;
|
||||
node_group_id?: number | null;
|
||||
for_calculation?: boolean;
|
||||
created_at: number;
|
||||
updated_at: number;
|
||||
};
|
||||
|
||||
type NodeGroup = {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
sort: number;
|
||||
for_calculation: boolean;
|
||||
min_traffic_gb?: number;
|
||||
max_traffic_gb?: number;
|
||||
created_at: number;
|
||||
updated_at: number;
|
||||
};
|
||||
|
||||
type Subscribe = {
|
||||
id: number;
|
||||
name: string;
|
||||
unit_price: number;
|
||||
unit_time: number;
|
||||
show?: boolean;
|
||||
sell?: boolean;
|
||||
sort: number;
|
||||
created_at: number;
|
||||
updated_at: number;
|
||||
};
|
||||
|
||||
type SubscribeGroupMapping = {
|
||||
subscribe_id: number;
|
||||
user_group_id: string;
|
||||
created_at: number;
|
||||
updated_at: number;
|
||||
};
|
||||
|
||||
type SubscribeGroupMappingInfo = {
|
||||
id: number;
|
||||
subscribe_id: number;
|
||||
user_group_id: number;
|
||||
subscribe?: Subscribe;
|
||||
user_group?: UserGroup;
|
||||
created_at: number;
|
||||
updated_at: number;
|
||||
};
|
||||
|
||||
type GroupHistory = {
|
||||
id: number;
|
||||
group_mode: string;
|
||||
trigger_type: string;
|
||||
total_users: number;
|
||||
success_count: number;
|
||||
failed_count: number;
|
||||
start_time?: number;
|
||||
end_time?: number;
|
||||
operator?: string;
|
||||
error_log?: string;
|
||||
created_at: number;
|
||||
};
|
||||
|
||||
type GroupHistoryDetailItem = {
|
||||
id: number;
|
||||
history_id: number;
|
||||
user_group_id: string;
|
||||
node_group_id: string;
|
||||
user_count: number;
|
||||
node_count: number;
|
||||
user_data?: string;
|
||||
created_at: number;
|
||||
};
|
||||
|
||||
type GroupHistoryDetail = {
|
||||
id: number;
|
||||
group_mode: string;
|
||||
trigger_type: string;
|
||||
total_users: number;
|
||||
success_count: number;
|
||||
failed_count: number;
|
||||
start_time?: number;
|
||||
end_time?: number;
|
||||
operator?: string;
|
||||
error_log?: string;
|
||||
created_at: number;
|
||||
config_snapshot?: {
|
||||
group_details?: GroupHistoryDetailItem[];
|
||||
config?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
|
||||
type RecalculationState = {
|
||||
state: string;
|
||||
progress: number;
|
||||
total: number;
|
||||
};
|
||||
|
||||
// ===== Group Request/Response Types =====
|
||||
|
||||
type GetUserGroupListRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
group_id?: string;
|
||||
};
|
||||
|
||||
type GetUserGroupListResponse = {
|
||||
total: number;
|
||||
list: UserGroup[];
|
||||
};
|
||||
|
||||
type CreateUserGroupRequest = {
|
||||
name: string;
|
||||
description?: string;
|
||||
sort?: number;
|
||||
node_group_id?: number | null;
|
||||
for_calculation?: boolean | null;
|
||||
};
|
||||
|
||||
type UpdateUserGroupRequest = {
|
||||
id: number;
|
||||
name?: string;
|
||||
description?: string;
|
||||
sort?: number;
|
||||
node_group_id?: number | null;
|
||||
for_calculation?: boolean | null;
|
||||
};
|
||||
|
||||
type DeleteUserGroupRequest = {
|
||||
id: number;
|
||||
};
|
||||
|
||||
type BindNodeGroupsRequest = {
|
||||
user_group_ids: number[];
|
||||
node_group_id?: number | null;
|
||||
};
|
||||
|
||||
type GetNodeGroupListRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
group_id?: string;
|
||||
};
|
||||
|
||||
type GetNodeGroupListResponse = {
|
||||
total: number;
|
||||
list: NodeGroup[];
|
||||
};
|
||||
|
||||
type CreateNodeGroupRequest = {
|
||||
name: string;
|
||||
description?: string;
|
||||
sort?: number;
|
||||
for_calculation?: boolean;
|
||||
min_traffic_gb?: number;
|
||||
max_traffic_gb?: number;
|
||||
};
|
||||
|
||||
type UpdateNodeGroupRequest = {
|
||||
id: number;
|
||||
name?: string;
|
||||
description?: string;
|
||||
sort?: number;
|
||||
for_calculation?: boolean;
|
||||
min_traffic_gb?: number;
|
||||
max_traffic_gb?: number;
|
||||
};
|
||||
|
||||
type DeleteNodeGroupRequest = {
|
||||
id: number;
|
||||
};
|
||||
|
||||
type GetSubscribeMappingRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
subscribe_id?: number;
|
||||
user_group_id?: number;
|
||||
};
|
||||
|
||||
type GetSubscribeMappingResponse = {
|
||||
total: number;
|
||||
list: SubscribeGroupMappingInfo[];
|
||||
};
|
||||
|
||||
type UpdateSubscribeMappingRequest = {
|
||||
subscribe_id: number;
|
||||
user_group_id: number;
|
||||
};
|
||||
|
||||
type GetGroupConfigResponse = {
|
||||
enabled: boolean;
|
||||
mode: string;
|
||||
};
|
||||
|
||||
type UpdateGroupConfigRequest = {
|
||||
enabled?: boolean;
|
||||
mode?: string;
|
||||
};
|
||||
|
||||
type RecalculateGroupRequest = {
|
||||
mode: string;
|
||||
};
|
||||
|
||||
type GetGroupHistoryRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
group_mode?: string;
|
||||
trigger_type?: string;
|
||||
};
|
||||
|
||||
type GetGroupHistoryResponse = {
|
||||
total: number;
|
||||
list: GroupHistory[];
|
||||
};
|
||||
|
||||
type GetGroupHistoryDetailRequest = {
|
||||
id: number;
|
||||
};
|
||||
|
||||
type GetGroupHistoryDetailResponse = GroupHistoryDetail;
|
||||
|
||||
type ExportGroupResultRequest = {
|
||||
history_id?: number;
|
||||
};
|
||||
type MigrateUsersRequest = {
|
||||
from_user_group_id: number;
|
||||
to_user_group_id: number;
|
||||
include_locked?: boolean;
|
||||
};
|
||||
type MigrateUsersResponse = {
|
||||
success_count: number;
|
||||
failed_count: number;
|
||||
};
|
||||
type ResetGroupsRequest = {
|
||||
confirm: boolean;
|
||||
};
|
||||
|
||||
type PreviewUserNodesRequest = {
|
||||
user_id: number;
|
||||
};
|
||||
|
||||
type PreviewUserNodesResponse = {
|
||||
user_id: number;
|
||||
node_groups: Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
nodes: Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
address: string;
|
||||
port: number;
|
||||
}>;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user