From 109de8cac8d5e01a01234e2f91ba42507e97d24c Mon Sep 17 00:00:00 2001 From: Long Nguyen Date: Tue, 4 May 2021 20:28:31 +0700 Subject: [PATCH 1/2] chore: allow user to connect subdomain of app domain. Some apps (eg: tiredofit/docker-baserow) require multiple domains. To reduce friction for the user, we don't want to split the app into multiple sub-apps or require the user to manually change Nginx template config or mess with Le'tsEncrypt certs. The simplest solution is to allow users to add app's subdomain. For Caprover's server with root domain is root.domain.com, we have app1 at app1.root.domain.com. Allow users to connect subdomain api.app1.root.domain.com. --- src/user/ServiceManager.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/user/ServiceManager.ts b/src/user/ServiceManager.ts index 704ac94..a66ef5c 100644 --- a/src/user/ServiceManager.ts +++ b/src/user/ServiceManager.ts @@ -275,6 +275,7 @@ class ServiceManager { .then(function () { const rootDomain = self.dataStore.getRootDomain() const dotRootDomain = `.${rootDomain}` + const appDomain = appName + dotRootDomain if (!customDomain || !/^[a-z0-9\-\.]+$/.test(customDomain)) { throw ApiStatusCodes.createError( @@ -301,7 +302,7 @@ class ServiceManager { customDomain.indexOf(dotRootDomain) >= 0 && customDomain.indexOf(dotRootDomain) + dotRootDomain.length === - customDomain.length + appDomain.length ) { throw ApiStatusCodes.createError( ApiStatusCodes.STATUS_ERROR_BAD_NAME, From 28889039b712209f8e4a444f13f76f161b7cee82 Mon Sep 17 00:00:00 2001 From: Long Nguyen Date: Wed, 5 May 2021 10:19:51 +0700 Subject: [PATCH 2/2] fix: add app's subdomain & add test case --- src/user/ServiceManager.ts | 34 ++++--------------------------- src/utils/Utils.ts | 30 ++++++++++++++++++++++++++++ tests/utils.test.ts | 41 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 30 deletions(-) diff --git a/src/user/ServiceManager.ts b/src/user/ServiceManager.ts index a66ef5c..0d983c7 100644 --- a/src/user/ServiceManager.ts +++ b/src/user/ServiceManager.ts @@ -274,39 +274,13 @@ class ServiceManager { return Promise.resolve() .then(function () { const rootDomain = self.dataStore.getRootDomain() - const dotRootDomain = `.${rootDomain}` - const appDomain = appName + dotRootDomain - if (!customDomain || !/^[a-z0-9\-\.]+$/.test(customDomain)) { + try { + Utils.checkCustomDomain(customDomain, appName, rootDomain) + } catch (error) { throw ApiStatusCodes.createError( ApiStatusCodes.STATUS_ERROR_BAD_NAME, - 'Domain name is not accepted. Please use alphanumerical domains such as myapp.google123.ca' - ) - } - - if (customDomain.length > 80) { - throw ApiStatusCodes.createError( - ApiStatusCodes.STATUS_ERROR_BAD_NAME, - 'Domain name is not accepted. Please use alphanumerical domains less than 80 characters in length.' - ) - } - - if (customDomain.indexOf('..') >= 0) { - throw ApiStatusCodes.createError( - ApiStatusCodes.STATUS_ERROR_BAD_NAME, - 'Domain name is not accepted. You cannot have two consecutive periods ".." inside a domain name. Please use alphanumerical domains such as myapp.google123.ca' - ) - } - - if ( - customDomain.indexOf(dotRootDomain) >= 0 && - customDomain.indexOf(dotRootDomain) + - dotRootDomain.length === - appDomain.length - ) { - throw ApiStatusCodes.createError( - ApiStatusCodes.STATUS_ERROR_BAD_NAME, - 'Domain name is not accepted. Custom domain cannot be subdomain of root domain.' + error ) } }) diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 381bbd7..7a23735 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -120,4 +120,34 @@ export default class Utils { return Promise.resolve() } + + static checkCustomDomain( + customDomain: string, + appName: string, + rootDomain: string + ) { + const dotRootDomain = `.${rootDomain}` + const dotAppDomain = `.${appName}${dotRootDomain}` + + if (!customDomain || !/^[a-z0-9\-\.]+$/.test(customDomain)) { + throw 'Domain name is not accepted. Please use alphanumerical domains such as myapp.google123.ca' + } + + if (customDomain.length > 80) { + throw 'Domain name is not accepted. Please use alphanumerical domains less than 80 characters in length.' + } + + if (customDomain.indexOf('..') >= 0) { + throw 'Domain name is not accepted. You cannot have two consecutive periods ".." inside a domain name. Please use alphanumerical domains such as myapp.google123.ca' + } + + if ( + customDomain.indexOf(dotAppDomain) === -1 && + customDomain.indexOf(dotRootDomain) >= 0 && + customDomain.indexOf(dotRootDomain) + dotRootDomain.length === + customDomain.length + ) { + throw 'Domain name is not accepted. Custom domain cannot be subdomain of root domain.' + } + } } diff --git a/tests/utils.test.ts b/tests/utils.test.ts index 505dcb6..6b0777b 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -83,3 +83,44 @@ test('Testing filter in place - remove 2nd', () => { expect(originalArray[0].val1).toBe('e-1-1') expect(originalArray[0].val2).toBe('e-2-1') }) + +test('Testing code to check custom domain', () => { + const rootDomain = 'google123.ca' + const appName = 'app1' + + expect(() => Utils.checkCustomDomain('', appName, rootDomain)).toThrow( + 'Domain name is not accepted. Please use alphanumerical domains such as myapp.google123.ca' + ) + + expect(() => + Utils.checkCustomDomain('x'.repeat(81), appName, rootDomain) + ).toThrow( + 'Domain name is not accepted. Please use alphanumerical domains less than 80 characters in length.' + ) + + expect(() => + Utils.checkCustomDomain('..google321.ca', appName, rootDomain) + ).toThrow( + 'Domain name is not accepted. You cannot have two consecutive periods ".." inside a domain name. Please use alphanumerical domains such as myapp.google123.ca' + ) + + expect(() => + Utils.checkCustomDomain(`app2.${rootDomain}`, appName, rootDomain) + ).toThrow( + 'Domain name is not accepted. Custom domain cannot be subdomain of root domain.' + ) + + expect(() => + Utils.checkCustomDomain( + `api.${appName}.${rootDomain}`, + appName, + rootDomain + ) + ).not.toThrow( + 'Domain name is not accepted. Custom domain cannot be subdomain of root domain.' + ) + + expect(() => + Utils.checkCustomDomain(`caprover123.ca`, appName, rootDomain) + ).not.toThrow() +})