スキル一覧に戻る
Pamir-AI

port-proxy

by Pamir-AI

1🍴 1📅 2025年11月30日
GitHubで見るManusで実行

SKILL.md


name: port-proxy description: This skill should be used when users want to expose local web applications publicly through the Distiller platform's built-in reverse proxy, or when fixing path issues (CSS/JS not loading, API 404s) caused by absolute paths behind reverse proxies. Use when users mention making apps public, proxy errors, or MIME type issues.

Distiller Reverse Proxy

Overview

The Distiller platform provides a built-in reverse proxy capability that exposes local web applications to the internet via HTTPS without requiring tunnel services, port forwarding, or proxy configuration. This skill addresses the common issue of absolute paths breaking behind reverse proxies and provides tools to fix these issues automatically.

ℹ️ You might also see frpc/frps mentioned elsewhere. Those tunnels are optional: the Distiller proxy works entirely on-device. Use the FRP context in this doc only if your deployment already relies on /etc/frp/frpc.toml to advertise a hostname such as test.devices.pamir.ai.

Key Pattern:

Local App (port 5000) → Distiller Proxy → Public HTTPS URL
http://localhost:5000  → https://subdomain.devices.pamir.ai/distiller/proxy/5000/

When to Use This Skill

Use this skill when users:

  • Want to make a local web app publicly accessible
  • Report CSS or JavaScript not loading (MIME type errors)
  • Experience API calls returning 404 errors
  • Ask about exposing apps through the Distiller proxy
  • Mention reverse proxy path issues

Quick Start

Expose Any App in 3 Steps

Step 1: Run the app locally

# Any web server on any port
python app.py              # Flask on 5000
npm run dev                # Vite on 3000
python -m http.server 8080 # HTTP server on 8080

Step 2: Access via proxy URL

https://{subdomain}.devices.pamir.ai/distiller/proxy/{PORT}/

Where do I get the subdomain?

  • If you’re using the Distiller proxy only, the Devices dashboard shows the assigned subdomain.
  • If you’re also tunneling with frpc, you can inspect /etc/frp/frpc.toml:
    cat /etc/frp/frpc.toml | grep subdomain
    # Example output: subdomain = "test"
    # Your URL: https://test.devices.pamir.ai/distiller/proxy/5000/
    

Step 3: Fix path issues if needed

# Check for issues
./scripts/check-paths.sh /path/to/app

# Auto-fix common patterns
./scripts/fix-paths.sh /path/to/app

The Absolute vs Relative Path Problem

Root cause: Absolute paths (starting with /) resolve to domain root, breaking when behind reverse proxies.

Symptoms:

  • CSS not loading: "MIME type 'application/json' is not a supported stylesheet"
  • JavaScript 404 errors
  • API calls fail with 404
  • Images don't load

Examples:

Broken (absolute paths):

<link rel="stylesheet" href="/styles.css">
<script src="/main.js"></script>

Resolves to: https://domain.com/styles.css (wrong - not behind proxy path)

Working (relative paths):

<link rel="stylesheet" href="styles.css">
<script src="main.js"></script>

Resolves to: https://domain.com/distiller/proxy/5000/styles.css (correct)

Fixing Path Issues

Use the provided script to automatically fix common patterns:

# Dry run (preview changes)
./scripts/fix-paths.sh /path/to/app --dry-run

# Apply fixes
./scripts/fix-paths.sh /path/to/app

The script fixes:

  • HTML: href="/..."href="..."
  • HTML: src="/..."src="..."
  • JavaScript: API_BASE = '/api'API_BASE = 'api'
  • JavaScript: fetch('/api/...')fetch('api/...')

⚠️ The fixer is intentionally conservative but still performs in-place edits. Run with --dry-run first, keep backups (e.g., via git), and review the diff—projects sometimes rely on intentional absolute URLs.

Manual Fix

HTML Files:

<!-- BEFORE -->
<link rel="stylesheet" href="/styles.css">
<script src="/main.js"></script>
<img src="/logo.png">

<!-- AFTER -->
<link rel="stylesheet" href="styles.css">
<script src="main.js"></script>
<img src="logo.png">

JavaScript Files:

// BEFORE
const API_BASE = '/api';
fetch('/api/data');

// AFTER
const API_BASE = 'api';
fetch('api/data');

Exception: Keep absolute paths for external resources:

<!-- These are fine (external URLs) -->
<script src="https://cdn.example.com/library.js"></script>

Serving Under a Base Path (e.g., /watchdog)

Sometimes you must keep the proxy path segment (such as /distiller/proxy/5000/watchdog) instead of stripping the /. The pattern below lets your app live under any prefix without code rewrites each time.

  1. Server-side base path variable

    # systemd / shell
    export BASE_PATH=/watchdog
    
    // Express example
    const basePath = (process.env.BASE_PATH || '/').replace(/\/$/, '');
    app.use(`${basePath}/static`, express.static('public'));
    app.use(basePath, router);
    app.get(`${basePath}/health`, handler);
    
  2. Inject the base path into rendered HTML

    <script>window.BASE_PATH = "{{ basePath }}";</script>
    <link rel="stylesheet" href="{{ basePath }}/static/dashboard.css">
    <script src="{{ basePath }}/static/app.js"></script>
    
  3. Teach the front-end to respect it

    const BASE = window.BASE_PATH || '';
    await fetch(`${BASE}/api/status`);
    

