diff --git a/src/dashboard.py b/src/dashboard.py index ec57ef9d..0667b3cb 100644 --- a/src/dashboard.py +++ b/src/dashboard.py @@ -690,15 +690,16 @@ def API_updatePeerSettings(configName): preshared_key = data['preshared_key'] mtu = data['mtu'] keepalive = data['keepalive'] + notes = data.get('notes', "") wireguardConfig = WireguardConfigurations[configName] foundPeer, peer = wireguardConfig.searchPeer(id) if foundPeer: if wireguardConfig.Protocol == 'wg': status, msg = peer.updatePeer(name, private_key, preshared_key, dns_addresses, - allowed_ip, endpoint_allowed_ip, mtu, keepalive) + allowed_ip, endpoint_allowed_ip, mtu, keepalive, notes) else: status, msg = peer.updatePeer(name, private_key, preshared_key, dns_addresses, - allowed_ip, endpoint_allowed_ip, mtu, keepalive, "off") + allowed_ip, endpoint_allowed_ip, mtu, keepalive, "off", notes) wireguardConfig.getPeers() DashboardWebHooks.RunWebHook('peer_updated', { "configuration": wireguardConfig.Name, @@ -849,6 +850,7 @@ def API_addPeers(configName): mtu: int = data.get('mtu', None) keep_alive: int = data.get('keepalive', None) preshared_key: str = data.get('preshared_key', "") + notes: str = data.get('notes', "") if type(mtu) is not int or mtu < 0 or mtu > 1460: default: str = DashboardConfig.GetConfig("Peers", "peer_mtu")[1] @@ -900,6 +902,7 @@ def API_addPeers(configName): "allowed_ip": ip, "name": f"BulkPeer_{(addedCount + 1)}_{datetime.now().strftime('%Y%m%d_%H%M%S')}", "DNS": dns_addresses, + "notes": "", "endpoint_allowed_ip": endpoint_allowed_ip, "mtu": mtu, "keepalive": keep_alive, @@ -963,6 +966,7 @@ def API_addPeers(configName): "preshared_key": preshared_key, "endpoint_allowed_ip": endpoint_allowed_ip, "DNS": dns_addresses, + "notes": notes, "mtu": mtu, "keepalive": keep_alive, "advanced_security": "off" @@ -1703,4 +1707,4 @@ def index(): if __name__ == "__main__": startThreads() DashboardPlugins.startThreads() - app.run(host=app_ip, debug=False, port=app_port) \ No newline at end of file + app.run(host=app_ip, debug=False, port=app_port) diff --git a/src/modules/AmneziaWGPeer.py b/src/modules/AmneziaWGPeer.py index 17101b6b..65ce184b 100644 --- a/src/modules/AmneziaWGPeer.py +++ b/src/modules/AmneziaWGPeer.py @@ -17,7 +17,7 @@ class AmneziaWGPeer(Peer): def updatePeer(self, name: str, private_key: str, preshared_key: str, dns_addresses: str, allowed_ip: str, endpoint_allowed_ip: str, mtu: int, - keepalive: int, advanced_security: str) -> tuple[bool, str] or tuple[bool, None]: + keepalive: int, advanced_security: str, notes: str = "") -> tuple[bool, str] or tuple[bool, None]: if not self.configuration.getStatus(): self.configuration.toggleConfiguration() @@ -81,7 +81,8 @@ class AmneziaWGPeer(Peer): "mtu": mtu, "keepalive": keepalive, "preshared_key": preshared_key, - "advanced_security": advanced_security + "advanced_security": advanced_security, + "notes": notes }).where( self.configuration.peersTable.c.id == self.id ) @@ -89,4 +90,4 @@ class AmneziaWGPeer(Peer): self.configuration.getPeers() return True, None except subprocess.CalledProcessError as exc: - return False, exc.output.decode("UTF-8").strip() \ No newline at end of file + return False, exc.output.decode("UTF-8").strip() diff --git a/src/modules/AmneziaWireguardConfiguration.py b/src/modules/AmneziaWireguardConfiguration.py index 6ada7d5f..ac94d222 100644 --- a/src/modules/AmneziaWireguardConfiguration.py +++ b/src/modules/AmneziaWireguardConfiguration.py @@ -77,6 +77,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): sqlalchemy.Column('advanced_security', sqlalchemy.String(255)), sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text), sqlalchemy.Column('name', sqlalchemy.Text), + sqlalchemy.Column('notes', sqlalchemy.Text), sqlalchemy.Column('total_receive', sqlalchemy.Float), sqlalchemy.Column('total_sent', sqlalchemy.Float), sqlalchemy.Column('total_data', sqlalchemy.Float), @@ -101,6 +102,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): sqlalchemy.Column('advanced_security', sqlalchemy.String(255)), sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text), sqlalchemy.Column('name', sqlalchemy.Text), + sqlalchemy.Column('notes', sqlalchemy.Text), sqlalchemy.Column('total_receive', sqlalchemy.Float), sqlalchemy.Column('total_sent', sqlalchemy.Float), sqlalchemy.Column('total_data', sqlalchemy.Float), @@ -138,6 +140,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): sqlalchemy.Column('advanced_security', sqlalchemy.String(255)), sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text), sqlalchemy.Column('name', sqlalchemy.Text), + sqlalchemy.Column('notes', sqlalchemy.Text), sqlalchemy.Column('total_receive', sqlalchemy.Float), sqlalchemy.Column('total_sent', sqlalchemy.Float), sqlalchemy.Column('total_data', sqlalchemy.Float), @@ -171,6 +174,24 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): ) self.metadata.create_all(self.engine) + self.__ensurePeerNotesColumn() + + def __ensurePeerNotesColumn(self): + inspector = sqlalchemy.inspect(self.engine) + existing_tables = set(inspector.get_table_names()) + targets = [self.peersTable, self.peersRestrictedTable, self.peersDeletedTable] + with self.engine.begin() as conn: + for table in targets: + if table.name not in existing_tables: + continue + cols = [c["name"] for c in inspector.get_columns(table.name)] + if "notes" in cols: + continue + ddl = sqlalchemy.schema.DDL( + "ALTER TABLE %(table)s ADD COLUMN notes TEXT", + context={"table": table} + ) + conn.execute(ddl) def getPeers(self): self.Peers.clear() @@ -217,6 +238,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): "endpoint_allowed_ip": self.DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[ 1], "name": i.get("name"), + "notes": "", "total_receive": 0, "total_sent": 0, "total_data": 0, @@ -266,6 +288,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): "DNS": i['DNS'], "endpoint_allowed_ip": i['endpoint_allowed_ip'], "name": i['name'], + "notes": i.get("notes", ""), "total_receive": 0, "total_sent": 0, "total_data": 0, @@ -319,4 +342,4 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): with self.engine.connect() as conn: restricted = conn.execute(self.peersRestrictedTable.select()).mappings().fetchall() for i in restricted: - self.RestrictedPeers.append(AmneziaWGPeer(i, self)) \ No newline at end of file + self.RestrictedPeers.append(AmneziaWGPeer(i, self)) diff --git a/src/modules/Peer.py b/src/modules/Peer.py index 9201a9f0..be73ad9c 100644 --- a/src/modules/Peer.py +++ b/src/modules/Peer.py @@ -22,6 +22,7 @@ class Peer: self.DNS = tableData["DNS"] self.endpoint_allowed_ip = tableData["endpoint_allowed_ip"] self.name = tableData["name"] + self.notes = tableData.get("notes", "") self.total_receive = tableData["total_receive"] self.total_sent = tableData["total_sent"] self.total_data = tableData["total_data"] @@ -52,7 +53,7 @@ class Peer: def updatePeer(self, name: str, private_key: str, preshared_key: str, dns_addresses: str, allowed_ip: str, endpoint_allowed_ip: str, mtu: int, - keepalive: int) -> tuple[bool, str] or tuple[bool, None]: + keepalive: int, notes: str = "") -> tuple[bool, str] or tuple[bool, None]: if not self.configuration.getStatus(): self.configuration.toggleConfiguration() @@ -114,7 +115,8 @@ class Peer: "endpoint_allowed_ip": endpoint_allowed_ip, "mtu": mtu, "keepalive": keepalive, - "preshared_key": preshared_key + "preshared_key": preshared_key, + "notes": notes }).where( self.configuration.peersTable.c.id == self.id ) @@ -351,4 +353,4 @@ class Peer: hours, remainder = divmod(delta.total_seconds(), 3600) minutes, seconds = divmod(remainder, 60) - return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}" \ No newline at end of file + return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}" diff --git a/src/modules/WireguardConfiguration.py b/src/modules/WireguardConfiguration.py index f1fdfe16..58ca4ee2 100644 --- a/src/modules/WireguardConfiguration.py +++ b/src/modules/WireguardConfiguration.py @@ -241,6 +241,7 @@ class WireguardConfiguration: sqlalchemy.Column('DNS', sqlalchemy.Text), sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text), sqlalchemy.Column('name', sqlalchemy.Text), + sqlalchemy.Column('notes', sqlalchemy.Text), sqlalchemy.Column('total_receive', sqlalchemy.Float), sqlalchemy.Column('total_sent', sqlalchemy.Float), sqlalchemy.Column('total_data', sqlalchemy.Float), @@ -264,6 +265,7 @@ class WireguardConfiguration: sqlalchemy.Column('DNS', sqlalchemy.Text), sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text), sqlalchemy.Column('name', sqlalchemy.Text), + sqlalchemy.Column('notes', sqlalchemy.Text), sqlalchemy.Column('total_receive', sqlalchemy.Float), sqlalchemy.Column('total_sent', sqlalchemy.Float), sqlalchemy.Column('total_data', sqlalchemy.Float), @@ -310,6 +312,7 @@ class WireguardConfiguration: sqlalchemy.Column('DNS', sqlalchemy.Text), sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text), sqlalchemy.Column('name', sqlalchemy.Text), + sqlalchemy.Column('notes', sqlalchemy.Text), sqlalchemy.Column('total_receive', sqlalchemy.Float), sqlalchemy.Column('total_sent', sqlalchemy.Float), sqlalchemy.Column('total_data', sqlalchemy.Float), @@ -334,6 +337,24 @@ class WireguardConfiguration: ) self.metadata.create_all(self.engine) + self.__ensurePeerNotesColumn() + + def __ensurePeerNotesColumn(self): + inspector = sqlalchemy.inspect(self.engine) + existing_tables = set(inspector.get_table_names()) + targets = [self.peersTable, self.peersRestrictedTable, self.peersDeletedTable] + with self.engine.begin() as conn: + for table in targets: + if table.name not in existing_tables: + continue + cols = [c["name"] for c in inspector.get_columns(table.name)] + if "notes" in cols: + continue + ddl = sqlalchemy.schema.DDL( + "ALTER TABLE %(table)s ADD COLUMN notes TEXT", + context={"table": table} + ) + conn.execute(ddl) def __dumpDatabase(self): with self.engine.connect() as conn: @@ -442,6 +463,7 @@ class WireguardConfiguration: "endpoint_allowed_ip": self.DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[ 1], "name": i.get("name"), + "notes": "", "total_receive": 0, "total_sent": 0, "total_data": 0, @@ -534,6 +556,7 @@ class WireguardConfiguration: "DNS": i['DNS'], "endpoint_allowed_ip": i['endpoint_allowed_ip'], "name": i['name'], + "notes": i.get("notes", ""), "total_receive": 0, "total_sent": 0, "total_data": 0, @@ -1291,4 +1314,4 @@ class WireguardConfiguration: conn.execute(sqlalchemy.text('VACUUM;')) except Exception as e: return False - return True \ No newline at end of file + return True diff --git a/src/static/app/src/components/configurationComponents/peerAddModal.vue b/src/static/app/src/components/configurationComponents/peerAddModal.vue index 5212464c..d89a0341 100644 --- a/src/static/app/src/components/configurationComponents/peerAddModal.vue +++ b/src/static/app/src/components/configurationComponents/peerAddModal.vue @@ -22,6 +22,7 @@ const peerData = ref({ bulkAdd: false, bulkAddAmount: 0, name: "", + notes: "", allowed_ips: [], private_key: "", public_key: "", @@ -124,6 +125,18 @@ watch(() => {
+
+ + +
@@ -175,4 +188,4 @@ watch(() => { \ No newline at end of file + diff --git a/src/static/app/src/components/configurationComponents/peerDetailsModal.vue b/src/static/app/src/components/configurationComponents/peerDetailsModal.vue index e49b2cb2..fd60dcde 100644 --- a/src/static/app/src/components/configurationComponents/peerDetailsModal.vue +++ b/src/static/app/src/components/configurationComponents/peerDetailsModal.vue @@ -92,6 +92,16 @@ defineEmits(['close'])
+
+
+
+

+ +

+

{{selectedPeer.notes}}

+
+
+
@@ -178,4 +188,4 @@ defineEmits(['close']) \ No newline at end of file + diff --git a/src/static/app/src/components/configurationComponents/peerSettings.vue b/src/static/app/src/components/configurationComponents/peerSettings.vue index 7fa4eeee..61b29078 100644 --- a/src/static/app/src/components/configurationComponents/peerSettings.vue +++ b/src/static/app/src/components/configurationComponents/peerSettings.vue @@ -25,6 +25,9 @@ export default { reset(){ if (this.selectedPeer){ this.data = JSON.parse(JSON.stringify(this.selectedPeer)) + if (this.data.notes === undefined || this.data.notes === null){ + this.data.notes = "" + } this.dataChanged = false; } }, @@ -60,7 +63,7 @@ export default { this.reset(); }, mounted() { - this.$el.querySelectorAll("input").forEach(x => { + this.$el.querySelectorAll("input, textarea").forEach(x => { x.addEventListener("change", () => { this.dataChanged = true; }); @@ -99,6 +102,19 @@ export default { v-model="this.data.name" id="peer_name_textbox" placeholder="">
+
+ + +