Files
AgapHost/agap-mcp/src/vaultwarden.js
Alvis b8efe4732d 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
2026-07-04 13:28:04 +00:00

103 lines
2.9 KiB
JavaScript

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()]));
}