summarylogtreecommitdiffstats
path: root/spxc-web
blob: 835734465df50c7f6da044220f54babeddb00355 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/bin/bash
VERSION="V1.7.4"

case "$1" in
    -v)
        echo "$VERSION"
        ;;
    -crdts)
        echo "Creator: Spaceship"
        echo "Discord: https://discord.gg/uCDMuqfSeX"
        ;;
    -mkserv)
        DIR=$2
        PORT=$3
        if [ -z "$DIR" ] || [ -z "$PORT" ]; then echo "Usage: spxc -mkserv [directory] [port]"; exit 1; fi
        mkdir -p "$DIR"
        if [ -f "$DIR/index.html" ]; then
            read -p "index.html already exists. Override? (y/N): " choice
            case "$choice" in y|Y ) ;; * ) CREATE_INDEX=false ;; esac
        fi
        if [ "$CREATE_INDEX" != false ]; then
            cat > "$DIR/index.html" <<EOF
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Spxc Server Active</title>
    <style>
        :root { --bg: #0f172a; --card: #1e293b; --text: #f8fafc; --accent: #38bdf8; --live: #22c55e; }
        body { background: var(--bg); color: var(--text); font-family: 'Inter', sans-serif; margin: 0; display: flex; flex-direction: column; align-items: center; min-height: 100vh; justify-content: center; overflow-x: hidden; }
        .container { max-width: 900px; width: 95%; text-align: center; animation: fadeIn 0.8s ease-out; }
        .status-badge { background: var(--card); padding: 1.5rem 2.5rem; border-radius: 1.5rem; box-shadow: 0 10px 25px -5px rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.05); margin-bottom: 2rem; position: relative; }
        .live-indicator { display: flex; align-items: center; justify-content: center; gap: 0.5rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; font-size: 0.875rem; color: var(--live); margin-bottom: 0.5rem; }
        .dot { width: 8px; height: 8px; background: var(--live); border-radius: 50%; box-shadow: 0 0 10px var(--live); animation: pulse 1.5s infinite; }
        h1 { font-size: 2.5rem; margin: 0.5rem 0; background: linear-gradient(to right, #38bdf8, #818cf8); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
        .stats { display: flex; justify-content: center; gap: 2rem; margin-top: 1.5rem; font-size: 0.9rem; opacity: 0.8; }
        .stat-item span { color: var(--accent); font-weight: bold; display: block; font-size: 1.1rem; }
        .tips-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem; margin-top: 3rem; width: 100%; }
        .tip-card { background: rgba(30,41,59,0.5); padding: 1.25rem; border-radius: 1rem; border: 1px solid rgba(255,255,255,0.05); text-align: left; transition: transform 0.2s; }
        .tip-card:hover { transform: translateY(-5px); background: rgba(30,41,59,0.8); }
        .tip-card h3 { margin: 0 0 0.5rem 0; font-size: 1rem; color: var(--accent); }
        .tip-card p { margin: 0; font-size: 0.85rem; line-height: 1.5; opacity: 0.7; }
        .footer { margin-top: 4rem; font-size: 0.8rem; opacity: 0.4; letter-spacing: 0.025em; }
        @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
        @keyframes pulse { 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7); } 70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(34, 197, 94, 0); } 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); } }
    </style>
</head>
<body>
    <div class="container">
        <div class="status-badge">
            <div class="live-indicator"><div class="dot"></div> Running</div>
            <h1>Spxc Server Set Up Complete</h1>
            <div class="stats">
                <div class="stat-item">Started at <span id="start-time">--:--</span></div>
                <div class="stat-item">Uptime <span id="uptime">0s</span></div>
            </div>
        </div>

        <div class="tips-grid">
            <div class="tip-card"><h3>Pro Tip #1</h3><p>Use <code>spxc -sysfo</code> to quickly check your system's hardware specs from the terminal.</p></div>
            <div class="tip-card"><h3>Pro Tip #2</h3><p>Bundle any website into a single offline-ready HTML file using <code>spxc -dweb [url]</code>.</p></div>
            <div class="tip-card"><h3>Pro Tip #3</h3><p>Run <code>spxc -caupd</code> to perform a clean update and wipe your AUR cache in one go.</p></div>
            <div class="tip-card"><h3>Pro Tip #4</h3><p>Static servers started with <code>-mkserv</code> are perfect for testing local web projects instantly.</p></div>
            <div class="tip-card"><h3>Pro Tip #5</h3><p>The <code>spxc-webtools</code> extension adds powerful web capabilities to your core CLI toolkit.</p></div>
            <div class="tip-card"><h3>Pro Tip #6</h3><p>Check <code>spxc --help</code> anytime to discover new features and command usage.</p></div>
        </div>

        <div class="footer">Powered by spxc-webtools ${VERSION}</div>
    </div>

    <script>
        const serverStartTime = new Date("$(date -R)");
        document.getElementById('start-time').textContent = serverStartTime.toLocaleTimeString();
        
        setInterval(() => {
            const now = new Date();
            const diff = Math.floor((now - serverStartTime) / 1000);
            const h = Math.floor(diff / 3600);
            const m = Math.floor((diff % 3600) / 60);
            const s = diff % 60;
            document.getElementById('uptime').textContent = 
                (h > 0 ? h + 'h ' : '') + (m > 0 || h > 0 ? m + 'm ' : '') + s + 's';
        }, 1000);
    </script>
