Ver Fonte

feat(server): add the force_expose option and logic to app config

Nicolas Meienberger há 2 anos atrás
pai
commit
0e81ced999

+ 1 - 0
src/server/services/apps/apps.helpers.ts

@@ -36,6 +36,7 @@ export const appInfoSchema = z.object({
   author: z.string(),
   source: z.string(),
   website: z.string().optional(),
+  force_expose: z.boolean().optional().default(false),
   categories: z
     .nativeEnum(APP_CATEGORIES)
     .array()

+ 20 - 0
src/server/services/apps/apps.service.test.ts

@@ -206,6 +206,16 @@ describe('Install app', () => {
     // act & assert
     await expect(AppsService.installApp(appInfo.id, { TEST_FIELD: 'test' })).rejects.toThrowError(`App ${appInfo.id} has invalid config.json file`);
   });
+
+  it('should throw if app is not exposed and config has force_expose set to true', async () => {
+    // arrange
+    const { MockFiles, appInfo } = await createApp({ forceExpose: true }, db);
+    // @ts-expect-error - Mocking fs
+    fs.__createMockFiles(MockFiles);
+
+    // act & assert
+    await expect(AppsService.installApp(appInfo.id, { TEST_FIELD: 'test' })).rejects.toThrowError(`App ${appInfo.id} works only with exposed domain`);
+  });
 });
 
 describe('Uninstall app', () => {
@@ -436,6 +446,16 @@ describe('Update app config', () => {
 
     await expect(AppsService.updateAppConfig(appInfo.id, { TEST_FIELD: 'test' })).rejects.toThrowError(`App ${appInfo.id} has invalid config.json`);
   });
+
+  it('should throw if app is not exposed and config has force_expose set to true', async () => {
+    // arrange
+    const { MockFiles, appInfo } = await createApp({ forceExpose: true, installed: true }, db);
+    // @ts-expect-error - Mocking fs
+    fs.__createMockFiles(MockFiles);
+
+    // act & assert
+    await expect(AppsService.updateAppConfig(appInfo.id, { TEST_FIELD: 'test' })).rejects.toThrowError(`App ${appInfo.id} works only with exposed domain`);
+  });
 });
 
 describe('Get app config', () => {

+ 8 - 0
src/server/services/apps/apps.service.ts

@@ -139,6 +139,10 @@ export class AppServiceClass {
         throw new Error(`App ${id} is not exposable`);
       }
 
+      if ((appInfo.force_expose && !exposed) || (appInfo.force_expose && !domain)) {
+        throw new Error(`App ${id} works only with exposed domain`);
+      }
+
       if (exposed) {
         const appsWithSameDomain = await this.prisma.app.findMany({ where: { domain, exposed: true } });
         if (appsWithSameDomain.length > 0) {
@@ -213,6 +217,10 @@ export class AppServiceClass {
       throw new Error(`App ${id} is not exposable`);
     }
 
+    if ((appInfo.force_expose && !exposed) || (appInfo.force_expose && !domain)) {
+      throw new Error(`App ${id} works only with exposed domain`);
+    }
+
     if (exposed) {
       const appsWithSameDomain = await this.prisma.app.findMany({ where: { domain, exposed: true, id: { not: id } } });
       if (appsWithSameDomain.length > 0) {

+ 3 - 1
src/server/tests/apps.factory.ts

@@ -12,6 +12,7 @@ interface IProps {
   exposed?: boolean;
   domain?: string;
   exposable?: boolean;
+  forceExpose?: boolean;
   supportedArchitectures?: Architecture[];
 }
 
@@ -31,7 +32,7 @@ const createAppConfig = (props?: Partial<AppInfo>) =>
   });
 
 const createApp = async (props: IProps, db?: PrismaClient) => {
-  const { installed = false, status = 'running', randomField = false, exposed = false, domain = '', exposable = false, supportedArchitectures } = props;
+  const { installed = false, status = 'running', randomField = false, exposed = false, domain = '', exposable = false, supportedArchitectures, forceExpose = false } = props;
 
   const categories = Object.values(APP_CATEGORIES);
 
@@ -57,6 +58,7 @@ const createApp = async (props: IProps, db?: PrismaClient) => {
     source: faker.internet.url(),
     categories: [categories[faker.datatype.number({ min: 0, max: categories.length - 1 })]] as AppInfo['categories'],
     exposable,
+    force_expose: forceExpose,
     supported_architectures: supportedArchitectures,
     version: String(faker.datatype.number({ min: 1, max: 10 })),
     https: false,