generated from knso/webklar-preview-template
Release-Vorbereitung: Proxy, Kostenoptimierung, Freemium, Store-Assets
- Backend-Proxy (Cloudflare Worker in proxy/): API-Key serverseitig, Modell-Whitelist, max_tokens-Deckel, Tageslimits pro Gerät und IP - Modell-Split: Analyse auf Sonnet 5, Übersetzung/Antwort auf Haiku 4.5 - Bild-Downscaling auf 2000px vor dem Upload (ScanScreen) - Expo SDK 56 -> 57 Upgrade - Freemium: 3 Gratis-Analysen mit lokalem Zähler und Paywall-Hinweis - Einstellungen: API-Key-Feld nur noch im Dev-Modus (NUTZT_PROXY) - Build-Setup: Bundle-IDs, eas.json, Splash-Screen-Plugin - Tests: jest-expo + 17 Tests für die Ampel-/Fristlogik - Rechtliches: Datenschutzerklärung (Entwurf), Webseite (webseite/, live auf behoerdenklar.pages.dev), Store-Texte (store/) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
88c49e1b54
commit
9dc1d8d87b
26 files changed
+6587
-480
No files matched your search
+55
-1
@@ -14,6 +14,16 @@ import { BriefEintrag } from '../types';
|
||||
const KEY_API = 'anthropic_api_key';
|
||||
const KEY_BRIEFE = 'behoerdenklar_briefe';
|
||||
const KEY_CONSENT = 'behoerdenklar_consent';
|
||||
const KEY_GERAETE_ID = 'behoerdenklar_geraete_id';
|
||||
const KEY_SCAN_ANZAHL = 'behoerdenklar_scan_anzahl';
|
||||
|
||||
/**
|
||||
* Freemium: so viele Brief-Analysen sind kostenlos. Danach braucht es das
|
||||
* Abo (kommt mit dem IAP-Einbau). Der Zähler liegt lokal — das ist bewusst
|
||||
* "weich" (Neuinstallation setzt ihn zurück); die harte Grenze ist das
|
||||
* Tageslimit des Backend-Proxys. Echte Abo-Prüfung folgt serverseitig.
|
||||
*/
|
||||
export const GRATIS_ANALYSEN = 3;
|
||||
|
||||
// ---- API-Key (sensibel -> SecureStore) ----
|
||||
|
||||
@@ -29,6 +39,42 @@ export async function speichereApiKey(key: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Geräte-ID (anonym, nur fürs Tageslimit des Backend-Proxys) ----
|
||||
|
||||
/**
|
||||
* Liefert eine zufällige, anonyme Geräte-ID (wird beim ersten Aufruf erzeugt).
|
||||
* Sie enthält keine Gerätedaten und dient dem Proxy ausschließlich dazu,
|
||||
* das Anfrage-Tageslimit pro Installation durchzusetzen.
|
||||
*/
|
||||
export async function holeGeraeteId(): Promise<string> {
|
||||
let id = await AsyncStorage.getItem(KEY_GERAETE_ID);
|
||||
if (!id) {
|
||||
// Math.random reicht hier: die ID ist kein Geheimnis, nur ein Zähl-Schlüssel
|
||||
id =
|
||||
'g_' +
|
||||
Array.from({ length: 24 }, () =>
|
||||
Math.floor(Math.random() * 36).toString(36)
|
||||
).join('');
|
||||
await AsyncStorage.setItem(KEY_GERAETE_ID, id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
// ---- Freemium-Zähler ----
|
||||
|
||||
/** Wie viele Brief-Analysen wurden auf diesem Gerät schon durchgeführt? */
|
||||
export async function holeAnzahlAnalysen(): Promise<number> {
|
||||
const roh = await AsyncStorage.getItem(KEY_SCAN_ANZAHL);
|
||||
const anzahl = roh ? parseInt(roh, 10) : 0;
|
||||
return Number.isFinite(anzahl) ? anzahl : 0;
|
||||
}
|
||||
|
||||
/** Nach jeder erfolgreichen Analyse aufrufen. */
|
||||
export async function zaehleAnalyse(): Promise<void> {
|
||||
const bisher = await holeAnzahlAnalysen();
|
||||
await AsyncStorage.setItem(KEY_SCAN_ANZAHL, String(bisher + 1));
|
||||
}
|
||||
|
||||
// ---- Einwilligung (Datenschutz-Consent) ----
|
||||
|
||||
export async function holeConsent(): Promise<boolean> {
|
||||
@@ -59,6 +105,14 @@ export async function speichereBriefe(briefe: BriefEintrag[]): Promise<void> {
|
||||
// ---- Alles löschen (Datenschutz-Anforderung) ----
|
||||
|
||||
export async function loescheAlleDaten(): Promise<void> {
|
||||
await AsyncStorage.multiRemove([KEY_BRIEFE, KEY_CONSENT]);
|
||||
// "Alle Daten löschen" heißt wirklich alle — auch Zähler und Geräte-ID.
|
||||
// Dass damit das Gratis-Kontingent zurücksetzbar ist, nehmen wir in Kauf
|
||||
// (Neuinstallation könnte das ohnehin); der Proxy limitiert weiterhin.
|
||||
await AsyncStorage.multiRemove([
|
||||
KEY_BRIEFE,
|
||||
KEY_CONSENT,
|
||||
KEY_GERAETE_ID,
|
||||
KEY_SCAN_ANZAHL,
|
||||
]);
|
||||
await SecureStore.deleteItemAsync(KEY_API);
|
||||
}
|
||||
Reference in new issue
Block a user