Hotfix-cant-run-tools-when-no-credits (#5955)

Tested:
* Can sign in on saas -> can run local tools with or without credits->
can run saas only tools (if credits) -> can't run saas only tools
without credits
* Can sign in self-hosted -> can run all tools on remote if available ->
can run local when self-hosted unavailable

Clouds show on saas tools when connected
Tools are disabled when connected to self-hosted but cannot find server.
You also get banner


#cantwaitforplaywritetests
This commit is contained in:
ConnorYoh
2026-03-17 13:01:08 +00:00
committed by GitHub
parent b656e1e2d1
commit 214dc20c2e
8 changed files with 61 additions and 29 deletions
@@ -194,6 +194,11 @@ export const convertOperationConfig = {
customProcessor: convertProcessor, // Can't use callback version here
operationType: 'convert',
defaultParameters,
endpoint: (params: ConvertParameters): string | undefined => {
if (!params.fromExtension || !params.toExtension) return undefined;
const actualToExtension = params.toExtension === 'pdfx' ? 'pdfa' : params.toExtension;
return getEndpointUrl(params.fromExtension, actualToExtension) ?? undefined;
},
} as const;
export const useConvertOperation = (parameters?: ConvertParameters) => {
@@ -101,7 +101,13 @@ export interface CustomToolOperationConfig<TParams> extends BaseToolOperationCon
toolType: ToolType.custom;
buildFormData?: undefined;
endpoint?: undefined;
/**
* Optional endpoint for routing decisions (credit check, cloud detection).
* Not used for the API call itself — customProcessor handles that directly.
* Provide a function when the endpoint depends on runtime parameters.
*/
endpoint?: string | ((params: TParams) => string | undefined);
/**
* Custom processing logic that completely bypasses standard file processing.
@@ -70,14 +70,15 @@ export const useToolOperation = <TParams>(
const { processFiles, cancelOperation: cancelApiCalls } = useToolApiCalls<TParams>();
const { generateThumbnails, createDownloadInfo, cleanupBlobUrls, extractZipFiles } = useToolResources();
const { checkCredits } = useCreditCheck(config.operationType);
// Determine endpoint for cloud usage check
const endpointString = config.toolType !== ToolType.custom && config.endpoint
// Determine endpoint for cloud usage check and credit routing.
// For function endpoints, use defaultParameters to get a representative static value.
const endpointString = config.endpoint
? (typeof config.endpoint === 'function'
? (config.defaultParameters ? config.endpoint(config.defaultParameters) : undefined)
? (config.defaultParameters ? (config.endpoint(config.defaultParameters) ?? undefined) : undefined)
: config.endpoint)
: undefined;
const { checkCredits } = useCreditCheck(config.operationType, endpointString);
const willUseCloud = useWillUseCloud(endpointString);
// Track last operation for undo functionality
@@ -114,22 +115,24 @@ export const useToolOperation = <TParams>(
return;
}
// Get endpoint (static or dynamic) for backend readiness check
const endpoint = config.customProcessor
? undefined // Custom processors may not have endpoints
: typeof config.endpoint === 'function'
? config.endpoint(params)
: config.endpoint;
// Resolve the runtime endpoint from params (static string or function result).
// Custom processors may omit endpoint entirely — result is undefined in that case.
const runtimeEndpoint: string | undefined = config.endpoint
? (typeof config.endpoint === 'function' ? (config.endpoint(params) ?? undefined) : config.endpoint)
: undefined;
// Credit check — no-op in core builds, real check in desktop/SaaS versions
const creditError = await checkCredits();
// Credit check — no-op in core builds, real check in desktop/SaaS versions.
// Pass runtime endpoint so the check can determine if this routes locally (no credits needed).
const creditError = await checkCredits(runtimeEndpoint);
if (creditError !== null) {
actions.setError(creditError);
return;
}
// Backend readiness check (will skip for SaaS-routed endpoints)
const backendReady = await ensureBackendReady(endpoint);
// Backend readiness check (will skip for SaaS-routed endpoints).
// Custom processors without an endpoint skip this — they manage their own backend calls.
const endpointForReadyCheck = config.toolType !== ToolType.custom ? runtimeEndpoint : undefined;
const backendReady = await ensureBackendReady(endpointForReadyCheck);
if (!backendReady) {
actions.setError(t('backendHealth.offline', 'Embedded backend is offline. Please try again shortly.'));
return;
+2 -2
View File
@@ -3,8 +3,8 @@
* Desktop layer shadows this with the real implementation
* In web builds, always allows the operation (no credit system)
*/
export function useCreditCheck(_operationType?: string) {
export function useCreditCheck(_operationType?: string, _endpoint?: string) {
return {
checkCredits: async (): Promise<string | null> => null, // null = allowed
checkCredits: async (_runtimeEndpoint?: string): Promise<string | null> => null, // null = allowed
};
}