应佬友的要求再出一篇文章,前面薅了wispbyte.com的羊毛,但是吧容器比较弱小,强一点的就是huggingface.co的容器:

Free版本就是2vCPU和16GB RAM,就已经非常强了

image-20260106092514521

那先介绍一下huggingface,其实这是一家非常强悍的公司,可以称作是“AI界的GitHub”,可以分享模型,数据集,然后演示应用。

我们用到的就是这个演示应用(Spaces)

首先注册好账号,然后右上角点击头像按钮,新建一个Space

image-20260106104401915

然后填入必须项

  • Space name:就写myhome
  • Short description:Save Our Home
  • License:MIT
  • SDK:选择Docker
  • Docker template: 选择Blank
  • Space hardware:选择Free

image-20260106104916165

其它都不选,最后CreateSpace即可

然后就建好了,其实是给你开了一个git的repo

我们到右上角,选择Files选项

image-20260106105206257

然后打开的文件页面,只有两个文件

image-20260106105253425

老套路,建立一个Dockerfile文件,就是容器的打包文件

image-20260106105349755

Dockerfile的内容,编辑好,然后提交,注意:Linux有严格的大小写要求,务必遵守

FROM nikolaik/python-nodejs:python3.14-nodejs22

WORKDIR /tmp

COPY index.js /tmp/index.js
COPY package.json /tmp/package.json

COPY index.html /tmp/index.html

CMD npm install && node index.js

image-20260106114337260

然后我们把index.js准备好,需要修改几个地方

const http = require('http');
const https = require('https');
const fs = require('fs');
const axios = require('axios');
const net = require('net');
const path = require('path');
const crypto = require('crypto');
const { Buffer } = require('buffer');
const { WebSocket, createWebSocketStream } = require('ws');

// 生成 UUID v4
function generateUUID() {
  return crypto.randomUUID();
}

const UUID = process.env.UUID || generateUUID();
const DOMAIN = process.env.DOMAIN || '1234.abc.com';       // 填写项目域名
const WSPATH = process.env.WSPATH || UUID.slice(0, 8);     // 节点路径,默认获取uuid前8位
const SUB_PATH = process.env.SUB_PATH || 'sub';            // 获取节点的订阅路径
const NAME = process.env.NAME || '';                       // 节点名称
const PORT = process.env.PORT || 7860;                     // http和ws服务端口
const TLS_KEY_PATH = process.env.TLS_KEY_PATH || 'tls.key';
const TLS_CERT_PATH = process.env.TLS_CERT_PATH || 'tls.crt'; 

let ISP = '';
const GetISP = async () => {
  try {
    const res = await axios.get('https://api.ip.sb/geoip');
    const data = res.data;
    ISP = `${data.country_code}-${data.isp}`.replace(/ /g, '_');
  } catch (e) {
    ISP = 'Unknown';
  }
}
GetISP();

function normalizeSecret(value) {
  if (!value) return '';
  return value.includes('\\n') ? value.replace(/\\n/g, '\n') : value;
}

function readOptionalFile(filePath) {
  if (!filePath) return '';
  const resolvedPath = path.isAbsolute(filePath) ? filePath : path.join(__dirname, filePath);
  try {
    return fs.readFileSync(resolvedPath, 'utf8');
  } catch (error) {
    console.warn(`Failed to read TLS file ${resolvedPath}: ${error.message}`);
    return '';
  }
}

function getTLSOptions() {
  let key = normalizeSecret(process.env.TLS_KEY);
  let cert = normalizeSecret(process.env.TLS_CERT);

  if (!key) {
    key = readOptionalFile(TLS_KEY_PATH);
  }
  if (!cert) {
    cert = readOptionalFile(TLS_CERT_PATH);
  }

  if (key && cert) {
    return { key, cert };
  }
  if (key || cert) {
    console.warn('Both TLS key and certificate are required; falling back to HTTP.');
  }
  return null;
}

