fix: self-hosted useSend support + purchase-first activate UX

- Pass USESEND_BASE_URL to UseSend SDK for self-hosted instances
- Add debug logging for email sends
- Redesign activate page: prominent Purchase Now button, collapsible license key input
This commit is contained in:
Sanju Sivalingam
2026-02-18 23:08:10 +05:30
parent 9b2ca21d28
commit e9d1c863e1
3 changed files with 67 additions and 40 deletions

View File

@@ -8,6 +8,7 @@ import * as schema from './db/schema';
import { sendEmail } from './email';
export const auth = betterAuth({
baseURL: process.env.BETTER_AUTH_URL || 'http://localhost:5173',
database: drizzleAdapter(db, {
provider: 'pg',
schema
@@ -15,14 +16,16 @@ export const auth = betterAuth({
plugins: [sveltekitCookies(getRequestEvent), apiKey()],
emailVerification: {
sendVerificationEmail: async ({ user, url }) => {
console.log('[Email] sendVerificationEmail called for:', user.email, 'url:', url);
try {
await sendEmail({
const result = await sendEmail({
to: user.email,
subject: 'Verify your DroidClaw email',
text: `Hi ${user.name || 'there'},\n\nClick the link below to verify your email:\n\n${url}\n\nThis link expires in 1 hour.\n\n-- DroidClaw`
});
console.log('[Email] sendEmail result:', JSON.stringify(result));
} catch (err) {
console.error('Failed to send verification email:', err);
console.error('[Email] Failed to send verification email:', err);
}
},
sendOnSignUp: true,

View File

@@ -1,12 +1,13 @@
import { env } from '$env/dynamic/private';
import { UseSend } from 'usesend-js';
if (!env.USESEND_API_KEY) throw new Error('USESEND_API_KEY is not set');
const usesend = new UseSend(env.USESEND_API_KEY);
const EMAIL_FROM = 'noreply@app.droidclaw.ai';
function getClient() {
if (!env.USESEND_API_KEY) throw new Error('USESEND_API_KEY is not set');
return new UseSend(env.USESEND_API_KEY, env.USESEND_BASE_URL);
}
export async function sendEmail({
to,
subject,
@@ -16,7 +17,8 @@ export async function sendEmail({
subject: string;
text: string;
}) {
return usesend.emails.send({
console.log('[Email] API key prefix:', env.USESEND_API_KEY?.slice(0, 15) + '...');
return getClient().emails.send({
to,
from: EMAIL_FROM,
subject,

View File

@@ -5,6 +5,8 @@
import { LICENSE_ACTIVATE_CHECKOUT, LICENSE_ACTIVATE_MANUAL, LICENSE_PURCHASE_CLICK } from '$lib/analytics/events';
const checkoutId = page.url.searchParams.get('checkout_id');
let showKeyInput = $state(false);
</script>
{#if checkoutId}
@@ -69,18 +71,42 @@
</form>
</div>
{:else}
<!-- Manual activation (no checkout ID) -->
<!-- Purchase-first flow -->
<div class="mx-auto max-w-md pt-20">
<div class="mb-8 text-center">
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-xl bg-neutral-100">
<Icon icon="ph:seal-check-duotone" class="h-6 w-6 text-neutral-600" />
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-xl bg-neutral-900">
<Icon icon="ph:robot-duotone" class="h-6 w-6 text-white" />
</div>
<h2 class="text-2xl font-bold">Activate your license</h2>
<p class="mt-1 text-neutral-500">
Enter the license key you received after purchasing DroidClaw.
<h2 class="text-2xl font-bold">Get started with DroidClaw</h2>
<p class="mt-2 text-neutral-500">
Unlock AI-powered Android device control.
</p>
</div>
<a
href="https://sandbox-api.polar.sh/v1/checkout-links/polar_cl_5pGavRIJJhM8ge6p0UaeaadT2bCiqL04CYXgW3bwVac/redirect"
data-umami-event={LICENSE_PURCHASE_CLICK}
class="flex w-full items-center justify-center gap-2 rounded-xl bg-neutral-900 px-4 py-3 text-base font-medium text-white hover:bg-neutral-800"
>
<Icon icon="ph:credit-card-duotone" class="h-5 w-5" />
Purchase Now
</a>
<div class="mt-10">
<button
type="button"
onclick={() => showKeyInput = !showKeyInput}
class="flex w-full items-center justify-center gap-1.5 text-sm text-neutral-400 hover:text-neutral-600"
>
Already have a license key?
<Icon
icon="ph:caret-down"
class="h-3.5 w-3.5 transition-transform {showKeyInput ? 'rotate-180' : ''}"
/>
</button>
{#if showKeyInput}
<div class="mt-4">
<form {...activateLicense} class="space-y-4">
<label class="block">
<span class="flex items-center gap-1.5 text-sm font-medium text-neutral-700">
@@ -100,18 +126,14 @@
<button
type="submit"
data-umami-event={LICENSE_ACTIVATE_MANUAL}
class="flex w-full items-center justify-center gap-2 rounded-xl bg-neutral-900 px-4 py-2.5 font-medium text-white hover:bg-neutral-800"
class="flex w-full items-center justify-center gap-2 rounded-xl border border-neutral-300 px-4 py-2.5 text-sm font-medium text-neutral-700 hover:bg-neutral-50"
>
<Icon icon="ph:seal-check-duotone" class="h-4 w-4" />
Activate
</button>
</form>
<p class="mt-6 text-center text-sm text-neutral-400">
Don't have a key?
<a href="https://sandbox-api.polar.sh/v1/checkout-links/polar_cl_5pGavRIJJhM8ge6p0UaeaadT2bCiqL04CYXgW3bwVac/redirect" data-umami-event={LICENSE_PURCHASE_CLICK} class="font-medium text-neutral-700 underline hover:text-neutral-900">
Purchase here
</a>
</p>
</div>
{/if}
</div>
</div>
{/if}