# Netrinos Edgenode API Documentation

## Authentication

### Login to Obtain Access Token

To access the API, you must first log in to the device and obtain an access token. 
The account used also needs the API role assigned in the portal.
The token will be valid for the duration of the client login. 

Alternatively, if the reqest is received on localhost (127.0.0.1) the token requirement will be ignored.

#### Request


```json
POST http://edgedevice:88/api/login
{
    "Username": "username",
    "Password": "password"
}
```

#### Response

```json
Status: 200 OK
{
  "success": "Login successful",
  "error": "",
  "data": {
    "AuthToken": "e0db2c0d81...d52cd273db",
    "Username": "username"
  }
}
```

#### Error Response

```json
Status: 401 Unauthorized
{
  "success": "",
  "error": "Invalid username or password",
  "data": null
}
```

Use `AuthToken` as a Bearer token in the header of API requests:

```
Authorization: Bearer e0db2c0d81...d52cd273db
```

### Test the Token

Check if the token is valid, send a ping request to the API. 

#### Request

```json
GET http://edgedevice:88/api/ping
```
#### Response
```json
Status: 200 OK
{
  "success": "Success",
  "error": "",
  "data": null
}
```

#### Error Response

```json
Status: 401 Unauthorized
Unauthorized - Invalid token
```

## Networks

### Create or Update

#### Request

```json
POST http://edgedevice:88/api/edge/network/192.168.2.0-24
POST http://edgedevice:88/api/edge/network/192.168.2.0
```
#### Response

```json
Status: 200 OK
{
  "success": "Network created",
  "error": "",
  "data": {
    "Network": {
      "Name": "192.168.2.0/24",
      "IFname": "",
      "HostIPMask": "",
      "HostIP": "",
      "HostMask": 0,
      "NetIPMask": "192.168.2.0/24",
      "NetIP": "192.168.2.0",
      "NetMask": 24,
      "IPint": 3232236032,
      "LastScan": "0001-01-01T00:00:00Z",
      "Updated": "0001-01-01T00:00:00Z"
    }
  }
}
```

### Get Network(s)

Manipulate networks registered on the device. Specify CIDR notation in URLs with a `-` in place of a `/`.

#### Request

```json
GET http://edgedevice:88/api/edge/network/192.168.2.0-24
GET http://edgedevice:88/api/edge/network/192.168.2.0
```

#### Response

```json
Status: 200 OK
{
  "success": "Network found",
  "error": "",
  "data": {
    "Network": {
      "Name": "192.168.2.0/24",
      "IFname": "",
      "HostIPMask": "",
      "HostIP": "",
      "HostMask": 0,
      "NetIPMask": "192.168.2.0/24",
      "NetIP": "192.168.2.0",
      "NetMask": 24,
      "IPint": 3232236032,
      "LastScan": "0001-01-01T00:00:00Z",
      "Updated": "0001-01-01T00:00:00Z"
    }
  }
}
```

#### Request

```json
GET http://edgedevice:88/api/edge/network
```

#### Response

```json
Status: 200 OK
{
  "success": "Networks found",
  "error": "",
  "data": {
    "Count": 2,
    "Networks": [
      {
        "Name": "192.168.2.0/24",
        "IFname": "",
        "HostIPMask": "",
        "HostIP": "",
        "HostMask": 0,
        "NetIPMask": "192.168.2.0/24",
        "NetIP": "192.168.2.0",
        "NetMask": 24,
        "IPint": 3232236032,
        "LastScan": "0001-01-01T00:00:00Z",
        "Updated": "0001-01-01T00:00:00Z"
      },
      ...
    ]
  }
}
```

#### Error Response

```json
Status: 404 Not Found
{
  "success": "",
  "error": "Network not found",
  "data": null
}
```


### Delete

#### Request

```json
DELETE http://edgedevice:88/api/edge/network/192.168.2.0-24
DELETE http://edgedevice:88/api/edge/network/192.168.2.0
```

#### Response

```json
Status: 200 OK
{
  "success": "Network deleted",
  "error": "",
  "data": null
}
```