const requestHandler = (req, res) => {
  if (req.url === '/') {
    const filePath = path.join(__dirname, 'index.html');
    fs.readFile(filePath, 'utf8', (err, content) => {
      if (err) {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.end('Hello world!');
        return;
      }
      res.writeHead(200, { 'Content-Type': 'text/html' });
      res.end(content);
    });
    return;
  } else if (req.url === `/${SUB_PATH}`) {
    const namePart = NAME ? `${NAME}-${ISP}` : ISP;
    const vlessURL = `vless://${UUID}@${DOMAIN}?encryption=none&security=tls&sni=${DOMAIN}&fp=chrome&type=ws&host=${DOMAIN}&path=%2F${WSPATH}#${namePart}`;
    const trojanURL = `trojan://${UUID}@${DOMAIN}?security=tls&sni=${DOMAIN}&fp=chrome&type=ws&host=${DOMAIN}&path=%2F${WSPATH}#${namePart}`;
    const subscription = vlessURL + '\n' + trojanURL;
    const base64Content = Buffer.from(subscription).toString('base64');

    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end(base64Content + '\n');
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found\n');
  }
};

const tlsOptions = getTLSOptions();
const httpServer = tlsOptions ? https.createServer(tlsOptions, requestHandler) : http.createServer(requestHandler);

const wss = new WebSocket.Server({ server: httpServer });
const uuid = UUID.replace(/-/g, "");
const DNS_SERVERS = ['8.8.4.4', '1.1.1.1'];
// Custom DNS
function resolveHost(host) {
  return new Promise((resolve, reject) => {
    if (/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(host)) {
      resolve(host);
      return;
    }
    let attempts = 0;
    function tryNextDNS() {
      if (attempts >= DNS_SERVERS.length) {
        reject(new Error(`Failed to resolve ${host} with all DNS servers`));
        return;
      }
      const dnsServer = DNS_SERVERS[attempts];
      attempts++;
      const dnsQuery = `https://dns.google/resolve?name=${encodeURIComponent(host)}&type=A`;
      axios.get(dnsQuery, {
        timeout: 5000,
        headers: {
          'Accept': 'application/dns-json'
        }
      })
      .then(response => {
        const data = response.data;
        if (data.Status === 0 && data.Answer && data.Answer.length > 0) {
          const ip = data.Answer.find(record => record.type === 1);
          if (ip) {
            resolve(ip.data);
            return;
          }
        }
        tryNextDNS();
      })
      .catch(error => {
        tryNextDNS();
      });
    }

    tryNextDNS();
  });
}

// VLE-SS处理
function handleVlessConnection(ws, msg) {
  const [VERSION] = msg;
  const id = msg.slice(1, 17);
  if (!id.every((v, i) => v == parseInt(uuid.substr(i * 2, 2), 16))) return false;

  let i = msg.slice(17, 18).readUInt8() + 19;
  const port = msg.slice(i, i += 2).readUInt16BE(0);
  const ATYP = msg.slice(i, i += 1).readUInt8();
  const host = ATYP == 1 ? msg.slice(i, i += 4).join('.') :
    (ATYP == 2 ? new TextDecoder().decode(msg.slice(i + 1, i += 1 + msg.slice(i, i + 1).readUInt8())) :
    (ATYP == 3 ? msg.slice(i, i += 16).reduce((s, b, i, a) => (i % 2 ? s.concat(a.slice(i - 1, i + 1)) : s), []).map(b => b.readUInt16BE(0).toString(16)).join(':') : ''));
  ws.send(new Uint8Array([VERSION, 0]));
  const duplex = createWebSocketStream(ws);
  resolveHost(host)
    .then(resolvedIP => {
      net.connect({ host: resolvedIP, port }, function() {
        this.write(msg.slice(i));
        duplex.on('error', () => {}).pipe(this).on('error', () => {}).pipe(duplex);
      }).on('error', () => {});
    })
    .catch(error => {
      net.connect({ host, port }, function() {
        this.write(msg.slice(i));
        duplex.on('error', () => {}).pipe(this).on('error', () => {}).pipe(duplex);
      }).on('error', () => {});
    });

  return true;
}

