feat: implement user authentication and license management system
- Added schema for users, licenses, and license hostnames in the database. - Created storage utility for reading and writing JSON files. - Developed user service for user registration, authentication, and retrieval. - Implemented authentication middleware to protect routes. - Built LicenseCard component to display license details. - Created SiteNav component for navigation with user authentication status. - Established AuthContext for managing authentication state and actions. - Developed Home page to display available plugins. - Created LicenseManager page for managing licenses with forms for creation and verification. - Implemented PluginDetail page to show detailed information about a specific plugin. - Added utility functions for date formatting.
This commit is contained in:
204
server/lib/pluginService.js
Normal file
204
server/lib/pluginService.js
Normal file
@@ -0,0 +1,204 @@
|
||||
import { readJsonFile } from "./storage.js";
|
||||
import { getCached, setCached } from "./cache.js";
|
||||
|
||||
export function parseRepoEntry(entry) {
|
||||
if (typeof entry === "string") {
|
||||
return {
|
||||
provider: "github",
|
||||
ownerRepo: entry,
|
||||
baseUrl: "https://github.com"
|
||||
};
|
||||
}
|
||||
const provider = (entry?.provider || "github").toLowerCase();
|
||||
const ownerRepo = entry?.repo || entry?.ownerRepo || "";
|
||||
const baseUrl = entry?.baseUrl || (provider === "github" ? "https://github.com" : "");
|
||||
return { provider, ownerRepo, baseUrl };
|
||||
}
|
||||
|
||||
export function normalizeRepoInput(input, extras = {}) {
|
||||
if (typeof input === "string") {
|
||||
return normalizeRepoInput({ repo: input }, extras);
|
||||
}
|
||||
const source = input && typeof input === "object" ? input : {};
|
||||
const repo = source.repo || source.ownerRepo || source.fullName || extras.repo;
|
||||
if (!repo) {
|
||||
return null;
|
||||
}
|
||||
const provider = (source.provider || extras.provider || "github").toLowerCase();
|
||||
const normalized = {
|
||||
repo,
|
||||
provider
|
||||
};
|
||||
const baseUrl = source.baseUrl || extras.baseUrl || (provider === "github" ? "https://github.com" : undefined);
|
||||
if (baseUrl) {
|
||||
normalized.baseUrl = baseUrl;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export async function readRepos(reposFile) {
|
||||
const parsed = await readJsonFile(reposFile, []);
|
||||
return Array.isArray(parsed) ? parsed : [];
|
||||
}
|
||||
|
||||
async function fetchJson(url, cacheKey, opts = {}) {
|
||||
const cached = getCached(cacheKey);
|
||||
if (cached) return cached;
|
||||
const headers = {
|
||||
"User-Agent": "siti-plugin-repo",
|
||||
Accept: "application/json"
|
||||
};
|
||||
if (!opts.provider || opts.provider === "github") {
|
||||
headers.Accept = "application/vnd.github+json";
|
||||
}
|
||||
const response = await fetch(url, { headers });
|
||||
if (!response.ok) {
|
||||
throw new Error(`${opts.provider || "git"} request failed for ${url}`);
|
||||
}
|
||||
const data = await response.json().catch(async () => {
|
||||
const text = await response.text();
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
setCached(cacheKey, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function fetchRepo(entry) {
|
||||
const { provider, ownerRepo, baseUrl } = parseRepoEntry(entry);
|
||||
const cacheKey = `repo:${provider}:${ownerRepo}`;
|
||||
const cached = getCached(cacheKey);
|
||||
if (cached) return cached;
|
||||
|
||||
let data;
|
||||
if (provider === "gitea") {
|
||||
const url = `${baseUrl.replace(/\/$/, "")}/api/v1/repos/${ownerRepo}`;
|
||||
data = await fetchJson(url, `repo-raw:${provider}:${ownerRepo}`, { provider });
|
||||
const mapped = {
|
||||
fullName: data.full_name || `${ownerRepo}`,
|
||||
name: data.name || ownerRepo.split("/")[1] || ownerRepo,
|
||||
description: data.description || null,
|
||||
repoUrl: `${baseUrl.replace(/\/$/, "")}/${ownerRepo}`,
|
||||
defaultBranch: data.default_branch || "main",
|
||||
stars: data.stargazers_count || data.watchers || 0,
|
||||
forks: data.forks_count || data.forks || 0,
|
||||
issues: data.open_issues_count || 0,
|
||||
updatedAt: data.updated_at || data.updated || null,
|
||||
topics: data.topics || [],
|
||||
provider,
|
||||
ownerRepo,
|
||||
baseUrl
|
||||
};
|
||||
setCached(cacheKey, mapped);
|
||||
return mapped;
|
||||
}
|
||||
|
||||
data = await fetchJson(`https://api.github.com/repos/${ownerRepo}`, `repo-raw:github:${ownerRepo}`, { provider: "github" });
|
||||
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 || [],
|
||||
provider,
|
||||
ownerRepo,
|
||||
baseUrl: "https://github.com"
|
||||
};
|
||||
setCached(cacheKey, mapped);
|
||||
return mapped;
|
||||
}
|
||||
|
||||
export async function fetchManifest(entry, defaultBranch) {
|
||||
const { provider, ownerRepo, baseUrl } = parseRepoEntry(entry);
|
||||
const cacheKey = `manifest:${provider}:${ownerRepo}`;
|
||||
const cached = getCached(cacheKey);
|
||||
if (cached) return cached;
|
||||
|
||||
const branches = [defaultBranch, "main", "master"].filter(Boolean);
|
||||
const [owner, repo] = ownerRepo.split("/");
|
||||
for (const branch of branches) {
|
||||
let url;
|
||||
if (provider === "gitea") {
|
||||
url = `${baseUrl.replace(/\/$/, "")}/repos/${owner}/${repo}/raw/${branch}/manifest.json`;
|
||||
} else {
|
||||
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().catch(() => null);
|
||||
setCached(cacheKey, manifest);
|
||||
return manifest;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function fetchReleases(entry) {
|
||||
const { provider, ownerRepo, baseUrl } = parseRepoEntry(entry);
|
||||
if (provider === "gitea") {
|
||||
const url = `${baseUrl.replace(/\/$/, "")}/api/v1/repos/${ownerRepo}/releases?limit=5`;
|
||||
const data = await fetchJson(url, `releases:${provider}:${ownerRepo}`, { provider });
|
||||
return Array.isArray(data)
|
||||
? data.map((release) => ({
|
||||
tag: release.tag_name || release.name,
|
||||
name: release.name || release.tag_name,
|
||||
url: release.html_url || `${baseUrl.replace(/\/$/, "")}/${ownerRepo}/releases`,
|
||||
publishedAt: release.published_at || release.created_at
|
||||
}))
|
||||
: [];
|
||||
}
|
||||
|
||||
const data = await fetchJson(
|
||||
`https://api.github.com/repos/${ownerRepo}/releases?per_page=5`,
|
||||
`releases:github:${ownerRepo}`,
|
||||
{ provider: "github" }
|
||||
);
|
||||
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
|
||||
}))
|
||||
: [];
|
||||
}
|
||||
|
||||
export async function fetchCommits(entry) {
|
||||
const { provider, ownerRepo, baseUrl } = parseRepoEntry(entry);
|
||||
if (provider === "gitea") {
|
||||
const url = `${baseUrl.replace(/\/$/, "")}/api/v1/repos/${ownerRepo}/commits?limit=5`;
|
||||
const data = await fetchJson(url, `commits:${provider}:${ownerRepo}`, { provider });
|
||||
return Array.isArray(data)
|
||||
? data.map((commit) => ({
|
||||
sha: commit.sha,
|
||||
message: commit.commit?.message || commit.message,
|
||||
author: commit.commit?.author?.name || commit.author?.name,
|
||||
date: commit.commit?.author?.date || commit.author?.date,
|
||||
url: commit.html_url || `${baseUrl.replace(/\/$/, "")}/${ownerRepo}/commit/${commit.sha}`
|
||||
}))
|
||||
: [];
|
||||
}
|
||||
|
||||
const data = await fetchJson(
|
||||
`https://api.github.com/repos/${ownerRepo}/commits?per_page=5`,
|
||||
`commits:github:${ownerRepo}`,
|
||||
{ provider: "github" }
|
||||
);
|
||||
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
|
||||
}))
|
||||
: [];
|
||||
}
|
||||
Reference in New Issue
Block a user