Don't crash if supabase isn't configured

This commit is contained in:
Connor Yoh 2025-11-19 21:08:08 +00:00
parent 426008da7f
commit ae2d3ad322
5 changed files with 54 additions and 10 deletions

View File

@ -1,10 +1,20 @@
import { createClient } from '@supabase/supabase-js';
import { createClient, SupabaseClient } from '@supabase/supabase-js';
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY;
if (!supabaseUrl || !supabaseAnonKey) {
throw new Error('Missing Supabase environment variables');
}
// Check if Supabase is configured
export const isSupabaseConfigured = !!(supabaseUrl && supabaseAnonKey);
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
// Create client only if configured, otherwise export null
export const supabase: SupabaseClient | null = isSupabaseConfigured
? createClient(supabaseUrl, supabaseAnonKey)
: null;
// Log warning if not configured (for self-hosted installations)
if (!isSupabaseConfigured) {
console.warn(
'Supabase is not configured. Checkout and billing features will be disabled. ' +
'Static plan information will be displayed instead.'
);
}

View File

@ -6,6 +6,7 @@ import { useCheckout } from '@app/contexts/CheckoutContext';
import { useLicense } from '@app/contexts/LicenseContext';
import { mapLicenseToTier } from '@app/services/licenseService';
import LocalIcon from '@app/components/shared/LocalIcon';
import { isSupabaseConfigured } from '@app/services/supabaseClient';
/**
* UpgradeBanner - Dismissable top banner encouraging users to upgrade
@ -35,6 +36,12 @@ const UpgradeBanner: React.FC = () => {
return;
}
// Don't show if Supabase is not configured (no checkout available)
if (!isSupabaseConfigured) {
setIsVisible(false);
return;
}
// Don't show while license is loading
if (licenseLoading) {
return;

View File

@ -11,6 +11,7 @@ import { alert } from '@app/components/toast';
import LocalIcon from '@app/components/shared/LocalIcon';
import { Z_INDEX_OVER_CONFIG_MODAL } from '@app/styles/zIndex';
import { ManageBillingButton } from '@app/components/shared/ManageBillingButton';
import { isSupabaseConfigured } from '@app/services/supabaseClient';
const AdminPlanSection: React.FC = () => {
const { t } = useTranslation();
@ -25,9 +26,9 @@ const AdminPlanSection: React.FC = () => {
// Check if we should use static version
useEffect(() => {
// Check if Stripe is configured
// Check if Stripe and Supabase are configured
const stripeKey = import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY;
if (!stripeKey || error) {
if (!stripeKey || !isSupabaseConfigured || error) {
setUseStaticVersion(true);
}
}, [error]);
@ -146,8 +147,8 @@ const AdminPlanSection: React.FC = () => {
/>
</Group>
{/* Manage Subscription Button - Only show if user has active license */}
{licenseInfo?.licenseKey && (
{/* Manage Subscription Button - Only show if user has active license and Supabase is configured */}
{licenseInfo?.licenseKey && isSupabaseConfigured && (
<Group justify="space-between" align="center">
<Text size="sm" c="dimmed">
{t('plan.manageSubscription.description', 'Manage your subscription, billing, and payment methods')}

View File

@ -7,6 +7,7 @@ import { userManagementService } from '@app/services/userManagementService';
import { alert } from '@app/components/toast';
import { pollLicenseKeyWithBackoff, activateLicenseKey, resyncExistingLicense } from '@app/utils/licenseCheckoutUtils';
import { useLicense } from '@app/contexts/LicenseContext';
import { isSupabaseConfigured } from '@app/services/supabaseClient';
export interface CheckoutOptions {
minimumSeats?: number; // Override calculated seats for enterprise
@ -202,6 +203,11 @@ export const CheckoutProvider: React.FC<CheckoutProviderProps> = ({
try {
setIsLoading(true);
// Check if Supabase is configured
if (!isSupabaseConfigured) {
throw new Error('Checkout is not available. Supabase is not configured.');
}
// Update currency if provided
const currency = options.currency || currentCurrency;
if (currency !== currentCurrency) {

View File

@ -1,5 +1,5 @@
import apiClient from '@app/services/apiClient';
import { supabase } from '@app/services/supabaseClient';
import { supabase, isSupabaseConfigured } from '@app/services/supabaseClient';
import { getCheckoutMode } from '@app/utils/protocolDetection';
export interface PlanFeature {
@ -110,6 +110,11 @@ const licenseService = {
*/
async getPlans(currency: string = 'gbp'): Promise<PlansResponse> {
try {
// Check if Supabase is configured
if (!isSupabaseConfigured || !supabase) {
throw new Error('Supabase is not configured. Please use static plans instead.');
}
// Fetch all self-hosted prices from Stripe
const { data, error } = await supabase.functions.invoke<{
prices: Record<string, {
@ -358,6 +363,11 @@ const licenseService = {
* Create a Stripe checkout session for upgrading
*/
async createCheckoutSession(request: CheckoutSessionRequest): Promise<CheckoutSessionResponse> {
// Check if Supabase is configured
if (!isSupabaseConfigured || !supabase) {
throw new Error('Supabase is not configured. Checkout is not available.');
}
// Detect if HTTPS is available to determine checkout mode
const checkoutMode = getCheckoutMode();
const baseUrl = window.location.origin;
@ -395,6 +405,11 @@ const licenseService = {
* Uses license key for self-hosted authentication
*/
async createBillingPortalSession(returnUrl: string, licenseKey: string): Promise<BillingPortalResponse> {
// Check if Supabase is configured
if (!isSupabaseConfigured || !supabase) {
throw new Error('Supabase is not configured. Billing portal is not available.');
}
const { data, error} = await supabase.functions.invoke('manage-billing', {
body: {
return_url: returnUrl,
@ -429,6 +444,11 @@ const licenseService = {
* Check if license key is ready for the given installation ID
*/
async checkLicenseKey(installationId: string): Promise<LicenseKeyResponse> {
// Check if Supabase is configured
if (!isSupabaseConfigured || !supabase) {
throw new Error('Supabase is not configured. License key lookup is not available.');
}
const { data, error } = await supabase.functions.invoke('get-license-key', {
body: {
installation_id: installationId,