feat(lifecycle): add billing lifecycle with auto-suspend/delete

- New AppLifecycleService with cron-based scanner (60s interval)
- State machine: ACTIVE → SUSPENDED → PENDING_DELETION → DELETED
- Auto-renew hourly plans from wallet
- Admin-configurable grace periods via PlatformSettings table
- New LifecycleController (GET/PATCH /lifecycle/settings)
- PlatformSetting entity for runtime admin config
- BillingService: calculateCostForApp, deductWallet
- Application entity: planId, billingCycle, lifecycleStatus, planExpiresAt
- New enums: AppLifecycleStatus, BillingCycle
This commit is contained in:
keyhan
2026-04-22 16:44:20 +03:30
parent 0e408d1baa
commit 8ca787d46e
11 changed files with 562 additions and 11 deletions
+20 -1
View File
@@ -23,7 +23,7 @@ export default () => ({
},
registry: {
url: process.env.REGISTRY_URL || 'registry.example.com',
url: process.env.REGISTRY_URL || 'registry.cloudhost-builds.svc.cluster.local:5000',
pullUrl: process.env.REGISTRY_PULL_URL || process.env.REGISTRY_URL || 'localhost:30500',
username: process.env.REGISTRY_USERNAME || 'admin',
password: process.env.REGISTRY_PASSWORD || '',
@@ -38,4 +38,23 @@ export default () => ({
domain: process.env.PLATFORM_DOMAIN || 'apps.cloudhost.local',
uploadDir: process.env.UPLOAD_DIR || './uploads',
},
// Lifecycle defaults (can be overridden via PlatformSettings entity by admin)
lifecycle: {
// How long after plan expires to suspend the app (scale to 0)
hourly: {
suspendAfterMs: parseInt(process.env.LIFECYCLE_HOURLY_SUSPEND_MS || String(0), 10), // immediately on expiry
deleteAfterMs: parseInt(process.env.LIFECYCLE_HOURLY_DELETE_MS || String(24 * 3600 * 1000), 10), // 24h after suspend
},
monthly: {
suspendAfterMs: parseInt(process.env.LIFECYCLE_MONTHLY_SUSPEND_MS || String(0), 10),
deleteAfterMs: parseInt(process.env.LIFECYCLE_MONTHLY_DELETE_MS || String(3 * 24 * 3600 * 1000), 10), // 3 days
},
yearly: {
suspendAfterMs: parseInt(process.env.LIFECYCLE_YEARLY_SUSPEND_MS || String(0), 10),
deleteAfterMs: parseInt(process.env.LIFECYCLE_YEARLY_DELETE_MS || String(7 * 24 * 3600 * 1000), 10), // 7 days
},
// Cron interval for the lifecycle scanner
scanIntervalMs: parseInt(process.env.LIFECYCLE_SCAN_INTERVAL_MS || String(60_000), 10), // 1 min
},
});