import 'dart:convert' as convert; import 'package:http/http.dart' as http; import 'package:pi_dashboard/logger.dart'; import 'model.dart'; class ProxmoxWebService { ProxmoxWebService({ required this.protocol, required this.hostname, required this.port, required this.username, required this.password, }); final String protocol; final String hostname; final int port; final String username; final String password; String? _csrfToken; String? _ticket; bool _authenticated = false; Future authenticate() async { final resp = await http.post( Uri.https( hostname, "/api2/json/access/ticket", { 'username': username, 'password': password, }, ), ); if (resp.statusCode == 200) { final body = convert.jsonDecode(resp.body) as Map; final data = body["data"] as Map; _csrfToken = data["CSRFPreventionToken"]; _ticket = data["ticket"]; _authenticated = true; } else { _authenticated = false; Log().info( "Authentication returned error code ${resp.statusCode}: ${resp.body}"); } return _authenticated; } Future?> _doGet(String endpoint) async { if (!_authenticated) return null; final resp = await http.get( Uri.https( hostname, endpoint, ), headers: { "CSRFPreventionToken": _csrfToken as String, "Cookie": "PVEAuthCookie=$_ticket" }); if (resp.statusCode != 200) { Log().info("Get returned error code ${resp.statusCode}: ${resp.body}"); return null; } return convert.jsonDecode(resp.body) as Map; } Future?> _doPost( String endpoint, Map payload, {debug = false}) async { if (!_authenticated) return null; final resp = await http.post( Uri.https( hostname, endpoint, ), headers: { "CSRFPreventionToken": _csrfToken as String, "Cookie": "PVEAuthCookie=$_ticket" }, body: payload, ); if (resp.statusCode != 200) { Log().info("Post returned error code ${resp.statusCode}: ${resp.body}"); return null; } return convert.jsonDecode(resp.body) as Map; } Future> listNodes() async { Log().debug("Querying nodes"); List nodes = []; final resp = await _doGet("/api2/json/nodes"); if (resp == null) return []; for (final nodeJson in resp["data"]) { nodes.add(ProxmoxNode.fromJson(nodeJson as Map)); } return nodes; } Future> listVms(String node) async { Log().debug("Querying vms"); List vms = []; final resp = await _doGet("/api2/json/nodes/$node/qemu"); if (resp == null) return []; for (final vmJson in resp["data"]) { vms.add(ProxmoxVm.fromJson(vmJson as Map)); } vms.sort((a, b) => a.vmid.compareTo(b.vmid)); return vms; } Future toggleVm(ProxmoxNode node, ProxmoxVm vm) async { if (!_authenticated) return false; final isRunning = vm.status == "running"; Log().info("toggling VM: ${isRunning ? "stopping" : "starting"}"); final endpoint = "/api2/json/nodes/${node.node}/qemu/${vm.vmid}/status/${isRunning ? "shutdown" : "start"}"; final resp = await _doPost(endpoint, {}, debug: true); if (resp == null) return false; return true; } }