feat: add plugin download endpoint and improve download URL construction
This commit is contained in:
100
server/index.js
100
server/index.js
@@ -155,13 +155,32 @@ app.get("/api/plugins", async (_req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
function buildPluginDownloadEndpoint(ownerRepo, { provider, baseUrl, version } = {}) {
|
||||
const [owner, repo] = ownerRepo.split("/");
|
||||
if (!owner || !repo) {
|
||||
return null;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
if (provider) {
|
||||
params.set("provider", provider);
|
||||
}
|
||||
if (baseUrl) {
|
||||
params.set("baseUrl", baseUrl);
|
||||
}
|
||||
if (version) {
|
||||
params.set("version", version);
|
||||
}
|
||||
const search = params.toString();
|
||||
return `/api/plugins/${owner}/${repo}/download${search ? `?${search}` : ""}`;
|
||||
}
|
||||
|
||||
app.get("/api/plugins/:owner/:repo", async (req, res) => {
|
||||
const ownerRepo = `${req.params.owner}/${req.params.repo}`;
|
||||
try {
|
||||
const provider = (req.query.provider || "github").toLowerCase();
|
||||
const baseUrl = req.query.baseUrl || (provider === "github" ? "https://github.com" : "");
|
||||
const entry = provider === "github" ? ownerRepo : { provider, repo: ownerRepo, baseUrl };
|
||||
const normalizedEntry = normalizeRepoInput(entry, { repo: ownerRepo, provider, baseUrl });
|
||||
const baseUrlQuery = req.query.baseUrl || (provider === "github" ? "https://github.com" : "");
|
||||
const entry = provider === "github" ? ownerRepo : { provider, repo: ownerRepo, baseUrl: baseUrlQuery };
|
||||
const normalizedEntry = normalizeRepoInput(entry, { repo: ownerRepo, provider, baseUrl: baseUrlQuery });
|
||||
|
||||
const info = await fetchRepo(entry);
|
||||
const [manifest, releases, commits] = await Promise.all([
|
||||
@@ -170,13 +189,28 @@ app.get("/api/plugins/:owner/:repo", async (req, res) => {
|
||||
fetchCommits(entry).catch(() => [])
|
||||
]);
|
||||
|
||||
const effectiveBaseUrl =
|
||||
normalizedEntry?.baseUrl ||
|
||||
baseUrlQuery ||
|
||||
info.baseUrl ||
|
||||
(provider === "github" ? "https://github.com" : "");
|
||||
|
||||
const releasesWithDownload =
|
||||
normalizedEntry && releases.length > 0
|
||||
? releases.map((release) => {
|
||||
const tagOrName = release.tag || release.name;
|
||||
if (!tagOrName) {
|
||||
return release;
|
||||
}
|
||||
const endpoint = buildPluginDownloadEndpoint(ownerRepo, {
|
||||
provider,
|
||||
baseUrl: effectiveBaseUrl,
|
||||
version: tagOrName
|
||||
});
|
||||
return {
|
||||
...release,
|
||||
downloadUrl: tagOrName ? buildDownloadUrl(normalizedEntry, tagOrName, "release") : null
|
||||
downloadUrl: endpoint,
|
||||
sourceDownloadUrl: buildDownloadUrl(normalizedEntry, tagOrName, "release")
|
||||
};
|
||||
})
|
||||
: releases;
|
||||
@@ -187,8 +221,14 @@ app.get("/api/plugins/:owner/:repo", async (req, res) => {
|
||||
const sourceType = latestRelease ? "release" : "branch";
|
||||
const version = latestRelease?.tag || latestRelease?.name || info.defaultBranch || "main";
|
||||
if (version) {
|
||||
const endpoint = buildPluginDownloadEndpoint(ownerRepo, {
|
||||
provider,
|
||||
baseUrl: effectiveBaseUrl,
|
||||
version
|
||||
});
|
||||
downloadMeta = {
|
||||
url: buildDownloadUrl(normalizedEntry, version, sourceType),
|
||||
url: endpoint,
|
||||
sourceUrl: buildDownloadUrl(normalizedEntry, version, sourceType),
|
||||
version,
|
||||
sourceType
|
||||
};
|
||||
@@ -207,6 +247,56 @@ app.get("/api/plugins/:owner/:repo", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/plugins/:owner/:repo/download", async (req, res) => {
|
||||
const ownerRepo = `${req.params.owner}/${req.params.repo}`;
|
||||
try {
|
||||
const provider = (req.query.provider || "github").toLowerCase();
|
||||
const baseUrl = req.query.baseUrl || undefined;
|
||||
const version = req.query.version || "latest";
|
||||
|
||||
if (provider === "gitea" && !baseUrl) {
|
||||
return res.status(400).json({ error: "Gitea downloads vereisen een baseUrl." });
|
||||
}
|
||||
|
||||
const repoEntry = normalizeRepoInput(
|
||||
{ repo: ownerRepo, provider, baseUrl },
|
||||
{ repo: ownerRepo, provider, baseUrl }
|
||||
);
|
||||
|
||||
if (!repoEntry) {
|
||||
return res.status(400).json({ error: "Ongeldige repository." });
|
||||
}
|
||||
|
||||
const source = await resolveRepoDownload(repoEntry, version);
|
||||
const remoteResponse = await fetch(source.url);
|
||||
if (!remoteResponse.ok || !remoteResponse.body) {
|
||||
return res.status(502).json({ error: "Kon plugin versie niet downloaden." });
|
||||
}
|
||||
|
||||
res.setHeader("Content-Type", remoteResponse.headers.get("content-type") || "application/zip");
|
||||
const length = remoteResponse.headers.get("content-length");
|
||||
if (length) {
|
||||
res.setHeader("Content-Length", length);
|
||||
}
|
||||
res.setHeader("Content-Disposition", `attachment; filename="${source.filename}"`);
|
||||
res.setHeader("X-Plugin-Version", source.version);
|
||||
|
||||
const stream = Readable.fromWeb(remoteResponse.body);
|
||||
stream.on("error", (err) => {
|
||||
console.error("Plugin download stream error:", err);
|
||||
res.destroy(err);
|
||||
});
|
||||
stream.pipe(res);
|
||||
} catch (error) {
|
||||
console.error("Plugin download endpoint error:", error);
|
||||
if (!res.headersSent) {
|
||||
res.status(500).json({ error: "Download mislukt." });
|
||||
} else {
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/licenses", requireAuth, async (req, res) => {
|
||||
try {
|
||||
let targetUserId = req.user.id;
|
||||
|
||||
Reference in New Issue
Block a user