182 lines
5.5 KiB
JavaScript
182 lines
5.5 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const { createLogger } = require('../utils/logger');
|
|
|
|
const log = createLogger('cache:projects');
|
|
|
|
function tryLoadProjectsHeatmapCache(pageId, config) {
|
|
if (!pageId) return null;
|
|
|
|
const cacheDirFromEnv = process.env.PROJECTS_CACHE_DIR
|
|
? String(process.env.PROJECTS_CACHE_DIR)
|
|
: '';
|
|
const cacheDirFromConfig =
|
|
config && config.site && config.site.github && config.site.github.cacheDir
|
|
? String(config.site.github.cacheDir)
|
|
: '';
|
|
const cacheDir = cacheDirFromEnv || cacheDirFromConfig || 'dev';
|
|
|
|
const cacheBaseDir = path.isAbsolute(cacheDir) ? cacheDir : path.join(process.cwd(), cacheDir);
|
|
const cachePath = path.join(cacheBaseDir, `${pageId}.heatmap-cache.json`);
|
|
if (!fs.existsSync(cachePath)) return null;
|
|
|
|
try {
|
|
const raw = fs.readFileSync(cachePath, 'utf8');
|
|
const parsed = JSON.parse(raw);
|
|
if (!parsed || typeof parsed !== 'object') return null;
|
|
|
|
const username = parsed.username ? String(parsed.username).trim() : '';
|
|
const html = parsed.html ? String(parsed.html) : '';
|
|
if (!username || !html) return null;
|
|
|
|
return {
|
|
username,
|
|
html,
|
|
meta: {
|
|
pageId: parsed.pageId || pageId,
|
|
generatedAt: parsed.generatedAt || '',
|
|
sourceUrl: parsed.sourceUrl || '',
|
|
},
|
|
};
|
|
} catch (e) {
|
|
log.warn('heatmap 缓存读取失败,将降级为运行时加载', { path: cachePath });
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function tryLoadProjectsRepoCache(pageId, config) {
|
|
if (!pageId) return null;
|
|
|
|
const cacheDirFromEnv = process.env.PROJECTS_CACHE_DIR
|
|
? String(process.env.PROJECTS_CACHE_DIR)
|
|
: '';
|
|
const cacheDirFromConfig =
|
|
config && config.site && config.site.github && config.site.github.cacheDir
|
|
? String(config.site.github.cacheDir)
|
|
: '';
|
|
const cacheDir = cacheDirFromEnv || cacheDirFromConfig || 'dev';
|
|
|
|
const cacheBaseDir = path.isAbsolute(cacheDir) ? cacheDir : path.join(process.cwd(), cacheDir);
|
|
const cachePath = path.join(cacheBaseDir, `${pageId}.repo-cache.json`);
|
|
if (!fs.existsSync(cachePath)) return null;
|
|
|
|
try {
|
|
const raw = fs.readFileSync(cachePath, 'utf8');
|
|
const parsed = JSON.parse(raw);
|
|
if (!parsed || typeof parsed !== 'object') return null;
|
|
|
|
const repos = Array.isArray(parsed.repos) ? parsed.repos : [];
|
|
const map = new Map();
|
|
repos.forEach((r) => {
|
|
const url = r && r.url ? String(r.url) : '';
|
|
if (!url) return;
|
|
map.set(url, {
|
|
language: r && r.language ? String(r.language) : '',
|
|
languageColor: r && r.languageColor ? String(r.languageColor) : '',
|
|
stars: Number.isFinite(r && r.stars) ? r.stars : null,
|
|
forks: Number.isFinite(r && r.forks) ? r.forks : null,
|
|
});
|
|
});
|
|
|
|
return {
|
|
map,
|
|
meta: {
|
|
pageId: parsed.pageId || pageId,
|
|
generatedAt: parsed.generatedAt || '',
|
|
},
|
|
};
|
|
} catch (e) {
|
|
log.warn('projects 缓存读取失败,将仅展示标题与描述', { path: cachePath });
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function normalizeGithubRepoUrl(url) {
|
|
if (!url) return '';
|
|
try {
|
|
const u = new URL(String(url));
|
|
if (u.hostname.toLowerCase() !== 'github.com') return '';
|
|
const parts = u.pathname.split('/').filter(Boolean);
|
|
if (parts.length < 2) return '';
|
|
const owner = parts[0];
|
|
const repo = parts[1].replace(/\\.git$/i, '');
|
|
if (!owner || !repo) return '';
|
|
return `https://github.com/${owner}/${repo}`;
|
|
} catch {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function applyRepoMetaToCategories(categories, repoMetaMap) {
|
|
if (!Array.isArray(categories) || !(repoMetaMap instanceof Map)) return;
|
|
|
|
const walk = (node) => {
|
|
if (!node || typeof node !== 'object') return;
|
|
if (Array.isArray(node.subcategories)) node.subcategories.forEach(walk);
|
|
if (Array.isArray(node.groups)) node.groups.forEach(walk);
|
|
if (Array.isArray(node.subgroups)) node.subgroups.forEach(walk);
|
|
|
|
if (Array.isArray(node.sites)) {
|
|
node.sites.forEach((site) => {
|
|
if (!site || typeof site !== 'object' || !site.url) return;
|
|
const canonical = normalizeGithubRepoUrl(site.url);
|
|
if (!canonical) return;
|
|
const meta = repoMetaMap.get(canonical);
|
|
if (!meta) return;
|
|
|
|
site.language = meta.language || '';
|
|
site.languageColor = meta.languageColor || '';
|
|
site.stars = meta.stars;
|
|
site.forks = meta.forks;
|
|
});
|
|
}
|
|
};
|
|
|
|
categories.forEach(walk);
|
|
}
|
|
|
|
function normalizeGithubHeatmapColor(input) {
|
|
const raw = String(input || '')
|
|
.trim()
|
|
.replace(/^#/, '');
|
|
const color = raw.toLowerCase();
|
|
if (/^[0-9a-f]{6}$/.test(color)) return color;
|
|
if (/^[0-9a-f]{3}$/.test(color)) return color;
|
|
return '339af0';
|
|
}
|
|
|
|
function getGithubUsernameFromConfig(config) {
|
|
const username =
|
|
config && config.site && config.site.github && config.site.github.username
|
|
? String(config.site.github.username).trim()
|
|
: '';
|
|
return username;
|
|
}
|
|
|
|
function buildProjectsMeta(config) {
|
|
const username = getGithubUsernameFromConfig(config);
|
|
if (!username) return null;
|
|
|
|
const color = normalizeGithubHeatmapColor(
|
|
config && config.site && config.site.github && config.site.github.heatmapColor
|
|
? config.site.github.heatmapColor
|
|
: '339af0'
|
|
);
|
|
|
|
return {
|
|
heatmap: {
|
|
username,
|
|
profileUrl: `https://github.com/${username}`,
|
|
imageUrl: `https://ghchart.rshah.org/${color}/${username}`,
|
|
},
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
tryLoadProjectsRepoCache,
|
|
tryLoadProjectsHeatmapCache,
|
|
applyRepoMetaToCategories,
|
|
buildProjectsMeta,
|
|
};
|