diff --git a/app/models.py b/app/models.py index 9aee967..9bf2b58 100644 --- a/app/models.py +++ b/app/models.py @@ -1039,7 +1039,9 @@ class Record(object): }) postdata_for_new = {"rrsets": final_records} - + logging.info(postdata_for_new) + logging.info(postdata_for_delete) + logging.info(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain)) try: headers = {} headers['X-API-Key'] = PDNS_API_KEY @@ -1358,3 +1360,83 @@ class Setting(db.Model): logging.debug(traceback.format_exec()) db.session.rollback() return False + + +class DomainTemplate(db.Model): + __tablename__ = "domain_template" + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(255), index=True, unique=True) + description = db.Column(db.String(255)) + records = db.relationship('DomainTemplateRecord', back_populates='template', cascade="all, delete-orphan") + + def __repr__(self): + return '' % self.name + + def __init__(self, name=None, description=None): + self.id = None + self.name = name + self.description = description + + def replace_records(self, records): + try: + self.records = [] + for record in records: + self.records.append(record) + db.session.commit() + return {'status': 'ok', 'msg': 'Template records have been modified'} + except Exception, e: + logging.error('Cannot create template records' + str(e)) + db.session.rollback() + return {'status': 'error', 'msg': 'Can not create template records'} + + def create(self): + try: + db.session.add(self) + db.session.commit() + return {'status': 'ok', 'msg': 'Template has been created'} + except Exception, e: + logging.error('Can not update domain template table.' + str(e)) + db.session.rollback() + return {'status': 'error', 'msg': 'Can not update domain template table'} + + def delete_template(self): + try: + self.records = [] + db.session.delete(self) + db.session.commit() + return {'status': 'ok', 'msg': 'Template has been deleted'} + except Exception, e: + logging.error('Can not delete domain template.' + str(e)) + db.session.rollback() + return {'status': 'error', 'msg': 'Can not delete domain template'} + + +class DomainTemplateRecord(db.Model): + __tablename__ = "domain_template_record" + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(255)) + type = db.Column(db.String(64)) + ttl = db.Column(db.Integer) + data = db.Column(db.String(255)) + status = db.Column(db.Boolean) + template_id = db.Column(db.Integer, db.ForeignKey('domain_template.id')) + template = db.relationship('DomainTemplate', back_populates='records') + + def __repr__(self): + return '' % self.id + + def __init__(self, id=None, name=None, type=None, ttl=None, data=None, status=None): + self.id = id + self.name = name + self.type = type + self.ttl = ttl + self.data = data + self.status = status + + def apply(self): + try: + db.session.commit() + except Exception, e: + logging.error('Can not update domain template table.' + str(e)) + db.session.rollback() + return {'status': 'error', 'msg': 'Can not update domain template table'} diff --git a/app/templates/base.html b/app/templates/base.html index 06f239b..81eec24 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -127,6 +127,7 @@
  • New Domain
  • ADMINISTRATION
  • Admin Console
  • +
  • Domain Templates
  • Users
  • History
  • Settings
  • diff --git a/app/templates/domain_add.html b/app/templates/domain_add.html index 04add24..8da216a 100644 --- a/app/templates/domain_add.html +++ b/app/templates/domain_add.html @@ -47,6 +47,15 @@ +
    + + +
    diff --git a/app/templates/template.html b/app/templates/template.html new file mode 100644 index 0000000..ac58c82 --- /dev/null +++ b/app/templates/template.html @@ -0,0 +1,119 @@ +{% extends "base.html" %} +{% block title %}DNS Control Panel - Templates{% endblock %} + +{% block dashboard_stat %} + +
    +

    + Templates + List +

    + +
    +{% endblock %} +{% block content %} + +
    + {% with errors = get_flashed_messages(category_filter=["error"]) %} {% if errors %} +
    +
    +
    + +

    + Error! +

    +
    + x +
      + {%- for msg in errors %} +
    • {{ msg }}
    • {% endfor -%} +
    +
    +
    +
    +
    +{% endif %} {% endwith %} +
    +
    +
    +
    +

    Templates

    +
    + +
    + + + + + + + + + + + + {% for template in templates %} + + + + + + + + {% endfor %} + +
    NameDescriptionNumber of RecordsEditDelete
    + {{ template.name }} + + {{ template.description }} + + {{ template.records|count }} + + + + + + + + +
    +
    + +
    + +
    + +
    + +
    + +{% endblock %} +{% block extrascripts %} + +{% endblock %} +{% block modals %} +{% endblock %} diff --git a/app/templates/template_add.html b/app/templates/template_add.html new file mode 100644 index 0000000..772c204 --- /dev/null +++ b/app/templates/template_add.html @@ -0,0 +1,104 @@ +{% extends "base.html" %} +{% block title %}DNS Control Panel - Create Template{% endblock %} + +{% block dashboard_stat %} + +
    +

    + Template + Create +

    + +
    +{% endblock %} + +{% block content %} +
    +{% with errors = get_flashed_messages(category_filter=["error"]) %} {% +if errors %} +
    +
    +
    + +

    + Error! +

    +
    + x +
      + {%- for msg in errors %} +
    • {{ msg }}
    • {% endfor -%} +
    +
    +
    +
    +
    +{% endif %} {% endwith %} + +
    +
    +
    +
    +

    Create new template

    +
    + + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    +

    Help with creating a new template

    +
    +
    +
    +
    Template name
    +
    Enter your template name, this is the name of the template that + will be shown to users. The name should not have any spaces but + can have symbols.
    +
    Template description
    +
    Enter your template description, this is to help better + identify the template.
    +
    +
    +
    +
    +
    +
    +{% endblock %} +{% block extrascripts %} + +{% endblock %} diff --git a/app/templates/template_edit.html b/app/templates/template_edit.html new file mode 100644 index 0000000..64f9799 --- /dev/null +++ b/app/templates/template_edit.html @@ -0,0 +1,415 @@ +{% extends "base.html" %} +{% block title %}DNS Control Panel - Edit Template{% endblock %} + +{% block dashboard_stat %} +
    +

    + Edit template {{ template }} +

    + +
    +{% endblock %} + +{% block content %} +
    +
    +
    +
    +
    +

    Manage Template Records for {{ template }}

    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + {% for record in records %} + + + + + + + + + + + + {% endfor %} + +
    NameTypeStatusTTLDataEditDeleteID
    + {{ record.name }} + + {{ record.type }} + + {{ record.status }} + + {{ record.ttl }} + + {{ record.data }} + + + + + + {{ record.id }} +
    +
    +
    +
    +
    +
    +{% endblock %} +{% block extrascripts %} + +{% endblock %} +{% block modals %} + + + +{% endblock %} diff --git a/app/views.py b/app/views.py index 8cc8761..8cab755 100644 --- a/app/views.py +++ b/app/views.py @@ -11,12 +11,12 @@ from io import BytesIO import jinja2 import qrcode as qrc import qrcode.image.svg as qrc_svg -from flask import g, request, make_response, jsonify, render_template, session, redirect, url_for, send_from_directory, abort +from flask import g, request, make_response, jsonify, render_template, session, redirect, url_for, send_from_directory, abort, flash from flask_login import login_user, logout_user, current_user, login_required from werkzeug import secure_filename from werkzeug.security import gen_salt -from .models import User, Domain, Record, Server, History, Anonymous, Setting, DomainSetting +from .models import User, Domain, Record, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord from app import app, login_manager, github from lib import utils @@ -331,10 +331,12 @@ def domain(domain_name): @login_required @admin_role_required def domain_add(): + templates = DomainTemplate.query.all() if request.method == 'POST': try: domain_name = request.form.getlist('domain_name')[0] domain_type = request.form.getlist('radio_type')[0] + domain_template = request.form.getlist('domain_template')[0] soa_edit_api = request.form.getlist('radio_type_soa_edit_api')[0] if ' ' in domain_name or not domain_name or not domain_type: @@ -352,12 +354,27 @@ def domain_add(): if result['status'] == 'ok': history = History(msg='Add domain %s' % domain_name, detail=str({'domain_type': domain_type, 'domain_master_ips': domain_master_ips}), created_by=current_user.username) history.add() + if domain_template != '0': + template = DomainTemplate.query.filter(DomainTemplate.id == domain_template).first() + template_records = DomainTemplateRecord.query.filter(DomainTemplateRecord.template_id == domain_template).all() + record_data = [] + for template_record in template_records: + record_row = {'record_data': template_record.data, 'record_name': template_record.name, 'record_status': template_record.status, 'record_ttl': template_record.ttl, 'record_type': template_record.type} + record_data.append(record_row) + r = Record() + result = r.apply(domain_name, record_data) + if result['status'] == 'ok': + history = History(msg='Applying template %s to %s, created records successfully.' % (template.name, domain_name), detail=str(result), created_by=current_user.username) + history.add() + else: + history = History(msg='Applying template %s to %s, FAILED to created records.' % (template.name, domain_name), detail=str(result), created_by=current_user.username) + history.add() return redirect(url_for('dashboard')) else: return render_template('errors/400.html', msg=result['msg']), 400 except: return redirect(url_for('error', code=500)) - return render_template('domain_add.html') + return render_template('domain_add.html', templates=templates) @app.route('/admin/domain//delete', methods=['GET']) @@ -518,6 +535,120 @@ def admin_setdomainsetting(domain_name): return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400) +@app.route('/templates', methods=['GET', 'POST']) +@app.route('/templates/list', methods=['GET', 'POST']) +@login_required +@admin_role_required +def templates(): + templates = DomainTemplate.query.all() + return render_template('template.html', templates=templates) + + +@app.route('/template/create', methods=['GET', 'POST']) +@login_required +@admin_role_required +def create_template(): + if request.method == 'GET': + return render_template('template_add.html') + if request.method == 'POST': + try: + name = request.form.getlist('name')[0] + description = request.form.getlist('description')[0] + + if ' ' in name or not name or not type: + flash("Please correct your input", 'error') + return redirect(url_for('create_template')) + + if DomainTemplate.query.filter(DomainTemplate.name == name).first(): + flash("A template with the name %s already exists!" % name, 'error') + return redirect(url_for('create_template')) + t = DomainTemplate(name=name, description=description) + result = t.create() + if result['status'] == 'ok': + history = History(msg='Add domain template %s' % name, detail=str({'name': name, 'description': description}), created_by=current_user.username) + history.add() + return redirect(url_for('templates')) + else: + flash(result['msg'], 'error') + return redirect(url_for('create_template')) + except Exception: + print traceback.format_exc() + return redirect(url_for('error', code=500)) + return redirect(url_for('templates')) + + +@app.route('/template//edit', methods=['GET']) +@login_required +@admin_role_required +def edit_template(template): + try: + t = DomainTemplate.query.filter(DomainTemplate.name == template).first() + if t is not None: + records = [] + for jr in t.records: + if jr.type in app.config['RECORDS_ALLOW_EDIT']: + record = DomainTemplateRecord(name=jr.name, type=jr.type, status='Disabled' if jr.status else 'Active', ttl=jr.ttl, data=jr.data) + records.append(record) + + return render_template('template_edit.html', template=t.name, records=records, editable_records=app.config['RECORDS_ALLOW_EDIT']) + except Exception: + print traceback.format_exc() + return redirect(url_for('error', code=500)) + return redirect(url_for('templates')) + + +@app.route('/template//apply', methods=['POST'], strict_slashes=False) +@login_required +def apply_records(template): + try: + pdata = request.data + jdata = json.loads(pdata) + records = [] + + for j in jdata: + name = '@' if j['record_name'] in ['@', ''] else j['record_name'] + type = j['record_type'] + data = j['record_data'] + disabled = True if j['record_status'] == 'Disabled' else False + ttl = int(j['record_ttl']) if j['record_ttl'] else 3600 + + dtr = DomainTemplateRecord(name=name, type=type, data=data, status=disabled, ttl=ttl) + records.append(dtr) + + t = DomainTemplate.query.filter(DomainTemplate.name == template).first() + result = t.replace_records(records) + if result['status'] == 'ok': + history = History(msg='Apply domain template record changes to domain template %s' % template, detail=str(jdata), created_by=current_user.username) + history.add() + return make_response(jsonify(result), 200) + else: + return make_response(jsonify(result), 400) + except: + print traceback.format_exc() + return make_response(jsonify({'status': 'error', 'msg': 'Error when applying new changes'}), 500) + + +@app.route('/template//delete', methods=['GET']) +@login_required +@admin_role_required +def delete_template(template): + try: + t = DomainTemplate.query.filter(DomainTemplate.name == template).first() + if t is not None: + result = t.delete_template() + if result['status'] == 'ok': + history = History(msg='Deleted domain template %s' % template, detail=str({'name': template}), created_by=current_user.username) + history.add() + return redirect(url_for('templates')) + else: + flash(result['msg'], 'error') + return redirect(url_for('templates')) + except Exception: + print traceback.format_exc() + return redirect(url_for('error', code=500)) + return redirect(url_for('templates')) + + @app.route('/admin', methods=['GET', 'POST']) @login_required @admin_role_required diff --git a/create_db.py b/create_db.py index 41c874d..25899fd 100755 --- a/create_db.py +++ b/create_db.py @@ -4,7 +4,7 @@ from migrate.versioning import api from config import SQLALCHEMY_DATABASE_URI from config import SQLALCHEMY_MIGRATE_REPO from app import db -from app.models import Role, Setting +from app.models import Role, Setting, DomainTemplate import os.path import time import sys @@ -65,6 +65,23 @@ def init_settings(db, setting_names): for setting in settings: db.session.add(setting) + +def init_domain_templates(db, domain_template_names): + + # Get key name of data + name_of_domain_templates = map(lambda r: r.name, domain_template_names) + + # Query to get current data + rows = db.session.query(DomainTemplate).filter(DomainTemplate.name.in_(name_of_domain_templates)).all() + + # Check which data that need to insert + name_of_rows = map(lambda r: r.name, rows) + domain_templates = filter(lambda r: r.name not in name_of_rows, domain_template_names) + + # Insert data + for domain_template in domain_templates: + db.session.add(domain_template) + def init_records(): # Create initial user roles and turn off maintenance mode init_roles(db, [ @@ -80,7 +97,12 @@ def init_records(): Setting('default_domain_table_size', '10'), Setting('auto_ptr','False') ]) - + # TODO: add sample records to sample templates + init_domain_templates(db, [ + DomainTemplate('basic_template_1', 'Basic Template #1'), + DomainTemplate('basic_template_2', 'Basic Template #2'), + DomainTemplate('basic_template_3', 'Basic Template #3') + ]) db_commit = db.session.commit() commit_version_control(db_commit)