From 34295c0c18ddbadfd9f15a330322d4c8b5889177 Mon Sep 17 00:00:00 2001 From: it00147 Date: Wed, 26 Nov 2025 13:02:45 +0000 Subject: [PATCH] Latest Version --- Dockerfile | 2 +- README.md | 20 +------- app.py | 125 +++++++++++++++++++++++++++++++++++++-------- static/favicon.png | Bin 0 -> 2650 bytes static/logo.png | Bin 0 -> 11603 bytes static/myip.css | 37 ++++++++++++++ 6 files changed, 145 insertions(+), 39 deletions(-) create mode 100644 static/favicon.png create mode 100644 static/myip.css diff --git a/Dockerfile b/Dockerfile index dfcbf85..55136b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,4 +4,4 @@ COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 -CMD ["python","app.py"] +CMD ["python","app.py"] \ No newline at end of file diff --git a/README.md b/README.md index aba9c29..6c40a3c 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,3 @@ -# IP WebApp +# myip WebApp Modern UI -Una semplice webapp Flask che mostra: -- IP pubblico del visitatore -- Informazioni RIPE (ASN, holder, prefix) - -## Build - -``` -docker build -t ip-webapp . -``` - -## Run - -``` -docker run --rm -p 8080:8000 ip-webapp -``` - -Visita: http://localhost:8080 +Include Montserrat, animations, copy-IP button, favicon. diff --git a/app.py b/app.py index f6c8bc2..382f322 100644 --- a/app.py +++ b/app.py @@ -1,53 +1,138 @@ from flask import Flask, request, render_template_string import requests +from werkzeug.middleware.proxy_fix import ProxyFix app = Flask(__name__) +app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1) HTML_TEMPLATE = """ - Il tuo IP pubblico + myip – IP & RIPE Lookup + + + + + + + -

Il tuo IP pubblico è: {{ ip }}

- {% if ripe %} -

Informazioni RIPE

-

Provider: {{ ripe.holder }}

-

Prefisso: {{ ripe.prefix }}

-

ASN: {{ ripe.asns|join(', ') }}

- {% endif %} +
+ {% if show_logo %} + + {% endif %} +
+ + IP & RIPE Lookup +
+

Il tuo IP pubblico è:

+ +
+ {{ ip }} + +
+ +
+ + {% if ripe %} +
Informazioni RIPE
+ {% if ripe.holder %} +
Provider: {{ ripe.holder }}
+ {% endif %} + {% if ripe.prefix %} +
Prefisso: {{ ripe.prefix }}
+ {% endif %} + {% if ripe.asns %} +
ASN: {{ ripe.asns | join(', ') }}
+ {% endif %} +
Dati ottenuti tramite RIPEstat Data API.
+ {% else %} +
Informazioni RIPE
+
+ Non è stato possibile recuperare i dettagli RIPE per questo indirizzo. +
+ {% endif %} +
+ + """ +RIPESTAT_BASE = "https://stat.ripe.net/data" + def get_client_ip(): xff = request.headers.get("X-Forwarded-For", "") if xff: - return xff.split(",")[0].strip() + parts = [p.strip() for p in xff.split(",") if p.strip()] + if parts: + return parts[0] + xri = request.headers.get("X-Real-IP") + if xri: + return xri.strip() return request.remote_addr or "Sconosciuto" def fetch_ripe_info(ip: str): try: - ni = requests.get("https://stat.ripe.net/data/network-info/data.json", - params={"resource": ip}, timeout=2).json().get("data", {}) - prefix = ni.get("prefix") - asns = ni.get("asns") or [] - holder=None + ni_resp = requests.get(f"{RIPESTAT_BASE}/network-info/data.json", params={"resource": ip}, timeout=2) + ni_resp.raise_for_status() + ni_data = ni_resp.json().get("data", {}) + + prefix = ni_data.get("prefix") + asns = ni_data.get("asns") or [] + holder = None + if asns: - ao = requests.get("https://stat.ripe.net/data/as-overview/data.json", - params={"resource": asns[0]}, timeout=2).json().get("data", {}) - holder = ao.get("holder") - return {"prefix": prefix, "asns":[f"AS{a}" for a in asns], "holder": holder} + first_asn = asns[0] + ao_resp = requests.get(f"{RIPESTAT_BASE}/as-overview/data.json", params={"resource": first_asn}, timeout=2) + ao_resp.raise_for_status() + holder = ao_resp.json().get("data", {}).get("holder") + + class R: pass + r = R() + r.prefix = prefix + r.asns = [f"AS{x}" for x in asns] if asns else [] + r.holder = holder + + return r if any([r.prefix, r.asns, r.holder]) else None except: return None @app.route("/") def index(): ip = get_client_ip() - ripe = fetch_ripe_info(ip) if ip!="Sconosciuto" else None - return render_template_string(HTML_TEMPLATE, ip=ip, ripe=ripe) + ripe = fetch_ripe_info(ip) if ip != "Sconosciuto" else None + return render_template_string(HTML_TEMPLATE, ip=ip, ripe=ripe, show_logo=True) if __name__ == "__main__": app.run(host="0.0.0.0", port=8000) diff --git a/static/favicon.png b/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..71399c1b1c6946d12d69992605ed31ff0c09f8bd GIT binary patch literal 2650 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 IM6N<$g5l5^n*aa+ literal 0 HcmV?d00001 diff --git a/static/logo.png b/static/logo.png index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fa08dc46603d60887130a062a481fd1d765791c7 100644 GIT binary patch literal 11603 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 RD>zFUK~7ppssaQF{0|Fh6x09! literal 0 HcmV?d00001 diff --git a/static/myip.css b/static/myip.css new file mode 100644 index 0000000..f258416 --- /dev/null +++ b/static/myip.css @@ -0,0 +1,37 @@ +body { + margin: 0; + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + font-family: 'Montserrat', Helvetica, Arial, sans-serif; + background: #0181C4; +} +.card { + background: #ffffff; + padding: 2.5rem 3rem; + border-radius: 0; + box-shadow: 0 18px 40px rgba(15, 23, 42, 0.6); + max-width: 520px; + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} +.title { + font-size: 1.25rem; + color: #101F2D; + margin-bottom: 0.5rem; +} +.ripe-title { + font-size: 1.1rem; + font-weight: 600; + margin-bottom: 0.5rem; + color: #101F2D; +} +.ripe-item { + font-size: 0.95rem; + color: #101F2D; + margin: 0.2rem 0; +} \ No newline at end of file