← back to Handbag Auth Nextjs

scripts/manageHandbagAffiliates.ts

195 lines

// Script to manage handbag affiliate program registry
// Add, update, and query affiliate partnerships

import fs from "fs";
import path from "path";
import type { HandbagAffiliateProgram } from "../src/types/handbagRss";

const AFFILIATES_FILE = path.resolve("data/handbag_affiliates.json");

// Load all affiliate programs
export function loadHandbagAffiliates(): HandbagAffiliateProgram[] {
  if (!fs.existsSync(AFFILIATES_FILE)) {
    return [];
  }
  return JSON.parse(fs.readFileSync(AFFILIATES_FILE, "utf8"));
}

// Save affiliate programs
export function saveHandbagAffiliates(list: HandbagAffiliateProgram[]) {
  const dir = path.dirname(AFFILIATES_FILE);
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
  fs.writeFileSync(AFFILIATES_FILE, JSON.stringify(list, null, 2));
}

// Add or update an affiliate program
export function addOrUpdateAffiliate(program: HandbagAffiliateProgram) {
  const list = loadHandbagAffiliates();
  const existing = list.find((p) => p.domain === program.domain);

  if (existing) {
    Object.assign(existing, program);
    console.log(`Updated affiliate: ${program.name}`);
  } else {
    list.push(program);
    console.log(`Added new affiliate: ${program.name}`);
  }

  saveHandbagAffiliates(list);
  return program;
}

// Get affiliate by domain
export function getAffiliateByDomain(domain: string): HandbagAffiliateProgram | undefined {
  const list = loadHandbagAffiliates();
  return list.find((p) => p.domain === domain);
}

// Get affiliates by merchant type
export function getAffiliatesByType(type: string): HandbagAffiliateProgram[] {
  const list = loadHandbagAffiliates();
  return list.filter((p) => p.merchantType === type);
}

// Extract domain from URL
export function getDomain(url: string): string {
  try {
    const u = new URL(url);
    return u.hostname.replace(/^www\./, "");
  } catch {
    return url.replace(/^https?:\/\//, "").split("/")[0].replace(/^www\./, "");
  }
}

// List all affiliates
export function listAffiliates() {
  const list = loadHandbagAffiliates();
  console.log(`\n=== Handbag Affiliate Programs (${list.length}) ===\n`);

  const grouped = list.reduce((acc, prog) => {
    if (!acc[prog.merchantType]) acc[prog.merchantType] = [];
    acc[prog.merchantType].push(prog);
    return acc;
  }, {} as Record<string, HandbagAffiliateProgram[]>);

  for (const [type, programs] of Object.entries(grouped)) {
    console.log(`${type.toUpperCase()} (${programs.length}):`);
    programs.forEach((p) => {
      console.log(`  - ${p.name} (${p.domain})`);
      if (p.affiliateNetwork) console.log(`    Network: ${p.affiliateNetwork}`);
      if (p.commissionStructure) console.log(`    Commission: ${p.commissionStructure}`);
    });
    console.log();
  }
}

// Import from merchants CSV
export function importFromMerchantsCsv() {
  const csvPath = path.resolve("data/handbag_merchants.csv");
  if (!fs.existsSync(csvPath)) {
    console.error("Merchants CSV not found at:", csvPath);
    return;
  }

  const csv = fs.readFileSync(csvPath, "utf8");
  const lines = csv.split("\n").slice(1); // Skip header

  let imported = 0;
  const existingList = loadHandbagAffiliates();

  for (const line of lines) {
    if (!line.trim()) continue;

    const [name, domain, merchantType, affiliateNetwork, signupUrl, notes] = line
      .split(",")
      .map((s) => s.trim());

    if (!domain) continue;

    // Check if already exists
    if (existingList.find((p) => p.domain === domain)) {
      continue;
    }

    const program: HandbagAffiliateProgram = {
      id: domain.replace(/\./g, "-"),
      name,
      website: `https://${domain}`,
      domain,
      merchantType: merchantType as any,
      affiliateNetwork: affiliateNetwork || undefined,
      signupUrl: signupUrl || undefined,
      notes: notes || undefined,
    };

    addOrUpdateAffiliate(program);
    imported++;
  }

  console.log(`Imported ${imported} new affiliate programs from CSV.`);
}

// CLI interface
const isMainModule = import.meta.url === `file://${process.argv[1]}`;

if (isMainModule) {
  const command = process.argv[2];

  switch (command) {
    case "list":
      listAffiliates();
      break;

    case "add": {
      const sample: HandbagAffiliateProgram = {
        id: "fashionphile",
        name: "Fashionphile",
        website: "https://www.fashionphile.com",
        domain: "fashionphile.com",
        merchantType: "reseller",
        affiliateNetwork: "Impact",
        commissionStructure: "5% commission",
        cookieDurationDays: 30,
        signupUrl: "https://fashionphile.com/affiliates",
        notes: "Leading pre-owned luxury handbag reseller with authentication guarantee",
      };

      addOrUpdateAffiliate(sample);
      console.log("\nSample affiliate added. Edit the script to add your own.");
      break;
    }

    case "import-csv":
      importFromMerchantsCsv();
      break;

    case "by-type": {
      const type = process.argv[3];
      if (!type) {
        console.error("Usage: ts-node manageHandbagAffiliates.ts by-type <type>");
        console.log("Types: brand, retailer, reseller, auction");
        process.exit(1);
      }
      const programs = getAffiliatesByType(type);
      console.log(`\n${type.toUpperCase()} Affiliates (${programs.length}):\n`);
      programs.forEach((p) => {
        console.log(`- ${p.name} (${p.domain})`);
      });
      break;
    }

    default:
      console.log("Handbag Affiliate Program Manager");
      console.log("\nUsage:");
      console.log("  ts-node scripts/manageHandbagAffiliates.ts <command>");
      console.log("\nCommands:");
      console.log("  list          - List all affiliate programs");
      console.log("  add           - Add sample affiliate (edit script to customize)");
      console.log("  import-csv    - Import from handbag_merchants.csv");
      console.log("  by-type <type> - List affiliates by merchant type");
      process.exit(1);
  }
}