// Tro-jan处理
function handleTrojanConnection(ws, msg) {
  try {
    if (msg.length < 58) return false;
    const receivedPasswordHash = msg.slice(0, 56).toString();
    const possiblePasswords = [
      UUID,
    ];

    let matchedPassword = null;
    for (const pwd of possiblePasswords) {
      const hash = crypto.createHash('sha224').update(pwd).digest('hex');
      if (hash === receivedPasswordHash) {
        matchedPassword = pwd;
        break;
      }
    }

    if (!matchedPassword) return false;
    let offset = 56;
    if (msg[offset] === 0x0d && msg[offset + 1] === 0x0a) {
      offset += 2;
    }

    const cmd = msg[offset];
    if (cmd !== 0x01) return false;
    offset += 1;
    const atyp = msg[offset];
    offset += 1;
    let host, port;
    if (atyp === 0x01) {
      host = msg.slice(offset, offset + 4).join('.');
      offset += 4;
    } else if (atyp === 0x03) {
      const hostLen = msg[offset];
      offset += 1;
      host = msg.slice(offset, offset + hostLen).toString();
      offset += hostLen;
    } else if (atyp === 0x04) {
      host = msg.slice(offset, offset + 16).reduce((s, b, i, a) => 
        (i % 2 ? s.concat(a.slice(i - 1, i + 1)) : s), [])
        .map(b => b.readUInt16BE(0).toString(16)).join(':');
      offset += 16;
    } else {
      return false;
    }

    port = msg.readUInt16BE(offset);
    offset += 2;

    if (offset < msg.length && msg[offset] === 0x0d && msg[offset + 1] === 0x0a) {
      offset += 2;
    }

    const duplex = createWebSocketStream(ws);

    resolveHost(host)
      .then(resolvedIP => {
        net.connect({ host: resolvedIP, port }, function() {
          if (offset < msg.length) {
            this.write(msg.slice(offset));
          }
          duplex.on('error', () => {}).pipe(this).on('error', () => {}).pipe(duplex);
        }).on('error', () => {});
      })
      .catch(error => {
        net.connect({ host, port }, function() {
          if (offset < msg.length) {
            this.write(msg.slice(offset));
          }
          duplex.on('error', () => {}).pipe(this).on('error', () => {}).pipe(duplex);
        }).on('error', () => {});
      });

    return true;
  } catch (error) {
    return false;
  }
}
// Ws 连接处理
wss.on('connection', (ws, req) => {
  const url = req.url || '';
  ws.once('message', msg => {
    if (msg.length > 17 && msg[0] === 0) {
      const id = msg.slice(1, 17);
      const isVless = id.every((v, i) => v == parseInt(uuid.substr(i * 2, 2), 16));
      if (isVless) {
        if (!handleVlessConnection(ws, msg)) {
          ws.close();
        }
        return;
      }
    }

    if (!handleTrojanConnection(ws, msg)) {
      ws.close();
    }
  }).on('error', () => {});
});

httpServer.listen(PORT, () => {
  const scheme = tlsOptions ? 'HTTPS/WSS' : 'HTTP/WS';
  console.log(`Server is running on ${scheme} port ${PORT}`);
});

其中几个地方有修改,先看看huggingface给咱们的域名,右边,Settings右边的三个点按钮,

点开,然后点 Embed this Space

image-20260106112435004

看src那里,大善人给了咱们一个免费的域名,这个域名是自带证书的!!!我们把域名拷贝下来

然后根据实际情况修改 index.js

const DOMAIN = process.env.DOMAIN || 'username-spacename.hf.space';   // 填写大善人给的域名域名
const SUB_PATH = process.env.SUB_PATH || 'sub';                    // 获取节点的订阅路径,最好改掉
const TLS_KEY_PATH = process.env.TLS_KEY_PATH || '';               // 善人给了证书,就不用设置了,改成空
const TLS_CERT_PATH = process.env.TLS_CERT_PATH || '';             // 善人给了证书,就不用设置了,改成空

改完后是这样的:

image-20260106112722331

然后改好了,别急着贴进去:

去到 去到 https://obfuscator.io/legacy-playground

贴进去代码,混淆一下,弄成谁也认不得的模样,然后Copy,再贴进去

image-20260106140116857

然后再建立 package.json 文件

{
  "name": "js02",
  "version": "0.0.2",
  "description": "Nodejs-server",
  "main": "index.js",
  "private": false,
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "ws": "^8.14.2",
    "axios": "^1.12.2"
  },
  "engines": {
    "node": ">=14"
  }
}

