域名出售页+escrow.com出售链接。(Caddy + Node.js)

环境

腾讯云cloudos9 系统

主要文件:

domains.json (域名列表文件)

{
  "**.sale": {
    "price": "1100",
    "description": "sales, a high-quality domain name suitable for start-up enterprises."
  }
}

server.js (数据处理文件)

const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
const PORT = 3000;

// ===== 配置区域 =====
const ESCROW_EMAIL = '**@**.com'; // 【重要】改成你的 Escrow 注册邮箱
const DOMAINS_FILE = path.join(__dirname, 'domains.json');
const TEMPLATE_FILE = path.join(__dirname, 'template.html');

// 读取模板和数据
let template = fs.readFileSync(TEMPLATE_FILE, 'utf8');
let domainsData = JSON.parse(fs.readFileSync(DOMAINS_FILE, 'utf8'));

// 监听文件变化,热更新数据
fs.watchFile(DOMAINS_FILE, () => {
    try {
        domainsData = JSON.parse(fs.readFileSync(DOMAINS_FILE, 'utf8'));
        console.log('域名数据已更新');
    } catch (e) {
        console.error('域名数据格式错误');
    }
});

// 1. Caddy 验证接口
app.get('/caddy-check', (req, res) => {
    const domain = req.query.domain;
    if (domainsData[domain]) {
        res.status(200).send('OK');
    } else {
        res.status(404).send('Not Found');
    }
});

// 2. 主页面路由
app.get('/', (req, res) => {
    let host = req.headers.host;
    if (!host) return res.status(400).send('Bad Request');
    host = host.split(':')[0];

    const info = domainsData[host];
    if (!info) {
        return res.status(404).send('<h1>404 - 该域名暂不出售</h1>');
    }

    // 注意:这里不再生成 buy_link 传给前端了
    let html = template
        .replace(/\{\{domain\}\}/g, host)
        .replace(/\{\{price\}\}/g, info.price)
        .replace(/\{\{description\}\}/g, info.description || '这是一个优质域名,正在出售中。');

    res.send(html);
});

// 3. 【新增】隐藏邮箱的支付跳转接口
app.get('/checkout', (req, res) => {
    // 获取当前访问的域名
    let host = req.headers.host;
    if (!host) return res.status(400).send('无法识别域名');
    host = host.split(':')[0];

    const info = domainsData[host];
    
    // 如果域名不在出售列表,拒绝跳转
    if (!info) {
        return res.send('该域名暂不出售');
    }

    // 在后端构造 Escrow 链接(用户在浏览器前端看不到这行代码)
    const escrowUrl = `https://www.escrow.com/checkout?domain=${host}&price=${info.price}&currency=USD&seller_email=${encodeURIComponent(ESCROW_EMAIL)}`;

    // 302 重定向:用户点击按钮 -> 来到这里 -> 瞬间跳转到 Escrow
    res.redirect(escrowUrl);
});

app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

