fd4f02b2f8
## What & why Two related fixes to how new cloud organizations get onboarded onto the Free plan. ### 1. Route new cloud orgs through plan selection New cloud organizations were created already activated, so they skipped the plan-selection step and went straight to creating projects — which meant their plan and usage limits were never set up. They're now created deactivated and routed through plan selection, which activates them once a plan is chosen. Self-hosted installs have no plan-selection step, so they're activated immediately on creation and are unaffected. The `Organization.v3Enabled` field is renamed to `isActivated` to better describe what it now gates. It's mapped to the existing `v3Enabled` column, so there's no data migration — only a schema/code rename. ### 2. Allow selecting the Free plan without GitHub verification Choosing the Free plan no longer requires connecting and verifying a GitHub account. The plan is applied immediately when selected. This removes: - the "Connect to GitHub" dialog and the GitHub-verified badge from the plan picker - the account-rejected state - the now-unreachable GitHub-connect return routes ## Notes - These changes pair with the corresponding change in the billing service that applies the Free plan directly; they should be released together. ## Testing Verified locally end to end: a new cloud org is routed to plan selection, the Free plan applies in one click with no GitHub step, the org is activated, its usage allowance is provisioned, and it lands on the new-project page. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
70 lines
2.2 KiB
TypeScript
70 lines
2.2 KiB
TypeScript
import type { PrismaClient } from "@trigger.dev/database";
|
|
import { randomBytes } from "node:crypto";
|
|
import { seedTestPAT, seedTestUser } from "./seedTestPAT";
|
|
|
|
function randomHex(len = 12): string {
|
|
return randomBytes(Math.ceil(len / 2))
|
|
.toString("hex")
|
|
.slice(0, len);
|
|
}
|
|
|
|
// Composite test fixture: a User, an Organization with that user as a
|
|
// member, a Project owned by the org, a DEVELOPMENT environment, and a
|
|
// non-revoked PAT for the user.
|
|
//
|
|
// Used by the PAT-comprehensive matrix (TRI-8741) to exercise routes
|
|
// like GET /api/v1/projects/:projectRef/runs whose access check is
|
|
// `findProjectByRef(externalRef, userId)` — i.e. the project's org
|
|
// must have the userId in its members. seedTestEnvironment alone
|
|
// doesn't create the OrgMember link, which is why this helper exists.
|
|
//
|
|
// Caller passes `projectDeleted: true` to test the soft-deleted-
|
|
// project path; `userAdmin: true` to confirm the global admin flag
|
|
// doesn't add cross-org visibility (the route is per-user).
|
|
export async function seedTestUserProject(
|
|
prisma: PrismaClient,
|
|
opts: { userAdmin?: boolean; projectDeleted?: boolean } = {}
|
|
) {
|
|
const suffix = randomHex(8);
|
|
const apiKey = `tr_dev_${randomHex(24)}`;
|
|
const pkApiKey = `pk_dev_${randomHex(24)}`;
|
|
|
|
const user = await seedTestUser(prisma, { admin: opts.userAdmin ?? false });
|
|
|
|
const organization = await prisma.organization.create({
|
|
data: {
|
|
title: `e2e-pat-org-${suffix}`,
|
|
slug: `e2e-pat-org-${suffix}`,
|
|
isActivated: true,
|
|
members: { create: { userId: user.id, role: "ADMIN" } },
|
|
},
|
|
});
|
|
|
|
const project = await prisma.project.create({
|
|
data: {
|
|
name: `e2e-pat-project-${suffix}`,
|
|
slug: `e2e-pat-proj-${suffix}`,
|
|
externalRef: `proj_${suffix}`,
|
|
organizationId: organization.id,
|
|
engine: "V2",
|
|
deletedAt: opts.projectDeleted ? new Date() : null,
|
|
},
|
|
});
|
|
|
|
const environment = await prisma.runtimeEnvironment.create({
|
|
data: {
|
|
slug: "dev",
|
|
type: "DEVELOPMENT",
|
|
apiKey,
|
|
pkApiKey,
|
|
shortcode: suffix.slice(0, 4),
|
|
projectId: project.id,
|
|
organizationId: organization.id,
|
|
},
|
|
});
|
|
|
|
const pat = await seedTestPAT(prisma, user.id);
|
|
|
|
return { user, organization, project, environment, pat };
|
|
}
|