diff --git a/backend/src/services/secret-sync/railway/railway-sync-fns.ts b/backend/src/services/secret-sync/railway/railway-sync-fns.ts index ce18e526f..5f1fa4b8a 100644 --- a/backend/src/services/secret-sync/railway/railway-sync-fns.ts +++ b/backend/src/services/secret-sync/railway/railway-sync-fns.ts @@ -42,85 +42,109 @@ export const RailwaySyncFns = { /** * Syncs secrets to Railway and redeploys the service if needed. - * + * * Gets existing Railway vars, merges with new secrets (keeping Railway vars if deletion is disabled), - * then replaces every variable with the new values, if variable is not in the secretMap, it is deleted. + * then replaces every variable with the new values, if variable is not in the secretMap, it is deleted. * If there's a service, triggers a redeploy to pick up the changes. */ async syncSecrets(secretSync: TRailwaySyncWithCredentials, secretMap: TSecretMap) { - const { - syncOptions: { disableSecretDeletion } - } = secretSync; - const railwaySecrets = await this.getSecrets(secretSync); - const config = secretSync.destinationConfig; + try { + const { + syncOptions: { disableSecretDeletion } + } = secretSync; + const railwaySecrets = await this.getSecrets(secretSync); + const config = secretSync.destinationConfig; - const railwaySecretsMap = Object.fromEntries(Object.entries(railwaySecrets).map(([key, secret]) => [key, secret.value])); - const secretMapMap = Object.fromEntries(Object.entries(secretMap).map(([key, secret]) => [key, secret.value])); + const railwaySecretsMap = Object.fromEntries( + Object.entries(railwaySecrets).map(([key, secret]) => [key, secret.value]) + ); + const secretMapMap = Object.fromEntries(Object.entries(secretMap).map(([key, secret]) => [key, secret.value])); - const toReplace = disableSecretDeletion - ? { ...railwaySecretsMap, ...secretMapMap } - : secretMapMap; + const toReplace = disableSecretDeletion ? { ...railwaySecretsMap, ...secretMapMap } : secretMapMap; - const upserted = await RailwayPublicAPI.upsertCollection(secretSync.connection, { - input: { - projectId: config.projectId, - environmentId: config.environmentId, - serviceId: config.serviceId || undefined, - skipDeploys: true, - variables: toReplace, - replace: true, - } - }); + const upserted = await RailwayPublicAPI.upsertCollection(secretSync.connection, { + input: { + projectId: config.projectId, + environmentId: config.environmentId, + serviceId: config.serviceId || undefined, + skipDeploys: true, + variables: toReplace, + replace: true + } + }); - if (!upserted) throw new SecretSyncError({ - message: "Failed to upsert secrets to Railway", - }) + if (!upserted) + throw new SecretSyncError({ + message: "Failed to upsert secrets to Railway" + }); - if (!config.serviceId) return; + if (!config.serviceId) return; - const latestDeployment = await RailwayPublicAPI.getDeployments(secretSync.connection, { - input: { - serviceId: config.serviceId, - environmentId: config.environmentId - }, - first: 1, - }); + const latestDeployment = await RailwayPublicAPI.getDeployments(secretSync.connection, { + input: { + serviceId: config.serviceId, + environmentId: config.environmentId + }, + first: 1 + }); - const latestDeploymentId = latestDeployment?.deployments.edges[0].node.id; + const latestDeploymentId = latestDeployment?.deployments.edges[0].node.id; - if (!latestDeploymentId) throw new SecretSyncError({ - message: "Failed to get latest deployment from Railway", - }) + if (!latestDeploymentId) + throw new SecretSyncError({ + message: "Failed to get latest deployment from Railway" + }); - await RailwayPublicAPI.redeployDeployment(secretSync.connection, { - input: { - deploymentId: latestDeploymentId - } - }); + await RailwayPublicAPI.redeployDeployment(secretSync.connection, { + input: { + deploymentId: latestDeploymentId + } + }); + } catch (error) { + if (error instanceof SecretSyncError) throw error; + + throw new SecretSyncError({ + error, + message: "Failed to sync secrets to Railway" + }); + } }, async removeSecrets(secretSync: TRailwaySyncWithCredentials, secretMap: TSecretMap) { const existing = await this.getSecrets(secretSync); const config = secretSync.destinationConfig; - for await (const secret of Object.keys(existing)) { - try { - if (secret in secretMap) { - await RailwayPublicAPI.deleteVariable(secretSync.connection, { - input: { - projectId: config.projectId, - environmentId: config.environmentId, - serviceId: config.serviceId || undefined, - name: secret - } - }); + // Create a new variables object excluding secrets that exist in secretMap + const remainingVariables = Object.fromEntries( + Object.entries(existing) + .filter(([key]) => !(key in secretMap)) + .map(([key, secret]) => [key, secret.value]) + ); + + try { + const upserted = await RailwayPublicAPI.upsertCollection(secretSync.connection, { + input: { + projectId: config.projectId, + environmentId: config.environmentId, + serviceId: config.serviceId || undefined, + skipDeploys: true, + variables: remainingVariables, + replace: true } - } catch (error) { + }); + + if (!upserted) { throw new SecretSyncError({ - error, - secretKey: secret + message: "Failed to remove secrets from Railway" }); } + } catch (error) { + if (error instanceof SecretSyncError) throw error; + + throw new SecretSyncError({ + error, + message: "Failed to remove secrets from Railway" + }); } } };