initial support for adding tcp interfaces

This commit is contained in:
liamcottle 2024-05-30 00:03:13 +12:00
commit 8d8198b5f8
2 changed files with 161 additions and 4 deletions

View file

@ -687,6 +687,13 @@
<div class="ml-2">Reticulum WebChat must be restarted for any interface changes to take effect.</div>
</div>
<button @click="tab = 'interfaces.add'" type="button" class="my-auto inline-flex items-center gap-x-1 rounded-md bg-gray-500 px-2 py-1 text-sm font-semibold text-white shadow-sm hover:bg-gray-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
<span>Add Interface</span>
</button>
<!-- interface list -->
<div v-for="(iface, interface_name) of interfaces" class="border rounded bg-white shadow overflow-hidden">
@ -728,7 +735,7 @@
</span>
<!-- tcp client interface -->
<span v-if="iface.type === 'TCPClientInterface'">
<span v-else-if="iface.type === 'TCPClientInterface'">
{{ iface.type }} • {{ iface.target_host }}:{{ iface.target_port }}
</span>
@ -798,6 +805,51 @@
</div>
</div>
<!-- add interface tab -->
<div v-if="tab === 'interfaces.add'" class="overflow-y-auto p-2 space-y-2">
<!-- add interface form -->
<div class="bg-white rounded shadow divide-y divide-gray-200">
<div class="p-2 font-bold">Add Interface</div>
<div class="p-2 space-y-3">
<!-- interface name -->
<div>
<label class="block mb-2 text-sm font-medium text-gray-900">Name</label>
<input type="text" placeholder="New Interface Name" v-model="newInterfaceName" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
</div>
<!-- interface type -->
<div class="mb-2">
<label class="block mb-2 text-sm font-medium text-gray-900">Type</label>
<select v-model="newInterfaceType" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
<option value="AutoInterface">AutoInterface</option>
<option value="TCPClientInterface">TCPClientInterface</option>
</select>
</div>
<!-- interface target host -->
<div v-if="newInterfaceType === 'TCPClientInterface'" class="mb-2">
<label class="block mb-2 text-sm font-medium text-gray-900">Target Host</label>
<input type="text" placeholder="example.com" v-model="newInterfaceTargetHost" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
</div>
<!-- interface target port -->
<div v-if="newInterfaceType === 'TCPClientInterface'" class="mb-2">
<label class="block mb-2 text-sm font-medium text-gray-900">Target Port</label>
<input type="text" placeholder="1234" v-model="newInterfaceTargetPort" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
</div>
<!-- add button -->
<button @click="addInterface" type="button" class="bg-green-500 hover:bg-green-400 focus-visible:outline-green-500 my-auto inline-flex items-center gap-x-1 rounded-md p-2 text-sm font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2">
Add Interface
</button>
</div>
</div>
</div>
</div>
</div>
@ -861,6 +913,11 @@
interfaces: {},
interfaceStats: {},
newInterfaceName: null,
newInterfaceType: "TCPClientInterface",
newInterfaceTargetHost: null,
newInterfaceTargetPort: null,
};
},
mounted: function() {
@ -1998,6 +2055,42 @@
// reload interfaces
await this.loadInterfaces();
},
async addInterface() {
try {
// add interface
const response = await window.axios.post(`/api/v1/reticulum/interfaces/add`, {
name: this.newInterfaceName,
type: this.newInterfaceType,
target_host: this.newInterfaceTargetHost,
target_port: this.newInterfaceTargetPort,
});
// clear add interface form
this.newInterfaceName = null;
this.newInterfaceType = "TCPClientInterface";
this.newInterfaceTargetHost = null;
this.newInterfaceTargetPort = null;
// go to interfaces tab
this.tab = "interfaces";
// show success message
if(response.data.message){
alert(response.data.message);
}
} catch(e) {
const message = e.response?.data?.message ?? "failed to add interface";
alert(message);
console.log(e);
}
// reload interfaces
await this.loadInterfaces();
},
onChatItemClick: function(chatItem) {
if(!chatItem.is_actions_expanded){

70
web.py
View file

@ -186,7 +186,7 @@ class ReticulumWebChat:
self.reticulum.config.write()
return web.json_response({
"message": "Interface Enabled",
"message": "Interface is now enabled",
})
# disable reticulum interface
@ -209,7 +209,7 @@ class ReticulumWebChat:
self.reticulum.config.write()
return web.json_response({
"message": "Interface Disabled",
"message": "Interface is now disabled",
})
# delete reticulum interface
@ -228,7 +228,71 @@ class ReticulumWebChat:
self.reticulum.config.write()
return web.json_response({
"message": "Interface Deleted",
"message": "Interface has been deleted",
})
# add reticulum interface
@routes.post("/api/v1/reticulum/interfaces/add")
async def index(request):
# get request data
data = await request.json()
interface_name = data.get('name')
interface_type = data.get('type')
# ensure name is provided
if interface_name is None or interface_name == "":
return web.json_response({
"message": "Name is required",
}, status=422)
# ensure type name provided
if interface_type is None or interface_type == "":
return web.json_response({
"message": "Type is required",
}, status=422)
# get existing interfaces
interfaces = {}
if "interfaces" in self.reticulum.config:
interfaces = self.reticulum.config["interfaces"]
# create interface details
interface_details = {
"type": interface_type,
"interface_enabled": str(data.get('enabled', False)),
}
# handle tcp client interface
if interface_type == "TCPClientInterface":
interface_target_host = data.get('target_host')
interface_target_port = data.get('target_port')
# ensure target host provided
if interface_target_host is None or interface_target_host == "":
return web.json_response({
"message": "Target Host is required",
}, status=422)
# ensure target port provided
if interface_target_port is None or interface_target_port == "":
return web.json_response({
"message": "Target Port is required",
}, status=422)
interface_details["target_host"] = data.get('target_host')
interface_details["target_port"] = data.get('target_port')
# merge new interface into existing interfaces
interfaces[interface_name] = interface_details
self.reticulum.config["interfaces"] = interfaces
# save config
self.reticulum.config.write()
return web.json_response({
"message": "Interface has been added",
})
# handle websocket clients