template.html  (模板文件)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{domain}} - Domain For Sale</title>
    <style>
        :root {
            --primary-color: #2563eb;
            --price-color: #dc2626;
            --bg-color: #f3f4f6;
            --card-bg: #ffffff;
        }
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            background-color: var(--bg-color);
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
        }
        .container {
            background: var(--card-bg);
            width: 90%;
            max-width: 600px;
            border-radius: 16px;
            box-shadow: 0 10px 25px rgba(0,0,0,0.05);
            padding: 40px;
            text-align: center;
        }
        .badge {
            background-color: #e0f2fe;
            color: #0284c7;
            padding: 5px 12px;
            border-radius: 20px;
            font-size: 14px;
            font-weight: bold;
            display: inline-block;
            margin-bottom: 20px;
        }
        h1 {
            font-size: 32px;
            color: #111827;
            margin-bottom: 10px;
            word-break: break-all;
        }
        .description {
            color: #6b7280;
            margin-bottom: 30px;
            font-size: 16px;
        }
        .price-box {
            margin: 30px 0;
        }
        .price-label {
            font-size: 14px;
            color: #9ca3af;
            text-transform: uppercase;
            letter-spacing: 1px;
        }
        .price-value {
            font-size: 48px;
            font-weight: 800;
            color: var(--price-color);
            margin: 5px 0;
        }
        .price-unit {
            font-size: 18px;
            color: #4b5563;
        }
        .buy-btn {
            display: inline-block;
            background-color: var(--primary-color);
            color: white;
            padding: 16px 40px;
            border-radius: 8px;
            text-decoration: none;
            font-size: 18px;
            font-weight: bold;
            transition: background 0.3s;
            margin-top: 20px;
        }
        .buy-btn:hover {
            background-color: #1d4ed8;
        }
        .secure-info {
            margin-top: 40px;
            font-size: 12px;
            color: #9ca3af;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 5px;
        }
        .footer {
            margin-top: 30px;
            font-size: 12px;
            color: #9ca3af;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="badge">Domain For Sale</div>
        
        <h1>{{domain}}</h1>
        <p class="description">{{description}}</p>

        <div class="price-box">
            <div class="price-label">Current Price</div>
            <div class="price-value">${{price}} <span class="price-unit">USD</span></div>
        </div>

        <!-- Purchase button, link generated by the backend -->
        <a href="/checkout" class="buy-btn" target="_blank">Buy Now</a>

        <div class="secure-info">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" fill="#90EE90"></path>
            </svg>
            <span>The transaction is guaranteed by <span style="color:green;font-weight:bold">Escrow.com</span>, ensuring the safety of funds and the domain name.</span>
        </div>

        <div class="footer">
            &copy; 2026. All rights reserved.
        </div>
    </div>
</body>
</html>

 caddyfile

nano /etc/caddy/Caddyfile

 内容

# ==========================================
# 1. 全局配置块 (必须是文件开头,不要删除这个大括号)
# ==========================================
{
    # 你的邮箱,用于接收 Let's Encrypt 证书过期提醒
    email usasbe@outlook.com
    
    # 开启按需申请证书功能,并设置验证接口
    on_demand_tls {
        ask http://localhost:3000/caddy-check
    }
}

# ==========================================
# 2. HTTPS 服务 (监听 443 端口)
# ==========================================
:443 {
    # 在此处启用按需 TLS
    tls {
        on_demand
    }
    
    # 启用 Gzip 压缩
    encode gzip
    
    # 反向代理到本地的 Node.js 程序
    reverse_proxy localhost:3000
}

# ==========================================
# 3. HTTP 服务 (监听 80 端口,自动跳转 HTTPS)
# ==========================================
http:// {
    redir https://{host}{uri} permanent
}

 

 


 

部署流程:

第一步:环境准备

1、安装 Caddy (官方源安装):

# 1. 更新系统
yum update -y

# 2. 安装 Node.js (v18)
curl -fsSL https://rpm.nodesource.com/setup_18.x | bash -
yum install -y nodejs

# 3. 安装 Caddy (Web服务器)
# 这里使用 Caddy 官方提供的安装脚本
yum install -y yum-plugin-copr
yum copr enable @caddy/caddy -y
yum install -y caddy

# 4. 安装进程管理器 PM2 (保证服务不挂掉)
npm install -g pm2

2、安装 Node.js (同上,略)。

第二步:构建“域名展示系统”

1、创建项目目录

    mkdir -p /var/www/domain-sale
    cd /var/www/domain-sale
    npm init -y
    npm install express axios # 安装 web框架 和 http请求库
    

2、构建项目文件

# 创建项目目录
mkdir -p /var/www/domain-sale
cd /var/www/domain-sale

# 初始化项目
npm init -y

# 安装 Express (Web框架)
npm install express

1. 创建域名数据文件 (domains.json)

nano domains.json

写入以下内容

{
  "examp**leA.com": {
    "price": "100",
    "description": "适合初创企业的优质域名"
  },
  "exampl**eB.net": {
    "price": "2000",
    "description": "极具潜力的行业关键词"
  },
  "mydo**main.cn": {
    "price": "500",
    "description": "中国区首选品牌"
  }
}

