Onboarding

This commit is contained in:
2026-05-08 13:00:30 +02:00
parent d37b49f1f6
commit 9386ae1be7
37 changed files with 5606 additions and 2275 deletions

View File

@@ -25,8 +25,9 @@ loadEnvFiles([
path.join(__dirname, '.env.local'),
]);
const { closeDatabase, getDefaultDbPath, openDatabase, get } = require('./lib/postgres');
const { closeDatabase, getDefaultDbPath, openDatabase, get } = require('./lib/postgres');
const {
deleteAccount: authDeleteAccount,
ensureAuthSchema,
signUp: authSignUp,
login: authLogin,
@@ -70,10 +71,10 @@ const app = express();
const port = Number(process.env.PORT || 3000);
const plantsPublicDir = path.join(__dirname, 'public', 'plants');
const SCAN_PRIMARY_COST = 1;
const SCAN_REVIEW_COST = 1;
const SEMANTIC_SEARCH_COST = 2;
const HEALTH_CHECK_COST = 2;
const SCAN_PRIMARY_COST = 1;
const SCAN_REVIEW_COST = 0;
const SEMANTIC_SEARCH_COST = 2;
const HEALTH_CHECK_COST = 2;
const LOW_CONFIDENCE_REVIEW_THRESHOLD = 0.8;
let catalogCache = null;
@@ -525,6 +526,7 @@ app.get('/', (_request, response) => {
'POST /auth/signup',
'POST /auth/login',
'POST /auth/apple',
'DELETE /auth/account',
'GET /v1/billing/summary',
'POST /v1/billing/sync-revenuecat',
'POST /v1/scan',
@@ -909,11 +911,12 @@ app.post('/v1/health-check', async (request, response) => {
: language === 'es'
? 'Volver a escanear cuando la conexión sea estable.'
: 'Try scanning again when your connection is stable.';
const fallbackHealthCheck = {
generatedAt: nowIso(),
overallHealthScore: 50,
status: 'watch',
likelyIssues: [{
const fallbackHealthCheck = {
generatedAt: nowIso(),
overallHealthScore: 50,
status: 'watch',
analysisSummary: unavailableIssue,
likelyIssues: [{
title: language === 'de' ? 'Analyse nicht verfügbar' : language === 'es' ? 'Análisis no disponible' : 'Analysis unavailable',
confidence: 0.1,
details: unavailableIssue,
@@ -944,11 +947,12 @@ app.post('/v1/health-check', async (request, response) => {
);
}
const healthCheck = {
generatedAt: nowIso(),
overallHealthScore: analysis.overallHealthScore,
status: analysis.status,
likelyIssues: analysis.likelyIssues,
const healthCheck = {
generatedAt: nowIso(),
overallHealthScore: analysis.overallHealthScore,
status: analysis.status,
analysisSummary: analysis.analysisSummary,
likelyIssues: analysis.likelyIssues,
actionsNow: analysis.actionsNow,
plan7Days: analysis.plan7Days,
creditsCharged,
@@ -1081,7 +1085,27 @@ app.post('/auth/apple', async (request, response) => {
// ─── Startup ───────────────────────────────────────────────────────────────
const start = async () => {
app.delete('/auth/account', async (request, response) => {
try {
const authHeader = request.header('authorization') || request.header('Authorization') || '';
if (!authHeader.startsWith('Bearer ')) {
return response.status(401).json({ code: 'UNAUTHORIZED', message: 'Missing bearer token.' });
}
const payload = verifyJwt(authHeader.slice(7));
if (!payload?.sub) {
return response.status(401).json({ code: 'UNAUTHORIZED', message: 'Invalid bearer token.' });
}
await authDeleteAccount(db, String(payload.sub));
response.status(204).send();
} catch (error) {
const status = error.status || 500;
response.status(status).json({ code: error.code || 'SERVER_ERROR', message: error.message });
}
});
const start = async () => {
db = await openDatabase();
await ensurePlantSchema(db);
await ensureBillingSchema(db);