再建立 index.html 文件, 这是个用来装饰的环保单页面,如果不加,就会显示hello world,太假了,可以让 gemini 给你生成一个,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Green Network - Protect Earth</title>

    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        :root {
            --primary: #2e7d32;
            --secondary: #4caf50;
            --accent: #8bc34a;
            --light: #e8f5e9;
            --dark: #1b5e20;
            --text: #333333;
            --white: #ffffff;
        }

        body {
            background-color: var(--light);
            color: var(--text);
            line-height: 1.7; /* Slightly increased line height for readability */
            font-size: 16px;
        }

        header {
            background: rgba(46, 125, 50, 0.95); /* Slightly transparent primary color */
            color: var(--white);
            padding: 1rem 0;
            position: sticky;
            top: 0;
            z-index: 100;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); /* Enhanced shadow */
        }

        .container {
            width: 90%;
            max-width: 1200px;
            margin: 0 auto;
        }

        .nav-container {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .logo {
            display: flex;
            align-items: center;
            gap: 10px;
            font-size: 1.8rem;
            font-weight: 700;
        }

        .logo i {
            font-size: 2rem;
        }

        nav ul {
            display: flex;
            list-style: none;
        }

        nav ul li {
            margin-left: 2rem;
        }

        nav ul li a {
            color: var(--white);
            text-decoration: none;
            font-weight: 500;
            transition: all 0.3s ease;
            padding: 0.5rem 0;
            position: relative;
        }

        nav ul li a:hover {
            color: var(--accent);
        }

        nav ul li a::after {
            content: '';
            position: absolute;
            bottom: 0;
            left: 0;
            width: 0;
            height: 2px;
            background-color: var(--accent);
            transition: width 0.3s ease;
        }

        nav ul li a:hover::after {
            width: 100%;
        }

        .hero {
            background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.4)), url('https://images.unsplash.com/photo-1542601906990-b4d3fb778b09?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2073&q=80');
            background-size: cover;
            background-position: center;
            height: 80vh;
            display: flex;
            align-items: center;
            text-align: center;
            color: var(--white);
        }

        .hero-content {
            max-width: 800px;
            margin: 0 auto;
        }

        .hero h1 {
            font-size: 3.5rem;
            margin-bottom: 1rem;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
        }

        .hero p {
            font-size: 1.3rem;
            margin-bottom: 2rem;
        }

        .btn {
            display: inline-block;
            background-color: var(--accent);
            color: var(--white);
            padding: 0.9rem 2.2rem; /* Slightly larger padding */
            border-radius: 8px; /* More modern rounded corners */
            text-decoration: none;
            font-weight: 600;
            transition: all 0.3s ease;
            border: none;
            cursor: pointer;
            box-shadow: 0 6px 10px rgba(0, 0, 0, 0.1); /* Slightly enhanced shadow */
        }

        .btn:hover {
            background-color: var(--primary);
            transform: translateY(-5px); /* More pronounced lift effect */
            box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2); /* Stronger hover shadow */
        }

        section {
            padding: 5rem 0;
        }

        .section-title {
            text-align: center;
            margin-bottom: 3rem;
        }

        .section-title h2 {
            font-size: 2.5rem;
            color: var(--primary);
            margin-bottom: 1rem;
            position: relative;
            display: inline-block;
        }

        .section-title h2::after {
            content: '';
            position: absolute;
            bottom: -10px;
            left: 50%;
            transform: translateX(-50%);
            width: 90px; /* Slightly wider underline */
            height: 5px; /* Thicker underline */
            background-color: var(--accent);
            border-radius: 3px;
        }

        .section-title p {
            color: var(--text);
            max-width: 700px;
            margin: 0 auto;
            font-size: 1.1rem;
        }

        .about-content {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 3rem;
            align-items: center;
        }

        .about-img {
            border-radius: 12px; /* More rounded */
            overflow: hidden;
            box-shadow: 0 12px 25px rgba(0, 0, 0, 0.15); /* Enhanced shadow */
        }

        .about-img img {
            width: 100%;
            height: auto;
            display: block;
            transition: transform 0.5s ease;
        }

        .about-img:hover img {
            transform: scale(1.08); /* More pronounced zoom */
        }

        .about-text h3 {
            font-size: 2rem;
            margin-bottom: 1.5rem;
            color: var(--dark);
        }

        .about-text p {
            margin-bottom: 1.5rem;
            font-size: 1.05rem;
        }

        .stats {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 2rem;
            margin-top: 4rem; /* More space */
        }

        .stat-item {
            text-align: center;
            padding: 2.5rem 1.5rem; /* More padding */
            background-color: var(--white);
            border-radius: 12px; /* More rounded */
            box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08); /* Enhanced shadow */
            transition: transform 0.3s ease, box-shadow 0.3s ease;
        }

        .stat-item:hover {
            transform: translateY(-12px); /* More pronounced lift */
            box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15); /* Stronger hover shadow */
        }

        .stat-item h3 {
            font-size: 3rem; /* Larger numbers */
            color: var(--dark);
            margin-bottom: 0.8rem;
        }

        .stat-item p {
            color: var(--text);
            font-size: 1.1rem;
        }

        .initiatives {
            background-color: var(--white);
        }

        .initiatives-grid {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 2.5rem;
        }

        .initiative-card {
            background-color: var(--light);
            border-radius: 12px; /* More rounded */
            overflow: hidden;
            box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08); /* Enhanced shadow */
            transition: transform 0.3s ease, box-shadow 0.3s ease;
        }

        .initiative-card:hover {
            transform: translateY(-12px); /* More pronounced lift */
            box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15); /* Stronger hover shadow */
        }

        .initiative-img {
            height: 220px; /* Slightly taller images */
            overflow: hidden;
        }

        .initiative-img img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            transition: transform 0.5s ease;
        }

        .initiative-card:hover .initiative-img img {
            transform: scale(1.15); /* More pronounced zoom */
        }

        .initiative-content {
            padding: 2rem;
        }

        .initiative-content h3 {
            font-size: 1.6rem;
            margin-bottom: 1rem;
            color: var(--dark);
        }

        .initiative-content p {
            margin-bottom: 1.8rem;
            font-size: 1.05rem;
        }

        .cta {
            background: linear-gradient(to right, var(--primary), var(--secondary));
            color: var(--white);
            text-align: center;
            padding: 6rem 0; /* More padding */
        }

        .cta h2 {
            font-size: 3rem; /* Larger heading */
            margin-bottom: 1.8rem;
        }

        .cta p {
            font-size: 1.3rem;
            margin-bottom: 2.5rem;
            max-width: 700px;
            margin-left: auto;
            margin-right: auto;
        }

        .cta .btn {
            background-color: var(--white);
            color: var(--primary);
        }

        .cta .btn:hover {
            background-color: var(--accent);
            color: var(--white);
        }

        footer {
            background-color: var(--dark);
            color: var(--white);
            padding: 4rem 0 2rem; /* More padding */
        }

        .footer-content {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 2.5rem; /* More gap */
            margin-bottom: 2.5rem;
        }

        .footer-column h3 {
            font-size: 1.4rem;
            margin-bottom: 1.8rem;
            position: relative;
            padding-bottom: 12px;
        }

        .footer-column h3::after {
            content: '';
            position: absolute;
            bottom: 0;
            left: 0;
            width: 50px; /* Wider underline */
            height: 3px; /* Thicker underline */
            background-color: var(--accent);
        }

        .footer-column p {
            margin-bottom: 1.2rem;
        }

        .footer-links {
            list-style: none;
        }

        .footer-links li {
            margin-bottom: 1rem;
        }

        .footer-links li a {
            color: var(--light);
            text-decoration: none;
            transition: all 0.3s ease;
        }

        .footer-links li a:hover {
            color: var(--accent);
            padding-left: 8px; /* More pronounced slide effect */
        }

        .social-links {
            display: flex;
            gap: 1.2rem;
            margin-top: 1.5rem;
        }

        .social-links a {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 50px; /* Larger social buttons */
            height: 50px;
            background-color: rgba(255, 255, 255, 0.15); /* Slightly more visible background */
            border-radius: 8px; /* Square with rounded corners */
            color: var(--white);
            text-decoration: none;
            font-weight: 600;
            transition: all 0.3s ease;
            font-size: 0.9rem;
        }

        .social-links a:hover {
            background-color: var(--accent);
            transform: translateY(-7px); /* More pronounced lift */
        }

        .copyright {
            text-align: center;
            padding-top: 2.5rem;
            border-top: 1px solid rgba(255, 255, 255, 0.15);
        }

        .mobile-menu {
            display: none;
            font-size: 1.5rem;
            cursor: pointer;
            padding: 0.5rem 1rem;
            border: 1px solid rgba(255, 255, 255, 0.3);
            border-radius: 5px;
            transition: all 0.3s ease;
        }

        .mobile-menu:hover {
            background-color: rgba(255, 255, 255, 0.1);
        }

        @media (max-width: 992px) {
            .about-content {
                grid-template-columns: 1fr;
            }

            .initiatives-grid {
                grid-template-columns: repeat(2, 1fr);
            }

            .footer-content {
                grid-template-columns: repeat(2, 1fr);
            }

            .stats {
                grid-template-columns: repeat(2, 1fr);
            }
        }

        @media (max-width: 768px) {
            .hero h1 {
                font-size: 2.8rem; /* Adjusted for better readability on smaller screens */
            }

            .hero p {
                font-size: 1.2rem; /* Adjusted for better readability on smaller screens */
            }

            nav ul {
                display: none;
                position: absolute;
                top: 100%;
                left: 0;
                width: 100%;
                background-color: var(--primary);
                flex-direction: column;
                padding: 1rem 0;
                text-align: center;
                box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
            }

            nav ul.active {
                display: flex;
            }

            nav ul li {
                margin: 0;
                padding: 0.8rem 0;
            }

            .mobile-menu {
                display: block;
                font-size: 1.2rem; /* Slightly smaller for mobile */
                padding: 0.4rem 0.8rem;
            }

            .initiatives-grid {
                grid-template-columns: 1fr;
            }

            .footer-content {
                grid-template-columns: 1fr;
            }

            .stats {
                grid-template-columns: 1fr;
            }

            .social-links a {
                width: 45px; /* Slightly smaller social buttons */
                height: 45px;
                font-size: 0.85rem;
            }
        }

        /* Animation for stats counter */
        .counter {
            transition: all 0.5s ease;
        }
    </style>
