From 144da958ddac2c25d5b0a7fce9dd66f9ce821d1f Mon Sep 17 00:00:00 2001 From: wiktr Date: Fri, 21 Aug 2026 23:19:27 +0200 Subject: [PATCH] Initial commit --- Dockerfile | 16 +++++++++++ app/main.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 8 ++++++ requirements.txt | 1 + run.sh | 9 ++++++ 5 files changed, 103 insertions(+) create mode 100644 Dockerfile create mode 100644 app/main.py create mode 100644 docker-compose.yml create mode 100644 requirements.txt create mode 100755 run.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a2c9da8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.14.7-alpine3.24 + +# Install dnsmasq +RUN apk --update --no-cache add dnsmasq + +# Install libraries +COPY ./requirements.txt /requirements.txt +RUN pip3 install -r /requirements.txt + +# Copy source +COPY ./run.sh /run.sh +COPY ./app/ /app + +RUN mkdir /app/hosts.d/ + +CMD ["/run.sh"] diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..ff28e30 --- /dev/null +++ b/app/main.py @@ -0,0 +1,69 @@ +from flask import Flask, jsonify, request +import re + + + +HOSTS_PATH = "/app/hosts.d/izbi" + + + +app = Flask(__name__) + +hosts = {} + + + +def update_hosts(path: str, hosts: dict = {}): bool + try: + hosts_str = "" + for domain, ip in hosts.items(): + hosts_str += f"{ip}\t{domain}\n" + with open(path, "w") as f: + f.write(hosts_str) + print(f"ddns - updated {path}") + return True + except Exception as e: + print(e) + return False + + + +def validate_ip(ip: str): bool + exp = "^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$" + return re.search(exp, ip) + + + +@app.route('/', methods=["GET"]) +def health(): + return jsonify({"status": "healthy", "comment": ""}) + + + +@app.route('/update', methods=["GET"]) +def update_addr(): + if 'domain' not in request.args: + return jsonify({"status": "400", "code": "domain-error", "comment": "Missing or invalid domain"}), 400 + domain = request.args['domain'] + + if 'ip' not in request.args: + ip = request.remote_addr + elif not validate_ip(request.args['ip']): + return jsonify({"status": "400", "code": "ip-error", "comment": "Invalid IP"}), 400 + else: + ip = request.args['ip'] + + print(f"ddns - update request of [{domain}, {ip}] from {request.remote_addr}") + + hosts[domain] = ip + success = update_hosts(HOSTS_PATH, hosts) + + if not success: + return jsonify({"status": "500", "code": "internal-server-error", "An error occurred while processing request"}), 500 + + return jsonify({"status": "200", "code": "ok", "comment": "Updated successfully"}) + + + +if __name__ == '__main__': + app.run(host="0.0.0.0", port=8080, debug=True) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6c0544d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +services: + ddns: + image: izbi-dns + build: . + restart: unless-stopped + ports: + - "127.0.0.1:8080:8080" + - "127.0.0.1:55:5353/udp" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7e10602 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +flask diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..5b9c8d8 --- /dev/null +++ b/run.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +/usr/sbin/dnsmasq -d --hostsdir /app/hosts.d/ -p 5353 & + +/usr/local/bin/python3 /app/main.py & + +wait -n + +exit $?