mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:01:09 +02:00
🔥 chore(picture): remove PostHog analytics for GDPR compliance
- Remove posthog-js dependency from picture web app - Delete PostHog integration module and setup documentation - Remove PostHog initialization from root layout - Clean up environment variables from .env.example - Update logger comments to remove Sentry references - Update PROJECT_OVERVIEW.md to reflect Umami as analytics tool
This commit is contained in:
parent
13754f2d55
commit
cb130191ab
10 changed files with 2176 additions and 1778 deletions
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Centralized logging utility
|
||||
* - Development: Logs to console
|
||||
* - Production: Can be integrated with Sentry, LogRocket, etc.
|
||||
* - Production: Logs to console (can be extended for custom error tracking)
|
||||
*/
|
||||
|
||||
const isDevelopment = __DEV__;
|
||||
|
|
@ -38,7 +38,7 @@ export const logger = {
|
|||
*/
|
||||
error: (...args: any[]) => {
|
||||
console.error('[ERROR]', ...args);
|
||||
// TODO: Send to error tracking service (Sentry, etc.)
|
||||
// In production: can be extended for custom error tracking
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,7 +11,3 @@ PUBLIC_APPLE_CLIENT_ID=
|
|||
# Umami Analytics
|
||||
PUBLIC_UMAMI_URL=https://your-umami-instance.com
|
||||
PUBLIC_UMAMI_WEBSITE_ID=your-website-id
|
||||
|
||||
# PostHog Analytics
|
||||
PUBLIC_POSTHOG_KEY=phc_your_posthog_project_api_key
|
||||
PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
"@picture/design-tokens": "workspace:*",
|
||||
"@picture/shared": "workspace:*",
|
||||
"konva": "^10.0.2",
|
||||
"posthog-js": "^1.273.1",
|
||||
"svelte-i18n": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,85 +0,0 @@
|
|||
import posthog from 'posthog-js';
|
||||
import { browser } from '$app/environment';
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
export function initPostHog() {
|
||||
const posthogKey = env.PUBLIC_POSTHOG_KEY;
|
||||
const posthogHost = env.PUBLIC_POSTHOG_HOST;
|
||||
|
||||
if (browser && posthogKey && posthogHost) {
|
||||
posthog.init(posthogKey, {
|
||||
api_host: posthogHost,
|
||||
person_profiles: 'identified_only', // Only track identified users
|
||||
capture_pageview: true, // Automatically capture pageviews
|
||||
capture_pageleave: true, // Track when users leave pages
|
||||
// Privacy-friendly settings
|
||||
opt_out_capturing_by_default: false,
|
||||
persistence: 'localStorage',
|
||||
autocapture: false, // Disable automatic event capture for better control
|
||||
// Session recording (optional - can be disabled)
|
||||
disable_session_recording: true, // Set to false if you want session recordings
|
||||
// Performance
|
||||
loaded: (posthog) => {
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('PostHog loaded');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions for common tracking events
|
||||
export const analytics = {
|
||||
// Track page view (usually automatic, but available for manual tracking)
|
||||
pageView: (url?: string) => {
|
||||
if (browser) {
|
||||
posthog.capture('$pageview', { url: url || window.location.href });
|
||||
}
|
||||
},
|
||||
|
||||
// Identify a user
|
||||
identify: (userId: string, properties?: Record<string, any>) => {
|
||||
if (browser) {
|
||||
posthog.identify(userId, properties);
|
||||
}
|
||||
},
|
||||
|
||||
// Track custom events
|
||||
track: (eventName: string, properties?: Record<string, any>) => {
|
||||
if (browser) {
|
||||
posthog.capture(eventName, properties);
|
||||
}
|
||||
},
|
||||
|
||||
// Reset user identity (e.g., on logout)
|
||||
reset: () => {
|
||||
if (browser) {
|
||||
posthog.reset();
|
||||
}
|
||||
},
|
||||
|
||||
// Set user properties
|
||||
setUserProperties: (properties: Record<string, any>) => {
|
||||
if (browser) {
|
||||
posthog.setPersonProperties(properties);
|
||||
}
|
||||
},
|
||||
|
||||
// Feature flags
|
||||
isFeatureEnabled: (featureKey: string): boolean => {
|
||||
if (browser) {
|
||||
return posthog.isFeatureEnabled(featureKey) ?? false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// Get feature flag value
|
||||
getFeatureFlag: (featureKey: string): string | boolean | undefined => {
|
||||
if (browser) {
|
||||
return posthog.getFeatureFlag(featureKey);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
};
|
||||
|
||||
export default posthog;
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import Toast from '$lib/components/ui/Toast.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { initPostHog, analytics } from '$lib/analytics/posthog';
|
||||
|
||||
// Import and initialize theme
|
||||
import { theme } from '$lib/stores/theme';
|
||||
|
|
@ -18,18 +17,8 @@
|
|||
// Initialize theme (applies CSS variables and loads from localStorage)
|
||||
const cleanupTheme = theme.initialize();
|
||||
|
||||
// Initialize PostHog
|
||||
initPostHog();
|
||||
|
||||
// Initialize auth with Mana Core
|
||||
authStore.initialize().then(() => {
|
||||
// Identify user in PostHog if logged in
|
||||
if (authStore.user) {
|
||||
analytics.identify(authStore.user.id, {
|
||||
email: authStore.user.email,
|
||||
});
|
||||
}
|
||||
});
|
||||
authStore.initialize();
|
||||
|
||||
return () => {
|
||||
cleanupTheme();
|
||||
|
|
|
|||
|
|
@ -1,503 +0,0 @@
|
|||
# PostHog Analytics Setup
|
||||
|
||||
This document describes how to set up PostHog Analytics for the Picture web application.
|
||||
|
||||
## Overview
|
||||
|
||||
PostHog is a powerful product analytics platform that provides:
|
||||
- 📊 **Product Analytics**: Track user behavior and product usage
|
||||
- 🎯 **Feature Flags**: Roll out features gradually and A/B test
|
||||
- 📹 **Session Recordings**: Watch how users interact with your app
|
||||
- 🔥 **Heatmaps**: Visualize where users click and scroll
|
||||
- 👥 **User Profiles**: Understand individual user journeys
|
||||
|
||||
## Architecture
|
||||
|
||||
### Web App (SvelteKit)
|
||||
- **Location**: `apps/web/src/lib/analytics/posthog.ts`
|
||||
- **Framework**: SvelteKit
|
||||
- **SDK**: posthog-js
|
||||
- **Integration**: Initialized in root layout with automatic user identification
|
||||
|
||||
## Configuration
|
||||
|
||||
PostHog is configured using environment variables:
|
||||
|
||||
```bash
|
||||
PUBLIC_POSTHOG_KEY=phc_your_posthog_project_api_key
|
||||
PUBLIC_POSTHOG_HOST=https://us.i.posthog.com # or https://eu.i.posthog.com for EU
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Create or update `apps/web/.env`:
|
||||
|
||||
```bash
|
||||
PUBLIC_POSTHOG_KEY=phc_xxxxxxxxxxxxxxxxx
|
||||
PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
|
||||
```
|
||||
|
||||
> **Note**: The `PUBLIC_` prefix is required in SvelteKit to expose variables to the client.
|
||||
|
||||
## Features
|
||||
|
||||
### 🔐 Privacy-First Configuration
|
||||
- ✅ **Identified Users Only**: `person_profiles: 'identified_only'` - only tracks logged-in users
|
||||
- ✅ **localStorage Persistence**: Uses localStorage instead of cookies
|
||||
- ✅ **No Autocapture**: Manual event tracking for better control
|
||||
- ✅ **Session Recording Disabled**: Can be enabled if needed
|
||||
|
||||
### 🚀 Automatic Features
|
||||
- ✅ **Pageview Tracking**: Automatically tracks page navigation
|
||||
- ✅ **Page Leave Tracking**: Tracks when users leave pages
|
||||
- ✅ **User Identification**: Automatically identifies users on login/logout
|
||||
- ✅ **User Properties**: Tracks email and account creation date
|
||||
|
||||
### 📊 Current Implementation
|
||||
|
||||
#### User Identification
|
||||
Users are automatically identified on login with:
|
||||
- User ID (from Supabase)
|
||||
- Email address
|
||||
- Account creation timestamp
|
||||
|
||||
```typescript
|
||||
// Automatically done on login
|
||||
analytics.identify(user.id, {
|
||||
email: user.email,
|
||||
created_at: user.created_at
|
||||
});
|
||||
```
|
||||
|
||||
#### Session Management
|
||||
- **Login**: User is identified in PostHog
|
||||
- **Logout**: PostHog session is reset
|
||||
- **Auth State Changes**: PostHog identity is updated automatically
|
||||
|
||||
## Setup Steps
|
||||
|
||||
### 1. Create PostHog Account
|
||||
|
||||
You have two options:
|
||||
|
||||
#### Option A: PostHog Cloud (Recommended)
|
||||
1. Sign up at [posthog.com](https://posthog.com)
|
||||
2. Create a new project
|
||||
3. Select your region (US or EU)
|
||||
4. Copy your Project API Key (starts with `phc_`)
|
||||
|
||||
#### Option B: Self-Hosted PostHog
|
||||
1. Follow the [PostHog deployment guide](https://posthog.com/docs/self-host)
|
||||
2. Deploy to your preferred platform
|
||||
3. Create a project
|
||||
4. Note your instance URL and API key
|
||||
|
||||
### 2. Configure Environment Variables
|
||||
|
||||
#### Development
|
||||
1. Copy `.env.example` to `.env` in `apps/web`:
|
||||
```bash
|
||||
cd apps/web
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Add your PostHog credentials:
|
||||
```bash
|
||||
PUBLIC_POSTHOG_KEY=phc_your_actual_api_key
|
||||
PUBLIC_POSTHOG_HOST=https://us.i.posthog.com # or eu.i.posthog.com
|
||||
```
|
||||
|
||||
#### Production
|
||||
Add environment variables to your deployment platform:
|
||||
|
||||
**Vercel:**
|
||||
```bash
|
||||
vercel env add PUBLIC_POSTHOG_KEY
|
||||
vercel env add PUBLIC_POSTHOG_HOST
|
||||
```
|
||||
|
||||
**Netlify:**
|
||||
- Go to Site settings → Environment variables
|
||||
- Add `PUBLIC_POSTHOG_KEY` and `PUBLIC_POSTHOG_HOST`
|
||||
|
||||
**Other platforms:**
|
||||
- Follow your platform's documentation for environment variables
|
||||
- Ensure variables are prefixed with `PUBLIC_`
|
||||
|
||||
### 3. Verify Integration
|
||||
|
||||
1. Start the development server:
|
||||
```bash
|
||||
cd apps/web
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
2. Open browser DevTools → Console
|
||||
3. Look for "PostHog loaded" message (in dev mode)
|
||||
4. Navigate between pages
|
||||
5. Log in with a test account
|
||||
6. Check PostHog dashboard for events
|
||||
|
||||
## Usage
|
||||
|
||||
### Tracking Custom Events
|
||||
|
||||
The analytics helper provides easy-to-use methods:
|
||||
|
||||
```typescript
|
||||
import { analytics } from '$lib/analytics/posthog';
|
||||
|
||||
// Track a custom event
|
||||
analytics.track('button_clicked', {
|
||||
button_name: 'sign_up',
|
||||
page: '/pricing'
|
||||
});
|
||||
|
||||
// Track image generation
|
||||
analytics.track('image_generated', {
|
||||
model: 'flux-dev',
|
||||
prompt_length: 150,
|
||||
aspect_ratio: '16:9'
|
||||
});
|
||||
|
||||
// Track feature usage
|
||||
analytics.track('feature_used', {
|
||||
feature: 'batch_generation',
|
||||
count: 5
|
||||
});
|
||||
```
|
||||
|
||||
### User Properties
|
||||
|
||||
Set additional user properties:
|
||||
|
||||
```typescript
|
||||
import { analytics } from '$lib/analytics/posthog';
|
||||
|
||||
analytics.setUserProperties({
|
||||
plan: 'pro',
|
||||
images_generated: 42,
|
||||
favorite_model: 'flux-dev'
|
||||
});
|
||||
```
|
||||
|
||||
### Feature Flags
|
||||
|
||||
Use feature flags for gradual rollouts and A/B testing:
|
||||
|
||||
```typescript
|
||||
import { analytics } from '$lib/analytics/posthog';
|
||||
|
||||
// Check if a feature is enabled
|
||||
const showNewUI = analytics.isFeatureEnabled('new_ui_redesign');
|
||||
|
||||
// Get feature flag value (for multivariate flags)
|
||||
const buttonColor = analytics.getFeatureFlag('button_color_test');
|
||||
```
|
||||
|
||||
### Page View Tracking
|
||||
|
||||
Page views are tracked automatically. For manual tracking:
|
||||
|
||||
```typescript
|
||||
import { analytics } from '$lib/analytics/posthog';
|
||||
|
||||
// Track a specific page view
|
||||
analytics.pageView('/custom-page');
|
||||
```
|
||||
|
||||
## Example Use Cases
|
||||
|
||||
### Track Image Generation
|
||||
|
||||
```typescript
|
||||
// In your image generation component
|
||||
import { analytics } from '$lib/analytics/posthog';
|
||||
|
||||
async function generateImage(prompt: string, settings: Settings) {
|
||||
// Track the generation attempt
|
||||
analytics.track('image_generation_started', {
|
||||
model: settings.model,
|
||||
prompt_length: prompt.length,
|
||||
aspect_ratio: settings.aspectRatio,
|
||||
num_images: settings.numImages
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await generateImageAPI(prompt, settings);
|
||||
|
||||
// Track success
|
||||
analytics.track('image_generation_completed', {
|
||||
model: settings.model,
|
||||
generation_time_ms: result.duration
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
// Track errors
|
||||
analytics.track('image_generation_failed', {
|
||||
model: settings.model,
|
||||
error: error.message
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Track User Engagement
|
||||
|
||||
```typescript
|
||||
// Track when users interact with images
|
||||
analytics.track('image_liked', {
|
||||
image_id: image.id,
|
||||
model: image.model
|
||||
});
|
||||
|
||||
analytics.track('image_shared', {
|
||||
image_id: image.id,
|
||||
platform: 'twitter'
|
||||
});
|
||||
|
||||
analytics.track('image_downloaded', {
|
||||
image_id: image.id,
|
||||
format: 'png'
|
||||
});
|
||||
```
|
||||
|
||||
### Track Feature Discovery
|
||||
|
||||
```typescript
|
||||
// Track when users discover features
|
||||
analytics.track('feature_discovered', {
|
||||
feature: 'batch_generation',
|
||||
source: 'tooltip'
|
||||
});
|
||||
|
||||
analytics.track('tutorial_completed', {
|
||||
tutorial: 'first_generation'
|
||||
});
|
||||
```
|
||||
|
||||
## PostHog Dashboard
|
||||
|
||||
### Key Metrics to Track
|
||||
|
||||
1. **User Engagement**
|
||||
- Daily/Weekly/Monthly Active Users
|
||||
- Session duration
|
||||
- Pages per session
|
||||
|
||||
2. **Feature Usage**
|
||||
- Image generation count
|
||||
- Most used models
|
||||
- Popular aspect ratios
|
||||
- Batch generation usage
|
||||
|
||||
3. **User Journey**
|
||||
- Signup → First generation time
|
||||
- Feature adoption rate
|
||||
- Retention cohorts
|
||||
|
||||
4. **Errors & Issues**
|
||||
- Generation failures
|
||||
- Error rates by model
|
||||
- API timeout frequency
|
||||
|
||||
### Creating Insights
|
||||
|
||||
1. **Funnel Analysis**
|
||||
```
|
||||
Sign Up → First Generation → Image Download → Share
|
||||
```
|
||||
|
||||
2. **Retention**
|
||||
```
|
||||
Track users who return after first generation
|
||||
```
|
||||
|
||||
3. **Trends**
|
||||
```
|
||||
Image generations over time
|
||||
Model popularity trends
|
||||
```
|
||||
|
||||
## Feature Flags Setup
|
||||
|
||||
### Create a Feature Flag
|
||||
|
||||
1. Go to PostHog → Feature Flags
|
||||
2. Click "New feature flag"
|
||||
3. Set flag key (e.g., `new_ui_redesign`)
|
||||
4. Configure rollout percentage
|
||||
5. Save and deploy
|
||||
|
||||
### Use in Code
|
||||
|
||||
```svelte
|
||||
<script lang="ts">
|
||||
import { analytics } from '$lib/analytics/posthog';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let showNewUI = false;
|
||||
|
||||
onMount(() => {
|
||||
showNewUI = analytics.isFeatureEnabled('new_ui_redesign');
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if showNewUI}
|
||||
<NewUIComponent />
|
||||
{:else}
|
||||
<OldUIComponent />
|
||||
{/if}
|
||||
```
|
||||
|
||||
## Session Recordings (Optional)
|
||||
|
||||
Session recordings are **disabled by default** for privacy. To enable:
|
||||
|
||||
1. Update `apps/web/src/lib/analytics/posthog.ts`:
|
||||
```typescript
|
||||
disable_session_recording: false, // Enable recordings
|
||||
```
|
||||
|
||||
2. Configure in PostHog dashboard:
|
||||
- Set recording sample rate
|
||||
- Configure privacy settings
|
||||
- Set up retention period
|
||||
|
||||
3. Add privacy notice to your app
|
||||
|
||||
## Privacy & Compliance
|
||||
|
||||
### GDPR Compliance
|
||||
- ✅ Only tracks identified (logged-in) users
|
||||
- ✅ Users can opt-out via PostHog settings
|
||||
- ✅ Data retention policies can be configured
|
||||
- ✅ Personal data can be deleted on request
|
||||
|
||||
### User Opt-Out
|
||||
|
||||
To allow users to opt-out:
|
||||
|
||||
```typescript
|
||||
import posthog from '$lib/analytics/posthog';
|
||||
|
||||
// Opt user out
|
||||
posthog.opt_out_capturing();
|
||||
|
||||
// Opt user back in
|
||||
posthog.opt_in_capturing();
|
||||
```
|
||||
|
||||
### Data Deletion
|
||||
|
||||
To delete user data (on account deletion):
|
||||
|
||||
```typescript
|
||||
import { analytics } from '$lib/analytics/posthog';
|
||||
|
||||
// Reset all user data
|
||||
analytics.reset();
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### PostHog Not Loading
|
||||
- ✅ Verify `PUBLIC_POSTHOG_KEY` is set correctly
|
||||
- ✅ Check `PUBLIC_POSTHOG_HOST` matches your region
|
||||
- ✅ Ensure environment variables have `PUBLIC_` prefix
|
||||
- ✅ Restart dev server after adding env vars
|
||||
|
||||
### No Events in Dashboard
|
||||
- ✅ Verify API key matches PostHog project
|
||||
- ✅ Check browser console for errors
|
||||
- ✅ Ensure user is logged in (only tracks identified users)
|
||||
- ✅ Check ad blocker isn't blocking requests
|
||||
|
||||
### Feature Flags Not Working
|
||||
- ✅ Wait a few minutes after creating flag
|
||||
- ✅ Verify flag key matches code
|
||||
- ✅ Check user is identified
|
||||
- ✅ Ensure flag is enabled in PostHog
|
||||
|
||||
### Development vs Production
|
||||
- **Development**: All events are tracked, console logging enabled
|
||||
- **Production**: Production mode, no console logs
|
||||
- **Testing**: Use a separate PostHog project for testing
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Custom Initialization
|
||||
|
||||
To customize PostHog initialization, edit `apps/web/src/lib/analytics/posthog.ts`:
|
||||
|
||||
```typescript
|
||||
posthog.init(PUBLIC_POSTHOG_KEY, {
|
||||
api_host: PUBLIC_POSTHOG_HOST,
|
||||
|
||||
// Capture settings
|
||||
capture_pageview: true,
|
||||
capture_pageleave: true,
|
||||
|
||||
// Session settings
|
||||
session_recording: {
|
||||
recordCrossOriginIframes: false,
|
||||
maskAllInputs: true,
|
||||
maskTextSelector: '.sensitive'
|
||||
},
|
||||
|
||||
// Privacy settings
|
||||
respect_dnt: true,
|
||||
opt_out_capturing_by_default: false,
|
||||
|
||||
// Performance
|
||||
persistence: 'localStorage',
|
||||
autocapture: false,
|
||||
|
||||
// Advanced features
|
||||
enable_recording_console_log: true,
|
||||
disable_compression: false
|
||||
});
|
||||
```
|
||||
|
||||
### Proxy Setup (Avoid Ad Blockers)
|
||||
|
||||
For production, proxy PostHog through your domain:
|
||||
|
||||
1. Set up a reverse proxy (e.g., Cloudflare Worker)
|
||||
2. Update `PUBLIC_POSTHOG_HOST` to your proxy URL
|
||||
3. Configure CORS headers
|
||||
|
||||
Example Cloudflare Worker:
|
||||
```javascript
|
||||
addEventListener('fetch', event => {
|
||||
event.respondWith(handleRequest(event.request))
|
||||
})
|
||||
|
||||
async function handleRequest(request) {
|
||||
const url = new URL(request.url)
|
||||
url.hostname = 'us.i.posthog.com'
|
||||
|
||||
return fetch(url, {
|
||||
method: request.method,
|
||||
headers: request.headers,
|
||||
body: request.body
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [PostHog Documentation](https://posthog.com/docs)
|
||||
- [PostHog JavaScript SDK](https://posthog.com/docs/libraries/js)
|
||||
- [Feature Flags Guide](https://posthog.com/docs/feature-flags)
|
||||
- [Session Recordings](https://posthog.com/docs/session-replay)
|
||||
- [Privacy Controls](https://posthog.com/docs/privacy)
|
||||
|
||||
## Support
|
||||
|
||||
For issues specific to:
|
||||
- **PostHog Platform**: [PostHog Support](https://posthog.com/support)
|
||||
- **Integration Code**: Create an issue in this repository
|
||||
- **Privacy/Compliance**: Consult with your legal team
|
||||
|
|
@ -774,7 +774,7 @@ services:
|
|||
- synapse_data:/data
|
||||
ports:
|
||||
- "8008:8008"
|
||||
- "9000:9000"
|
||||
- "9002:9002" # Metrics (9000 used by MinIO)
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-fSs", "http://localhost:8008/health"]
|
||||
interval: 30s
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ app_service_config_files: []
|
|||
|
||||
report_stats: false
|
||||
enable_metrics: true
|
||||
metrics_port: 9000
|
||||
metrics_port: 9002
|
||||
|
||||
# ============================================
|
||||
# Caching
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ Memoro transformiert Audio-Aufnahmen in strukturierte, durchsuchbare Inhalte mit
|
|||
| Audio | expo-audio, Azure Speech Services |
|
||||
| State | Zustand |
|
||||
| Payments | RevenueCat |
|
||||
| Analytics | PostHog, Sentry |
|
||||
| Analytics | Umami (self-hosted) |
|
||||
| i18n | react-i18next (32 Sprachen) |
|
||||
|
||||
#### Projektstruktur
|
||||
|
|
|
|||
3338
pnpm-lock.yaml
generated
3338
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue