mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 15:41:27 +00:00
--------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Scott Wilson <[email protected]>
This commit is contained in:
co-authored by
greptile-apps[bot]
Scott Wilson
parent
0e07ebae7b
commit
ecc15bb432
@@ -0,0 +1,4 @@
|
||||
export * from "./supabase-sync-constants";
|
||||
export * from "./supabase-sync-fns";
|
||||
export * from "./supabase-sync-schemas";
|
||||
export * from "./supabase-sync-types";
|
||||
@@ -0,0 +1,10 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
export const SUPABASE_SYNC_LIST_OPTION: TSecretSyncListItem = {
|
||||
name: "Supabase",
|
||||
destination: SecretSync.Supabase,
|
||||
connection: AppConnection.Supabase,
|
||||
canImportSecrets: false
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
/* eslint-disable no-continue */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
|
||||
import { chunkArray } from "@app/lib/fn";
|
||||
import { TSupabaseSecret } from "@app/services/app-connection/supabase";
|
||||
import { SupabasePublicAPI } from "@app/services/app-connection/supabase/supabase-connection-public-client";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
|
||||
import { SecretSyncError } from "../secret-sync-errors";
|
||||
import { SECRET_SYNC_NAME_MAP } from "../secret-sync-maps";
|
||||
import { TSecretMap } from "../secret-sync-types";
|
||||
import { TSupabaseSyncWithCredentials } from "./supabase-sync-types";
|
||||
|
||||
const SUPABASE_INTERNAL_SECRETS = ["SUPABASE_URL", "SUPABASE_ANON_KEY", "SUPABASE_SERVICE_ROLE_KEY", "SUPABASE_DB_URL"];
|
||||
|
||||
export const SupabaseSyncFns = {
|
||||
async getSecrets(secretSync: TSupabaseSyncWithCredentials) {
|
||||
throw new Error(`${SECRET_SYNC_NAME_MAP[secretSync.destination]} does not support importing secrets.`);
|
||||
},
|
||||
|
||||
async syncSecrets(secretSync: TSupabaseSyncWithCredentials, secretMap: TSecretMap) {
|
||||
const {
|
||||
environment,
|
||||
syncOptions: { disableSecretDeletion, keySchema }
|
||||
} = secretSync;
|
||||
const config = secretSync.destinationConfig;
|
||||
|
||||
const variables = await SupabasePublicAPI.getVariables(secretSync.connection, config.projectId);
|
||||
|
||||
const supabaseSecrets = new Map(variables!.map((variable) => [variable.name, variable]));
|
||||
|
||||
const toCreate: TSupabaseSecret[] = [];
|
||||
|
||||
for (const key of Object.keys(secretMap)) {
|
||||
const variable: TSupabaseSecret = { name: key, value: secretMap[key].value ?? "" };
|
||||
toCreate.push(variable);
|
||||
}
|
||||
|
||||
for await (const batch of chunkArray(toCreate, 100)) {
|
||||
try {
|
||||
await SupabasePublicAPI.createVariables(secretSync.connection, config.projectId, ...batch);
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: batch[0].name // Use the first key in the batch for error reporting
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (disableSecretDeletion) return;
|
||||
|
||||
const toDelete: string[] = [];
|
||||
|
||||
for (const key of supabaseSecrets.keys()) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(key, environment?.slug || "", keySchema) || SUPABASE_INTERNAL_SECRETS.includes(key)) continue;
|
||||
|
||||
if (!secretMap[key]) {
|
||||
toDelete.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
for await (const batch of chunkArray(toDelete, 100)) {
|
||||
try {
|
||||
await SupabasePublicAPI.deleteVariables(secretSync.connection, config.projectId, ...batch);
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: batch[0] // Use the first key in the batch for error reporting
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async removeSecrets(secretSync: TSupabaseSyncWithCredentials, secretMap: TSecretMap) {
|
||||
const config = secretSync.destinationConfig;
|
||||
|
||||
const variables = await SupabasePublicAPI.getVariables(secretSync.connection, config.projectId);
|
||||
|
||||
const supabaseSecrets = new Map(variables!.map((variable) => [variable.name, variable]));
|
||||
|
||||
const toDelete: string[] = [];
|
||||
|
||||
for (const key of supabaseSecrets.keys()) {
|
||||
if (SUPABASE_INTERNAL_SECRETS.includes(key) || !(key in secretMap)) continue;
|
||||
|
||||
toDelete.push(key);
|
||||
}
|
||||
|
||||
for await (const batch of chunkArray(toDelete, 100)) {
|
||||
try {
|
||||
await SupabasePublicAPI.deleteVariables(secretSync.connection, config.projectId, ...batch);
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: batch[0] // Use the first key in the batch for error reporting
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import {
|
||||
BaseSecretSyncSchema,
|
||||
GenericCreateSecretSyncFieldsSchema,
|
||||
GenericUpdateSecretSyncFieldsSchema
|
||||
} from "@app/services/secret-sync/secret-sync-schemas";
|
||||
import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
const SupabaseSyncDestinationConfigSchema = z.object({
|
||||
projectId: z.string().max(255).min(1, "Project ID is required"),
|
||||
projectName: z.string().max(255).min(1, "Project Name is required")
|
||||
});
|
||||
|
||||
const SupabaseSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: false };
|
||||
|
||||
export const SupabaseSyncSchema = BaseSecretSyncSchema(SecretSync.Supabase, SupabaseSyncOptionsConfig).extend({
|
||||
destination: z.literal(SecretSync.Supabase),
|
||||
destinationConfig: SupabaseSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const CreateSupabaseSyncSchema = GenericCreateSecretSyncFieldsSchema(
|
||||
SecretSync.Supabase,
|
||||
SupabaseSyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: SupabaseSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const UpdateSupabaseSyncSchema = GenericUpdateSecretSyncFieldsSchema(
|
||||
SecretSync.Supabase,
|
||||
SupabaseSyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: SupabaseSyncDestinationConfigSchema.optional()
|
||||
});
|
||||
|
||||
export const SupabaseSyncListItemSchema = z.object({
|
||||
name: z.literal("Supabase"),
|
||||
connection: z.literal(AppConnection.Supabase),
|
||||
destination: z.literal(SecretSync.Supabase),
|
||||
canImportSecrets: z.literal(false)
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import z from "zod";
|
||||
|
||||
import { TSupabaseConnection } from "@app/services/app-connection/supabase";
|
||||
|
||||
import { CreateSupabaseSyncSchema, SupabaseSyncListItemSchema, SupabaseSyncSchema } from "./supabase-sync-schemas";
|
||||
|
||||
export type TSupabaseSyncListItem = z.infer<typeof SupabaseSyncListItemSchema>;
|
||||
|
||||
export type TSupabaseSync = z.infer<typeof SupabaseSyncSchema>;
|
||||
|
||||
export type TSupabaseSyncInput = z.infer<typeof CreateSupabaseSyncSchema>;
|
||||
|
||||
export type TSupabaseSyncWithCredentials = TSupabaseSync & {
|
||||
connection: TSupabaseConnection;
|
||||
};
|
||||
|
||||
export type TSupabaseVariablesGraphResponse = {
|
||||
data: {
|
||||
variables: Record<string, string>;
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user