feat: add snapshot/rollback system with download capability

- Add AppSnapshot entity with type (pre_deploy/manual), status tracking, and file paths
- Add SnapshotsService with create, capture, rollback, prune (max 10), and download logic
- Add SnapshotsController with REST endpoints for CRUD, rollback, and file downloads
- Add K8s methods: exportDatabaseDump, archiveWpContent, restoreWpContent
- Auto-create pre-deploy snapshots before each deployment for rollback safety
- Support downloading current live state (source, wp-content, database) without snapshots
- Add snapshot management UI in app detail page with create, rollback, download, delete
- Wire circular dependencies with forwardRef between Deployments and Snapshots modules
This commit is contained in:
keyhan
2026-04-08 01:55:00 +03:30
parent ac5278d73b
commit d3a5240528
11 changed files with 1286 additions and 3 deletions
@@ -6,6 +6,7 @@ import { ApplicationsService } from '../applications/applications.service';
import { KubernetesService } from '../kubernetes/kubernetes.service';
import { BuildService } from '../build/build.service';
import { DeploymentStatus } from '../common/enums';
import { SnapshotsService } from '../snapshots/snapshots.service';
@Injectable()
export class DeploymentsService {
@@ -18,11 +19,22 @@ export class DeploymentsService {
private applicationsService: ApplicationsService,
private kubernetesService: KubernetesService,
private buildService: BuildService,
@Inject(forwardRef(() => SnapshotsService))
private snapshotsService: SnapshotsService,
) {}
async triggerDeployment(applicationId: string, userId: string): Promise<Deployment> {
const app = await this.applicationsService.findOne(applicationId, userId);
// Auto-snapshot before deploying (capture current state for rollback)
try {
const version = `v${Date.now()}`;
await this.snapshotsService.createPreDeploySnapshot(app.id, userId, version);
this.logger.log(`Pre-deploy snapshot created for ${app.name}`);
} catch (e: any) {
this.logger.warn(`Pre-deploy snapshot failed for ${app.name}: ${e.message} — continuing deploy`);
}
// Create deployment record
const deployment = this.deploymentsRepository.create({
applicationId: app.id,
@@ -173,6 +185,15 @@ export class DeploymentsService {
throw new NotFoundException('No source code available. Upload code or set a git URL first.');
}
// Auto-snapshot before redeploy
try {
const version = `v${Date.now()}`;
await this.snapshotsService.createPreDeploySnapshot(app.id, userId, version);
this.logger.log(`Pre-redeploy snapshot created for ${app.name}`);
} catch (e: any) {
this.logger.warn(`Pre-redeploy snapshot failed for ${app.name}: ${e.message} — continuing`);
}
// Create new deployment record
const deployment = this.deploymentsRepository.create({
applicationId: app.id,