From 58496200caa3d8289f8449606dae3478d61515da Mon Sep 17 00:00:00 2001 From: Bryan Cerrati Date: Tue, 4 Mar 2025 14:42:20 -0500 Subject: [PATCH] Fully Working --- app.py | 33 ----------------- app/__init__.py | 30 ++++++++++++++++ app/models.py | 14 ++++++++ app/routes/__init__.py | 0 app/routes/domain.py | 60 +++++++++++++++++++++++++++++++ app/routes/ssl.py | 51 ++++++++++++++++++++++++++ app/templates/add_domain.html | 27 ++++++++++++++ app/templates/add_ssl_domain.html | 27 ++++++++++++++ app/templates/base.html | 27 ++++++++++++++ app/templates/domains.html | 27 ++++++++++++++ app/templates/home.html | 6 ++++ app/templates/ssl.html | 35 ++++++++++++++++++ app/utils.py | 16 +++++++++ run.py | 6 ++++ templates/add_domain.html | 33 ----------------- templates/home.html | 28 --------------- 16 files changed, 326 insertions(+), 94 deletions(-) delete mode 100644 app.py create mode 100644 app/__init__.py create mode 100644 app/models.py create mode 100644 app/routes/__init__.py create mode 100644 app/routes/domain.py create mode 100644 app/routes/ssl.py create mode 100644 app/templates/add_domain.html create mode 100644 app/templates/add_ssl_domain.html create mode 100644 app/templates/base.html create mode 100644 app/templates/domains.html create mode 100644 app/templates/home.html create mode 100644 app/templates/ssl.html create mode 100644 app/utils.py create mode 100644 run.py delete mode 100644 templates/add_domain.html delete mode 100644 templates/home.html diff --git a/app.py b/app.py deleted file mode 100644 index b363ae1..0000000 --- a/app.py +++ /dev/null @@ -1,33 +0,0 @@ -from flask import Flask, render_template, request, redirect, url_for -import whois - -app = Flask(__name__) - -# In-memory list to store domain info. In production, you’d use a database. -domains = [] - -@app.route('/') -def home(): - return render_template('home.html', domains=domains) - -@app.route('/add', methods=['GET', 'POST']) -def add_domain(): - if request.method == 'POST': - # Get a list of domains from the form (one or multiple) - domain_list = request.form.getlist('domain') - for domain in domain_list: - if domain.strip(): # ignore empty strings - try: - w = whois.whois(domain) - # Some WHOIS responses return a list of dates. - exp_date = w.expiration_date - if isinstance(exp_date, list): - exp_date = exp_date[0] - except Exception as e: - exp_date = f"Error: {e}" - domains.append({'domain': domain, 'expiration_date': exp_date}) - return redirect(url_for('home')) - return render_template('add_domain.html') - -if __name__ == '__main__': - app.run(host='0.0.0.0', port=5050, debug=True) diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..24d649d --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,30 @@ +import os +from flask import Flask +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() + +def create_app(): + # Create the Flask app and set the template folder (located in app/templates) + app = Flask(__name__, template_folder='templates') + + # Configure the SQLite database + app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'domains.db') + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + + # Initialize SQLAlchemy with the app + db.init_app(app) + + with app.app_context(): + # Import models so that they are registered with SQLAlchemy before creating tables + from app import models + db.create_all() + + # Register blueprints + from app.routes.domain import domain_bp + app.register_blueprint(domain_bp) + + from app.routes.ssl import ssl_bp + app.register_blueprint(ssl_bp) + + return app diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000..60e63f1 --- /dev/null +++ b/app/models.py @@ -0,0 +1,14 @@ +from app import db + +class Domain(db.Model): + __tablename__ = 'domain' + __table_args__ = {'extend_existing': True} + + id = db.Column(db.Integer, primary_key=True) + domain = db.Column(db.String(255), unique=True, nullable=False) + expiration_date = db.Column(db.String(255)) # WHOIS expiration date + ssl_expiration_date = db.Column(db.String(255)) # SSL certificate expiration date + ssl_only = db.Column(db.Boolean, default=False) # True if added via the SSL-only form + + def __repr__(self): + return f"" diff --git a/app/routes/__init__.py b/app/routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/routes/domain.py b/app/routes/domain.py new file mode 100644 index 0000000..d960412 --- /dev/null +++ b/app/routes/domain.py @@ -0,0 +1,60 @@ +from flask import Blueprint, render_template, request, redirect, url_for +import whois +from app.models import Domain +from app import db +from app.utils import get_ssl_expiration + +domain_bp = Blueprint('domain', __name__) + +@domain_bp.route('/') +def home(): + return render_template('home.html') + +@domain_bp.route('/domains') +def domains(): + # Show only non-ssl_only domains on the normal Domains page. + domains_list = Domain.query.filter_by(ssl_only=False).all() + return render_template('domains.html', domains=domains_list) + +@domain_bp.route('/add', methods=['GET', 'POST']) +def add_domain(): + if request.method == 'POST': + domain_list = request.form.getlist('domain') + for domain_name in domain_list: + if domain_name.strip(): + # Perform WHOIS lookup + try: + w = whois.whois(domain_name) + exp_date = w.expiration_date + if isinstance(exp_date, list): + exp_date = exp_date[0] + if exp_date and hasattr(exp_date, "strftime"): + whois_exp_date_str = exp_date.strftime("%m/%d/%Y") + else: + whois_exp_date_str = str(exp_date) + except Exception as e: + whois_exp_date_str = f"Error: {e}" + + # Perform SSL lookup using our utility function + ssl_exp_date_str = get_ssl_expiration(domain_name) + + # Only add if not already present. + if not Domain.query.filter_by(domain=domain_name).first(): + new_domain = Domain( + domain=domain_name, + expiration_date=whois_exp_date_str, + ssl_expiration_date=ssl_exp_date_str, + ssl_only=False + ) + db.session.add(new_domain) + db.session.commit() + return redirect(url_for('domain.domains')) + return render_template('add_domain.html') + +@domain_bp.route('/delete/', methods=['GET']) +def delete_domain(domain_id): + domain_to_delete = Domain.query.get_or_404(domain_id) + db.session.delete(domain_to_delete) + db.session.commit() + # Redirect back to the Domains page (or SSL page if needed) + return redirect(url_for('domain.domains')) diff --git a/app/routes/ssl.py b/app/routes/ssl.py new file mode 100644 index 0000000..915aa8c --- /dev/null +++ b/app/routes/ssl.py @@ -0,0 +1,51 @@ +from flask import Blueprint, render_template, request, redirect, url_for +from app.models import Domain +from app import db +from app.utils import get_ssl_expiration + +ssl_bp = Blueprint('ssl', __name__) + +@ssl_bp.route('/ssl') +def ssl_page(): + # Get all domains (both normal and ssl_only) + domains = Domain.query.all() + # Update each domain's SSL expiration (optional, to refresh if needed) + for d in domains: + current_ssl_exp = get_ssl_expiration(d.domain) + if d.ssl_expiration_date != current_ssl_exp: + d.ssl_expiration_date = current_ssl_exp + db.session.commit() + return render_template('ssl.html', domains=domains) + +@ssl_bp.route('/ssl/add', methods=['GET', 'POST']) +def add_ssl_domain(): + if request.method == 'POST': + domain_list = request.form.getlist('domain') + for domain_name in domain_list: + if domain_name.strip(): + # Both lookups are performed when adding an SSL-only domain. + try: + w = whois.whois(domain_name) + exp_date = w.expiration_date + if isinstance(exp_date, list): + exp_date = exp_date[0] + if exp_date and hasattr(exp_date, "strftime"): + whois_exp_date_str = exp_date.strftime("%m/%d/%Y") + else: + whois_exp_date_str = str(exp_date) + except Exception as e: + whois_exp_date_str = f"Error: {e}" + + ssl_exp_date_str = get_ssl_expiration(domain_name) + + if not Domain.query.filter_by(domain=domain_name).first(): + new_domain = Domain( + domain=domain_name, + expiration_date=whois_exp_date_str, + ssl_expiration_date=ssl_exp_date_str, + ssl_only=True + ) + db.session.add(new_domain) + db.session.commit() + return redirect(url_for('ssl.ssl_page')) + return render_template('add_ssl_domain.html') diff --git a/app/templates/add_domain.html b/app/templates/add_domain.html new file mode 100644 index 0000000..e2516ff --- /dev/null +++ b/app/templates/add_domain.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} +{% block title %}Add Domain{% endblock %} +{% block content %} +

