feat: Polar license key integration and dashboard UI improvements

- Add license activation flow: server validates/activates keys via Polar SDK
- Auto-activate from Polar checkout redirect (checkout_id in URL)
- Gate dashboard behind license activation (redirect to /activate if no plan)
- Preserve redirect URL through login flow for post-purchase activation
- Show plan status badge in sidebar and overview page
- Add account section to settings (email, plan, license key)
- Add svelte-sonner toasts with custom black theme and iconify icons
- Improve validation messages across all forms
- Update API key prefix to droidclaw_
- Add cursor pointer globally for clickable elements
- Support Polar sandbox mode via POLAR_SANDBOX env flag
This commit is contained in:
Sanju Sivalingam
2026-02-18 22:11:37 +05:30
parent 5aace17096
commit b34088ceb7
28 changed files with 1555 additions and 87 deletions

View File

@@ -0,0 +1,69 @@
import { form, query, getRequestEvent } from '$app/server';
import { redirect } from '@sveltejs/kit';
import { db } from '$lib/server/db';
import { user } from '$lib/server/db/schema';
import { eq } from 'drizzle-orm';
import { env } from '$env/dynamic/private';
import { activateLicenseSchema, activateCheckoutSchema } from '$lib/schema/license';
export const getLicenseStatus = query(async () => {
const { locals } = getRequestEvent();
if (!locals.user) return null;
const rows = await db
.select({ plan: user.plan, polarLicenseKey: user.polarLicenseKey })
.from(user)
.where(eq(user.id, locals.user.id))
.limit(1);
const row = rows[0];
return {
activated: !!row?.plan,
plan: row?.plan ?? null,
licenseKey: row?.polarLicenseKey ?? null
};
});
export const activateLicense = form(activateLicenseSchema, async (data) => {
const { locals } = getRequestEvent();
if (!locals.user) return;
const serverUrl = env.SERVER_URL || 'http://localhost:8080';
const internalSecret = env.INTERNAL_SECRET || '';
const res = await fetch(`${serverUrl}/license/activate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-internal-secret': internalSecret,
'x-internal-user-id': locals.user.id
},
body: JSON.stringify({ key: data.key })
});
if (res.ok) {
redirect(303, '/dashboard');
}
});
export const activateFromCheckout = form(activateCheckoutSchema, async (data) => {
const { locals } = getRequestEvent();
if (!locals.user) return;
const serverUrl = env.SERVER_URL || 'http://localhost:8080';
const internalSecret = env.INTERNAL_SECRET || '';
const res = await fetch(`${serverUrl}/license/activate-checkout`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-internal-secret': internalSecret,
'x-internal-user-id': locals.user.id
},
body: JSON.stringify({ checkoutId: data.checkoutId })
});
if (res.ok) {
redirect(303, '/dashboard');
}
});