#### Error Response

```json
Status: 404 Not Found
{
  "success": "",
  "error": "Network not found",
  "data": null
}
```

## Devices


### Create or Update Device

Create or update a device in the network.

#### Request

```json
POST http://edgedevice:88/api/edge/device/192.168.8.10
{
  "Name": "Brother MFC",
  "Hostname": "brother",
  "IP": "192.168.8.10",
  "Network": "192.168.8.0/24",
  "Vendor": "Brother Industries Ltd."
}
```

#### Response

```json
Status: 200 OK
{
  "success": "Network updated",
  "error": "",
  "data": {
    "Device": {
      "Name": "Brother MFC",
      "Hostname": "brother",
      "IP": "192.168.8.10",
      "IPint": 3232237578,
      "Network": "192.168.8.0/24",
      "MAC": "",
      "Vendor": "Brother Industries Ltd.",
      "Notes": "",
      "Ports": null,
      "Forwards": null,
      "ProxyMap": null,
      "ForwardsMap": null,
      "Created": "0001-01-01T00:00:00Z",
      "Updated": "0001-01-01T00:00:00Z"
    }
  }
}
```

### Get Device Information
   
   Retrieve information about a specific device (node) in a network.
   
   #### Request
   
  ```json
  GET http://edgedevice:88/api/edge/device/192.168.8.10
  ```
   
   #### Response
   
   ```json
   Status: 200 OK
   {
     "success": "Device found",
     "error": "",
     "data": {
       "Node": {
         "Name": "brother",
         "Hostname": "brother",
         "IP": "192.168.8.10",
         "IPint": 3232237578,
         "Network": "192.168.8.0/24",
         "MAC": "e8:65:38:28:7d:53",
         "Vendor": "CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD.",
         "Notes": "",
         "Ports": null,
         "Forwards": [
           {
             "Name": "Admin UI",
             "Nodename": "",
             "IP": "192.168.8.10",
             "IPint": 3232237578,
             "PortNum": 443,
             "Protocol": "tcp",
             "Method": "Forward",
             "Service": "",
             "SourceIP": "0.0.0.0",
             "SourcePort": 10000,
             "OpenAs": "https",
             "Command": "https://device.account.2ho.ca:10000",
             "Enabled": true,
             "Updated": "0001-01-01T00:00:00Z"
           }
         ],
         "ProxyMap": null,
         "ForwardsMap": null,
         "Created": "0001-01-01T00:00:00Z",
         "Updated": "2024-12-11T10:55:33.768678777-05:00"
       }
     }
   }
   ```

### Get All Devices

Retrieve a list of all devices in the network. Specify the network CIDR in the URL. If the network is not specified, all devices are returned. Use a `-` in place of a `/` in the CIDR notation.

#### Request

```json
GET http://edgedevice:88/api/edge/devices
GET http://edgedevice:88/api/edge/devices/192.168.8.0-24
```

#### Response

```json
Status: 200 OK
{
  "success": "Devices found",
  "error": "",
  "data": {
    "Count": 19,
    "Network": [
      {
        "Name": "unifi",
        "Hostname": "unifi",
        "IP": "192.168.8.1",
        "IPint": 3232237569,
        "Network": "192.168.8.0/24",
        "MAC": "0e:ea:14:4d:1a:cc",
        "Vendor": "Unknown Vendor",
        "Notes": "",
        "Ports": null,
        "Forwards": [...],
        "ProxyMap": null,
        "ForwardsMap": null,
        "Created": "0001-01-01T00:00:00Z",
        "Updated": "2024-12-13T14:18:11.800682015-05:00"
      },
      ...
    ]
  }
}
```

### Delete Device

Delete a device from the network.

#### Request

```json
DELETE http://edgedevice:88/api/edge/device/192.168.8.10
```

#### Response

```json
Status: 200 OK
{
  "success": "Device deleted",
  "error": "",
  "data": null
}
```

### Forwarding Rules

