( userId: string, newVars: Record<string, string> )
| 246 | * Only overwrites keys whose decrypted value has actually changed. |
| 247 | */ |
| 248 | export async function upsertPersonalEnvVars( |
| 249 | userId: string, |
| 250 | newVars: Record<string, string> |
| 251 | ): Promise<EnvUpsertResult> { |
| 252 | const added: string[] = [] |
| 253 | const updated: string[] = [] |
| 254 | if (Object.keys(newVars).length === 0) return { added, updated } |
| 255 | |
| 256 | const existingData = await db |
| 257 | .select() |
| 258 | .from(environment) |
| 259 | .where(eq(environment.userId, userId)) |
| 260 | .limit(1) |
| 261 | const existingEncrypted = (existingData[0]?.variables as Record<string, string>) || {} |
| 262 | |
| 263 | const toEncrypt: Record<string, string> = {} |
| 264 | for (const [key, newVal] of Object.entries(newVars)) { |
| 265 | if (!(key in existingEncrypted)) { |
| 266 | toEncrypt[key] = newVal |
| 267 | added.push(key) |
| 268 | } else { |
| 269 | try { |
| 270 | const { decrypted } = await decryptSecret(existingEncrypted[key]) |
| 271 | if (decrypted !== newVal) { |
| 272 | toEncrypt[key] = newVal |
| 273 | updated.push(key) |
| 274 | } |
| 275 | } catch { |
| 276 | toEncrypt[key] = newVal |
| 277 | updated.push(key) |
| 278 | } |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | const newlyEncrypted: Record<string, string> = {} |
| 283 | for (const [key, val] of Object.entries(toEncrypt)) { |
| 284 | const { encrypted } = await encryptSecret(val) |
| 285 | newlyEncrypted[key] = encrypted |
| 286 | } |
| 287 | |
| 288 | const finalEncrypted = { ...existingEncrypted, ...newlyEncrypted } |
| 289 | |
| 290 | await db |
| 291 | .insert(environment) |
| 292 | .values({ |
| 293 | id: generateId(), |
| 294 | userId, |
| 295 | variables: finalEncrypted, |
| 296 | updatedAt: new Date(), |
| 297 | }) |
| 298 | .onConflictDoUpdate({ |
| 299 | target: [environment.userId], |
| 300 | set: { variables: finalEncrypted, updatedAt: new Date() }, |
| 301 | }) |
| 302 | |
| 303 | invalidateEffectiveDecryptedEnvCache({ userId }) |
| 304 | await syncPersonalEnvCredentialsForUser({ |
| 305 | userId, |
no test coverage detected