domain registration, start on DNS

This commit is contained in:
2026-05-14 11:15:30 -04:00
parent 8c55871866
commit 43d4730803
12 changed files with 415 additions and 28 deletions
+1
View File
@@ -1,2 +1,3 @@
node_modules node_modules
ndm.db ndm.db
*.env
+80
View File
@@ -68,6 +68,86 @@ body {
padding-right: 8px; padding-right: 8px;
} }
.InsetContentBox {
border: 1px inset;
min-height: 200px;
background-color: white;
display: inline-block;
box-sizing: border-box;
vertical-align: middle;
}
.ContentBox {
border: 2px solid #4682b4;
min-height: 200px;
background-color: white;
display: inline-block;
box-sizing: border-box;
vertical-align: middle;
padding: 8px;
}
#DomainOptions {
text-align: center;
float: right;
}
#DomainSelection {
overflow: scroll;
zoom: 1;
}
#DomainSelection li:hover {
background-color: #2e69cb;
color: white;
cursor: pointer;
}
.SelectedListItem {
background-color: #2e69cb;
color: white;
}
ul {
margin: 0;
padding: 0;
}
li {
list-style: none;
padding: 8px;
}
.MutedText {
color: #afafaf;
}
.TitleText {
font-weight: bold;
font-size: 14px;
}
.Hidden {
display: none;
}
a {
text-decoration: none;
}
a:hover {
color: #800000;
}
a {
color: #0000ee;
}
.DefaultButton {
background-color: #f5cd2f;
cursor: pointer;
}
.VerticalInputForm { .VerticalInputForm {
display: inline-block; display: inline-block;
} }
+72
View File
@@ -0,0 +1,72 @@
// This script is designed to support at a minimum IE 6
//
// All menu functions are defined outside of initMenu.
//
// This is so I'm not duplicating functions between-
// checks for what browser we have
function resetSelection() {
var elements = document.getElementsByName("domain_selection");
for (var i = 0; i < elements.length; i++) {
(function(li) {
li.className = "";
})(elements[i]);
}
}
function toggleSelection(li) {
var domainId = li.value;
var editDNS = document.getElementById("options_edit_dns");
var unreg = document.getElementById("options_unregister");
if(li.className == "SelectedListItem") {
editDNS.className = "Hidden";
unreg.className = "Hidden";
li.className = "";
return;
}
resetSelection();
li.className = "SelectedListItem";
editDNS.className = "";
editDNS.href = "/dns/edit/" + domainId;
unreg.className = "";
unreg.href = "/domains/unregister/" + domainId;
}
// Once the page has fully loaded, connect each button to its code
function initDomainSelection() {
// Register selection events
var elements = document.getElementsByName("domain_selection");
for (var i = 0; i < elements.length; i++) {
(function(li) {
if(window.addEventListener) {
li.addEventListener("click", function() {
toggleSelection(li);
});
} else {
li.attachEvent("onclick", function() {
toggleSelection(li);
});
}
})(elements[i]);
}
}
// Register load / onload event
if(window.addEventListener) {
window.addEventListener('load', initDomainSelection);
} else if(window.attachEvent) {
window.attachEvent('onload', initDomainSelection);
} else {
alert("Unsupported browser.");
}
+42
View File
@@ -0,0 +1,42 @@
function FQDNToCoreDNSPath(fqdn) {
}
function RegisterNewDomain(domainLabel) {
}
function GetAllRecords() {
}
function CreateARecord(domain, host) {
}
function DeleteARecord(domain, host) {
}
function UpdateARecord(domain, host) {
}
function CreateMXRecord(domain, host) {
}
function DeleteMXRecord(domain, host) {
}
function UpdateMXRecord(domain, host) {
}
module.exports = {
RegisterNewDomain,
GetAllRecords,
CreateARecord,
DeleteARecord
}
+12 -2
View File
@@ -34,13 +34,23 @@ const User = seqConn.define('User', {
const RegisteredDomain = seqConn.define('RegisteredDomain', { const RegisteredDomain = seqConn.define('RegisteredDomain', {
domain: { domain: {
type: Sequelize.TEXT, type: Sequelize.TEXT,
unique: true, allowNull: false
},
tld: {
type: Sequelize.TEXT,
allowNull: false allowNull: false
}, },
owner: { owner: {
type: Sequelize.BIGINT.UNSIGNED, type: Sequelize.BIGINT.UNSIGNED,
allowNull: false allowNull: false
} },
}, {
indexes: [
{
unique: true,
fields: ['domain', 'tld'],
},
],
}); });
////// Exports ////// ////// Exports //////
+30
View File
@@ -0,0 +1,30 @@
const express = require('express');
const router = express.Router();
const domainValidator = require('../validators/domain');
const authMw = require('../session');
const database = require('../database.js');
const dbConnection = database.db;
const Sequelize = require('sequelize');
const pageTitle = 'Domain Manager | Edit DNS';
// Manage domains
router.get('/dns/edit/:domainId', authMw.AllowIfAuthenticated, async (req, res, next) => {
const result = await dbConnection.transaction(async(t) => {
const ownedDomain = await database.models.RegisteredDomain.findOne({
where: {
id: req.params.domainId,
owner: req.session.userId
}
}, {transaction: t});
return ownedDomain;
});
if(!result)
return next();
res.render('dns', {title: pageTitle, domain: result});
});
module.exports = router;
+86
View File
@@ -0,0 +1,86 @@
const express = require('express');
const router = express.Router();
const domainValidator = require('../validators/domain');
const authMw = require('../session');
const database = require('../database.js');
const dbConnection = database.db;
const Sequelize = require('sequelize');
const pageTitle = 'Domain Manager | Register New Domain';
const supportedTLDs = [
"local",
"tomato",
"secret",
"money",
"lol",
"lmao"
];
// Manage domains
router.get('/domains', authMw.AllowIfAuthenticated, async (req, res) => {
const result = await dbConnection.transaction(async(t) => {
const ownedDomains = await database.models.RegisteredDomain.findAll({
where: {
owner: req.session.userId
}
}, {transaction: t});
return ownedDomains;
});
let registeredDomains = result;
res.render('domains', {title: 'Domain Manager | Your Domains', registeredDomains: registeredDomains});
});
//// Register domains ////
// GET
// Frontend page to register a new domain
router.get('/domains/new', authMw.AllowIfAuthenticated, async(req, res) => {
res.render('newdomain', {title: 'Domain Manager | Register Domain', supportedTLDs: supportedTLDs});
});
// POST
// Backend post handler to register the domain
router.post('/domains/new', authMw.AllowIfAuthenticated, async(req, res, next) => {
const reqBody = req.body;
const validationResult = domainValidator.test(reqBody);
const validationError = validationResult.error;
let errors = [];
if(validationError !== undefined)
errors = validationError.details;
try {
if(errors.length === 0) {
const result = await dbConnection.transaction(async(t) => {
const newDomain = await database.models.RegisteredDomain.create({
domain: reqBody.register_domain_label,
tld: reqBody.register_domain_tld,
owner: req.session.userId
}, {transaction: t});
return newDomain;
});
if(result !== undefined) {
return res.redirect('/domains');
} else {
errors.push({message: 'Failed to register new domain.'})
}
}
} catch(error) {
if(error instanceof Sequelize.UniqueConstraintError) {
errors.push({message: 'Domain is not available.'});
} else {
error.status = 500;
return next(error);
}
}
// if we're here we failed, I specify true for csrfToken to force reset it
return res.render('newdomain', {title: pageTitle, supportedTLDs: supportedTLDs, errors: errors, csrfToken: req.csrfToken(true) });
});
module.exports = router;
+11
View File
@@ -0,0 +1,11 @@
const Joi = require('joi');
const domainRegistrySchema = Joi.object().keys({
register_domain_label: Joi.string().alphanum().min(1).max(63).required(),
register_domain_tld: Joi.string().valid("local", "tomato", "secret", "money", "lol", "lmao").required()
}).unknown(true);
module.exports = {
test: (body) => {
return domainRegistrySchema.validate(body);
}
}
+1
View File
@@ -0,0 +1 @@
<p>Configuring DNS records for {{domain.domain}}.{{domain.tld}}</p>
+34
View File
@@ -0,0 +1,34 @@
{{{getScript "/scripts/domainmgmt.js" nonce}}}
<p>Manage your registered domains</p>
{{#if errors}}
<ul>
{{#each errors}}
<li>{{this.message}}</li>
{{/each}}
</ul>
{{/if}}
<div class="InsetContentBox" id="DomainSelection">
<div style="width: 600px;"></div>
<ul>
{{#if (lenEq registeredDomains 0)}}
<li class="MutedText">You don't currently have any registered domains</li>
{{else}}
{{#each registeredDomains}}
<li name="domain_selection" value="{{this.id}}">{{this.domain}}.{{this.tld}}</li>
{{/each}}
{{/if}}
</ul>
</div>
<div class="ContentBox" id="DomainOptions">
<div style="width: 200px;"></div>
<p class="TitleText">Options</p>
<ul>
<li><a href="/domains/new">Register New Domain</a></li>
<li><a class="Hidden" href="/dns/edit" id="options_edit_dns">Edit DNS</a></li>
<li><a class="Hidden" href="/domains/unregister" id="options_unregister">Unregister Domain</a></li>
</ul>
</div>
+1 -2
View File
@@ -23,8 +23,6 @@
<div id="Header-Left"> <div id="Header-Left">
<a href="/">Home</a> <a href="/">Home</a>
{{#if isLoggedIn}} {{#if isLoggedIn}}
<span>|</span>
<a href="/dns">DNS</a>
<span>|</span> <span>|</span>
<a href="/domains">Domains</a> <a href="/domains">Domains</a>
{{/if}} {{/if}}
@@ -43,6 +41,7 @@
</div> </div>
<br> <br>
<hr> <hr>
</div>
{{{body}}} {{{body}}}
</div> </div>
+21
View File
@@ -0,0 +1,21 @@
<p>Register a new domain</p>
{{#if errors}}
<ul>
{{#each errors}}
<li>{{this.message}}</li>
{{/each}}
</ul>
{{/if}}
<form action="/domains/new" method="post">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="text" name="register_domain_label">
<select name="register_domain_tld">
{{#each supportedTLDs}}
<option value="{{this}}">.{{this}}</option>
{{/each}}
</select>
<br><br>
<input type="submit" class="DefaultButton" value="Register">
</form>