diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 1c40c21..68d49c9 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -32,10 +32,11 @@ model User { } model Tenant { - id String @id @default(uuid()) - clerkOrganizationId String @unique - users User[] - tenantConfig TenantConfig? + id String @id @default(uuid()) + clerkOrganizationId String @unique + + users User[] + tenantConfig TenantConfig? name String slug String @unique @@ -49,6 +50,17 @@ model TenantConfig { tenant Tenant @relation(fields: [tenantId], references: [id]) tenantId String @unique + twilioConfig TwilioConfig? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model TwilioConfig { + id String @id @default(uuid()) + tenantConfig TenantConfig @relation(fields: [tenantConfigId], references: [id]) + tenantConfigId String @unique + accountSID String authToken String phoneNumber String diff --git a/src/routes/app/settings/+page.server.ts b/src/routes/app/settings/+page.server.ts index 3dfb6df..765f48d 100644 --- a/src/routes/app/settings/+page.server.ts +++ b/src/routes/app/settings/+page.server.ts @@ -10,7 +10,15 @@ export const load = async (event) => { const configs = await prisma.tenantConfig.findUnique({ where: { tenantId: tenantId }, - select: { accountSID: true, authToken: true, phoneNumber: true }, + select: { + twilioConfig: { + select: { + accountSID: true, + authToken: true, + phoneNumber: true, + }, + }, + }, }); return { @@ -23,21 +31,21 @@ export const actions = { const form = await event.request.formData(); const tenantId = event.locals.tenant.id; - if (!form.has('accountSID')) { + if (!form.has('twilioAccountSID')) { return fail(400, { error: 'account_sid_missing' }); } - if (!form.has('authToken')) { + if (!form.has('twilioAuthToken')) { return fail(400, { error: 'auth_token_missing' }); } - if (!form.has('phoneNumber')) { + if (!form.has('twilioPhoneNumber')) { return fail(400, { error: 'phone_number_missing' }); } - const accountSID = form.get('accountSID'); + const accountSID = form.get('twilioAccountSID'); if (typeof accountSID !== 'string') { return fail(400, { error: 'invalid_account_sid' }); } - const authToken = form.get('authToken'); + const authToken = form.get('twilioAuthToken'); if (typeof authToken !== 'string') { return fail(400, { error: 'invalid_auth_token' }); } @@ -45,7 +53,7 @@ export const actions = { success: phoneSuccess, data: phoneNumber, error: phoneError, - } = zod.string().regex(PhoneRegex).safeParse(form.get('phoneNumber')); + } = zod.string().regex(PhoneRegex).safeParse(form.get('twilioPhoneNumber')); if (!phoneSuccess) { logger.error(phoneError); return fail(400, { error: 'invalid_phone_number' }); @@ -57,17 +65,25 @@ export const actions = { }, create: { tenantId: tenantId, - accountSID: encrypt(accountSID), - authToken: encrypt(authToken), - phoneNumber: encrypt(phoneNumber), + twilioConfig: { + create: { + accountSID: encrypt(accountSID), + authToken: encrypt(authToken), + phoneNumber: encrypt(phoneNumber), + }, + }, }, update: { tenantId: tenantId, - accountSID: accountSID, - authToken: authToken, - phoneNumber: phoneNumber, + twilioConfig: { + update: { + accountSID: accountSID, + authToken: authToken, + phoneNumber: phoneNumber, + }, + }, }, - select: { accountSID: true, authToken: true, phoneNumber: true }, + select: { twilioConfig: true }, }); return { diff --git a/src/routes/app/settings/+page.svelte b/src/routes/app/settings/+page.svelte index a0c35b8..360d245 100644 --- a/src/routes/app/settings/+page.svelte +++ b/src/routes/app/settings/+page.svelte @@ -31,10 +31,11 @@
+

{messages.settings_category_twilio()}

{ const configs = await prisma.tenantConfig.findUnique({ where: { tenantId: tenantId }, select: { - accountSID: true, - authToken: true, - phoneNumber: true, + twilioConfig: { + select: { + accountSID: true, + authToken: true, + phoneNumber: true, + }, + }, }, }); + const { success, error: validationError } = zod + .object({ + accountSID: zod.string(), + }) + .safeParse(configs?.twilioConfig); + + if (!success) { + logger.warn(validationError.message); + } + return { - configs: configs, + isTwilioConfigured: success, }; }; diff --git a/src/routes/app/sms/+page.svelte b/src/routes/app/sms/+page.svelte index eccbece..c8ea2d5 100644 --- a/src/routes/app/sms/+page.svelte +++ b/src/routes/app/sms/+page.svelte @@ -15,7 +15,6 @@ form: ActionData; }; let { data, form }: Props = $props(); - let isConfigMissing = $derived(!data.configs); {#snippet PhoneLabel()} @@ -35,7 +34,7 @@ {/snippet} {form.error} - {:else if isConfigMissing} + {:else if !data.isTwilioConfigured} {#snippet icon()} @@ -53,7 +52,7 @@