feat: add @polar-sh/sveltekit (#539)

* feat add @polar-sh/sveltekit

* remove all lemonsqueezy text
This commit is contained in:
Rotimi Best
2025-01-09 01:30:01 +01:00
committed by GitHub
parent 4d4b1f12e2
commit 13fb58176f
46 changed files with 12475 additions and 10197 deletions
+97 -19
View File
@@ -1,33 +1,111 @@
<script>
import ErrorIcon from '$lib/components/Icons/ErrorIcon.svelte';
</script>
<div
class="error-container m-auto flex flex-col-reverse lg:flex-row md:flex-col-reverse items-center justify-center h-screen w-screen py-10 dark:bg-black"
>
<div class="md:w-full p-2 lg:w-2/4">
<h2 class="w-full text-lg lg:text-3xl font-medium lg:font-normal">
Something unexpected occured.
</h2>
<p class="dark:text-white w-full text-base font-normal my-6 text-gray-600">
<div class="error-container">
<div class="error-content">
<img src="/logo-512.png" class="logo" alt="ClassroomIO - Logo" />
<h2>Something unexpected occured.</h2>
<p>
Don't worry, your learning is safe. It isn't your fault, it is ours. We have gotten the error
notification and will push a fix ASAP. In the meantime, take a short break and come back a bit
later.
</p>
<a
class="bg-primary-700 hover:bg-primary-900 hover:no-underline transition-all py-3 px-9 rounded-xl text-white mt-3"
href="mailto:support@classroomio.com"
>Contact Us</a
>
<a href="mailto:support@classroomio.com">Contact Us</a>
</div>
<div class="w-72 lg:w-2/4 flex items-center justify-center">
<ErrorIcon />
<div class="error-icon">
<img src="/500-error.svg" alt="Error" />
</div>
</div>
<style>
body {
margin: 0;
}
.error-container {
max-width: 964px;
margin: auto;
display: flex;
flex-direction: column-reverse;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
padding: 0;
}
:global(.dark) .error-container {
background-color: black;
}
.error-content {
padding: 0.5rem;
width: 100%;
}
.error-content h2 {
width: 100%;
font-size: 1.125rem;
font-weight: 500;
}
.error-content p {
width: 100%;
font-size: 1rem;
font-weight: 400;
margin: 1.5rem 0;
color: rgb(75, 85, 99);
}
:global(.dark) .error-content p {
color: white;
}
.error-content .logo {
width: 5rem;
}
.error-content a {
background-color: rgb(29, 78, 216);
padding: 0.75rem 2.25rem;
border-radius: 0.75rem;
color: white;
margin-top: 0.75rem;
transition: all 0.2s;
display: inline-block;
}
.error-content a:hover {
background-color: rgb(30, 58, 138);
text-decoration: none;
}
.error-icon {
width: 18rem;
display: flex;
align-items: center;
justify-content: center;
}
@media (min-width: 768px) {
.error-content {
width: 90%;
}
}
@media (min-width: 1024px) {
.error-container {
flex-direction: row;
}
.error-content {
width: 50%;
}
.error-content h2 {
font-size: 1.875rem;
font-weight: 400;
}
.error-icon {
width: 50%;
}
}
</style>
+35
View File
@@ -0,0 +1,35 @@
import { validateUser } from '$lib/utils/services/middlewares';
import type { Handle } from '@sveltejs/kit';
const PUBLIC_ROUTES = [
'/api/completion',
'student_prove_payment',
'teacher_student_buycourse',
'/api/polar'
];
function isPublicRoute(pathname: string) {
return PUBLIC_ROUTES.some((route) => pathname.includes(route));
}
export const handle: Handle = async ({ event, resolve }) => {
const response = await resolve(event);
const { pathname } = event.url;
// Only validate API routes
if (!pathname.includes('/api')) {
return response;
}
// Skip public routes
if (isPublicRoute(pathname)) {
return response;
}
const accessToken = event.request.headers.get('Authorization')!;
await validateUser(accessToken);
return response;
};
@@ -116,7 +116,7 @@
<div class="relative mb-5">
{#if !isLMS && !isOnLandingPage}
<OverflowMenu
class="absolute right-2 top-2 z-50 rounded-full bg-gray-200 opacity-0 transition-all delay-150 duration-200 ease-in-out group-hover:opacity-100 dark:bg-neutral-800"
class="absolute right-2 top-2 z-40 rounded-full bg-gray-200 opacity-0 transition-all delay-150 duration-200 ease-in-out group-hover:opacity-100 dark:bg-neutral-800"
size="sm"
on:click={(e) => e.stopPropagation()}
>
@@ -1,32 +1,27 @@
<script>
import { Grid, Row, Column } from 'carbon-components-svelte';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
import { currentOrgPlan, isFreePlan } from '$lib/utils/store/org';
import SectionTitle from '../SectionTitle.svelte';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { t } from '$lib/utils/functions/translations';
let isRedirecting = false;
import { currentOrgPlan, isFreePlan } from '$lib/utils/store/org';
import { Column, Grid, Row } from 'carbon-components-svelte';
import SectionTitle from '../SectionTitle.svelte';
async function onOpenBilling() {
console.log({
plan: $currentOrgPlan
});
if ($currentOrgPlan?.subscriptionId) {
isRedirecting = true;
fetch('/api/lmz/subscriptions?subscriptionId=' + $currentOrgPlan?.subscriptionId)
.then((response) => response.json())
.then((res) => {
isRedirecting = false;
window.open(res.customerPortal, '_blank');
});
if ($currentOrgPlan?.customerId) {
const url = new URL('/api/polar/portal', window.location.origin);
url.searchParams.set('customerId', $currentOrgPlan?.customerId);
window.open(url.toString(), '_blank');
}
}
</script>
<Grid class="border-c rounded border-gray-200 dark:border-neutral-600 w-full mt-5">
<Row class="flex lg:flex-row flex-col py-7 border-bottom-c">
<Grid class="border-c mt-5 w-full rounded border-gray-200 dark:border-neutral-600">
<Row class="border-bottom-c flex flex-col py-7 lg:flex-row">
<Column sm={4} md={4} lg={4}
><SectionTitle>{$t('settings.billing.sub_title')}</SectionTitle></Column
>
@@ -35,13 +30,13 @@
{#if $isFreePlan}
{$t('settings.billing.active')}
{:else}
<p class="mb-3">{$t('settings.billing.lemonsqueezy')}</p>
<p class="mb-3">{$t('settings.billing.provider')}</p>
<PrimaryButton
label={$t('settings.billing.open_billing')}
variant={VARIANTS.CONTAINED_DARK}
onClick={onOpenBilling}
isLoading={isRedirecting}
isDisabled={!$currentOrgPlan?.customerId}
/>
{/if}
</Column>
@@ -1,15 +1,12 @@
import { supabase } from '$lib/utils/functions/supabase';
import { getAccessToken } from '$lib/utils/functions/supabase';
export async function queryUnsplash(searchQuery: string) {
const { data } = await supabase.auth.getSession();
const accessToken = data.session?.access_token || '';
try {
const response = await fetch('/api/unsplash', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: accessToken
Authorization: await getAccessToken()
},
body: JSON.stringify({ searchQuery })
});
@@ -63,17 +63,14 @@ loading.subscribe(async ($loading) => {
}
});
export async function handleLocaleChange(newLocale) {
export function handleLocaleChange(newLocale: string) {
if (!newLocale) {
return;
}
locale.set(newLocale);
await fetch('/api/i18n', {
body: JSON.stringify({ locale: newLocale }),
method: 'POST'
});
selectedLocale.set(newLocale);
}
export function lessonFallbackNote(
@@ -1,11 +0,0 @@
import { env } from '$env/dynamic/private';
import { lemonSqueezySetup } from '@lemonsqueezy/lemonsqueezy.js';
export function configureLemonSqueezy() {
return lemonSqueezySetup({
apiKey: env.LEMON_SQUEEZY_API_KEY,
onError: (error) => {
throw new Error(`Lemon Squeezy API error: ${error.message}`);
}
});
}
@@ -1,24 +0,0 @@
import axios from 'axios';
interface SubscriptionData {
productId: string;
email: string;
name: string;
triggeredBy: string;
orgId: string;
}
interface SubscriptionResponse {
success: boolean;
checkoutURL: string;
}
export async function subscribeToProduct(data: SubscriptionData): Promise<SubscriptionResponse> {
const response = await axios({
method: 'POST',
url: '/api/lmz/subscribe',
data
});
return response.data;
}
@@ -0,0 +1,20 @@
import { getServerSupabase } from '$lib/utils/functions/supabase.server';
const supabase = getServerSupabase();
export const validateUser = async (accessToken: string) => {
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
throw new Error('Unauthenticated user');
}
return user;
};
@@ -0,0 +1 @@
export * from './authentication.server';
@@ -1,4 +0,0 @@
1. Welcome new user into classroomio
2. Sends teacher welcome to course
3. Notify teacher someone wants to buy course
4
@@ -77,7 +77,7 @@ export async function getOrganizations(userId: string, isOrgSite?: boolean, orgS
organization_plan(
plan_name,
is_active,
subscriptionId:payload->id
customerId:payload->customerId
)
)
`
@@ -1147,8 +1147,8 @@
"sub_title": "Rechnungsdetails",
"manage": "Verwalten Sie Ihre Abrechnung",
"active": "Sie haben keine aktive Abrechnung",
"lemonsqueezy": "Wir verwenden Lemonsqueezy, um Sie bei der Verwaltung Ihrer Abrechnung zu unterstützen",
"open_billing": "Abrechnung öffnen"
"open_billing": "Abrechnung öffnen",
"provider": "Wir verwenden Polar, um Ihre Abrechnung zu verwalten"
},
"integrations": {
"heading": "Telegramm",
@@ -242,7 +242,7 @@
"sub_title": "Billing details",
"manage": "Manage your billing",
"active": "You don't have any active billing",
"lemonsqueezy": "We use Lemonsqueezy to help manage your billing",
"provider": "We use Polar to help manage your billing",
"open_billing": "Open billing"
}
},
@@ -1147,8 +1147,8 @@
"sub_title": "Detalles de facturación",
"manage": "Gestiona tu facturación",
"active": "No tienes ninguna facturación activa",
"lemonsqueezy": "Usamos Lemonsqueezy para ayudarlo a administrar su facturación",
"open_billing": "Facturación abierta"
"open_billing": "Facturación abierta",
"provider": "Usamos Polar para ayudarte a gestionar tu facturación"
},
"integrations": {
"heading": "Telegrama",
@@ -1147,8 +1147,8 @@
"sub_title": "Détails de la facturation",
"manage": "Gérez votre facturation",
"active": "Vous n'avez aucune facturation active",
"lemonsqueezy": "Nous utilisons Lemonsqueezy pour vous aider à gérer votre facturation",
"open_billing": "Facturation ouverte"
"open_billing": "Facturation ouverte",
"provider": "Nous utilisons Polar pour vous aider à gérer votre facturation"
},
"integrations": {
"heading": "Télégramme",
@@ -1147,8 +1147,8 @@
"sub_title": "बिलिंग विवरण",
"manage": "अपनी बिलिंग प्रबंधित करें",
"active": "आपके पास कोई सक्रिय बिलिंग नहीं है",
"lemonsqueezy": "हम आपकी बिलिंग प्रबंधित करने में सहायता के लिए लेमनस्क्वीज़ी का उपयोग करते हैं",
"open_billing": "बिलिंग खोलें"
"open_billing": "बिलिंग खोलें",
"provider": "हम आपकी बिलिंग प्रबंधित करने में सहायता के लिए पोलर का उपयोग करते हैं"
},
"integrations": {
"heading": "तार",
@@ -1147,8 +1147,8 @@
"sub_title": "Detalhes de cobrança",
"manage": "Gerencie seu faturamento",
"active": "Você não tem nenhum faturamento ativo",
"lemonsqueezy": "Usamos Lemonsqueezy para ajudar a gerenciar seu faturamento",
"open_billing": "Faturamento aberto"
"open_billing": "Faturamento aberto",
"provider": "Usamos a Polar para ajudar a gerenciar seu faturamento"
},
"integrations": {
"heading": "Telegrama",
@@ -1147,8 +1147,8 @@
"sub_title": "Платежные реквизиты",
"manage": "Управляйте своим биллингом",
"active": "У вас нет активных платежных данных.",
"lemonsqueezy": "Мы используем Lemonsqueezy для управления вашими счетами.",
"open_billing": "Открыть биллинг"
"open_billing": "Открыть биллинг",
"provider": "Мы используем Polar, чтобы управлять вашими платежами."
},
"integrations": {
"heading": "Телеграмма",
@@ -1147,8 +1147,8 @@
"sub_title": "Chi tiết thanh toán",
"manage": "Quản lý thanh toán của bạn",
"active": "Bạn không có bất kỳ thanh toán nào đang hoạt động",
"lemonsqueezy": "Chúng tôi sử dụng Lemonsqueezy để giúp quản lý hóa đơn của bạn",
"open_billing": "Thanh toán mở"
"open_billing": "Thanh toán mở",
"provider": "Chúng tôi sử dụng Polar để giúp quản lý việc thanh toán của bạn"
},
"integrations": {
"heading": "điện tín",
+1 -1
View File
@@ -29,7 +29,7 @@ export interface CurrentOrg {
customization: OrgCustomization;
theme: string;
organization_plan: {
subscriptionId: string;
customerId: string;
plan_name: string;
is_active: boolean;
}[];
@@ -9,24 +9,10 @@ const CACHE_DURATION = 60 * 5; // 5 minutes
export async function POST({ setHeaders, request }) {
const { orgId } = await request.json();
const accessToken = request.headers.get('Authorization');
if (!orgId || !accessToken) {
if (!orgId) {
return json({ success: false, message: 'Request is missing required fields' }, { status: 400 });
}
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
setHeaders({
'cache-control': `max-age=${CACHE_DURATION}`,
'content-type': 'application/json'
@@ -15,24 +15,10 @@ const CACHE_DURATION = 60 * 5; // 5 minutes
export async function POST({ setHeaders, request }) {
const { userId, courseId, orgId } = await request.json();
const accessToken = request.headers.get('Authorization');
if (!userId || !accessToken) {
if (!userId) {
return json({ success: false, message: 'Request is missing required fields' }, { status: 400 });
}
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
setHeaders({
'cache-control': `max-age=${CACHE_DURATION}`,
'content-type': 'application/json'
@@ -1,19 +1,15 @@
import { json } from '@sveltejs/kit';
import { getSupabase } from '$lib/utils/functions/supabase';
import {
getConfigResponse,
addDomainToVercel,
getConfigResponse,
removeDomainFromVercelProject
} from '$lib/utils/services/org/domain';
const supabase = getSupabase();
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const { params } = await request.json();
const accessToken = request.headers.get('Authorization');
console.log('/GET api/domain', params);
if (!params?.key || !accessToken) {
if (!params?.key) {
return json({ success: false, message: 'Missing fields' }, { status: 400 });
}
@@ -21,18 +17,6 @@ export async function POST({ request }) {
return json({ success: false, message: 'Domain cannot contain classroomio' }, { status: 400 });
}
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
try {
switch (params.key) {
case 'verify_domain': {
@@ -1,29 +1,13 @@
import { json } from '@sveltejs/kit';
import { getSupabase } from '$lib/utils/functions/supabase';
import sendEmail from '$mail/sendEmail';
const supabase = getSupabase();
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const { to, content, orgName, exerciseTitle } = await request.json();
const accessToken = request.headers.get('Authorization') || '';
if (!to || !content || !orgName) {
return json({ success: false, message: 'Missing required fields' }, { status: 400 });
}
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
const emailData = [
{
from: `"${orgName} (via ClassroomIO.com)" <notify@mail.classroomio.com>`,
@@ -1,7 +1,7 @@
import { json } from '@sveltejs/kit';
import { getServerSupabase } from '$lib/utils/functions/supabase.server';
import { getFeedForNotification } from '$lib/utils/services/newsfeed/index';
import sendEmail from '$mail/sendEmail';
import { json } from '@sveltejs/kit';
const supabase = getServerSupabase();
@@ -67,25 +67,12 @@ const sendEmailNotification = async (feedId: string, authorId: string, comment?:
export async function POST({ request }) {
const { authorId, feedId, comment } = await request.json();
const accessToken = request.headers.get('Authorization') || '';
console.log('/POST api/email/course/newsfeed');
if (!authorId || !feedId || !accessToken) {
if (!authorId || !feedId) {
return json({ success: false, message: 'Missing required fields' }, { status: 400 });
}
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
await sendEmailNotification(feedId, authorId, comment);
// TODO: Support sending to other platforms like telegram and discord
// sendDiscordNotification(); sendTelegramNotification();
@@ -1,31 +1,15 @@
import { json } from '@sveltejs/kit';
import { getSupabase } from '$lib/utils/functions/supabase';
import sendEmail from '$mail/sendEmail';
const supabase = getSupabase();
import { json } from '@sveltejs/kit';
// Sends email to student when they successfully join a course
export async function POST({ request }) {
const { to, orgName, courseName } = await request.json();
const accessToken = request.headers.get('Authorization') || '';
console.log('/POST api/email/course/student_welcome', to, orgName);
if (!to || !orgName || !courseName) {
return json({ success: false, message: 'Missing required fields' }, { status: 400 });
}
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
const emailData = [
{
from: `"${orgName} (via ClassroomIO.com)" <notify@mail.classroomio.com>`,
@@ -1,29 +1,13 @@
import { json } from '@sveltejs/kit';
import { getSupabase } from '$lib/utils/functions/supabase';
import sendEmail from '$mail/sendEmail';
const supabase = getSupabase();
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const { to, content, orgName } = await request.json();
const accessToken = request.headers.get('Authorization') || '';
if (!to || !content || !orgName) {
return json({ success: false, message: 'Missing required fields' }, { status: 400 });
}
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
const emailData = [
{
from: `"${orgName} (via ClassroomIO.com)" <notify@mail.classroomio.com>`,
@@ -1,12 +1,8 @@
import { json } from '@sveltejs/kit';
// import { getSupabase } from '$lib/utils/functions/supabase';
import sendEmail from '$mail/sendEmail';
// const supabase = getSupabase();
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const { to, courseName, studentEmail, studentFullname } = await request.json();
// const accessToken = request.headers.get('Authorization') || '';
console.log(
'/POST api/email/course/teacher_student_buycourse',
to,
@@ -19,18 +15,6 @@ export async function POST({ request }) {
return json({ success: false, message: 'Missing required fields' }, { status: 400 });
}
// let user;
// try {
// const { data } = await supabase.auth.getUser(accessToken);
// user = data.user;
// } catch (error) {
// console.error(error);
// }
// if (!user) {
// return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
// }
const emailData = [
{
from: `"ClassroomIO" <notify@mail.classroomio.com>`,
@@ -1,30 +1,14 @@
import { json } from '@sveltejs/kit';
import { getSupabase } from '$lib/utils/functions/supabase';
import sendEmail from '$mail/sendEmail';
const supabase = getSupabase();
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const { to, courseName, studentName, studentEmail } = await request.json();
const accessToken = request.headers.get('Authorization') || '';
console.log('/POST api/email/course/teacher_student_joined', to, courseName);
if (!to || !courseName || !studentName || !studentEmail) {
return json({ success: false, message: 'Missing required fields' }, { status: 400 });
}
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
const emailData = [
{
from: `"ClassroomIO" <notify@mail.classroomio.com>`,
@@ -1,30 +1,14 @@
import { json } from '@sveltejs/kit';
import { getSupabase } from '$lib/utils/functions/supabase';
import sendEmail from '$mail/sendEmail';
const supabase = getSupabase();
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const { to, name, orgName, courseName, orgSiteName } = await request.json();
const accessToken = request.headers.get('Authorization') || '';
console.log('/POST api/email/course/teacher_welcome', to, name, orgName);
if (!to || !name || !orgName || !courseName || !orgSiteName) {
return json({ success: false, message: 'Missing required fields' }, { status: 400 });
}
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
const origin = request.headers.get('origin');
const inviteLink = `${origin}/org/${orgSiteName}/courses`;
@@ -1,36 +1,20 @@
import { json } from '@sveltejs/kit';
import { getSupabase } from '$lib/utils/functions/supabase';
import sendEmail from '$mail/sendEmail';
const supabase = getSupabase();
import { json } from '@sveltejs/kit';
// API to send invite to teacher
export async function POST({ request }) {
const body = await request.json();
const { org, email } = body;
const accessToken = request.headers.get('Authorization');
console.log('/POST api/email/invite', body);
if (!org || !Object.keys(org).length || !email || !accessToken) {
if (!org || !Object.keys(org).length || !email) {
return json(
{ success: false, message: 'Org data and Teacher name are required' },
{ status: 400 }
);
}
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
const { id, name, siteName } = org;
const origin = request.headers.get('origin');
@@ -1,30 +1,14 @@
import { json } from '@sveltejs/kit';
import { getSupabase } from '$lib/utils/functions/supabase';
import sendEmail from '$mail/sendEmail';
const supabase = getSupabase();
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const { to, profileId, orgSiteName, fullname } = await request.json();
const accessToken = request.headers.get('Authorization');
console.log('/POST api/email/verify_email', to, profileId);
if (!to || !profileId || !accessToken) {
if (!to || !profileId) {
return json({ success: false, message: 'Name and To are required fields' }, { status: 400 });
}
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
const origin = request.headers.get('origin');
const verificationData = JSON.stringify({
profileId,
@@ -1,30 +1,14 @@
import { json } from '@sveltejs/kit';
import { getSupabase } from '$lib/utils/functions/supabase';
import sendEmail from '$mail/sendEmail';
const supabase = getSupabase();
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const { to, name } = await request.json();
const accessToken = request.headers.get('Authorization');
console.log('/POST api/email/welcome', to, name);
if (!to || !name || !accessToken) {
if (!to || !name) {
return json({ success: false, message: 'Name and To are required fields' }, { status: 400 });
}
let user;
try {
const { data } = await supabase.auth.getUser(accessToken);
user = data.user;
} catch (error) {
console.error(error);
}
if (!user) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
const emailData = [
{
from: `"Best from ClassroomIO" <notify@mail.classroomio.com>`,
@@ -1,13 +0,0 @@
import { selectedLocale } from '$lib/utils/functions/translations';
import { json, type RequestHandler } from '@sveltejs/kit';
export const POST = (async ({ request }) => {
try {
const body = await request.json();
selectedLocale.set(body.locale);
} catch (e) {
selectedLocale.set('en');
}
return json({ status: 200 });
}) satisfies RequestHandler;
@@ -1,38 +0,0 @@
import { json } from '@sveltejs/kit';
import { createCheckout } from '@lemonsqueezy/lemonsqueezy.js';
import { env } from '$env/dynamic/private';
import { configureLemonSqueezy } from '$lib/utils/services/lemonsqueezy';
export async function POST({ request }) {
const { email, name, orgId, productId, triggeredBy } = await request.json();
await configureLemonSqueezy();
if (!productId) {
return json({ success: false, message: 'Product Id is required' }, { status: 400 });
}
const checkoutData = {
email: email,
name: name,
custom: {
org_id: orgId,
triggered_by: `${triggeredBy}`
}
};
console.log({ checkoutData });
const { data } = await createCheckout(env.LEMON_SQUEEZY_STORE_ID, productId, {
checkoutData
});
const checkoutURL = data?.data.attributes.url;
console.log({ checkoutURL });
return json({
success: true,
checkoutURL
});
}
@@ -1,23 +0,0 @@
import { json } from '@sveltejs/kit';
import { getSubscription } from '@lemonsqueezy/lemonsqueezy.js';
import { configureLemonSqueezy } from '$lib/utils/services/lemonsqueezy';
export async function GET({ url }) {
const subscriptionId = url.searchParams.get('subscriptionId');
await configureLemonSqueezy();
if (!subscriptionId) {
return json({ success: false, message: 'Subscription id is required' }, { status: 400 });
}
const response = await getSubscription(subscriptionId);
const customerPortal = response?.data?.data?.attributes?.urls?.customer_portal;
console.log({ customerPortal });
return json({
success: true,
customerPortal
});
}
@@ -1,83 +0,0 @@
import { env } from '$env/dynamic/private';
import { getServerSupabase } from '$lib/utils/functions/supabase.server';
import { cancelOrgPlan, createOrgPlan } from '$lib/utils/services/org';
import { json } from '@sveltejs/kit';
import crypto from 'crypto';
import { PLAN } from 'shared/src/plans/constants';
export async function POST({ request }) {
try {
const supabase = getServerSupabase();
// Catch the event type
const clonedReq = request.clone();
const eventType = request.headers.get('X-Event-Name');
const body = await request.json();
// Check signature
const secret = env.LEMON_SQUEEZY_WEBHOOK_SECRET;
const hmac = crypto.createHmac('sha256', secret);
const digest = Buffer.from(hmac.update(await clonedReq.text()).digest('hex'), 'utf8');
const signature = Buffer.from(request.headers.get('X-Signature') || '', 'utf8');
if (!crypto.timingSafeEqual(digest, signature)) {
throw new Error('Invalid signature.');
}
console.log(body);
// Logic according to event
if (eventType === 'subscription_created' || eventType === 'subscription_resumed') {
const orgId = body.meta.custom_data.org_id;
const triggeredBy = body.meta.custom_data.triggered_by;
const subscriptionId = body.data.id;
const isSuccessful = body.data.attributes.status === 'active';
console.log({
subscriptionId,
triggeredBy,
isSuccessful
});
if (isSuccessful && orgId) {
const { data, error } = await createOrgPlan({
supabase,
subscriptionId,
orgId,
triggeredBy: parseInt(triggeredBy),
planName: PLAN.EARLY_ADOPTER,
data: body.data
});
if (error) console.error('Error creating org plan', error);
console.log('Create plan', data);
}
}
if (
['subscription_cancelled', 'subscription_expired', 'subscription_payment_refunded'].includes(
`${eventType}`
)
) {
const subscriptionId = body.data.id;
const { data, error } = await cancelOrgPlan({
subscriptionId,
data: body.data
});
if (error) console.error('Error canceling org plan', error);
console.log('Cancel plan', data);
}
return json({ message: 'Webhook received' });
} catch (err) {
console.error(err);
return json({ message: 'Server error' }, { status: 500 });
}
}
export function GET() {
return json({
success: true
});
}
@@ -1,24 +0,0 @@
import { json } from '@sveltejs/kit';
const downloadTranscript = async () => {};
const uploadTranscriptToS3 = async () => {};
export async function GET({ url }) {
const museFid = url.searchParams.get('museFid');
console.log('museFid');
// check the transcript table and see if we find a record
// if we find one
// check if downloaded = true
// if true
// return the transcript
// if false
// download the transcript from muse
// upload it to S3
// return the transcript
// if we dont
return json({ success: true, museFid });
}
@@ -0,0 +1,9 @@
import { dev } from '$app/environment';
import { env } from '$env/dynamic/private';
import { CustomerPortal } from '@polar-sh/sveltekit';
export const GET = CustomerPortal({
accessToken: env.POLAR_ACCESS_TOKEN,
getCustomerId: async (e) => e.url.searchParams.get('customerId')!,
server: dev ? 'sandbox' : 'production'
});
@@ -1,5 +1,6 @@
import { dev } from '$app/environment';
import { env } from '$env/dynamic/private';
import { Checkout } from 'polar-sveltekit';
import { Checkout } from '@polar-sh/sveltekit';
export const GET = ({ ...params }) => {
const metadata = JSON.parse(params.url.searchParams.get('metadata') ?? '{}');
@@ -7,7 +8,8 @@ export const GET = ({ ...params }) => {
const request = Checkout({
accessToken: env.POLAR_ACCESS_TOKEN,
successUrl
successUrl,
server: dev ? 'sandbox' : 'production'
});
return request(params);
@@ -2,7 +2,7 @@ import { env } from '$env/dynamic/private';
import { getServerSupabase } from '$lib/utils/functions/supabase.server';
import { cancelOrgPlan, createOrgPlan, updateOrgPlan } from '$lib/utils/services/org';
import type { PolarWebhookPayload } from '$lib/utils/types/polar';
import { Webhooks } from 'polar-sveltekit';
import { Webhooks } from '@polar-sh/sveltekit';
import { PLAN } from 'shared/src/plans/constants';
export const POST = Webhooks({
@@ -1,57 +0,0 @@
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
try {
// Catch the event type
// const clonedReq = request.clone();
// const eventType = request.headers.get('X-Event-Name');
const body = await request.json();
// // Check signature
// const secret = LEMON_SQUEEZY_WEBHOOK_SECRET;
// const hmac = crypto.createHmac('sha256', secret);
// const digest = Buffer.from(hmac.update(await clonedReq.text()).digest('hex'), 'utf8');
// const signature = Buffer.from(request.headers.get('X-Signature') || '', 'utf8');
// if (!crypto.timingSafeEqual(digest, signature)) {
// throw new Error('Invalid signature.');
// }
console.log(body);
// Logic according to event
// if (eventType === 'order_created') {
// const orgId = body.meta.custom_data.org_id;
// const triggeredBy = body.meta.custom_data.triggered_by;
// const isSuccessful = body.data.attributes.status === 'paid';
// console.log({
// orgId,
// triggeredBy,
// isSuccessful
// });
// if (isSuccessful) {
// const { error } = await createOrgPlan({
// orgId,
// triggeredBy,
// planName: PLAN.EARLY_ADOPTER,
// data: body.data
// });
// if (error) console.error('Error creating org plan', error);
// }
// }
return json({ message: 'Webhook received' });
} catch (err) {
console.error(err);
return json({ message: 'Server error' }, { status: 500 });
}
}
export function GET() {
return json({
success: true
});
}