117 lines
2.8 KiB
TypeScript
117 lines
2.8 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { boardConfig } from "../../../board.config";
|
|
|
|
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() {
|
|
const branch = boardConfig.branch || undefined;
|
|
const path = boardConfig.photosPath;
|
|
const parsedRepo = parseRepo(boardConfig.repo);
|
|
|
|
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."
|
|
});
|
|
}
|