fix(appstore): handle App Store backend failures and empty RPC responses

This commit is contained in:
2026-05-27 21:32:32 +00:00
parent 9bac0a5f71
commit 78d7479b4a
6 changed files with 113 additions and 30 deletions
+61 -7
View File
@@ -84,13 +84,53 @@ export class CloudlyAppStoreManager {
public async start() {}
public async stop() {}
private getErrorMessage(errorArg: unknown): string {
if (errorArg instanceof Error) return errorArg.message;
return String(errorArg);
}
private getSafeAppStoreErrorMessage(errorArg: unknown): string {
const message = this.getErrorMessage(errorArg);
const lowerMessage = message.toLowerCase();
if (
lowerMessage.includes('fetch') ||
lowerMessage.includes('connect') ||
lowerMessage.includes('connection refused') ||
lowerMessage.includes('network') ||
/http \d+/.test(lowerMessage)
) {
return 'The App Store backend is currently unreachable. Please retry later.';
}
if (
lowerMessage.includes('domain is required') ||
lowerMessage.includes('missing required app env var') ||
lowerMessage.includes('unsupported platform requirement') ||
lowerMessage.includes('published port') ||
lowerMessage.includes('app requires cloudly')
) {
return message;
}
return 'The App Store request failed. Please retry later.';
}
private createSafeAppStoreTypedError(actionArg: string, errorArg: unknown): plugins.typedrequest.TypedResponseError {
console.warn(`${actionArg}: ${this.getErrorMessage(errorArg)}`);
return new plugins.typedrequest.TypedResponseError(
`${actionArg}: ${this.getSafeAppStoreErrorMessage(errorArg)}`,
);
}
private registerHandlers() {
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.appstore.IReq_Any_GetAppStoreTemplates>(
'getAppStoreTemplates',
async (dataArg) => {
await this.passAdminIdentity(dataArg);
return { apps: await this.getApps() };
try {
return { apps: await this.getApps() };
} catch (error) {
throw this.createSafeAppStoreTypedError('Could not load App Store templates', error);
}
},
),
);
@@ -100,10 +140,17 @@ export class CloudlyAppStoreManager {
'getAppStoreConfig',
async (dataArg) => {
await this.passAdminIdentity(dataArg);
return {
config: await this.getAppVersionConfig(dataArg.appId, dataArg.version),
appMeta: await this.getAppMeta(dataArg.appId),
};
try {
return {
config: await this.getAppVersionConfig(dataArg.appId, dataArg.version),
appMeta: await this.getAppMeta(dataArg.appId),
};
} catch (error) {
throw this.createSafeAppStoreTypedError(
`Could not load App Store details for ${dataArg.appId}@${dataArg.version || 'latest'}`,
error,
);
}
},
),
);
@@ -113,8 +160,15 @@ export class CloudlyAppStoreManager {
'installAppStoreApp',
async (dataArg) => {
await this.passAdminIdentity(dataArg);
const service = await this.installApp(dataArg.install);
return { service: await service.createSavableObject() };
try {
const service = await this.installApp(dataArg.install);
return { service: await service.createSavableObject() };
} catch (error) {
throw this.createSafeAppStoreTypedError(
`Could not install App Store app ${dataArg.install?.appId || 'unknown'}`,
error,
);
}
},
),
);