2. 创建前端页面模板 (template.html)

nano template.html

 

3、创建 server.js (核心逻辑):(代码见前文)

nano server.js

 

第三步:配置 Caddy (自动 HTTPS 的秘密)

Caddy 的配置文件通常在 /etc/caddy/Caddyfile

利用 Caddy 的 On-Demand TLS 功能,它可以自动为任何解析到该服务器的域名申请证书,不需要提前写死域名列表。

    1. 编辑配置:
          nano /etc/caddy/Caddyfile    
    2. 写入内容:(见前文)
        
    3. 重启 Caddy
          systemctl restart caddy    
    4. 启动服务-启动nodejs
    5.     cd /var/www/domain-sale
          pm2 start server.js --name domain-sale
          pm2 save
          pm2 startup
          

       

  1. 启动 Caddy 服务
        systemctl restart caddy
        systemctl enable caddy
        

    开放防火墙端口 (腾讯云轻量服务器):

    • 方法一(控制台): 登录腾讯云控制台 -> 轻量应用服务器 -> 找到你的实例 -> 防火墙 -> 添加规则。
      • 放通 TCP 80 端口 (HTTP)
      • 放通 TCP 443 端口 (HTTPS)
    • 方法二(服务器内部): 如果你有 firewalld,确保允许服务。
    •         firewall-cmd --permanent --add-service=http
              firewall-cmd --permanent --add-service=https
              firewall-cmd --reload        

      第五步:如何使用?

    •  
      思考结束

      这是一个非常棒的实战需求。既然你有腾讯云轻量服务器,我们将采用 Caddy (自动HTTPS) + Node.js (后端逻辑) + HTML (前端页面) 的架构,搭建一个完全自动化、自带SSL证书、集成Escrow支付的专业域名出售平台。

      以下是完整的、可复制粘贴的部署方案。

      第一步:环境准备

      登录你的腾讯云轻量服务器,安装必要的软件。

       
      # 1. 更新系统
      yum update -y
      
      # 2. 安装 Node.js (v18)
      curl -fsSL https://rpm.nodesource.com/setup_18.x | bash -
      yum install -y nodejs
      
      # 3. 安装 Caddy (Web服务器)
      # 这里使用 Caddy 官方提供的安装脚本
      yum install -y yum-plugin-copr
      yum copr enable @caddy/caddy -y
      yum install -y caddy
      
      # 4. 安装进程管理器 PM2 (保证服务不挂掉)
      npm install -g pm2
      

      第二步:构建项目文件

      我们在 /var/www 目录下创建项目。

       
      # 创建项目目录
      mkdir -p /var/www/domain-sale
      cd /var/www/domain-sale
      
      # 初始化项目
      npm init -y
      
      # 安装 Express (Web框架)
      npm install express
      

      现在我们需要创建三个核心文件:domains.json (数据)、template.html (页面)、server.js (逻辑)。

      1. 创建域名数据文件 (domains.json)

      这是你的“数据库”,以后每增加一个域名,只需在这里添加一行。

       
      nano domains.json
      

      写入以下内容:

       
      {
        "exampleA.com": {
          "price": "100",
          "description": "适合初创企业的优质域名"
        },
        "exampleB.net": {
          "price": "2000",
          "description": "极具潜力的行业关键词"
        },
        "mydomain.cn": {
          "price": "500",
          "description": "中国区首选品牌"
        }
      }
      

      *(注意:请将键名改为你要出售的真实域名)*

      2. 创建前端页面模板 (template.html)

      这是一个专业的响应式出售页面。

       
      nano template.html
      

      写入完整代码:(见前文)

       

      代码生成完成

      HTML代码

      3. 创建后端逻辑 (server.js)

      这是核心处理程序,负责读取数据、替换页面内容、生成Escrow链接。

       
      nano server.js
      

      写入完整代码: (见前文)

      第三步:配置 Caddy (自动 HTTPS)

      Caddy 将监听 80 和 443 端口,自动申请 SSL 证书,并将流量转发给你的 Node.js 程序。

      1. 编辑 Caddy 配置文件:
       
          nano /etc/caddy/Caddyfile
      
      1. 删除原有内容,写入内容: (见前文)
       

      第四步:启动服务与防火墙

      1. 启动 Node.js 服务:
       
          cd /var/www/domain-sale
          pm2 start server.js --name domain-sale
          pm2 save
          pm2 startup
      
      1. 启动 Caddy 服务:
       
          systemctl restart caddy
          systemctl enable caddy
      
      1. 开放防火墙端口 (腾讯云轻量服务器):
        • 方法一(控制台): 登录腾讯云控制台 -> 轻量应用服务器 -> 找到你的实例 -> 防火墙 -> 添加规则。
          • 放通 TCP 80 端口 (HTTP)
          • 放通 TCP 443 端口 (HTTPS)
        • 方法二(服务器内部): 如果你有 firewalld,确保允许服务。
       
              firewall-cmd --permanent --add-service=http
              firewall-cmd --permanent --add-service=https
              firewall-cmd --reload
      

      第五步:如何使用?

      现在你的平台已经搭建好了,操作流程非常简单:

      1. 添加新域名:

        • 编辑 /var/www/domain-sale/domains.json,加上新域名和价格。
        • 无需重启任何服务,代码会自动热更新。
      2. DNS 解析:

        • 去你的域名 DNS 管理处(如 DNSPod),将域名的 A 记录 指向你的腾讯云服务器 IP。
      3. 访问效果:

        • 用户访问 http://example***A.com -> 自动跳转 https://example***A.com (浏览器出现小绿锁) -> 页面显示价格 $100。
        • 用户点击“立即购买” -> 跳转到 escrow.com 预填好的支付页面 -> 你坐等收款。
