dev: custom domain routing (the easy part)

With this commit alone, any user can specify "domain" on their
subdomains. This means they could affect the route for domains they
don't control, so this is not production-ready without further changes.
This commit is contained in:
KernelDeimos
2025-05-09 15:27:25 -04:00
committed by Neal Shah
parent 436f2d679f
commit 49b0805014
6 changed files with 43 additions and 8 deletions
@@ -500,7 +500,11 @@ class WebServerService extends BaseService {
if (allowedDomains.some(allowedDomain => hostName === allowedDomain || hostName.endsWith('.' + allowedDomain))) {
next(); // Proceed if the host is valid
} else {
return res.status(400).send('Invalid Host header.');
if ( ! config.custom_domains_enabled ) {
return res.status(400).send('Invalid Host header.');
}
req.is_custom_domain = true;
next();
}
})
+13
View File
@@ -54,6 +54,19 @@ module.exports = {
}
}
},
domain: {
type: 'string',
maxlen: 253,
// It turns out validating domain names kind of sucks
// source: https://stackoverflow.com/questions/10306690
regex: '^(((?!-))(xn--|_)?[a-z0-9-]{0,61}[a-z0-9]{1,1}\.)*(xn--)?([a-z0-9][a-z0-9\-]{0,60}|[a-z0-9-]{1,30}\.[a-z]{2,})$',
// TODO: can this 'adapt' be data instead?
async adapt (value) {
return value.toLowerCase();
},
},
root_dir: {
type: 'puter-node',
fs_permission: 'read',
+15 -5
View File
@@ -41,10 +41,17 @@ class PuterSiteMiddleware extends AdvancedBase {
app.use(this.run.bind(this));
}
async run (req, res, next) {
if (
! req.hostname.endsWith(config.static_hosting_domain)
&& ( req.subdomains[0] !== 'devtest' )
) return next();
! req.hostname.endsWith(config.static_hosting_domain)
&& ( req.subdomains[0] !== 'devtest' )
const is_subdomain =
req.hostname.endsWith(config.static_hosting_domain)
||
req.subdomains[0] === 'devtest'
;
if ( ! is_subdomain && ! req.is_custom_domain ) return next();
res.setHeader('Access-Control-Allow-Origin', '*');
@@ -64,6 +71,7 @@ class PuterSiteMiddleware extends AdvancedBase {
}
async run_ (req, res, next) {
const subdomain =
req.is_custom_domain ? req.hostname :
req.subdomains[0] === 'devtest' ? 'devtest' :
req.hostname.slice(0, -1 * (config.static_hosting_domain.length + 1));
@@ -100,7 +108,9 @@ class PuterSiteMiddleware extends AdvancedBase {
await get_username_site() ||
await (async () => {
const svc_puterSite = services.get('puter-site');
const site = await svc_puterSite.get_subdomain(subdomain);
const site = await svc_puterSite.get_subdomain(subdomain, {
is_custom_domain: req.is_custom_domain,
});
return site;
})();
+5 -2
View File
@@ -113,15 +113,18 @@ class PuterSiteService extends BaseService {
* @returns {Promise<Object|null>} Returns an object with subdomain details or null if not found.
* @note In development environment, 'devtest' subdomain returns hardcoded values.
*/
async get_subdomain (subdomain) {
async get_subdomain (subdomain, options) {
if ( subdomain === 'devtest' && this.global_config.env === 'dev' ) {
return {
user_id: null,
root_dir_id: this.config.devtest_directory,
};
}
console.log('???', subdomain, options);
const rows = await this.db.read(
`SELECT * FROM subdomains WHERE subdomain = ? LIMIT 1`,
`SELECT * FROM subdomains WHERE ${
options.is_custom_domain ? 'domain' : 'subdomain'
} = ? LIMIT 1`,
[subdomain]
);
if ( rows.length === 0 ) return null;
@@ -161,6 +161,9 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
[33, [
'0036_dev-to-app.sql',
]],
[34, [
'0038_custom-domains.sql',
]],
];
// Database upgrade logic
@@ -0,0 +1,2 @@
ALTER TABLE `subdomains` ADD COLUMN `domain` varchar(256) DEFAULT NULL;
-- reminder: add index