Fully Working
parent
431bde4e44
commit
58496200ca
33
app.py
33
app.py
|
|
@ -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)
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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,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'))
|
||||||
|
|
@ -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')
|
||||||
|
|
@ -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 %}
|
||||||
|
|
@ -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 %}
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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 %}
|
||||||
|
|
@ -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 %}
|
||||||
|
|
@ -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 %}
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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>
|
|
||||||
Loading…
Reference in New Issue