</body>
</html>
EOF
            echo "Created $DIR/index.html"
        fi
        echo "Starting server on port $PORT... (Ctrl+C to close)"
        cd "$DIR" && python3 -m http.server "$PORT"
        ;;
    -rmweb)
        DIR=$2
        if [ -z "$DIR" ] || [ ! -d "$DIR" ]; then echo "Usage: spxc -rmweb [directory]"; exit 1; fi
        mapfile -t files < <(find "$DIR" -maxdepth 1 -name "*.html")
        if [ ${#files[@]} -eq 0 ]; then echo "No HTML files found."; exit 0; fi
        echo "Deleting: ${files[@]}"
        read -p "Are you sure? (y/N): " choice
        case "$choice" in y|Y ) for f in "${files[@]}"; do rm "$f"; done; echo "Removed."; ;; * ) echo "Cancelled."; ;; esac
        ;;
    -dweb)
        DIR=$2; URL=$3; NAME=$4
        if [ -z "$DIR" ] || [ -z "$URL" ] || [ -z "$NAME" ]; then echo "Usage: spxc -dweb [directory] [URL] [name]"; exit 1; fi
        if [[ ! "$NAME" =~ \.html$ ]]; then NAME="$NAME.html"; fi
        mkdir -p "$DIR"; FILEPATH="$DIR/$NAME"
        if [ -f "$FILEPATH" ]; then read -p "Overwrite? (y/N): " choice; case "$choice" in y|Y ) ;; * ) exit 0 ;; esac; fi
        export SPXC_URL="$URL"
        export SPXC_FILEPATH="$FILEPATH"
        export SPXC_BASE_DOMAIN=$(python3 -c "import urllib.parse; print(urllib.parse.urlparse('$URL').netloc)")
        python3 - <<'EOF'
import urllib.request, urllib.parse, re, sys, os, json, base64
url, filepath, base_domain = os.environ['SPXC_URL'], os.environ['SPXC_FILEPATH'], os.environ['SPXC_BASE_DOMAIN']
headers = {'User-Agent': 'Mozilla/5.0'}
pages = {} 
def fetch_res(u, b=False):
    try:
        with urllib.request.urlopen(urllib.request.Request(u, headers=headers), timeout=15) as r:
            d = r.read(); return d if b else d.decode('utf-8', errors='ignore')
    except: return None
def crawl(u, d=0):
    p = urllib.parse.urlparse(u).path or "/index.html"
    if p in pages: return
    print(f" Crawling: {u}"); c = fetch_res(u)
    if not c: return
    c = re.sub(r'<link[^>]+rel=["\'](manifest|preload|prefetch|preconnect)["\'][^>]*>', '', c)
    c = re.sub(r'navigator\.serviceWorker\.register\(.*?\)', '', c)
    def assets(match):
        t, is_j = match.group(0), '<script' in match.group(0)
        m = re.search(('src' if is_j else 'href') + f'=["\']([^"\']+)["\']', t)
        if m:
            a_u = urllib.parse.urljoin(u, m.group(1)); data = fetch_res(a_u, True)
            if data:
                b64 = base64.b64encode(data).decode('utf-8')
                return f'<script>try{{eval(atob("{b64}"));}}catch(e){{}}</script>' if is_j else f'<style>@import url("data:text/css;base64,{b64}");</style>'
        return t
    c = re.sub(r'<link[^>]+rel=["\']stylesheet["\'][^>]*>', assets, c)
    c = re.sub(r'<script[^>]+src=["\'][^"\']+["\'][^>]*>.*?</script>', assets, c, flags=re.DOTALL)
    def links(match):
        h = match.group(1)
        if h.startswith(('#', 'j', 'd')): return match.group(0)
        l_u = urllib.parse.urljoin(u, h)
        if urllib.parse.urlparse(l_u).netloc == base_domain: return f'href="#!{urllib.parse.urlparse(l_u).path or "/index.html"}"'
        return match.group(0)
    c = re.sub(r'href=["\']([^"\']+)["\']', links, c)
    pages[p] = base64.b64encode(c.encode('utf-8')).decode('utf-8')
    if d < 1:
        for lp in re.findall(r'href=["\']#!([^"\']+)["\']', c):
            if lp not in pages and not lp.endswith(('.png', '.jpg', '.ico', '.pdf')): crawl(urllib.parse.urljoin(url, lp), d + 1)
crawl(url)
router = f"<script>(function b(){{window.onerror=()=>true;const p={json.dumps(pages)};function l(path){{const b=p[path]||p[path+'.html']||p[path+'/index.html'];if(b){{document.open();document.write(atob(b)+'<script>('+b.toString()+')()<\/script>');document.close();}}}}window.onhashchange=()=>{{if(window.location.hash.startsWith('#!'))l(window.location.hash.substring(2));}};window.fetch=()=>Promise.reject();if(window.location.hash.startsWith('#!'))l(window.location.hash.substring(2));}})();<\/script>"
with open(filepath, "w", encoding='utf-8') as f: f.write(base64.b64decode(pages[urllib.parse.urlparse(url).path or "/index.html"]).decode('utf-8') + router)
EOF
        ;;
    *) exit 1 ;;
esac