feat: add orphan EC2 cleanup on startup, fix SetupWizard routing, fix SSH key gen

- On startup, scan for EC2 instances with pp:managed=true and terminate any orphans
- SetupWizard now renders as full-page (no Layout wrapper)
- Fixed SSH RSA key generation (generateKeyPairSync with spki format)
- Removed unused import in orphanCleanup.ts
- Frontend rebuild with App.tsx routing fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-25 00:20:36 +02:00
parent 40d484bede
commit 6434be8bed
13 changed files with 1151 additions and 93 deletions
+74
View File
@@ -0,0 +1,74 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var orphanCleanup_exports = {};
__export(orphanCleanup_exports, {
runOrphanCleanup: () => runOrphanCleanup
});
module.exports = __toCommonJS(orphanCleanup_exports);
var import_db = require("../lib/db");
var import_logger = require("../lib/logger");
var import_ec2 = require("./ec2");
const log = (0, import_logger.createLogger)("ORPHAN_CLEANUP");
async function runOrphanCleanup() {
log.info("Running orphan EC2 instance cleanup");
const users = await import_db.prisma.user.findMany({
where: {
awsAccessKeyId: { not: null },
awsSecretAccessKey: { not: null },
awsRegion: { not: null }
}
});
const activePreviews = await import_db.prisma.preview.findMany({
where: { status: { notIn: ["STOPPED", "IGNORED"] }, instanceId: { not: null } },
select: { id: true, instanceId: true }
});
const activeInstanceIds = new Set(activePreviews.map((p) => p.instanceId));
for (const user of users) {
try {
const ec2 = (0, import_ec2.makeEc2Client)(user);
const instances = await (0, import_ec2.describeAllManagedInstances)(ec2);
for (const inst of instances) {
const instanceId = inst.InstanceId;
if (!instanceId) continue;
const tags = Object.fromEntries((inst.Tags || []).map((t) => [t.Key, t.Value]));
const previewId = parseInt(tags["pp:previewId"] || "0", 10);
if (!activeInstanceIds.has(instanceId)) {
log.info({ instanceId, previewId }, "Terminating orphan instance");
try {
const keyName = `pp-preview-${previewId}`;
const sgName = `pp-preview-${previewId}`;
await (0, import_ec2.deleteKeyPairAws)(ec2, keyName);
await (0, import_ec2.terminateInstance)(ec2, instanceId);
setTimeout(() => (0, import_ec2.deleteSecurityGroupAws)(ec2, sgName), 3e4);
} catch (e) {
log.warn({ e, instanceId }, "Failed to terminate orphan");
}
}
}
} catch (e) {
log.warn({ e, userId: user.id }, "Failed to check orphans for user");
}
}
log.info("Orphan cleanup complete");
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
runOrphanCleanup
});
//# sourceMappingURL=orphanCleanup.js.map