mirror of
https://github.com/getnamingo/registry.git
synced 2025-05-30 09:20:22 +02:00
450 lines
No EOL
24 KiB
Twig
450 lines
No EOL
24 KiB
Twig
{% extends "layouts/app.twig" %}
|
|
|
|
{% block title %}{{ __('Create Domain') }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="page-wrapper">
|
|
<!-- Page header -->
|
|
<div class="page-header d-print-none">
|
|
<div class="container-xl">
|
|
<div class="row g-2 align-items-center">
|
|
<div class="col">
|
|
<!-- Page pre-title -->
|
|
<div class="page-pretitle">
|
|
{{ __('Overview') }}
|
|
</div>
|
|
<h2 class="page-title">
|
|
{{ __('Create Domain') }}
|
|
</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Page body -->
|
|
<div class="page-body">
|
|
<div class="container-xl">
|
|
<div class="col-12">
|
|
{% include 'partials/flash.twig' %}
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<form id="domainCreateForm" action="/domain/create" method="post">
|
|
{{ csrf.field | raw }}
|
|
<div class="mb-3">
|
|
<label for="domainName" class="form-label required">{{ __('Your Domain Name') }}</label>
|
|
<input type="text" class="form-control mb-2" placeholder="example.com" name="domainName" id="domainName" required="required" autocapitalize="none">
|
|
</div>
|
|
|
|
{% if registrars and not registrar %}
|
|
<div class="form-group mb-3">
|
|
<label for="registrarDropdown" class="form-label required">{{ __('Select Registrar') }}</label>
|
|
<select id="registrarDropdown" name="registrar" class="form-control">
|
|
{% for registrar in registrars %}
|
|
<option value="{{ registrar.id }}">{{ registrar.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Slider for years -->
|
|
<div class="mb-3">
|
|
<label for="registrationYears" class="form-label">{{ __('Registration Years') }}</label>
|
|
<input type="range" class="form-range" min="1" max="10" step="1" id="registrationYears" name="registrationYears" value="1">
|
|
<span id="yearValue">1 Year</span>
|
|
</div>
|
|
|
|
<!-- Placeholder for displaying domain price -->
|
|
<div class="mb-3" id="domainPriceDisplay" style="display:none;">
|
|
<strong>{{ __('Estimated Price') }}: </strong><span id="domainPrice">{{ currency }} 0.00</span>
|
|
</div>
|
|
|
|
<!-- Fields for 4 contacts with roles -->
|
|
<div class="mb-3">
|
|
<label for="contactRegistrant" class="form-label required">{{ __('Contacts') }}</label>
|
|
<input type="text" class="form-control mb-2" placeholder="{{ __('Registrant Contact') }}" name="contactRegistrant" id="contactRegistrant" required="required">
|
|
<input type="text" class="form-control mb-2" placeholder="{{ __('Admin Contact') }}" name="contactAdmin">
|
|
<input type="text" class="form-control mb-2" placeholder="{{ __('Tech Contact') }}" name="contactTech">
|
|
<input type="text" class="form-control mb-2" placeholder="{{ __('Billing Contact') }}" name="contactBilling">
|
|
</div>
|
|
|
|
<!-- Fields for nameservers -->
|
|
<div id="nameserverFields">
|
|
<label class="form-label">{{ __('Nameservers') }} <button type="button" id="addNameserver" class="btn btn-success btn-sm mb-2">+</button> <button type="button" id="removeNameserver" class="btn btn-danger btn-sm mb-2">-</button></label>
|
|
|
|
<div class="nameserver-group mb-1 row">
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control mb-1" placeholder="{{ __('Nameserver') }} 1" name="nameserver[]" autocapitalize="none">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control mb-1" placeholder="{{ __('Nameserver') }} 1 - IPv4" name="nameserver_ipv4[]">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control mb-1" placeholder="{{ __('Nameserver') }} 1 - IPv6" name="nameserver_ipv6[]" autocapitalize="none">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="nameserver-group mb-1 row">
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control mb-1" placeholder="{{ __('Nameserver') }} 2" name="nameserver[]" autocapitalize="none">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control mb-1" placeholder="{{ __('Nameserver') }} 2 - IPv4" name="nameserver_ipv4[]">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control mb-1" placeholder="{{ __('Nameserver') }} 2 - IPv6" name="nameserver_ipv6[]" autocapitalize="none">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DNSSEC Data with checkbox -->
|
|
<div class="mb-3 mt-2 form-check">
|
|
<input type="checkbox" class="form-check-input" id="addDnssec" name="addDnssec">
|
|
<label class="form-check-label" for="addDnssec">{{ __('Add DNSSEC Data') }}</label>
|
|
</div>
|
|
<div id="dnssecData" style="display: none;">
|
|
<div class="mb-3">
|
|
<label for="dsKeyTag" class="form-label">{{ __('DS Record') }}</label>
|
|
<input type="text" class="form-control mb-2" placeholder="{{ __('Key Tag') }}" name="dsKeyTag" id="dsKeyTag">
|
|
<select class="form-control mb-2" name="dsAlg">
|
|
<option value="" disabled selected>{{ __('Select Algorithm') }}</option>
|
|
<option value="8">RSA/SHA-256</option>
|
|
<option value="13">ECDSA Curve P-256 with SHA-256</option>
|
|
<option value="14">ECDSA Curve P-384 with SHA-384</option>
|
|
<option value="15">Ed25519</option>
|
|
<option value="16">Ed448</option>
|
|
</select>
|
|
<select class="form-control mb-2" name="dsDigestType">
|
|
<option value="" disabled selected>{{ __('Select Digest Type') }}</option>
|
|
<option value="2">SHA-256</option>
|
|
<option value="4">SHA-384</option>
|
|
</select>
|
|
<input type="text" class="form-control mb-2" placeholder="{{ __('Digest') }}" name="dsDigest">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="dnskeyFlags" class="form-label">{{ __('DNSKEY Record') }}</label>
|
|
<input type="number" class="form-control mb-2" placeholder="{{ __('Flags') }}" name="dnskeyFlags" id="dnskeyFlags">
|
|
<input type="number" class="form-control mb-2" placeholder="{{ __('Protocol') }}" name="dnskeyProtocol" value="3" readonly> <!-- Protocol is typically set to 3 -->
|
|
<select class="form-control mb-2" name="dnskeyAlg">
|
|
<option value="" disabled selected>{{ __('Select Algorithm') }}</option>
|
|
<option value="8">RSA/SHA-256</option>
|
|
<option value="13">ECDSA Curve P-256 with SHA-256</option>
|
|
<option value="14">ECDSA Curve P-384 with SHA-384</option>
|
|
<option value="15">Ed25519</option>
|
|
<option value="16">Ed448</option>
|
|
</select>
|
|
<input type="text" class="form-control mb-2" placeholder="{{ __('Public Key') }}" name="dnskeyPubKey">
|
|
</div>
|
|
</div>
|
|
<!-- AuthInfo -->
|
|
<div class="mb-3">
|
|
<label for="authInfo" class="form-label required">{{ __('Auth Info') }}</label>
|
|
<input type="text" class="form-control" id="authInfo" name="authInfo" required>
|
|
</div>
|
|
|
|
<div class="mb-3 mt-3">
|
|
<div class="form-label">{{ __('Statuses') }}</div>
|
|
<div>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="clientStatuses[clientHold]">
|
|
<span class="form-check-label">clientHold</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="clientStatuses[clientRenewProhibited]">
|
|
<span class="form-check-label">clientRenewProhibited</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="clientStatuses[clientTransferProhibited]">
|
|
<span class="form-check-label">clientTransferProhibited</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="clientStatuses[clientDeleteProhibited]">
|
|
<span class="form-check-label">clientDeleteProhibited</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="clientStatuses[clientUpdateProhibited]">
|
|
<span class="form-check-label">clientUpdateProhibited</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
{% if roles == 0 %}
|
|
<div class="mb-3 mt-3">
|
|
<div class="form-label">{{ __('Server Statuses') }}</div>
|
|
<div>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="serverStatuses[serverHold]">
|
|
<span class="form-check-label">serverHold</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="serverStatuses[serverRenewProhibited]">
|
|
<span class="form-check-label">serverRenewProhibited</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="serverStatuses[serverTransferProhibited]">
|
|
<span class="form-check-label">serverTransferProhibited</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="serverStatuses[serverDeleteProhibited]">
|
|
<span class="form-check-label">serverDeleteProhibited</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="serverStatuses[serverUpdateProhibited]">
|
|
<span class="form-check-label">serverUpdateProhibited</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if launch_phases == 'on' %}
|
|
<div class="mb-3">
|
|
<label for="phaseType" class="form-label">{{ __('Phase Type') }}</label>
|
|
<select class="form-select" id="phaseType" name="phaseType">
|
|
<option value="none">N/A</option>
|
|
<option value="sunrise">Sunrise</option>
|
|
<option value="landrush">Landrush</option>
|
|
<option value="claims">Claims</option>
|
|
<option value="open">Open</option>
|
|
<option value="custom">Custom</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="phaseName" class="form-label">{{ __('Phase Name') }}</label>
|
|
<input type="text" class="form-control" id="phaseName" name="phaseName" placeholder="Enter phase name">
|
|
<small class="form-hint">The "Phase name" field is required only if the "Type" is set to "Custom".</small>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">{{ __('Signed Mark Information') }}</label>
|
|
<textarea class="form-control" data-bs-toggle="autosize" name="smd" placeholder="{{ __('Paste SMD contents') }}…"></textarea>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="noticeid" class="form-label">{{ __('Notice ID') }}</label>
|
|
<input type="text" class="form-control" name="noticeid">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="notafter" class="form-label">{{ __('Not After Date') }}</label>
|
|
<input type="datetime-local" class="form-control" name="notafter">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="accepted" class="form-label">{{ __('Accepted Date') }}</label>
|
|
<input type="datetime-local" class="form-control" name="accepted">
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="mb-3">
|
|
<label for="token" class="form-label">{{ __('Allocation Token') }}</label>
|
|
<input type="text" class="form-control" placeholder="{{ __('Allocation token') }}" name="token" autocapitalize="none">
|
|
</div>
|
|
|
|
</div>
|
|
<div class="card-footer">
|
|
<div class="row align-items-center">
|
|
<div class="col-auto">
|
|
<button type="submit" class="btn btn-primary">{{ __('Create Domain') }}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% include 'partials/footer.twig' %}
|
|
</div>
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
|
|
window.currencySymbol = "{{ currencySymbol }}";
|
|
window.currencyPosition = "{{ currencyPosition }}";
|
|
|
|
const yearSlider = document.getElementById('registrationYears');
|
|
const yearValueDisplay = document.getElementById('yearValue');
|
|
const addNameserverBtn = document.getElementById('addNameserver');
|
|
const removeNameserverBtn = document.getElementById('removeNameserver');
|
|
const nameserverFields = document.getElementById('nameserverFields');
|
|
const authInfoField = document.getElementById('authInfo');
|
|
|
|
// Display year value from slider
|
|
yearSlider.addEventListener('input', function() {
|
|
yearValueDisplay.textContent = `${yearSlider.value} Year${yearSlider.value > 1 ? 's' : ''}`;
|
|
});
|
|
|
|
function createNameserverGroup(count) {
|
|
const group = document.createElement('div');
|
|
group.className = 'nameserver-group mb-1 row';
|
|
|
|
const nameserverCol = document.createElement('div');
|
|
nameserverCol.className = 'col-md-4';
|
|
const nameserverField = document.createElement('input');
|
|
nameserverField.type = 'text';
|
|
nameserverField.className = 'form-control mb-1';
|
|
nameserverField.placeholder = `{{ __('Nameserver') }} ${count}`;
|
|
nameserverField.name = `nameserver[]`;
|
|
nameserverCol.appendChild(nameserverField);
|
|
|
|
const ipv4Col = document.createElement('div');
|
|
ipv4Col.className = 'col-md-4';
|
|
const ipv4Field = document.createElement('input');
|
|
ipv4Field.type = 'text';
|
|
ipv4Field.className = 'form-control mb-1';
|
|
ipv4Field.placeholder = `{{ __('Nameserver') }} ${count} - IPv4`;
|
|
ipv4Field.name = `nameserver_ipv4[]`;
|
|
ipv4Col.appendChild(ipv4Field);
|
|
|
|
const ipv6Col = document.createElement('div');
|
|
ipv6Col.className = 'col-md-4';
|
|
const ipv6Field = document.createElement('input');
|
|
ipv6Field.type = 'text';
|
|
ipv6Field.className = 'form-control mb-1';
|
|
ipv6Field.placeholder = `{{ __('Nameserver') }} ${count} - IPv6`;
|
|
ipv6Field.name = `nameserver_ipv6[]`;
|
|
ipv6Col.appendChild(ipv6Field);
|
|
|
|
group.appendChild(nameserverCol);
|
|
group.appendChild(ipv4Col);
|
|
group.appendChild(ipv6Col);
|
|
|
|
return group;
|
|
}
|
|
|
|
// Add nameserver fields
|
|
let nameserverCount = 2;
|
|
addNameserverBtn.addEventListener('click', function() {
|
|
if (nameserverCount < 13) {
|
|
nameserverCount++;
|
|
const nameserverGroup = createNameserverGroup(nameserverCount);
|
|
nameserverFields.appendChild(nameserverGroup);
|
|
}
|
|
});
|
|
|
|
// Remove nameserver group
|
|
removeNameserverBtn.addEventListener('click', function() {
|
|
if (nameserverCount > 2) {
|
|
const lastGroup = nameserverFields.querySelector('.nameserver-group:last-child');
|
|
if (lastGroup) {
|
|
nameserverFields.removeChild(lastGroup);
|
|
nameserverCount--;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Generate random AuthInfo and set it to the field
|
|
function generateAuthInfo() {
|
|
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
let result = "";
|
|
for (let i = 0; i < 16; i++) {
|
|
result += charset.charAt(Math.floor(Math.random() * charset.length));
|
|
}
|
|
return result;
|
|
}
|
|
authInfoField.value = generateAuthInfo();
|
|
|
|
// Display DNSSEC data when the checkbox is ticked
|
|
document.getElementById('addDnssec').addEventListener('change', function() {
|
|
const dnssecData = document.getElementById('dnssecData');
|
|
if (this.checked) {
|
|
dnssecData.style.display = 'block';
|
|
} else {
|
|
dnssecData.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
const domainInput = document.getElementById('domainName');
|
|
const yearInput = document.getElementById('registrationYears');
|
|
const priceDisplay = document.getElementById('domainPriceDisplay');
|
|
const priceValue = document.getElementById('domainPrice');
|
|
|
|
function extractTLD(domain) {
|
|
const parts = domain.split('.');
|
|
|
|
// If the domain has more than two segments (e.g., 'test.com.test'), return the last two segments
|
|
if (parts.length > 2) {
|
|
return parts.slice(-2).join('.').toLowerCase();
|
|
}
|
|
// If the domain has two or fewer segments (e.g., 'test.test' or 'test'), return the last segment
|
|
else {
|
|
return parts[parts.length - 1].toLowerCase();
|
|
}
|
|
}
|
|
|
|
function getDomainPrice(domain, years) {
|
|
const tld = extractTLD(domain);
|
|
if (!tld) {
|
|
return Promise.reject("Invalid TLD");
|
|
}
|
|
|
|
// Regular expression for exact TLD match
|
|
const tldRegex = new RegExp(`${tld.toLowerCase()}$`);
|
|
|
|
// Fetch both promotional pricing and regular pricing
|
|
return Promise.all([
|
|
fetch(`/api/records/promotion_pricing?join=domain_tld`).then(response => response.json()),
|
|
fetch(`/api/records/domain_price?join=domain_tld`).then(response => response.json())
|
|
])
|
|
.then(([promoData, pricingData]) => {
|
|
const today = new Date();
|
|
|
|
// Check for a valid promotion
|
|
const promo = promoData.records.find(record =>
|
|
record.tld_id && tldRegex.test(record.tld_id.tld.toLowerCase()) &&
|
|
new Date(record.start_date) <= today &&
|
|
new Date(record.end_date) >= today &&
|
|
(!record.years_of_promotion || record.years_of_promotion >= years)
|
|
);
|
|
|
|
// Find the regular price for the TLD
|
|
const tldData = pricingData.records.find(record =>
|
|
record.tldid && tldRegex.test(record.tldid.tld.toLowerCase()) &&
|
|
record.command === 'create'
|
|
);
|
|
|
|
if (tldData) {
|
|
const priceField = `m${years * 12}`;
|
|
let price = parseFloat(tldData[priceField]);
|
|
if (!isNaN(price)) {
|
|
if (promo) {
|
|
// Apply the promotion discount
|
|
price -= (price * parseFloat(promo.discount_percentage) / 100);
|
|
}
|
|
return price;
|
|
}
|
|
}
|
|
return Promise.reject("TLD price not found");
|
|
})
|
|
.catch(error => {
|
|
console.error("Error fetching pricing data:", error);
|
|
return Promise.reject("Error fetching pricing data");
|
|
});
|
|
}
|
|
|
|
function formatPrice(price) {
|
|
switch(window.currencyPosition) {
|
|
case 'before':
|
|
return `{{ currency }} ${price.toFixed(2)}`;
|
|
case 'after':
|
|
return `${price.toFixed(2)} {{ currency }}`;
|
|
default:
|
|
return price.toFixed(2);
|
|
}
|
|
}
|
|
|
|
function updatePrice() {
|
|
if (domainInput.value) {
|
|
getDomainPrice(domainInput.value, yearInput.value).then(price => {
|
|
priceValue.innerText = formatPrice(price);
|
|
priceDisplay.style.display = 'block';
|
|
});
|
|
} else {
|
|
priceDisplay.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
domainInput.addEventListener('input', updatePrice);
|
|
yearInput.addEventListener('input', updatePrice);
|
|
});
|
|
</script>
|
|
{% endblock %} |