From 2144ae053d3c679b88a2addc2e6352e3a5f6e910 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Tue, 7 Jan 2025 22:56:14 -0500 Subject: [PATCH 01/20] add tenant config table --- prisma/schema.prisma | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4f15db1..e8b23a7 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -40,3 +40,10 @@ model Tenant { clerkOrganizationId String @unique users User[] } + +model TenantConfig { + tenantId String @id + accountSID String + authToken String + phoneNumber String +} -- 2.45.3 From bc2be3302a143ad3f6cb705819b5a816f3ac7eb9 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Wed, 8 Jan 2025 16:14:24 -0500 Subject: [PATCH 02/20] add encryption/decryption + env vars --- .env | 11 +++++-- scripts/validate-env.ts | 3 ++ src/lib/server/crypto/encryption.test.ts | 20 ++++++++++++ src/lib/server/crypto/encryption.ts | 39 ++++++++++++++++++++++++ src/lib/server/crypto/index.ts | 1 + 5 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 src/lib/server/crypto/encryption.test.ts create mode 100644 src/lib/server/crypto/encryption.ts create mode 100644 src/lib/server/crypto/index.ts diff --git a/.env b/.env index 1ed8778..e55fda6 100644 --- a/.env +++ b/.env @@ -1,9 +1,14 @@ NODE_ENV= # TWILIO -TWILIO_ACCOUNT_SID= -TWILIO_AUTH_TOKEN= -TWILIO_PHONE_NUMBER= +TWILIO_ACCOUNT_SID=secret_do_not_commit_or_change_this_create_.env.local_instead +TWILIO_AUTH_TOKEN=secret_do_not_commit_or_change_this_create_.env.local_instead +TWILIO_PHONE_NUMBER=secret_do_not_commit_or_change_this_create_.env.local_instead + +# SECRETS +SECRETS_PASSWORD=secret_do_not_commit_or_change_this_create_.env.local_instead +SECRETS_SALT=secret_do_not_commit_or_change_this_create_.env.local_instead +SECRETS_IV_POSITION=secret_do_not_commit_or_change_this_create_.env.local_instead # PRISMA DATABASE_URL="postgres://hestia:test123@localhost:5432/hestia" diff --git a/scripts/validate-env.ts b/scripts/validate-env.ts index da28326..888b6fd 100644 --- a/scripts/validate-env.ts +++ b/scripts/validate-env.ts @@ -7,6 +7,9 @@ const ValidateEnvironment = () => { TWILIO_ACCOUNT_SID: z.string().min(1), TWILIO_AUTH_TOKEN: z.string().min(1), TWILIO_PHONE_NUMBER: z.string().regex(PhoneRegex), + SECRETS_PASSWORD: z.string().length(32), + SECRETS_SALT: z.string().min(16), + SECRETS_IV_POSITION: z.number().positive(), }) .safeParse(process.env); diff --git a/src/lib/server/crypto/encryption.test.ts b/src/lib/server/crypto/encryption.test.ts new file mode 100644 index 0000000..3d421ae --- /dev/null +++ b/src/lib/server/crypto/encryption.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, it, vi } from 'vitest'; +import { decrypt, encrypt } from './encryption'; + +describe('Encryption', () => { + it('should encrypt and decrypt data', () => { + const data = 'aye its ya boi!'; + + vi.mock('$env/static/private', () => ({ + SECRETS_PASSWORD: 'aac7405eb3384e68c285fc252dbf68b2', + SECRETS_SALT: 'c4aeaf8bda72ea45e8c23269ca849013', + SECRETS_IV_POSITION: 9, + })); + + const encrypted = encrypt(data); + console.log(encrypted); + const decrypted = decrypt(encrypted); + + expect(decrypted).toEqual(data); + }); +}); diff --git a/src/lib/server/crypto/encryption.ts b/src/lib/server/crypto/encryption.ts new file mode 100644 index 0000000..3e077db --- /dev/null +++ b/src/lib/server/crypto/encryption.ts @@ -0,0 +1,39 @@ +import { SECRETS_PASSWORD, SECRETS_IV_POSITION, SECRETS_SALT } from '$env/static/private'; +import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto'; + +const algorithm = 'aes-256-gcm'; +const password = SECRETS_PASSWORD; +const salt = SECRETS_SALT; +const iv_position = Number(SECRETS_IV_POSITION); + +function construct(encrypted: string, tag: Buffer, iv: { value: Buffer; position: number }) { + return `${encrypted.slice(0, iv.position)}${iv.value.toString('hex')}${encrypted.slice(iv.position)}${tag.toString('hex')}`; +} + +function deconstruct(encrypted: string, position: number): [Buffer, string, Buffer] { + const iv = encrypted.slice(position, position + 16); + const text = `${encrypted.slice(0, position)}${encrypted.slice(position + 16, encrypted.length - 32)}`; + const authTag = encrypted.slice(encrypted.length - 32); + return [Buffer.from(iv, 'hex'), text, Buffer.from(authTag, 'hex')]; +} + +export function encrypt(value: string): string { + const key = scryptSync(password, salt, 32); + const iv = randomBytes(8); + + const cipher = createCipheriv(algorithm, key, iv); + const encrypted = cipher.update(value, 'utf-8', 'hex') + cipher.final('hex'); + const authTag = cipher.getAuthTag(); + + return construct(encrypted, authTag, { value: iv, position: iv_position }); +} + +export function decrypt(value: string): string { + const key = scryptSync(password, salt, 32); + const [iv, text, authTag] = deconstruct(value, iv_position); + + const decipher = createDecipheriv(algorithm, key, iv); + decipher.setAuthTag(authTag); + + return decipher.update(text, 'hex', 'utf-8') + decipher.final('utf-8'); +} diff --git a/src/lib/server/crypto/index.ts b/src/lib/server/crypto/index.ts new file mode 100644 index 0000000..73ebae8 --- /dev/null +++ b/src/lib/server/crypto/index.ts @@ -0,0 +1 @@ +export * from './encryption'; -- 2.45.3 From 3d73f9fa950fda63466b75ccce95670c8da9a466 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Wed, 8 Jan 2025 17:02:58 -0500 Subject: [PATCH 03/20] generate secret and validate iv position is number --- package.json | 3 ++- scripts/generate-secret.ts | 6 ++++++ scripts/validate-env.ts | 19 +++++++++++++++++-- src/routes/app/settings/+page.svelte | 4 ++++ 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 scripts/generate-secret.ts create mode 100644 src/routes/app/settings/+page.svelte diff --git a/package.json b/package.json index b7fe197..1602198 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "format": "prettier --write .", + "generate-secret": "bun ./scripts/generate-secret.ts", "lint": "prettier --check . && eslint .", "test:unit": "vitest", "test": "bun run test:unit -- --run && bun run test:e2e", @@ -44,7 +45,7 @@ "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/kit": "^2.15.3", "@sveltejs/vite-plugin-svelte": "^5.0.3", - "@types/bun": "^1.1.14", + "@types/bun": "^1.1.15", "autoprefixer": "^10.4.20", "daisyui": "^4.12.22", "eslint": "^9.7.0", diff --git a/scripts/generate-secret.ts b/scripts/generate-secret.ts new file mode 100644 index 0000000..2dce9de --- /dev/null +++ b/scripts/generate-secret.ts @@ -0,0 +1,6 @@ +import { randomBytes } from 'node:crypto'; + +console.log('SECRET: ', { + password: randomBytes(16).toString('hex'), + salt: randomBytes(16).toString('hex'), +}); diff --git a/scripts/validate-env.ts b/scripts/validate-env.ts index 888b6fd..cb121e8 100644 --- a/scripts/validate-env.ts +++ b/scripts/validate-env.ts @@ -1,5 +1,5 @@ -import { PhoneRegex } from '../src/lib/regex/phone'; import { z } from 'zod'; +import { PhoneRegex } from '../src/lib/regex/phone'; const ValidateEnvironment = () => { const { success, error } = z @@ -9,7 +9,22 @@ const ValidateEnvironment = () => { TWILIO_PHONE_NUMBER: z.string().regex(PhoneRegex), SECRETS_PASSWORD: z.string().length(32), SECRETS_SALT: z.string().min(16), - SECRETS_IV_POSITION: z.number().positive(), + SECRETS_IV_POSITION: z.string().transform((val, ctx) => { + const parsed = parseInt(val); + if (isNaN(parsed)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Not a number', + }); + + // This is a special symbol you can use to + // return early from the transform function. + // It has type `never` so it does not affect the + // inferred return type. + return z.NEVER; + } + return parsed; + }), }) .safeParse(process.env); diff --git a/src/routes/app/settings/+page.svelte b/src/routes/app/settings/+page.svelte new file mode 100644 index 0000000..b637efa --- /dev/null +++ b/src/routes/app/settings/+page.svelte @@ -0,0 +1,4 @@ + + +
-- 2.45.3 From 6817bbd3d41b99720e87c12a852c19949b457072 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Thu, 9 Jan 2025 00:17:16 -0500 Subject: [PATCH 04/20] expect errors --- src/lib/server/crypto/encryption.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/server/crypto/encryption.ts b/src/lib/server/crypto/encryption.ts index 3e077db..1872f59 100644 --- a/src/lib/server/crypto/encryption.ts +++ b/src/lib/server/crypto/encryption.ts @@ -21,6 +21,7 @@ export function encrypt(value: string): string { const key = scryptSync(password, salt, 32); const iv = randomBytes(8); + // @ts-expect-error Bun typing mismatch, but it still works! const cipher = createCipheriv(algorithm, key, iv); const encrypted = cipher.update(value, 'utf-8', 'hex') + cipher.final('hex'); const authTag = cipher.getAuthTag(); @@ -32,7 +33,9 @@ export function decrypt(value: string): string { const key = scryptSync(password, salt, 32); const [iv, text, authTag] = deconstruct(value, iv_position); + // @ts-expect-error Bun typing mismatch, but it still works! const decipher = createDecipheriv(algorithm, key, iv); + // @ts-expect-error Bun typing mismatch, but it still works! decipher.setAuthTag(authTag); return decipher.update(text, 'hex', 'utf-8') + decipher.final('utf-8'); -- 2.45.3 From 26eb93dbab1e9c15c68afaf391409a7e0a9608a5 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Thu, 9 Jan 2025 22:20:13 -0500 Subject: [PATCH 05/20] remove TWILIO env vars --- .env | 5 ----- scripts/validate-env.ts | 4 ---- 2 files changed, 9 deletions(-) diff --git a/.env b/.env index e55fda6..5a40385 100644 --- a/.env +++ b/.env @@ -1,10 +1,5 @@ NODE_ENV= -# TWILIO -TWILIO_ACCOUNT_SID=secret_do_not_commit_or_change_this_create_.env.local_instead -TWILIO_AUTH_TOKEN=secret_do_not_commit_or_change_this_create_.env.local_instead -TWILIO_PHONE_NUMBER=secret_do_not_commit_or_change_this_create_.env.local_instead - # SECRETS SECRETS_PASSWORD=secret_do_not_commit_or_change_this_create_.env.local_instead SECRETS_SALT=secret_do_not_commit_or_change_this_create_.env.local_instead diff --git a/scripts/validate-env.ts b/scripts/validate-env.ts index cb121e8..18a7b77 100644 --- a/scripts/validate-env.ts +++ b/scripts/validate-env.ts @@ -1,12 +1,8 @@ import { z } from 'zod'; -import { PhoneRegex } from '../src/lib/regex/phone'; const ValidateEnvironment = () => { const { success, error } = z .object({ - TWILIO_ACCOUNT_SID: z.string().min(1), - TWILIO_AUTH_TOKEN: z.string().min(1), - TWILIO_PHONE_NUMBER: z.string().regex(PhoneRegex), SECRETS_PASSWORD: z.string().length(32), SECRETS_SALT: z.string().min(16), SECRETS_IV_POSITION: z.string().transform((val, ctx) => { -- 2.45.3 From c57923c29ce6c67da1717fb089dd6e4420df7d40 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Fri, 10 Jan 2025 16:57:08 -0500 Subject: [PATCH 06/20] settings page impl --- messages/en.json | 8 ++- src/routes/app/settings/+page.server.ts | 77 ++++++++++++++++++++++ src/routes/app/settings/+page.svelte | 88 ++++++++++++++++++++++++- src/routes/app/sms/+page.server.ts | 24 ++++++- 4 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 src/routes/app/settings/+page.server.ts diff --git a/messages/en.json b/messages/en.json index cc9ea3b..6b0e882 100644 --- a/messages/en.json +++ b/messages/en.json @@ -21,5 +21,11 @@ "sms_label_phone": "Phone Number", "sms_label_message": "Message", "sms_button_submit": "Send Message", + "settings_title": "Settings", + "settings_category_twilio": "Twilio Config", + "settings_twilio_account_sid": "Account SID", + "settings_twilio_auth_token": "Auth Token", + "settings_twilio_phone_number": "Phone Number", + "settings_save": "Save Settings", "error_page_go_home": "Go Home" -} +} \ No newline at end of file diff --git a/src/routes/app/settings/+page.server.ts b/src/routes/app/settings/+page.server.ts new file mode 100644 index 0000000..2c60456 --- /dev/null +++ b/src/routes/app/settings/+page.server.ts @@ -0,0 +1,77 @@ +import { PhoneRegex } from '$lib/regex'; +import { encrypt } from '$lib/server/crypto/encryption.js'; +import { logger } from '$lib/server/logger'; +import { prisma } from '$lib/server/prisma'; +import { fail, type Actions } from '@sveltejs/kit'; +import zod from 'zod'; + +export const load = async (event) => { + const tenantId = event.locals.auth!.orgId!; + + const configs = await prisma.tenantConfig.findUnique({ + where: { tenantId: tenantId }, + select: { accountSID: true, authToken: true, phoneNumber: true }, + }); + + return { + configs: configs, + }; +}; + +export const actions = { + update: async (event) => { + const form = await event.request.formData(); + const tenantId = event.locals.auth!.orgId!; + + if (!form.has('accountSID')) { + return fail(400, { error: 'account_sid_missing' }); + } + if (!form.has('authToken')) { + return fail(400, { error: 'auth_token_missing' }); + } + if (!form.has('phoneNumber')) { + return fail(400, { error: 'phone_number_missing' }); + } + + const accountSID = form.get('accountSID'); + if (typeof accountSID !== 'string') { + return fail(400, { error: 'invalid_account_sid' }); + } + const authToken = form.get('authToken'); + if (typeof authToken !== 'string') { + return fail(400, { error: 'invalid_auth_token' }); + } + const { + success: phoneSuccess, + data: phoneNumber, + error: phoneError, + } = zod.string().regex(PhoneRegex).safeParse(form.get('phone')); + if (!phoneSuccess) { + logger.error(phoneError); + return fail(400, { error: 'invalid_phone_number' }); + } + + const configs = await prisma.tenantConfig.upsert({ + where: { + tenantId: tenantId, + }, + create: { + tenantId: tenantId, + accountSID: encrypt(accountSID), + authToken: encrypt(authToken), + phoneNumber: encrypt(phoneNumber), + }, + update: { + tenantId: tenantId, + accountSID: accountSID, + authToken: authToken, + phoneNumber: phoneNumber, + }, + select: { accountSID: true, authToken: true, phoneNumber: true }, + }); + + return { + configs: configs, + }; + }, +} satisfies Actions; diff --git a/src/routes/app/settings/+page.svelte b/src/routes/app/settings/+page.svelte index b637efa..e8759d6 100644 --- a/src/routes/app/settings/+page.svelte +++ b/src/routes/app/settings/+page.svelte @@ -1,4 +1,90 @@ -
+
+
+
+

{messages.settings_title()}

+ {#if form?.error} + + {/if} +
+
+
+ +

{messages.settings_category_twilio()}

+ + {#snippet label()} +
+ + {messages.settings_twilio_account_sid()} +
+ {/snippet} +
+ + {#snippet label()} +
+ + {messages.settings_twilio_auth_token()} +
+ {/snippet} +
+ + {#snippet label()} +
+ + {messages.settings_twilio_phone_number()} +
+ {/snippet} +
+
+
+ +
+
+
+
+ + diff --git a/src/routes/app/sms/+page.server.ts b/src/routes/app/sms/+page.server.ts index b447710..3dc5f38 100644 --- a/src/routes/app/sms/+page.server.ts +++ b/src/routes/app/sms/+page.server.ts @@ -1,10 +1,32 @@ import { TWILIO_PHONE_NUMBER } from '$env/static/private'; import { PhoneRegex } from '$lib/regex'; import { logger } from '$lib/server/logger'; +import { prisma } from '$lib/server/prisma/index.js'; import { TwilioClient } from '$lib/server/twilio'; -import { fail, type Actions } from '@sveltejs/kit'; +import { error, fail, type Actions } from '@sveltejs/kit'; import zod from 'zod'; +export const load = async (event) => { + const tenantId = event.locals.auth!.orgId!; + + const configs = await prisma.tenantConfig.findUnique({ + where: { tenantId: tenantId }, + select: { + accountSID: true, + authToken: true, + phoneNumber: true, + }, + }); + + if (!configs || Object.keys(configs).length === 0) { + return error(500, new Error('twilio_configs_not_set')); + } + + return { + configs: configs, + }; +}; + export const actions = { push: async (event) => { const form = await event.request.formData(); -- 2.45.3 From cb645e43a967d19ae1d334a2d5d1afdda2342bc3 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Wed, 15 Jan 2025 14:43:30 -0500 Subject: [PATCH 07/20] update schema definitions after Mostaphas Tenant impl --- prisma/schema.prisma | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e8b23a7..1c40c21 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -32,18 +32,27 @@ model User { } model Tenant { - id String @id @default(uuid()) - name String - slug String @unique - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - clerkOrganizationId String @unique + id String @id @default(uuid()) + clerkOrganizationId String @unique users User[] + tenantConfig TenantConfig? + + name String + slug String @unique + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model TenantConfig { - tenantId String @id + id String @id @default(uuid()) + tenant Tenant @relation(fields: [tenantId], references: [id]) + tenantId String @unique + accountSID String authToken String phoneNumber String -} + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} \ No newline at end of file -- 2.45.3 From bbe9184f5490c29a2e354359db058675a96cf1d3 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Wed, 15 Jan 2025 15:29:41 -0500 Subject: [PATCH 08/20] load user env --- src/app.d.ts | 4 ++ src/hooks.server.ts | 4 +- src/lib/server/middleware/index.ts | 1 + src/lib/server/middleware/server/index.ts | 1 + .../server/middleware/server/loadUserEnv.ts | 40 +++++++++++++++++++ src/routes/app/settings/+page.server.ts | 2 +- 6 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 src/lib/server/middleware/index.ts create mode 100644 src/lib/server/middleware/server/index.ts create mode 100644 src/lib/server/middleware/server/loadUserEnv.ts diff --git a/src/app.d.ts b/src/app.d.ts index 086138d..5aa6da4 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,5 +1,7 @@ // See https://svelte.dev/docs/kit/types#app.d.ts +import type { Tenant, User } from '@prisma/client'; + // for information about these interfaces declare global { namespace App { @@ -10,6 +12,8 @@ declare global { orgId?: string | null; sessionId?: string; }; + user: User; + tenant: Tenant; } // interface PageData {} // interface PageState {} diff --git a/src/hooks.server.ts b/src/hooks.server.ts index e182571..5d5908e 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,3 +1,5 @@ +import { loadUserEnv } from '$lib/server/middleware'; +import { sequence } from '@sveltejs/kit/hooks'; import { withClerkHandler } from 'clerk-sveltekit/server'; -export const handle = withClerkHandler(); +export const handle = sequence(withClerkHandler(), loadUserEnv()); diff --git a/src/lib/server/middleware/index.ts b/src/lib/server/middleware/index.ts new file mode 100644 index 0000000..0ce5251 --- /dev/null +++ b/src/lib/server/middleware/index.ts @@ -0,0 +1 @@ +export * from './server'; diff --git a/src/lib/server/middleware/server/index.ts b/src/lib/server/middleware/server/index.ts new file mode 100644 index 0000000..2bcd448 --- /dev/null +++ b/src/lib/server/middleware/server/index.ts @@ -0,0 +1 @@ +export * from './loadUserEnv'; diff --git a/src/lib/server/middleware/server/loadUserEnv.ts b/src/lib/server/middleware/server/loadUserEnv.ts new file mode 100644 index 0000000..78630a0 --- /dev/null +++ b/src/lib/server/middleware/server/loadUserEnv.ts @@ -0,0 +1,40 @@ +import { logger } from '$lib/server/logger'; +import { prisma } from '$lib/server/prisma'; +import type { Handle } from '@sveltejs/kit'; + +const unAuthedRoutes = ['/login']; + +export function loadUserEnv(): Handle { + return async ({ event, resolve }) => { + if (event.route.id === null || unAuthedRoutes.includes(event.route.id)) { + return resolve(event); + } + try { + const tenant = await prisma.tenant.findUniqueOrThrow({ + where: { + clerkOrganizationId: event.locals.auth.orgId ?? undefined, + }, + }); + const user = await prisma.user.findUniqueOrThrow({ + where: { + clerkId_tenantId: { clerkId: event.locals.auth.userId!, tenantId: tenant.id }, + }, + }); + + event.locals.tenant = tenant; + event.locals.user = user; + } catch (error) { + if (error instanceof Error) { + logger.error(error.message); + } + return new Response(null, { + status: 307, + headers: { + location: '/login', + }, + }); + } + + return resolve(event); + }; +} diff --git a/src/routes/app/settings/+page.server.ts b/src/routes/app/settings/+page.server.ts index 2c60456..6586588 100644 --- a/src/routes/app/settings/+page.server.ts +++ b/src/routes/app/settings/+page.server.ts @@ -21,7 +21,7 @@ export const load = async (event) => { export const actions = { update: async (event) => { const form = await event.request.formData(); - const tenantId = event.locals.auth!.orgId!; + const tenantId = event.locals.tenant.id; if (!form.has('accountSID')) { return fail(400, { error: 'account_sid_missing' }); -- 2.45.3 From a99c21f82609b99b2424aa941848aaa23e230d2a Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Fri, 17 Jan 2025 11:20:09 -0500 Subject: [PATCH 09/20] just return empty config --- src/routes/app/sms/+page.server.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/routes/app/sms/+page.server.ts b/src/routes/app/sms/+page.server.ts index 3dc5f38..45c0976 100644 --- a/src/routes/app/sms/+page.server.ts +++ b/src/routes/app/sms/+page.server.ts @@ -3,7 +3,7 @@ import { PhoneRegex } from '$lib/regex'; import { logger } from '$lib/server/logger'; import { prisma } from '$lib/server/prisma/index.js'; import { TwilioClient } from '$lib/server/twilio'; -import { error, fail, type Actions } from '@sveltejs/kit'; +import { fail, type Actions } from '@sveltejs/kit'; import zod from 'zod'; export const load = async (event) => { @@ -18,10 +18,6 @@ export const load = async (event) => { }, }); - if (!configs || Object.keys(configs).length === 0) { - return error(500, new Error('twilio_configs_not_set')); - } - return { configs: configs, }; -- 2.45.3 From adfb2aabc7b7f1a3af7cfacee3d754eef7fc93f7 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Fri, 17 Jan 2025 11:20:29 -0500 Subject: [PATCH 10/20] add Settings menu item --- src/routes/app/+layout.svelte | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/routes/app/+layout.svelte b/src/routes/app/+layout.svelte index 4ea34d0..a28013b 100644 --- a/src/routes/app/+layout.svelte +++ b/src/routes/app/+layout.svelte @@ -5,7 +5,7 @@ import { messages } from '$lib/i18n'; import 'clerk-sveltekit/client'; import SignOutButton from 'clerk-sveltekit/client/SignOutButton.svelte'; - import { LogOut, MessageCircleMore } from 'lucide-svelte'; + import { Cog, LogOut, MessageCircleMore } from 'lucide-svelte'; import type { Snippet } from 'svelte'; import { onMount } from 'svelte'; import type { PageData } from './$types'; @@ -68,11 +68,23 @@ class="menu dropdown-content menu-lg z-[1] mt-4 w-52 rounded-box bg-base-200 p-2 text-right shadow" >
  • - + +
  • +
  • + +
  • +
  • + + + {messages.nav_menu_logout()} +
  • -
  • {messages.nav_menu_logout()}
  • {/snippet} -- 2.45.3 From 102643dcc64d3378b9e72c98491acaa2b16b25c9 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Fri, 17 Jan 2025 11:33:30 -0500 Subject: [PATCH 11/20] check if settings are present and provide warning if not --- src/routes/app/sms/+page.svelte | 51 ++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/src/routes/app/sms/+page.svelte b/src/routes/app/sms/+page.svelte index 470c2c4..eccbece 100644 --- a/src/routes/app/sms/+page.svelte +++ b/src/routes/app/sms/+page.svelte @@ -1,43 +1,59 @@ {#snippet PhoneLabel()} - {messages.sms_label_phone()} + {messages.sms_label_phone()} {/snippet} {#snippet MessageLabel()} - {messages.sms_label_message()} -{/snippet} - -{#snippet alert(message: string)} - + {messages.sms_label_message()} {/snippet}
    + {#if form?.error} + + {#snippet icon()} + + {/snippet} + {form.error} + + {:else if isConfigMissing} + + {#snippet icon()} + + {/snippet} + + Twilio must be configured on the goto('/app/settings')} + >Settings page + + + {/if}

    {messages.sms_prompt()}

    - {#if form?.error} - {@render alert(form.error)} - {/if}