initial shame board
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
type GithubContent = {
|
||||
name: string;
|
||||
path: string;
|
||||
type: "file" | "dir";
|
||||
download_url: string | null;
|
||||
html_url: string;
|
||||
};
|
||||
|
||||
type ShamePhoto = {
|
||||
name: string;
|
||||
path: string;
|
||||
url: string;
|
||||
htmlUrl: string;
|
||||
};
|
||||
|
||||
const imageExtensionPattern = /\.(avif|gif|jpe?g|png|webp)$/i;
|
||||
|
||||
function parseRepo(value: string | null) {
|
||||
const clean = value?.trim().replace(/^https:\/\/github\.com\//, "") ?? "";
|
||||
const [owner, repo] = clean.split("/");
|
||||
|
||||
if (!owner || !repo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
owner,
|
||||
repo: repo.replace(/\.git$/, "")
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchDirectory(owner: string, repo: string, path: string, branch?: string) {
|
||||
const encodedPath = path
|
||||
.split("/")
|
||||
.filter(Boolean)
|
||||
.map((part) => encodeURIComponent(part))
|
||||
.join("/");
|
||||
const url = new URL(`https://api.github.com/repos/${owner}/${repo}/contents/${encodedPath}`);
|
||||
|
||||
if (branch) {
|
||||
url.searchParams.set("ref", branch);
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28"
|
||||
},
|
||||
next: { revalidate: 60 }
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
ok: false as const,
|
||||
status: response.status,
|
||||
message: response.status === 404 ? "Repository or folder was not found." : "GitHub did not return the photo list."
|
||||
};
|
||||
}
|
||||
|
||||
const content = (await response.json()) as GithubContent[] | GithubContent;
|
||||
const items = Array.isArray(content) ? content : [content];
|
||||
|
||||
return {
|
||||
ok: true as const,
|
||||
items
|
||||
};
|
||||
}
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const repoValue = searchParams.get("repo") || process.env.NEXT_PUBLIC_GITHUB_REPO || null;
|
||||
const branch = searchParams.get("branch") || process.env.NEXT_PUBLIC_GITHUB_BRANCH || undefined;
|
||||
const path = searchParams.get("path") || process.env.NEXT_PUBLIC_GITHUB_PHOTOS_PATH || "";
|
||||
const parsedRepo = parseRepo(repoValue);
|
||||
|
||||
if (!parsedRepo) {
|
||||
return NextResponse.json({
|
||||
photos: [],
|
||||
repo: null,
|
||||
path,
|
||||
message: "Set a GitHub repository to load the board."
|
||||
});
|
||||
}
|
||||
|
||||
const directory = await fetchDirectory(parsedRepo.owner, parsedRepo.repo, path, branch);
|
||||
|
||||
if (!directory.ok) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
photos: [],
|
||||
repo: `${parsedRepo.owner}/${parsedRepo.repo}`,
|
||||
path,
|
||||
message: directory.message
|
||||
},
|
||||
{ status: directory.status }
|
||||
);
|
||||
}
|
||||
|
||||
const photos: ShamePhoto[] = directory.items
|
||||
.filter((item) => item.type === "file" && item.download_url && imageExtensionPattern.test(item.name))
|
||||
.map((item) => ({
|
||||
name: item.name.replace(imageExtensionPattern, ""),
|
||||
path: item.path,
|
||||
url: item.download_url as string,
|
||||
htmlUrl: item.html_url
|
||||
}));
|
||||
|
||||
return NextResponse.json({
|
||||
photos,
|
||||
repo: `${parsedRepo.owner}/${parsedRepo.repo}`,
|
||||
path,
|
||||
branch: branch ?? null,
|
||||
message: photos.length ? null : "No image files were found in that GitHub folder."
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user