文件预览

config.js

查看 imap-smtp-email 技能包中的文件内容。

文件内容

scripts/config.js

#!/usr/bin/env node

const path = require('path');
const os = require('os');
const fs = require('fs');
const dotenv = require('dotenv');
const { PROVIDERS } = require('./providers');

// Config file locations
const LEGACY_ENV_PATH = path.join(os.homedir(), '.config', 'imap-smtp-email', '.env');
const SHARED_ENV_PATH = path.join(os.homedir(), '.config', 'mail-skills', '.env');
const FALLBACK_ENV_PATH = path.resolve(__dirname, '../.env');

function findEnvPath() {
  if (fs.existsSync(LEGACY_ENV_PATH)) return { path: LEGACY_ENV_PATH, type: 'legacy' };
  if (fs.existsSync(SHARED_ENV_PATH)) return { path: SHARED_ENV_PATH, type: 'shared' };
  if (fs.existsSync(FALLBACK_ENV_PATH)) return { path: FALLBACK_ENV_PATH, type: 'legacy' };
  return null;
}

// Parse and strip --account <name> from process.argv
function parseAccountFromArgv(argv) {
  const args = argv.slice(2);
  const idx = args.indexOf('--account');
  if (idx !== -1 && idx + 1 < args.length) {
    const name = args[idx + 1];
    args.splice(idx, 2);
    return { accountName: name, remainingArgs: args };
  }
  return { accountName: null, remainingArgs: args };
}

// Build config from legacy format (IMAP_HOST/IMAP_USER etc.)
function buildConfigFromLegacy(env, prefix) {
  const p = prefix ? `${prefix}_` : '';

  if (prefix && !env[`${p}IMAP_HOST`]) {
    console.error(`Error: Account "${prefix.toLowerCase()}" not found in config. Check ~/.config/imap-smtp-email/.env`);
    process.exit(1);
  }

  return {
    imap: {
      host: env[`${p}IMAP_HOST`] || '127.0.0.1',
      port: parseInt(env[`${p}IMAP_PORT`]) || 1143,
      user: env[`${p}IMAP_USER`],
      pass: env[`${p}IMAP_PASS`],
      tls: env[`${p}IMAP_TLS`] === 'true',
      rejectUnauthorized: env[`${p}IMAP_REJECT_UNAUTHORIZED`] !== 'false',
      mailbox: env[`${p}IMAP_MAILBOX`] || 'INBOX',
    },
    smtp: {
      host: env[`${p}SMTP_HOST`],
      port: parseInt(env[`${p}SMTP_PORT`]) || 587,
      user: env[`${p}SMTP_USER`],
      pass: env[`${p}SMTP_PASS`],
      secure: env[`${p}SMTP_SECURE`] === 'true',
      from: env[`${p}SMTP_FROM`] || env[`${p}SMTP_USER`],
      rejectUnauthorized: env[`${p}SMTP_REJECT_UNAUTHORIZED`] !== 'false',
    },
    allowedReadDirs: (env.ALLOWED_READ_DIRS || '').split(',').map(d => d.trim()).filter(Boolean),
    allowedWriteDirs: (env.ALLOWED_WRITE_DIRS || '').split(',').map(d => d.trim()).filter(Boolean),
  };
}

// Build config from shared format (PROVIDER/USERNAME/PASSWORD)
function buildConfigFromShared(env, prefix) {
  const p = prefix ? `${prefix}_` : '';

  const provider = env[`${p}PROVIDER`];
  if (!provider) return null;

  const username = env[`${p}USERNAME`];
  const password = env[`${p}PASSWORD`];

  if (!username || !password) return null;

  let imapPreset, smtpPreset;

  if (provider === 'custom') {
    const imapHost = env[`${p}IMAP_HOST`];
    if (!imapHost) return null;
    imapPreset = {
      host: imapHost,
      port: parseInt(env[`${p}IMAP_PORT`]) || 993,
      tls: env[`${p}IMAP_TLS`] !== 'false',
      rejectUnauthorized: env[`${p}IMAP_REJECT_UNAUTHORIZED`] !== 'false',
    };
    smtpPreset = {
      host: env[`${p}SMTP_HOST`],
      port: parseInt(env[`${p}SMTP_PORT`]) || 587,
      secure: env[`${p}SMTP_SECURE`] === 'true',
      rejectUnauthorized: env[`${p}SMTP_REJECT_UNAUTHORIZED`] !== 'false',
    };
  } else {
    const preset = PROVIDERS[provider];
    if (!preset || !preset.imap) return null;
    imapPreset = preset.imap;
    smtpPreset = preset.smtp;
  }

  return {
    imap: {
      host: imapPreset.host,
      port: imapPreset.port,
      user: username,
      pass: password,
      tls: imapPreset.tls,
      rejectUnauthorized: env[`${p}IMAP_REJECT_UNAUTHORIZED`] !== undefined
        ? env[`${p}IMAP_REJECT_UNAUTHORIZED`] !== 'false'
        : imapPreset.rejectUnauthorized,
      mailbox: env[`${p}IMAP_MAILBOX`] || 'INBOX',
    },
    smtp: {
      host: smtpPreset.host,
      port: smtpPreset.port,
      user: username,
      pass: password,
      secure: smtpPreset.secure,
      from: username,
      rejectUnauthorized: env[`${p}SMTP_REJECT_UNAUTHORIZED`] !== undefined
        ? env[`${p}SMTP_REJECT_UNAUTHORIZED`] !== 'false'
        : smtpPreset.rejectUnauthorized,
    },
    allowedReadDirs: (env.ALLOWED_READ_DIRS || '~/Downloads,~/Documents').split(',').map(d => d.trim()).filter(Boolean),
    allowedWriteDirs: (env.ALLOWED_WRITE_DIRS || '~/Downloads').split(',').map(d => d.trim()).filter(Boolean),
  };
}

