From 215f70f87dcb64f9183a33f8d06f007270ba9811 Mon Sep 17 00:00:00 2001 From: psiddharthdesign <107192927+psiddharthdesign@users.noreply.github.com> Date: Tue, 30 Jul 2024 19:29:53 +0530 Subject: [PATCH] fix / optimizing tfvars logic --- .../(specific-project-pages)/TFVarTable.tsx | 23 ++--- .../TFVarsDetails.tsx | 20 ++--- .../(specific-project-pages)/tfvars/page.tsx | 57 ++----------- src/data/admin/encryption.ts | 14 +--- src/data/user/tfvars.ts | 84 +++++++++++++++++++ 5 files changed, 106 insertions(+), 92 deletions(-) diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/TFVarTable.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/TFVarTable.tsx index 6676b567..2ec899f0 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/TFVarTable.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/TFVarTable.tsx @@ -9,6 +9,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { Separator } from "@/components/ui/separator"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Textarea } from "@/components/ui/textarea"; +import { tfvarsOnBulkUpdate, tfvarsOnDelete, tfvarsOnUpdate } from "@/data/user/tfvars"; import { EnvVar } from "@/types/userTypes"; import { motion } from 'framer-motion'; import { Copy, Edit, LockKeyhole, Plus, Save, Trash, Unlock } from 'lucide-react'; @@ -19,9 +20,7 @@ import { toast } from 'sonner'; type TFVarTableProps = { envVars: EnvVar[]; - onUpdate: (oldName: string, newName: string, value: string, isSecret: boolean) => Promise; - onDelete: (name: string) => Promise; - onBulkUpdate: (vars: EnvVar[]) => Promise; + projectId: string; }; const EmptyState: React.FC<{ onAddVariable: () => void }> = ({ onAddVariable }) => { @@ -51,7 +50,7 @@ const EmptyState: React.FC<{ onAddVariable: () => void }> = ({ onAddVariable }) ); }; -export default function TFVarTable({ envVars, onUpdate, onDelete, onBulkUpdate }: TFVarTableProps) { +export default function TFVarTable({ projectId, envVars }: TFVarTableProps) { const [editingVar, setEditingVar] = useState<{ originalName: string, currentVar: EnvVar } | null>(null); const [newVar, setNewVar] = useState>({ name: '', value: '', is_secret: false }); const [bulkEditMode, setBulkEditMode] = useState(false); @@ -70,7 +69,6 @@ export default function TFVarTable({ envVars, onUpdate, onDelete, onBulkUpdate } }); }; - const handleSave = async () => { if (editingVar) { if (editingVar.currentVar.name.toLowerCase() !== editingVar.originalName.toLowerCase() && @@ -80,11 +78,12 @@ export default function TFVarTable({ envVars, onUpdate, onDelete, onBulkUpdate } } setIsLoading(true); try { - await onUpdate( + await tfvarsOnUpdate( editingVar.originalName, editingVar.currentVar.name, editingVar.currentVar.value, - editingVar.currentVar.is_secret + editingVar.currentVar.is_secret, + projectId ); toast.success('Variable updated successfully'); setEditingVar(null); @@ -97,7 +96,6 @@ export default function TFVarTable({ envVars, onUpdate, onDelete, onBulkUpdate } } }; - const handleAddNew = async () => { if (newVar.name && newVar.value) { if (envVars.some(v => v.name.toLowerCase() === newVar.name.toLowerCase())) { @@ -106,7 +104,7 @@ export default function TFVarTable({ envVars, onUpdate, onDelete, onBulkUpdate } } setIsLoading(true); try { - await onUpdate(newVar.name, newVar.name, newVar.value, newVar.is_secret); + await tfvarsOnUpdate(newVar.name, newVar.name, newVar.value, newVar.is_secret, projectId); toast.success('New variable added successfully'); setNewVar({ name: '', value: '', is_secret: false }); setShowAddForm(false); @@ -122,7 +120,7 @@ export default function TFVarTable({ envVars, onUpdate, onDelete, onBulkUpdate } const handleDeleteVar = async (name: string) => { setIsLoading(true); try { - await onDelete(name); + await tfvarsOnDelete(name, projectId); toast.success('Variable deleted successfully'); router.refresh(); } catch (error) { @@ -136,7 +134,6 @@ export default function TFVarTable({ envVars, onUpdate, onDelete, onBulkUpdate } try { const parsedVars = JSON.parse(bulkEditValue); if (Array.isArray(parsedVars)) { - // Check for duplicate names in the parsed vars const names = parsedVars.map(v => v.name.toLowerCase()); if (new Set(names).size !== names.length) { toast.error('Duplicate variable names are not allowed'); @@ -144,7 +141,7 @@ export default function TFVarTable({ envVars, onUpdate, onDelete, onBulkUpdate } } setIsLoading(true); - await onBulkUpdate(parsedVars); + await tfvarsOnBulkUpdate(parsedVars, projectId); toast.success('Bulk update successful'); setBulkEditMode(false); router.refresh(); @@ -156,7 +153,6 @@ export default function TFVarTable({ envVars, onUpdate, onDelete, onBulkUpdate } } }; - const toggleBulkEdit = () => { if (!bulkEditMode) { const nonSecretVars = envVars.filter(v => !v.is_secret).map(({ name, value }) => ({ name, value })); @@ -345,7 +341,6 @@ export default function TFVarTable({ envVars, onUpdate, onDelete, onBulkUpdate } Edit all environment variables at once in JSON format. Be careful with this operation.

- {/* */} diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/TFVarsDetails.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/TFVarsDetails.tsx index 0860738d..d9c35032 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/TFVarsDetails.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/TFVarsDetails.tsx @@ -1,3 +1,4 @@ +// TFVarsDetails.tsx 'use client' import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; @@ -8,18 +9,11 @@ import { motion } from "framer-motion"; import TFVarTable from "./TFVarTable"; type TFVarsDetailsProps = { - tfvarsdata: { - id: string; - project_id: string; - tfvars: EnvVar[]; - updated_at: string; - }; - onUpdate: (oldName: string, newName: string, value: string, isSecret: boolean) => Promise; - onDelete: (name: string) => Promise; - onBulkUpdate: (vars: EnvVar[]) => Promise; + projectId: string; + initialEnvVars: EnvVar[]; } -export default function TFVarsDetails({ tfvarsdata, onUpdate, onDelete, onBulkUpdate }: TFVarsDetailsProps) { +export default function TFVarsDetails({ projectId, initialEnvVars }: TFVarsDetailsProps) { return ( diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/tfvars/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/tfvars/page.tsx index 383c375d..5b2ba9c7 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/tfvars/page.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/tfvars/page.tsx @@ -1,6 +1,6 @@ -import { deleteEnvVar, getAllEnvVars, storeEncryptedEnvVar } from "@/data/admin/encryption"; +// page.tsx +import { getAllEnvVars } from "@/data/admin/encryption"; import { getSlimProjectBySlug } from "@/data/user/projects"; -import { EnvVar } from "@/types/userTypes"; import { projectSlugParamSchema } from "@/utils/zod-schemas/params"; import type { Metadata } from "next"; import TFVarsDetails from '../TFVarsDetails'; @@ -28,60 +28,13 @@ export default async function TFVarsPage({ params }: { params: unknown }) { throw new Error('MASTER_PASSWORD or ENCRYPTION_SALT is not set'); } - const envVars = await getAllEnvVars(project.id, MASTER_PASSWORD, ENCRYPTION_SALT); - - async function handleUpdate(oldName: string, newName: string, value: string, isSecret: boolean) { - 'use server' - if (oldName !== newName) { - await deleteEnvVar(project.id, oldName); - } - await storeEncryptedEnvVar(project.id, newName, value, isSecret, MASTER_PASSWORD, ENCRYPTION_SALT); - return getAllEnvVars(project.id, MASTER_PASSWORD, ENCRYPTION_SALT); - } - - async function handleDelete(name: string) { - 'use server' - await deleteEnvVar(project.id, name); - return getAllEnvVars(project.id, MASTER_PASSWORD, ENCRYPTION_SALT); - } - - async function handleBulkUpdate(vars: EnvVar[]) { - 'use server' - const currentVars = await getAllEnvVars(project.id, MASTER_PASSWORD, ENCRYPTION_SALT); - const currentVarsMap = Object.fromEntries(currentVars.map(v => [v.name, v])); - - for (const newVar of vars) { - const currentVar = currentVarsMap[newVar.name]; - if (currentVar) { - if (!currentVar.is_secret && (currentVar.value !== newVar.value || currentVar.name !== newVar.name)) { - await handleUpdate(currentVar.name, newVar.name, newVar.value, currentVar.is_secret); - } - } else { - await handleUpdate(newVar.name, newVar.name, newVar.value, false); - } - } - - for (const currentVar of currentVars) { - if (!vars.some(v => v.name === currentVar.name) && !currentVar.is_secret) { - await deleteEnvVar(project.id, currentVar.name); - } - } - - return getAllEnvVars(project.id, MASTER_PASSWORD, ENCRYPTION_SALT); - } + const envVars = await getAllEnvVars(project.id); return (
); diff --git a/src/data/admin/encryption.ts b/src/data/admin/encryption.ts index c6de87d1..0bce3cfc 100644 --- a/src/data/admin/encryption.ts +++ b/src/data/admin/encryption.ts @@ -61,8 +61,6 @@ export async function storeEncryptedEnvVar( name: string, value: string, isSecret: boolean, - masterPassword: string, - salt: string, ) { console.log('Encryption: Storing encrypted var:', { projectId, @@ -93,11 +91,7 @@ export async function storeEncryptedEnvVar( console.log('Encryption: Variable stored successfully'); return data; } -export async function getDecryptedEnvVar( - projectId: string, - name: string, - masterPassword: string, -) { +export async function getDecryptedEnvVar(projectId: string, name: string) { const { data, error } = await supabaseAdminClient .from('encrypted_env_vars') .select('encrypted_value, is_secret') @@ -133,11 +127,7 @@ export async function deleteEnvVar(projectId: string, name: string) { if (error) throw error; } -export async function getAllEnvVars( - projectId: string, - masterPassword: string, - salt: string, -): Promise { +export async function getAllEnvVars(projectId: string): Promise { const { data, error } = await supabaseAdminClient .from('encrypted_env_vars') .select('name, encrypted_value, is_secret, updated_at') diff --git a/src/data/user/tfvars.ts b/src/data/user/tfvars.ts index e69de29b..fee2d479 100644 --- a/src/data/user/tfvars.ts +++ b/src/data/user/tfvars.ts @@ -0,0 +1,84 @@ +// tfvars.ts +'use server'; + +import { EnvVar } from '@/types/userTypes'; +import { + deleteEnvVar, + getAllEnvVars, + storeEncryptedEnvVar, +} from '../admin/encryption'; + +export async function tfvarsOnUpdate( + oldName: string, + newName: string, + value: string, + isSecret: boolean, + projectId: string, +): Promise { + if (oldName !== newName) { + await deleteEnvVar(projectId, oldName); + } + await storeEncryptedEnvVar(projectId, newName, value, isSecret); + const vars = await getAllEnvVars(projectId); + return vars.map((v) => ({ ...v, updated_at: new Date().toISOString() })); +} + +export async function tfvarsOnDelete( + name: string, + projectId: string, +): Promise { + await deleteEnvVar(projectId, name); + const vars = await getAllEnvVars(projectId); + return vars.map((v) => ({ ...v, updated_at: new Date().toISOString() })); +} + +export async function tfvarsOnBulkUpdate( + vars: EnvVar[], + projectId: string, +): Promise { + const currentVars = await getAllEnvVars(projectId); + const currentVarsMap = Object.fromEntries( + currentVars.map((v) => [v.name, v]), + ); + + for (const newVar of vars) { + const currentVar = currentVarsMap[newVar.name]; + if (currentVar) { + if ( + !currentVar.is_secret && + (currentVar.value !== newVar.value || currentVar.name !== newVar.name) + ) { + await tfvarsOnUpdate( + currentVar.name, + newVar.name, + newVar.value, + currentVar.is_secret, + projectId, + ); + } + } else { + await tfvarsOnUpdate( + newVar.name, + newVar.name, + newVar.value, + false, + projectId, + ); + } + } + + for (const currentVar of currentVars) { + if ( + !vars.some((v) => v.name === currentVar.name) && + !currentVar.is_secret + ) { + await tfvarsOnDelete(currentVar.name, projectId); + } + } + + const updatedVars = await getAllEnvVars(projectId); + return updatedVars.map((v) => ({ + ...v, + updated_at: new Date().toISOString(), + })); +}