Files
SatsFaucet/backend/src/config.ts
Michilis dc7007f708 Add sponsors system with time slider, LNbits invoices, and UX improvements
- Sponsors table, LNbits createInvoice, webhook handler
- Sponsor routes: create, homepage, list, my-ads, click, extend, check-payment
- Admin routes for sponsor management
- Frontend: SponsorForm, SponsorTimeSlider, SponsorCard, SponsorsSection
- Sponsors page, My Ads page, homepage sponsor block
- Header login dropdown with My Ads, Create Sponsor
- Transactions integration for sponsor payments
- View/click tracking
- OG meta fetch for sponsor images
- Sponsor modal spacing, invoice polling fallback
- Remove Lightning address and Category fields from sponsor form

Made-with: Cursor
2026-03-16 00:01:19 +00:00

92 lines
4.0 KiB
TypeScript

import "dotenv/config";
function env(key: string, defaultValue?: string): string {
const v = process.env[key];
if (v !== undefined) return v;
if (defaultValue !== undefined) return defaultValue;
throw new Error(`Missing required env: ${key}`);
}
function envInt(key: string, defaultValue: number): number {
const v = process.env[key];
if (v === undefined) return defaultValue;
const n = parseInt(v, 10);
if (Number.isNaN(n)) throw new Error(`Invalid integer env: ${key}`);
return n;
}
function envBool(key: string, defaultValue: boolean): boolean {
const v = process.env[key];
if (v === undefined) return defaultValue;
return v === "true" || v === "1";
}
export const config = {
port: envInt("PORT", 3001),
trustProxy: envBool("TRUST_PROXY", false),
/** Public path prefix when behind a reverse proxy that strips it (e.g. nginx /api/ -> backend /). */
publicBasePath: (process.env.PUBLIC_BASE_PATH ?? "").replace(/\/$/, ""),
/** Host of the API subdomain (e.g. faucetapi.lnpulse.app). When request Host matches, OpenAPI server URL is /. */
apiHost: (process.env.API_HOST ?? "faucetapi.lnpulse.app").split(":")[0],
allowedOrigins: (process.env.ALLOWED_ORIGINS ?? process.env.FRONTEND_URL ?? "http://localhost:5173,http://localhost:5174").split(",").map((s) => s.trim()),
// Database: omit DATABASE_URL for SQLite; set for Postgres
databaseUrl: process.env.DATABASE_URL as string | undefined,
sqlitePath: process.env.SQLITE_PATH ?? "./data/faucet.db",
// Security
hmacIpSecret: env("HMAC_IP_SECRET"),
jwtSecret: env("JWT_SECRET", process.env.HMAC_IP_SECRET ?? ""),
jwtExpiresInSeconds: envInt("JWT_EXPIRES_IN_SECONDS", 86400 * 7), // 7 days
nip98MaxSkewSeconds: envInt("NIP98_MAX_SKEW_SECONDS", 300),
nonceTtlSeconds: envInt("NONCE_TTL_SECONDS", 600),
// Faucet economics
faucetEnabled: envBool("FAUCET_ENABLED", true),
emergencyStop: envBool("EMERGENCY_STOP", false),
faucetMinSats: envInt("FAUCET_MIN_SATS", 1),
faucetMaxSats: envInt("FAUCET_MAX_SATS", 5),
dailyBudgetSats: envInt("DAILY_BUDGET_SATS", 10000),
maxClaimsPerDay: envInt("MAX_CLAIMS_PER_DAY", 100),
minWalletBalanceSats: envInt("MIN_WALLET_BALANCE_SATS", 1000),
// Eligibility
minAccountAgeDays: envInt("MIN_ACCOUNT_AGE_DAYS", 14),
minActivityScore: envInt("MIN_ACTIVITY_SCORE", 30),
minNotesCount: envInt("MIN_NOTES_COUNT", 5),
minFollowingCount: envInt("MIN_FOLLOWING_COUNT", 10),
minFollowersCount: envInt("MIN_FOLLOWERS_COUNT", 0),
activityLookbackDays: envInt("ACTIVITY_LOOKBACK_DAYS", 90),
// Cooldowns
cooldownDays: envInt("COOLDOWN_DAYS", 7),
ipCooldownDays: envInt("IP_COOLDOWN_DAYS", 7),
maxClaimsPerIpPerPeriod: envInt("MAX_CLAIMS_PER_IP_PER_PERIOD", 1),
// Nostr (defaults include relays common for remote signers / NIP-05)
nostrRelays: (process.env.NOSTR_RELAYS ?? "wss://relay.damus.io,wss://relay.nostr.band,wss://relay.getalby.com,wss://nos.lol").split(",").map((s) => s.trim()),
relayTimeoutMs: envInt("RELAY_TIMEOUT_MS", 5000),
maxEventsFetch: envInt("MAX_EVENTS_FETCH", 500),
metadataCacheHours: envInt("METADATA_CACHE_HOURS", 24),
// LNbits
lnbitsBaseUrl: env("LNBITS_BASE_URL").replace(/\/$/, ""),
lnbitsAdminKey: env("LNBITS_ADMIN_KEY"),
lnbitsWalletId: env("LNBITS_WALLET_ID"),
depositLightningAddress: process.env.DEPOSIT_LIGHTNING_ADDRESS ?? "",
depositLnurlp: process.env.DEPOSIT_LNURLP ?? "",
cashuRedeemApiUrl: (process.env.CASHU_REDEEM_API_URL ?? "https://cashu-redeem.azzamo.net").replace(/\/$/, ""),
// Sponsors
baseSponsorPricePerDay: envInt("BASE_SPONSOR_PRICE_PER_DAY", 200),
sponsorMaxActivePerUser: envInt("SPONSOR_MAX_ACTIVE_PER_USER", 5),
sponsorMaxVisible: envInt("SPONSOR_MAX_VISIBLE", 6),
adminPubkeys: (process.env.ADMIN_PUBKEYS ?? "").split(",").map((s) => s.trim()).filter(Boolean),
/** Public API base URL for LNbits webhook (e.g. https://api.example.com). Required for sponsor payments. */
publicApiUrl: (process.env.PUBLIC_API_URL ?? process.env.FRONTEND_URL ?? "").replace(/\/$/, ""),
};
export function usePostgres(): boolean {
return Boolean(config.databaseUrl);
}