~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
75 lines · 1.7 KB Raw History
/**
 * Bridge to `git credential` so app passwords can live in the user's
 * configured credential store under protocol=atproto.
 */

import { spawnSync } from 'node:child_process';

/**
 * @typedef {Object} Credential
 * @property {string} [username]
 * @property {string} [password]
 */

/**
 * @param {'fill'|'approve'|'reject'} action
 * @param {Record<string, string>} fields
 * @returns {Credential}
 */
function credential(action, fields) {
  const input = `${Object.entries(fields)
    .map(([k, v]) => `${k}=${v}`)
    .join('\n')}\n\n`;
  const result = spawnSync('git', ['credential', action], {
    input,
    encoding: 'utf8',
    stdio: ['pipe', 'pipe', 'pipe'],
  });
  if (result.status !== 0) return {};
  /** @type {Credential} */
  const parsed = {};
  for (const line of (result.stdout ?? '').split('\n')) {
    const eq = line.indexOf('=');
    if (eq === -1) continue;
    const key = line.slice(0, eq);
    if (key === 'username') parsed.username = line.slice(eq + 1);
    if (key === 'password') parsed.password = line.slice(eq + 1);
  }
  return parsed;
}

/**
 * @param {string} host
 * @returns {Credential}
 */
export function credentialFill(host) {
  return credential('fill', { protocol: 'atproto', host });
}

/**
 * @param {string} host
 * @param {Credential} cred
 */
export function credentialApprove(host, cred) {
  if (!cred.password) return;
  credential('approve', {
    protocol: 'atproto',
    host,
    username: cred.username ?? '',
    password: cred.password,
  });
}

/**
 * @param {string} host
 * @param {Credential} cred
 */
export function credentialReject(host, cred) {
  if (!cred.password) return;
  credential('reject', {
    protocol: 'atproto',
    host,
    username: cred.username ?? '',
    password: cred.password,
  });
}