</head>
<body>
    <!-- Header -->
    <header>
        <div class="container nav-container">
            <div class="logo">
                <span>Green Network</span>
            </div>
            <nav>
                <div class="mobile-menu">
                    Menu
                </div>
                <ul>
                    <li><a href="#home">Home</a></li>
                    <li><a href="#about">About</a></li>
                    <li><a href="#initiatives">Initiatives</a></li>
                    <li><a href="#get-involved">Get Involved</a></li>
                    <li><a href="#contact">Contact</a></li>
                </ul>
            </nav>
        </div>
    </header>

    <!-- Hero Section -->
    <section class="hero" id="home">
        <div class="container hero-content">
            <h1>Protect Our Planet, Preserve Our Future</h1>
            <p>Join the global movement to create a sustainable world through conservation, education, and community action.</p>
            <a href="#get-involved" class="btn">Join Our Network</a>
        </div>
    </section>

    <!-- About Section -->
    <section id="about">
        <div class="container">
            <div class="section-title">
                <h2>About Green Network</h2>
                <p>We are a global community dedicated to environmental protection and sustainable development.</p>
            </div>
            <div class="about-content">
                <div class="about-img">
                    <img src="https://images.unsplash.com/photo-1507591064344-4c6ce005b128?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80" alt="Team planting trees">
                </div>
                <div class="about-text">
                    <h3>Our Mission</h3>
                    <p>Green Network is committed to creating a sustainable future by protecting natural ecosystems, promoting renewable energy, and empowering communities to take environmental action.</p>
                    <p>Founded in 2010, we've grown from a small grassroots organization to an international network with over 50,000 members across 120 countries.</p>
                    <p>Our approach combines scientific research, community engagement, and policy advocacy to address the most pressing environmental challenges of our time.</p>
                    <a href="#" class="btn">Learn More</a>
                </div>
            </div>
            <div class="stats">
                <div class="stat-item">
                    <h3 class="counter" data-target="2500000">0</h3>
                    <p>Trees Planted</p>
                </div>
                <div class="stat-item">
                    <h3 class="counter" data-target="50000">0</h3>
                    <p>Active Members</p>
                </div>
                <div class="stat-item">
                    <h3 class="counter" data-target="120">0</h3>
                    <p>Countries Reached</p>
                </div>
                <div class="stat-item">
                    <h3 class="counter" data-target="1500">0</h3>
                    <p>Cleanup Projects</p>
                </div>
            </div>
        </div>
    </section>

    <!-- Initiatives Section -->
    <section class="initiatives" id="initiatives">
        <div class="container">
            <div class="section-title">
                <h2>Our Initiatives</h2>
                <p>Discover the key programs and projects we're implementing to protect our planet.</p>
            </div>
            <div class="initiatives-grid">
                <div class="initiative-card">
                    <div class="initiative-img">
                        <img src="https://dialogue.earth/content/uploads/2021/04/lessons-from-the-rush-to-reforest-china-dialogue-2400x1599.jpg" alt="Reforestation">
                    </div>
                    <div class="initiative-content">
                        <h3>Global Reforestation</h3>
                        <p>Planting millions of trees worldwide to restore ecosystems, combat climate change, and protect biodiversity.</p>
                        <a href="#" class="btn">Learn More</a>
                    </div>
                </div>
                <div class="initiative-card">
                    <div class="initiative-img">
                        <img src="https://images.unsplash.com/photo-1621451537084-482c73073a0f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1974&q=80" alt="Ocean Conservation">
                    </div>
                    <div class="initiative-content">
                        <h3>Ocean Conservation</h3>
                        <p>Protecting marine ecosystems, reducing plastic pollution, and promoting sustainable fishing practices.</p>
                        <a href="#" class="btn">Learn More</a>
                    </div>
                </div>
                <div class="initiative-card">
                    <div class="initiative-img">
                        <img src="https://images.unsplash.com/photo-1508514177221-188b1cf16e9d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2072&q=80" alt="Renewable Energy">
                    </div>
                    <div class="initiative-content">
                        <h3>Renewable Energy</h3>
                        <p>Promoting solar, wind, and other clean energy sources to reduce dependence on fossil fuels.</p>
                        <a href="#" class="btn">Learn More</a>
                    </div>
                </div>
            </div>
        </div>
    </section>

    <!-- CTA Section -->
    <section class="cta" id="get-involved">
        <div class="container">
            <h2>Join Our Global Movement</h2>
            <p>Together, we can create a sustainable future for generations to come. Every action counts, no matter how small.</p>
            <a href="#" class="btn">Become a Member</a>
        </div>
    </section>

    <!-- Footer -->
    <footer id="contact">
        <div class="container">
            <div class="footer-content">
                <div class="footer-column">
                    <h3>Green Network</h3>
                    <p>We are dedicated to protecting our planet through collaborative action, education, and sustainable solutions.</p>
                    <div class="social-links">
                        <a href="#">Facebook</a>
                        <a href="#">Twitter</a>
                        <a href="#">Instagram</a>
                        <a href="#">LinkedIn</a>
                    </div>
                </div>
                <div class="footer-column">
                    <h3>Quick Links</h3>
                    <ul class="footer-links">
                        <li><a href="#home">Home</a></li>
                        <li><a href="#about">About Us</a></li>
                        <li><a href="#initiatives">Initiatives</a></li>
                        <li><a href="#get-involved">Get Involved</a></li>
                        <li><a href="#contact">Contact</a></li>
                    </ul>
                </div>
                <div class="footer-column">
                    <h3>Our Programs</h3>
                    <ul class="footer-links">
                        <li><a href="#">Reforestation</a></li>
                        <li><a href="#">Ocean Cleanup</a></li>
                        <li><a href="#">Wildlife Protection</a></li>
                        <li><a href="#">Climate Education</a></li>
                        <li><a href="#">Sustainable Agriculture</a></li>
                    </ul>
                </div>
                <div class="footer-column">
                    <h3>Contact Us</h3>
                    <p>Copper Creek Drive, New York</p>
                    <p>+1 (312) 171-0771</p>
                    <p>support@greennetwork.org</p>
                </div>
            </div>
            <div class="copyright">
                <p>&copy; 2025 Green Network. All rights reserved.</p>
            </div>
        </div>
    </footer>

    <script>
        // Mobile Menu Toggle
        document.querySelector('.mobile-menu').addEventListener('click', function() {
            document.querySelector('nav ul').classList.toggle('active');
        });

        // Counter Animation
        function animateCounters() {
            const counters = document.querySelectorAll('.counter');
            const speed = 200; // The lower the slower

            counters.forEach(counter => {
                const target = +counter.getAttribute('data-target');
                const count = +counter.innerText;

                const inc = target / speed;

                if(count < target) {
                    counter.innerText = Math.ceil(count + inc);
                    setTimeout(animateCounters, 1);
                } else {
                    counter.innerText = target;
                }
            });
        }

        // Initialize counters when page loads
        window.addEventListener('load', animateCounters);

        // Smooth scrolling for navigation links
        document.querySelectorAll('a[href^="#"]').forEach(anchor => {
            anchor.addEventListener('click', function(e) {
                e.preventDefault();

                const targetId = this.getAttribute('href');
                if(targetId === '#') return;

                const targetElement = document.querySelector(targetId);
                if(targetElement) {
                    window.scrollTo({
                        top: targetElement.offsetTop - 80,
                        behavior: 'smooth'
                    });

                    document.querySelector('nav ul').classList.remove('active');
                }
            });
        });
    </script>
