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:
102
agap-mcp/src/vaultwarden.js
Normal file
102
agap-mcp/src/vaultwarden.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import { execFileSync } from 'child_process';
|
||||
|
||||
const BW = 'bw';
|
||||
const ORG_ID = '4bd75130-b4d3-48d4-a4cb-e52b70295a51';
|
||||
const AI_COLLECTION = '5be27a82-8475-4c38-96b2-fa94ec8c957b';
|
||||
|
||||
let _session = null;
|
||||
|
||||
function bwEnv() {
|
||||
const env = { ...process.env };
|
||||
for (const k of ['HTTPS_PROXY','HTTP_PROXY','ALL_PROXY','https_proxy','http_proxy','all_proxy'])
|
||||
delete env[k];
|
||||
env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||
return env;
|
||||
}
|
||||
|
||||
function run(args, input) {
|
||||
return execFileSync(BW, args, {
|
||||
env: bwEnv(),
|
||||
encoding: 'utf8',
|
||||
input,
|
||||
stdio: input ? ['pipe','pipe','pipe'] : ['ignore','pipe','pipe'],
|
||||
}).trim();
|
||||
}
|
||||
|
||||
export async function initVaultwarden() {
|
||||
const email = process.env.BW_EMAIL || 'allogn@gmail.com';
|
||||
const password = process.env.BW_PASSWORD;
|
||||
|
||||
// Data dir is mounted from host — server already configured, skip bw config server
|
||||
|
||||
let status = 'unauthenticated';
|
||||
try {
|
||||
status = JSON.parse(run(['status'])).status;
|
||||
} catch {}
|
||||
|
||||
if (status === 'unauthenticated') {
|
||||
run(['login', email, password, '--raw']);
|
||||
}
|
||||
|
||||
_session = run(['unlock', password, '--raw']);
|
||||
run(['sync', '--session', _session]);
|
||||
console.log('Vaultwarden: ready');
|
||||
}
|
||||
|
||||
function session() {
|
||||
if (!_session) throw new Error('Vaultwarden not initialized');
|
||||
return _session;
|
||||
}
|
||||
|
||||
export function vwGetPassword(name) {
|
||||
return run(['get', 'password', name, '--session', session()]);
|
||||
}
|
||||
|
||||
export function vwGetItem(name) {
|
||||
return JSON.parse(run(['get', 'item', name, '--session', session()]));
|
||||
}
|
||||
|
||||
export function vwListItems(search) {
|
||||
const args = ['list', 'items', '--session', session()];
|
||||
if (search) args.push('--search', search);
|
||||
return JSON.parse(run(args));
|
||||
}
|
||||
|
||||
export function vwListOrgItems(search) {
|
||||
const args = ['list', 'items', '--organizationid', ORG_ID, '--session', session()];
|
||||
if (search) args.push('--search', search);
|
||||
return JSON.parse(run(args));
|
||||
}
|
||||
|
||||
export function vwCreateLogin({ name, username, password, url, notes }) {
|
||||
const item = {
|
||||
organizationId: ORG_ID,
|
||||
collectionIds: [AI_COLLECTION],
|
||||
folderId: null,
|
||||
type: 1,
|
||||
name,
|
||||
notes: notes || null,
|
||||
favorite: false,
|
||||
login: {
|
||||
username: username || null,
|
||||
password,
|
||||
uris: url ? [{ match: null, uri: url }] : [],
|
||||
},
|
||||
};
|
||||
const encoded = run(['encode'], JSON.stringify(item));
|
||||
return JSON.parse(run(['create', 'item', encoded, '--session', session()]));
|
||||
}
|
||||
|
||||
export function vwUpdatePassword(nameOrId, newPassword) {
|
||||
let item;
|
||||
try {
|
||||
item = JSON.parse(run(['get', 'item', nameOrId, '--session', session()]));
|
||||
} catch {
|
||||
const items = vwListOrgItems(nameOrId);
|
||||
item = items.find(i => i.name === nameOrId);
|
||||
if (!item) throw new Error(`Item not found: ${nameOrId}`);
|
||||
}
|
||||
item.login.password = newPassword;
|
||||
const encoded = run(['encode'], JSON.stringify(item));
|
||||
return JSON.parse(run(['edit', 'item', item.id, encoded, '--session', session()]));
|
||||
}
|
||||
Reference in New Issue
Block a user