mirror of
https://github.com/better-auth/better-hub
synced 2026-08-09 12:26:04 +00:00
fix release fetch and GitHub release URL routing (#324)
Co-authored-by: Maxwell <145994855+ping-maxwell@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { getLatestRepoRelease } from "@/lib/github";
|
||||
|
||||
export default async function LatestReleaseRedirectPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ owner: string; repo: string }>;
|
||||
}) {
|
||||
const { owner, repo } = await params;
|
||||
const release = await getLatestRepoRelease(owner, repo);
|
||||
|
||||
if (!release?.tag_name) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
redirect(`/${owner}/${repo}/releases/${encodeURIComponent(release.tag_name)}`);
|
||||
}
|
||||
+2
@@ -7,6 +7,8 @@ export async function GET(
|
||||
},
|
||||
) {
|
||||
const { owner, repo, tag, filename } = await context.params;
|
||||
|
||||
// this route basically redirects users to the real github asset URL, if the extension is on it can loop unless that URL is allowed listed in extension rules
|
||||
const githubUrl = `https://github.com/${owner}/${repo}/releases/download/${encodeURIComponent(tag)}/${filename.join("/")}`;
|
||||
return NextResponse.redirect(githubUrl, { status: 302 });
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default async function GitHubStyleReleaseTagPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ owner: string; repo: string; tag: string }>;
|
||||
}) {
|
||||
const { owner, repo, tag } = await params;
|
||||
redirect(`/${owner}/${repo}/releases/${encodeURIComponent(tag)}`);
|
||||
}
|
||||
@@ -129,6 +129,9 @@ export function toInternalUrl(htmlUrl: string): string {
|
||||
if (type === "blob" && path) return `${base}/blob/${path}`;
|
||||
if (type === "commits") return `${base}/commits`;
|
||||
if (type === "commit" && path) return `${base}/commits/${path}`;
|
||||
if (type === "releases") return `${base}/releases`;
|
||||
if (type === "release" && path) return `${base}/releases/${encodeURIComponent(path)}`;
|
||||
if (type === "release_latest") return `${base}/releases/_latest`;
|
||||
if (type === "repo") return base;
|
||||
|
||||
return htmlUrl;
|
||||
@@ -202,7 +205,17 @@ type ParsedGitHubUrl =
|
||||
| {
|
||||
owner: string;
|
||||
repo: string;
|
||||
type: "repo" | "pull" | "issue" | "tree" | "blob" | "commits" | "commit";
|
||||
type:
|
||||
| "repo"
|
||||
| "pull"
|
||||
| "issue"
|
||||
| "tree"
|
||||
| "blob"
|
||||
| "commits"
|
||||
| "commit"
|
||||
| "releases"
|
||||
| "release"
|
||||
| "release_latest";
|
||||
number?: number;
|
||||
path?: string;
|
||||
}
|
||||
@@ -286,6 +299,17 @@ export function parseGitHubUrl(htmlUrl: string): ParsedGitHubUrl | null {
|
||||
tag: rest[2],
|
||||
filename: rest.slice(3).join("/"),
|
||||
};
|
||||
if (rest[0] === "releases" && rest.length === 1)
|
||||
return { owner, repo, type: "releases" };
|
||||
if (rest[0] === "releases" && rest[1] === "tag" && rest[2])
|
||||
return {
|
||||
owner,
|
||||
repo,
|
||||
type: "release",
|
||||
path: decodeURIComponent(rest.slice(2).join("/")),
|
||||
};
|
||||
if (rest[0] === "releases" && rest[1] === "latest")
|
||||
return { owner, repo, type: "release_latest" };
|
||||
if (rest[0] === "actions") {
|
||||
// /actions/runs/:runId or /actions/runs/:runId/jobs/:jobId
|
||||
if (rest[1] === "runs" && rest[2]) {
|
||||
|
||||
+25
-13
@@ -2850,7 +2850,7 @@ export async function getRepoReleases(owner: string, repo: string) {
|
||||
const authCtx = await getGitHubAuthContext();
|
||||
const cacheKey = buildRepoReleasesCacheKey(owner, repo);
|
||||
|
||||
return readLocalFirstGitData({
|
||||
const releases = await readLocalFirstGitData({
|
||||
authCtx,
|
||||
cacheKey,
|
||||
cacheType: "repo_releases",
|
||||
@@ -2859,6 +2859,18 @@ export async function getRepoReleases(owner: string, repo: string) {
|
||||
jobPayload: { owner, repo },
|
||||
fetchRemote: (octokit) => fetchRepoReleasesFromGitHub(octokit, owner, repo),
|
||||
});
|
||||
|
||||
if (releases.length > 0 || !authCtx?.octokit) return releases;
|
||||
|
||||
try {
|
||||
const fresh = await fetchRepoReleasesFromGitHub(authCtx.octokit, owner, repo);
|
||||
upsertGithubCacheEntry(authCtx.userId, cacheKey, "repo_releases", fresh).catch(
|
||||
() => {},
|
||||
);
|
||||
return fresh;
|
||||
} catch {
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRepoReleasesPage(owner: string, repo: string, page: number) {
|
||||
@@ -2892,18 +2904,6 @@ export async function getRepoReleaseByTag(owner: string, repo: string, tag: stri
|
||||
const authCtx = await getGitHubAuthContext();
|
||||
if (!authCtx?.octokit) return null;
|
||||
|
||||
if (tag === "latest") {
|
||||
try {
|
||||
const { data } = await authCtx.octokit.repos.getLatestRelease({
|
||||
owner,
|
||||
repo,
|
||||
});
|
||||
return data;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const { data } = await authCtx.octokit.repos.getReleaseByTag({ owner, repo, tag });
|
||||
return data;
|
||||
@@ -2912,6 +2912,18 @@ export async function getRepoReleaseByTag(owner: string, repo: string, tag: stri
|
||||
}
|
||||
}
|
||||
|
||||
export async function getLatestRepoRelease(owner: string, repo: string) {
|
||||
const authCtx = await getGitHubAuthContext();
|
||||
if (!authCtx?.octokit) return null;
|
||||
|
||||
try {
|
||||
const { data } = await authCtx.octokit.repos.getLatestRelease({ owner, repo });
|
||||
return data;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getFileContent(owner: string, repo: string, path: string, ref?: string) {
|
||||
const authCtx = await getGitHubAuthContext();
|
||||
const normalizedRef = normalizeRef(ref);
|
||||
|
||||
@@ -173,6 +173,17 @@ function buildRules(host) {
|
||||
resourceTypes: ["main_frame"],
|
||||
},
|
||||
},
|
||||
// --- Allow release asset download URLs to prevent redirect loops ---
|
||||
{
|
||||
id: 251,
|
||||
priority: 5,
|
||||
action: { type: "allow" },
|
||||
condition: {
|
||||
regexFilter:
|
||||
"^https://github\\.com/[^/]+/[^/]+/releases/download/.+",
|
||||
resourceTypes: ["main_frame"],
|
||||
},
|
||||
},
|
||||
|
||||
// --- Specific path rewrites ---
|
||||
{
|
||||
|
||||
@@ -101,6 +101,15 @@
|
||||
"resourceTypes": ["main_frame"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"priority": 5,
|
||||
"action": { "type": "allow" },
|
||||
"condition": {
|
||||
"regexFilter": "^https://github\\.com/[^/]+/[^/]+/releases/download/.+",
|
||||
"resourceTypes": ["main_frame"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"priority": 5,
|
||||
|
||||
@@ -132,6 +132,16 @@ function buildRules(host) {
|
||||
resourceTypes: ["main_frame"],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 251,
|
||||
priority: 5,
|
||||
action: { type: "allow" },
|
||||
condition: {
|
||||
regexFilter:
|
||||
"^https://github\\.com/[^/]+/[^/]+/releases/download/.+",
|
||||
resourceTypes: ["main_frame"],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
id: 101,
|
||||
|
||||
@@ -101,6 +101,15 @@
|
||||
"resourceTypes": ["main_frame"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"priority": 5,
|
||||
"action": { "type": "allow" },
|
||||
"condition": {
|
||||
"regexFilter": "^https://github\\.com/[^/]+/[^/]+/releases/download/.+",
|
||||
"resourceTypes": ["main_frame"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"priority": 5,
|
||||
|
||||
Reference in New Issue
Block a user