-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
fix(webapp): keep branches list pagination when archiving a branch #4723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| area: webapp | ||
| type: fix | ||
| --- | ||
|
|
||
| Archiving a preview branch now returns you to the same page of the branches list instead of resetting to the first page. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -180,6 +180,10 @@ export class BranchesPresenter { | |
| }, | ||
| }); | ||
|
|
||
| // Archiving shrinks the list, so a restored page number can point past the end. | ||
| const totalPages = Math.ceil(visibleCount / BRANCHES_PER_PAGE); | ||
| const currentPage = totalPages > 0 ? Math.min(page, totalPages) : 1; | ||
|
|
||
|
Comment on lines
+183
to
+186
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Normalize the no-branchable-environment response. The clamp at Lines 183-186 is bypassed when Return Proposed fix- currentPage: page,
+ currentPage: 1, |
||
| const limits = await checkBranchLimit({ | ||
| prisma: this.#prismaClient, | ||
| organizationId: project.organizationId, | ||
|
|
@@ -222,7 +226,7 @@ export class BranchesPresenter { | |
| orderBy: { | ||
| branchName: "asc", | ||
| }, | ||
| skip: (page - 1) * BRANCHES_PER_PAGE, | ||
| skip: (currentPage - 1) * BRANCHES_PER_PAGE, | ||
| take: BRANCHES_PER_PAGE, | ||
| }); | ||
|
|
||
|
|
@@ -252,8 +256,8 @@ export class BranchesPresenter { | |
|
|
||
| return { | ||
| branchableEnvironment, | ||
| currentPage: page, | ||
| totalPages: Math.ceil(visibleCount / BRANCHES_PER_PAGE), | ||
| currentPage, | ||
| totalPages, | ||
| hasBranches: totalBranches > 0, | ||
| branches: branchesSorted, | ||
| hasFilters, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,11 @@ const schema = ArchiveBranchOptions.and( | |
| }) | ||
| ); | ||
|
|
||
| // Only same-origin paths are safe to redirect to, since redirectPath comes from the form. | ||
| function internalRedirectPath(path: string): string | undefined { | ||
| return path.startsWith("/") && !path.startsWith("//") ? path : undefined; | ||
| } | ||
|
|
||
|
Comment on lines
+28
to
+32
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
file="apps/webapp/app/routes/resources.branches.archive.tsx"
printf '%s\n' '--- relevant source ---'
cat -n "$file" | sed -n '1,100p'
printf '%s\n' '--- related tests/usages ---'
rg -n --glob '*.{ts,tsx,js,jsx}' \
'internalRedirectPath|resources\.branches\.archive|redirectPath' \
apps/webapp packages 2>/dev/null | head -200
printf '%s\n' '--- WHATWG URL behavior ---'
node - <<'JS'
const base = "https://internal.invalid";
for (const path of ["/\\evil.example", "/\\/evil.example", "//evil.example", "/safe", "/%5Cevil.example", "/\\`@evil.example`"]) {
try {
const url = new URL(path, base);
console.log(JSON.stringify({path, href: url.href, origin: url.origin}));
} catch (error) {
console.log(JSON.stringify({path, error: String(error)}));
}
}
JSRepository: triggerdotdev/trigger.dev Length of output: 15653 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- redirect helpers ---'
rg -n -A35 -B5 \
'function redirectWith(Error|Success)Message|const redirectWith(Error|Success)Message|export .*redirectWith(Error|Success)Message' \
apps/webapp/app/models/message.server.ts
printf '%s\n' '--- archive form construction ---'
cat -n apps/webapp/app/routes/resources.branches.archive.tsx | sed -n '100,135p'Repository: triggerdotdev/trigger.dev Length of output: 3614 Validate the resolved origin before redirecting.
|
||
| export async function action({ request }: ActionFunctionArgs) { | ||
| const userId = await requireUserId(request); | ||
|
|
||
|
|
@@ -45,16 +50,24 @@ export async function action({ request }: ActionFunctionArgs) { | |
| ); | ||
|
|
||
| if (result.success) { | ||
| return redirectWithSuccessMessage( | ||
| const listPath = | ||
| result.branch.type === "DEVELOPMENT" | ||
| ? branchesDevPath(result.organization, result.project, result.branch) | ||
| : branchesPath(result.organization, result.project, result.branch), | ||
| : branchesPath(result.organization, result.project, result.branch); | ||
|
|
||
| // Return to the page the user archived from, so filters and pagination survive. | ||
| return redirectWithSuccessMessage( | ||
| internalRedirectPath(submission.value.redirectPath) ?? listPath, | ||
| request, | ||
| `Branch "${result.branch.branchName}" archived` | ||
| ); | ||
| } | ||
|
|
||
| return redirectWithErrorMessage(submission.value.redirectPath, request, result.error); | ||
| return redirectWithErrorMessage( | ||
| internalRedirectPath(submission.value.redirectPath) ?? "/", | ||
| request, | ||
| result.error | ||
| ); | ||
| } | ||
|
|
||
| export function ArchiveButton({ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Branches list shows an error page when the page number in the address bar is zero or negative
The requested page number is only capped at the top end (
Math.min(page, totalPages)atapps/webapp/app/presenters/v3/BranchesPresenter.server.ts:185) and never given a lower bound, so a page number below one makes the list query ask the database to skip a negative number of rows and the page fails to load.Impact: A user who edits the page number in the URL to 0 or a negative value sees a generic "Something went wrong" error instead of the branches list.
How a below-range page number reaches the database query
BranchesOptions(apps/webapp/app/utils/branches.ts) coercespagewithNumber(...)and only rejectsNaN, so?page=0or?page=-2parses successfully and is passed straight to the presenter by both list loaders (apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx:108-114and the dev-branches loader). WithtotalPages > 0,currentPage = Math.min(page, totalPages)keeps the non-positive value, givingskip: (currentPage - 1) * BRANCHES_PER_PAGEa negative value, which Prisma rejects; the loader's catch turns that into a 400 response. The new clamp is the natural place to also floor the value at 1 (the PR describes it as covering hand-edited URLs).Was this helpful? React with 👍 or 👎 to provide feedback.