</body>

</html>

那就一切完工,看下都有什么文件:

image-20260106115356395

一切就绪,点击App运行:

image-20260106115438878

那会看到运行完毕,开了7860这个端口

image-20260106114145700

那直接点击左上的myhome链接,就能看到环保页面了。

image-20260106114229897

那大善人huggingface就弄好了。

解释一下具体原理:

huggingface 跑了个前置的Nginx代理,自动申请了证书,代理后端容器的7860端口,为什么是7860端口呢?

因为最常见的用途是托管基于 Gradio 构建的机器学习模型演示。Gradio 是一个非常流行的 Python 库,用于快速创建交互式 Web 界面来展示 ML 模型。它的默认启动端口就是7860。

那接着我们去薅大善人cloudflare,首先弄好一个域名并托管到CF上面,比如免费的qzz.io

点开左边的菜单:Build –> Compute & AI –> Workers & Pages

image-20260106132111456

新建一个应用,Create application

image-20260106132924016

然后选 Start with Hello World!

image-20260106133004705

然后就会建出一个来,点击编辑代码,Edit Code

image-20260106133043495

我们把以下代码贴进去,注意修改域名,只写域名,没有https的前缀,从huggingface得到的免费域名:

把代码中username-spacename.hf.space换成自己的域名


export default {
    async fetch(request, env) {
        let url = new URL(request.url);
        if (url.pathname.startsWith('/')) {
            var arrStr = [
                'username-spacename.hf.space', // 此处单引号里填写你的节点伪装域名
            ];
            url.protocol = 'https:'
            url.hostname = getRandomArray(arrStr)
            let new_request = new Request(url, request);
            return fetch(new_request);
        }
        return env.ASSETS.fetch(request);
    },
};
function getRandomArray(array) {
  const randomIndex = Math.floor(Math.random() * array.length);
  return array[randomIndex];
}

然后点 Deploy ,部署

返回这个worker的空间,点击Settings,下面的Domains & Routes ,右边点击 +Add

image-20260106133628143

弹出的对话框,选择Custom domain

image-20260106133740727

然后填入自己心满意足的域名

image-20260106134025791

点击Add domain就完事了。

然后我们打开我们心满意足的域名:

image-20260106134220759

环保页面,然后打开我们的订阅页面,缺省是/sub,当然改成只有自己知道的路径为好

出现订阅的Base64字符就ok了。

image-20260106134549359

我们用v2rayN导入即可。

完工,还是说一下原理:

利用cf大善人的网络代理加速,利用hug大善人的免费域名和证书,达到我们的目的

image-20260106135830657

最后,这个的index.js和上一篇的不同啊,唯一区别就是端口,hug端口肯定是443,而wispbyte的端口是非标的。

注意注意!!!