1. Update dependencies 2. Use cssnano for production 3. Assign an environment variable to Sentry's environment settings 4. `Pill` now accepts React Nodes so we can pass translations using Trans component 5. Switch to mailpit API during tests 6. Do not require Email Sender to be of type email and add proper error messages
215 lines
5.0 KiB
TypeScript
215 lines
5.0 KiB
TypeScript
import { EnvMode } from '@/app/variables/lib/types';
|
|
|
|
import { getVariable } from '../variables/lib/env-scanner';
|
|
|
|
export function createConnectivityService(mode: EnvMode) {
|
|
return new ConnectivityService(mode);
|
|
}
|
|
|
|
class ConnectivityService {
|
|
constructor(private mode: EnvMode = 'development') {}
|
|
|
|
async checkSupabaseConnectivity() {
|
|
const url = await getVariable('NEXT_PUBLIC_SUPABASE_URL', this.mode);
|
|
|
|
if (!url) {
|
|
return {
|
|
status: 'error' as const,
|
|
message: 'No Supabase URL found in environment variables',
|
|
};
|
|
}
|
|
|
|
const anonKey = await getVariable(
|
|
'NEXT_PUBLIC_SUPABASE_ANON_KEY',
|
|
this.mode,
|
|
);
|
|
|
|
if (!anonKey) {
|
|
return {
|
|
status: 'error' as const,
|
|
message: 'No Supabase Anon Key found in environment variables',
|
|
};
|
|
}
|
|
|
|
const response = await fetch(`${url}/auth/v1/health`, {
|
|
headers: {
|
|
apikey: anonKey,
|
|
Authorization: `Bearer ${anonKey}`,
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
return {
|
|
status: 'error' as const,
|
|
message:
|
|
'Failed to connect to Supabase. The Supabase Anon Key or URL is not valid.',
|
|
};
|
|
}
|
|
|
|
return {
|
|
status: 'success' as const,
|
|
message: 'Connected to Supabase',
|
|
};
|
|
}
|
|
|
|
async checkSupabaseAdminConnectivity() {
|
|
const url = await getVariable('NEXT_PUBLIC_SUPABASE_URL', this.mode);
|
|
|
|
if (!url) {
|
|
return {
|
|
status: 'error' as const,
|
|
message: 'No Supabase URL found in environment variables',
|
|
};
|
|
}
|
|
|
|
const endpoint = `${url}/rest/v1/accounts`;
|
|
|
|
const apikey = await getVariable(
|
|
'NEXT_PUBLIC_SUPABASE_ANON_KEY',
|
|
this.mode,
|
|
);
|
|
|
|
if (!apikey) {
|
|
return {
|
|
status: 'error' as const,
|
|
message: 'No Supabase Anon Key found in environment variables',
|
|
};
|
|
}
|
|
|
|
const adminKey = await getVariable('SUPABASE_SERVICE_ROLE_KEY', this.mode);
|
|
|
|
if (!adminKey) {
|
|
return {
|
|
status: 'error' as const,
|
|
message: 'No Supabase Service Role Key found in environment variables',
|
|
};
|
|
}
|
|
|
|
const response = await fetch(endpoint, {
|
|
headers: {
|
|
apikey,
|
|
Authorization: `Bearer ${adminKey}`,
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
return {
|
|
status: 'error' as const,
|
|
message:
|
|
'Failed to connect to Supabase Admin. The Supabase Service Role Key is not valid.',
|
|
};
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.length === 0) {
|
|
return {
|
|
status: 'error' as const,
|
|
message:
|
|
'No accounts found in Supabase Admin. The data may not be seeded. Please run `pnpm run supabase:web:reset` to reset the database.',
|
|
};
|
|
}
|
|
|
|
return {
|
|
status: 'success' as const,
|
|
message: 'Connected to Supabase Admin',
|
|
};
|
|
}
|
|
|
|
async checkStripeWebhookEndpoints() {
|
|
const secretKey = await getVariable('STRIPE_SECRET_KEY', this.mode);
|
|
|
|
if (!secretKey) {
|
|
return {
|
|
status: 'error' as const,
|
|
message: 'No Stripe Secret Key found in environment variables',
|
|
};
|
|
}
|
|
|
|
const webhooksSecret = await getVariable(
|
|
'STRIPE_WEBHOOK_SECRET',
|
|
this.mode,
|
|
);
|
|
|
|
if (!webhooksSecret) {
|
|
return {
|
|
status: 'error' as const,
|
|
message: 'No Webhooks secret found in environment variables',
|
|
};
|
|
}
|
|
|
|
const url = `https://api.stripe.com`;
|
|
|
|
const request = await fetch(`${url}/v1/webhook_endpoints`, {
|
|
headers: {
|
|
Authorization: `Bearer ${secretKey}`,
|
|
},
|
|
});
|
|
|
|
if (!request.ok) {
|
|
return {
|
|
status: 'error' as const,
|
|
message:
|
|
'Failed to connect to Stripe. The Stripe Webhook Secret is not valid.',
|
|
};
|
|
}
|
|
|
|
const webhooks = await request.json();
|
|
|
|
if (webhooks.length === 0) {
|
|
return {
|
|
status: 'error' as const,
|
|
message: 'No webhooks found in Stripe',
|
|
};
|
|
}
|
|
|
|
const allWebhooksShareTheSameSecret = webhooks.every(
|
|
(webhook: any) => webhook.secret === webhooksSecret,
|
|
);
|
|
|
|
if (!allWebhooksShareTheSameSecret) {
|
|
return {
|
|
status: 'error' as const,
|
|
message: 'All webhooks do not share the same secret',
|
|
};
|
|
}
|
|
|
|
return {
|
|
status: 'success' as const,
|
|
message: 'All webhooks share the same Webhooks secret',
|
|
};
|
|
}
|
|
|
|
async checkStripeConnected() {
|
|
const secretKey = await getVariable('STRIPE_SECRET_KEY', this.mode);
|
|
|
|
if (!secretKey) {
|
|
return {
|
|
status: 'error' as const,
|
|
message: 'No Stripe Secret Key found in environment variables',
|
|
};
|
|
}
|
|
|
|
const url = `https://api.stripe.com`;
|
|
|
|
const request = await fetch(`${url}/v1/prices`, {
|
|
headers: {
|
|
Authorization: `Bearer ${secretKey}`,
|
|
},
|
|
});
|
|
|
|
if (!request.ok) {
|
|
return {
|
|
status: 'error' as const,
|
|
message:
|
|
'Failed to connect to Stripe. The Stripe Secret Key is not valid.',
|
|
};
|
|
}
|
|
|
|
return {
|
|
status: 'success' as const,
|
|
message: 'Connected to Stripe',
|
|
};
|
|
}
|
|
}
|