Initial commit for Greenlens

This commit is contained in:
Timo Knuth
2026-03-16 21:31:46 +01:00
parent 307135671f
commit 05d4f6e78b
573 changed files with 54233 additions and 1891 deletions

View File

@@ -1,17 +1,17 @@
const { get, run } = require('./sqlite');
const FREE_MONTHLY_CREDITS = 15;
const PRO_MONTHLY_CREDITS = 50;
const PRO_MONTHLY_CREDITS = 250;
const TOPUP_DEFAULT_CREDITS = 60;
const TOPUP_CREDITS_BY_PRODUCT = {
pro_monthly: 0,
topup_small: 50,
topup_small: 25,
topup_medium: 120,
topup_large: 300,
};
const AVAILABLE_PRODUCTS = ['pro_monthly', 'topup_small', 'topup_medium', 'topup_large'];
const AVAILABLE_PRODUCTS = ['monthly_pro', 'yearly_pro', 'topup_small', 'topup_medium', 'topup_large'];
const nowIso = () => new Date().toISOString();
@@ -68,7 +68,7 @@ const normalizeAccountRow = (row) => {
return {
userId: String(row.userId),
plan: row.plan === 'pro' ? 'pro' : 'free',
provider: typeof row.provider === 'string' && row.provider ? row.provider : 'stripe',
provider: typeof row.provider === 'string' && row.provider ? row.provider : 'revenuecat',
cycleStartedAt: String(row.cycleStartedAt),
cycleEndsAt: String(row.cycleEndsAt),
monthlyAllowance: Number(row.monthlyAllowance) || FREE_MONTHLY_CREDITS,
@@ -84,7 +84,7 @@ const buildDefaultAccount = (userId, now) => {
return {
userId,
plan: 'free',
provider: 'stripe',
provider: 'revenuecat',
cycleStartedAt: cycleStartedAt.toISOString(),
cycleEndsAt: cycleEndsAt.toISOString(),
monthlyAllowance: FREE_MONTHLY_CREDITS,
@@ -305,6 +305,20 @@ const consumeCreditsWithIdempotency = async (db, userId, key, cost) => {
};
const getBillingSummary = async (db, userId) => {
if (userId === 'guest') {
return {
entitlement: { plan: 'free', provider: 'mock', status: 'active', renewsAt: null },
credits: {
monthlyAllowance: 5,
usedThisCycle: 0,
topupBalance: 0,
available: 5,
cycleStartedAt: nowIso(),
cycleEndsAt: nowIso()
},
availableProducts: AVAILABLE_PRODUCTS,
};
}
return runInTransaction(db, async () => {
const account = await getOrCreateAccount(db, userId);
account.updatedAt = nowIso();
@@ -314,6 +328,20 @@ const getBillingSummary = async (db, userId) => {
};
const getAccountSnapshot = async (db, userId) => {
if (userId === 'guest') {
return {
userId: 'guest',
plan: 'free',
provider: 'mock',
cycleStartedAt: nowIso(),
cycleEndsAt: nowIso(),
monthlyAllowance: 5,
usedThisCycle: 0,
topupBalance: 0,
renewsAt: null,
updatedAt: nowIso(),
};
}
return runInTransaction(db, async () => {
const account = await getOrCreateAccount(db, userId);
account.updatedAt = nowIso();
@@ -342,11 +370,11 @@ const simulatePurchase = async (db, userId, idempotencyKey, productId) => {
const account = await getOrCreateAccount(db, userId);
if (productId === 'pro_monthly') {
if (productId === 'monthly_pro' || productId === 'yearly_pro') {
const now = new Date();
const { cycleStartedAt, cycleEndsAt } = getCycleBounds(now);
account.plan = 'pro';
account.provider = 'stripe';
account.provider = 'revenuecat';
account.monthlyAllowance = PRO_MONTHLY_CREDITS;
account.usedThisCycle = 0;
account.cycleStartedAt = cycleStartedAt.toISOString();
@@ -441,7 +469,7 @@ const ensureBillingSchema = async (db) => {
`CREATE TABLE IF NOT EXISTS billing_accounts (
userId TEXT PRIMARY KEY,
plan TEXT NOT NULL DEFAULT 'free',
provider TEXT NOT NULL DEFAULT 'stripe',
provider TEXT NOT NULL DEFAULT 'revenuecat',
cycleStartedAt TEXT NOT NULL,
cycleEndsAt TEXT NOT NULL,
monthlyAllowance INTEGER NOT NULL DEFAULT 15,