give twilio its own table
This commit is contained in:
parent
3d562e4009
commit
aeaafcefe1
5 changed files with 75 additions and 33 deletions
|
|
@ -34,6 +34,7 @@ model User {
|
||||||
model Tenant {
|
model Tenant {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
clerkOrganizationId String @unique
|
clerkOrganizationId String @unique
|
||||||
|
|
||||||
users User[]
|
users User[]
|
||||||
tenantConfig TenantConfig?
|
tenantConfig TenantConfig?
|
||||||
|
|
||||||
|
|
@ -49,6 +50,17 @@ model TenantConfig {
|
||||||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||||||
tenantId String @unique
|
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
|
accountSID String
|
||||||
authToken String
|
authToken String
|
||||||
phoneNumber String
|
phoneNumber String
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,15 @@ export const load = async (event) => {
|
||||||
|
|
||||||
const configs = await prisma.tenantConfig.findUnique({
|
const configs = await prisma.tenantConfig.findUnique({
|
||||||
where: { tenantId: tenantId },
|
where: { tenantId: tenantId },
|
||||||
select: { accountSID: true, authToken: true, phoneNumber: true },
|
select: {
|
||||||
|
twilioConfig: {
|
||||||
|
select: {
|
||||||
|
accountSID: true,
|
||||||
|
authToken: true,
|
||||||
|
phoneNumber: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -23,21 +31,21 @@ export const actions = {
|
||||||
const form = await event.request.formData();
|
const form = await event.request.formData();
|
||||||
const tenantId = event.locals.tenant.id;
|
const tenantId = event.locals.tenant.id;
|
||||||
|
|
||||||
if (!form.has('accountSID')) {
|
if (!form.has('twilioAccountSID')) {
|
||||||
return fail(400, { error: 'account_sid_missing' });
|
return fail(400, { error: 'account_sid_missing' });
|
||||||
}
|
}
|
||||||
if (!form.has('authToken')) {
|
if (!form.has('twilioAuthToken')) {
|
||||||
return fail(400, { error: 'auth_token_missing' });
|
return fail(400, { error: 'auth_token_missing' });
|
||||||
}
|
}
|
||||||
if (!form.has('phoneNumber')) {
|
if (!form.has('twilioPhoneNumber')) {
|
||||||
return fail(400, { error: 'phone_number_missing' });
|
return fail(400, { error: 'phone_number_missing' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountSID = form.get('accountSID');
|
const accountSID = form.get('twilioAccountSID');
|
||||||
if (typeof accountSID !== 'string') {
|
if (typeof accountSID !== 'string') {
|
||||||
return fail(400, { error: 'invalid_account_sid' });
|
return fail(400, { error: 'invalid_account_sid' });
|
||||||
}
|
}
|
||||||
const authToken = form.get('authToken');
|
const authToken = form.get('twilioAuthToken');
|
||||||
if (typeof authToken !== 'string') {
|
if (typeof authToken !== 'string') {
|
||||||
return fail(400, { error: 'invalid_auth_token' });
|
return fail(400, { error: 'invalid_auth_token' });
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +53,7 @@ export const actions = {
|
||||||
success: phoneSuccess,
|
success: phoneSuccess,
|
||||||
data: phoneNumber,
|
data: phoneNumber,
|
||||||
error: phoneError,
|
error: phoneError,
|
||||||
} = zod.string().regex(PhoneRegex).safeParse(form.get('phoneNumber'));
|
} = zod.string().regex(PhoneRegex).safeParse(form.get('twilioPhoneNumber'));
|
||||||
if (!phoneSuccess) {
|
if (!phoneSuccess) {
|
||||||
logger.error(phoneError);
|
logger.error(phoneError);
|
||||||
return fail(400, { error: 'invalid_phone_number' });
|
return fail(400, { error: 'invalid_phone_number' });
|
||||||
|
|
@ -57,17 +65,25 @@ export const actions = {
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
tenantId: tenantId,
|
tenantId: tenantId,
|
||||||
|
twilioConfig: {
|
||||||
|
create: {
|
||||||
accountSID: encrypt(accountSID),
|
accountSID: encrypt(accountSID),
|
||||||
authToken: encrypt(authToken),
|
authToken: encrypt(authToken),
|
||||||
phoneNumber: encrypt(phoneNumber),
|
phoneNumber: encrypt(phoneNumber),
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
update: {
|
update: {
|
||||||
tenantId: tenantId,
|
tenantId: tenantId,
|
||||||
|
twilioConfig: {
|
||||||
|
update: {
|
||||||
accountSID: accountSID,
|
accountSID: accountSID,
|
||||||
authToken: authToken,
|
authToken: authToken,
|
||||||
phoneNumber: phoneNumber,
|
phoneNumber: phoneNumber,
|
||||||
},
|
},
|
||||||
select: { accountSID: true, authToken: true, phoneNumber: true },
|
},
|
||||||
|
},
|
||||||
|
select: { twilioConfig: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,11 @@
|
||||||
<form id="sms" method="POST" action="?/update" use:enhance>
|
<form id="sms" method="POST" action="?/update" use:enhance>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<Divider />
|
<Divider />
|
||||||
|
<!-- Twilio -->
|
||||||
<h2 class="text-2xl font-semibold">{messages.settings_category_twilio()}</h2>
|
<h2 class="text-2xl font-semibold">{messages.settings_category_twilio()}</h2>
|
||||||
<TextInput
|
<TextInput
|
||||||
defaultvalue={configs?.accountSID}
|
defaultvalue={configs?.twilioConfig?.accountSID}
|
||||||
name="accountSID"
|
name="twilioAccountSID"
|
||||||
placeholder="..."
|
placeholder="..."
|
||||||
bordered
|
bordered
|
||||||
fade
|
fade
|
||||||
|
|
@ -47,8 +48,8 @@
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</TextInput>
|
</TextInput>
|
||||||
<TextInput
|
<TextInput
|
||||||
defaultvalue={configs?.authToken}
|
defaultvalue={configs?.twilioConfig?.authToken}
|
||||||
name="authToken"
|
name="twilioAuthToken"
|
||||||
placeholder="..."
|
placeholder="..."
|
||||||
type="password"
|
type="password"
|
||||||
bordered
|
bordered
|
||||||
|
|
@ -62,8 +63,8 @@
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</TextInput>
|
</TextInput>
|
||||||
<TextInput
|
<TextInput
|
||||||
defaultvalue={configs?.phoneNumber}
|
defaultvalue={configs?.twilioConfig?.phoneNumber}
|
||||||
name="phoneNumber"
|
name="twilioPhoneNumber"
|
||||||
placeholder="+1XXX-XXX-XXXX"
|
placeholder="+1XXX-XXX-XXXX"
|
||||||
bordered
|
bordered
|
||||||
fade
|
fade
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,29 @@ export const load = async (event) => {
|
||||||
|
|
||||||
const configs = await prisma.tenantConfig.findUnique({
|
const configs = await prisma.tenantConfig.findUnique({
|
||||||
where: { tenantId: tenantId },
|
where: { tenantId: tenantId },
|
||||||
|
select: {
|
||||||
|
twilioConfig: {
|
||||||
select: {
|
select: {
|
||||||
accountSID: true,
|
accountSID: true,
|
||||||
authToken: true,
|
authToken: true,
|
||||||
phoneNumber: true,
|
phoneNumber: true,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { success, error: validationError } = zod
|
||||||
|
.object({
|
||||||
|
accountSID: zod.string(),
|
||||||
|
})
|
||||||
|
.safeParse(configs?.twilioConfig);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
logger.warn(validationError.message);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
configs: configs,
|
isTwilioConfigured: success,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
form: ActionData;
|
form: ActionData;
|
||||||
};
|
};
|
||||||
let { data, form }: Props = $props();
|
let { data, form }: Props = $props();
|
||||||
let isConfigMissing = $derived(!data.configs);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet PhoneLabel()}
|
{#snippet PhoneLabel()}
|
||||||
|
|
@ -35,7 +34,7 @@
|
||||||
{/snippet}
|
{/snippet}
|
||||||
<span>{form.error}</span>
|
<span>{form.error}</span>
|
||||||
</Alert>
|
</Alert>
|
||||||
{:else if isConfigMissing}
|
{:else if !data.isTwilioConfigured}
|
||||||
<Alert status="warning">
|
<Alert status="warning">
|
||||||
{#snippet icon()}
|
{#snippet icon()}
|
||||||
<TriangleAlert />
|
<TriangleAlert />
|
||||||
|
|
@ -53,7 +52,7 @@
|
||||||
<form id="sms" method="POST" action="?/push" use:enhance>
|
<form id="sms" method="POST" action="?/push" use:enhance>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<TextInput
|
<TextInput
|
||||||
disabled={isConfigMissing}
|
disabled={!data.isTwilioConfigured}
|
||||||
type="tel"
|
type="tel"
|
||||||
name="phone"
|
name="phone"
|
||||||
label={PhoneLabel}
|
label={PhoneLabel}
|
||||||
|
|
@ -62,7 +61,7 @@
|
||||||
fade
|
fade
|
||||||
/>
|
/>
|
||||||
<Textarea
|
<Textarea
|
||||||
disabled={isConfigMissing}
|
disabled={!data.isTwilioConfigured}
|
||||||
label={MessageLabel}
|
label={MessageLabel}
|
||||||
size="lg"
|
size="lg"
|
||||||
error={form?.error}
|
error={form?.error}
|
||||||
|
|
@ -72,7 +71,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions justify-center px-8 pb-4">
|
<div class="card-actions justify-center px-8 pb-4">
|
||||||
<Button disabled={isConfigMissing} type="submit" variant="outline" full>
|
<Button disabled={!data.isTwilioConfigured} type="submit" variant="outline" full>
|
||||||
{messages.sms_button_submit()}
|
{messages.sms_button_submit()}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue