CI / Build & Test (push) Successful in 3m19s
- Add unit tests (hostname-validator, TLDs, payload-schemas) and integration tests for message handler registry - Refactor native host message router into handler registry (handlers/state, tunnels, ssh, rdp, backup, ca, connections) - Add ESLint config and npm test + lint steps in CI - Dashboard: visibility-based refresh pause, configurable refresh interval (2s/5s/10s/paused) - Accessibility: ARIA on nav and modals, focus trap and restore, prefers-reduced-motion - Empty states: primary action buttons for virtual hosts, servers, service tunnels - Native host rate limiting for backup and CA operations; update SECURITY.md - CONTRIBUTING: "Adding a new dashboard page", dev workflow; add npm run dev script
60 lines
2.4 KiB
JavaScript
60 lines
2.4 KiB
JavaScript
// Depends on: data/tlds.js (REAL_TLDS, REAL_SLD_TLDS)
|
|
|
|
/**
|
|
* Validate a virtual host hostname.
|
|
* Rules:
|
|
* 1. Must have at least 2 dots (3 labels minimum: host.second.tld)
|
|
* 2. Each label: only [a-z0-9-], no leading/trailing hyphen, non-empty
|
|
* 3. The last two labels (base domain, e.g. "hole.sail") must not be a
|
|
* real public TLD or second-level public suffix
|
|
* Returns { ok: true } or { ok: false, error: string }
|
|
*/
|
|
function isValidVhostHostname(hostname) {
|
|
if (!hostname) return { ok: false, error: 'Hostname is required' };
|
|
const labels = hostname.split('.');
|
|
if (labels.length < 3) {
|
|
return { ok: false, error: 'Hostname must have the form host.second.tld (e.g. myapp.hole.sail) — single-dot names are not allowed' };
|
|
}
|
|
for (const label of labels) {
|
|
if (!label) return { ok: false, error: 'Hostname contains empty labels' };
|
|
if (!/^[a-z0-9-]+$/.test(label)) return { ok: false, error: 'Hostname contains invalid characters — only letters, digits and hyphens allowed' };
|
|
if (label.startsWith('-') || label.endsWith('-')) return { ok: false, error: 'Hostname labels must not start or end with a hyphen' };
|
|
}
|
|
const tld = labels[labels.length - 1];
|
|
const baseDomain = labels.slice(-2).join('.');
|
|
if (REAL_TLDS.has(tld) || REAL_SLD_TLDS.has(baseDomain)) {
|
|
return { ok: false, error: 'The TLD ".' + baseDomain + '" is a real registered domain — use a private TLD like .hole.sail or .my.internal' };
|
|
}
|
|
return { ok: true };
|
|
}
|
|
|
|
/**
|
|
* Extract the two-label base domain from a hostname.
|
|
* @example extractBaseDomain('myapp.hole.sail') // => 'hole.sail'
|
|
* @param {string} hostname
|
|
* @returns {string}
|
|
*/
|
|
function extractBaseDomain(hostname) {
|
|
const parts = hostname.split('.');
|
|
return parts.slice(-2).join('.');
|
|
}
|
|
|
|
/**
|
|
* Extract unique two-label base domains from a list of virtual host objects.
|
|
* Always includes `hole.sail` as the baseline TLD.
|
|
* @param {Array<{hostname: string}>} virtualHosts
|
|
* @returns {string[]} Array of TLD suffixes with leading dot (e.g. `['.hole.sail', '.hs']`).
|
|
*/
|
|
function extractActiveTlds(virtualHosts) {
|
|
const seen = new Set();
|
|
seen.add('hole.sail'); // always include baseline
|
|
for (const v of (virtualHosts || [])) {
|
|
if (v.hostname) seen.add(extractBaseDomain(v.hostname));
|
|
}
|
|
return Array.from(seen).map(b => '.' + b);
|
|
}
|
|
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
module.exports = { isValidVhostHostname, extractBaseDomain, extractActiveTlds };
|
|
}
|