Add Domain(s)

+
+
+ +
+
+ +

+ +
+
+Back to Domains + +{% endblock %} diff --git a/app/templates/add_ssl_domain.html b/app/templates/add_ssl_domain.html new file mode 100644 index 0000000..befe376 --- /dev/null +++ b/app/templates/add_ssl_domain.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} +{% block title %}Add SSL Domain{% endblock %} +{% block content %} +

Add SSL Domain(s)

+
+
+ +
+
+ +

+ +
+
+Back to SSL Page + +{% endblock %} diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 0000000..073f4f1 --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,27 @@ + + + + + {% block title %}My Domain App{% endblock %} + + + + + {% block content %}{% endblock %} + + diff --git a/app/templates/domains.html b/app/templates/domains.html new file mode 100644 index 0000000..ccabe4a --- /dev/null +++ b/app/templates/domains.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} +{% block title %}Domains{% endblock %} +{% block content %} +

Domains List

+ + + + + + + + + + {% for domain in domains %} + + + + + + {% endfor %} + +
DomainExpiration DateAction
{{ domain.domain }}{{ domain.expiration_date }} + X +
+
+ +{% endblock %} diff --git a/app/templates/home.html b/app/templates/home.html new file mode 100644 index 0000000..7a5a558 --- /dev/null +++ b/app/templates/home.html @@ -0,0 +1,6 @@ +{% extends "base.html" %} +{% block title %}Home{% endblock %} +{% block content %} +

