Fully Working

main
Bryan Cerrati 2025-03-04 14:42:20 -05:00
parent 431bde4e44
commit 58496200ca
16 changed files with 326 additions and 94 deletions

33
app.py
View File

@ -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)

30
app/__init__.py Normal file
View File

@ -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

14
app/models.py Normal file
View File

@ -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"<Domain {self.domain} | Whois: {self.expiration_date} | SSL: {self.ssl_expiration_date} | ssl_only: {self.ssl_only}>"

0
app/routes/__init__.py Normal file
View File

60
app/routes/domain.py Normal file
View File

@ -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/<int:domain_id>', 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'))

51
app/routes/ssl.py Normal file
View File

@ -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')

View File

@ -0,0 +1,27 @@
{% extends "base.html" %}
{% block title %}Add Domain{% endblock %}
{% block content %}
<h1>Add Domain(s)</h1>
<form method="post">
<div id="domainFields">
<input type="text" name="domain" placeholder="Enter domain name">
</div>
<br>
<button type="button" onclick="addField()">Add Another</button>
<br><br>
<input type="submit" value="Save">
</form>
<br>
<a href="{{ url_for('domain.domains') }}">Back to Domains</a>
<script>
function addField() {
var container = document.getElementById("domainFields");
var input = document.createElement("input");
input.type = "text";
input.name = "domain";
input.placeholder = "Enter domain name";
container.appendChild(document.createElement("br"));
container.appendChild(input);
}
</script>
{% endblock %}

View File

@ -0,0 +1,27 @@
{% extends "base.html" %}
{% block title %}Add SSL Domain{% endblock %}
{% block content %}
<h1>Add SSL Domain(s)</h1>
<form method="post">
<div id="domainFields">
<input type="text" name="domain" placeholder="Enter domain name">
</div>
<br>
<button type="button" onclick="addField()">Add Another</button>
<br><br>
<input type="submit" value="Save">
</form>
<br>
<a href="{{ url_for('ssl.ssl_page') }}">Back to SSL Page</a>
<script>
function addField() {
var container = document.getElementById("domainFields");
var input = document.createElement("input");
input.type = "text";
input.name = "domain";
input.placeholder = "Enter domain name";
container.appendChild(document.createElement("br"));
container.appendChild(input);
}
</script>
{% endblock %}

27
app/templates/base.html Normal file
View File

@ -0,0 +1,27 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}My Domain App{% endblock %}</title>
<style>
nav {
background-color: #eee;
padding: 10px;
margin-bottom: 20px;
}
nav a {
margin-right: 15px;
text-decoration: none;
color: #333;
}
</style>
</head>
<body>
<nav>
<a href="{{ url_for('domain.home') }}">Home</a>
<a href="{{ url_for('domain.domains') }}">Domains</a>
<a href="{{ url_for('ssl.ssl_page') }}">SSL</a>
</nav>
{% block content %}{% endblock %}
</body>
</html>

View File

@ -0,0 +1,27 @@
{% extends "base.html" %}
{% block title %}Domains{% endblock %}
{% block content %}
<h1>Domains List</h1>
<table border="1" cellpadding="5" cellspacing="0">
<thead>
<tr>
<th>Domain</th>
<th>Expiration Date</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for domain in domains %}
<tr>
<td>{{ domain.domain }}</td>
<td>{{ domain.expiration_date }}</td>
<td>
<a href="{{ url_for('domain.delete_domain', domain_id=domain.id) }}" onclick="return confirm('Are you sure you want to delete this domain?');">X</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<br>
<button onclick="window.location.href='{{ url_for('domain.add_domain') }}'">Add Domain</button>
{% endblock %}

6
app/templates/home.html Normal file
View File

@ -0,0 +1,6 @@
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
<h1>Welcome to the Domain App</h1>
<p>Use the navigation bar to view your domains or add new ones.</p>
{% endblock %}

35
app/templates/ssl.html Normal file
View File

@ -0,0 +1,35 @@
{% extends "base.html" %}
{% block title %}SSL Certificates{% endblock %}
{% block content %}
<h1>SSL Certificates</h1>
<table border="1" cellpadding="5" cellspacing="0">
<thead>
<tr>
<th>Domain</th>
<th>SSL Expiration</th>
<th>Type</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for d in domains %}
<tr>
<td>{{ d.domain }}</td>
<td>{{ d.ssl_expiration_date }}</td>
<td>
{% if d.ssl_only %}
Email
{% else %}
Domain
{% endif %}
</td>
<td>
<a href="{{ url_for('domain.delete_domain', domain_id=d.id) }}" onclick="return confirm('Are you sure you want to delete this domain?');">X</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<br>
<button onclick="window.location.href='{{ url_for('ssl.add_ssl_domain') }}'">Add SSL Domain</button>
{% endblock %}

16
app/utils.py Normal file
View File

@ -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"

6
run.py Normal file
View File

@ -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)

View File

@ -1,33 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Add Domain</title>
<script>
function addField() {
var container = document.getElementById("domainFields");
var input = document.createElement("input");
input.type = "text";
input.name = "domain";
input.placeholder = "Enter domain name";
// Create a line break for spacing
container.appendChild(document.createElement("br"));
container.appendChild(input);
}
</script>
</head>
<body>
<h1>Add Domain(s)</h1>
<form method="post">
<div id="domainFields">
<input type="text" name="domain" placeholder="Enter domain name">
</div>
<br>
<button type="button" onclick="addField()">Add Another</button>
<br><br>
<input type="submit" value="Save">
</form>
<br>
<a href="{{ url_for('home') }}">Back to Home</a>
</body>
</html>

View File

@ -1,28 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Domain List</title>
</head>
<body>
<h1>Domain List</h1>
<table border="1" cellpadding="5" cellspacing="0">
<thead>
<tr>
<th>Domain</th>
<th>Expiration Date</th>
</tr>
</thead>
<tbody>
{% for domain in domains %}
<tr>
<td>{{ domain.domain }}</td>
<td>{{ domain.expiration_date }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<br>
<a href="{{ url_for('add_domain') }}">Add Domain</a>
</body>
</html>