diff --git a/src/App.vue b/src/App.vue index 7be8c70..c710041 100644 --- a/src/App.vue +++ b/src/App.vue @@ -8,8 +8,9 @@ import { Toaster } from 'vue-sonner' import { useNostr } from '@/composables/useNostr' import { identity } from '@/composables/useIdentity' import { toast } from 'vue-sonner' +import { config } from '@/lib/config' -const relays = JSON.parse(import.meta.env.VITE_NOSTR_RELAYS as string) +const relays = config.nostr.relays const { isConnected, isConnecting, error, connect, disconnect } = useNostr({ relays }) const showPasswordDialog = ref(false) diff --git a/src/components/events/PurchaseTicketDialog.vue b/src/components/events/PurchaseTicketDialog.vue index 56f1bea..08767e3 100644 --- a/src/components/events/PurchaseTicketDialog.vue +++ b/src/components/events/PurchaseTicketDialog.vue @@ -6,6 +6,7 @@ import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import QRCode from 'qrcode' +import { config } from '@/lib/config' interface Props { event: { @@ -49,14 +50,14 @@ async function handleSubmit() { isLoading.value = true error.value = '' - const apiUrl = `${import.meta.env.VITE_API_BASE_URL}/events/api/v1/tickets/${props.event.id}/${encodeURIComponent(name.value)}/${encodeURIComponent(email.value)}` + const apiUrl = `${config.api.baseUrl}/events/api/v1/tickets/${props.event.id}/${encodeURIComponent(name.value)}/${encodeURIComponent(email.value)}` console.log('Calling API:', apiUrl) try { const response = await fetch(apiUrl, { headers: { 'Accept': 'application/json', - 'X-API-KEY': import.meta.env.VITE_API_KEY + 'X-API-KEY': config.api.key } }) diff --git a/src/components/nostr/NostrFeed.vue b/src/components/nostr/NostrFeed.vue index f5f49eb..f611520 100644 --- a/src/components/nostr/NostrFeed.vue +++ b/src/components/nostr/NostrFeed.vue @@ -7,6 +7,7 @@ import { ScrollArea } from '@/components/ui/scroll-area' import { Badge } from '@/components/ui/badge' import { formatDistanceToNow } from 'date-fns' import { Megaphone } from 'lucide-vue-next' +import { config, configUtils } from '@/lib/config' const props = defineProps<{ relays?: string[] @@ -17,14 +18,11 @@ const notes = ref([]) const isLoading = ref(true) const error = ref(null) -const relayUrls = props.relays || JSON.parse(import.meta.env.VITE_NOSTR_RELAYS as string) +const relayUrls = props.relays || config.nostr.relays const { disconnect } = useNostr({ relays: relayUrls }) -// Get admin/moderator pubkeys from environment -// These should be hex pubkeys of trusted moderators/admins -const adminPubkeys = import.meta.env.VITE_ADMIN_PUBKEYS - ? JSON.parse(import.meta.env.VITE_ADMIN_PUBKEYS as string) - : [] +// Get admin/moderator pubkeys from centralized config +const adminPubkeys = config.nostr.adminPubkeys async function loadNotes() { try { diff --git a/src/composables/useSocial.ts b/src/composables/useSocial.ts index 620f864..51a6112 100644 --- a/src/composables/useSocial.ts +++ b/src/composables/useSocial.ts @@ -152,5 +152,5 @@ export function useSocial(relayUrls: string[]) { } // Export singleton instance for global use -const relayUrls = JSON.parse(import.meta.env.VITE_NOSTR_RELAYS as string) -export const social = useSocial(relayUrls) \ No newline at end of file +import { config } from '@/lib/config' +export const social = useSocial(config.nostr.relays) \ No newline at end of file diff --git a/src/lib/api/events.ts b/src/lib/api/events.ts index fac98d5..eb1b4b5 100644 --- a/src/lib/api/events.ts +++ b/src/lib/api/events.ts @@ -1,7 +1,8 @@ import type { Event, EventsApiError } from '../types/event' +import { config } from '@/lib/config' -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://lnbits' -const API_KEY = import.meta.env.VITE_API_KEY +const API_BASE_URL = config.api.baseUrl || 'http://lnbits' +const API_KEY = config.api.key export async function fetchEvents(allWallets = true): Promise { try { diff --git a/src/lib/config/index.ts b/src/lib/config/index.ts new file mode 100644 index 0000000..44ca3b2 --- /dev/null +++ b/src/lib/config/index.ts @@ -0,0 +1,69 @@ +// Centralized configuration management +// Handles all environment variables and app configuration + +interface NostrConfig { + relays: string[] + adminPubkeys: string[] +} + +interface ApiConfig { + baseUrl: string + key: string +} + +interface AppConfig { + nostr: NostrConfig + api: ApiConfig + support: { + npub: string + } +} + +// Parse JSON environment variables safely +function parseJsonEnv(envVar: string | undefined, fallback: any = []): any { + if (!envVar) return fallback + + try { + return JSON.parse(envVar) + } catch (error) { + console.warn(`Failed to parse environment variable: ${envVar}`) + return fallback + } +} + +// Create the configuration object +export const config: AppConfig = { + nostr: { + relays: parseJsonEnv(import.meta.env.VITE_NOSTR_RELAYS, []), + adminPubkeys: parseJsonEnv(import.meta.env.VITE_ADMIN_PUBKEYS, []) + }, + api: { + baseUrl: import.meta.env.VITE_API_BASE_URL || '', + key: import.meta.env.VITE_API_KEY || '' + }, + support: { + npub: import.meta.env.VITE_SUPPORT_NPUB || '' + } +} as const + +// Utility functions for common config operations +export const configUtils = { + isAdminPubkey: (pubkey: string): boolean => { + return config.nostr.adminPubkeys.includes(pubkey) + }, + + hasNostrConfig: (): boolean => { + return config.nostr.relays.length > 0 + }, + + hasApiConfig: (): boolean => { + return Boolean(config.api.baseUrl && config.api.key) + }, + + getDefaultRelays: (): string[] => { + return config.nostr.relays + } +} + +// Export individual config sections for convenience +export const { nostr: nostrConfig, api: apiConfig } = config \ No newline at end of file