Files
lunarfront-app/packages/backend/src/services/settings.service.ts
Ryan Moon b8e39369f1
Some checks failed
Build & Release / build (push) Failing after 35s
feat: add app settings table, encryption utility, and generic email service
- app_settings table with encrypted field support (AES-256-GCM, key from ENCRYPTION_KEY env)
- SettingsService for transparent encrypt/decrypt on get/set
- EmailService factory with Resend and SendGrid providers (SMTP stub) — provider config lives in app_settings
- Seeds initial admin user and email settings from env vars on first startup if not already present
- Migration 0039_app_settings.sql

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 10:27:20 -05:00

42 lines
1.3 KiB
TypeScript

import { eq } from 'drizzle-orm'
import type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'
import { appSettings } from '../db/schema/settings.js'
import { encrypt, decrypt } from '../utils/encryption.js'
export const SettingsService = {
async get(db: PostgresJsDatabase<any>, key: string): Promise<string | null> {
const [row] = await db
.select()
.from(appSettings)
.where(eq(appSettings.key, key))
.limit(1)
if (!row || row.value === null) return null
if (row.isEncrypted && row.iv) return decrypt(row.value, row.iv)
return row.value
},
async set(db: PostgresJsDatabase<any>, key: string, value: string, encrypted = false): Promise<void> {
let storedValue = value
let iv: string | null = null
if (encrypted) {
const result = encrypt(value)
storedValue = result.ciphertext
iv = result.iv
}
await db
.insert(appSettings)
.values({ key, value: storedValue, isEncrypted: encrypted, iv, updatedAt: new Date() })
.onConflictDoUpdate({
target: appSettings.key,
set: { value: storedValue, isEncrypted: encrypted, iv, updatedAt: new Date() },
})
},
async delete(db: PostgresJsDatabase<any>, key: string): Promise<void> {
await db.delete(appSettings).where(eq(appSettings.key, key))
},
}