feat: add react-router-dom for routing and implement plugin detail view
- Updated package.json to include react-router-dom dependency. - Refactored App component to use React Router for navigation. - Created PluginDetail component to display detailed information about a selected plugin. - Added fetch functions for releases and commits in the server code. - Enhanced UI with new styles for dark mode and improved layout. - Implemented caching for API responses to optimize performance.
This commit is contained in:
127
server/index.js
127
server/index.js
@@ -35,45 +35,119 @@ function setCached(key, value) {
|
||||
cache.set(key, { value, expiresAt: Date.now() + CACHE_TTL_MS });
|
||||
}
|
||||
|
||||
async function fetchRepo(ownerRepo) {
|
||||
const cached = getCached(ownerRepo);
|
||||
async function fetchJson(url, cacheKey) {
|
||||
const cached = getCached(cacheKey);
|
||||
if (cached) return cached;
|
||||
|
||||
const response = await fetch(`https://api.github.com/repos/${ownerRepo}`);
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
"User-Agent": "siti-plugin-repo"
|
||||
}
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitHub request failed for ${ownerRepo}`);
|
||||
throw new Error(`GitHub request failed for ${url}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
setCached(cacheKey, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
async function fetchRepo(ownerRepo) {
|
||||
const cached = getCached(`repo:${ownerRepo}`);
|
||||
if (cached) return cached;
|
||||
|
||||
const data = await fetchJson(`https://api.github.com/repos/${ownerRepo}`, `repo-raw:${ownerRepo}`);
|
||||
const mapped = {
|
||||
fullName: data.full_name,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
repoUrl: data.html_url,
|
||||
defaultBranch: data.default_branch,
|
||||
stars: data.stargazers_count,
|
||||
forks: data.forks_count,
|
||||
issues: data.open_issues_count,
|
||||
updatedAt: data.updated_at,
|
||||
topics: data.topics || []
|
||||
};
|
||||
setCached(ownerRepo, mapped);
|
||||
setCached(`repo:${ownerRepo}`, mapped);
|
||||
return mapped;
|
||||
}
|
||||
|
||||
async function fetchManifest(ownerRepo, defaultBranch) {
|
||||
const cached = getCached(`manifest:${ownerRepo}`);
|
||||
if (cached) return cached;
|
||||
|
||||
const branches = [defaultBranch, "main", "master"].filter(Boolean);
|
||||
for (const branch of branches) {
|
||||
const url = `https://raw.githubusercontent.com/${ownerRepo}/${branch}/manifest.json`;
|
||||
const response = await fetch(url, {
|
||||
headers: { "User-Agent": "siti-plugin-repo" }
|
||||
});
|
||||
if (response.ok) {
|
||||
const manifest = await response.json();
|
||||
setCached(`manifest:${ownerRepo}`, manifest);
|
||||
return manifest;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function fetchReleases(ownerRepo) {
|
||||
const data = await fetchJson(
|
||||
`https://api.github.com/repos/${ownerRepo}/releases?per_page=5`,
|
||||
`releases:${ownerRepo}`
|
||||
);
|
||||
return Array.isArray(data)
|
||||
? data.map((release) => ({
|
||||
tag: release.tag_name,
|
||||
name: release.name || release.tag_name,
|
||||
url: release.html_url,
|
||||
publishedAt: release.published_at
|
||||
}))
|
||||
: [];
|
||||
}
|
||||
|
||||
async function fetchCommits(ownerRepo) {
|
||||
const data = await fetchJson(
|
||||
`https://api.github.com/repos/${ownerRepo}/commits?per_page=5`,
|
||||
`commits:${ownerRepo}`
|
||||
);
|
||||
return Array.isArray(data)
|
||||
? data.map((commit) => ({
|
||||
sha: commit.sha,
|
||||
message: commit.commit?.message,
|
||||
author: commit.commit?.author?.name,
|
||||
date: commit.commit?.author?.date,
|
||||
url: commit.html_url
|
||||
}))
|
||||
: [];
|
||||
}
|
||||
|
||||
app.get("/api/plugins", async (_req, res) => {
|
||||
try {
|
||||
const repos = await readRepos();
|
||||
const results = await Promise.all(
|
||||
repos.map((repo) => fetchRepo(repo).catch(() => ({
|
||||
fullName: repo,
|
||||
name: repo.split("/")[1] || repo,
|
||||
description: "Kon gegevens niet ophalen.",
|
||||
repoUrl: `https://github.com/${repo}`,
|
||||
stars: 0,
|
||||
forks: 0,
|
||||
issues: 0,
|
||||
updatedAt: null,
|
||||
topics: []
|
||||
})))
|
||||
repos.map(async (repo) => {
|
||||
try {
|
||||
const info = await fetchRepo(repo);
|
||||
const manifest = await fetchManifest(repo, info.defaultBranch);
|
||||
return { ...info, manifest };
|
||||
} catch {
|
||||
return {
|
||||
fullName: repo,
|
||||
name: repo.split("/")[1] || repo,
|
||||
description: "Kon gegevens niet ophalen.",
|
||||
repoUrl: `https://github.com/${repo}`,
|
||||
stars: 0,
|
||||
forks: 0,
|
||||
issues: 0,
|
||||
updatedAt: null,
|
||||
topics: [],
|
||||
manifest: null
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
res.json({
|
||||
@@ -86,6 +160,27 @@ app.get("/api/plugins", async (_req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/plugins/:owner/:repo", async (req, res) => {
|
||||
const ownerRepo = `${req.params.owner}/${req.params.repo}`;
|
||||
try {
|
||||
const info = await fetchRepo(ownerRepo);
|
||||
const [manifest, releases, commits] = await Promise.all([
|
||||
fetchManifest(ownerRepo, info.defaultBranch).catch(() => null),
|
||||
fetchReleases(ownerRepo).catch(() => []),
|
||||
fetchCommits(ownerRepo).catch(() => [])
|
||||
]);
|
||||
|
||||
res.json({
|
||||
...info,
|
||||
manifest,
|
||||
releases,
|
||||
commits
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: "Kon plugin details niet laden." });
|
||||
}
|
||||
});
|
||||
|
||||
app.use(express.static(distDir));
|
||||
app.get("*", (_req, res) => {
|
||||
res.sendFile(path.join(distDir, "index.html"));
|
||||
|
||||
Reference in New Issue
Block a user