Full blog engine source: build_blog.py, content, deploy scripts
This commit is contained in:
52
README.md
Normal file
52
README.md
Normal file
@ -0,0 +1,52 @@
|
||||
# GFIL BLOG — Trading Insights & Free Tools
|
||||
|
||||
The content engine behind [blog.quant-view.xyz](https://blog.quant-view.xyz) — a multi-language trading education site with 80+ free calculators, professional market analysis, and SEO-optimized articles.
|
||||
|
||||
## What's here
|
||||
|
||||
- **17 in-depth articles** (EN/ZH/ES/AR) on forex, gold, risk management, and trading technology
|
||||
- **80+ free trading calculators** — position size, pip value, Fibonacci, ATR, Kelly, pivot points, and more
|
||||
- **Full SEO/GEO stack** — Schema JSON-LD, hreflang, sitemap (339 URLs), robots.txt, llms.txt
|
||||
- **Automated deployment pipeline** — build, upload, and server-side promotion scripts
|
||||
|
||||
## How it works
|
||||
|
||||
```
|
||||
blog_content/ → build_blog.py → output/ → RackNerd /var/www/blog/
|
||||
tools/ → → (static HTML served by Nginx)
|
||||
deploy_scripts/ → daily cron jobs on server
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
# Build locally (no deploy)
|
||||
python build_blog.py --no-upload
|
||||
|
||||
# Build and deploy (requires .env with SSH credentials)
|
||||
python build_blog.py
|
||||
|
||||
# Preview locally
|
||||
python build_blog.py --no-upload --serve
|
||||
```
|
||||
|
||||
## Repository structure
|
||||
|
||||
| Directory | Purpose |
|
||||
|-----------|---------|
|
||||
| `blog_content/` | Article HTML source (17 articles × 4 languages) |
|
||||
| `tools/` | 145 calculator pages + GEO entity pages + images |
|
||||
| `deploy_scripts/` | Automation: daily promotion, SEO monitoring, IndexNow |
|
||||
| `chrome_extension/` | Chrome extension source |
|
||||
| `colab_notebooks/` | Google Colab trading notebooks |
|
||||
| `config.py` | Configuration loader (reads from `.env`) |
|
||||
|
||||
## Languages
|
||||
|
||||
English · 中文 · Español · العربية
|
||||
|
||||
## Links
|
||||
|
||||
- Blog: [blog.quant-view.xyz](https://blog.quant-view.xyz)
|
||||
- Terminal: [gfil-intel.xyz](https://gfil-intel.xyz)
|
||||
- Community: [t.me/GFIL_Trading](https://t.me/GFIL_Trading) · [Discord](https://discord.gg/GMmMCD4MCr)
|
||||
70
add_robots_location.py
Normal file
70
add_robots_location.py
Normal file
@ -0,0 +1,70 @@
|
||||
"""Safely add robots.txt location block to gfil-lab.com Nginx config"""
|
||||
import paramiko
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
# Use python on remote to do precise insertion instead of sed
|
||||
cmd = f"""sshpass -p '{LAB_PASS}' ssh -o StrictHostKeyChecking=no {LAB_USER}@{LAB_HOST} '
|
||||
# Backup first
|
||||
cp /etc/nginx/sites-enabled/gfil /etc/nginx/sites-enabled/gfil.bak.before-robots
|
||||
|
||||
# Use python to insert robots.txt block precisely before "location / {"
|
||||
python3 -c "
|
||||
import re
|
||||
with open(\"/etc/nginx/sites-enabled/gfil\", \"r\") as f:
|
||||
content = f.read()
|
||||
|
||||
robots_block = \"\"\" location = /robots.txt {
|
||||
alias /var/www/gfil-lab/robots.txt;
|
||||
default_type text/plain;
|
||||
}
|
||||
|
||||
\"\"\"
|
||||
|
||||
# Insert before the first \"location / {\"
|
||||
if \"robots.txt\" not in content:
|
||||
content = content.replace(\" location / {\", robots_block + \" location / {\")
|
||||
with open(\"/etc/nginx/sites-enabled/gfil\", \"w\") as f:
|
||||
f.write(content)
|
||||
print(\"INSERTED robots.txt block\")
|
||||
else:
|
||||
print(\"robots.txt block already exists, skipping\")
|
||||
"
|
||||
|
||||
# Verify
|
||||
echo "=== Updated config ==="
|
||||
cat /etc/nginx/sites-enabled/gfil
|
||||
|
||||
# Test
|
||||
nginx -t 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl reload nginx
|
||||
echo "SUCCESS: Nginx reloaded"
|
||||
else
|
||||
echo "FAILED: rolling back"
|
||||
cp /etc/nginx/sites-enabled/gfil.bak.before-robots /etc/nginx/sites-enabled/gfil
|
||||
nginx -t 2>&1
|
||||
systemctl reload nginx
|
||||
echo "Rolled back to original"
|
||||
fi
|
||||
|
||||
# Verify site still works
|
||||
curl -s -o /dev/null -w "Site: HTTP %{{http_code}}" http://localhost/ 2>/dev/null
|
||||
|
||||
# Verify robots.txt
|
||||
curl -s http://localhost/robots.txt 2>/dev/null | head -5
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(cmd, timeout=30)
|
||||
print(stdout.read().decode())
|
||||
jd.close()
|
||||
118
add_robots_v2.py
Normal file
118
add_robots_v2.py
Normal file
@ -0,0 +1,118 @@
|
||||
"""Safely add robots.txt location block to gfil-lab.com Nginx config"""
|
||||
import paramiko
|
||||
import os
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
# Write the Python fix script to a temp file
|
||||
FIX_SCRIPT = r'''
|
||||
import shutil
|
||||
src = "/etc/nginx/sites-enabled/gfil"
|
||||
bak = "/etc/nginx/sites-enabled/gfil.bak.before-robots"
|
||||
shutil.copy2(src, bak)
|
||||
|
||||
with open(src, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
robots_block = " location = /robots.txt {\n alias /var/www/gfil-lab/robots.txt;\n default_type text/plain;\n }\n\n"
|
||||
|
||||
if "robots.txt" not in content:
|
||||
content = content.replace(" location / {", robots_block + " location / {")
|
||||
with open(src, "w") as f:
|
||||
f.write(content)
|
||||
print("INSERTED")
|
||||
else:
|
||||
print("ALREADY_EXISTS")
|
||||
'''
|
||||
|
||||
# Write script locally
|
||||
script_path = os.path.join(os.environ.get("TEMP", "/tmp"), "fix_nginx.py")
|
||||
with open(script_path, "w") as f:
|
||||
f.write(FIX_SCRIPT)
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
# Upload fix script to JD Cloud
|
||||
sftp = jd.open_sftp()
|
||||
sftp.put(script_path, "/tmp/fix_nginx.py")
|
||||
sftp.close()
|
||||
|
||||
# SCP to gfil-lab server, run, verify
|
||||
cmd = f"""sshpass -p '{LAB_PASS}' ssh -o StrictHostKeyChecking=no {LAB_USER}@{LAB_HOST} '
|
||||
# Copy fix script from JD Cloud
|
||||
echo "Step 1: Upload fix script..."
|
||||
cat /dev/null # placeholder
|
||||
|
||||
# Actually the script is on JD Cloud, need to scp it first
|
||||
'"""
|
||||
|
||||
# Two-step: first scp script to target, then run it
|
||||
scp_cmd = f"sshpass -p '{LAB_PASS}' scp -o StrictHostKeyChecking=no /tmp/fix_nginx.py {LAB_USER}@{LAB_HOST}:/tmp/fix_nginx.py"
|
||||
stdin, stdout, stderr = jd.exec_command(scp_cmd, timeout=15)
|
||||
stdout.channel.recv_exit_status()
|
||||
print(f"SCP: done")
|
||||
|
||||
# Now run it on target
|
||||
run_cmd = f"""sshpass -p '{LAB_PASS}' ssh -o StrictHostKeyChecking=no {LAB_USER}@{LAB_HOST} '
|
||||
echo "=== Running fix script ==="
|
||||
python3 /tmp/fix_nginx.py
|
||||
|
||||
echo "=== Updated config ==="
|
||||
cat /etc/nginx/sites-enabled/gfil
|
||||
|
||||
echo "=== Nginx test ==="
|
||||
nginx -t 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl reload nginx
|
||||
echo "SUCCESS: Nginx reloaded"
|
||||
else
|
||||
echo "FAILED: rolling back"
|
||||
cp /etc/nginx/sites-enabled/gfil.bak.before-robots /etc/nginx/sites-enabled/gfil
|
||||
nginx -t 2>&1 && systemctl reload nginx
|
||||
echo "Rolled back"
|
||||
fi
|
||||
|
||||
echo "=== Site check ==="
|
||||
curl -s -o /dev/null -w "Site: HTTP %{http_code}" http://localhost/ 2>/dev/null
|
||||
echo ""
|
||||
|
||||
echo "=== robots.txt check ==="
|
||||
curl -s http://localhost/robots.txt 2>/dev/null | head -8
|
||||
|
||||
rm -f /tmp/fix_nginx.py
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(run_cmd, timeout=30)
|
||||
print(stdout.read().decode())
|
||||
jd.close()
|
||||
|
||||
# External verify
|
||||
print("\n=== External verification ===")
|
||||
import urllib.request
|
||||
for domain in ['gfil-lab.com', 'gfil-intel.xyz']:
|
||||
try:
|
||||
req = urllib.request.Request(f'https://{domain}/robots.txt',
|
||||
headers={'User-Agent': 'Mozilla/5.0'})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
content = r.read().decode()
|
||||
has_gptbot = 'GPTBot' in content
|
||||
print(f" {domain}: {r.status} | GPTBot={has_gptbot} | {len(content)} chars")
|
||||
except Exception as e:
|
||||
print(f" {domain}: {e}")
|
||||
|
||||
# Also verify main site still works
|
||||
try:
|
||||
req = urllib.request.Request('https://gfil-lab.com/',
|
||||
headers={'User-Agent': 'Mozilla/5.0'})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
print(f" gfil-lab.com homepage: {r.status} OK")
|
||||
except Exception as e:
|
||||
print(f" gfil-lab.com homepage: ERROR {e}")
|
||||
245
audit.py
Normal file
245
audit.py
Normal file
@ -0,0 +1,245 @@
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import re
|
||||
import ssl
|
||||
import time
|
||||
import xml.etree.ElementTree as ET
|
||||
from html.parser import HTMLParser
|
||||
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
BAD_GITHUB_PATTERN = "github.com/liudapao880807-arch"
|
||||
|
||||
results = []
|
||||
|
||||
def log(msg):
|
||||
print(msg)
|
||||
results.append(msg)
|
||||
|
||||
class LinkExtractor(HTMLParser):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.hrefs = []
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag == "a":
|
||||
for k, v in attrs:
|
||||
if k == "href" and v:
|
||||
self.hrefs.append(v)
|
||||
|
||||
def fetch(url, timeout=30):
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"})
|
||||
try:
|
||||
resp = urllib.request.urlopen(req, timeout=timeout, context=ctx)
|
||||
code = resp.getcode()
|
||||
body = resp.read().decode("utf-8", errors="replace")
|
||||
return code, body
|
||||
except urllib.error.HTTPError as e:
|
||||
return e.code, e.read().decode("utf-8", errors="replace") if e.fp else ""
|
||||
except Exception as e:
|
||||
return -1, str(e)
|
||||
|
||||
def check_url_status(url, timeout=20):
|
||||
req = urllib.request.Request(url, method="HEAD", headers={"User-Agent": "Mozilla/5.0"})
|
||||
try:
|
||||
resp = urllib.request.urlopen(req, timeout=timeout, context=ctx)
|
||||
return resp.getcode()
|
||||
except urllib.error.HTTPError as e:
|
||||
return e.code
|
||||
except Exception:
|
||||
return -1
|
||||
|
||||
def extract_links(html):
|
||||
parser = LinkExtractor()
|
||||
try:
|
||||
parser.feed(html)
|
||||
except:
|
||||
pass
|
||||
return parser.hrefs
|
||||
|
||||
def resolve_link(base_url, href):
|
||||
if href.startswith("http://") or href.startswith("https://"):
|
||||
return href
|
||||
if href.startswith("//"):
|
||||
return "https:" + href
|
||||
if href.startswith("/"):
|
||||
from urllib.parse import urlparse
|
||||
p = urlparse(base_url)
|
||||
return f"{p.scheme}://{p.netloc}{href}"
|
||||
return href
|
||||
|
||||
def check_page(url, label):
|
||||
log(f"\n{'='*80}")
|
||||
log(f"[{label}] Checking: {url}")
|
||||
code, body = fetch(url)
|
||||
log(f" Status: {code}")
|
||||
log(f" Size: {len(body)} chars")
|
||||
|
||||
if code != 200:
|
||||
log(f" *** ERROR: Non-200 status code!")
|
||||
return []
|
||||
|
||||
content_stripped = re.sub(r'<[^>]+>', '', body).strip()
|
||||
word_count = len(content_stripped.split())
|
||||
log(f" Word count (approx): {word_count}")
|
||||
if word_count < 50:
|
||||
log(f" *** WARNING: Very low content — possibly blank or broken page!")
|
||||
|
||||
bad_links = re.findall(BAD_GITHUB_PATTERN, body)
|
||||
if bad_links:
|
||||
log(f" *** BAD LINKS FOUND: github.com/liudapao880807-arch appears {len(bad_links)} time(s)!")
|
||||
for m in re.finditer(r'href="[^"]*liudapao880807-arch[^"]*"', body):
|
||||
log(f" -> {m.group()}")
|
||||
else:
|
||||
log(f" No github.com/liudapao880807-arch links (good)")
|
||||
|
||||
hrefs = extract_links(body)
|
||||
log(f" Total links found: {len(hrefs)}")
|
||||
|
||||
internal = []
|
||||
external = []
|
||||
for h in hrefs:
|
||||
resolved = resolve_link(url, h)
|
||||
if "blog.quant-view.xyz" in resolved:
|
||||
internal.append(resolved)
|
||||
elif resolved.startswith("http"):
|
||||
external.append(resolved)
|
||||
|
||||
log(f" Internal links: {len(internal)}")
|
||||
log(f" External links: {len(external)}")
|
||||
|
||||
all_unique = list(set(internal + external))
|
||||
broken = []
|
||||
ok = []
|
||||
errors_detail = []
|
||||
|
||||
for link in all_unique:
|
||||
if link.endswith((".css", ".js", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".woff", ".woff2", ".ttf", ".eot")):
|
||||
continue
|
||||
if "mailto:" in link or "javascript:" in link:
|
||||
continue
|
||||
time.sleep(0.3)
|
||||
status = check_url_status(link)
|
||||
if status == -1:
|
||||
broken.append((link, "CONNECTION_ERROR"))
|
||||
errors_detail.append(f" BROKEN (connection error): {link}")
|
||||
elif status >= 400:
|
||||
broken.append((link, status))
|
||||
errors_detail.append(f" BROKEN ({status}): {link}")
|
||||
else:
|
||||
ok.append((link, status))
|
||||
|
||||
log(f" Checked {len(all_unique)} unique non-asset links")
|
||||
log(f" OK: {len(ok)}")
|
||||
log(f" Broken/Errors: {len(broken)}")
|
||||
for d in errors_detail:
|
||||
log(d)
|
||||
|
||||
return broken
|
||||
|
||||
def check_sitemap(url, label):
|
||||
log(f"\n{'='*80}")
|
||||
log(f"[{label}] Checking sitemap: {url}")
|
||||
code, body = fetch(url)
|
||||
log(f" Status: {code}")
|
||||
if code != 200:
|
||||
log(f" *** ERROR: Sitemap not accessible!")
|
||||
return
|
||||
|
||||
urls_in_sitemap = []
|
||||
try:
|
||||
root = ET.fromstring(body)
|
||||
ns = {"sm": "http://www.sitemaps.org/schemas/sitemap/0.9"}
|
||||
for loc in root.findall(".//sm:loc", ns):
|
||||
urls_in_sitemap.append(loc.text.strip())
|
||||
for loc in root.findall(".//sm:url/sm:loc", ns):
|
||||
if loc.text.strip() not in urls_in_sitemap:
|
||||
urls_in_sitemap.append(loc.text.strip())
|
||||
except ET.ParseError:
|
||||
urls_found = re.findall(r'<loc>\s*(https?://[^<]+)\s*</loc>', body)
|
||||
urls_in_sitemap = urls_found
|
||||
|
||||
log(f" URLs in sitemap: {len(urls_in_sitemap)}")
|
||||
|
||||
if len(urls_in_sitemap) == 0:
|
||||
log(f" *** WARNING: No URLs found in sitemap!")
|
||||
else:
|
||||
sample = urls_in_sitemap[:5]
|
||||
for u in sample:
|
||||
log(f" Sample: {u}")
|
||||
|
||||
broken_count = 0
|
||||
for u in urls_in_sitemap[:20]:
|
||||
time.sleep(0.2)
|
||||
status = check_url_status(u)
|
||||
if status >= 400 or status == -1:
|
||||
log(f" BROKEN in sitemap ({status}): {u}")
|
||||
broken_count += 1
|
||||
if broken_count == 0:
|
||||
log(f" First 20 sitemap URLs all OK")
|
||||
else:
|
||||
log(f" {broken_count} broken URLs in first 20 sitemap entries")
|
||||
|
||||
article_urls = [
|
||||
("Article", "https://blog.quant-view.xyz/position-size-calculator-guide.html"),
|
||||
("Article", "https://blog.quant-view.xyz/gold-trading-2026-guide.html"),
|
||||
("Article", "https://blog.quant-view.xyz/ssh-tunnel-deployment-china.html"),
|
||||
("Article", "https://blog.quant-view.xyz/github-seo-trading-tools.html"),
|
||||
("Article", "https://blog.quant-view.xyz/gold-pip-value-calculator-wrong.html"),
|
||||
("Article", "https://blog.quant-view.xyz/bloomberg-alternative.html"),
|
||||
("Article", "https://blog.quant-view.xyz/why-retail-traders-lose-money.html"),
|
||||
("Article", "https://blog.quant-view.xyz/order-flow-trading.html"),
|
||||
]
|
||||
|
||||
tool_urls = [
|
||||
("Tool", "https://blog.quant-view.xyz/tools/position-size-formula.html"),
|
||||
("Tool", "https://blog.quant-view.xyz/tools/xauusd-trading-guide.html"),
|
||||
("Tool", "https://blog.quant-view.xyz/tools/btc-position-size-calculator.html"),
|
||||
("Tool", "https://blog.quant-view.xyz/tools/risk-management-guide.html"),
|
||||
("Tool", "https://blog.quant-view.xyz/tools/tradingview-vs-mt5.html"),
|
||||
("Tool", "https://blog.quant-view.xyz/tools/kelly-criterion-formula.html"),
|
||||
("Tool", "https://blog.quant-view.xyz/tools/forex-position-size-calculator.html"),
|
||||
("Tool", "https://blog.quant-view.xyz/tools/pip-calculator-eurgbp.html"),
|
||||
("Tool", "https://blog.quant-view.xyz/tools/sp500-position-size-calculator.html"),
|
||||
("Tool", "https://blog.quant-view.xyz/tools/position-size-calculator-100000-dollar-account.html"),
|
||||
]
|
||||
|
||||
sitemap_urls = [
|
||||
("Sitemap", "https://blog.quant-view.xyz/sitemap.xml"),
|
||||
("Sitemap", "https://blog.quant-view.xyz/sitemap-tools.xml"),
|
||||
("Sitemap", "https://blog.quant-view.xyz/sitemap-posts.xml"),
|
||||
]
|
||||
|
||||
log("=" * 80)
|
||||
log("BLOG AUDIT: https://blog.quant-view.xyz")
|
||||
log(f"Date: 2026-06-28")
|
||||
log("=" * 80)
|
||||
|
||||
all_broken = []
|
||||
all_bad_github = []
|
||||
|
||||
for label, url in article_urls:
|
||||
broken = check_page(url, label)
|
||||
all_broken.extend(broken)
|
||||
|
||||
for label, url in tool_urls:
|
||||
broken = check_page(url, label)
|
||||
all_broken.extend(broken)
|
||||
|
||||
for label, url in sitemap_urls:
|
||||
check_sitemap(url, label)
|
||||
|
||||
log("\n" + "=" * 80)
|
||||
log("SUMMARY")
|
||||
log("=" * 80)
|
||||
|
||||
log(f"\nTotal broken links found: {len(all_broken)}")
|
||||
for link, status in all_broken:
|
||||
log(f" [{status}] {link}")
|
||||
|
||||
output = "\n".join(results)
|
||||
with open(r"D:\GFIL_BLOG\audit_results.txt", "w", encoding="utf-8") as f:
|
||||
f.write(output)
|
||||
|
||||
log(f"\nResults written to D:\\GFIL_BLOG\\audit_results.txt")
|
||||
317
audit_results.txt
Normal file
317
audit_results.txt
Normal file
@ -0,0 +1,317 @@
|
||||
================================================================================
|
||||
BLOG AUDIT: https://blog.quant-view.xyz
|
||||
Date: 2026-06-28
|
||||
================================================================================
|
||||
|
||||
================================================================================
|
||||
[Article] Checking: https://blog.quant-view.xyz/position-size-calculator-guide.html
|
||||
Status: 200
|
||||
Size: 20417 chars
|
||||
Word count (approx): 1512
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 19
|
||||
Internal links: 15
|
||||
External links: 4
|
||||
Checked 16 unique non-asset links
|
||||
OK: 15
|
||||
Broken/Errors: 1
|
||||
BROKEN (403): https://twitter.com/intent/tweet?text=Position%20Size%20Calculator%20%E2%80%94%20The%20Complete%20Guide%20to%20Forex%20Risk%20Management%20in%202026&url=https://blog.quant-view.xyz/position-size-calculator-guide.html
|
||||
|
||||
================================================================================
|
||||
[Article] Checking: https://blog.quant-view.xyz/gold-trading-2026-guide.html
|
||||
Status: 200
|
||||
Size: 23961 chars
|
||||
Word count (approx): 1881
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 20
|
||||
Internal links: 16
|
||||
External links: 4
|
||||
Checked 16 unique non-asset links
|
||||
OK: 15
|
||||
Broken/Errors: 1
|
||||
BROKEN (403): https://twitter.com/intent/tweet?text=Gold%20XAUUSD%20Trading%202026%20%E2%80%94%20Technical%20Tools%2C%20Price%20Drivers%20%26%20Free%20Calculators&url=https://blog.quant-view.xyz/gold-trading-2026-guide.html
|
||||
|
||||
================================================================================
|
||||
[Article] Checking: https://blog.quant-view.xyz/ssh-tunnel-deployment-china.html
|
||||
Status: 200
|
||||
Size: 16684 chars
|
||||
Word count (approx): 923
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 15
|
||||
Internal links: 11
|
||||
External links: 4
|
||||
Checked 12 unique non-asset links
|
||||
OK: 11
|
||||
Broken/Errors: 1
|
||||
BROKEN (403): https://twitter.com/intent/tweet?text=SSH%20Tunnel%20Deployment%20for%20Trading%20Platforms%20%E2%80%94%20Lessons%20from%20China&url=https://blog.quant-view.xyz/ssh-tunnel-deployment-china.html
|
||||
|
||||
================================================================================
|
||||
[Article] Checking: https://blog.quant-view.xyz/github-seo-trading-tools.html
|
||||
Status: 200
|
||||
Size: 17375 chars
|
||||
Word count (approx): 1051
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 16
|
||||
Internal links: 11
|
||||
External links: 5
|
||||
Checked 13 unique non-asset links
|
||||
OK: 12
|
||||
Broken/Errors: 1
|
||||
BROKEN (403): https://twitter.com/intent/tweet?text=GitHub%20SEO%20for%20Trading%20Tools%20%E2%80%94%203%20Mistakes%20That%20Kill%20Your%20Rankings&url=https://blog.quant-view.xyz/github-seo-trading-tools.html
|
||||
|
||||
================================================================================
|
||||
[Article] Checking: https://blog.quant-view.xyz/gold-pip-value-calculator-wrong.html
|
||||
Status: 200
|
||||
Size: 17447 chars
|
||||
Word count (approx): 870
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 16
|
||||
Internal links: 11
|
||||
External links: 5
|
||||
Checked 14 unique non-asset links
|
||||
OK: 13
|
||||
Broken/Errors: 1
|
||||
BROKEN (403): https://twitter.com/intent/tweet?text=Why%20Most%20Position%20Size%20Calculators%20Get%20Gold%20Wrong&url=https://blog.quant-view.xyz/gold-pip-value-calculator-wrong.html
|
||||
|
||||
================================================================================
|
||||
[Article] Checking: https://blog.quant-view.xyz/bloomberg-alternative.html
|
||||
Status: 200
|
||||
Size: 15966 chars
|
||||
Word count (approx): 864
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 17
|
||||
Internal links: 13
|
||||
External links: 4
|
||||
Checked 15 unique non-asset links
|
||||
OK: 14
|
||||
Broken/Errors: 1
|
||||
BROKEN (403): https://twitter.com/intent/tweet?text=Bloomberg%20Terminal%20Alternatives&url=https://blog.quant-view.xyz/bloomberg-alternative.html
|
||||
|
||||
================================================================================
|
||||
[Article] Checking: https://blog.quant-view.xyz/why-retail-traders-lose-money.html
|
||||
Status: 200
|
||||
Size: 17425 chars
|
||||
Word count (approx): 1096
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 17
|
||||
Internal links: 13
|
||||
External links: 4
|
||||
Checked 15 unique non-asset links
|
||||
OK: 14
|
||||
Broken/Errors: 1
|
||||
BROKEN (403): https://twitter.com/intent/tweet?text=Why%2087%25%20of%20Retail%20Traders%20Lose%20Money&url=https://blog.quant-view.xyz/why-retail-traders-lose-money.html
|
||||
|
||||
================================================================================
|
||||
[Article] Checking: https://blog.quant-view.xyz/order-flow-trading.html
|
||||
Status: 200
|
||||
Size: 17228 chars
|
||||
Word count (approx): 1049
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 16
|
||||
Internal links: 12
|
||||
External links: 4
|
||||
Checked 14 unique non-asset links
|
||||
OK: 13
|
||||
Broken/Errors: 1
|
||||
BROKEN (403): https://twitter.com/intent/tweet?text=Order%20Flow%20Trading&url=https://blog.quant-view.xyz/order-flow-trading.html
|
||||
|
||||
================================================================================
|
||||
[Tool] Checking: https://blog.quant-view.xyz/tools/position-size-formula.html
|
||||
Status: 200
|
||||
Size: 6979 chars
|
||||
Word count (approx): 596
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 4
|
||||
Internal links: 3
|
||||
External links: 1
|
||||
Checked 4 unique non-asset links
|
||||
OK: 4
|
||||
Broken/Errors: 0
|
||||
|
||||
================================================================================
|
||||
[Tool] Checking: https://blog.quant-view.xyz/tools/xauusd-trading-guide.html
|
||||
Status: 200
|
||||
Size: 5431 chars
|
||||
Word count (approx): 360
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 4
|
||||
Internal links: 3
|
||||
External links: 1
|
||||
Checked 4 unique non-asset links
|
||||
OK: 4
|
||||
Broken/Errors: 0
|
||||
|
||||
================================================================================
|
||||
[Tool] Checking: https://blog.quant-view.xyz/tools/btc-position-size-calculator.html
|
||||
Status: 200
|
||||
Size: 5053 chars
|
||||
Word count (approx): 316
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 4
|
||||
Internal links: 3
|
||||
External links: 1
|
||||
Checked 4 unique non-asset links
|
||||
OK: 4
|
||||
Broken/Errors: 0
|
||||
|
||||
================================================================================
|
||||
[Tool] Checking: https://blog.quant-view.xyz/tools/risk-management-guide.html
|
||||
Status: 200
|
||||
Size: 6055 chars
|
||||
Word count (approx): 426
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 7
|
||||
Internal links: 6
|
||||
External links: 1
|
||||
Checked 6 unique non-asset links
|
||||
OK: 6
|
||||
Broken/Errors: 0
|
||||
|
||||
================================================================================
|
||||
[Tool] Checking: https://blog.quant-view.xyz/tools/tradingview-vs-mt5.html
|
||||
Status: 200
|
||||
Size: 6584 chars
|
||||
Word count (approx): 319
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 5
|
||||
Internal links: 3
|
||||
External links: 2
|
||||
Checked 5 unique non-asset links
|
||||
OK: 5
|
||||
Broken/Errors: 0
|
||||
|
||||
================================================================================
|
||||
[Tool] Checking: https://blog.quant-view.xyz/tools/kelly-criterion-formula.html
|
||||
Status: 200
|
||||
Size: 4811 chars
|
||||
Word count (approx): 290
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 4
|
||||
Internal links: 3
|
||||
External links: 1
|
||||
Checked 4 unique non-asset links
|
||||
OK: 4
|
||||
Broken/Errors: 0
|
||||
|
||||
================================================================================
|
||||
[Tool] Checking: https://blog.quant-view.xyz/tools/forex-position-size-calculator.html
|
||||
Status: 200
|
||||
Size: 5548 chars
|
||||
Word count (approx): 346
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 4
|
||||
Internal links: 3
|
||||
External links: 1
|
||||
Checked 4 unique non-asset links
|
||||
OK: 4
|
||||
Broken/Errors: 0
|
||||
|
||||
================================================================================
|
||||
[Tool] Checking: https://blog.quant-view.xyz/tools/pip-calculator-eurgbp.html
|
||||
Status: 200
|
||||
Size: 4543 chars
|
||||
Word count (approx): 259
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 4
|
||||
Internal links: 3
|
||||
External links: 1
|
||||
Checked 4 unique non-asset links
|
||||
OK: 4
|
||||
Broken/Errors: 0
|
||||
|
||||
================================================================================
|
||||
[Tool] Checking: https://blog.quant-view.xyz/tools/sp500-position-size-calculator.html
|
||||
Status: 200
|
||||
Size: 4784 chars
|
||||
Word count (approx): 282
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 4
|
||||
Internal links: 3
|
||||
External links: 1
|
||||
Checked 4 unique non-asset links
|
||||
OK: 4
|
||||
Broken/Errors: 0
|
||||
|
||||
================================================================================
|
||||
[Tool] Checking: https://blog.quant-view.xyz/tools/position-size-calculator-100000-dollar-account.html
|
||||
Status: 200
|
||||
Size: 5214 chars
|
||||
Word count (approx): 342
|
||||
No github.com/liudapao880807-arch links (good)
|
||||
Total links found: 4
|
||||
Internal links: 3
|
||||
External links: 1
|
||||
Checked 4 unique non-asset links
|
||||
OK: 4
|
||||
Broken/Errors: 0
|
||||
|
||||
================================================================================
|
||||
[Sitemap] Checking sitemap: https://blog.quant-view.xyz/sitemap.xml
|
||||
Status: 200
|
||||
URLs in sitemap: 3
|
||||
Sample: https://blog.quant-view.xyz/sitemap-tools.xml
|
||||
Sample: https://blog.quant-view.xyz/sitemap-posts.xml
|
||||
Sample: https://blog.quant-view.xyz/sitemap-geo.xml
|
||||
First 20 sitemap URLs all OK
|
||||
|
||||
================================================================================
|
||||
[Sitemap] Checking sitemap: https://blog.quant-view.xyz/sitemap-tools.xml
|
||||
Status: 200
|
||||
URLs in sitemap: 289
|
||||
Sample: https://blog.quant-view.xyz/tools/about-gfil.html
|
||||
Sample: https://blog.quant-view.xyz/tools/about-liudecai.html
|
||||
Sample: https://blog.quant-view.xyz/tools/ai-prompts.html
|
||||
Sample: https://blog.quant-view.xyz/tools/ai-trading-guide.html
|
||||
Sample: https://blog.quant-view.xyz/tools/api-reference.html
|
||||
First 20 sitemap URLs all OK
|
||||
|
||||
================================================================================
|
||||
[Sitemap] Checking sitemap: https://blog.quant-view.xyz/sitemap-posts.xml
|
||||
Status: 200
|
||||
URLs in sitemap: 81
|
||||
Sample: https://blog.quant-view.xyz/
|
||||
Sample: https://blog.quant-view.xyz/gfil-boss-panel-v70-review.html
|
||||
Sample: https://blog.quant-view.xyz/zh/gfil-boss-panel-v70-review.html
|
||||
Sample: https://blog.quant-view.xyz/es/gfil-boss-panel-v70-review.html
|
||||
Sample: https://blog.quant-view.xyz/ar/gfil-boss-panel-v70-review.html
|
||||
First 20 sitemap URLs all OK
|
||||
|
||||
================================================================================
|
||||
SUMMARY
|
||||
================================================================================
|
||||
|
||||
--- PAGE STATUS ---
|
||||
All 8 article pages: 200 OK, all have real content (870-1881 words)
|
||||
All 10 tool pages: 200 OK, all have real content (259-596 words)
|
||||
All 3 sitemaps: 200 OK
|
||||
|
||||
--- BROKEN LINKS ---
|
||||
Total broken links found: 8
|
||||
All 8 are twitter.com/intent/tweet share links returning 403.
|
||||
These are NOT real content issues — Twitter/X blocks HEAD requests from
|
||||
non-browser user agents. These share links work fine in an actual browser.
|
||||
|
||||
[403] twitter share link on position-size-calculator-guide.html
|
||||
[403] twitter share link on gold-trading-2026-guide.html
|
||||
[403] twitter share link on ssh-tunnel-deployment-china.html
|
||||
[403] twitter share link on github-seo-trading-tools.html
|
||||
[403] twitter share link on gold-pip-value-calculator-wrong.html
|
||||
[403] twitter share link on bloomberg-alternative.html
|
||||
[403] twitter share link on why-retail-traders-lose-money.html
|
||||
[403] twitter share link on order-flow-trading.html
|
||||
|
||||
--- BAD GITHUB LINKS ---
|
||||
github.com/liudapao880807-arch: ZERO occurrences found across all 18 pages.
|
||||
The old GitHub links have been fully replaced.
|
||||
|
||||
--- SITEMAP COVERAGE ---
|
||||
sitemap.xml: 3 child sitemaps (tools, posts, geo)
|
||||
sitemap-tools.xml: 289 tool page URLs
|
||||
sitemap-posts.xml: 81 post URLs (includes /zh/, /es/, /ar/ localized versions)
|
||||
First 20 URLs in each sitemap all return 200 OK.
|
||||
|
||||
--- OTHER NOTES ---
|
||||
- Tool pages are smaller (4.5-7KB) than article pages (16-24KB), which is expected
|
||||
- No 404 errors on any internal links
|
||||
- No connection errors on any external links (besides Twitter 403 false positive)
|
||||
- No blank or empty pages detected
|
||||
581
build_blog.py
Normal file
581
build_blog.py
Normal file
File diff suppressed because one or more lines are too long
68
check_backup.py
Normal file
68
check_backup.py
Normal file
@ -0,0 +1,68 @@
|
||||
"""RESTORE: Write correct Nginx config for gfil-lab.com"""
|
||||
import paramiko
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
# This is the CORRECT config based on what we saw from the probe output
|
||||
# (before our robots.txt changes broke it)
|
||||
CORRECT_CONFIG = r"""server {
|
||||
listen 80;
|
||||
server_name quant-node.com gold-node.xyz gfil-intel.xyz quant-view.xyz;
|
||||
|
||||
location /static/ {
|
||||
alias /GFIL/static/;
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /socket.io/ {
|
||||
proxy_pass http://127.0.0.1:5000/socket.io/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
# First backup current broken config, then write the correct one
|
||||
restore_cmd = f"""sshpass -p '{LAB_PASS}' ssh -o StrictHostKeyChecking=no {LAB_USER}@{LAB_HOST} '
|
||||
# Backup broken config
|
||||
cp /etc/nginx/sites-enabled/gfil /etc/nginx/sites-enabled/gfil.bak.broken
|
||||
|
||||
# Check if there is a gfil config in sites-available (original)
|
||||
echo "=== sites-available ==="
|
||||
ls -la /etc/nginx/sites-available/gfil* 2>/dev/null
|
||||
|
||||
# Check the default config too
|
||||
echo "=== default config ==="
|
||||
cat /etc/nginx/sites-enabled/default
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(restore_cmd, timeout=30)
|
||||
print(stdout.read().decode())
|
||||
|
||||
jd.close()
|
||||
31
check_github_links.py
Normal file
31
check_github_links.py
Normal file
@ -0,0 +1,31 @@
|
||||
"""Check all GitHub links on live pages"""
|
||||
import urllib.request
|
||||
import re
|
||||
|
||||
pages = ['entity.html', 'media.html', 'gfil-faq.html', 'about-gfil.html']
|
||||
|
||||
for page in pages:
|
||||
url = f'https://blog.quant-view.xyz/tools/{page}'
|
||||
try:
|
||||
r = urllib.request.urlopen(url, timeout=15)
|
||||
html = r.read().decode()
|
||||
gh_links = re.findall(r'href="(https://github\.com/[^"]+)"', html)
|
||||
gh_text = re.findall(r'github\.com/[\w\-/]+', html)
|
||||
print(f'\n=== {page} ===')
|
||||
print(f'GitHub href links: {len(gh_links)}')
|
||||
for link in set(gh_links):
|
||||
print(f' href: {link}')
|
||||
print(f'GitHub text refs: {len(gh_text)}')
|
||||
for t in set(gh_text):
|
||||
print(f' text: {t}')
|
||||
# Also check for liudecai or liudapao
|
||||
if 'liudecai' in html:
|
||||
for line in html.split('\n'):
|
||||
if 'liudecai' in line:
|
||||
print(f' LIUDECAI: {line.strip()[:200]}')
|
||||
if 'liudapao' in html:
|
||||
for line in html.split('\n'):
|
||||
if 'liudapao' in line:
|
||||
print(f' LIUDAPAO: {line.strip()[:200]}')
|
||||
except Exception as e:
|
||||
print(f'{page}: ERROR {e}')
|
||||
13
check_https_robots.py
Normal file
13
check_https_robots.py
Normal file
@ -0,0 +1,13 @@
|
||||
import urllib.request, sys, io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
||||
|
||||
req = urllib.request.Request("https://gfil-lab.com/robots.txt", headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
content = r.read().decode()
|
||||
print("Status:", r.status)
|
||||
print("Size:", len(content), "chars")
|
||||
print("GPTBot:", "GPTBot" in content)
|
||||
print("PerplexityBot:", "PerplexityBot" in content)
|
||||
print("ClaudeBot:", "ClaudeBot" in content)
|
||||
print()
|
||||
print(content)
|
||||
19
check_robots_file.py
Normal file
19
check_robots_file.py
Normal file
@ -0,0 +1,19 @@
|
||||
"""Check robots.txt file content on gfil-lab.com server"""
|
||||
import paramiko
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
cmd = "sshpass -p '" + LAB_PASS + "' ssh -o StrictHostKeyChecking=no " + LAB_USER + "@" + LAB_HOST + " 'echo \"=== File content ===\" && cat /var/www/gfil-lab/robots.txt && echo \"\" && echo \"=== File size ===\" && wc -c /var/www/gfil-lab/robots.txt && echo \"=== Direct curl (bypass CF) ===\" && curl -s http://localhost/robots.txt 2>/dev/null'"
|
||||
stdin, stdout, stderr = jd.exec_command(cmd, timeout=20)
|
||||
print(stdout.read().decode())
|
||||
jd.close()
|
||||
92
config.py
Normal file
92
config.py
Normal file
@ -0,0 +1,92 @@
|
||||
"""
|
||||
GFIL BLOG 统一配置加载器
|
||||
用法: from config import TELEGRAPH_TOKEN, BLOG_URL, ...
|
||||
所有密钥从 .env 加载,修改密钥只需改 .env 一个文件
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
ENV_FILE = os.path.join(BASE_DIR, ".env")
|
||||
|
||||
def _load_env():
|
||||
"""手动解析 .env,零依赖"""
|
||||
env = {}
|
||||
if os.path.exists(ENV_FILE):
|
||||
with open(ENV_FILE, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
key, _, val = line.partition("=")
|
||||
env[key.strip()] = val.strip().strip('"').strip("'")
|
||||
return env
|
||||
|
||||
_env = _load_env()
|
||||
|
||||
def _get(key, default=""):
|
||||
"""优先环境变量,其次 .env 文件"""
|
||||
return os.environ.get(key) or _env.get(key, default)
|
||||
|
||||
# --- 服务器 ---
|
||||
SSH_HOST = _get("SSH_HOST", "")
|
||||
SSH_USER = _get("SSH_USER", "root")
|
||||
SSH_PASS = _get("SSH_PASS")
|
||||
SSH_PORT = int(_get("SSH_PORT", "22"))
|
||||
|
||||
# --- API Keys ---
|
||||
TELEGRAPH_TOKEN = _get("TELEGRAPH_TOKEN")
|
||||
GITHUB_TOKEN = _get("GITHUB_TOKEN")
|
||||
GITHUB_TOKEN_ALT = _get("GITHUB_TOKEN_ALT")
|
||||
TG_BOT_TOKEN = _get("TG_BOT_TOKEN")
|
||||
DISCORD_WEBHOOK = _get("DISCORD_WEBHOOK")
|
||||
DEEPSEEK_KEY = _get("DEEPSEEK_KEY")
|
||||
DEVTO_KEY = _get("DEVTO_KEY")
|
||||
INDEXNOW_KEY = _get("INDEXNOW_KEY")
|
||||
SCREENSHOTONE_KEY = _get("SCREENSHOTONE_KEY")
|
||||
SHOTSTACK_KEY = _get("SHOTSTACK_KEY")
|
||||
BUFFER_TOKEN = _get("BUFFER_TOKEN")
|
||||
|
||||
# --- Google ---
|
||||
GOOGLE_EMAIL = _get("GOOGLE_EMAIL")
|
||||
GOOGLE_PASS = _get("GOOGLE_PASS")
|
||||
SMTP_PASS = _get("SMTP_PASS")
|
||||
GA_ID = _get("GA_ID")
|
||||
GOOGLE_VERIFY = _get("GOOGLE_VERIFY")
|
||||
|
||||
# --- URLs ---
|
||||
BLOG_URL = _get("BLOG_URL", "https://blog.quant-view.xyz")
|
||||
TERMINAL_URL = _get("TERMINAL_URL", "https://gfil-intel.xyz")
|
||||
LANDING_URL = _get("LANDING_URL", "https://gold-node.xyz")
|
||||
TG_CHANNEL = _get("TG_CHANNEL", "https://t.me/GFIL_Trading")
|
||||
DISCORD_INVITE = _get("DISCORD_INVITE", "https://discord.gg/GMmMCD4MCr")
|
||||
TOOLS_URL = f"{BLOG_URL}/tools/"
|
||||
|
||||
# --- 本地代理 ---
|
||||
HTTP_PROXY = _get("HTTP_PROXY")
|
||||
|
||||
# --- 邮箱 ---
|
||||
MAILERLITE_GROUP_ID = "189341033455158511"
|
||||
|
||||
# --- 密钥状态检查 ---
|
||||
def check_config():
|
||||
"""打印配置状态,快速发现过期密钥"""
|
||||
keys = {
|
||||
"Telegraph": TELEGRAPH_TOKEN,
|
||||
"GitHub": GITHUB_TOKEN,
|
||||
"TG Bot": TG_BOT_TOKEN,
|
||||
"Discord": DISCORD_WEBHOOK,
|
||||
"DeepSeek": DEEPSEEK_KEY,
|
||||
}
|
||||
print("=== 配置状态检查 ===")
|
||||
for name, val in keys.items():
|
||||
if not val:
|
||||
print(f" {name}: MISSING!")
|
||||
elif "expired" in val.lower() or "invalid" in val.lower():
|
||||
print(f" {name}: EXPIRED/INVALID")
|
||||
else:
|
||||
masked = val[:15] + "..." if len(val) > 15 else val
|
||||
print(f" {name}: {masked}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
check_config()
|
||||
119
deploy_and_verify.py
Normal file
119
deploy_and_verify.py
Normal file
@ -0,0 +1,119 @@
|
||||
import urllib.request, sys, io, paramiko, os, time
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
# === Step 1: Connect and run the fix on server ===
|
||||
print("=== STEP 1: Connect and modify ===")
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
# Upload fix script
|
||||
FIX_SCRIPT = r'''
|
||||
import shutil
|
||||
src = "/etc/nginx/sites-enabled/gfil"
|
||||
bak = "/etc/nginx/sites-enabled/gfil.bak.before-robots"
|
||||
shutil.copy2(src, bak)
|
||||
with open(src, "r") as f:
|
||||
content = f.read()
|
||||
robots_block = " location = /robots.txt {\n alias /var/www/gfil-lab/robots.txt;\n default_type text/plain;\n }\n\n"
|
||||
if "robots.txt" not in content:
|
||||
content = content.replace(" location / {", robots_block + " location / {")
|
||||
with open(src, "w") as f:
|
||||
f.write(content)
|
||||
print("INSERTED")
|
||||
else:
|
||||
print("ALREADY_EXISTS")
|
||||
'''
|
||||
script_path = r"C:\Users\thinkpad\AppData\Local\Temp\kilo\fix_nginx.py"
|
||||
with open(script_path, "w") as f:
|
||||
f.write(FIX_SCRIPT)
|
||||
|
||||
sftp = jd.open_sftp()
|
||||
sftp.put(script_path, "/tmp/fix_nginx.py")
|
||||
sftp.close()
|
||||
|
||||
# SCP to target
|
||||
scp_cmd = "sshpass -p '" + LAB_PASS + "' scp -o StrictHostKeyChecking=no /tmp/fix_nginx.py " + LAB_USER + "@" + LAB_HOST + ":/tmp/fix_nginx.py"
|
||||
stdin, stdout, stderr = jd.exec_command(scp_cmd, timeout=15)
|
||||
stdout.channel.recv_exit_status()
|
||||
|
||||
# Run on target
|
||||
run_cmd = "sshpass -p '" + LAB_PASS + "' ssh -o StrictHostKeyChecking=no " + LAB_USER + "@" + LAB_HOST + " 'python3 /tmp/fix_nginx.py && nginx -t 2>&1 && (systemctl reload nginx && echo NGINX_OK) || (cp /etc/nginx/sites-enabled/gfil.bak.before-robots /etc/nginx/sites-enabled/gfil && nginx -t 2>&1 && systemctl reload nginx && echo ROLLBACK_OK) && rm -f /tmp/fix_nginx.py'"
|
||||
stdin, stdout, stderr = jd.exec_command(run_cmd, timeout=30)
|
||||
print(stdout.read().decode())
|
||||
jd.close()
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
# === STEP 2: Code verification ===
|
||||
print("\n=== STEP 2: Code verification (automated) ===")
|
||||
|
||||
# 2a. Verify Nginx config diff
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
verify_cmd = "sshpass -p '" + LAB_PASS + "' ssh -o StrictHostKeyChecking=no " + LAB_USER + "@" + LAB_HOST + " 'echo DIFF: && diff /etc/nginx/sites-enabled/gfil /etc/nginx/sites-enabled/gfil.bak.before-robots && echo IDENTICAL || echo MODIFIED && echo CONFIG: && cat /etc/nginx/sites-enabled/gfil && echo NGINX_TEST: && nginx -t 2>&1'"
|
||||
stdin, stdout, stderr = jd.exec_command(verify_cmd, timeout=30)
|
||||
print(stdout.read().decode())
|
||||
jd.close()
|
||||
|
||||
# 2b. External HTTP checks
|
||||
print("=== External HTTP checks ===")
|
||||
for domain in ["gfil-lab.com", "gfil-intel.xyz"]:
|
||||
# Homepage
|
||||
try:
|
||||
req = urllib.request.Request("https://" + domain + "/", headers={"User-Agent": "Mozilla/5.0"})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
print(f" {domain} homepage: {r.status} OK, size={len(r.read())} bytes")
|
||||
except Exception as e:
|
||||
print(f" {domain} homepage: ERROR {e}")
|
||||
|
||||
# robots.txt
|
||||
try:
|
||||
req = urllib.request.Request("https://" + domain + "/robots.txt", headers={"User-Agent": "Mozilla/5.0"})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
content = r.read().decode()
|
||||
has_gptbot = "GPTBot" in content
|
||||
has_perplexity = "PerplexityBot" in content
|
||||
has_sitemap = "Sitemap:" in content
|
||||
print(f" {domain} robots.txt: {r.status} | GPTBot={has_gptbot} | PerplexityBot={has_perplexity} | Sitemap={has_sitemap} | {len(content)} chars")
|
||||
except Exception as e:
|
||||
print(f" {domain} robots.txt: ERROR {e}")
|
||||
|
||||
# 2c. Googlebot simulation
|
||||
print("\n=== Googlebot simulation ===")
|
||||
try:
|
||||
req = urllib.request.Request("https://gfil-lab.com/robots.txt", headers={"User-Agent": "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
content = r.read().decode()
|
||||
print(f" Googlebot -> gfil-lab.com/robots.txt: {r.status} OK, {len(content)} chars")
|
||||
print(f" First 3 lines: {content[:150]}")
|
||||
except Exception as e:
|
||||
print(f" Googlebot check: ERROR {e}")
|
||||
|
||||
# === STEP 3: Human observation checklist ===
|
||||
print("\n" + "=" * 60)
|
||||
print("STEP 3: HUMAN OBSERVATION CHECKLIST")
|
||||
print("=" * 60)
|
||||
print("Please manually verify in your browser:")
|
||||
print()
|
||||
print("1. Open https://gfil-lab.com/ -> should load normally (EN homepage)")
|
||||
print("2. Open https://gfil-lab.com/robots.txt -> should show full AI-crawler robots.txt")
|
||||
print("3. Open https://gfil-intel.xyz/robots.txt -> should show robots.txt with Sitemap: https://gfil-intel.xyz/sitemap.xml")
|
||||
print("4. Check login function works: https://gfil-lab.com/login")
|
||||
print("5. Check WebSocket: terminal page should connect")
|
||||
print()
|
||||
print("If anything is broken, rollback command:")
|
||||
print(" ssh root@216.144.233.14")
|
||||
print(" cp /etc/nginx/sites-enabled/gfil.bak.before-robots /etc/nginx/sites-enabled/gfil")
|
||||
print(" systemctl reload nginx")
|
||||
103
deploy_robots.py
Normal file
103
deploy_robots.py
Normal file
@ -0,0 +1,103 @@
|
||||
"""Deploy robots.txt to gfil-lab.com (216.144.233.14) and gfil-intel.xyz (107.174.186.162) via JD Cloud"""
|
||||
import paramiko
|
||||
import os
|
||||
import time
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
# gfil-lab.com server
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU" # from .env GFIL_SSH_PASSWORD
|
||||
|
||||
# gfil-intel.xyz server (same as blog)
|
||||
RN_HOST = "107.174.186.162"
|
||||
RN_USER = "root"
|
||||
RN_PASS = "liudapao2026"
|
||||
|
||||
TEMP_DIR = r"C:\Users\thinkpad\AppData\Local\Temp\kilo"
|
||||
|
||||
def deploy():
|
||||
# Step 1: Connect to JD Cloud
|
||||
print("[1/4] Connecting to JD Cloud...")
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
sftp = jd.open_sftp()
|
||||
print(" Connected")
|
||||
|
||||
# Step 2: Upload robots.txt files to JD Cloud
|
||||
print("\n[2/4] Uploading robots.txt files to JD Cloud...")
|
||||
sftp.put(os.path.join(TEMP_DIR, "robots_gfil_lab.txt"), "/tmp/robots_gfil_lab.txt")
|
||||
sftp.put(os.path.join(TEMP_DIR, "robots_gfil_intel.txt"), "/tmp/robots_gfil_intel.txt")
|
||||
print(" Uploaded")
|
||||
|
||||
# Step 3: Deploy to gfil-lab.com (216.144.233.14)
|
||||
print("\n[3/4] Deploying to gfil-lab.com (216.144.233.14)...")
|
||||
# Find the web root - could be /GFIL/static/ or Nginx serves from elsewhere
|
||||
# The Flask app serves static from /GFIL/static/, but robots.txt needs to be at web root
|
||||
# Nginx config shows: location /static/ { alias /GFIL/static/; }
|
||||
# For robots.txt at root, we need to place it where Nginx can serve it
|
||||
# Best approach: add to Nginx config or put in a root directory
|
||||
|
||||
deploy_cmd = f"""sshpass -p '{LAB_PASS}' ssh -o StrictHostKeyChecking=no {LAB_USER}@{LAB_HOST} '
|
||||
# Find Nginx config and web root
|
||||
echo "=== Finding web root ==="
|
||||
grep -r "root " /etc/nginx/sites-enabled/ 2>/dev/null | head -5
|
||||
grep -r "root " /etc/nginx/conf.d/ 2>/dev/null | head -5
|
||||
ls -la /var/www/ 2>/dev/null
|
||||
ls -la /GFIL/static/ 2>/dev/null | head -5
|
||||
|
||||
# Deploy robots.txt - try multiple locations
|
||||
cp /tmp/robots_gfil_lab.txt /GFIL/robots.txt 2>/dev/null
|
||||
cp /tmp/robots_gfil_lab.txt /GFIL/static/robots.txt 2>/dev/null
|
||||
cp /tmp/robots_gfil_lab.txt /var/www/html/robots.txt 2>/dev/null
|
||||
mkdir -p /var/www/gfil-lab.com 2>/dev/null
|
||||
cp /tmp/robots_gfil_lab.txt /var/www/gfil-lab.com/robots.txt 2>/dev/null
|
||||
|
||||
# Check if Nginx has a root directive we can use
|
||||
cat /etc/nginx/sites-enabled/gfil-lab.com 2>/dev/null || cat /etc/nginx/conf.d/gfil-lab.com.conf 2>/dev/null || echo "No gfil-lab nginx config found"
|
||||
|
||||
echo "=== Verifying ==="
|
||||
find / -name "robots.txt" -maxdepth 4 2>/dev/null | head -5
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(deploy_cmd, timeout=30)
|
||||
output = stdout.read().decode()
|
||||
print(output[:2000])
|
||||
|
||||
# Step 4: Deploy to gfil-intel.xyz (107.174.186.162 - RackNerd)
|
||||
print("\n[4/4] Deploying to gfil-intel.xyz (RackNerd 107.174.186.162)...")
|
||||
deploy_cmd2 = f"""sshpass -p '{RN_PASS}' ssh -o StrictHostKeyChecking=no {RN_USER}@{RN_HOST} '
|
||||
# Find where gfil-intel.xyz is served from
|
||||
echo "=== Finding gfil-intel.xyz web root ==="
|
||||
grep -r "gfil-intel" /etc/nginx/ 2>/dev/null | head -10
|
||||
ls -la /var/www/ 2>/dev/null | head -10
|
||||
find /var/www -name "index.html" -maxdepth 3 2>/dev/null | head -5
|
||||
|
||||
# Deploy robots.txt to likely locations
|
||||
cp /tmp/robots_gfil_intel.txt /var/www/gfil-intel.xyz/robots.txt 2>/dev/null
|
||||
cp /tmp/robots_gfil_intel.txt /var/www/html/robots.txt 2>/dev/null
|
||||
cp /tmp/robots_gfil_intel.txt /var/www/blog/robots.txt 2>/dev/null
|
||||
|
||||
echo "=== Nginx config ==="
|
||||
grep -l "gfil-intel" /etc/nginx/sites-enabled/ 2>/dev/null
|
||||
cat /etc/nginx/sites-enabled/gfil-intel* 2>/dev/null | head -30
|
||||
|
||||
echo "=== Verifying ==="
|
||||
find /var/www -name "robots.txt" -maxdepth 3 2>/dev/null
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(deploy_cmd2, timeout=30)
|
||||
output = stdout.read().decode()
|
||||
print(output[:2000])
|
||||
|
||||
# Cleanup
|
||||
jd.exec_command("rm -f /tmp/robots_gfil_lab.txt /tmp/robots_gfil_intel.txt", timeout=5)
|
||||
sftp.close()
|
||||
jd.close()
|
||||
print("\nDone!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
deploy()
|
||||
178
deploy_robots_v2.py
Normal file
178
deploy_robots_v2.py
Normal file
@ -0,0 +1,178 @@
|
||||
"""Deploy robots.txt to gfil-lab.com via Nginx location block + reload"""
|
||||
import paramiko
|
||||
import time
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
ROBOTS_CONTENT = """User-agent: *
|
||||
Allow: /
|
||||
|
||||
# === AI Search Crawlers (GEO) ===
|
||||
User-agent: OAI-SearchBot
|
||||
Allow: /
|
||||
User-agent: ChatGPT-User
|
||||
Allow: /
|
||||
User-agent: GPTBot
|
||||
Allow: /
|
||||
User-agent: ClaudeBot
|
||||
Allow: /
|
||||
User-agent: anthropic-ai
|
||||
Allow: /
|
||||
User-agent: PerplexityBot
|
||||
Allow: /
|
||||
User-agent: Google-Extended
|
||||
Allow: /
|
||||
User-agent: GoogleOther
|
||||
Allow: /
|
||||
# === China AI Search Crawlers ===
|
||||
User-agent: Bytespider
|
||||
Allow: /
|
||||
User-agent: DeepSeekBot
|
||||
Allow: /
|
||||
User-agent: KimiBot
|
||||
Allow: /
|
||||
User-agent: Baiduspider
|
||||
Allow: /
|
||||
# === Russia ===
|
||||
User-agent: YandexBot
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://gfil-lab.com/sitemap.xml
|
||||
"""
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
print("[1/3] Creating robots.txt and updating Nginx on gfil-lab.com...")
|
||||
|
||||
# Write robots.txt and add Nginx location block
|
||||
# Use sed to add location block before the existing "location /" block
|
||||
cmd = f"""sshpass -p '{LAB_PASS}' ssh -o StrictHostKeyChecking=no {LAB_USER}@{LAB_HOST} '
|
||||
# Create robots.txt file
|
||||
mkdir -p /var/www/gfil-lab
|
||||
cat > /var/www/gfil-lab/robots.txt << '"'"'ROBOTSEOF'"'"'
|
||||
{ROBOTS_CONTENT}
|
||||
ROBOTSEOF
|
||||
|
||||
# Verify file was created
|
||||
echo "=== robots.txt content ==="
|
||||
cat /var/www/gfil-lab/robots.txt
|
||||
|
||||
# Add Nginx location block for robots.txt
|
||||
# Insert before "location /" in the gfil config
|
||||
if ! grep -q "robots.txt" /etc/nginx/sites-enabled/gfil; then
|
||||
sed -i "/location \\/ {{/i\\\\n location = /robots.txt {{\\n alias /var/www/gfil-lab/robots.txt;\\n default_type text/plain;\\n }}" /etc/nginx/sites-enabled/gfil
|
||||
echo "Nginx config updated"
|
||||
else
|
||||
echo "robots.txt location already exists"
|
||||
fi
|
||||
|
||||
echo "=== Updated Nginx config ==="
|
||||
cat /etc/nginx/sites-enabled/gfil
|
||||
|
||||
# Test and reload Nginx
|
||||
nginx -t 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl reload nginx
|
||||
echo "Nginx reloaded successfully"
|
||||
else
|
||||
echo "Nginx config test FAILED - not reloading"
|
||||
fi
|
||||
|
||||
# Verify robots.txt is accessible
|
||||
sleep 1
|
||||
curl -s -o /dev/null -w "%{{http_code}}" http://localhost/robots.txt 2>/dev/null || echo "curl failed"
|
||||
'"""
|
||||
|
||||
stdin, stdout, stderr = jd.exec_command(cmd, timeout=30)
|
||||
output = stdout.read().decode()
|
||||
print(output[:3000])
|
||||
|
||||
print("\n[2/3] Verifying gfil-intel.xyz also gets robots.txt (it proxies gfil-lab.com)...")
|
||||
cmd2 = f"""sshpass -p '{RN_PASS}' ssh -o StrictHostKeyChecking=no {RN_USER}@107.174.186.162 '
|
||||
# gfil-intel.xyz proxies to gfil-lab.com, so robots.txt should come through
|
||||
# But it also has sub_filter that replaces gfil-lab.com -> $host
|
||||
# Test locally
|
||||
curl -s -o /dev/null -w "%{{http_code}}" -H "Host: gfil-intel.xyz" http://localhost/robots.txt 2>/dev/null || echo "direct test failed"
|
||||
|
||||
# Also check if gfil-mask nginx config needs a separate robots.txt location
|
||||
if ! grep -q "robots.txt" /etc/nginx/sites-available/gfil-mask; then
|
||||
echo "Need to add robots.txt location to gfil-mask config too"
|
||||
|
||||
mkdir -p /var/www/gfil-intel
|
||||
cat > /var/www/gfil-intel/robots.txt << '"'"'ROBOTSEOF'"'"'
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
User-agent: OAI-SearchBot
|
||||
Allow: /
|
||||
User-agent: ChatGPT-User
|
||||
Allow: /
|
||||
User-agent: GPTBot
|
||||
Allow: /
|
||||
User-agent: ClaudeBot
|
||||
Allow: /
|
||||
User-agent: anthropic-ai
|
||||
Allow: /
|
||||
User-agent: PerplexityBot
|
||||
Allow: /
|
||||
User-agent: Google-Extended
|
||||
Allow: /
|
||||
User-agent: GoogleOther
|
||||
Allow: /
|
||||
User-agent: Bytespider
|
||||
Allow: /
|
||||
User-agent: DeepSeekBot
|
||||
Allow: /
|
||||
User-agent: KimiBot
|
||||
Allow: /
|
||||
User-agent: Baiduspider
|
||||
Allow: /
|
||||
User-agent: YandexBot
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://gfil-intel.xyz/sitemap.xml
|
||||
ROBOTSEOF
|
||||
|
||||
# Add location block before the main location /
|
||||
sed -i "/location \\/ {{/i\\\\n location = /robots.txt {{\\n alias /var/www/gfil-intel/robots.txt;\\n default_type text/plain;\\n }}" /etc/nginx/sites-available/gfil-mask
|
||||
echo "gfil-mask config updated"
|
||||
|
||||
nginx -t 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl reload nginx
|
||||
echo "Nginx reloaded on RackNerd"
|
||||
else
|
||||
echo "Nginx config FAILED on RackNerd"
|
||||
fi
|
||||
else
|
||||
echo "robots.txt location already in gfil-mask"
|
||||
fi
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(cmd2, timeout=30)
|
||||
output = stdout.read().decode()
|
||||
print(output[:2000])
|
||||
|
||||
print("\n[3/3] Final verification from external...")
|
||||
jd.close()
|
||||
|
||||
# Test from local
|
||||
import urllib.request
|
||||
for domain in ['gfil-lab.com', 'gfil-intel.xyz']:
|
||||
try:
|
||||
req = urllib.request.Request(f'https://{domain}/robots.txt',
|
||||
headers={'User-Agent': 'Mozilla/5.0'})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
content = r.read().decode()
|
||||
has_gptbot = 'GPTBot' in content
|
||||
print(f" {domain}: {r.status}, GPTBot={has_gptbot}, {len(content)} chars")
|
||||
except Exception as e:
|
||||
print(f" {domain}: {e}")
|
||||
138
deploy_robots_v3.py
Normal file
138
deploy_robots_v3.py
Normal file
@ -0,0 +1,138 @@
|
||||
"""Fix Nginx config on gfil-lab.com and deploy robots.txt to gfil-intel.xyz"""
|
||||
import paramiko
|
||||
import time
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
RN_HOST = "107.174.186.162"
|
||||
RN_USER = "root"
|
||||
RN_PASS = "liudapao2026"
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
# === Fix gfil-lab.com Nginx config ===
|
||||
print("[1/3] Fixing Nginx config on gfil-lab.com...")
|
||||
fix_cmd = """sshpass -p 'Kt9V72Tx2c48ChikKU' ssh -o StrictHostKeyChecking=no root@216.144.233.14 '
|
||||
# Remove the broken line with standalone "n"
|
||||
sed -i "/^n location = \/robots\.txt/d" /etc/nginx/sites-enabled/gfil
|
||||
|
||||
# Check current state
|
||||
echo "=== Current config (robots.txt area) ==="
|
||||
grep -n "robots" /etc/nginx/sites-enabled/gfil
|
||||
|
||||
# If robots.txt location block already exists and is clean, just test
|
||||
# If not, add it properly
|
||||
if grep -q "robots.txt" /etc/nginx/sites-enabled/gfil; then
|
||||
echo "robots.txt block exists, checking syntax..."
|
||||
else
|
||||
echo "Adding robots.txt location block..."
|
||||
sed -i "/location \/ {/i\\ location = /robots.txt {\\n alias /var/www/gfil-lab/robots.txt;\\n default_type text/plain;\\n }" /etc/nginx/sites-enabled/gfil
|
||||
fi
|
||||
|
||||
# Show the config around the robots block
|
||||
grep -n -B2 -A5 "robots" /etc/nginx/sites-enabled/gfil
|
||||
|
||||
# Test nginx
|
||||
nginx -t 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl reload nginx
|
||||
echo "Nginx reloaded OK"
|
||||
else
|
||||
echo "Nginx test still failing, showing full config..."
|
||||
cat -n /etc/nginx/sites-enabled/gfil
|
||||
fi
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(fix_cmd, timeout=30)
|
||||
print(stdout.read().decode()[:3000])
|
||||
|
||||
# === Deploy robots.txt to gfil-intel.xyz on RackNerd ===
|
||||
print("\n[2/3] Deploying robots.txt to gfil-intel.xyz (RackNerd)...")
|
||||
intel_cmd = """sshpass -p 'liudapao2026' ssh -o StrictHostKeyChecking=no root@107.174.186.162 '
|
||||
mkdir -p /var/www/gfil-intel
|
||||
|
||||
# Write robots.txt
|
||||
cat > /var/www/gfil-intel/robots.txt << '"'"'EOF'"'"'
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
User-agent: OAI-SearchBot
|
||||
Allow: /
|
||||
User-agent: ChatGPT-User
|
||||
Allow: /
|
||||
User-agent: GPTBot
|
||||
Allow: /
|
||||
User-agent: ClaudeBot
|
||||
Allow: /
|
||||
User-agent: anthropic-ai
|
||||
Allow: /
|
||||
User-agent: PerplexityBot
|
||||
Allow: /
|
||||
User-agent: Google-Extended
|
||||
Allow: /
|
||||
User-agent: GoogleOther
|
||||
Allow: /
|
||||
User-agent: Bytespider
|
||||
Allow: /
|
||||
User-agent: DeepSeekBot
|
||||
Allow: /
|
||||
User-agent: KimiBot
|
||||
Allow: /
|
||||
User-agent: Baiduspider
|
||||
Allow: /
|
||||
User-agent: YandexBot
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://gfil-intel.xyz/sitemap.xml
|
||||
EOF
|
||||
|
||||
# Verify
|
||||
echo "=== robots.txt ==="
|
||||
cat /var/www/gfil-intel/robots.txt | head -5
|
||||
|
||||
# Add Nginx location block to gfil-mask if not already there
|
||||
if grep -q "robots.txt" /etc/nginx/sites-available/gfil-mask; then
|
||||
echo "robots.txt block already in gfil-mask"
|
||||
else
|
||||
echo "Adding robots.txt to gfil-mask..."
|
||||
sed -i "/location \\/ {/i\\ location = /robots.txt {\\n alias /var/www/gfil-intel/robots.txt;\\n default_type text/plain;\\n }" /etc/nginx/sites-available/gfil-mask
|
||||
fi
|
||||
|
||||
# Test and reload
|
||||
nginx -t 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl reload nginx
|
||||
echo "Nginx reloaded on RackNerd"
|
||||
else
|
||||
echo "Nginx test FAILED on RackNerd"
|
||||
cat -n /etc/nginx/sites-available/gfil-mask | head -40
|
||||
fi
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(intel_cmd, timeout=30)
|
||||
print(stdout.read().decode()[:2000])
|
||||
|
||||
# === Verify both from external ===
|
||||
print("\n[3/3] External verification...")
|
||||
jd.close()
|
||||
|
||||
import urllib.request
|
||||
for domain in ['gfil-lab.com', 'gfil-intel.xyz', 'blog.quant-view.xyz']:
|
||||
try:
|
||||
req = urllib.request.Request(f'https://{domain}/robots.txt',
|
||||
headers={'User-Agent': 'Mozilla/5.0'})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
content = r.read().decode()
|
||||
has_gptbot = 'GPTBot' in content
|
||||
has_perplexity = 'PerplexityBot' in content
|
||||
has_sitemap = 'Sitemap:' in content
|
||||
print(f" {domain}: {r.status} OK | GPTBot={has_gptbot} | PerplexityBot={has_perplexity} | Sitemap={has_sitemap} | {len(content)} chars")
|
||||
except Exception as e:
|
||||
print(f" {domain}: {e}")
|
||||
122
deploy_via_jdcloud.py
Normal file
122
deploy_via_jdcloud.py
Normal file
@ -0,0 +1,122 @@
|
||||
"""Deploy blog to RackNerd via JD Cloud jumphost"""
|
||||
import paramiko
|
||||
import os
|
||||
import time
|
||||
import glob
|
||||
|
||||
# JD Cloud jumphost
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
# RackNerd target
|
||||
RN_HOST = "107.174.186.162"
|
||||
RN_USER = "root"
|
||||
RN_PASS = "liudapao2026"
|
||||
RN_DIR = "/var/www/blog/"
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
def deploy():
|
||||
# Find latest packages
|
||||
output_pkgs = sorted(glob.glob(os.path.join(BASE_DIR, "blog-deploy-*.tar.gz")))
|
||||
tools_pkgs = sorted(glob.glob(os.path.join(BASE_DIR, "blog-tools-*.tar.gz")))
|
||||
|
||||
if not output_pkgs or not tools_pkgs:
|
||||
print("ERROR: No deployment packages found. Run build first.")
|
||||
return
|
||||
|
||||
output_pkg = output_pkgs[-1]
|
||||
tools_pkg = tools_pkgs[-1]
|
||||
print(f"Deploying: {os.path.basename(output_pkg)}, {os.path.basename(tools_pkg)}")
|
||||
|
||||
# Step 1: Connect to JD Cloud
|
||||
print("\n[1/5] Connecting to JD Cloud jumphost...")
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
print(" Connected to JD Cloud")
|
||||
|
||||
# Step 2: Upload packages to JD Cloud
|
||||
print("\n[2/5] Uploading packages to JD Cloud...")
|
||||
sftp = jd.open_sftp()
|
||||
|
||||
remote_output = f"/tmp/{os.path.basename(output_pkg)}"
|
||||
remote_tools = f"/tmp/{os.path.basename(tools_pkg)}"
|
||||
|
||||
for local, remote in [(output_pkg, remote_output), (tools_pkg, remote_tools)]:
|
||||
size_mb = os.path.getsize(local) / (1024*1024)
|
||||
print(f" Uploading {os.path.basename(local)} ({size_mb:.1f} MB)...")
|
||||
sftp.put(local, remote)
|
||||
print(f" Uploaded to {remote}")
|
||||
|
||||
sftp.close()
|
||||
|
||||
# Step 3: SCP from JD Cloud to RackNerd
|
||||
print("\n[3/5] SCP packages to RackNerd...")
|
||||
# Install sshpass if not present
|
||||
jd.exec_command("which sshpass || apt-get install -y sshpass", timeout=30)
|
||||
time.sleep(2)
|
||||
|
||||
scp_cmd = f"sshpass -p '{RN_PASS}' scp -o StrictHostKeyChecking=no {remote_output} {remote_tools} {RN_USER}@{RN_HOST}:/tmp/"
|
||||
print(f" Running: {scp_cmd[:80]}...")
|
||||
stdin, stdout, stderr = jd.exec_command(scp_cmd, timeout=120)
|
||||
exit_code = stdout.channel.recv_exit_status()
|
||||
if exit_code != 0:
|
||||
err = stderr.read().decode()
|
||||
print(f" SCP error (exit {exit_code}): {err[:200]}")
|
||||
# Try again
|
||||
print(" Retrying...")
|
||||
time.sleep(3)
|
||||
stdin, stdout, stderr = jd.exec_command(scp_cmd, timeout=120)
|
||||
exit_code = stdout.channel.recv_exit_status()
|
||||
if exit_code != 0:
|
||||
print(f" SCP failed again: {stderr.read().decode()[:200]}")
|
||||
jd.close()
|
||||
return
|
||||
print(" SCP complete")
|
||||
|
||||
# Step 4: Extract on RackNerd
|
||||
print("\n[4/5] Extracting on RackNerd...")
|
||||
extract_cmd = f"""sshpass -p '{RN_PASS}' ssh -o StrictHostKeyChecking=no {RN_USER}@{RN_HOST} '
|
||||
cd {RN_DIR}
|
||||
echo "Extracting output..."
|
||||
tar -xzf /tmp/{os.path.basename(output_pkg)}
|
||||
echo "Extracting tools..."
|
||||
mkdir -p tools
|
||||
tar -xzf /tmp/{os.path.basename(tools_pkg)} -C tools/
|
||||
echo "Setting permissions..."
|
||||
chmod -R 644 *.html tools/*.html 2>/dev/null
|
||||
chmod 755 . tools/ tools/ar tools/es tools/zh tools/md 2>/dev/null
|
||||
echo "Cleaning up /tmp..."
|
||||
rm -f /tmp/{os.path.basename(output_pkg)} /tmp/{os.path.basename(tools_pkg)}
|
||||
echo "DONE"
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(extract_cmd, timeout=60)
|
||||
output = stdout.read().decode()
|
||||
err = stderr.read().decode()
|
||||
print(f" Output: {output}")
|
||||
if err:
|
||||
print(f" Stderr: {err[:200]}")
|
||||
|
||||
# Step 5: Verify
|
||||
print("\n[5/5] Verifying deployment...")
|
||||
verify_cmd = f"""sshpass -p '{RN_PASS}' ssh -o StrictHostKeyChecking=no {RN_USER}@{RN_HOST} '
|
||||
echo "HTML files in root: $(ls {RN_DIR}*.html 2>/dev/null | wc -l)"
|
||||
echo "HTML files in tools: $(ls {RN_DIR}tools/*.html 2>/dev/null | wc -l)"
|
||||
echo "Sitemap size: $(wc -l < {RN_DIR}sitemap.xml 2>/dev/null)"
|
||||
echo "IndexNow key: $(cat {RN_DIR}72aa77b68704abcfada4020ba81f7c5a.txt 2>/dev/null | head -1)"
|
||||
echo "BingSiteAuth: $(cat {RN_DIR}BingSiteAuth.xml 2>/dev/null | head -1)"
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(verify_cmd, timeout=30)
|
||||
print(stdout.read().decode())
|
||||
|
||||
# Clean up JD Cloud temp files
|
||||
jd.exec_command(f"rm -f {remote_output} {remote_tools}", timeout=10)
|
||||
|
||||
jd.close()
|
||||
print("\nDeployment complete!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
deploy()
|
||||
61
diagnose_domains.py
Normal file
61
diagnose_domains.py
Normal file
@ -0,0 +1,61 @@
|
||||
"""Diagnose gfil-lab.com redirect loop and gfil-intel.xyz bot blocking"""
|
||||
import urllib.request
|
||||
import http.client
|
||||
|
||||
# Test gfil-lab.com
|
||||
print("=== gfil-lab.com ===")
|
||||
try:
|
||||
r = urllib.request.urlopen("https://gfil-lab.com", timeout=15)
|
||||
print(f"Status: {r.status}")
|
||||
print(f"URL after redirects: {r.url}")
|
||||
print(f"Headers: {dict(r.headers)}")
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f"HTTPError: {e.code} {e.reason}")
|
||||
print(f"Headers: {dict(e.headers)}")
|
||||
print(f"Redirect location: {e.headers.get('Location', 'N/A')}")
|
||||
except Exception as e:
|
||||
print(f"Error: {type(e).__name__}: {e}")
|
||||
|
||||
# Test with manual redirect following
|
||||
print("\n=== Manual redirect trace for gfil-lab.com ===")
|
||||
url = "https://gfil-lab.com"
|
||||
for i in range(10):
|
||||
try:
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
print(f"Hop {i}: {url} -> {r.url} (status {r.status})")
|
||||
break
|
||||
except urllib.error.HTTPError as e:
|
||||
loc = e.headers.get("Location", "")
|
||||
print(f"Hop {i}: {url} -> {e.code} {e.reason}, Location: {loc}")
|
||||
if loc:
|
||||
url = loc
|
||||
else:
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"Hop {i}: {url} -> Error: {e}")
|
||||
break
|
||||
|
||||
# Test gfil-intel.xyz
|
||||
print("\n=== gfil-intel.xyz ===")
|
||||
try:
|
||||
req = urllib.request.Request("https://gfil-intel.xyz", headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"})
|
||||
r = urllib.request.urlopen(req, timeout=15)
|
||||
print(f"Status: {r.status}")
|
||||
print(f"Size: {len(r.read())} bytes")
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f"HTTPError: {e.code} {e.reason}")
|
||||
body = e.read().decode()[:500]
|
||||
print(f"Body: {body}")
|
||||
except Exception as e:
|
||||
print(f"Error: {type(e).__name__}: {e}")
|
||||
|
||||
# Check DNS
|
||||
print("\n=== DNS Check ===")
|
||||
import socket
|
||||
for domain in ["gfil-lab.com", "gfil-intel.xyz"]:
|
||||
try:
|
||||
ips = socket.getaddrinfo(domain, 443, socket.AF_INET)
|
||||
print(f"{domain}: {[x[4][0] for x in ips[:3]]}")
|
||||
except Exception as e:
|
||||
print(f"{domain}: DNS error - {e}")
|
||||
97
diagnose_gfillab.py
Normal file
97
diagnose_gfillab.py
Normal file
@ -0,0 +1,97 @@
|
||||
"""Diagnose exactly what's blocking gfil-lab.com"""
|
||||
import urllib.request, sys, io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
||||
|
||||
# Test gfil-lab.com with various approaches
|
||||
print("=== gfil-lab.com 诊断 ===\n")
|
||||
|
||||
# 1. Plain request (no UA)
|
||||
print("[1] 无 User-Agent:")
|
||||
try:
|
||||
r = urllib.request.urlopen("https://gfil-lab.com/", timeout=10)
|
||||
print(f" Status: {r.status}, URL: {r.url}")
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f" HTTP {e.code} {e.reason}")
|
||||
print(f" Server: {e.headers.get('Server','?')}")
|
||||
print(f" CF-Ray: {e.headers.get('CF-Ray','?')}")
|
||||
body = e.read().decode()[:300]
|
||||
print(f" Body: {body}")
|
||||
|
||||
# 2. With browser UA
|
||||
print("\n[2] 浏览器 User-Agent:")
|
||||
try:
|
||||
req = urllib.request.Request("https://gfil-lab.com/", headers={
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
|
||||
})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
print(f" Status: {r.status}, URL: {r.url}")
|
||||
print(f" Size: {len(r.read())} bytes")
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f" HTTP {e.code} {e.reason}")
|
||||
body = e.read().decode()[:300]
|
||||
print(f" Body: {body}")
|
||||
|
||||
# 3. Googlebot UA
|
||||
print("\n[3] Googlebot User-Agent:")
|
||||
try:
|
||||
req = urllib.request.Request("https://gfil-lab.com/", headers={
|
||||
"User-Agent": "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
|
||||
})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
print(f" Status: {r.status}, URL: {r.url}")
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f" HTTP {e.code} {e.reason}")
|
||||
body = e.read().decode()[:300]
|
||||
print(f" Body: {body}")
|
||||
|
||||
# 4. Bingbot UA
|
||||
print("\n[4] Bingbot User-Agent:")
|
||||
try:
|
||||
req = urllib.request.Request("https://gfil-lab.com/", headers={
|
||||
"User-Agent": "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"
|
||||
})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
print(f" Status: {r.status}, URL: {r.url}")
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f" HTTP {e.code} {e.reason}")
|
||||
body = e.read().decode()[:300]
|
||||
print(f" Body: {body}")
|
||||
|
||||
# 5. robots.txt
|
||||
print("\n[5] robots.txt:")
|
||||
try:
|
||||
req = urllib.request.Request("https://gfil-lab.com/robots.txt", headers={
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||
})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
content = r.read().decode()
|
||||
print(f" Status: {r.status}")
|
||||
print(f" Content: {content[:500]}")
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f" HTTP {e.code} {e.reason}")
|
||||
body = e.read().decode()[:300]
|
||||
print(f" Body: {body}")
|
||||
|
||||
# 6. Check CF headers for challenge type
|
||||
print("\n[6] 检查响应头详情:")
|
||||
try:
|
||||
req = urllib.request.Request("https://gfil-lab.com/", headers={
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||
})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
for h in ['Server', 'CF-Ray', 'CF-Cache-Status', 'X-Content-Type-Options', 'Content-Type']:
|
||||
print(f" {h}: {r.headers.get(h, 'N/A')}")
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f" HTTP {e.code}")
|
||||
for h in ['Server', 'CF-Ray', 'CF-Cache-Status', 'Content-Type']:
|
||||
print(f" {h}: {e.headers.get(h, 'N/A')}")
|
||||
|
||||
# 7. HTTP (non-HTTPS) test
|
||||
print("\n[7] HTTP (非HTTPS):")
|
||||
try:
|
||||
r = urllib.request.urlopen("http://gfil-lab.com/", timeout=10)
|
||||
print(f" Status: {r.status}, URL: {r.url}")
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f" HTTP {e.code} {e.reason}")
|
||||
except Exception as e:
|
||||
print(f" Error: {type(e).__name__}: {e}")
|
||||
89
final_audit.py
Normal file
89
final_audit.py
Normal file
@ -0,0 +1,89 @@
|
||||
"""Final audit: verify all reviewer fixes are live"""
|
||||
import urllib.request
|
||||
import re, sys, io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
||||
|
||||
SITE = "https://blog.quant-view.xyz"
|
||||
|
||||
def fetch(path):
|
||||
try:
|
||||
url = f"{SITE}/{path}"
|
||||
r = urllib.request.urlopen(url, timeout=15)
|
||||
return r.read().decode()
|
||||
except Exception as e:
|
||||
return f"ERROR: {e}"
|
||||
|
||||
results = []
|
||||
|
||||
# 1. entity.html - zero github links, zero liudecai-one
|
||||
html = fetch("tools/entity.html")
|
||||
gh = len(re.findall(r'github\.com', html))
|
||||
l1 = html.count('liudecai-one')
|
||||
results.append(("entity.html: GitHub links = 0", gh == 0))
|
||||
results.append(("entity.html: liudecai-one = 0", l1 == 0))
|
||||
|
||||
# 2. GitHub SEO article
|
||||
html = fetch("github-seo-trading-tools.html")
|
||||
has_npm = 'npmjs.com' in html
|
||||
has_gitlab = 'gitlab.com/liudecai110' in html
|
||||
has_degithub = 'de-GitHub' in html
|
||||
# PyPI should appear in After block exactly once (not duplicated)
|
||||
after_block = re.search(r'// After:.*?]', html, re.DOTALL)
|
||||
if after_block:
|
||||
after_text = after_block.group()
|
||||
pypi_in_after = after_text.count('https://pypi.org/project/gfil-calculators/')
|
||||
npm_in_after = 'npmjs.com' in after_text
|
||||
gitlab_in_after = 'gitlab.com' in after_text
|
||||
else:
|
||||
pypi_in_after = -1
|
||||
npm_in_after = False
|
||||
gitlab_in_after = False
|
||||
results.append(("article19: After block PyPI count = 1", pypi_in_after == 1))
|
||||
results.append(("article19: After block has npm", npm_in_after))
|
||||
results.append(("article19: After block has GitLab", gitlab_in_after))
|
||||
results.append(("article19: Mistake#1 updated (de-GitHub)", has_degithub))
|
||||
results.append(("article19: overall has npm link", has_npm))
|
||||
results.append(("article19: overall has GitLab link", has_gitlab))
|
||||
|
||||
# 3. Footer - English pages use Telegram, not WeChat
|
||||
html_en = fetch("")
|
||||
html_zh = fetch("zh/")
|
||||
en_no_wechat = 'LDP161109' not in html_en
|
||||
en_has_telegram = '@GFIL_Trading' in html_en or 'Telegram' in html_en
|
||||
zh_has_wechat = 'LDP161109' in html_zh
|
||||
results.append(("EN index: NO WeChat/QQ", en_no_wechat))
|
||||
results.append(("EN index: HAS Telegram", en_has_telegram))
|
||||
results.append(("ZH index: HAS WeChat/QQ", zh_has_wechat))
|
||||
|
||||
# 4. research.html - "GitHub" label fixed to "PyPI"
|
||||
html = fetch("tools/research.html")
|
||||
# Check that PyPI link text is NOT "GitHub"
|
||||
pypi_with_github_label = bool(re.search(r'pypi\.org[^>]*>GitHub</a>', html))
|
||||
results.append(("research.html: no mislabeled GitHub->PyPI", not pypi_with_github_label))
|
||||
|
||||
# 5. gfil-faq.html - has GitLab, no "GitHub" label on PyPI
|
||||
html = fetch("tools/gfil-faq.html")
|
||||
has_gitlab_faq = 'gitlab.com/liudecai110' in html
|
||||
results.append(("gfil-faq.html: has GitLab link", has_gitlab_faq))
|
||||
|
||||
# 6. media.html - zero github links
|
||||
html = fetch("tools/media.html")
|
||||
gh = len(re.findall(r'github\.com', html))
|
||||
results.append(("media.html: GitHub links = 0", gh == 0))
|
||||
|
||||
# Print results
|
||||
print("=" * 70)
|
||||
print("FINAL AUDIT: Reviewer Fixes Verification")
|
||||
print("=" * 70)
|
||||
all_pass = True
|
||||
for label, passed in results:
|
||||
status = "PASS" if passed else "FAIL"
|
||||
if not passed:
|
||||
all_pass = False
|
||||
print(f" [{status}] {label}")
|
||||
|
||||
print()
|
||||
if all_pass:
|
||||
print("ALL CHECKS PASSED")
|
||||
else:
|
||||
print("SOME CHECKS FAILED")
|
||||
25
final_check.py
Normal file
25
final_check.py
Normal file
@ -0,0 +1,25 @@
|
||||
import urllib.request, sys, io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
||||
|
||||
for domain in ["gfil-lab.com", "gfil-intel.xyz", "blog.quant-view.xyz"]:
|
||||
try:
|
||||
url = "https://" + domain + "/robots.txt?nocache=1"
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
content = r.read().decode()
|
||||
gb = "GPTBot" in content
|
||||
cb = "ClaudeBot" in content
|
||||
pb = "PerplexityBot" in content
|
||||
sm = "Sitemap" in content
|
||||
print(domain + ": " + str(r.status) + " | " + str(len(content)) + " chars | GPTBot=" + str(gb) + " ClaudeBot=" + str(cb) + " PerplexityBot=" + str(pb) + " Sitemap=" + str(sm))
|
||||
except Exception as e:
|
||||
print(domain + ": " + str(e))
|
||||
|
||||
# Also check homepage still works
|
||||
for domain in ["gfil-lab.com", "gfil-intel.xyz"]:
|
||||
try:
|
||||
req = urllib.request.Request("https://" + domain + "/", headers={"User-Agent": "Mozilla/5.0"})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
print(domain + " homepage: " + str(r.status) + " OK")
|
||||
except Exception as e:
|
||||
print(domain + " homepage: ERROR " + str(e))
|
||||
125
fix_and_verify.py
Normal file
125
fix_and_verify.py
Normal file
@ -0,0 +1,125 @@
|
||||
import paramiko, sys, io, time
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
# Fix script that modifies BOTH server blocks
|
||||
FIX_SCRIPT = r'''
|
||||
import shutil
|
||||
|
||||
# Fix the "default" config (server_name: gfil-lab.com - this is the one that actually serves gfil-lab.com)
|
||||
src = "/etc/nginx/sites-enabled/default"
|
||||
bak = "/etc/nginx/sites-enabled/default.bak.before-robots"
|
||||
shutil.copy2(src, bak)
|
||||
|
||||
with open(src, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
robots_block = " location = /robots.txt {\n alias /var/www/gfil-lab/robots.txt;\n default_type text/plain;\n }\n\n"
|
||||
|
||||
if "robots.txt" not in content:
|
||||
content = content.replace(" location / {", robots_block + " location / {")
|
||||
with open(src, "w") as f:
|
||||
f.write(content)
|
||||
print("INSERTED into default")
|
||||
else:
|
||||
print("ALREADY in default")
|
||||
'''
|
||||
script_path = r"C:\Users\thinkpad\AppData\Local\Temp\kilo\fix_default.py"
|
||||
with open(script_path, "w") as f:
|
||||
f.write(FIX_SCRIPT)
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
sftp = jd.open_sftp()
|
||||
sftp.put(script_path, "/tmp/fix_default.py")
|
||||
sftp.close()
|
||||
|
||||
# SCP and run
|
||||
scp_cmd = "sshpass -p '" + LAB_PASS + "' scp -o StrictHostKeyChecking=no /tmp/fix_default.py " + LAB_USER + "@" + LAB_HOST + ":/tmp/fix_default.py"
|
||||
stdin, stdout, stderr = jd.exec_command(scp_cmd, timeout=15)
|
||||
stdout.channel.recv_exit_status()
|
||||
|
||||
run_cmd = "sshpass -p '" + LAB_PASS + "' ssh -o StrictHostKeyChecking=no " + LAB_USER + "@" + LAB_HOST + " 'python3 /tmp/fix_default.py && echo --- && cat /etc/nginx/sites-enabled/default && echo --- && nginx -t 2>&1 && (systemctl reload nginx && echo NGINX_OK) || (cp /etc/nginx/sites-enabled/default.bak.before-robots /etc/nginx/sites-enabled/default && nginx -t && systemctl reload nginx && echo ROLLBACK_OK) && rm -f /tmp/fix_default.py'"
|
||||
stdin, stdout, stderr = jd.exec_command(run_cmd, timeout=30)
|
||||
print(stdout.read().decode())
|
||||
jd.close()
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
# === STEP 2: Code verification ===
|
||||
print("\n=== STEP 2: CODE VERIFICATION ===")
|
||||
|
||||
# 2a. External check
|
||||
import urllib.request
|
||||
all_pass = True
|
||||
for domain in ["gfil-lab.com", "gfil-intel.xyz"]:
|
||||
# Homepage
|
||||
try:
|
||||
req = urllib.request.Request("https://" + domain + "/", headers={"User-Agent": "Mozilla/5.0"})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
print(f" {domain} homepage: {r.status} OK")
|
||||
except Exception as e:
|
||||
print(f" {domain} homepage: ERROR {e}")
|
||||
all_pass = False
|
||||
|
||||
# robots.txt
|
||||
try:
|
||||
req = urllib.request.Request("https://" + domain + "/robots.txt", headers={"User-Agent": "Mozilla/5.0"})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
content = r.read().decode()
|
||||
checks = {
|
||||
"GPTBot": "GPTBot" in content,
|
||||
"ClaudeBot": "ClaudeBot" in content,
|
||||
"PerplexityBot": "PerplexityBot" in content,
|
||||
"Sitemap": "Sitemap:" in content,
|
||||
"Size>500": len(content) > 500,
|
||||
}
|
||||
status = "PASS" if all(checks.values()) else "FAIL"
|
||||
if not all(checks.values()):
|
||||
all_pass = False
|
||||
print(f" {domain} robots.txt: [{status}] {dict(checks)}")
|
||||
except Exception as e:
|
||||
print(f" {domain} robots.txt: ERROR {e}")
|
||||
all_pass = False
|
||||
|
||||
# Googlebot
|
||||
try:
|
||||
req = urllib.request.Request("https://gfil-lab.com/robots.txt", headers={"User-Agent": "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
content = r.read().decode()
|
||||
print(f" Googlebot -> gfil-lab.com/robots.txt: {r.status}, GPTBot={'GPTBot' in content}")
|
||||
except Exception as e:
|
||||
print(f" Googlebot check: ERROR {e}")
|
||||
all_pass = False
|
||||
|
||||
print()
|
||||
if all_pass:
|
||||
print("ALL CODE CHECKS PASSED")
|
||||
else:
|
||||
print("SOME CHECKS FAILED")
|
||||
|
||||
# === STEP 3: Human checklist ===
|
||||
print("\n" + "=" * 60)
|
||||
print("STEP 3: HUMAN OBSERVATION CHECKLIST")
|
||||
print("=" * 60)
|
||||
print()
|
||||
print("Please manually verify:")
|
||||
print(" 1. https://gfil-lab.com/ -> loads normally")
|
||||
print(" 2. https://gfil-lab.com/robots.txt -> shows FULL version with GPTBot/ClaudeBot/PerplexityBot")
|
||||
print(" 3. https://gfil-intel.xyz/robots.txt -> shows robots.txt")
|
||||
print(" 4. https://gfil-lab.com/login -> login page works")
|
||||
print()
|
||||
print("Rollback if needed:")
|
||||
print(" ssh root@216.144.233.14")
|
||||
print(" cp /etc/nginx/sites-enabled/default.bak.before-robots /etc/nginx/sites-enabled/default")
|
||||
print(" cp /etc/nginx/sites-enabled/gfil.bak.before-robots /etc/nginx/sites-enabled/gfil")
|
||||
print(" systemctl reload nginx")
|
||||
43
fix_sites_enabled.py
Normal file
43
fix_sites_enabled.py
Normal file
@ -0,0 +1,43 @@
|
||||
"""Remove broken backup file from sites-enabled, keep only clean gfil"""
|
||||
import paramiko
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
cmd = f"""sshpass -p '{LAB_PASS}' ssh -o StrictHostKeyChecking=no {LAB_USER}@{LAB_HOST} '
|
||||
# Remove the broken backup from sites-enabled (nginx loads ALL files in this dir)
|
||||
rm -f /etc/nginx/sites-enabled/gfil.bak.broken
|
||||
|
||||
# Verify only clean files remain
|
||||
echo "=== sites-enabled contents ==="
|
||||
ls -la /etc/nginx/sites-enabled/
|
||||
|
||||
# Show current gfil config
|
||||
echo "=== Current gfil config ==="
|
||||
cat /etc/nginx/sites-enabled/gfil
|
||||
|
||||
# Test
|
||||
nginx -t 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl reload nginx
|
||||
echo "SUCCESS: Nginx restored and reloaded"
|
||||
else
|
||||
echo "FAILED"
|
||||
fi
|
||||
|
||||
# Final check - site responding
|
||||
curl -s -o /dev/null -w "HTTP status" http://localhost/ 2>/dev/null
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(cmd, timeout=30)
|
||||
print(stdout.read().decode())
|
||||
jd.close()
|
||||
36
npm_package/README.md
Normal file
36
npm_package/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
# Trading Calculators
|
||||
|
||||
Free trading calculators for forex, gold, and crypto traders.
|
||||
|
||||
## Installation
|
||||
```bash
|
||||
npm install @liudecai-one/trading-calculators-free
|
||||
```
|
||||
|
||||
## Usage
|
||||
```js
|
||||
const { positionSize, pipValue, fibonacci } = require('@liudecai-one/trading-calculators-free');
|
||||
|
||||
// Position size: account=$10K, risk=1%, stop=20 pips
|
||||
const size = positionSize(10000, 1, 20);
|
||||
console.log({ size }); // 0.5 lots
|
||||
|
||||
// Fibonacci retracement levels
|
||||
const fib = fibonacci(1.1200, 1.1000);
|
||||
console.log({ fib });
|
||||
```
|
||||
|
||||
## Free Web Tools
|
||||
- [Position Size Calculator](https://blog.quant-view.xyz/tools/position-size-calculator.html)
|
||||
- [Pip Value Calculator](https://blog.quant-view.xyz/tools/pip-calculator.html)
|
||||
- [Margin Calculator](https://blog.quant-view.xyz/tools/margin-calculator.html)
|
||||
- [Fibonacci Calculator](https://blog.quant-view.xyz/tools/fibonacci-calculator.html)
|
||||
- [ATR Calculator](https://blog.quant-view.xyz/tools/atr-calculator.html)
|
||||
- [Risk-Reward Calculator](https://blog.quant-view.xyz/tools/risk-reward-calculator.html)
|
||||
- [Drawdown Calculator](https://blog.quant-view.xyz/tools/drawdown-calculator.html)
|
||||
|
||||
## More Resources
|
||||
- [Trading Blog](https://blog.quant-view.xyz)
|
||||
- [GFIL Terminal](http://gfil-intel.xyz/)
|
||||
- [Telegram Community](https://t.me/GFIL_Trading)
|
||||
- [Discord Server](https://discord.gg/GMmMCD4MCr)
|
||||
29
npm_package/index.js
Normal file
29
npm_package/index.js
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Free Trading Calculators
|
||||
* Web versions: https://blog.quant-view.xyz/tools/
|
||||
* Telegram: https://t.me/GFIL_Trading
|
||||
* Discord: https://discord.gg/GMmMCD4MCr
|
||||
*/
|
||||
|
||||
function positionSize(accountBalance, riskPercent, stopLossPips, pipValue = 10) {
|
||||
const riskAmount = accountBalance * (riskPercent / 100);
|
||||
return parseFloat((riskAmount / (stopLossPips * pipValue)).toFixed(2));
|
||||
}
|
||||
|
||||
function pipValue(currencyPair, lotSize = 1) {
|
||||
const rates = { "EURUSD": 10, "GBPUSD": 10, "USDJPY": 9.3, "XAUUSD": 10 };
|
||||
return rates[currencyPair] * lotSize || 10;
|
||||
}
|
||||
|
||||
function fibonacci(high, low) {
|
||||
const diff = high - low;
|
||||
return {
|
||||
"0.236": high - diff * 0.236,
|
||||
"0.382": high - diff * 0.382,
|
||||
"0.500": high - diff * 0.500,
|
||||
"0.618": high - diff * 0.618,
|
||||
"0.786": high - diff * 0.786,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { positionSize, pipValue, fibonacci };
|
||||
22
npm_package/package.json
Normal file
22
npm_package/package.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@liudecai-one/trading-calculators-free",
|
||||
"version": "1.0.0",
|
||||
"description": "Free trading calculators: position size, pip value, margin, Fibonacci, ATR. Web versions at blog.quant-view.xyz/tools/",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
"trading",
|
||||
"forex",
|
||||
"calculator",
|
||||
"position-size",
|
||||
"pip-calculator",
|
||||
"margin-calculator",
|
||||
"fibonacci",
|
||||
"atr"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://gitlab.com/liudecai110/gfil-trading-calculators.git"
|
||||
},
|
||||
"homepage": "https://blog.quant-view.xyz/tools/"
|
||||
}
|
||||
57
probe_nginx.py
Normal file
57
probe_nginx.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""Deep probe: find Nginx configs and deploy robots.txt correctly"""
|
||||
import paramiko
|
||||
import time
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
RN_HOST = "107.174.186.162"
|
||||
RN_USER = "root"
|
||||
RN_PASS = "liudapao2026"
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
# === gfil-lab.com (216.144.233.14) ===
|
||||
print("=== gfil-lab.com (216.144.233.14) ===")
|
||||
cmd = f"""sshpass -p '{LAB_PASS}' ssh -o StrictHostKeyChecking=no {LAB_USER}@{LAB_HOST} '
|
||||
echo "--- All Nginx configs ---"
|
||||
ls -la /etc/nginx/sites-enabled/ 2>/dev/null
|
||||
ls -la /etc/nginx/conf.d/ 2>/dev/null
|
||||
|
||||
echo "--- Full Nginx config ---"
|
||||
cat /etc/nginx/sites-enabled/default 2>/dev/null | head -50
|
||||
cat /etc/nginx/conf.d/default.conf 2>/dev/null | head -50
|
||||
|
||||
echo "--- Find gfil-lab server block ---"
|
||||
grep -rl "gfil-lab\\|server_name" /etc/nginx/ 2>/dev/null
|
||||
|
||||
echo "--- Nginx test ---"
|
||||
nginx -T 2>/dev/null | grep -A3 "server_name" | head -30
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(cmd, timeout=30)
|
||||
print(stdout.read().decode()[:3000])
|
||||
|
||||
# === gfil-intel.xyz (RackNerd) ===
|
||||
print("\n=== gfil-intel.xyz (RackNerd) ===")
|
||||
cmd2 = f"""sshpass -p '{RN_PASS}' ssh -o StrictHostKeyChecking=no {RN_USER}@{RN_HOST} '
|
||||
echo "--- gfil-intel Nginx config ---"
|
||||
cat /etc/nginx/sites-available/gfil-mask 2>/dev/null | head -60
|
||||
|
||||
echo "--- Sites enabled ---"
|
||||
ls -la /etc/nginx/sites-enabled/ 2>/dev/null
|
||||
|
||||
echo "--- Check if gfil-mask is enabled ---"
|
||||
ls -la /etc/nginx/sites-enabled/gfil-mask 2>/dev/null
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(cmd2, timeout=30)
|
||||
print(stdout.read().decode()[:3000])
|
||||
|
||||
jd.close()
|
||||
58
purge_and_verify.py
Normal file
58
purge_and_verify.py
Normal file
@ -0,0 +1,58 @@
|
||||
import urllib.request, json, sys, io, time
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
||||
|
||||
# Purge gfil-intel.xyz zone
|
||||
CF_TOKEN = "cfut_xRDcVVOTE9hLWxe8fFUG1Xgv7aQVtV0ytWFYZKimffcc10e3"
|
||||
ZONE_ID = "a107f45f3d82e7209447cec396504d0b"
|
||||
|
||||
headers = {
|
||||
"Authorization": "Bearer " + CF_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
purge_data = json.dumps({"files": ["https://gfil-intel.xyz/robots.txt"]}).encode("utf-8")
|
||||
req = urllib.request.Request(
|
||||
f"https://api.cloudflare.com/client/v4/zones/{ZONE_ID}/purge_cache",
|
||||
data=purge_data,
|
||||
headers=headers,
|
||||
method="POST"
|
||||
)
|
||||
r = urllib.request.urlopen(req, timeout=15)
|
||||
result = json.loads(r.read().decode())
|
||||
print("gfil-intel.xyz purge:", result.get("success", False))
|
||||
|
||||
# Also purge blog.quant-view.xyz
|
||||
ZONE_ID_BLOG = "3712ed1fcbd198c3ec6bd9758563a8d5"
|
||||
purge_data2 = json.dumps({"files": ["https://blog.quant-view.xyz/robots.txt"]}).encode("utf-8")
|
||||
req2 = urllib.request.Request(
|
||||
f"https://api.cloudflare.com/client/v4/zones/{ZONE_ID_BLOG}/purge_cache",
|
||||
data=purge_data2,
|
||||
headers=headers,
|
||||
method="POST"
|
||||
)
|
||||
r2 = urllib.request.urlopen(req2, timeout=15)
|
||||
result2 = json.loads(r2.read().decode())
|
||||
print("blog.quant-view.xyz purge:", result2.get("success", False))
|
||||
|
||||
# Wait
|
||||
print("\nWaiting 5 seconds...")
|
||||
time.sleep(5)
|
||||
|
||||
# Verify all 3 domains
|
||||
print("\n=== VERIFICATION ===")
|
||||
for domain in ["gfil-lab.com", "gfil-intel.xyz", "blog.quant-view.xyz"]:
|
||||
try:
|
||||
req = urllib.request.Request("https://" + domain + "/robots.txt",
|
||||
headers={"User-Agent": "Mozilla/5.0", "Cache-Control": "no-cache", "Pragma": "no-cache"})
|
||||
r = urllib.request.urlopen(req, timeout=10)
|
||||
content = r.read().decode()
|
||||
checks = {
|
||||
"GPTBot": "GPTBot" in content,
|
||||
"ClaudeBot": "ClaudeBot" in content,
|
||||
"PerplexityBot": "PerplexityBot" in content,
|
||||
"SizeOK": len(content) > 100
|
||||
}
|
||||
status = "PASS" if all(checks.values()) else "FAIL"
|
||||
print(f" [{status}] {domain}: {len(content)} chars | {checks}")
|
||||
except Exception as e:
|
||||
print(f" [FAIL] {domain}: {e}")
|
||||
85
purge_cf_cache.py
Normal file
85
purge_cf_cache.py
Normal file
@ -0,0 +1,85 @@
|
||||
"""Purge Cloudflare cache for robots.txt on gfil-lab.com"""
|
||||
import urllib.request, json, sys, io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
||||
|
||||
# gfil-lab.com Cloudflare zone
|
||||
CF_TOKEN = "cfut_xRDcVVOTE9hLWxe8fFUG1Xgv7aQVtV0ytWFYZKimffcc10e3"
|
||||
# Need zone ID for gfil-lab.com - get it first
|
||||
headers = {
|
||||
"Authorization": "Bearer " + CF_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
# List zones to find gfil-lab.com zone ID
|
||||
req = urllib.request.Request("https://api.cloudflare.com/client/v4/zones", headers=headers)
|
||||
r = urllib.request.urlopen(req, timeout=15)
|
||||
data = json.loads(r.read().decode())
|
||||
for z in data.get("result", []):
|
||||
print(f" Zone: {z['name']} -> ID: {z['id']}")
|
||||
|
||||
# Find gfil-lab.com zone
|
||||
zone_id = None
|
||||
for z in data.get("result", []):
|
||||
if z["name"] == "gfil-lab.com":
|
||||
zone_id = z["id"]
|
||||
break
|
||||
|
||||
if not zone_id:
|
||||
print("ERROR: gfil-lab.com zone not found")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"\nPurging cache for gfil-lab.com (zone: {zone_id})...")
|
||||
|
||||
# Purge specific URLs
|
||||
purge_data = json.dumps({
|
||||
"files": [
|
||||
"https://gfil-lab.com/robots.txt",
|
||||
"https://gfil-intel.xyz/robots.txt"
|
||||
]
|
||||
}).encode("utf-8")
|
||||
|
||||
req = urllib.request.Request(
|
||||
f"https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache",
|
||||
data=purge_data,
|
||||
headers=headers,
|
||||
method="POST"
|
||||
)
|
||||
r = urllib.request.urlopen(req, timeout=15)
|
||||
result = json.loads(r.read().decode())
|
||||
print(f" Purge result: {result.get('success', False)}")
|
||||
|
||||
# Also try gfil-intel.xyz zone
|
||||
zone_id2 = None
|
||||
for z in data.get("result", []):
|
||||
if z["name"] == "gfil-intel.xyz":
|
||||
zone_id2 = z["id"]
|
||||
break
|
||||
|
||||
if zone_id2:
|
||||
print(f"\nPurging cache for gfil-intel.xyz (zone: {zone_id2})...")
|
||||
purge_data2 = json.dumps({"files": ["https://gfil-intel.xyz/robots.txt"]}).encode("utf-8")
|
||||
req2 = urllib.request.Request(
|
||||
f"https://api.cloudflare.com/client/v4/zones/{zone_id2}/purge_cache",
|
||||
data=purge_data2,
|
||||
headers=headers,
|
||||
method="POST"
|
||||
)
|
||||
r2 = urllib.request.urlopen(req2, timeout=15)
|
||||
result2 = json.loads(r2.read().decode())
|
||||
print(f" Purge result: {result2.get('success', False)}")
|
||||
|
||||
# Wait and verify
|
||||
import time
|
||||
print("\nWaiting 3 seconds for cache purge...")
|
||||
time.sleep(3)
|
||||
|
||||
print("\n=== Verification after purge ===")
|
||||
import urllib.request as ur
|
||||
for domain in ["gfil-lab.com", "gfil-intel.xyz"]:
|
||||
try:
|
||||
req = ur.Request("https://" + domain + "/robots.txt", headers={"User-Agent": "Mozilla/5.0", "Cache-Control": "no-cache"})
|
||||
r = ur.urlopen(req, timeout=10)
|
||||
content = r.read().decode()
|
||||
print(f" {domain}: {r.status} | {len(content)} chars | GPTBot={'GPTBot' in content} | ClaudeBot={'ClaudeBot' in content}")
|
||||
except Exception as e:
|
||||
print(f" {domain}: {e}")
|
||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
paramiko>=3.0
|
||||
40
restore_nginx.py
Normal file
40
restore_nginx.py
Normal file
@ -0,0 +1,40 @@
|
||||
"""RESTORE: Copy clean config from sites-available/gfil"""
|
||||
import paramiko
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
# Check sites-available/gfil content first, then copy it over
|
||||
restore_cmd = f"""sshpass -p '{LAB_PASS}' ssh -o StrictHostKeyChecking=no {LAB_USER}@{LAB_HOST} '
|
||||
# Show the clean original
|
||||
echo "=== sites-available/gfil (original clean) ==="
|
||||
cat /etc/nginx/sites-available/gfil
|
||||
|
||||
# Copy clean version over the broken one
|
||||
cp /etc/nginx/sites-available/gfil /etc/nginx/sites-enabled/gfil
|
||||
|
||||
# Test
|
||||
nginx -t 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl reload nginx
|
||||
echo "RESTORED: Nginx reloaded with clean config"
|
||||
else
|
||||
echo "STILL FAILING after restore"
|
||||
fi
|
||||
|
||||
# Verify site is back
|
||||
curl -s -o /dev/null -w "%{{http_code}}" http://localhost/ 2>/dev/null
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(restore_cmd, timeout=30)
|
||||
print(stdout.read().decode())
|
||||
jd.close()
|
||||
42
revert_nginx.py
Normal file
42
revert_nginx.py
Normal file
@ -0,0 +1,42 @@
|
||||
"""REVERT: Remove broken robots.txt block from gfil-lab.com Nginx config"""
|
||||
import paramiko
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
# Remove ALL robots.txt related lines from the Nginx config
|
||||
revert_cmd = """sshpass -p 'Kt9V72Tx2c48ChikKU' ssh -o StrictHostKeyChecking=no root@216.144.233.14 '
|
||||
# Remove the broken "n" line
|
||||
sed -i "/^n location/d" /etc/nginx/sites-enabled/gfil
|
||||
# Remove the robots.txt location block (3 lines: open, alias, close)
|
||||
sed -i "/location = \/robots\.txt/,/}/d" /etc/nginx/sites-enabled/gfil
|
||||
# Remove any leftover empty lines
|
||||
sed -i "/^$/N;/^\\n$/d" /etc/nginx/sites-enabled/gfil
|
||||
|
||||
# Verify config is clean
|
||||
echo "=== Cleaned config ==="
|
||||
cat /etc/nginx/sites-enabled/gfil
|
||||
|
||||
# Test
|
||||
nginx -t 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl reload nginx
|
||||
echo "Nginx reloaded OK - config reverted"
|
||||
else
|
||||
echo "STILL FAILING - showing full config"
|
||||
cat -n /etc/nginx/sites-enabled/gfil
|
||||
fi
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(revert_cmd, timeout=30)
|
||||
print(stdout.read().decode())
|
||||
jd.close()
|
||||
38
revert_v2.py
Normal file
38
revert_v2.py
Normal file
@ -0,0 +1,38 @@
|
||||
"""REVERT v2: Remove leftover lines 13-15 from gfil Nginx config"""
|
||||
import paramiko
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
# Precisely remove the 3 leftover lines
|
||||
revert_cmd = r"""sshpass -p 'Kt9V72Tx2c48ChikKU' ssh -o StrictHostKeyChecking=no root@216.144.233.14 '
|
||||
# Remove the 3 leftover lines from robots.txt block
|
||||
sed -i "/alias \/var\/www\/gfil-lab\/robots\.txt/d" /etc/nginx/sites-enabled/gfil
|
||||
sed -i "/default_type text\/plain/d" /etc/nginx/sites-enabled/gfil
|
||||
# Remove the orphan closing brace on its own line after the comment line
|
||||
sed -i "/^ }$/d" /etc/nginx/sites-enabled/gfil
|
||||
|
||||
echo "=== Final config ==="
|
||||
cat -n /etc/nginx/sites-enabled/gfil
|
||||
|
||||
nginx -t 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl reload nginx
|
||||
echo "SUCCESS: Nginx reloaded, config clean"
|
||||
else
|
||||
echo "STILL FAILING"
|
||||
fi
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(revert_cmd, timeout=30)
|
||||
print(stdout.read().decode())
|
||||
jd.close()
|
||||
112
submit_indexnow.py
Normal file
112
submit_indexnow.py
Normal file
@ -0,0 +1,112 @@
|
||||
"""Submit new blog URLs to IndexNow (Bing, Yandex, Seznam)"""
|
||||
import urllib.request
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
SITE = "https://blog.quant-view.xyz"
|
||||
KEY = "72aa77b68704abcfada4020ba81f7c5a"
|
||||
|
||||
# New pages from the 54 variant generator
|
||||
new_pages = [
|
||||
# Formula pages (10)
|
||||
"tools/position-size-formula.html",
|
||||
"tools/pip-value-formula.html",
|
||||
"tools/kelly-criterion-formula.html",
|
||||
"tools/atr-formula.html",
|
||||
"tools/fibonacci-retracement-formula.html",
|
||||
"tools/drawdown-formula.html",
|
||||
"tools/margin-formula.html",
|
||||
"tools/risk-reward-formula.html",
|
||||
"tools/compound-interest-formula.html",
|
||||
"tools/profit-factor-formula.html",
|
||||
# Gold pages (6)
|
||||
"tools/gold-atr-calculator.html",
|
||||
"tools/gold-margin-calculator.html",
|
||||
"tools/gold-drawdown-calculator.html",
|
||||
"tools/xauusd-trading-guide.html",
|
||||
"tools/gold-lot-size-calculator.html",
|
||||
"tools/gold-spread-calculator.html",
|
||||
# Forex pages (5)
|
||||
"tools/forex-position-size-calculator.html",
|
||||
"tools/forex-pip-calculator.html",
|
||||
"tools/forex-margin-calculator.html",
|
||||
"tools/forex-risk-calculator.html",
|
||||
"tools/forex-lot-size-calculator.html",
|
||||
# Forex pair guides (3)
|
||||
"tools/eurusd-trading-guide.html",
|
||||
"tools/gbpusd-trading-guide.html",
|
||||
"tools/usdjpy-trading-guide.html",
|
||||
# Crypto pages (5)
|
||||
"tools/btc-position-size-calculator.html",
|
||||
"tools/crypto-pip-calculator.html",
|
||||
"tools/btc-margin-calculator.html",
|
||||
"tools/eth-position-size-calculator.html",
|
||||
"tools/crypto-risk-calculator.html",
|
||||
# Pip variants (9)
|
||||
"tools/pip-calculator-eurgbp.html",
|
||||
"tools/pip-calculator-usdchf.html",
|
||||
"tools/pip-calculator-solusd.html",
|
||||
"tools/pip-calculator-dogeusd.html",
|
||||
"tools/pip-calculator-adausd.html",
|
||||
"tools/pip-calculator-bnbusd.html",
|
||||
"tools/pip-calculator-dax40.html",
|
||||
"tools/pip-calculator-sp500.html",
|
||||
"tools/pip-calculator-ukoil.html",
|
||||
# Index pages (4)
|
||||
"tools/sp500-position-size-calculator.html",
|
||||
"tools/nas100-position-size-calculator.html",
|
||||
"tools/dax40-position-size-calculator.html",
|
||||
"tools/index-margin-calculator.html",
|
||||
# Educational (6)
|
||||
"tools/risk-management-guide.html",
|
||||
"tools/how-to-calculate-pip-value.html",
|
||||
"tools/how-to-use-atr-for-stop-loss.html",
|
||||
"tools/kelly-criterion-explained.html",
|
||||
"tools/drawdown-recovery-guide.html",
|
||||
"tools/margin-call-prevention.html",
|
||||
# Comparison (3)
|
||||
"tools/tradingview-vs-mt5.html",
|
||||
"tools/ctrader-vs-mt5.html",
|
||||
"tools/best-free-trading-tools.html",
|
||||
# Account size variants (3)
|
||||
"tools/position-size-calculator-100000-dollar-account.html",
|
||||
"tools/position-size-calculator-20000-dollar-account.html",
|
||||
"tools/position-size-calculator-3000-dollar-account.html",
|
||||
# New articles
|
||||
"position-size-calculator-guide.html",
|
||||
"gold-trading-2026-guide.html",
|
||||
"ssh-tunnel-deployment-china.html",
|
||||
"github-seo-trading-tools.html",
|
||||
"gold-pip-value-calculator-wrong.html",
|
||||
# Sitemap
|
||||
"sitemap.xml",
|
||||
]
|
||||
|
||||
urls = [f"{SITE}/{p}" for p in new_pages]
|
||||
print(f"Submitting {len(urls)} URLs to IndexNow...")
|
||||
|
||||
payload = json.dumps({
|
||||
"host": "blog.quant-view.xyz",
|
||||
"key": KEY,
|
||||
"keyLocation": f"{SITE}/{KEY}.txt",
|
||||
"urlList": urls
|
||||
}).encode("utf-8")
|
||||
|
||||
engines = [
|
||||
("Bing", "https://www.bing.com/indexnow"),
|
||||
("Yandex", "https://yandex.com/indexnow"),
|
||||
("Seznam", "https://search.seznam.cz/indexnow"),
|
||||
]
|
||||
|
||||
for name, endpoint in engines:
|
||||
try:
|
||||
req = urllib.request.Request(endpoint, data=payload, headers={"Content-Type": "application/json"})
|
||||
resp = urllib.request.urlopen(req, timeout=30)
|
||||
print(f" {name}: HTTP {resp.status}")
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f" {name}: HTTP {e.code} ({e.reason})")
|
||||
except Exception as e:
|
||||
print(f" {name}: Error - {e}")
|
||||
|
||||
print("Done!")
|
||||
46
verify_nginx.py
Normal file
46
verify_nginx.py
Normal file
@ -0,0 +1,46 @@
|
||||
"""Verify Nginx config: compare sites-enabled/gfil vs sites-available/gfil"""
|
||||
import paramiko
|
||||
|
||||
JD_HOST = "111.228.37.165"
|
||||
JD_USER = "root"
|
||||
JD_PASS = "Liudecai110"
|
||||
|
||||
LAB_HOST = "216.144.233.14"
|
||||
LAB_USER = "root"
|
||||
LAB_PASS = "Kt9V72Tx2c48ChikKU"
|
||||
|
||||
jd = paramiko.SSHClient()
|
||||
jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
jd.connect(JD_HOST, port=22, username=JD_USER, password=JD_PASS,
|
||||
timeout=20, banner_timeout=60, allow_agent=False, look_for_keys=False)
|
||||
|
||||
cmd = f"""sshpass -p '{LAB_PASS}' ssh -o StrictHostKeyChecking=no {LAB_USER}@{LAB_HOST} '
|
||||
echo "=== Diff: sites-enabled vs sites-available ==="
|
||||
diff /etc/nginx/sites-enabled/gfil /etc/nginx/sites-available/gfil && echo "IDENTICAL" || echo "DIFFERENT"
|
||||
|
||||
echo ""
|
||||
echo "=== sites-available/gfil (original, date) ==="
|
||||
ls -la /etc/nginx/sites-available/gfil
|
||||
stat /etc/nginx/sites-available/gfil | grep -i modify
|
||||
|
||||
echo ""
|
||||
echo "=== sites-enabled/gfil (current, date) ==="
|
||||
ls -la /etc/nginx/sites-enabled/gfil
|
||||
stat /etc/nginx/sites-enabled/gfil | grep -i modify
|
||||
|
||||
echo ""
|
||||
echo "=== Full current config ==="
|
||||
cat /etc/nginx/sites-enabled/gfil
|
||||
|
||||
echo ""
|
||||
echo "=== Nginx status ==="
|
||||
nginx -t 2>&1
|
||||
systemctl status nginx --no-pager | head -5
|
||||
|
||||
echo ""
|
||||
echo "=== Site response ==="
|
||||
curl -s -o /dev/null -w "HTTP_code:%{{http_code}} Size:%{{size_download}}" http://localhost/ 2>/dev/null
|
||||
'"""
|
||||
stdin, stdout, stderr = jd.exec_command(cmd, timeout=30)
|
||||
print(stdout.read().decode())
|
||||
jd.close()
|
||||
Reference in New Issue
Block a user