Add full application: receipt scanning, auth, billing, and account deletion
Brings the working codebase (Next.js app, auth system, Stripe billing, Docker/deploy config, tests, docs) into version control on top of the placeholder initial commit, and adds account self-deletion (Danger Zone in Settings, password + typed-email confirmation, cascading DB cleanup, Stripe cancellation) per GDPR right-to-erasure. Excludes local build caches, node_modules, and internal agent scratch files; .gitignore hardened to keep those out going forward. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
63
scripts/analyze_zimmerpflanze.mjs
Normal file
63
scripts/analyze_zimmerpflanze.mjs
Normal file
@@ -0,0 +1,63 @@
|
||||
import https from 'https';
|
||||
|
||||
const keywords = [
|
||||
'zimmerpflanze',
|
||||
'zimmerpflanzen',
|
||||
'zimmerpflanzen pflege',
|
||||
'pflanzen bestimmen',
|
||||
'pflanzen app',
|
||||
'pflanzendoktor',
|
||||
'pflanzen gießen erinnerung',
|
||||
'houseplant',
|
||||
'plant care'
|
||||
];
|
||||
|
||||
const storefronts = [
|
||||
{ code: 'de', name: 'Deutschland' },
|
||||
{ code: 'at', name: 'Österreich' },
|
||||
{ code: 'ch', name: 'Schweiz' }
|
||||
];
|
||||
|
||||
function fetchSearch(term, country) {
|
||||
return new Promise((resolve) => {
|
||||
const url = `https://itunes.apple.com/search?term=${encodeURIComponent(term)}&country=${country}&entity=software&limit=10`;
|
||||
https.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' } }, (res) => {
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
resolve(JSON.parse(data).results || []);
|
||||
} catch {
|
||||
resolve([]);
|
||||
}
|
||||
});
|
||||
}).on('error', () => resolve([]));
|
||||
});
|
||||
}
|
||||
|
||||
async function run() {
|
||||
console.log("Analysiere 'Zimmerpflanze' und verwandte Keywords im App Store...\n");
|
||||
|
||||
for (const sf of storefronts) {
|
||||
console.log(`========================================`);
|
||||
console.log(`Storefront: ${sf.name} (${sf.code.toUpperCase()})`);
|
||||
console.log(`========================================`);
|
||||
|
||||
for (const kw of keywords) {
|
||||
const apps = await fetchSearch(kw, sf.code);
|
||||
const top5 = apps.slice(0, 5);
|
||||
const avgReviews = top5.length ? Math.round(top5.reduce((s, a) => s + (a.userRatingCount || 0), 0) / top5.length) : 0;
|
||||
const top1 = top5[0] || {};
|
||||
|
||||
console.log(`\nKeyword: "${kw}"`);
|
||||
console.log(`- Apps gefunden: ${apps.length}`);
|
||||
console.log(`- Top 1: ${top1.trackName || 'Keine'} (${top1.userRatingCount || 0} Reviews, ★${top1.averageUserRating?.toFixed(1) || 0})`);
|
||||
console.log(`- Ø Reviews Top 5: ${avgReviews.toLocaleString('de-DE')}`);
|
||||
|
||||
await new Promise(r => setTimeout(r, 150));
|
||||
}
|
||||
console.log("\n");
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
Reference in New Issue
Block a user