#### Create or Update a Rule
   
   Creates a new forwarding rule or updates an existing one if the port already exists. If the device does not exist, it is automatically created.
   
   #### Request
   
   
   ```json
   POST http://edgedevice:88/api/edge/forward
{
  "Name": "Admin UI",
  "IP": "192.168.8.10",
  "PortNum": 80,
  "Protocol": "tcp",
  "OpenAs": "http",
  "Enabled": true
}
   ```

   
   #### Response
   
   ```json
{
  "success": "Forward created successfully",
  "error": "",
  "data": {
    "Forward": {
      "Name": "Admin UI",
      "Nodename": "",
      "IP": "192.168.8.10",
      "IPint": 3232237578,
      "PortNum": 80,
      "Protocol": "tcp",
      "Method": "Forward",
      "Service": "",
      "SourceIP": "0.0.0.0",
      "SourcePort": 10000,
      "OpenAs": "http",
      "Command": "",
      "Enabled": true,
      "Updated": "0001-01-01T00:00:00Z"
    }
  }
}
   ```
#### Read One
   
   Read a specified forwarding rule by port.
   
   #### Request
   
   ```json
   GET http://edgedevice:88/api/edge/forward/192.168.8.10/443
   ```
   
   #### Response
   
   ```json
Status: 200 OK
{
  "success": "Forward found",
  "error": "",
  "data": {
    "Forward": {
      "Name": "Admin UI",
      "Nodename": "",
      "IP": "192.168.8.10",
      "IPint": 3232237578,
      "PortNum": 80,
      "Protocol": "tcp",
      "Method": "Forward",
      "Service": "",
      "SourceIP": "0.0.0.0",
      "SourcePort": 10000,
      "OpenAs": "http",
      "Command": "",
      "Enabled": true,
      "Updated": "0001-01-01T00:00:00Z"
    }
  }
}
   ```
#### Read All
   
   Read all the forwarded ports from a device.
   
   #### Request
   
   ```json
   GET http://edgedevice:88/api/edge/forward/192.168.8.10
   ```
   
   #### Response
   
   ```json
  Status: 200 OK
  {
  "success": "Forwards found",
  "error": "",
  "data": {
    "Forwards": [
      {
        "Name": "Admin UI",
        "Nodename": "",
        "IP": "192.168.8.10",
        "IPint": 3232237578,
        "PortNum": 80,
        "Protocol": "tcp",
        "Method": "Forward",
        "Service": "",
        "SourceIP": "0.0.0.0",
        "SourcePort": 10000,
        "OpenAs": "http",
        "Command": "",
        "Enabled": true,
        "Updated": "0001-01-01T00:00:00Z"
      },
      ...
    ]
  }
}
   ```
#### Delete a Forward
   
   Delete a forwarding rule from the device.
   
   #### Request
   
   ```json
   DELETE http://edgedevice:88/api/edge/forward/192.168.8.10/80
   ```
   
   #### Response
   
   ```json
  Status: 200 OK
   {
     "success": true,
     "error": "",
     "message": "Forward deleted successfully"
   }
   ```

## Scanner

### Start a Network Scan

Start a network scan.

Request

```json
GET http://edgedevice:88/api/edge/scan/start
```

Response
```json
Status: 200 OK
{
  "success": "Continuous scanning started",
  "error": "",
  "data": null
}
```

### Stop a Network Scan

Stop a network scan.

Request

```json
GET http://edgedevice:88/api/edge/scan/stop
```

Response

```json
Status: 200 OK
{
  "success": "Scanning stopped",
  "error": "",
  "data": null
}
```

### Scan Once 

Scan a network once. i.e. Not continuously.

Request

```json
GET http://edgedevice:88/api/edge/scan/once
```

Response

```json
Status: 200 OK
{
  "success": "Single scan initiated",
  "error": "",
  "data": null
}
```

### Scan with ONVIF

Scan a network for ONVIF devices using the ONVIF protocol only. 

#### Request

```json
GET http://edgedevice:88/api/edge/scan/onvif
```

#### Response
```json
Status: 200 OK
{
  "success": "ONVIF scan initiated",
  "error": "",
  "data": null
}
```

### Scan a Specific Host

Scan a single host for open ports.

#### Request

```json
GET http://edgedevice:88/api/edge/scan/192.168.8.1
```

#### Response
```json
Status: 200 OK
{
  "success": "Host scan initiated",
  "error": "",
  "data": null
}
```

## Configuration

### Get Configuration

Get the configuration of the Netrinos Edge Services

#### Request

```json
GET http://edgedevice:88/api/edge/config
```

#### Response

```json
Status: 200 OK
{
  "success": "Edge config found",
  "error": "",
  "data": {
    "Config": {
      "LocalNet": {
        "Hostname": "",
        "IsStatic": false,
        "LocalIPn": "",
        "Gateway": "",
        "DNS": ""
      },
      "Server": {
        "IP": "0.0.0.0",
        "Port": "88"
      },
      "Proxy": {
        "IP": "0.0.0.0",
        "ServeIP": "192.168.8.20",
        "PortStart": 10000,
        "PortRange": 1000
      },
      "Socks": {
        "IP": "0.0.0.0",
        "Port": 1080
      },
      "Scan": {
        "Auto": false,
        "Interval": 0,
        "Workers": 0,
        "Continuous": false,
        "NoScanSubnets": "",
        "OnvifScan": false
      },
      "Monitor": {
        "Auto": false,
        "Target": ""
      }
    }
  }
}

```

### Get Scan Configuration

Get the scan configuration of the Netrinos Edge Services

#### Request

```json
GET http://edgedevice:88/api/edge/config/config
```

#### Response

```json
Status: 200 OK
{
  "success": "Scanner config found",
  "error": "",
  "data": {
    "Scan": {
      "Auto": false,
      "Interval": 0,
      "Workers": 0,
      "Continuous": false,
      "NoScanSubnets": "",
      "OnvifScan": false
    }
  }
}

```

### Set Scanner Configuration

Set parameters of the scan configuration 

#### Request

```json
POST http://edgedevice:88/api/edge/scan/config
{
  "Auto": false
}
```

#### Response

```json
Status: 200 OK
{
  "success": "Scan configuration updated",
  "error": "",
  "data": {
    "Scanner": {
      "Auto": false,
      "Interval": 0,
      "Workers": 0,
      "Continuous": false,
      "NoScanSubnets": "",
      "OnvifScan": false
    }
  }
}
```
#### Request

```json
POST http://edgedevice:88/api/edge/config
{
  "Continuous": true
}
```
#### Response
```json
Status: 200 OK
{
  "success": "Scan configuration updated",
  "error": "",
  "data": {
    "Scanner": {
      "Auto": false,
      "Interval": 0,
      "Workers": 0,
      "Continuous": true,
      "NoScanSubnets": "",
      "OnvifScan": false
    }
  }
}
```
## Example Usage

These examples are far from optimal. They are meant as simplified examples of how to use the API.

### Login and set a forward rule

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

const apiURL = "http://localhost:88" // API URL

var client *http.Client // Global HTTP client

func main() {

	client = &http.Client{}

	// Previously authenticated token. May not be current.
	token := "1234abcdthismayormaynotbeavalidtokenab6abcd1234"

	// Login credentials - be sure the account has the API role in the admin portal
	credentials := map[string]string{
		"Username": "username",
		"Password": "password",
	}

	// Check if the token is valid
	req, _ := http.NewRequest("GET", apiURL+"/api/ping", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	tokenResp, _ := client.Do(req)

	if tokenResp.StatusCode == http.StatusOK {
		fmt.Printf("Token is valid.")

	} else {
		fmt.Println("Token is invalid. Logging in to get a new token.")

		// Login to get a new token
		jsonData, _ := json.Marshal(credentials)

		resp, _ := http.Post(apiURL+"/api/login", "application/json", bytes.NewBuffer(jsonData))
		body, _ := io.ReadAll(resp.Body)
		defer resp.Body.Close()

		// Check that the login was successful
		if resp.StatusCode != http.StatusOK {
			fmt.Printf("Login failed. Status code: %d\n", resp.StatusCode)
			return
		}

		var loginResp map[string]interface{}
		json.Unmarshal(body, &loginResp)

		// Get the auth token from the nested response structure
		loginData := loginResp["data"].(map[string]interface{})
		token = loginData["AuthToken"].(string)
		fmt.Printf("Received token: %s\n", token)
	}

	// Create forward rule
	IP := "192.168.8.254"
	forwardRule := map[string]interface{}{
		"Name":     "RTSP Stream",
		"IP":       IP,
		"PortNum":  554,
		"Protocol": "tcp",
		"OpenAs":   "rtsp",
		"Enabled":  true,
	}
	ruleJSON, _ := json.Marshal(forwardRule)

	req, _ = http.NewRequest("POST", apiURL+"/api/edge/forward/"+IP, bytes.NewBuffer(ruleJSON))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")

	resp, _ := client.Do(req)
	body, _ := io.ReadAll(resp.Body)
	defer resp.Body.Close()

	fmt.Printf("Forward rule response: %s\n", string(body))
}
```
#### Result

```json
Token is invalid. Logging in to get a new token.
Received token: 1fbcc8daf8d212ca91285a7ee4772b6208b3cb6d56a14e682847616aaab6490a
Forward rule response: {
    "success": "Forward created successfully",
    "error": "",
    "data": {
        "Forward": {
            "Name": "RTSP Stream",
            "Nodename": "unknown",
            "IP": "192.168.8.254",
            "IPint": 3232237822,
            "PortNum": 554,
            "Protocol": "tcp",
            "Method": "Forward",
            "Service": "rtsp",
            "SourceIP": "0.0.0.0",
            "SourcePort": 10000,
            "OpenAs": "rtsp",
            "Command": "",
            "Enabled": true,
            "Updated": "2024-12-16T19:36:06.642964426-05:00"
        }
    }
}
```

### Perform an ONVIF Scan 

Scan the network for ONVIF devices and list the results

#### Example Code

```go
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"
)

const apiURL = "http://edgedevice:88"

func main() {
	// Known token from previous authentication
	token := "1234abcdthismayormaynotbeavalidtokenab6abcd1234"
	client := &http.Client{}

	// Start ONVIF scan
	req, _ := http.NewRequest("GET", apiURL+"/api/edge/scan/onvif", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	resp, _ := client.Do(req)
	body, _ := io.ReadAll(resp.Body)
	resp.Body.Close()

	fmt.Printf("Scan initiated: %s\n", string(body))

	// Poll scan status until complete
	for {
		req, _ = http.NewRequest("GET", apiURL+"/api/edge/scan", nil)
		req.Header.Set("Authorization", "Bearer "+token)
		resp, _ = client.Do(req)
		body, _ = io.ReadAll(resp.Body)
		resp.Body.Close()

		var respData map[string]interface{}
		json.Unmarshal(body, &respData)
		data := respData["data"].(map[string]interface{})

		if !data["running"].(bool) {
			break
		}
		time.Sleep(3 * time.Second)
	}
	fmt.Println()

	// Get discovered devices
	req, _ = http.NewRequest("GET", apiURL+"/api/edge/devices", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	resp, _ = client.Do(req)
	body, _ = io.ReadAll(resp.Body)
	resp.Body.Close()

	var result map[string]interface{}
	json.Unmarshal(body, &result)

	fmt.Println("\nDiscovered devices:")
	devices := result["data"].(map[string]interface{})["Network"].([]interface{})
	for _, device := range devices {
		dev := device.(map[string]interface{})
		fmt.Printf("Name: %s\nIP: %s\nVendor: %s\n\n",
			dev["Name"],
			dev["IP"],
			dev["Vendor"])
	}
}

```

#### Result

```json
Scan initiated: {
    "success": "Onvif scan initiated",
    "error": "",
    "data": null
}

Discovered devices:
Name: unknown
IP: 192.168.8.254
Vendor: Speco Technologies
```


