feat: add @polar-sh/sveltekit (#539)
* feat add @polar-sh/sveltekit * remove all lemonsqueezy text
This commit is contained in:
@@ -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:[email protected]"
|
||||
>Contact Us</a
|
||||
>
|
||||
<a href="mailto:[email protected]">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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)" <[email protected]>`,
|
||||
|
||||
@@ -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)" <[email protected]>`,
|
||||
|
||||
@@ -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)" <[email protected]>`,
|
||||
|
||||
@@ -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" <[email protected]>`,
|
||||
|
||||
@@ -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" <[email protected]>`,
|
||||
|
||||
@@ -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" <[email protected]>`,
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user