mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-28 08:37:42 +02:00
chore(analytics): Umami aus i18n, CSP, website-blocks-Feature, infra (Welle D)
i18n×5 (settings-footnote → 'kein Web-Analytics'), security-headers CSP (stats.mana.how raus, GlitchTip bleibt), website-blocks (Provider-Enum 'umami' raus, plausible bleibt; Analytics/Inspector/Test), privacy-faq DE/EN, infra gpu-box .env/compose/README. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
394e520a26
commit
3ea8703a94
14 changed files with 20 additions and 36 deletions
|
|
@ -69,8 +69,8 @@ export function getPrivacyFAQs(locale: string, options: PrivacyFAQOptions): FAQI
|
|||
? 'Wie unabhängig ist Mana von großen Tech-Konzernen?'
|
||||
: 'How independent is Mana from big tech companies?',
|
||||
answer: isDE
|
||||
? '<p>Mana ist bewusst <strong>technologisch unabhängig</strong> aufgebaut:</p><ul><li><strong>Eigene Server</strong>: Alle Dienste laufen auf einem eigenen Mac Mini Server — kein AWS, kein Google Cloud, kein Azure</li><li><strong>Eigene KI</strong>: Lokale KI-Modelle (Gemma, Qwen, LLaVA) laufen auf unserem eigenen GPU-Server mit NVIDIA RTX 3090 — deine Daten verlassen nie unsere Infrastruktur</li><li><strong>Keine Google/Apple-Anmeldung</strong>: Eigenes Auth-System (Mana Core Auth) — kein OAuth über Drittanbieter, keine Tracking-Cookies von Google oder Facebook</li><li><strong>Eigene Suche</strong>: SearXNG Meta-Suchmaschine statt Google Search API</li><li><strong>Eigener Speicher</strong>: MinIO (S3-kompatibel) statt AWS S3 oder Google Cloud Storage</li><li><strong>Eigene Datenbank</strong>: PostgreSQL auf eigenem Server statt Cloud-Datenbanken</li><li><strong>Keine Tracking-SDKs</strong>: Kein Google Analytics, kein Facebook Pixel, kein Amplitude — eigene Analytics mit Umami</li></ul><p>Das Ziel: Ein digitales Zuhause, das dir gehört — nicht Big Tech.</p>'
|
||||
: '<p>Mana is deliberately built to be <strong>technologically independent</strong>:</p><ul><li><strong>Own servers</strong>: All services run on a dedicated Mac Mini server — no AWS, no Google Cloud, no Azure</li><li><strong>Own AI</strong>: Local AI models (Gemma, Qwen, LLaVA) run on our own GPU server with NVIDIA RTX 3090 — your data never leaves our infrastructure</li><li><strong>No Google/Apple login</strong>: Own auth system (Mana Core Auth) — no OAuth via third parties, no tracking cookies from Google or Facebook</li><li><strong>Own search</strong>: SearXNG meta-search engine instead of Google Search API</li><li><strong>Own storage</strong>: MinIO (S3-compatible) instead of AWS S3 or Google Cloud Storage</li><li><strong>Own database</strong>: PostgreSQL on own server instead of cloud databases</li><li><strong>No tracking SDKs</strong>: No Google Analytics, no Facebook Pixel, no Amplitude — own analytics with Umami</li></ul><p>The goal: A digital home that belongs to you — not big tech.</p>',
|
||||
? '<p>Mana ist bewusst <strong>technologisch unabhängig</strong> aufgebaut:</p><ul><li><strong>Eigene Server</strong>: Alle Dienste laufen auf einem eigenen Mac Mini Server — kein AWS, kein Google Cloud, kein Azure</li><li><strong>Eigene KI</strong>: Lokale KI-Modelle (Gemma, Qwen, LLaVA) laufen auf unserem eigenen GPU-Server mit NVIDIA RTX 3090 — deine Daten verlassen nie unsere Infrastruktur</li><li><strong>Keine Google/Apple-Anmeldung</strong>: Eigenes Auth-System (Mana Core Auth) — kein OAuth über Drittanbieter, keine Tracking-Cookies von Google oder Facebook</li><li><strong>Eigene Suche</strong>: SearXNG Meta-Suchmaschine statt Google Search API</li><li><strong>Eigener Speicher</strong>: MinIO (S3-kompatibel) statt AWS S3 oder Google Cloud Storage</li><li><strong>Eigene Datenbank</strong>: PostgreSQL auf eigenem Server statt Cloud-Datenbanken</li><li><strong>Keine Tracking-SDKs</strong>: Kein Google Analytics, kein Facebook Pixel, kein Amplitude — und gar kein Web-Analytics</li></ul><p>Das Ziel: Ein digitales Zuhause, das dir gehört — nicht Big Tech.</p>'
|
||||
: '<p>Mana is deliberately built to be <strong>technologically independent</strong>:</p><ul><li><strong>Own servers</strong>: All services run on a dedicated Mac Mini server — no AWS, no Google Cloud, no Azure</li><li><strong>Own AI</strong>: Local AI models (Gemma, Qwen, LLaVA) run on our own GPU server with NVIDIA RTX 3090 — your data never leaves our infrastructure</li><li><strong>No Google/Apple login</strong>: Own auth system (Mana Core Auth) — no OAuth via third parties, no tracking cookies from Google or Facebook</li><li><strong>Own search</strong>: SearXNG meta-search engine instead of Google Search API</li><li><strong>Own storage</strong>: MinIO (S3-compatible) instead of AWS S3 or Google Cloud Storage</li><li><strong>Own database</strong>: PostgreSQL on own server instead of cloud databases</li><li><strong>No tracking SDKs</strong>: No Google Analytics, no Facebook Pixel, no Amplitude — and no web analytics at all</li></ul><p>The goal: A digital home that belongs to you — not big tech.</p>',
|
||||
category: 'privacy',
|
||||
order: 96,
|
||||
language: isDE ? 'de' : 'en',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Shared security headers for SvelteKit web apps.
|
||||
*
|
||||
* Sets standard security headers (CSP, X-Frame-Options, etc.)
|
||||
* with Umami analytics and GlitchTip error tracking pre-configured.
|
||||
* with GlitchTip error tracking pre-configured.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
|
|
@ -33,7 +33,7 @@ interface SecurityHeadersOptions {
|
|||
|
||||
/**
|
||||
* Set standard security headers on a Response object.
|
||||
* Includes Umami (stats.mana.how) and GlitchTip (glitchtip.mana.how) by default.
|
||||
* Includes GlitchTip (glitchtip.mana.how) by default.
|
||||
*/
|
||||
export function setSecurityHeaders(response: Response, options: SecurityHeadersOptions = {}): void {
|
||||
const {
|
||||
|
|
@ -67,10 +67,10 @@ export function setSecurityHeaders(response: Response, options: SecurityHeadersO
|
|||
// WebAssembly compilation, NOT eval()/new Function() — much narrower
|
||||
// than the legacy 'unsafe-eval' source. Supported by all evergreen
|
||||
// browsers.
|
||||
`script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' https://stats.mana.how https://glitchtip.mana.how ${scriptSrc.join(' ')}`.trim(),
|
||||
`script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' https://glitchtip.mana.how ${scriptSrc.join(' ')}`.trim(),
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
`img-src 'self' data: blob: https: ${imgSrc.join(' ')}`.trim(),
|
||||
`connect-src 'self' https://stats.mana.how https://glitchtip.mana.how ${connectSrc.join(' ')}`.trim(),
|
||||
`connect-src 'self' https://glitchtip.mana.how ${connectSrc.join(' ')}`.trim(),
|
||||
`font-src 'self' ${fontSrc.join(' ')}`.trim(),
|
||||
mediaSrc.length > 0 ? `media-src 'self' ${mediaSrc.join(' ')}`.trim() : '',
|
||||
"object-src 'none'",
|
||||
|
|
|
|||
|
|
@ -11,11 +11,6 @@
|
|||
if (block.props.scriptUrl) return block.props.scriptUrl;
|
||||
return 'https://plausible.io/js/script.js';
|
||||
});
|
||||
|
||||
const umamiSrc = $derived.by(() => {
|
||||
if (block.props.scriptUrl) return block.props.scriptUrl;
|
||||
return 'https://cloud.umami.is/script.js';
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if !isPublic}
|
||||
|
|
@ -30,8 +25,6 @@
|
|||
{:else if configured}
|
||||
{#if block.props.provider === 'plausible'}
|
||||
<script defer data-domain={block.props.siteKey} src={plausibleSrc}></script>
|
||||
{:else if block.props.provider === 'umami'}
|
||||
<script defer data-website-id={block.props.siteKey} src={umamiSrc}></script>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
if (provider === 'plausible') {
|
||||
return 'Trage hier die Domain ein, die du bei Plausible registriert hast (z.B. "meineseite.de"). Keine Cookies, DSGVO-konform.';
|
||||
}
|
||||
return 'Umami Website-ID (UUID). Keine Cookies, DSGVO-konform.';
|
||||
return 'Keine Cookies, DSGVO-konform.';
|
||||
});
|
||||
|
||||
const keyLabel = $derived(provider === 'plausible' ? 'Domain' : 'Website-ID');
|
||||
|
|
@ -25,7 +25,6 @@
|
|||
onchange={(e) => onChange({ provider: e.currentTarget.value as AnalyticsProps['provider'] })}
|
||||
>
|
||||
<option value="plausible">Plausible</option>
|
||||
<option value="umami">Umami</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,16 +2,15 @@ import { z } from 'zod';
|
|||
|
||||
/**
|
||||
* Analytics block — injects a tracking snippet into the published
|
||||
* page. Opt-in, no cookies by design (Plausible / Umami are
|
||||
* cookieless).
|
||||
* page. Opt-in, no cookies by design (Plausible is cookieless).
|
||||
*
|
||||
* The block renders nothing visible in edit/preview; in public mode
|
||||
* it emits a single <script> tag. No PII collection (no visitor IDs,
|
||||
* no fingerprinting), no admin UI access required.
|
||||
*/
|
||||
export const AnalyticsSchema = z.object({
|
||||
provider: z.enum(['plausible', 'umami']).default('plausible'),
|
||||
/** Plausible: the domain property; Umami: the website id (UUID). */
|
||||
provider: z.enum(['plausible']).default('plausible'),
|
||||
/** Plausible: the domain property. */
|
||||
siteKey: z.string().max(128).default(''),
|
||||
/**
|
||||
* Optional script-host override for self-hosted instances. Leave
|
||||
|
|
|
|||
|
|
@ -202,8 +202,8 @@ describe('moduleEmbed', () => {
|
|||
});
|
||||
|
||||
describe('analytics', () => {
|
||||
it('accepts both providers', () => {
|
||||
for (const provider of ['plausible', 'umami']) {
|
||||
it('accepts the provider', () => {
|
||||
for (const provider of ['plausible']) {
|
||||
expect(safeValidateSchema('analytics', { provider }).success).toBe(true);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue