Skip to content

Commit

Permalink
feat: DocType DNS Record
Browse files Browse the repository at this point in the history
  • Loading branch information
s-aga-r committed Oct 8, 2024
1 parent 96f62da commit 021e71e
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 45 deletions.
10 changes: 8 additions & 2 deletions mail/mail/doctype/dkim_key/dkim_key.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,14 @@
],
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-09-04 20:16:14.814445",
"links": [
{
"group": "General",
"link_doctype": "DNS Record",
"link_fieldname": "attached_to_docname"
}
],
"modified": "2024-10-08 12:36:24.443179",
"modified_by": "Administrator",
"module": "Mail",
"name": "DKIM Key",
Expand Down
27 changes: 14 additions & 13 deletions mail/mail/doctype/dkim_key/dkim_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from frappe import _, generate_hash
from frappe.model.document import Document
from frappe.utils.caching import request_cache
from mail.mail.doctype.dns_record.dns_record import create_or_update_dns_record


class DKIMKey(Document):
Expand All @@ -18,6 +19,7 @@ def validate(self) -> None:
self.generate_dkim_keys()

def after_insert(self) -> None:
self.create_or_update_dns_record()
self.disable_existing_dkim_keys()

def on_trash(self) -> None:
Expand Down Expand Up @@ -46,6 +48,18 @@ def generate_dkim_keys(self) -> None:

self.private_key, self.public_key = generate_dkim_keys(cint(self.key_size))

def create_or_update_dns_record(self) -> None:
"""Creates or Updates the DNS Record."""

create_or_update_dns_record(
host=f"{self.name}._domainkey",
type="TXT",
value=f"v=DKIM1; k=rsa; p={self.public_key}",
category="Sending Record",
attached_to_doctype=self.doctype,
attached_to_docname=self.name,
)

def disable_existing_dkim_keys(self) -> None:
"""Disables the existing DKIM Keys."""

Expand All @@ -60,19 +74,6 @@ def disable_existing_dkim_keys(self) -> None:
)
).run()

def get_dkim_record(self) -> dict:
"""Returns the DKIM Record."""

from mail.utils.cache import get_root_domain_name

return {
"category": "Sending Record",
"type": "TXT",
"host": f"{self.name}._domainkey.{get_root_domain_name()}",
"value": f"v=DKIM1; k=rsa; p={self.public_key}",
"ttl": frappe.db.get_single_value("Mail Settings", "default_ttl", cache=True),
}


def create_dkim_key(domain_name: str, key_size: int | None = None) -> "DKIMKey":
"""Creates a DKIM Key document."""
Expand Down
Empty file.
30 changes: 30 additions & 0 deletions mail/mail/doctype/dns_record/dns_record.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on("DNS Record", {
refresh(frm) {
frm.trigger("add_actions");
},
add_actions(frm) {
if (!frm.doc.__islocal) {
frm.add_custom_button(__("Verify DNS Record"), () => {
frm.trigger("verify_dns_record");
}, __("Actions"));
}
},
verify_dns_record(frm) {
frappe.call({
doc: frm.doc,
method: "verify_dns_record",
args: {
save: true,
},
freeze: true,
freeze_message: __("Verifying DNS Record..."),
callback: (r) => {
if (!r.exc) {
frm.refresh();
}
}
});
},
});
162 changes: 162 additions & 0 deletions mail/mail/doctype/dns_record/dns_record.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
{
"actions": [],
"autoname": "hash",
"creation": "2024-10-08 12:25:53.593747",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"section_break_lxj3",
"is_verified",
"column_break_ihb5",
"last_checked_at",
"section_break_bazr",
"attached_to_doctype",
"column_break_topm",
"attached_to_docname",
"section_break_gd3y",
"category",
"host",
"type",
"priority",
"ttl",
"column_break_hszw",
"value"
],
"fields": [
{
"fieldname": "type",
"fieldtype": "Select",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Type",
"options": "\nA\nAAAA\nCNAME\nMX\nTXT",
"reqd": 1,
"set_only_once": 1
},
{
"fieldname": "host",
"fieldtype": "Data",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Host",
"length": 255,
"reqd": 1,
"set_only_once": 1
},
{
"fieldname": "value",
"fieldtype": "Text",
"ignore_xss_filter": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Value",
"reqd": 1
},
{
"depends_on": "eval: doc.type == \"MX\"",
"fieldname": "priority",
"fieldtype": "Int",
"in_standard_filter": 1,
"label": "Priority",
"no_copy": 1,
"non_negative": 1
},
{
"fieldname": "ttl",
"fieldtype": "Int",
"in_standard_filter": 1,
"label": "TTL",
"non_negative": 1
},
{
"fieldname": "category",
"fieldtype": "Select",
"in_standard_filter": 1,
"label": "Category",
"options": "\nSending Record\nReceiving Record\nTracking Record\nServer Record",
"reqd": 1
},
{
"default": "0",
"depends_on": "eval: !doc.__islocal",
"fieldname": "is_verified",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Verified",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "section_break_lxj3",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_ihb5",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_bazr",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_hszw",
"fieldtype": "Column Break"
},
{
"fieldname": "attached_to_doctype",
"fieldtype": "Select",
"label": "Attached To DocType",
"options": "\nDKIM Key",
"set_only_once": 1
},
{
"fieldname": "column_break_topm",
"fieldtype": "Column Break"
},
{
"fieldname": "attached_to_docname",
"fieldtype": "Dynamic Link",
"label": "Attached To DocName",
"options": "attached_to_doctype",
"set_only_once": 1
},
{
"fieldname": "section_break_gd3y",
"fieldtype": "Section Break"
},
{
"fieldname": "last_checked_at",
"fieldtype": "Datetime",
"in_standard_filter": 1,
"label": "Last Checked At",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-10-08 12:25:53.593747",
"modified_by": "Administrator",
"module": "Mail",
"name": "DNS Record",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
9 changes: 9 additions & 0 deletions mail/mail/doctype/dns_record/dns_record.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

# import frappe
from frappe.model.document import Document


class DNSRecord(Document):
pass
78 changes: 78 additions & 0 deletions mail/mail/doctype/dns_record/test_dns_record.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

import frappe
from frappe import _
from frappe.utils import now
from frappe.model.document import Document


class DNSRecord(Document):
def validate(self):
if self.is_new():
self.validate_ttl()

def validate_ttl(self) -> None:
"""Validates the TTL value"""
if not self.ttl:
self.ttl = frappe.db.get_single_value("Mail Settings", "default_ttl", cache=True)

def get_fqdn(self) -> str:
"""Returns the Fully Qualified Domain Name"""

from mail.utils.cache import get_root_domain_name

return f"{self.host}.{get_root_domain_name()}"

@frappe.whitelist()
def verify_dns_record(self, save: bool = False) -> None:
"""Verifies the DNS Record"""

from mail.utils import verify_dns_record

self.is_verified = 0
self.last_checked_at = now()
if verify_dns_record(self.get_fqdn(), self.type, self.value):
self.is_verified = 1
frappe.msgprint(
_("Verified {0}:{1} record.").format(
frappe.bold(self.get_fqdn()), frappe.bold(self.type)
),
indicator="green",
alert=True,
)
if save:
self.save()


def create_or_update_dns_record(
host: str,
type: str,
value: str,
ttl: int | None = None,
priority: int | None = None,
category: str | None = None,
attached_to_doctype: str | None = None,
attached_to_docname: str | None = None,
) -> "DNSRecord":
"""Creates or updates a DNS Record"""

if dns_record := frappe.db.exists("DNS Record", {"host": host, "type": type}):
dns_record = frappe.get_doc("DNS Record", dns_record)
else:
dns_record = frappe.new_doc("DNS Record")
dns_record.host = host
dns_record.type = type
dns_record.attached_to_doctype = attached_to_doctype
dns_record.attached_to_docname = attached_to_docname
dns_record.value = value
dns_record.ttl = ttl
dns_record.priority = priority
dns_record.category = category
dns_record.save()

return dns_record


def after_doctype_insert():
frappe.db.add_unique("DNS Record", ["host", "type"])
Loading

0 comments on commit 021e71e

Please sign in to comment.