setup.ts raw

   1  import { writeFileSync, existsSync, mkdirSync } from 'fs';
   2  import { join, dirname } from 'path';
   3  import { getDb, closeDb } from './lib/db';
   4  import { seedProducts } from './lib/products';
   5  import bcrypt from 'bcryptjs';
   6  import crypto from 'crypto';
   7  
   8  function env(key: string, def = ''): string {
   9    return process.env[key] || def;
  10  }
  11  
  12  async function main() {
  13    console.log('\n=== Zlattea - Setup ===\n');
  14  
  15    const dbPath = env('DB_PATH', join(process.cwd(), 'data', 'shop.db'));
  16    const dataDir = dirname(dbPath);
  17    if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true });
  18  
  19    const siteUrl = env('SITE_URL', 'http://localhost:3000');
  20    const adminEmail = env('ADMIN_EMAIL', 'admin@zlattea.com');
  21    const adminPass = env('ADMIN_PASSWORD');
  22    const port = env('PORT', '3000');
  23    const apiPort = env('API_PORT', String(Number(port) + 1));
  24    const bind = env('HOST', '127.0.0.1');
  25    const nostrPubkey = env('NOSTR_PUBKEY');
  26    const lud16 = env('LUD16_ADDRESS', 'shop@zlattea.com');
  27  
  28    if (!adminPass) {
  29      console.error('ADMIN_PASSWORD must be set explicitly - refusing to create an admin with a default password.');
  30      process.exit(1);
  31    }
  32  
  33    const envFile = `# site
  34  SITE_URL=${siteUrl}
  35  PORT=${port}
  36  API_PORT=${apiPort}
  37  HOST=${bind}
  38  DB_PATH=${dbPath}
  39  
  40  # lightning
  41  LUD16_ADDRESS=${lud16}
  42  BTC_BGN_RATE=${env('BTC_BGN_RATE')}
  43  
  44  # stripe (card payments - google pay, apple pay come free with stripe)
  45  STRIPE_SECRET_KEY=${env('STRIPE_SECRET_KEY')}
  46  STRIPE_PUBLISHABLE_KEY=${env('STRIPE_PUBLISHABLE_KEY')}
  47  STRIPE_WEBHOOK_SECRET=${env('STRIPE_WEBHOOK_SECRET')}
  48  
  49  # btcpayserver (onchain bitcoin)
  50  BTCPAY_URL=${env('BTCPAY_URL')}
  51  BTCPAY_API_KEY=${env('BTCPAY_API_KEY')}
  52  BTCPAY_STORE_ID=${env('BTCPAY_STORE_ID')}
  53  BTCPAY_WEBHOOK_SECRET=${env('BTCPAY_WEBHOOK_SECRET')}
  54  
  55  # courier api credentials (optional - leave empty if not needed)
  56  SPEEDY_USERNAME=${env('SPEEDY_USERNAME')}
  57  SPEEDY_PASSWORD=${env('SPEEDY_PASSWORD')}
  58  
  59  # email (order confirmations; leave empty to disable)
  60  SMTP_HOST=${env('SMTP_HOST')}
  61  SMTP_PORT=${env('SMTP_PORT', '587')}
  62  SMTP_USER=${env('SMTP_USER')}
  63  SMTP_PASS=${env('SMTP_PASS')}
  64  MAIL_FROM=${env('MAIL_FROM', 'shop@zlattea.com')}
  65  `;
  66    writeFileSync(join(process.cwd(), '.env'), envFile);
  67  
  68    console.log('setting up database...');
  69    process.env.DB_PATH = dbPath;
  70    const db = getDb(); // applies schema + migration
  71    const seeded = seedProducts();
  72    console.log(`products: ${seeded}`);
  73  
  74    const existing = db.query('SELECT id FROM users WHERE email = ?').get(adminEmail) as any;
  75    if (existing) {
  76      console.log('admin user already exists.');
  77    } else {
  78      const hash = bcrypt.hashSync(adminPass, 10);
  79      db.run('INSERT INTO users (id, email, password_hash, name, is_admin) VALUES (?, ?, ?, ?, 1)',
  80        [crypto.randomUUID(), adminEmail, hash, 'Admin']);
  81      console.log('admin user created.');
  82    }
  83    if (nostrPubkey) {
  84      db.run('UPDATE users SET nostr_pubkey = ? WHERE email = ?', [nostrPubkey, adminEmail]);
  85    }
  86  
  87    // default shipping config
  88    db.run("INSERT OR IGNORE INTO content (key, value) VALUES ('shipping_rate_per_kg', '500')");
  89    db.run("INSERT OR IGNORE INTO content (key, value) VALUES ('free_shipping_threshold', '5000')");
  90    console.log('default shipping: 5.00 EUR/kg, free over 50.00 EUR');
  91    closeDb();
  92  
  93    console.log('\nsetup complete. run: bun run build && bun run start\n');
  94  }
  95  
  96  main().catch(e => { console.error(e); process.exit(1); });
  97