mirror of
https://github.com/HeyPuter/puter.git
synced 2026-05-04 08:30:39 +00:00
fix: app icons saving (#2477)
This commit is contained in:
@@ -20,6 +20,12 @@ import {
|
||||
validate_url,
|
||||
} from './lib/validation.js';
|
||||
|
||||
const APP_ICON_ENDPOINT_PATH_REGEX = /^\/?app-icon\/[^/?#]+\/[^/?#]+\/?$/;
|
||||
|
||||
const isAppIconEndpointPath = (value) => {
|
||||
return typeof value === 'string' && APP_ICON_ENDPOINT_PATH_REGEX.test(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* AppService contains an instance using the repository pattern
|
||||
*/
|
||||
@@ -385,6 +391,8 @@ export default class AppService extends BaseService {
|
||||
if ( object.icon !== undefined && object.icon !== null ) {
|
||||
if ( typeof object.icon === 'string' && object.icon.startsWith('data:') ) {
|
||||
validate_image_base64(object.icon, { key: 'icon' });
|
||||
} else if ( isAppIconEndpointPath(object.icon) ) {
|
||||
// Allow existing relative app icon endpoint references.
|
||||
} else {
|
||||
validate_url(object.icon, { key: 'icon', maxlen: 3000 });
|
||||
}
|
||||
@@ -631,6 +639,8 @@ export default class AppService extends BaseService {
|
||||
if ( object.icon !== undefined && object.icon !== null ) {
|
||||
if ( typeof object.icon === 'string' && object.icon.startsWith('data:') ) {
|
||||
validate_image_base64(object.icon, { key: 'icon' });
|
||||
} else if ( isAppIconEndpointPath(object.icon) ) {
|
||||
// Allow existing relative app icon endpoint references.
|
||||
} else {
|
||||
validate_url(object.icon, { key: 'icon', maxlen: 3000 });
|
||||
}
|
||||
|
||||
@@ -806,6 +806,33 @@ describe('AppService', () => {
|
||||
}));
|
||||
});
|
||||
|
||||
it('should allow relative app-icon endpoint path for icon', async () => {
|
||||
setupContextForWrite(createMockUserActor(1));
|
||||
mockDb.read.mockResolvedValue([createMockAppRow()]);
|
||||
validate_url.mockImplementation((_value, { key }) => {
|
||||
if ( key === 'icon' ) {
|
||||
throw new Error('icon should not be validated as a URL');
|
||||
}
|
||||
});
|
||||
|
||||
const crudQ = AppService.IMPLEMENTS['crud-q'];
|
||||
await crudQ.create.call(appService, {
|
||||
object: {
|
||||
name: 'test-app',
|
||||
title: 'Test',
|
||||
index_url: 'https://example.com',
|
||||
icon: '/app-icon/app-uid-123/64',
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockEventService.emit).toHaveBeenCalledWith(
|
||||
'app.new-icon',
|
||||
expect.objectContaining({
|
||||
data_url: '/app-icon/app-uid-123/64',
|
||||
}));
|
||||
expect(validate_url).toHaveBeenCalledWith('https://example.com', expect.objectContaining({ key: 'index_url' }));
|
||||
});
|
||||
|
||||
it('should handle filetype_associations', async () => {
|
||||
setupContextForWrite(createMockUserActor(1));
|
||||
mockDb.read.mockResolvedValue([createMockAppRow()]);
|
||||
@@ -1060,6 +1087,30 @@ describe('AppService', () => {
|
||||
}));
|
||||
});
|
||||
|
||||
it('should allow relative app-icon endpoint path when updating icon', async () => {
|
||||
setupContextForWrite(createMockUserActor(1));
|
||||
validate_url.mockImplementation((_value, { key }) => {
|
||||
if ( key === 'icon' ) {
|
||||
throw new Error('icon should not be validated as a URL');
|
||||
}
|
||||
});
|
||||
|
||||
const crudQ = AppService.IMPLEMENTS['crud-q'];
|
||||
await crudQ.update.call(appService, {
|
||||
object: {
|
||||
uid: 'app-uid-123',
|
||||
icon: '/app-icon/app-uid-123/64',
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockEventService.emit).toHaveBeenCalledWith(
|
||||
'app.new-icon',
|
||||
expect.objectContaining({
|
||||
app_uid: 'app-uid-123',
|
||||
data_url: '/app-icon/app-uid-123/64',
|
||||
}));
|
||||
});
|
||||
|
||||
it('should emit app.rename event when name changes', async () => {
|
||||
setupContextForWrite(createMockUserActor(1));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user