With those three pieces, requests to https://subdomain.devices.pamir.ai/distiller/proxy/5000/watchdog/… stay scoped to /watchdog on both server and client, preventing 404s while still allowing the app to run at / locally.

Framework-Specific Guides

Flask (Python)

Works well by default. Only fix HTML templates.

# No changes needed to Flask code
app = Flask(__name__, static_folder='static')

@app.route('/api/data')
def data():
    return jsonify({'status': 'ok'})

Fix: Change paths in HTML templates from absolute to relative.

Vite (JavaScript)

Add base path configuration:

// vite.config.js
export default defineConfig({
  base: './', // Use relative base path
  server: {
    host: '0.0.0.0',
    port: 3000
  }
})

Create React App

Update package.json:

{
  "homepage": "."
}

Express (Node.js)

const express = require('express');
const app = express();

app.use(express.static('public'));

app.get('/api/data', (req, res) => {
  res.json({ status: 'ok' });
});

app.listen(5000, '0.0.0.0');

Fix: HTML files to use relative paths.

Common Issues

CSS loads but styles not applied

Cause: MIME type mismatch

Solution:

  1. Check browser console for error
  2. Verify path is relative: href="styles.css" not href="/styles.css"

API calls return HTML instead of JSON

Cause: Flask returning index.html for missing routes

Solution:

# Make sure API routes come BEFORE catch-all
@app.route('/api/data')
def data():
    return jsonify({'status': 'ok'})

# This should be last
@app.route('/<path:path>')
def serve_static(path):
    return send_from_directory(app.static_folder, path)

Page loads but refresh gives 404

Cause: Client-side routing needs server fallback

Solution:

@app.errorhandler(404)
def not_found(e):
    # For SPA with client-side routing
    if request.path.startswith('/api/'):
        return jsonify(error='Not found'), 404
    return send_from_directory(app.static_folder, 'index.html')

POST requests timeout or return 404

Cause: Long-running operations (>30s) don't work well through proxy

Symptoms:

  • POST requests hang/timeout after 30-60 seconds
  • Request completes on server but browser never gets response
  • Works locally but fails through proxy

Solution: Use background tasks with immediate response

import threading

def run_long_task():
    """Background task"""
    # Do expensive work here
    time.sleep(60)
    # Save results to file/database

@app.route('/api/process', methods=['POST'])
def process_data():
    # Start task in background
    thread = threading.Thread(target=run_long_task, daemon=True)
    thread.start()

    # Return immediately
    return jsonify({
        'success': True,
        'message': 'Processing started. Check back in 1-2 minutes.'
    })

# Frontend polls for updates
// JavaScript
async function startProcess() {
    const res = await fetch('./api/process', { method: 'POST' });
    const data = await res.json();

    // Poll every 10 seconds for updates
    const interval = setInterval(async () => {
        const status = await fetch('./api/status');
        const result = await status.json();
        if (result.complete) {
            clearInterval(interval);
            // Update UI with results
        }
    }, 10000);
}

Why: The proxy can't maintain connections for long-running synchronous requests. Background tasks + polling is the standard pattern for web apps.

Best Practices

  1. Use relative paths everywhere

    • href="style.css"
    • href="/style.css"
  2. Namespace API routes

    • /api/users, /api/data
    • /users (conflicts with static files)
  3. Support a base path

    • Use env vars/config to mount your router under /watchdog (or similar) when the proxy requires it.
    • Render BASE_PATH into templates and prepend it inside JavaScript fetch calls.
  4. Test locally first

    • Test on http://localhost:5000/
    • Then test through proxy
  5. Document the public URL

    Public: https://subdomain.devices.pamir.ai/distiller/proxy/5000/
    Local: http://localhost:5000/
    

Port Management

Check what's running

# See all listening ports
netstat -tuln | grep LISTEN

# Check specific port
lsof -i :5000

Common port assignments

PortTypical Use
5000Flask apps
3000Vite/React dev servers
8000Django apps
8080Generic web servers

Kill process on port

lsof -i :5000
kill -9 {PID}

# Or one-liner
pkill -f "python app.py"

Resources

scripts/

check-paths.sh - Scans project files for absolute path issues and reports potential problems (uses ripgrep when available, falls back to grep). Accepts a project root and reports findings without modifying files.

fix-paths.sh - Automatically fixes common absolute path patterns in HTML and JavaScript files. Supports --dry-run to preview changes; always review the diff afterwards if you run it for real.

Usage examples are shown in the "Fixing Path Issues" section above.

Quick Reference

┌─────────────────────────────────────────────────┐
│ Distiller Proxy Quick Reference                │
├─────────────────────────────────────────────────┤
│ URL Pattern:                                    │
│ https://{subdomain}.devices.pamir.ai/          │
│        distiller/proxy/{PORT}/                  │
│                                                 │
│ Fix Checklist:                                  │
│ □ Remove leading / from href/src               │
│ □ Change API calls to relative paths           │
│ □ Test locally first                           │
│ □ Hard refresh browser (Ctrl+Shift+R)          │
│                                                 │
│ Common Fixes:                                   │
│ href="/style.css"  → href="style.css"          │
│ src="/main.js"     → src="main.js"             │
│ fetch('/api/...')  → fetch('api/...')          │
└─────────────────────────────────────────────────┘

スコア

総合スコア

50/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です