// List all configured accounts from all config sources
function listAccounts() {
  const allAccounts = [];
  const seen = new Set();
  let primaryConfigPath = null;

  // 1. Legacy config
  if (fs.existsSync(LEGACY_ENV_PATH)) {
    primaryConfigPath = LEGACY_ENV_PATH;
    const env = dotenv.config({ path: LEGACY_ENV_PATH }).parsed || {};
    const accounts = scanLegacyAccounts(env);
    for (const a of accounts) seen.add(a.name);
    allAccounts.push(...accounts);
  }

  // 2. Shared config
  if (fs.existsSync(SHARED_ENV_PATH)) {
    if (!primaryConfigPath) primaryConfigPath = SHARED_ENV_PATH;
    const env = dotenv.config({ path: SHARED_ENV_PATH }).parsed || {};
    const accounts = scanSharedAccounts(env);
    for (const a of accounts) {
      if (!seen.has(a.name)) {
        allAccounts.push(a);
        seen.add(a.name);
      }
    }
  }

  // 3. Fallback
  if (fs.existsSync(FALLBACK_ENV_PATH) && !primaryConfigPath) {
    primaryConfigPath = FALLBACK_ENV_PATH;
    const env = dotenv.config({ path: FALLBACK_ENV_PATH }).parsed || {};
    const accounts = scanLegacyAccounts(env);
    for (const a of accounts) {
      if (!seen.has(a.name)) {
        allAccounts.push(a);
        seen.add(a.name);
      }
    }
  }

  return { accounts: allAccounts, configPath: primaryConfigPath };
}

function scanLegacyAccounts(env) {
  const accounts = [];
  const seen = new Set();

  if (env.IMAP_HOST) {
    accounts.push(createAccountObject(env, '', 'default'));
    seen.add('default');
  }

  for (const key of Object.keys(env)) {
    const match = key.match(/^([A-Z0-9]+)_IMAP_HOST$/);
    if (match) {
      const prefix = match[1];
      const name = prefix.toLowerCase();
      if (!seen.has(name)) {
        accounts.push(createAccountObject(env, prefix + '_', name));
        seen.add(name);
      }
    }
  }

  return accounts;
}

function scanSharedAccounts(env) {
  const accounts = [];
  const seen = new Set();

  if (env.PROVIDER) {
    const preset = PROVIDERS[env.PROVIDER];
    accounts.push({
      name: 'default',
      email: env.USERNAME || '-',
      imapHost: (preset && preset.imap) ? preset.imap.host : (env.IMAP_HOST || '-'),
      smtpHost: (preset && preset.smtp) ? preset.smtp.host : (env.SMTP_HOST || '-'),
      isComplete: !!(env.USERNAME && env.PASSWORD && preset?.imap),
    });
    seen.add('default');
  }

  for (const key of Object.keys(env)) {
    const match = key.match(/^([A-Z0-9]+)_PROVIDER$/);
    if (match) {
      const rawPrefix = match[1];
      const name = rawPrefix.toLowerCase();
      if (!seen.has(name)) {
        const p = `${rawPrefix}_`;
        const preset = PROVIDERS[env[`${p}PROVIDER`]];
        accounts.push({
          name,
          email: env[`${p}USERNAME`] || '-',
          imapHost: (preset && preset.imap) ? preset.imap.host : (env[`${p}IMAP_HOST`] || '-'),
          smtpHost: (preset && preset.smtp) ? preset.smtp.host : (env[`${p}SMTP_HOST`] || '-'),
          isComplete: !!(env[`${p}USERNAME`] && env[`${p}PASSWORD`] && preset?.imap),
        });
        seen.add(name);
      }
    }
  }

  return accounts;
}

function createAccountObject(env, prefix, name) {
  const p = prefix;
  return {
    name,
    email: env[`${p}IMAP_USER`] || env[`${p}SMTP_FROM`] || '-',
    imapHost: env[`${p}IMAP_HOST`] || '-',
    smtpHost: env[`${p}SMTP_HOST`] || '-',
    isComplete: isAccountComplete(env, prefix)
  };
}

function isAccountComplete(env, prefix) {
  const p = prefix;
  return !!(
    env[`${p}IMAP_HOST`] &&
    env[`${p}IMAP_USER`] &&
    env[`${p}IMAP_PASS`] &&
    env[`${p}SMTP_HOST`]
  );
}

// --- Module initialization ---
const envInfo = findEnvPath();

const { accountName, remainingArgs } = parseAccountFromArgv(process.argv);
const prefix = accountName ? accountName.toUpperCase() : null;

process.argv = [process.argv[0], process.argv[1], ...remainingArgs];

let config;
if (envInfo) {
  const parsed = dotenv.config({ path: envInfo.path }).parsed || {};
  if (envInfo.type === 'shared') {
    config = buildConfigFromShared(parsed, prefix);
  } else {
    config = buildConfigFromLegacy(parsed, prefix);
  }
}

if (!config) {
  if (accountName) {
    console.error(`Error: Account "${accountName}" not found. Check ~/.config/imap-smtp-email/.env or ~/.config/mail-skills/.env`);
  } else {
    console.error('Error: No email configuration found. Run "bash setup.sh" to configure.');
  }
  process.exit(1);
}

module.exports = config;
module.exports.listAccounts = listAccounts;