Welcome to the Domain App

+

Use the navigation bar to view your domains or add new ones.

+{% endblock %} diff --git a/app/templates/ssl.html b/app/templates/ssl.html new file mode 100644 index 0000000..f5fa7aa --- /dev/null +++ b/app/templates/ssl.html @@ -0,0 +1,35 @@ +{% extends "base.html" %} +{% block title %}SSL Certificates{% endblock %} +{% block content %} +

SSL Certificates

+ + + + + + + + + + + {% for d in domains %} + + + + + + + {% endfor %} + +
DomainSSL ExpirationTypeAction
{{ d.domain }}{{ d.ssl_expiration_date }} + {% if d.ssl_only %} + Email + {% else %} + Domain + {% endif %} + + X +
+
+ +{% endblock %} diff --git a/app/utils.py b/app/utils.py new file mode 100644 index 0000000..4df8a42 --- /dev/null +++ b/app/utils.py @@ -0,0 +1,16 @@ +import socket, ssl, datetime + +def get_ssl_expiration(domain, port=443): + try: + ctx = ssl.create_default_context() + with socket.create_connection((domain, port), timeout=5) as sock: + with ctx.wrap_socket(sock, server_hostname=domain) as ssock: + cert = ssock.getpeercert() + notAfter = cert.get('notAfter') + if notAfter: + # Example certificate expiry format: "Jun 1 12:00:00 2025 GMT" + exp_date = datetime.datetime.strptime(notAfter, '%b %d %H:%M:%S %Y %Z') + return exp_date.strftime("%m/%d/%Y") + return "NO SSL" + except Exception: + return "NO SSL" diff --git a/run.py b/run.py new file mode 100644 index 0000000..da40171 --- /dev/null +++ b/run.py @@ -0,0 +1,6 @@ +from app import create_app + +app = create_app() + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5050, debug=True) \ No newline at end of file diff --git a/templates/add_domain.html b/templates/add_domain.html deleted file mode 100644 index 61d313f..0000000 --- a/templates/add_domain.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - Add Domain - - - -

Add Domain(s)

-
-
- -
-
- -

- -
-
- Back to Home - - diff --git a/templates/home.html b/templates/home.html deleted file mode 100644 index b8091ed..0000000 --- a/templates/home.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - Domain List - - -

Domain List

- - - - - - - - - {% for domain in domains %} - - - - - {% endfor %} - -
DomainExpiration Date
{{ domain.domain }}{{ domain.expiration_date }}
-
- Add Domain - -