diff --git a/app/routes/tools.py b/app/routes/tools.py
index cfacc25..721d8d5 100644
--- a/app/routes/tools.py
+++ b/app/routes/tools.py
@@ -1,6 +1,6 @@
import json
from flask import Blueprint, render_template, request, redirect, url_for, flash, Response
-from app.models import Domain, PasswordCategory
+from app.models import Client, Domain, ClientPassword, PasswordCategory
from app import db
tools_bp = Blueprint('tools', __name__)
@@ -9,48 +9,6 @@ tools_bp = Blueprint('tools', __name__)
def tools():
return render_template('tools.html')
-@tools_bp.route('/tools/export', methods=['GET'])
-def export_data():
- domains = Domain.query.all()
- data = []
- for d in domains:
- data.append({
- 'id': d.id,
- 'domain': d.domain,
- 'expiration_date': d.expiration_date,
- 'ssl_expiration_date': d.ssl_expiration_date,
- 'ssl_only': d.ssl_only
- })
- json_data = json.dumps(data)
- response = Response(json_data, mimetype='application/json')
- response.headers['Content-Disposition'] = 'attachment; filename=domains_data.json'
- return response
-
-@tools_bp.route('/tools/import', methods=['GET', 'POST'])
-def import_data():
- if request.method == 'POST':
- file = request.files.get('file')
- if file:
- try:
- data = json.load(file)
- for record in data:
- existing = Domain.query.filter_by(domain=record.get('domain')).first()
- if not existing:
- new_domain = Domain(
- domain=record.get('domain'),
- expiration_date=record.get('expiration_date'),
- ssl_expiration_date=record.get('ssl_expiration_date'),
- ssl_only=record.get('ssl_only', False)
- )
- db.session.add(new_domain)
- db.session.commit()
- flash("Data imported successfully", "success")
- except Exception as e:
- flash(f"Error importing data: {str(e)}", "danger")
- else:
- flash("No file uploaded", "warning")
- return redirect(url_for('tools.tools'))
- return render_template('tools.html')
@tools_bp.route('/tools/password_categories', methods=['GET', 'POST'])
def password_categories():
@@ -87,3 +45,160 @@ def delete_password_category(category_id):
db.session.commit()
flash("Category deleted and associated passwords reassigned to General.", "success")
return redirect(url_for('tools.password_categories'))
+
+@tools_bp.route('/tools/export_clients', methods=['GET'])
+def export_clients():
+ clients = Client.query.all()
+ data = []
+ for c in clients:
+ client_dict = {
+ 'name': c.name,
+ 'phone': c.phone,
+ 'address': c.address,
+ 'passwords': [],
+ 'domains': [],
+ 'ssl_domains': []
+ }
+ for p in c.passwords:
+ client_dict['passwords'].append({
+ 'item': p.item,
+ 'username': p.username,
+ 'password': p.password,
+ 'category': p.category.name if p.category else None
+ })
+ for d in c.domains:
+ client_dict['domains'].append({
+ 'domain': d.domain,
+ 'expiration_date': d.expiration_date,
+ 'ssl_expiration_date': d.ssl_expiration_date,
+ 'ssl_only': d.ssl_only
+ })
+ for d in c.ssl_domains:
+ client_dict['ssl_domains'].append({
+ 'domain': d.domain,
+ 'expiration_date': d.expiration_date,
+ 'ssl_expiration_date': d.ssl_expiration_date,
+ 'ssl_only': d.ssl_only
+ })
+ data.append(client_dict)
+ json_data = json.dumps(data)
+ response = Response(json_data, mimetype='application/json')
+ response.headers['Content-Disposition'] = 'attachment; filename=clients_data.json'
+ return response
+
+@tools_bp.route('/tools/import_clients', methods=['GET', 'POST'])
+def import_clients():
+ if request.method == 'POST':
+ file = request.files.get('file')
+ if file:
+ try:
+ data = json.load(file)
+ for client_data in data:
+ new_client = Client(
+ name = client_data.get('name'),
+ phone = client_data.get('phone'),
+ address = client_data.get('address')
+ )
+ db.session.add(new_client)
+ db.session.flush() # assign new_client.id
+ # Import passwords
+ for p in client_data.get('passwords', []):
+ category_name = p.get('category')
+ category = None
+ if category_name:
+ category = PasswordCategory.query.filter_by(name=category_name).first()
+ if not category:
+ category = PasswordCategory(name=category_name)
+ db.session.add(category)
+ db.session.flush()
+ new_password = ClientPassword(
+ item = p.get('item'),
+ username = p.get('username'),
+ password = p.get('password'),
+ client_id = new_client.id,
+ category = category
+ )
+ db.session.add(new_password)
+ # Import domain associations
+ for d in client_data.get('domains', []):
+ domain_name = d.get('domain')
+ if domain_name:
+ domain_obj = Domain.query.filter_by(domain=domain_name).first()
+ if not domain_obj:
+ domain_obj = Domain(
+ domain = domain_name,
+ expiration_date = d.get('expiration_date'),
+ ssl_expiration_date = d.get('ssl_expiration_date'),
+ ssl_only = d.get('ssl_only', False)
+ )
+ db.session.add(domain_obj)
+ db.session.flush()
+ new_client.domains.append(domain_obj)
+ # Import SSL domain associations
+ for d in client_data.get('ssl_domains', []):
+ domain_name = d.get('domain')
+ if domain_name:
+ domain_obj = Domain.query.filter_by(domain=domain_name).first()
+ if not domain_obj:
+ domain_obj = Domain(
+ domain = domain_name,
+ expiration_date = d.get('expiration_date'),
+ ssl_expiration_date = d.get('ssl_expiration_date'),
+ ssl_only = d.get('ssl_only', True)
+ )
+ db.session.add(domain_obj)
+ db.session.flush()
+ new_client.ssl_domains.append(domain_obj)
+ db.session.commit()
+ flash("Clients imported successfully", "success")
+ except Exception as e:
+ flash(f"Error importing clients: {str(e)}", "danger")
+ else:
+ flash("No file uploaded", "warning")
+ return redirect(url_for('tools.tools'))
+ return render_template('import_clients.html')
+
+
+@tools_bp.route('/tools/export_domains', methods=['GET'])
+def export_domains():
+ domains = Domain.query.all()
+ data = []
+ for d in domains:
+ data.append({
+ 'domain': d.domain,
+ 'expiration_date': d.expiration_date,
+ 'ssl_expiration_date': d.ssl_expiration_date,
+ 'ssl_only': d.ssl_only
+ })
+ json_data = json.dumps(data)
+ response = Response(json_data, mimetype='application/json')
+ response.headers['Content-Disposition'] = 'attachment; filename=domains_data.json'
+ return response
+
+@tools_bp.route('/tools/import_domains', methods=['GET', 'POST'])
+def import_domains():
+ if request.method == 'POST':
+ file = request.files.get('file')
+ if file:
+ try:
+ data = json.load(file)
+ for record in data:
+ domain_name = record.get('domain')
+ if domain_name:
+ existing = Domain.query.filter_by(domain=domain_name).first()
+ if not existing:
+ new_domain = Domain(
+ domain = domain_name,
+ expiration_date = record.get('expiration_date'),
+ ssl_expiration_date = record.get('ssl_expiration_date'),
+ ssl_only = record.get('ssl_only', False)
+ )
+ db.session.add(new_domain)
+ db.session.commit()
+ flash("Domains imported successfully", "success")
+ except Exception as e:
+ flash(f"Error importing domains: {str(e)}", "danger")
+ else:
+ flash("No file uploaded", "warning")
+ return redirect(url_for('tools.tools'))
+ return render_template('import_domains.html')
\ No newline at end of file
diff --git a/app/templates/client_detail.html b/app/templates/client_detail.html
index d0e7af0..3779f33 100644
--- a/app/templates/client_detail.html
+++ b/app/templates/client_detail.html
@@ -113,89 +113,99 @@
-
Domains
-
-
-
- | Domain |
- WHOIS Expiration |
- Action |
-
-
-
- {% for d in client.domains %}
-
- | {{ d.domain }} |
- {{ d.expiration_date }} |
-
-
- |
-
- {% endfor %}
-
-
-
-
+ Domains
+ {% if client.domains|length > 0 %}
+
+
+
+ | Domain |
+ WHOIS Expiration |
+ Action |
+
+
+
+ {% for d in client.domains %}
+
+ | {{ d.domain }} |
+ {{ d.expiration_date }} |
+
+
+ |
+
+ {% endfor %}
+
+
+ {% else %}
+ No domain associations.
+ {% endif %}
+
+
+
-
SSL
-
-
-
- | Domain |
- SSL Expiration |
- Action |
-
-
-
- {% for d in client.ssl_domains %}
-
- | {{ d.domain }} |
- {{ d.ssl_expiration_date }} |
-
-
- |
-
- {% endfor %}
-
-
-
-
+ SSL
+ {% if client.ssl_domains|length > 0 %}
+
+
+
+ | Domain |
+ SSL Expiration |
+ Action |
+
+
+
+ {% for d in client.ssl_domains %}
+
+ | {{ d.domain }} |
+ {{ d.ssl_expiration_date }} |
+
+
+ |
+
+ {% endfor %}
+
+
+ {% else %}
+ No SSL associations.
+ {% endif %}
+
+
+
diff --git a/app/templates/import_clients.html b/app/templates/import_clients.html
new file mode 100644
index 0000000..5b3cb83
--- /dev/null
+++ b/app/templates/import_clients.html
@@ -0,0 +1,16 @@
+{% extends "base.html" %}
+{% block title %}Import Clients{% endblock %}
+{% block content %}
+
+{% endblock %}
diff --git a/app/templates/import_domains.html b/app/templates/import_domains.html
new file mode 100644
index 0000000..e3b1cd5
--- /dev/null
+++ b/app/templates/import_domains.html
@@ -0,0 +1,16 @@
+{% extends "base.html" %}
+{% block title %}Import Domains{% endblock %}
+{% block content %}
+
+{% endblock %}
diff --git a/app/templates/tools.html b/app/templates/tools.html
index c6208e8..4d2d897 100644
--- a/app/templates/tools.html
+++ b/app/templates/tools.html
@@ -3,28 +3,32 @@
{% block content %}
Tools
+
+
+
-
+
+
+
+
+
+
{% endblock %}