Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions PasarGuardNodeBridge/abstract_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ async def start(
keep_alive: int,
exclude_inbounds: list[str],
timeout: int | None,
# Limit enforcer configuration (optional)
node_id: int = 0,
panel_api_url: str = "",
limit_check_interval: int = 30,
limit_refresh_interval: int = 60,
) -> service.BaseInfoResponse | None:
raise NotImplementedError

Expand Down
6 changes: 6 additions & 0 deletions PasarGuardNodeBridge/common/service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ message Backend {
repeated User users = 3;
uint64 keep_alive = 4;
repeated string exclude_inbounds = 5;

// Limit enforcer configuration (sent from panel)
int32 node_id = 6;
string panel_api_url = 7;
int32 limit_check_interval = 8; // seconds, default 30
int32 limit_refresh_interval = 9; // seconds, default 60
Comment on lines +26 to +31
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Proto3 “default” comments are not enforced.

In proto3, scalar fields default to zero on decode; the “default 30/60” comments won’t apply unless the server/client normalizes. This can lead to 0-second intervals if another client omits the fields.

Consider either:

  • Normalizing 0 to 30/60 in the node/panel logic, or
  • Using optional/wrapper types to detect presence.
💡 Minimal clarification (comment-only) if you keep current schema
-  int32 limit_check_interval = 8;   // seconds, default 30
-  int32 limit_refresh_interval = 9; // seconds, default 60
+  int32 limit_check_interval = 8;   // seconds; 0 means "use default" (e.g., 30)
+  int32 limit_refresh_interval = 9; // seconds; 0 means "use default" (e.g., 60)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Limit enforcer configuration (sent from panel)
int32 node_id = 6;
string panel_api_url = 7;
int32 limit_check_interval = 8; // seconds, default 30
int32 limit_refresh_interval = 9; // seconds, default 60
// Limit enforcer configuration (sent from panel)
int32 node_id = 6;
string panel_api_url = 7;
int32 limit_check_interval = 8; // seconds; 0 means "use default" (e.g., 30)
int32 limit_refresh_interval = 9; // seconds; 0 means "use default" (e.g., 60)
🤖 Prompt for AI Agents
In `@PasarGuardNodeBridge/common/service.proto` around lines 26 - 31, The proto
comments saying "default 30/60" are misleading because proto3 scalars decode to
zero; update handling for the fields limit_check_interval and
limit_refresh_interval so zero values don't become 0-second intervals: either
change the proto to use optional or wrapper types (e.g.,
google.protobuf.Int32Value) for limit_check_interval and limit_refresh_interval
so presence can be detected, or keep scalars but add normalization logic in the
node/panel code that reads node_id, panel_api_url, limit_check_interval, and
limit_refresh_interval and replaces 0 with the intended defaults (30 and 60)
before using them.

}

// log
Expand Down
84 changes: 42 additions & 42 deletions PasarGuardNodeBridge/common/service_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions PasarGuardNodeBridge/common/service_pb2.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,26 @@ class BaseInfoResponse(_message.Message):
def __init__(self, started: bool = ..., core_version: _Optional[str] = ..., node_version: _Optional[str] = ...) -> None: ...

class Backend(_message.Message):
__slots__ = ("type", "config", "users", "keep_alive", "exclude_inbounds")
__slots__ = ("type", "config", "users", "keep_alive", "exclude_inbounds", "node_id", "panel_api_url", "limit_check_interval", "limit_refresh_interval")
TYPE_FIELD_NUMBER: _ClassVar[int]
CONFIG_FIELD_NUMBER: _ClassVar[int]
USERS_FIELD_NUMBER: _ClassVar[int]
KEEP_ALIVE_FIELD_NUMBER: _ClassVar[int]
EXCLUDE_INBOUNDS_FIELD_NUMBER: _ClassVar[int]
NODE_ID_FIELD_NUMBER: _ClassVar[int]
PANEL_API_URL_FIELD_NUMBER: _ClassVar[int]
LIMIT_CHECK_INTERVAL_FIELD_NUMBER: _ClassVar[int]
LIMIT_REFRESH_INTERVAL_FIELD_NUMBER: _ClassVar[int]
type: BackendType
config: str
users: _containers.RepeatedCompositeFieldContainer[User]
keep_alive: int
exclude_inbounds: _containers.RepeatedScalarFieldContainer[str]
def __init__(self, type: _Optional[_Union[BackendType, str]] = ..., config: _Optional[str] = ..., users: _Optional[_Iterable[_Union[User, _Mapping]]] = ..., keep_alive: _Optional[int] = ..., exclude_inbounds: _Optional[_Iterable[str]] = ...) -> None: ...
node_id: int
panel_api_url: str
limit_check_interval: int
limit_refresh_interval: int
def __init__(self, type: _Optional[_Union[BackendType, str]] = ..., config: _Optional[str] = ..., users: _Optional[_Iterable[_Union[User, _Mapping]]] = ..., keep_alive: _Optional[int] = ..., exclude_inbounds: _Optional[_Iterable[str]] = ..., node_id: _Optional[int] = ..., panel_api_url: _Optional[str] = ..., limit_check_interval: _Optional[int] = ..., limit_refresh_interval: _Optional[int] = ...) -> None: ...

class Log(_message.Message):
__slots__ = ("detail",)
Expand Down
15 changes: 14 additions & 1 deletion PasarGuardNodeBridge/grpclib.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ async def start(
keep_alive: int = 0,
exclude_inbounds: list[str] = [],
timeout: int | None = None,
# Limit enforcer configuration (optional - sent to node for real-time enforcement)
node_id: int = 0,
panel_api_url: str = "",
limit_check_interval: int = 30,
limit_refresh_interval: int = 60,
) -> service.BaseInfoResponse | None:
"""Start the node with proper task management"""
timeout = timeout or self._default_timeout
Expand All @@ -113,7 +118,15 @@ async def start(
raise NodeAPIError(code=-4, detail="Invalid node")

req = service.Backend(
type=backend_type, config=config, users=users, keep_alive=keep_alive, exclude_inbounds=exclude_inbounds
type=backend_type,
config=config,
users=users,
keep_alive=keep_alive,
exclude_inbounds=exclude_inbounds,
node_id=node_id,
panel_api_url=panel_api_url,
limit_check_interval=limit_check_interval,
limit_refresh_interval=limit_refresh_interval,
)

async with self._node_lock:
Expand Down
9 changes: 9 additions & 0 deletions PasarGuardNodeBridge/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ async def start(
keep_alive: int = 0,
exclude_inbounds: list[str] = [],
timeout: int | None = None,
# Limit enforcer configuration (optional - sent to node for real-time enforcement)
node_id: int = 0,
panel_api_url: str = "",
limit_check_interval: int = 30,
limit_refresh_interval: int = 60,
):
"""Start the node with proper task management"""
timeout = timeout or self._default_timeout
Expand All @@ -138,6 +143,10 @@ async def start(
users=users,
keep_alive=keep_alive,
exclude_inbounds=exclude_inbounds,
node_id=node_id,
panel_api_url=panel_api_url,
limit_check_interval=limit_check_interval,
limit_refresh_interval=limit_refresh_interval,
),
proto_response_class=service.BaseInfoResponse,
)
Expand Down