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>
91 lines
2.3 KiB
JavaScript
91 lines
2.3 KiB
JavaScript
import https from 'https';
|
|
|
|
const EN_KEYWORDS = [
|
|
'receipt scanner',
|
|
'receipt to excel',
|
|
'receipt scanner to excel',
|
|
'expense tracker',
|
|
'receipt keeper',
|
|
'invoice scanner',
|
|
'tax receipts',
|
|
'ocr receipt scanner',
|
|
'smart receipts',
|
|
'mileage and receipts',
|
|
'receipt organizer',
|
|
'bookkeeping scanner',
|
|
'scan receipts for taxes',
|
|
'extract receipt to csv',
|
|
'business expense tracker'
|
|
];
|
|
|
|
const DE_KEYWORDS = [
|
|
'beleg scanner',
|
|
'belege digitalisieren',
|
|
'rechnung scanner',
|
|
'kassenbon scanner',
|
|
'quittung scanner',
|
|
'spesen app',
|
|
'buchhaltung scanner',
|
|
'datev scanner',
|
|
'receipt to excel',
|
|
'haushaltsbuch',
|
|
'ausgaben tracker',
|
|
'steuer belege',
|
|
'belegmanager'
|
|
];
|
|
|
|
const COUNTRIES = {
|
|
US: 'us',
|
|
GB: 'gb',
|
|
CA: 'ca',
|
|
AU: 'au',
|
|
DE: 'de'
|
|
};
|
|
|
|
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));
|
|
} catch {
|
|
resolve({ results: [] });
|
|
}
|
|
});
|
|
}).on('error', () => resolve({ results: [] }));
|
|
});
|
|
}
|
|
|
|
async function analyze() {
|
|
console.log("Analyzing Multi-Country App Store data...");
|
|
const countryResults = {};
|
|
|
|
for (const [cName, cCode] of Object.entries(COUNTRIES)) {
|
|
countryResults[cName] = {};
|
|
const kws = cName === 'DE' ? DE_KEYWORDS : EN_KEYWORDS;
|
|
for (const kw of kws) {
|
|
const data = await fetchSearch(kw, cCode);
|
|
const apps = data.results || [];
|
|
const top5 = apps.slice(0, 5);
|
|
const top5Reviews = top5.reduce((sum, a) => sum + (a.userRatingCount || 0), 0);
|
|
const avgTop5Reviews = Math.round(top5Reviews / (top5.length || 1));
|
|
const top1 = top5[0] || {};
|
|
|
|
countryResults[cName][kw] = {
|
|
resultCount: data.resultCount,
|
|
top1Name: top1.trackName || 'N/A',
|
|
top1Reviews: top1.userRatingCount || 0,
|
|
avgTop5Reviews,
|
|
};
|
|
await new Promise(r => setTimeout(r, 100));
|
|
}
|
|
}
|
|
|
|
console.log(JSON.stringify(countryResults, null, 2));
|
|
}
|
|
|
|
analyze();
|