fix(v2): critical bug fixes — root SSH, bootstrap wait, SG deletion order, PAT masking
- EC2 bootstrap now enables root SSH (copies authorized_keys to root, sets PermitRootLogin without-password, reloads sshd) so all commands run as root and NVM at /root/.nvm is accessible - Added /var/lib/pp-bootstrap-done sentinel; deploy waits for it before running any user commands — prevents race between SSH availability and user-data completion (docker/nvm install can take 3-5+ min) - Fixed stopPreview: terminate instance first, delete key pair next, then delete security group with 30s delay — SG deletion was previously attempted before termination causing it to fail - Fixed redeploy to always fetch fresh instanceIp/sshPrivateKey from DB rather than using potentially-stale preview parameter - Fixed .env writing to use base64 encoding via echo|base64-d to safely handle values with special characters, single quotes, and newlines - PAT and git clone URL now masked in preview logs (shows **** for password) - Fixed inactivity cron: removed dead inactivityMs variable, use join on repoConfig to avoid N+1, deduplicate pending INACTIVITY_STOP jobs - Fixed IAM policy UI: ec2:CreateKeyPair (backend uses CreateKeyPair, not ImportKeyPair which is a different AWS operation) - Admin panel: added Edit button with username/password form for users - Privacy page: fetch and display admin contactEmail from settings - pnpm-workspace.yaml: fix allowBuilds→onlyBuiltDependencies for pnpm 9 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -28,29 +28,32 @@ export function startCronWorkers() {
|
||||
}
|
||||
|
||||
async function checkInactivity() {
|
||||
const settings = await getAdminSettings();
|
||||
const now = new Date();
|
||||
|
||||
const running = await prisma.preview.findMany({
|
||||
where: { status: "RUNNING" },
|
||||
include: { repoConfig: { select: { inactivityHours: true } } },
|
||||
});
|
||||
|
||||
for (const preview of running) {
|
||||
const inactivityMs = settings.maxConcurrentInstancesPerUser; // will use actual inactivityHours from repoConfig
|
||||
const repoConfig = await prisma.repoConfig.findUnique({ where: { id: preview.repoConfigId } });
|
||||
if (!repoConfig) continue;
|
||||
|
||||
const deadline = new Date(preview.lastActivityAt.getTime() + repoConfig.inactivityHours * 3600 * 1000);
|
||||
const inactivityHours = preview.repoConfig.inactivityHours;
|
||||
const deadline = new Date(preview.lastActivityAt.getTime() + inactivityHours * 3600 * 1000);
|
||||
if (now >= deadline) {
|
||||
log.info({ previewId: preview.id }, "Preview inactive, enqueuing INACTIVITY_STOP");
|
||||
await prisma.job.create({
|
||||
data: {
|
||||
previewId: preview.id,
|
||||
type: "INACTIVITY_STOP",
|
||||
status: "PENDING",
|
||||
payload: {},
|
||||
},
|
||||
log.info({ previewId: preview.id, inactivityHours }, "Preview inactive, enqueuing INACTIVITY_STOP");
|
||||
// Only create one pending INACTIVITY_STOP per preview
|
||||
const existingStop = await prisma.job.findFirst({
|
||||
where: { previewId: preview.id, type: "INACTIVITY_STOP", status: { in: ["PENDING", "RUNNING"] } },
|
||||
});
|
||||
if (!existingStop) {
|
||||
await prisma.job.create({
|
||||
data: {
|
||||
previewId: preview.id,
|
||||
type: "INACTIVITY_STOP",
|
||||
status: "PENDING",
|
||||
payload: {},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user