Prechádzať zdrojové kódy

feat: don't allow multiple apps with same domain

Nicolas Meienberger 2 rokov pred
rodič
commit
bbe774857a

+ 21 - 0
packages/system-api/src/modules/apps/__tests__/apps.service.test.ts

@@ -151,6 +151,17 @@ describe('Install app', () => {
 
     await expect(AppsService.installApp(appInfo.id, { TEST_FIELD: 'test' }, true, 'test')).rejects.toThrowError('Domain test is not valid');
   });
+
+  it('Should throw if app is exposed and domain is already used', async () => {
+    const app2 = await createApp({ exposable: true });
+    const app3 = await createApp({ exposable: true });
+    // @ts-ignore
+    fs.__createMockFiles(Object.assign({}, app2.MockFiles, app3.MockFiles));
+
+    await AppsService.installApp(app2.appInfo.id, { TEST_FIELD: 'test' }, true, 'test.com');
+
+    await expect(AppsService.installApp(app3.appInfo.id, { TEST_FIELD: 'test' }, true, 'test.com')).rejects.toThrowError(`Domain test.com already in use by app ${app2.appInfo.id}`);
+  });
 });
 
 describe('Uninstall app', () => {
@@ -362,6 +373,16 @@ describe('Update app config', () => {
   it('Should throw if app is exposed and config does not allow it', () => {
     return expect(AppsService.updateAppConfig(app1.id, { TEST_FIELD: 'test' }, true, 'test.com')).rejects.toThrowError(`App ${app1.id} is not exposable`);
   });
+
+  it('Should throw if app is exposed and domain is already used', async () => {
+    const app2 = await createApp({ exposable: true, installed: true });
+    const app3 = await createApp({ exposable: true, installed: true });
+    // @ts-ignore
+    fs.__createMockFiles(Object.assign(app2.MockFiles, app3.MockFiles));
+
+    await AppsService.updateAppConfig(app2.appInfo.id, { TEST_FIELD: 'test' }, true, 'test.com');
+    await expect(AppsService.updateAppConfig(app3.appInfo.id, { TEST_FIELD: 'test' }, true, 'test.com')).rejects.toThrowError(`Domain test.com already in use by app ${app2.appInfo.id}`);
+  });
 });
 
 describe('Get app config', () => {

+ 14 - 0
packages/system-api/src/modules/apps/apps.service.ts

@@ -90,6 +90,13 @@ const installApp = async (id: string, form: Record<string, string>, exposed?: bo
       throw new Error(`App ${id} is not exposable`);
     }
 
+    if (exposed) {
+      const appsWithSameDomain = await App.find({ where: { domain, exposed: true } });
+      if (appsWithSameDomain.length > 0) {
+        throw new Error(`Domain ${domain} already in use by app ${appsWithSameDomain[0].id}`);
+      }
+    }
+
     app = await App.create({ id, status: AppStatusEnum.INSTALLING, config: form, version: Number(appInfo?.tipi_version || 0), exposed: exposed || false, domain }).save();
 
     // Create env file
@@ -145,6 +152,13 @@ const updateAppConfig = async (id: string, form: Record<string, string>, exposed
     throw new Error(`App ${id} is not exposable`);
   }
 
+  if (exposed) {
+    const appsWithSameDomain = await App.find({ where: { domain, exposed: true } });
+    if (appsWithSameDomain.length > 0) {
+      throw new Error(`Domain ${domain} already in use by app ${appsWithSameDomain[0].id}`);
+    }
+  }
+
   let app = await App.findOne({ where: { id } });
 
   if (!app) {