Client / Domain Import / Export Refactor
parent
a569ac5f14
commit
a5d6dea801
|
|
@ -1,6 +1,6 @@
|
||||||
import json
|
import json
|
||||||
from flask import Blueprint, render_template, request, redirect, url_for, flash, Response
|
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
|
from app import db
|
||||||
|
|
||||||
tools_bp = Blueprint('tools', __name__)
|
tools_bp = Blueprint('tools', __name__)
|
||||||
|
|
@ -9,48 +9,6 @@ tools_bp = Blueprint('tools', __name__)
|
||||||
def tools():
|
def tools():
|
||||||
return render_template('tools.html')
|
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'])
|
@tools_bp.route('/tools/password_categories', methods=['GET', 'POST'])
|
||||||
def password_categories():
|
def password_categories():
|
||||||
|
|
@ -87,3 +45,160 @@ def delete_password_category(category_id):
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("Category deleted and associated passwords reassigned to General.", "success")
|
flash("Category deleted and associated passwords reassigned to General.", "success")
|
||||||
return redirect(url_for('tools.password_categories'))
|
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')
|
||||||
|
|
@ -114,6 +114,7 @@
|
||||||
<!-- Section 3: Domains -->
|
<!-- Section 3: Domains -->
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<h2 class="section-title">Domains</h2>
|
<h2 class="section-title">Domains</h2>
|
||||||
|
{% if client.domains|length > 0 %}
|
||||||
<table class="table table-bordered table-hover">
|
<table class="table table-bordered table-hover">
|
||||||
<thead class="thead-light">
|
<thead class="thead-light">
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -139,11 +140,14 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p>No domain associations.</p>
|
||||||
|
{% endif %}
|
||||||
<form action="{{ url_for('clients.attach_domain', client_id=client.id) }}" method="post" class="form-inline">
|
<form action="{{ url_for('clients.attach_domain', client_id=client.id) }}" method="post" class="form-inline">
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
<label for="domain_id" class="sr-only">Select Domain</label>
|
<label for="domain_id" class="sr-only">Select Domain</label>
|
||||||
<select name="domain_id" class="form-control">
|
<select name="domain_id" class="form-control">
|
||||||
<option value=""> ---- </option>
|
<option value="">General</option>
|
||||||
{% for d in available_domains %}
|
{% for d in available_domains %}
|
||||||
<option value="{{ d.id }}">{{ d.domain }}</option>
|
<option value="{{ d.id }}">{{ d.domain }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
@ -151,13 +155,15 @@
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary mb-2 ml-2">Attach Domain</button>
|
<button type="submit" class="btn btn-primary mb-2 ml-2">Attach Domain</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<!-- Section 4: SSL -->
|
<!-- Section 4: SSL -->
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<h2 class="section-title">SSL</h2>
|
<h2 class="section-title">SSL</h2>
|
||||||
|
{% if client.ssl_domains|length > 0 %}
|
||||||
<table class="table table-bordered table-hover">
|
<table class="table table-bordered table-hover">
|
||||||
<thead class="thead-light">
|
<thead class="thead-light">
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -183,11 +189,14 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p>No SSL associations.</p>
|
||||||
|
{% endif %}
|
||||||
<form action="{{ url_for('clients.attach_ssl', client_id=client.id) }}" method="post" class="form-inline">
|
<form action="{{ url_for('clients.attach_ssl', client_id=client.id) }}" method="post" class="form-inline">
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
<label for="ssl_domain_id" class="sr-only">Select SSL Domain</label>
|
<label for="ssl_domain_id" class="sr-only">Select SSL Domain</label>
|
||||||
<select name="ssl_domain_id" class="form-control">
|
<select name="ssl_domain_id" class="form-control">
|
||||||
<option value=""> ---- </option>
|
<option value="">General</option>
|
||||||
{% for d in available_ssl %}
|
{% for d in available_ssl %}
|
||||||
<option value="{{ d.id }}">{{ d.domain }}</option>
|
<option value="{{ d.id }}">{{ d.domain }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
@ -195,7 +204,8 @@
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary mb-2 ml-2">Attach SSL Domain</button>
|
<button type="submit" class="btn btn-primary mb-2 ml-2">Attach SSL Domain</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Import Clients{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="my-4">Import Clients</h1>
|
||||||
|
<form action="{{ url_for('tools.import_clients') }}" method="post" enctype="multipart/form-data">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="file">Choose JSON file to import:</label>
|
||||||
|
<input type="file" name="file" id="file" class="form-control-file">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Import Clients</button>
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
<a href="{{ url_for('tools.tools') }}" class="btn btn-link">Back to Tools</a>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Import Domains{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="my-4">Import Domains</h1>
|
||||||
|
<form action="{{ url_for('tools.import_domains') }}" method="post" enctype="multipart/form-data">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="file">Choose JSON file to import:</label>
|
||||||
|
<input type="file" name="file" id="file" class="form-control-file">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Import Domains</button>
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
<a href="{{ url_for('tools.tools') }}" class="btn btn-link">Back to Tools</a>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -3,28 +3,32 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="my-4">Tools</h1>
|
<h1 class="my-4">Tools</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Password Categories Card -->
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">Import / Export Data</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="mb-4">
|
|
||||||
<form action="{{ url_for('tools.import_data') }}" method="post" enctype="multipart/form-data">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="file">Import Data:</label>
|
|
||||||
<input type="file" class="form-control-file" name="file" id="file">
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary">Import</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a href="{{ url_for('tools.export_data') }}" class="btn btn-secondary">Export Data</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Password Categories</div>
|
<div class="card-header">Password Categories</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<a href="{{ url_for('tools.password_categories') }}" class="btn btn-info">Manage Password Categories</a>
|
<a href="{{ url_for('tools.password_categories') }}" class="btn btn-info">Manage Password Categories</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Client Import / Export Card -->
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header">Client Import / Export</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<a href="{{ url_for('tools.export_clients') }}" class="btn btn-secondary">Export Clients</a>
|
||||||
|
<a href="{{ url_for('tools.import_clients') }}" class="btn btn-info">Import Clients</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Domain Import / Export Card (Matches the Client Section Style) -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">Domain Import / Export</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<a href="{{ url_for('tools.export_domains') }}" class="btn btn-secondary">Export Domains</a>
|
||||||
|
<a href="{{ url_for('tools.import_domains') }}" class="btn btn-info">Import Domains</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue