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

This commit is contained in:
Nicolas Meienberger 2023-04-17 09:16:51 +02:00 committed by Nicolas Meienberger
parent a3b2facdd2
commit 0e81ced999
4 changed files with 32 additions and 1 deletions

View file

@ -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()

View file

@ -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', () => {

View file

@ -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) {

View file

@ -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,