Add prepaid resource credits with prorated deploy billing.
When users delete an app before plan expiry, remaining resources become credits for a new deploy. The deploy calculator shows covered vs additional charges, prices optional services correctly, and prorates extras to days left on the credit. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -29,6 +29,8 @@ import { KubernetesService } from '../kubernetes/kubernetes.service';
|
||||
import { DeploymentsService } from '../deployments/deployments.service';
|
||||
import { AccessService } from '../access/access.service';
|
||||
import { CreateServiceAccessDto } from '../access/dto/service-access.dto';
|
||||
import { SnapshotsService } from '../snapshots/snapshots.service';
|
||||
import { BillingService } from '../billing/billing.service';
|
||||
|
||||
@ApiTags('Applications')
|
||||
@ApiBearerAuth()
|
||||
@@ -44,6 +46,10 @@ export class ApplicationsController {
|
||||
@Inject(forwardRef(() => DeploymentsService))
|
||||
private readonly deploymentsService: DeploymentsService,
|
||||
private readonly accessService: AccessService,
|
||||
@Inject(forwardRef(() => SnapshotsService))
|
||||
private readonly snapshotsService: SnapshotsService,
|
||||
@Inject(forwardRef(() => BillingService))
|
||||
private readonly billingService: BillingService,
|
||||
) {}
|
||||
|
||||
private isStaff(role: string): boolean {
|
||||
@@ -384,20 +390,19 @@ export class ApplicationsController {
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: 'Delete an application and all its resources' })
|
||||
@ApiOperation({ summary: 'Permanently delete an application and all its resources' })
|
||||
async delete(@Param('id') id: string, @Request() req: any) {
|
||||
const isStaff = req.user.role === UserRole.ADMIN || req.user.role === UserRole.TECHNICAL;
|
||||
// 1. Get the app first
|
||||
const app = await this.applicationsService.findOne(id, isStaff ? undefined : req.user.id);
|
||||
|
||||
// 2. Revoke temporary access grants
|
||||
const credit = await this.billingService.createCreditFromDeletedApp(app);
|
||||
|
||||
try {
|
||||
await this.accessService.revokeAllForApplication(app.id);
|
||||
} catch (e: any) {
|
||||
this.logger.warn(`Access grant cleanup failed for ${app.name}: ${e.message}`);
|
||||
}
|
||||
|
||||
// 3. Delete K8s resources (deployment, service, ingress, db, secrets, PVCs)
|
||||
try {
|
||||
await this.kubernetesService.deleteApplication(app);
|
||||
this.logger.log(`Deleted K8s resources for ${app.name}`);
|
||||
@@ -405,16 +410,23 @@ export class ApplicationsController {
|
||||
this.logger.warn(`K8s cleanup failed for ${app.name}: ${e.message}`);
|
||||
}
|
||||
|
||||
// 4. Delete deployment records from DB
|
||||
try {
|
||||
await this.deploymentsService.deleteAllForApplication(app.id);
|
||||
} catch (e: any) {
|
||||
this.logger.warn(`Deployment records cleanup failed for ${app.name}: ${e.message}`);
|
||||
}
|
||||
|
||||
// 5. Delete app (also deletes uploaded files)
|
||||
try {
|
||||
await this.snapshotsService.deleteAllForApplication(app.id);
|
||||
} catch (e: any) {
|
||||
this.logger.warn(`Snapshot cleanup failed for ${app.name}: ${e.message}`);
|
||||
}
|
||||
|
||||
await this.applicationsService.delete(id, isStaff ? app.userId : req.user.id);
|
||||
|
||||
return { message: `Application "${app.name}" and all resources deleted` };
|
||||
return {
|
||||
message: `Application "${app.name}" and all resources deleted`,
|
||||
resourceCredit: credit ? this.billingService.formatCreditForApi(credit) : null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user