注意: 如果你的 domains.json 经常变动,你可以让这个接口实时读取文件,或者每次更新数据后重启 Node 进程。

总结流程

  • 数据维护: 你只需要维护一个 domains.json 文件,添加新域名和价格。
  • DNS 解析: 将域名 A 记录指向腾讯云服务器 IP。
  • 自动握手: 用户访问域名 -> Caddy 检测到 HTTPS 请求 -> 询问 Node.js “我有这个域名吗?” -> Node.js 说 “有” -> Caddy 自动向 Let’s Encrypt 申请证书 -> 浏览器显示小绿锁。
  • 展示与交易: Node.js 返回带有价格的页面,按钮链接指向 Escrow.com 的预填付款页修改完 Caddyfile 后,执行以下命令:

关于 Escrow 集成的补充

上面的代码使用的是URL 参数预填模式 (escrow.com/checkout?...)。

  • 优点: 实现最简单,不需要处理 API 密钥在前后端交互的复杂性。
  • 缺点: 用户点击购买后,还需要在 Escrow 页面再确认一遍信息。

如果你追求极致体验(用户点击购买 -> 直接生成账单 -> 用户只管付钱),你需要使用 Escrow REST API
这需要你在 Node.js 后端使用 axios 发送 POST 请求到 https://api.escrow.com/transaction 创建交易,然后返回一个支付链接。这稍微复杂一点,且涉及到 API Key 的安全存储,但对于 $2000 级别的交易来说,这种专业度是值得的。上面的代码采用了折中的“预填链接”方案,既简单又实用。


 

另外:

修改完 Caddyfile 后,执行以下命令:

# 1. 检查 Caddy 配置语法
caddy validate --config /etc/caddy/Caddyfile

# 2. 如果验证通过,重启 Caddy 服务
systemctl restart caddy

# 3. 确保 Node.js 应用正在运行
pm2 restart domain-sale  # 如果之前已启动,可重启使配置生效

清空文件:

# 使用 echo 清空文件再编辑,防止旧内容残留
    echo "" > /etc/caddy/Caddyfile

修改文件后刷新

pm2 restart domain-sale

 

posted @ 2026-04-05 22:01  POTUS88  阅读(1)  评论(0)    收藏  举报