mirror of
https://github.com/rustdesk/rustdesk.git
synced 2025-11-28 16:50:24 +08:00
274 lines
8.9 KiB
Python
Executable file
274 lines
8.9 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
import requests
|
|
import argparse
|
|
import json
|
|
|
|
|
|
def check_response(response):
|
|
"""
|
|
Check API response and handle errors.
|
|
|
|
Two error cases:
|
|
1. Status code is not 200 -> exit with error
|
|
2. Response contains {"error": "xxx"} -> exit with error
|
|
"""
|
|
if response.status_code != 200:
|
|
print(f"Error: HTTP {response.status_code}: {response.text}")
|
|
exit(1)
|
|
|
|
# Check for {"error": "xxx"} in response
|
|
if response.text and response.text.strip():
|
|
try:
|
|
json_data = response.json()
|
|
if isinstance(json_data, dict) and "error" in json_data:
|
|
print(f"Error: {json_data['error']}")
|
|
exit(1)
|
|
return json_data
|
|
except ValueError:
|
|
return response.text
|
|
|
|
return None
|
|
|
|
|
|
def headers_with(token):
|
|
return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
|
|
|
|
|
|
# ---------- Device Group APIs ----------
|
|
|
|
def list_groups(url, token, name=None, page_size=50):
|
|
headers = headers_with(token)
|
|
params = {"pageSize": page_size}
|
|
if name:
|
|
params["name"] = name
|
|
data, current = [], 0
|
|
while True:
|
|
current += 1
|
|
params["current"] = current
|
|
r = requests.get(f"{url}/api/device-groups", headers=headers, params=params)
|
|
if r.status_code != 200:
|
|
print(f"Error: HTTP {r.status_code} - {r.text}")
|
|
exit(1)
|
|
res = r.json()
|
|
if "error" in res:
|
|
print(f"Error: {res['error']}")
|
|
exit(1)
|
|
rows = res.get("data", [])
|
|
data.extend(rows)
|
|
total = res.get("total", 0)
|
|
if len(rows) < page_size or current * page_size >= total:
|
|
break
|
|
return data
|
|
|
|
|
|
def get_group_by_name(url, token, name):
|
|
groups = list_groups(url, token, name)
|
|
for g in groups:
|
|
if str(g.get("name")) == name:
|
|
return g
|
|
return None
|
|
|
|
|
|
def create_group(url, token, name, note=None, accessed_from=None):
|
|
headers = headers_with(token)
|
|
payload = {"name": name}
|
|
if note:
|
|
payload["note"] = note
|
|
if accessed_from:
|
|
payload["allowed_incomings"] = accessed_from
|
|
r = requests.post(f"{url}/api/device-groups", headers=headers, json=payload)
|
|
return check_response(r)
|
|
|
|
|
|
def update_group(url, token, name, new_name=None, note=None, accessed_from=None):
|
|
headers = headers_with(token)
|
|
g = get_group_by_name(url, token, name)
|
|
if not g:
|
|
print(f"Error: Group '{name}' not found")
|
|
exit(1)
|
|
guid = g.get("guid")
|
|
payload = {}
|
|
if new_name is not None:
|
|
payload["name"] = new_name
|
|
if note is not None:
|
|
payload["note"] = note
|
|
if accessed_from is not None:
|
|
payload["allowed_incomings"] = accessed_from
|
|
r = requests.patch(f"{url}/api/device-groups/{guid}", headers=headers, json=payload)
|
|
check_response(r)
|
|
return "Success"
|
|
|
|
|
|
def delete_groups(url, token, names):
|
|
headers = headers_with(token)
|
|
if isinstance(names, str):
|
|
names = [names]
|
|
for n in names:
|
|
g = get_group_by_name(url, token, n)
|
|
if not g:
|
|
print(f"Error: Group '{n}' not found")
|
|
exit(1)
|
|
guid = g.get("guid")
|
|
r = requests.delete(f"{url}/api/device-groups/{guid}", headers=headers)
|
|
check_response(r)
|
|
return "Success"
|
|
|
|
|
|
# ---------- Device group assign APIs (name -> guid) ----------
|
|
|
|
def view_devices(url, token, group_name=None, id=None, device_name=None,
|
|
user_name=None, device_username=None, page_size=50):
|
|
"""View devices in a device group with filters"""
|
|
headers = headers_with(token)
|
|
|
|
# Separate exact match and fuzzy match params
|
|
params = {}
|
|
fuzzy_params = {
|
|
"id": id,
|
|
"device_name": device_name,
|
|
"user_name": user_name,
|
|
"device_username": device_username,
|
|
}
|
|
|
|
# Add device_group_name without wildcard (exact match)
|
|
if group_name:
|
|
params["device_group_name"] = group_name
|
|
|
|
# Add wildcard for fuzzy search to other params
|
|
for k, v in fuzzy_params.items():
|
|
if v is not None:
|
|
params[k] = "%" + v + "%" if (v != "-" and "%" not in v) else v
|
|
|
|
params["pageSize"] = page_size
|
|
|
|
data, current = [], 0
|
|
while True:
|
|
current += 1
|
|
params["current"] = current
|
|
r = requests.get(f"{url}/api/devices", headers=headers, params=params)
|
|
if r.status_code != 200:
|
|
return check_response(r)
|
|
res = r.json()
|
|
rows = res.get("data", [])
|
|
data.extend(rows)
|
|
total = res.get("total", 0)
|
|
if len(rows) < page_size or current * page_size >= total:
|
|
break
|
|
return data
|
|
|
|
|
|
def add_devices(url, token, group_name, device_ids):
|
|
headers = headers_with(token)
|
|
g = get_group_by_name(url, token, group_name)
|
|
if not g:
|
|
return f"Group '{group_name}' not found"
|
|
guid = g.get("guid")
|
|
payload = device_ids if isinstance(device_ids, list) else [device_ids]
|
|
r = requests.post(f"{url}/api/device-groups/{guid}", headers=headers, json=payload)
|
|
return check_response(r)
|
|
|
|
|
|
def remove_devices(url, token, group_name, device_ids):
|
|
headers = headers_with(token)
|
|
g = get_group_by_name(url, token, group_name)
|
|
if not g:
|
|
return f"Group '{group_name}' not found"
|
|
guid = g.get("guid")
|
|
payload = device_ids if isinstance(device_ids, list) else [device_ids]
|
|
r = requests.delete(f"{url}/api/device-groups/{guid}/devices", headers=headers, json=payload)
|
|
return check_response(r)
|
|
|
|
|
|
def parse_rules(s):
|
|
if not s:
|
|
return None
|
|
try:
|
|
v = json.loads(s)
|
|
if isinstance(v, list):
|
|
# expect list of {"type": number, "name": string}
|
|
return v
|
|
except Exception:
|
|
pass
|
|
return None
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Device Group manager")
|
|
parser.add_argument("command", choices=[
|
|
"view", "add", "update", "delete",
|
|
"view-devices", "add-devices", "remove-devices"
|
|
], help=(
|
|
"Command to execute. "
|
|
"[view/add/update/delete/add-devices/remove-devices: require Device Group Permission] "
|
|
"[view-devices: require Device Permission]"
|
|
))
|
|
parser.add_argument("--url", required=True)
|
|
parser.add_argument("--token", required=True)
|
|
|
|
parser.add_argument("--name", help="Device group name (exact match)")
|
|
parser.add_argument("--new-name", help="New device group name (for update)")
|
|
parser.add_argument("--note", help="Note")
|
|
|
|
parser.add_argument("--accessed-from", help="JSON array: '[{\"type\":0|2,\"name\":\"...\"}]' (0=User Group, 2=User)")
|
|
|
|
parser.add_argument("--ids", help="Comma separated device IDs for add-devices/remove-devices")
|
|
|
|
# Filters for view-devices command
|
|
parser.add_argument("--id", help="Device ID filter (for view-devices)")
|
|
parser.add_argument("--device-name", help="Device name filter (for view-devices)")
|
|
parser.add_argument("--user-name", help="User name filter (owner of device, for view-devices)")
|
|
parser.add_argument("--device-username", help="Device username filter (logged in user on device, for view-devices)")
|
|
|
|
args = parser.parse_args()
|
|
while args.url.endswith("/"): args.url = args.url[:-1]
|
|
|
|
if args.command == "view":
|
|
res = list_groups(args.url, args.token, args.name)
|
|
print(json.dumps(res, indent=2))
|
|
elif args.command == "add":
|
|
if not args.name:
|
|
print("Error: --name is required")
|
|
exit(1)
|
|
print(create_group(
|
|
args.url, args.token, args.name, args.note,
|
|
parse_rules(args.accessed_from)
|
|
))
|
|
elif args.command == "update":
|
|
if not args.name:
|
|
print("Error: --name is required")
|
|
exit(1)
|
|
print(update_group(
|
|
args.url, args.token, args.name, args.new_name, args.note,
|
|
parse_rules(args.accessed_from)
|
|
))
|
|
elif args.command == "delete":
|
|
if not args.name:
|
|
print("Error: --name is required (supports comma separated)")
|
|
exit(1)
|
|
names = [x.strip() for x in args.name.split(",") if x.strip()]
|
|
print(delete_groups(args.url, args.token, names))
|
|
elif args.command == "view-devices":
|
|
res = view_devices(
|
|
args.url,
|
|
args.token,
|
|
group_name=args.name,
|
|
id=args.id,
|
|
device_name=args.device_name,
|
|
user_name=args.user_name,
|
|
device_username=args.device_username
|
|
)
|
|
print(json.dumps(res, indent=2))
|
|
elif args.command in ("add-devices", "remove-devices"):
|
|
if not args.name or not args.ids:
|
|
print("Error: --name and --ids are required for add/remove devices")
|
|
exit(1)
|
|
ids = [x.strip() for x in args.ids.split(",") if x.strip()]
|
|
if args.command == "add-devices":
|
|
print(add_devices(args.url, args.token, args.name, ids))
|
|
else:
|
|
print(remove_devices(args.url, args.token, args.name, ids))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|