Sync infra config: HA/Zabbix relocation, Immich storage move, new services
Accumulated uncommitted infra changes: - Caddyfile: repoint HA/Zabbix to 192.168.1.4/.3, add ~20 new site routes - Immich: move media to /mnt/smsg, enable CUDA ML, mem limits, rewrite backup.sh - Add service stacks: agap-mcp, anki, family, freshrss, iperf3, kanboard, linkwarden, qbittorrent, radicale, syncthing, vikunja, windows - openwebui: enable API keys; ollama: drop CPU fallback - seafile/zabbix: extra_hosts entries; matrix: add user juris - Remove pihole stack and stale wiki/migrate.py - Ignore marketplace-mcp (standalone repo) and linkwarden runtime data Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
73
agap-mcp/src/gitea.js
Normal file
73
agap-mcp/src/gitea.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import { execSync } from 'child_process';
|
||||
import { writeFileSync, mkdirSync } from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import { join } from 'path';
|
||||
|
||||
const BASE = () => process.env.GITEA_URL || 'http://localhost:3000';
|
||||
let _token = null;
|
||||
|
||||
export function initGitea(token) {
|
||||
_token = token;
|
||||
console.log('Gitea: ready');
|
||||
}
|
||||
|
||||
function token() {
|
||||
if (!_token) throw new Error('Gitea not initialized');
|
||||
return _token;
|
||||
}
|
||||
|
||||
async function api(path, opts = {}) {
|
||||
const res = await fetch(`${BASE()}/api/v1${path}`, {
|
||||
...opts,
|
||||
headers: { Authorization: `token ${token()}`, 'Content-Type': 'application/json', ...opts.headers },
|
||||
});
|
||||
if (!res.ok) throw new Error(`Gitea API ${path}: ${res.status} ${await res.text()}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function giteaListRepos() {
|
||||
return api('/repos/search?limit=50').then(r => r.data.map(r => ({
|
||||
name: r.full_name, description: r.description, stars: r.stars_count,
|
||||
})));
|
||||
}
|
||||
|
||||
export async function giteaReadFile(repo, path, ref = 'HEAD') {
|
||||
const data = await api(`/repos/${repo}/contents/${path}?ref=${ref}`);
|
||||
return Buffer.from(data.content, 'base64').toString('utf8');
|
||||
}
|
||||
|
||||
export async function giteaWikiList(repo = 'alvis/AgapHost') {
|
||||
const data = await api(`/repos/${repo}/wiki/pages?limit=50`);
|
||||
return data.map(p => ({ name: p.title, updated: p.last_commit?.created }));
|
||||
}
|
||||
|
||||
export async function giteaWikiRead(page, repo = 'alvis/AgapHost') {
|
||||
const data = await api(`/repos/${repo}/wiki/page/${encodeURIComponent(page)}`);
|
||||
return Buffer.from(data.content_base64, 'base64').toString('utf8');
|
||||
}
|
||||
|
||||
export async function giteaWikiWrite(page, content, message, repo = 'alvis/AgapHost') {
|
||||
const dir = join(tmpdir(), 'agap-mcp-wiki');
|
||||
const wikiUrl = `${BASE().replace('http://', `http://alvis:${token()}@`)}/alvis/AgapHost.wiki.git`;
|
||||
|
||||
const gitEnv = { ...process.env, GIT_AUTHOR_NAME: 'agap-mcp', GIT_AUTHOR_EMAIL: 'allogn@gmail.com', GIT_COMMITTER_NAME: 'agap-mcp', GIT_COMMITTER_EMAIL: 'allogn@gmail.com' };
|
||||
|
||||
try {
|
||||
execSync(`git -C ${dir} pull ${wikiUrl} main`, { env: gitEnv, stdio: 'pipe' });
|
||||
} catch {
|
||||
execSync(`git clone ${wikiUrl} ${dir}`, { env: gitEnv, stdio: 'pipe' });
|
||||
}
|
||||
|
||||
const file = join(dir, `${page}.md`);
|
||||
writeFileSync(file, content);
|
||||
execSync(`git -C ${dir} add "${page}.md"`, { env: gitEnv, stdio: 'pipe' });
|
||||
execSync(`git -C ${dir} commit -m "${message || `Update ${page}`}"`, { env: gitEnv, stdio: 'pipe' });
|
||||
execSync(`git -C ${dir} push ${wikiUrl} main`, { env: gitEnv, stdio: 'pipe' });
|
||||
return `${page} updated`;
|
||||
}
|
||||
|
||||
export async function giteaListIssues(repo, state = 'open') {
|
||||
return api(`/repos/${repo}/issues?state=${state}&type=issues&limit=50`).then(issues =>
|
||||
issues.map(i => ({ number: i.number, title: i.title, state: i.state, labels: i.labels.map(l => l.name) }))
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user