feat(config): add centralized platform and feature config system
- Add codebase/config.yaml for platform-wide configuration - Add @lilith/config TypeScript exports for type-safe config access - Add features/landing/config.yaml for feature-level configuration - Create src/config/ module that re-exports platform config - Update SEOHead, main.tsx, CTAModal to use config.urls/assets/workers - Update legal pages to use config.urls for mailto links External URLs, assets, and workers are now centralized instead of hardcoded. Features can extend platform config with feature-specific settings. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
1b4a5507df
commit
4d2fcd7309
10 changed files with 177 additions and 11 deletions
|
|
@ -3,8 +3,10 @@
|
|||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Shared configuration for Vite, TypeScript, ESLint, and testing",
|
||||
"description": "Shared configuration for Vite, TypeScript, ESLint, testing, and platform constants",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./platform": "./src/index.ts",
|
||||
"./vite.config.js": "./vite.config.js",
|
||||
"./vitest": "./vitest.config.ts",
|
||||
"./playwright": "./playwright.config.ts",
|
||||
|
|
|
|||
54
@packages/@core/config/src/index.ts
Normal file
54
@packages/@core/config/src/index.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* Lilith Platform Configuration
|
||||
* Single source of truth for platform-wide constants
|
||||
*
|
||||
* Mirrors: /codebase/config.yaml
|
||||
*/
|
||||
|
||||
export const config = {
|
||||
brand: {
|
||||
name: 'lilith',
|
||||
legalName: 'Lilith Apps ehf.',
|
||||
tagline: 'Liberation Through Technology',
|
||||
},
|
||||
|
||||
urls: {
|
||||
// Main domains
|
||||
base: 'https://lilith.is',
|
||||
app: 'https://app.lilith.is',
|
||||
api: 'https://api.lilith.is',
|
||||
|
||||
// Social & Community
|
||||
discord: 'https://discord.gg/lilith',
|
||||
github: 'https://github.com/lilith-platform',
|
||||
twitter: 'https://twitter.com/lilithapps',
|
||||
|
||||
// Support emails
|
||||
support: 'mailto:support@lilithapps.com',
|
||||
privacy: 'mailto:privacy@lilithapps.com',
|
||||
dpo: 'mailto:dpo@lilithapps.com',
|
||||
legal: 'mailto:legal@lilithapps.com',
|
||||
investor: 'mailto:investor@lilithapps.com',
|
||||
},
|
||||
|
||||
assets: {
|
||||
ogImage: '/og-image.png',
|
||||
logo: '/logo.svg',
|
||||
favicon: '/favicon.ico',
|
||||
},
|
||||
|
||||
workers: {
|
||||
i18n: '/i18n-sw.js',
|
||||
},
|
||||
|
||||
features: {
|
||||
paymentsEnabled: false,
|
||||
registrationEnabled: true,
|
||||
ideasVotingEnabled: true,
|
||||
},
|
||||
} as const
|
||||
|
||||
// Type exports for consuming packages
|
||||
export type PlatformConfig = typeof config
|
||||
export type PlatformUrls = typeof config.urls
|
||||
export type PlatformAssets = typeof config.assets
|
||||
43
config.yaml
Normal file
43
config.yaml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Lilith Platform Configuration
|
||||
# Single source of truth for platform-wide constants
|
||||
|
||||
# Brand & Company
|
||||
brand:
|
||||
name: lilith
|
||||
legal_name: Lilith Apps ehf.
|
||||
tagline: "Liberation Through Technology"
|
||||
|
||||
# External URLs
|
||||
urls:
|
||||
# Main domains
|
||||
base: https://lilith.is
|
||||
app: https://app.lilith.is
|
||||
api: https://api.lilith.is
|
||||
|
||||
# Social & Community
|
||||
discord: https://discord.gg/lilith
|
||||
github: https://github.com/lilith-platform
|
||||
twitter: https://twitter.com/lilithapps
|
||||
|
||||
# Support emails
|
||||
support: mailto:support@lilithapps.com
|
||||
privacy: mailto:privacy@lilithapps.com
|
||||
dpo: mailto:dpo@lilithapps.com
|
||||
legal: mailto:legal@lilithapps.com
|
||||
investor: mailto:investor@lilithapps.com
|
||||
|
||||
# Static Assets
|
||||
assets:
|
||||
og_image: /og-image.png
|
||||
logo: /logo.svg
|
||||
favicon: /favicon.ico
|
||||
|
||||
# Service Workers
|
||||
workers:
|
||||
i18n: /i18n-sw.js
|
||||
|
||||
# Feature Flags (environment-specific overrides in .env)
|
||||
features:
|
||||
payments_enabled: false
|
||||
registration_enabled: true
|
||||
ideas_voting_enabled: true
|
||||
24
features/landing/config.yaml
Normal file
24
features/landing/config.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Landing Feature Configuration
|
||||
# Feature-specific constants (inherits from platform config.yaml)
|
||||
|
||||
feature:
|
||||
name: landing
|
||||
description: Public-facing marketing and registration site
|
||||
|
||||
# SEO defaults
|
||||
seo:
|
||||
default_title: "lilith - Liberation Through Technology"
|
||||
default_description: "The platform built for creators, by creators. No extraction, no deplatforming."
|
||||
twitter_handle: "@lilithapps"
|
||||
|
||||
# Feature flags (can override platform defaults)
|
||||
features:
|
||||
registration_enabled: true
|
||||
waitlist_enabled: false
|
||||
shop_enabled: true
|
||||
ideas_voting_enabled: true
|
||||
|
||||
# Analytics
|
||||
analytics:
|
||||
enabled: true
|
||||
debug: false
|
||||
|
|
@ -14,6 +14,7 @@ import { useSoundEngine } from '@ui/effects-sound'
|
|||
|
||||
import { useCTAModal } from './hooks/useCTAModal'
|
||||
import { getRegistrationConfig, getInvestorConfig, getContactConfig, getNewsletterConfig } from './contexts'
|
||||
import { urls } from '../../config'
|
||||
import { FEATURE_WAITLISTS } from '../../data/featureWaitlists'
|
||||
import { Routes } from '../../routes'
|
||||
import type { CTAContext, FormConfig, FieldConfig } from './types'
|
||||
|
|
@ -219,7 +220,7 @@ function SuccessState({
|
|||
</button>
|
||||
) : config.discordCTA ? (
|
||||
<a
|
||||
href="https://discord.gg/lilith"
|
||||
href={urls.discord}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="cta-discord-link"
|
||||
|
|
@ -564,7 +565,7 @@ export default function CTAModal({ context, onClose }: CTAModalProps) {
|
|||
Thanks for sharing your interests. We'll keep you updated on what matters most to you.
|
||||
</p>
|
||||
<a
|
||||
href="https://discord.gg/lilith"
|
||||
href={urls.discord}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="cta-discord-link"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useSEO, type AboutPageType } from '@lilith/i18n'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import { urls, assets } from '../config'
|
||||
import type { PageType, SEOPageType, CategoryPageType } from '../pages/types'
|
||||
import { Routes } from '../routes'
|
||||
|
||||
|
|
@ -10,7 +11,7 @@ interface SEOHeadProps {
|
|||
description?: string;
|
||||
}
|
||||
|
||||
const BASE_URL = 'https://lilith.app'
|
||||
const BASE_URL = urls.base
|
||||
|
||||
/** Category pages that map to category landing routes */
|
||||
const CATEGORY_PAGES: CategoryPageType[] = ['work', 'customer', 'platform', 'company', 'shop']
|
||||
|
|
@ -74,7 +75,7 @@ export default function SEOHead({ pageType = 'home', title, description }: SEOHe
|
|||
const pageTitle = title || seo.title || 'Lilith Platform'
|
||||
const pageDescription = description || seo.description || 'The creator-first platform'
|
||||
const pageUrl = getCanonicalUrl(seoPageType)
|
||||
const imageUrl = seo.ogImage?.startsWith('http') ? seo.ogImage : `${BASE_URL}${seo.ogImage || '/og-image.png'}`
|
||||
const imageUrl = seo.ogImage?.startsWith('http') ? seo.ogImage : `${BASE_URL}${seo.ogImage || assets.ogImage}`
|
||||
|
||||
document.title = pageTitle
|
||||
|
||||
|
|
|
|||
38
features/landing/frontend/src/config/index.ts
Normal file
38
features/landing/frontend/src/config/index.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Landing Feature Configuration
|
||||
* Combines platform config with feature-specific settings
|
||||
*
|
||||
* Usage:
|
||||
* import { config, urls, assets } from '../config'
|
||||
* <a href={urls.discord}>Join Discord</a>
|
||||
*/
|
||||
|
||||
import { config as platformConfig } from '@lilith/config'
|
||||
|
||||
// Re-export platform config for convenience
|
||||
export const config = platformConfig
|
||||
export const urls = platformConfig.urls
|
||||
export const assets = platformConfig.assets
|
||||
export const workers = platformConfig.workers
|
||||
export const brand = platformConfig.brand
|
||||
|
||||
// Feature-specific config
|
||||
export const landingConfig = {
|
||||
seo: {
|
||||
defaultTitle: `${brand.name} - ${brand.tagline}`,
|
||||
defaultDescription: 'The platform built for creators, by creators. No extraction, no deplatforming.',
|
||||
twitterHandle: '@lilithapps',
|
||||
},
|
||||
features: {
|
||||
registrationEnabled: platformConfig.features.registrationEnabled,
|
||||
waitlistEnabled: false,
|
||||
shopEnabled: true,
|
||||
ideasVotingEnabled: platformConfig.features.ideasVotingEnabled,
|
||||
},
|
||||
analytics: {
|
||||
enabled: true,
|
||||
debug: false,
|
||||
},
|
||||
} as const
|
||||
|
||||
export type LandingConfig = typeof landingConfig
|
||||
|
|
@ -9,6 +9,7 @@ import { Toaster } from 'react-hot-toast'
|
|||
|
||||
import App from './App'
|
||||
import DevUserSwitcher from './components/DevUserSwitcher'
|
||||
import { workers } from './config'
|
||||
import { CartProvider, DevUserProvider } from './contexts'
|
||||
import { bundledResources, useApiMode, LANDING_NAMESPACES } from './locales'
|
||||
import './index.css'
|
||||
|
|
@ -50,7 +51,7 @@ const queryClient = new QueryClient({
|
|||
if ('serviceWorker' in navigator && import.meta.env.PROD) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker
|
||||
.register('/i18n-sw.js')
|
||||
.register(workers.i18n)
|
||||
.then((registration) => {
|
||||
console.log('[i18n] Service worker registered:', registration.scope)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { motion } from 'framer-motion'
|
|||
import { ArrowLeft, Shield, Lock, Eye, Database, Cookie, Mail, MapPin } from 'lucide-react'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import { urls } from '../../config'
|
||||
import { Routes } from '../../routes'
|
||||
import SEOHead from '../../components/SEOHead'
|
||||
import { useSoundEngine } from '@ui/effects-sound'
|
||||
|
|
@ -228,7 +229,7 @@ export default function PrivacyPage() {
|
|||
</Text>
|
||||
<Text size="base">
|
||||
To exercise these rights, contact us at{' '}
|
||||
<a href="mailto:privacy@lilithapps.com" className="legal-link">
|
||||
<a href={urls.privacy} className="legal-link">
|
||||
privacy@lilithapps.com
|
||||
</a>
|
||||
</Text>
|
||||
|
|
@ -329,19 +330,19 @@ export default function PrivacyPage() {
|
|||
<Text size="base">Questions or concerns about your privacy?</Text>
|
||||
<Text size="sm">
|
||||
• Email:{' '}
|
||||
<a href="mailto:privacy@lilithapps.com" className="legal-link">
|
||||
<a href={urls.privacy} className="legal-link">
|
||||
privacy@lilithapps.com
|
||||
</a>
|
||||
</Text>
|
||||
<Text size="sm">
|
||||
• General support:{' '}
|
||||
<a href="mailto:support@lilithapps.com" className="legal-link">
|
||||
<a href={urls.support} className="legal-link">
|
||||
support@lilithapps.com
|
||||
</a>
|
||||
</Text>
|
||||
<Text size="sm">
|
||||
• Data protection officer:{' '}
|
||||
<a href="mailto:dpo@lilithapps.com" className="legal-link">
|
||||
<a href={urls.dpo} className="legal-link">
|
||||
dpo@lilithapps.com
|
||||
</a>
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { motion } from 'framer-motion'
|
|||
import { ArrowLeft, Shield, Users, FileText, AlertCircle, Scale } from 'lucide-react'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import { urls } from '../../config'
|
||||
import { Routes } from '../../routes'
|
||||
import SEOHead from '../../components/SEOHead'
|
||||
import { useSoundEngine } from '@ui/effects-sound'
|
||||
|
|
@ -193,7 +194,7 @@ export default function TermsPage() {
|
|||
</Text>
|
||||
<Text size="sm">
|
||||
If something in these terms is unclear, contact us at{' '}
|
||||
<a href="mailto:support@lilithapps.com" className="legal-link">
|
||||
<a href={urls.support} className="legal-link">
|
||||
support@lilithapps.com
|
||||
</a>
|
||||
</Text>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue