initial commit
This commit is contained in:
commit
1f25033d2d
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# docs.pyserve.org
|
||||||
|
|
||||||
|
This repository contains the source files for the documentation of the PyServe project, which can be found at [docs.pyserve.org](https://docs.pyserve.org).
|
||||||
44
getting-started/index.html
Normal file
44
getting-started/index.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Getting Started - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » Getting Started
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>Getting Started</h2>
|
||||||
|
|
||||||
|
<p>Get up and running with pyserve quickly.</p>
|
||||||
|
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="installation.html">Installation</a></td>
|
||||||
|
<td class="desc">Download and install pyserve</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="quickstart.html">Quick Start</a></td>
|
||||||
|
<td class="desc">Get up and running in 5 minutes</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
79
getting-started/installation.html
Normal file
79
getting-started/installation.html
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Installation - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » <a href="/getting-started/">Getting Started</a> » Installation
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>Installation</h2>
|
||||||
|
|
||||||
|
<h3>Requirements</h3>
|
||||||
|
<ul class="indent">
|
||||||
|
<li>Python 3.12 or higher</li>
|
||||||
|
<li>pip (Python package manager)</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Install from Release (Recommended)</h3>
|
||||||
|
<p>Download the latest wheel file from <a href="https://github.com/ShiftyX1/PyServe/releases">GitHub Releases</a> and install it:</p>
|
||||||
|
|
||||||
|
<pre><span class="comment"># Download the wheel file from releases</span>
|
||||||
|
<span class="comment"># Example: pyserve-0.7.0-py3-none-any.whl</span>
|
||||||
|
|
||||||
|
pip install pyserve-0.7.0-py3-none-any.whl</pre>
|
||||||
|
|
||||||
|
<p>After installation, the <code>pyserve</code> command will be available in your terminal:</p>
|
||||||
|
<pre>pyserve --version</pre>
|
||||||
|
|
||||||
|
<h3>Install from Source</h3>
|
||||||
|
<p>For development or if you want the latest changes:</p>
|
||||||
|
|
||||||
|
<pre><span class="comment"># Clone the repository</span>
|
||||||
|
git clone https://github.com/ShiftyX1/PyServe.git
|
||||||
|
cd PyServe
|
||||||
|
|
||||||
|
<span class="comment"># Install with Poetry (recommended for development)</span>
|
||||||
|
make init
|
||||||
|
|
||||||
|
<span class="comment"># Or build and install the package</span>
|
||||||
|
make build
|
||||||
|
pip install dist/pyserve-*.whl</pre>
|
||||||
|
|
||||||
|
<h3>Verify Installation</h3>
|
||||||
|
<p>Check that pyserve is installed correctly:</p>
|
||||||
|
<pre>pyserve --version
|
||||||
|
<span class="comment"># Output: pyserve 0.7.0</span></pre>
|
||||||
|
|
||||||
|
<h3>Dependencies</h3>
|
||||||
|
<p>pyserve automatically installs the following dependencies:</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><code>starlette</code> — ASGI framework</li>
|
||||||
|
<li><code>uvicorn</code> — ASGI server</li>
|
||||||
|
<li><code>pyyaml</code> — YAML configuration parsing</li>
|
||||||
|
<li><code>structlog</code> — Structured logging</li>
|
||||||
|
<li><code>httpx</code> — HTTP client for reverse proxy</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>Next:</strong> Continue to <a href="quickstart.html">Quick Start</a> to run your first server.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
121
getting-started/quickstart.html
Normal file
121
getting-started/quickstart.html
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Quick Start - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » <a href="/getting-started/">Getting Started</a> » Quick Start
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>Quick Start</h2>
|
||||||
|
|
||||||
|
<p>Get pyserve running in under 5 minutes.</p>
|
||||||
|
|
||||||
|
<h3>1. Create Configuration File</h3>
|
||||||
|
<p>Create a file named <code>config.yaml</code> in your project directory:</p>
|
||||||
|
|
||||||
|
<pre><span class="directive">http:</span>
|
||||||
|
<span class="directive">static_dir:</span> <span class="value">./static</span>
|
||||||
|
<span class="directive">templates_dir:</span> <span class="value">./templates</span>
|
||||||
|
|
||||||
|
<span class="directive">server:</span>
|
||||||
|
<span class="directive">host:</span> <span class="value">0.0.0.0</span>
|
||||||
|
<span class="directive">port:</span> <span class="value">8080</span>
|
||||||
|
|
||||||
|
<span class="directive">logging:</span>
|
||||||
|
<span class="directive">level:</span> <span class="value">INFO</span>
|
||||||
|
<span class="directive">console_output:</span> <span class="value">true</span>
|
||||||
|
|
||||||
|
<span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">routing</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">regex_locations:</span>
|
||||||
|
<span class="value">"__default__"</span>:
|
||||||
|
<span class="directive">root:</span> <span class="value">"./static"</span>
|
||||||
|
<span class="directive">index_file:</span> <span class="value">"index.html"</span></pre>
|
||||||
|
|
||||||
|
<h3>2. Create Static Directory</h3>
|
||||||
|
<p>Create a <code>static</code> folder and add an <code>index.html</code>:</p>
|
||||||
|
|
||||||
|
<pre>mkdir -p static
|
||||||
|
echo '<h1>Hello from pyserve!</h1>' > static/index.html</pre>
|
||||||
|
|
||||||
|
<h3>3. Start the Server</h3>
|
||||||
|
<pre>pyserve</pre>
|
||||||
|
|
||||||
|
<p>You should see output like:</p>
|
||||||
|
<pre>Starting PyServe server on 0.0.0.0:8080</pre>
|
||||||
|
|
||||||
|
<h3>4. Open in Browser</h3>
|
||||||
|
<p>Navigate to <a href="http://localhost:8080">http://localhost:8080</a> — you should see your page!</p>
|
||||||
|
|
||||||
|
<h3>Using CLI Options</h3>
|
||||||
|
<p>Override configuration via command line:</p>
|
||||||
|
|
||||||
|
<pre><span class="comment"># Use a different config file</span>
|
||||||
|
pyserve -c /path/to/config.yaml
|
||||||
|
|
||||||
|
<span class="comment"># Override host and port</span>
|
||||||
|
pyserve --host 127.0.0.1 --port 9000
|
||||||
|
|
||||||
|
<span class="comment"># Enable debug mode (verbose logging)</span>
|
||||||
|
pyserve --debug</pre>
|
||||||
|
|
||||||
|
<h3>Example: Serve Documentation</h3>
|
||||||
|
<p>Serve a documentation directory with proper caching:</p>
|
||||||
|
|
||||||
|
<pre><span class="directive">http:</span>
|
||||||
|
<span class="directive">static_dir:</span> <span class="value">./docs</span>
|
||||||
|
|
||||||
|
<span class="directive">server:</span>
|
||||||
|
<span class="directive">host:</span> <span class="value">0.0.0.0</span>
|
||||||
|
<span class="directive">port:</span> <span class="value">8000</span>
|
||||||
|
|
||||||
|
<span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">routing</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">regex_locations:</span>
|
||||||
|
<span class="value">"=/"</span>:
|
||||||
|
<span class="directive">root:</span> <span class="value">"./docs"</span>
|
||||||
|
<span class="directive">index_file:</span> <span class="value">"index.html"</span>
|
||||||
|
|
||||||
|
<span class="value">"~*\\.(css|js)$"</span>:
|
||||||
|
<span class="directive">root:</span> <span class="value">"./docs"</span>
|
||||||
|
<span class="directive">cache_control:</span> <span class="value">"public, max-age=3600"</span>
|
||||||
|
|
||||||
|
<span class="value">"~*\\.html$"</span>:
|
||||||
|
<span class="directive">root:</span> <span class="value">"./docs"</span>
|
||||||
|
<span class="directive">cache_control:</span> <span class="value">"no-cache"</span>
|
||||||
|
|
||||||
|
<span class="value">"__default__"</span>:
|
||||||
|
<span class="directive">root:</span> <span class="value">"./docs"</span>
|
||||||
|
<span class="directive">index_file:</span> <span class="value">"index.html"</span></pre>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>Next Steps:</strong>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><a href="../guides/process-orchestration.html">Process Orchestration</a> — Run apps in isolated processes</li>
|
||||||
|
<li><a href="../guides/configuration.html">Configuration Guide</a> — Full configuration reference</li>
|
||||||
|
<li><a href="../guides/routing.html">Routing Guide</a> — nginx-style URL patterns</li>
|
||||||
|
<li><a href="../guides/reverse-proxy.html">Reverse Proxy</a> — Proxy to backend services</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
269
guides/asgi-mount.html
Normal file
269
guides/asgi-mount.html
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>ASGI Mounting - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » <a href="index.html">Guides</a> » ASGI Mounting
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>ASGI Application Mounting (In-Process)</h2>
|
||||||
|
|
||||||
|
<p>The <code>asgi</code> extension mounts ASGI and WSGI applications directly in the pyserve process.
|
||||||
|
This is simpler and has lower latency, but all apps share the same process.</p>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>For production use cases requiring isolation, consider
|
||||||
|
<a href="process-orchestration.html">Process Orchestration</a></strong> which runs each app
|
||||||
|
in a separate subprocess with health monitoring and auto-restart.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Overview</h3>
|
||||||
|
<p>The ASGI mounting system provides:</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><strong>Multi-framework support</strong> — Mount FastAPI, Flask, Django, Starlette, or custom ASGI apps</li>
|
||||||
|
<li><strong>Path-based routing</strong> — Each app handles requests at its mounted path</li>
|
||||||
|
<li><strong>WSGI compatibility</strong> — Automatic WSGI-to-ASGI conversion for Flask/Django</li>
|
||||||
|
<li><strong>Factory pattern support</strong> — Create apps dynamically with arguments</li>
|
||||||
|
<li><strong>Path stripping</strong> — Optionally strip mount path from requests</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Configuration</h3>
|
||||||
|
<p>ASGI applications are mounted via the <code>asgi</code> extension:</p>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">asgi</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">mounts:</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">"/api"</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">"myapp.api:app"</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">asgi</span>
|
||||||
|
<span class="directive">name:</span> <span class="value">"api-app"</span>
|
||||||
|
<span class="directive">strip_path:</span> <span class="value">true</span></pre>
|
||||||
|
|
||||||
|
<h3>Mount Configuration Options</h3>
|
||||||
|
<dl>
|
||||||
|
<dt>path</dt>
|
||||||
|
<dd>URL path where the application will be mounted. Example: <code>/api</code></dd>
|
||||||
|
|
||||||
|
<dt>app_path</dt>
|
||||||
|
<dd>Python import path to the application. Format: <code>module.submodule:attribute</code></dd>
|
||||||
|
|
||||||
|
<dt>app_type</dt>
|
||||||
|
<dd>Application type: <code>asgi</code> or <code>wsgi</code>. Default: <code>asgi</code></dd>
|
||||||
|
|
||||||
|
<dt>module_path</dt>
|
||||||
|
<dd>Optional path to add to <code>sys.path</code> for module resolution</dd>
|
||||||
|
|
||||||
|
<dt>factory</dt>
|
||||||
|
<dd>If <code>true</code>, <code>app_path</code> points to a factory function. Default: <code>false</code></dd>
|
||||||
|
|
||||||
|
<dt>factory_args</dt>
|
||||||
|
<dd>Dictionary of arguments to pass to the factory function</dd>
|
||||||
|
|
||||||
|
<dt>name</dt>
|
||||||
|
<dd>Friendly name for logging. Default: uses <code>app_path</code></dd>
|
||||||
|
|
||||||
|
<dt>strip_path</dt>
|
||||||
|
<dd>Remove mount path from request URL. Default: <code>true</code></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Mounting FastAPI</h3>
|
||||||
|
<p>FastAPI applications are native ASGI:</p>
|
||||||
|
|
||||||
|
<pre><span class="comment"># myapp/api.py</span>
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
@app.get("/users")
|
||||||
|
async def get_users():
|
||||||
|
return [{"id": 1, "name": "Alice"}]</pre>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">asgi</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">mounts:</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">"/api"</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">"myapp.api:app"</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">asgi</span>
|
||||||
|
<span class="directive">name:</span> <span class="value">"fastapi-app"</span></pre>
|
||||||
|
|
||||||
|
<p>With this configuration:</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><code>GET /api/users</code> → handled by FastAPI as <code>GET /users</code></li>
|
||||||
|
<li>FastAPI docs available at <code>/api/docs</code></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Mounting Flask</h3>
|
||||||
|
<p>Flask applications are WSGI and will be automatically wrapped:</p>
|
||||||
|
|
||||||
|
<pre><span class="comment"># myapp/flask_api.py</span>
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@app.route("/hello")
|
||||||
|
def hello():
|
||||||
|
return {"message": "Hello from Flask!"}</pre>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">asgi</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">mounts:</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">"/flask"</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">"myapp.flask_api:app"</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">wsgi</span>
|
||||||
|
<span class="directive">name:</span> <span class="value">"flask-app"</span></pre>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>Note:</strong> WSGI wrapping requires either <code>a2wsgi</code> or <code>asgiref</code>
|
||||||
|
to be installed. Install with: <code>pip install a2wsgi</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Mounting Django</h3>
|
||||||
|
<p>Django can be mounted using its ASGI application:</p>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">asgi</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">mounts:</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">"/django"</span>
|
||||||
|
<span class="directive">django_settings:</span> <span class="value">"myproject.settings"</span>
|
||||||
|
<span class="directive">module_path:</span> <span class="value">"/path/to/django/project"</span>
|
||||||
|
<span class="directive">name:</span> <span class="value">"django-app"</span></pre>
|
||||||
|
|
||||||
|
<h3>Factory Pattern</h3>
|
||||||
|
<p>Use factory functions to create apps with custom configuration:</p>
|
||||||
|
|
||||||
|
<pre><span class="comment"># myapp/api.py</span>
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
def create_app(debug: bool = False, prefix: str = "/v1") -> FastAPI:
|
||||||
|
app = FastAPI(debug=debug)
|
||||||
|
|
||||||
|
@app.get(f"{prefix}/status")
|
||||||
|
async def status():
|
||||||
|
return {"debug": debug}
|
||||||
|
|
||||||
|
return app</pre>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">asgi</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">mounts:</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">"/api"</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">"myapp.api:create_app"</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">asgi</span>
|
||||||
|
<span class="directive">factory:</span> <span class="value">true</span>
|
||||||
|
<span class="directive">factory_args:</span>
|
||||||
|
<span class="directive">debug:</span> <span class="value">true</span>
|
||||||
|
<span class="directive">prefix:</span> <span class="value">"/v2"</span></pre>
|
||||||
|
|
||||||
|
<h3>Path Stripping</h3>
|
||||||
|
<p>By default, <code>strip_path: true</code> removes the mount prefix from requests:</p>
|
||||||
|
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td>Request</td>
|
||||||
|
<td><code>strip_path: true</code></td>
|
||||||
|
<td><code>strip_path: false</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>GET /api/users</code></td>
|
||||||
|
<td>App sees <code>/users</code></td>
|
||||||
|
<td>App sees <code>/api/users</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>GET /api/</code></td>
|
||||||
|
<td>App sees <code>/</code></td>
|
||||||
|
<td>App sees <code>/api/</code></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Multiple Mounts</h3>
|
||||||
|
<p>Mount multiple applications at different paths:</p>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">asgi</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">mounts:</span>
|
||||||
|
<span class="comment"># FastAPI for REST API</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">"/api"</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">"apps.api:app"</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">asgi</span>
|
||||||
|
|
||||||
|
<span class="comment"># Flask admin panel</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">"/admin"</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">"apps.admin:app"</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">wsgi</span>
|
||||||
|
|
||||||
|
<span class="comment"># Starlette websocket handler</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">"/ws"</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">"apps.websocket:app"</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">asgi</span>
|
||||||
|
|
||||||
|
<span class="comment"># Standard routing for static files</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">routing</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">regex_locations:</span>
|
||||||
|
<span class="value">"__default__"</span>:
|
||||||
|
<span class="directive">root:</span> <span class="value">"./static"</span></pre>
|
||||||
|
|
||||||
|
<h3>Mount Priority</h3>
|
||||||
|
<p>Mounts are matched by path length (longest first). Given mounts at
|
||||||
|
<code>/api</code> and <code>/api/v2</code>:</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><code>/api/v2/users</code> → matches <code>/api/v2</code> mount</li>
|
||||||
|
<li><code>/api/users</code> → matches <code>/api</code> mount</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Combining with Routing</h3>
|
||||||
|
<p>ASGI mounts work alongside the routing extension. The <code>asgi</code> extension
|
||||||
|
should be listed before <code>routing</code> to handle mounted paths first:</p>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
<span class="comment"># ASGI apps handle /api/* and /admin/*</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">asgi</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">mounts:</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">"/api"</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">"myapp:api"</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">asgi</span>
|
||||||
|
|
||||||
|
<span class="comment"># Routing handles everything else</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">routing</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">regex_locations:</span>
|
||||||
|
<span class="value">"=/health"</span>:
|
||||||
|
<span class="directive">return:</span> <span class="value">"200 OK"</span>
|
||||||
|
<span class="value">"__default__"</span>:
|
||||||
|
<span class="directive">spa_fallback:</span> <span class="value">true</span>
|
||||||
|
<span class="directive">root:</span> <span class="value">"./dist"</span></pre>
|
||||||
|
|
||||||
|
<h3>Python API</h3>
|
||||||
|
<p>For programmatic mounting, see <a href="../reference/asgi-mount.html">ASGI Mount API Reference</a>.</p>
|
||||||
|
|
||||||
|
<div class="warning">
|
||||||
|
<strong>Warning:</strong> Mounted applications share the same process.
|
||||||
|
Ensure your applications are compatible and don't have conflicting global state.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
179
guides/configuration.html
Normal file
179
guides/configuration.html
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Configuration - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » <a href="/guides/">Guides</a> » Configuration
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>Configuration Reference</h2>
|
||||||
|
|
||||||
|
<p>pyserve uses YAML configuration files. By default, it looks for
|
||||||
|
<code>config.yaml</code> in the current directory.</p>
|
||||||
|
|
||||||
|
<h3>http</h3>
|
||||||
|
<p>HTTP-related paths configuration.</p>
|
||||||
|
<dl>
|
||||||
|
<dt>static_dir</dt>
|
||||||
|
<dd>Path to static files directory. Default: <code>./static</code></dd>
|
||||||
|
|
||||||
|
<dt>templates_dir</dt>
|
||||||
|
<dd>Path to templates directory. Default: <code>./templates</code></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>server</h3>
|
||||||
|
<p>Core server settings.</p>
|
||||||
|
<dl>
|
||||||
|
<dt>host</dt>
|
||||||
|
<dd>Bind address. Default: <code>0.0.0.0</code></dd>
|
||||||
|
|
||||||
|
<dt>port</dt>
|
||||||
|
<dd>Listen port. Default: <code>8080</code></dd>
|
||||||
|
|
||||||
|
<dt>backlog</dt>
|
||||||
|
<dd>Connection queue size. Default: <code>5</code></dd>
|
||||||
|
|
||||||
|
<dt>default_root</dt>
|
||||||
|
<dd>Enable default root handler. Default: <code>false</code></dd>
|
||||||
|
|
||||||
|
<dt>proxy_timeout</dt>
|
||||||
|
<dd>Default timeout for proxy requests in seconds. Default: <code>30.0</code></dd>
|
||||||
|
|
||||||
|
<dt>redirect_instructions</dt>
|
||||||
|
<dd>Dictionary of redirect rules. Format: <code>"/from": "/to"</code></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>ssl</h3>
|
||||||
|
<p>SSL/TLS configuration for HTTPS.</p>
|
||||||
|
<dl>
|
||||||
|
<dt>enabled</dt>
|
||||||
|
<dd>Enable HTTPS. Default: <code>false</code></dd>
|
||||||
|
|
||||||
|
<dt>cert_file</dt>
|
||||||
|
<dd>Path to SSL certificate file. Default: <code>./ssl/cert.pem</code></dd>
|
||||||
|
|
||||||
|
<dt>key_file</dt>
|
||||||
|
<dd>Path to SSL private key file. Default: <code>./ssl/key.pem</code></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>logging</h3>
|
||||||
|
<p>Logging configuration with structlog support.</p>
|
||||||
|
<dl>
|
||||||
|
<dt>level</dt>
|
||||||
|
<dd>Log level: <code>DEBUG</code>, <code>INFO</code>, <code>WARNING</code>,
|
||||||
|
<code>ERROR</code>. Default: <code>INFO</code></dd>
|
||||||
|
|
||||||
|
<dt>console_output</dt>
|
||||||
|
<dd>Output to console. Default: <code>true</code></dd>
|
||||||
|
|
||||||
|
<dt>format</dt>
|
||||||
|
<dd>Format configuration object (see below)</dd>
|
||||||
|
|
||||||
|
<dt>console</dt>
|
||||||
|
<dd>Console handler configuration</dd>
|
||||||
|
|
||||||
|
<dt>files</dt>
|
||||||
|
<dd>List of file handlers for logging to files</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h4>logging.format</h4>
|
||||||
|
<dl>
|
||||||
|
<dt>type</dt>
|
||||||
|
<dd>Format type: <code>standard</code> or <code>json</code>. Default: <code>standard</code></dd>
|
||||||
|
|
||||||
|
<dt>use_colors</dt>
|
||||||
|
<dd>Enable colored output in console. Default: <code>true</code></dd>
|
||||||
|
|
||||||
|
<dt>show_module</dt>
|
||||||
|
<dd>Show module name in logs. Default: <code>true</code></dd>
|
||||||
|
|
||||||
|
<dt>timestamp_format</dt>
|
||||||
|
<dd>Timestamp format string. Default: <code>%Y-%m-%d %H:%M:%S</code></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h4>logging.files[]</h4>
|
||||||
|
<dl>
|
||||||
|
<dt>path</dt>
|
||||||
|
<dd>Path to log file</dd>
|
||||||
|
|
||||||
|
<dt>level</dt>
|
||||||
|
<dd>Log level for this file handler</dd>
|
||||||
|
|
||||||
|
<dt>format</dt>
|
||||||
|
<dd>Format configuration for this file</dd>
|
||||||
|
|
||||||
|
<dt>loggers</dt>
|
||||||
|
<dd>List of logger names to include (empty = all loggers)</dd>
|
||||||
|
|
||||||
|
<dt>max_bytes</dt>
|
||||||
|
<dd>Maximum file size before rotation. Default: <code>10485760</code> (10MB)</dd>
|
||||||
|
|
||||||
|
<dt>backup_count</dt>
|
||||||
|
<dd>Number of backup files to keep. Default: <code>5</code></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>extensions</h3>
|
||||||
|
<p>List of extension modules to load. See <a href="../reference/extensions.html">Extensions Reference</a>.</p>
|
||||||
|
|
||||||
|
<h3>Complete Example</h3>
|
||||||
|
<pre><span class="directive">http:</span>
|
||||||
|
<span class="directive">static_dir:</span> <span class="value">./static</span>
|
||||||
|
<span class="directive">templates_dir:</span> <span class="value">./templates</span>
|
||||||
|
|
||||||
|
<span class="directive">server:</span>
|
||||||
|
<span class="directive">host:</span> <span class="value">0.0.0.0</span>
|
||||||
|
<span class="directive">port:</span> <span class="value">8080</span>
|
||||||
|
<span class="directive">backlog:</span> <span class="value">5</span>
|
||||||
|
<span class="directive">default_root:</span> <span class="value">false</span>
|
||||||
|
<span class="directive">proxy_timeout:</span> <span class="value">30.0</span>
|
||||||
|
|
||||||
|
<span class="directive">ssl:</span>
|
||||||
|
<span class="directive">enabled:</span> <span class="value">false</span>
|
||||||
|
<span class="directive">cert_file:</span> <span class="value">./ssl/cert.pem</span>
|
||||||
|
<span class="directive">key_file:</span> <span class="value">./ssl/key.pem</span>
|
||||||
|
|
||||||
|
<span class="directive">logging:</span>
|
||||||
|
<span class="directive">level:</span> <span class="value">INFO</span>
|
||||||
|
<span class="directive">console_output:</span> <span class="value">true</span>
|
||||||
|
<span class="directive">format:</span>
|
||||||
|
<span class="directive">type:</span> <span class="value">standard</span>
|
||||||
|
<span class="directive">use_colors:</span> <span class="value">true</span>
|
||||||
|
<span class="directive">timestamp_format:</span> <span class="value">"%Y-%m-%d %H:%M:%S"</span>
|
||||||
|
<span class="directive">files:</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">./logs/pyserve.log</span>
|
||||||
|
<span class="directive">level:</span> <span class="value">DEBUG</span>
|
||||||
|
<span class="directive">max_bytes:</span> <span class="value">10485760</span>
|
||||||
|
<span class="directive">backup_count:</span> <span class="value">5</span>
|
||||||
|
|
||||||
|
<span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">routing</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">regex_locations:</span>
|
||||||
|
<span class="value">"__default__"</span>:
|
||||||
|
<span class="directive">root:</span> <span class="value">"./static"</span>
|
||||||
|
<span class="directive">index_file:</span> <span class="value">"index.html"</span></pre>
|
||||||
|
|
||||||
|
<div class="warning">
|
||||||
|
<strong>Warning:</strong> When running in production, always use SSL
|
||||||
|
and restrict the bind address appropriately.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
59
guides/index.html
Normal file
59
guides/index.html
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Guides - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » Guides
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>Guides</h2>
|
||||||
|
|
||||||
|
<p>In-depth guides for configuring and using pyserve.</p>
|
||||||
|
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="process-orchestration.html">Process Orchestration</a></td>
|
||||||
|
<td class="desc">Run apps in isolated processes with health monitoring</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="configuration.html">Configuration</a></td>
|
||||||
|
<td class="desc">Complete configuration reference</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="routing.html">Routing</a></td>
|
||||||
|
<td class="desc">URL routing and regex patterns</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="reverse-proxy.html">Reverse Proxy</a></td>
|
||||||
|
<td class="desc">Proxying requests to backend services</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="asgi-mount.html">ASGI Mounting</a></td>
|
||||||
|
<td class="desc">Mount Python web frameworks in-process</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
354
guides/process-orchestration.html
Normal file
354
guides/process-orchestration.html
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Process Orchestration - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">async http server</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » <a href="/guides/">Guides</a> » Process Orchestration
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>Process Orchestration</h2>
|
||||||
|
|
||||||
|
<p>Process Orchestration is pyserve's flagship feature for running multiple Python web
|
||||||
|
applications with full process isolation. Each application runs in its own subprocess
|
||||||
|
with independent lifecycle, health monitoring, and automatic restart on failure.</p>
|
||||||
|
|
||||||
|
<h3>Overview</h3>
|
||||||
|
<p>Unlike <a href="asgi-mount.html">ASGI Mounting</a> which runs apps in-process,
|
||||||
|
Process Orchestration provides:</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><strong>Process Isolation</strong> — Each app runs in a separate Python process</li>
|
||||||
|
<li><strong>Health Monitoring</strong> — Automatic health checks with configurable intervals</li>
|
||||||
|
<li><strong>Auto-restart</strong> — Failed processes restart with exponential backoff</li>
|
||||||
|
<li><strong>Multi-worker Support</strong> — Configure multiple uvicorn workers per app</li>
|
||||||
|
<li><strong>Dynamic Port Allocation</strong> — Automatic port assignment (9000-9999)</li>
|
||||||
|
<li><strong>WSGI Support</strong> — Flask/Django apps via automatic wrapping</li>
|
||||||
|
<li><strong>Request Tracing</strong> — X-Request-ID propagation through proxied requests</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Architecture</h3>
|
||||||
|
<pre>
|
||||||
|
PyServe Gateway (:8000)
|
||||||
|
│
|
||||||
|
┌────────────────┼────────────────┐
|
||||||
|
▼ ▼ ▼
|
||||||
|
FastAPI Flask Starlette
|
||||||
|
:9001 :9002 :9003
|
||||||
|
/api/* /admin/* /ws/*
|
||||||
|
</pre>
|
||||||
|
<p>PyServe acts as a gateway, routing requests to the appropriate subprocess based on URL path.</p>
|
||||||
|
|
||||||
|
<h3>Basic Configuration</h3>
|
||||||
|
<pre><span class="directive">server:</span>
|
||||||
|
<span class="directive">host:</span> <span class="value">0.0.0.0</span>
|
||||||
|
<span class="directive">port:</span> <span class="value">8000</span>
|
||||||
|
|
||||||
|
<span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">process_orchestration</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">apps:</span>
|
||||||
|
- <span class="directive">name:</span> <span class="value">api</span>
|
||||||
|
<span class="directive">path:</span> <span class="value">/api</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">myapp.api:app</span>
|
||||||
|
|
||||||
|
- <span class="directive">name:</span> <span class="value">admin</span>
|
||||||
|
<span class="directive">path:</span> <span class="value">/admin</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">myapp.admin:app</span></pre>
|
||||||
|
|
||||||
|
<h3>App Configuration Options</h3>
|
||||||
|
<dl>
|
||||||
|
<dt>name</dt>
|
||||||
|
<dd>Unique identifier for the application (required)</dd>
|
||||||
|
|
||||||
|
<dt>path</dt>
|
||||||
|
<dd>URL path prefix for routing requests (required)</dd>
|
||||||
|
|
||||||
|
<dt>app_path</dt>
|
||||||
|
<dd>Python import path. Format: <code>module:attribute</code> (required)</dd>
|
||||||
|
|
||||||
|
<dt>app_type</dt>
|
||||||
|
<dd>Application type: <code>asgi</code> or <code>wsgi</code>. Default: <code>asgi</code></dd>
|
||||||
|
|
||||||
|
<dt>workers</dt>
|
||||||
|
<dd>Number of uvicorn workers. Default: <code>1</code></dd>
|
||||||
|
|
||||||
|
<dt>port</dt>
|
||||||
|
<dd>Fixed port number. Default: auto-allocated from port_range</dd>
|
||||||
|
|
||||||
|
<dt>factory</dt>
|
||||||
|
<dd>If <code>true</code>, app_path points to a factory function. Default: <code>false</code></dd>
|
||||||
|
|
||||||
|
<dt>env</dt>
|
||||||
|
<dd>Environment variables to pass to the subprocess</dd>
|
||||||
|
|
||||||
|
<dt>module_path</dt>
|
||||||
|
<dd>Path to add to <code>sys.path</code> for module resolution</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Health Check Options</h3>
|
||||||
|
<dl>
|
||||||
|
<dt>health_check_enabled</dt>
|
||||||
|
<dd>Enable health monitoring. Default: <code>true</code></dd>
|
||||||
|
|
||||||
|
<dt>health_check_path</dt>
|
||||||
|
<dd>Endpoint to check for health. Default: <code>/health</code></dd>
|
||||||
|
|
||||||
|
<dt>health_check_interval</dt>
|
||||||
|
<dd>Interval between health checks in seconds. Default: <code>10.0</code></dd>
|
||||||
|
|
||||||
|
<dt>health_check_timeout</dt>
|
||||||
|
<dd>Timeout for health check requests. Default: <code>5.0</code></dd>
|
||||||
|
|
||||||
|
<dt>health_check_retries</dt>
|
||||||
|
<dd>Failed checks before restart. Default: <code>3</code></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Restart Options</h3>
|
||||||
|
<dl>
|
||||||
|
<dt>max_restart_count</dt>
|
||||||
|
<dd>Maximum restart attempts before giving up. Default: <code>5</code></dd>
|
||||||
|
|
||||||
|
<dt>restart_delay</dt>
|
||||||
|
<dd>Initial delay between restarts in seconds. Default: <code>1.0</code></dd>
|
||||||
|
|
||||||
|
<dt>shutdown_timeout</dt>
|
||||||
|
<dd>Timeout for graceful shutdown. Default: <code>30.0</code></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Global Configuration</h3>
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">process_orchestration</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">port_range:</span> <span class="value">[9000, 9999]</span>
|
||||||
|
<span class="directive">health_check_enabled:</span> <span class="value">true</span>
|
||||||
|
<span class="directive">proxy_timeout:</span> <span class="value">60.0</span>
|
||||||
|
<span class="directive">logging:</span>
|
||||||
|
<span class="directive">httpx_level:</span> <span class="value">warning</span>
|
||||||
|
<span class="directive">proxy_logs:</span> <span class="value">true</span>
|
||||||
|
<span class="directive">health_check_logs:</span> <span class="value">false</span>
|
||||||
|
<span class="directive">apps:</span>
|
||||||
|
<span class="comment"># ...</span></pre>
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt>port_range</dt>
|
||||||
|
<dd>Range for dynamic port allocation. Default: <code>[9000, 9999]</code></dd>
|
||||||
|
|
||||||
|
<dt>proxy_timeout</dt>
|
||||||
|
<dd>Timeout for proxied requests in seconds. Default: <code>60.0</code></dd>
|
||||||
|
|
||||||
|
<dt>logging.httpx_level</dt>
|
||||||
|
<dd>Log level for HTTP client (debug/info/warning/error). Default: <code>warning</code></dd>
|
||||||
|
|
||||||
|
<dt>logging.proxy_logs</dt>
|
||||||
|
<dd>Log proxied requests with latency. Default: <code>true</code></dd>
|
||||||
|
|
||||||
|
<dt>logging.health_check_logs</dt>
|
||||||
|
<dd>Log health check results. Default: <code>false</code></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>FastAPI Example</h3>
|
||||||
|
<pre><span class="comment"># myapp/api.py</span>
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
@app.get("/health")
|
||||||
|
async def health():
|
||||||
|
return {"status": "ok"}
|
||||||
|
|
||||||
|
@app.get("/users")
|
||||||
|
async def get_users():
|
||||||
|
return [{"id": 1, "name": "Alice"}]</pre>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">process_orchestration</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">apps:</span>
|
||||||
|
- <span class="directive">name:</span> <span class="value">api</span>
|
||||||
|
<span class="directive">path:</span> <span class="value">/api</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">myapp.api:app</span>
|
||||||
|
<span class="directive">workers:</span> <span class="value">4</span>
|
||||||
|
<span class="directive">health_check_path:</span> <span class="value">/health</span></pre>
|
||||||
|
|
||||||
|
<p>Requests to <code>/api/users</code> are proxied to the FastAPI process as <code>/users</code>.</p>
|
||||||
|
|
||||||
|
<h3>Flask Example (WSGI)</h3>
|
||||||
|
<pre><span class="comment"># myapp/admin.py</span>
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@app.route("/health")
|
||||||
|
def health():
|
||||||
|
return {"status": "ok"}
|
||||||
|
|
||||||
|
@app.route("/dashboard")
|
||||||
|
def dashboard():
|
||||||
|
return {"page": "dashboard"}</pre>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">process_orchestration</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">apps:</span>
|
||||||
|
- <span class="directive">name:</span> <span class="value">admin</span>
|
||||||
|
<span class="directive">path:</span> <span class="value">/admin</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">myapp.admin:app</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">wsgi</span>
|
||||||
|
<span class="directive">workers:</span> <span class="value">2</span></pre>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>Note:</strong> WSGI support requires <code>a2wsgi</code> package:
|
||||||
|
<code>pip install a2wsgi</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Factory Pattern</h3>
|
||||||
|
<pre><span class="comment"># myapp/api.py</span>
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
def create_app(debug: bool = False) -> FastAPI:
|
||||||
|
app = FastAPI(debug=debug)
|
||||||
|
|
||||||
|
@app.get("/health")
|
||||||
|
async def health():
|
||||||
|
return {"status": "ok", "debug": debug}
|
||||||
|
|
||||||
|
return app</pre>
|
||||||
|
|
||||||
|
<pre><span class="directive">apps:</span>
|
||||||
|
- <span class="directive">name:</span> <span class="value">api</span>
|
||||||
|
<span class="directive">path:</span> <span class="value">/api</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">myapp.api:create_app</span>
|
||||||
|
<span class="directive">factory:</span> <span class="value">true</span></pre>
|
||||||
|
|
||||||
|
<h3>Environment Variables</h3>
|
||||||
|
<p>Pass environment variables to subprocesses:</p>
|
||||||
|
<pre><span class="directive">apps:</span>
|
||||||
|
- <span class="directive">name:</span> <span class="value">api</span>
|
||||||
|
<span class="directive">path:</span> <span class="value">/api</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">myapp.api:app</span>
|
||||||
|
<span class="directive">env:</span>
|
||||||
|
<span class="directive">DATABASE_URL:</span> <span class="value">"postgresql://localhost/mydb"</span>
|
||||||
|
<span class="directive">REDIS_URL:</span> <span class="value">"redis://localhost:6379"</span>
|
||||||
|
<span class="directive">DEBUG:</span> <span class="value">"false"</span></pre>
|
||||||
|
|
||||||
|
<h3>Multiple Applications</h3>
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">process_orchestration</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">port_range:</span> <span class="value">[9000, 9999]</span>
|
||||||
|
<span class="directive">apps:</span>
|
||||||
|
<span class="comment"># FastAPI REST API</span>
|
||||||
|
- <span class="directive">name:</span> <span class="value">api</span>
|
||||||
|
<span class="directive">path:</span> <span class="value">/api</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">apps.api:app</span>
|
||||||
|
<span class="directive">workers:</span> <span class="value">4</span>
|
||||||
|
|
||||||
|
<span class="comment"># Flask Admin Panel</span>
|
||||||
|
- <span class="directive">name:</span> <span class="value">admin</span>
|
||||||
|
<span class="directive">path:</span> <span class="value">/admin</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">apps.admin:app</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">wsgi</span>
|
||||||
|
<span class="directive">workers:</span> <span class="value">2</span>
|
||||||
|
|
||||||
|
<span class="comment"># Starlette WebSocket Handler</span>
|
||||||
|
- <span class="directive">name:</span> <span class="value">websocket</span>
|
||||||
|
<span class="directive">path:</span> <span class="value">/ws</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">apps.websocket:app</span>
|
||||||
|
<span class="directive">workers:</span> <span class="value">1</span></pre>
|
||||||
|
|
||||||
|
<h3>Request Tracing</h3>
|
||||||
|
<p>PyServe automatically generates and propagates <code>X-Request-ID</code> headers:</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li>If a request has <code>X-Request-ID</code>, it's preserved</li>
|
||||||
|
<li>Otherwise, a UUID is generated</li>
|
||||||
|
<li>The ID is passed to subprocesses and included in response headers</li>
|
||||||
|
<li>All logs include the request ID for tracing</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Process Orchestration vs ASGI Mount</h3>
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<th>Feature</th>
|
||||||
|
<th>Process Orchestration</th>
|
||||||
|
<th>ASGI Mount</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Isolation</td>
|
||||||
|
<td>Full process isolation</td>
|
||||||
|
<td>Shared process</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Memory</td>
|
||||||
|
<td>Separate per app</td>
|
||||||
|
<td>Shared</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Crash Impact</td>
|
||||||
|
<td>Only that app restarts</td>
|
||||||
|
<td>All apps affected</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Health Checks</td>
|
||||||
|
<td>Yes, with auto-restart</td>
|
||||||
|
<td>No</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Multi-worker</td>
|
||||||
|
<td>Yes, per app</td>
|
||||||
|
<td>No</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Latency</td>
|
||||||
|
<td>HTTP proxy overhead</td>
|
||||||
|
<td>In-process (faster)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Use Case</td>
|
||||||
|
<td>Production, isolation needed</td>
|
||||||
|
<td>Development, simple setups</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>When to use Process Orchestration:</strong>
|
||||||
|
<ul class="indent">
|
||||||
|
<li>Running multiple apps that shouldn't affect each other</li>
|
||||||
|
<li>Need automatic restart on failure</li>
|
||||||
|
<li>Different resource requirements per app</li>
|
||||||
|
<li>Production deployments</li>
|
||||||
|
</ul>
|
||||||
|
<strong>When to use ASGI Mount:</strong>
|
||||||
|
<ul class="indent">
|
||||||
|
<li>Development and testing</li>
|
||||||
|
<li>Simple setups with trusted apps</li>
|
||||||
|
<li>Minimal latency requirements</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>See Also:</strong>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><a href="asgi-mount.html">ASGI Mounting Guide</a> — In-process app mounting</li>
|
||||||
|
<li><a href="../reference/extensions.html">Extensions Reference</a> — All extension types</li>
|
||||||
|
<li><a href="configuration.html">Configuration Guide</a> — Full configuration reference</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
156
guides/reverse-proxy.html
Normal file
156
guides/reverse-proxy.html
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Reverse Proxy - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » <a href="/guides/">Guides</a> » Reverse Proxy
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>Reverse Proxy</h2>
|
||||||
|
|
||||||
|
<p>pyserve can act as a reverse proxy, forwarding requests to backend services.</p>
|
||||||
|
|
||||||
|
<h3>Basic Proxy Configuration</h3>
|
||||||
|
<p>Use the <code>proxy_pass</code> directive in routing:</p>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">routing</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">regex_locations:</span>
|
||||||
|
<span class="value">"~^/api/"</span>:
|
||||||
|
<span class="directive">proxy_pass:</span> <span class="value">"http://localhost:9001"</span></pre>
|
||||||
|
|
||||||
|
<p>All requests to <code>/api/*</code> will be forwarded to <code>http://localhost:9001/api/*</code>.</p>
|
||||||
|
|
||||||
|
<h3>Proxy Headers</h3>
|
||||||
|
<p>pyserve automatically adds standard proxy headers:</p>
|
||||||
|
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td><code>X-Forwarded-For</code></td>
|
||||||
|
<td>Client's IP address</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>X-Forwarded-Proto</code></td>
|
||||||
|
<td>Original protocol (http/https)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>X-Forwarded-Host</code></td>
|
||||||
|
<td>Original Host header</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>X-Real-IP</code></td>
|
||||||
|
<td>Client's real IP address</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Custom Headers</h3>
|
||||||
|
<p>Add custom headers to proxied requests:</p>
|
||||||
|
|
||||||
|
<pre><span class="value">"~^/api/"</span>:
|
||||||
|
<span class="directive">proxy_pass:</span> <span class="value">"http://localhost:9001"</span>
|
||||||
|
<span class="directive">headers:</span>
|
||||||
|
- <span class="value">"X-Custom-Header: my-value"</span>
|
||||||
|
- <span class="value">"Authorization: Bearer token123"</span></pre>
|
||||||
|
|
||||||
|
<h3>Dynamic Headers with Captures</h3>
|
||||||
|
<p>Use regex capture groups to build dynamic headers:</p>
|
||||||
|
|
||||||
|
<pre><span class="value">"~^/api/v(?P<version>\\d+)/(?P<service>\\w+)"</span>:
|
||||||
|
<span class="directive">proxy_pass:</span> <span class="value">"http://localhost:9001"</span>
|
||||||
|
<span class="directive">headers:</span>
|
||||||
|
- <span class="value">"X-API-Version: {version}"</span>
|
||||||
|
- <span class="value">"X-Service: {service}"</span>
|
||||||
|
- <span class="value">"X-Client-IP: $remote_addr"</span></pre>
|
||||||
|
|
||||||
|
<p>Special variables:</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><code>{capture_name}</code> — Named capture group from regex</li>
|
||||||
|
<li><code>$remote_addr</code> — Client's IP address</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Proxy Timeout</h3>
|
||||||
|
<p>Configure timeout for proxy requests:</p>
|
||||||
|
|
||||||
|
<pre><span class="comment"># Global default timeout</span>
|
||||||
|
<span class="directive">server:</span>
|
||||||
|
<span class="directive">proxy_timeout:</span> <span class="value">30.0</span>
|
||||||
|
|
||||||
|
<span class="comment"># Per-route timeout</span>
|
||||||
|
<span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">routing</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">regex_locations:</span>
|
||||||
|
<span class="value">"~^/api/slow"</span>:
|
||||||
|
<span class="directive">proxy_pass:</span> <span class="value">"http://localhost:9001"</span>
|
||||||
|
<span class="directive">timeout:</span> <span class="value">120</span> <span class="comment"># 2 minutes for slow endpoints</span></pre>
|
||||||
|
|
||||||
|
<h3>URL Rewriting</h3>
|
||||||
|
<p>The proxy preserves the original request path by default:</p>
|
||||||
|
|
||||||
|
<pre><span class="comment"># Request: GET /api/users/123</span>
|
||||||
|
<span class="comment"># Proxied: GET http://backend:9001/api/users/123</span>
|
||||||
|
<span class="value">"~^/api/"</span>:
|
||||||
|
<span class="directive">proxy_pass:</span> <span class="value">"http://backend:9001"</span></pre>
|
||||||
|
|
||||||
|
<p>To proxy to a specific path:</p>
|
||||||
|
|
||||||
|
<pre><span class="comment"># Request: GET /api/users/123</span>
|
||||||
|
<span class="comment"># Proxied: GET http://backend:9001/v2/users/123 (path preserved)</span>
|
||||||
|
<span class="value">"~^/api/"</span>:
|
||||||
|
<span class="directive">proxy_pass:</span> <span class="value">"http://backend:9001/v2"</span></pre>
|
||||||
|
|
||||||
|
<h3>Load Balancing Example</h3>
|
||||||
|
<p>Route different services to different backends:</p>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">routing</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">regex_locations:</span>
|
||||||
|
<span class="value">"~^/api/users"</span>:
|
||||||
|
<span class="directive">proxy_pass:</span> <span class="value">"http://user-service:8001"</span>
|
||||||
|
|
||||||
|
<span class="value">"~^/api/orders"</span>:
|
||||||
|
<span class="directive">proxy_pass:</span> <span class="value">"http://order-service:8002"</span>
|
||||||
|
|
||||||
|
<span class="value">"~^/api/products"</span>:
|
||||||
|
<span class="directive">proxy_pass:</span> <span class="value">"http://product-service:8003"</span></pre>
|
||||||
|
|
||||||
|
<h3>Error Handling</h3>
|
||||||
|
<p>pyserve returns appropriate error codes for proxy failures:</p>
|
||||||
|
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td><code>502 Bad Gateway</code></td>
|
||||||
|
<td>Backend connection failed or returned invalid response</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>504 Gateway Timeout</code></td>
|
||||||
|
<td>Backend did not respond within timeout</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>Note:</strong> pyserve uses <code>httpx</code> for async HTTP requests
|
||||||
|
to backend services, supporting HTTP/1.1 and HTTP/2.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
169
guides/routing.html
Normal file
169
guides/routing.html
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Routing - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » <a href="/guides/">Guides</a> » Routing
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>Routing</h2>
|
||||||
|
|
||||||
|
<p>pyserve supports nginx-style routing patterns including exact matches,
|
||||||
|
regex locations, and SPA fallback.</p>
|
||||||
|
|
||||||
|
<h3>Location Types</h3>
|
||||||
|
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td><code>=</code></td>
|
||||||
|
<td>Exact match</td>
|
||||||
|
<td class="desc"><code>=/health</code> matches only <code>/health</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>~</code></td>
|
||||||
|
<td>Case-sensitive regex</td>
|
||||||
|
<td class="desc"><code>~^/api/v\d+/</code> matches <code>/api/v1/</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>~*</code></td>
|
||||||
|
<td>Case-insensitive regex</td>
|
||||||
|
<td class="desc"><code>~*\.(js|css)$</code> matches <code>.JS</code> and <code>.css</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>__default__</code></td>
|
||||||
|
<td>Default fallback</td>
|
||||||
|
<td class="desc">Matches when no other route matches</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Match Priority</h3>
|
||||||
|
<p>Routes are processed in the following order:</p>
|
||||||
|
<ol class="indent">
|
||||||
|
<li><strong>Exact matches</strong> (<code>=</code>) — checked first</li>
|
||||||
|
<li><strong>Regex patterns</strong> (<code>~</code> and <code>~*</code>) — in definition order</li>
|
||||||
|
<li><strong>Default fallback</strong> (<code>__default__</code>) — last resort</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h3>Routing Configuration</h3>
|
||||||
|
<p>Routing is configured via the <code>routing</code> extension:</p>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">routing</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">regex_locations:</span>
|
||||||
|
<span class="comment"># Exact match for health check</span>
|
||||||
|
<span class="value">"=/health"</span>:
|
||||||
|
<span class="directive">return:</span> <span class="value">"200 OK"</span>
|
||||||
|
<span class="directive">content_type:</span> <span class="value">"text/plain"</span>
|
||||||
|
|
||||||
|
<span class="comment"># Static files with caching</span>
|
||||||
|
<span class="value">"~*\\.(js|css|png|jpg|gif|ico)$"</span>:
|
||||||
|
<span class="directive">root:</span> <span class="value">"./static"</span>
|
||||||
|
<span class="directive">cache_control:</span> <span class="value">"public, max-age=31536000"</span>
|
||||||
|
|
||||||
|
<span class="comment"># HTML files without caching</span>
|
||||||
|
<span class="value">"~*\\.html$"</span>:
|
||||||
|
<span class="directive">root:</span> <span class="value">"./static"</span>
|
||||||
|
<span class="directive">cache_control:</span> <span class="value">"no-cache"</span>
|
||||||
|
|
||||||
|
<span class="comment"># Default fallback</span>
|
||||||
|
<span class="value">"__default__"</span>:
|
||||||
|
<span class="directive">root:</span> <span class="value">"./static"</span>
|
||||||
|
<span class="directive">index_file:</span> <span class="value">"index.html"</span></pre>
|
||||||
|
|
||||||
|
<h3>Location Directives</h3>
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt>root</dt>
|
||||||
|
<dd>Base directory for serving files.</dd>
|
||||||
|
|
||||||
|
<dt>index_file</dt>
|
||||||
|
<dd>Index file name for directory requests. Default: <code>index.html</code></dd>
|
||||||
|
|
||||||
|
<dt>proxy_pass</dt>
|
||||||
|
<dd>Upstream server URL for reverse proxy. See <a href="reverse-proxy.html">Reverse Proxy</a>.</dd>
|
||||||
|
|
||||||
|
<dt>return</dt>
|
||||||
|
<dd>Return a fixed response. Format: <code>"status message"</code> or <code>"status"</code></dd>
|
||||||
|
|
||||||
|
<dt>content_type</dt>
|
||||||
|
<dd>Response content type for <code>return</code> directive.</dd>
|
||||||
|
|
||||||
|
<dt>cache_control</dt>
|
||||||
|
<dd>Cache-Control header value.</dd>
|
||||||
|
|
||||||
|
<dt>headers</dt>
|
||||||
|
<dd>List of additional headers to add. Format: <code>"Header-Name: value"</code></dd>
|
||||||
|
|
||||||
|
<dt>spa_fallback</dt>
|
||||||
|
<dd>Enable SPA mode — serve index file for all routes.</dd>
|
||||||
|
|
||||||
|
<dt>exclude_patterns</dt>
|
||||||
|
<dd>URL patterns to exclude from SPA fallback.</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Named Capture Groups</h3>
|
||||||
|
<p>Regex locations support named capture groups that can be used in headers and proxy URLs:</p>
|
||||||
|
|
||||||
|
<pre><span class="value">"~^/api/v(?P<version>\\d+)/(?P<resource>\\w+)"</span>:
|
||||||
|
<span class="directive">proxy_pass:</span> <span class="value">"http://backend:9001"</span>
|
||||||
|
<span class="directive">headers:</span>
|
||||||
|
- <span class="value">"X-API-Version: {version}"</span>
|
||||||
|
- <span class="value">"X-Resource: {resource}"</span></pre>
|
||||||
|
|
||||||
|
<p>Request to <code>/api/v2/users</code> will have headers:</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><code>X-API-Version: 2</code></li>
|
||||||
|
<li><code>X-Resource: users</code></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>SPA Configuration</h3>
|
||||||
|
<p>For Single Page Applications, use <code>spa_fallback</code> with <code>exclude_patterns</code>:</p>
|
||||||
|
|
||||||
|
<pre><span class="value">"__default__"</span>:
|
||||||
|
<span class="directive">spa_fallback:</span> <span class="value">true</span>
|
||||||
|
<span class="directive">root:</span> <span class="value">"./dist"</span>
|
||||||
|
<span class="directive">index_file:</span> <span class="value">"index.html"</span>
|
||||||
|
<span class="directive">exclude_patterns:</span>
|
||||||
|
- <span class="value">"/api/"</span>
|
||||||
|
- <span class="value">"/assets/"</span>
|
||||||
|
- <span class="value">"/static/"</span></pre>
|
||||||
|
|
||||||
|
<p>This will:</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li>Serve <code>index.html</code> for routes like <code>/about</code>, <code>/users/123</code></li>
|
||||||
|
<li>Return 404 for <code>/api/*</code>, <code>/assets/*</code>, <code>/static/*</code> if file not found</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Static File Serving</h3>
|
||||||
|
<p>Basic static file configuration:</p>
|
||||||
|
|
||||||
|
<pre><span class="value">"~*\\.(css|js|png|jpg|gif|svg|woff2?)$"</span>:
|
||||||
|
<span class="directive">root:</span> <span class="value">"./static"</span>
|
||||||
|
<span class="directive">cache_control:</span> <span class="value">"public, max-age=86400"</span>
|
||||||
|
<span class="directive">headers:</span>
|
||||||
|
- <span class="value">"X-Content-Type-Options: nosniff"</span></pre>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>Note:</strong> pyserve automatically detects MIME types based on file extensions.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
118
index.html
Normal file
118
index.html
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>pyserve - Documentation</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>About</h2>
|
||||||
|
<p>
|
||||||
|
pyserve is a Python application orchestrator and HTTP server.
|
||||||
|
Built on top of <strong>Starlette</strong> and <strong>Uvicorn</strong>,
|
||||||
|
it manages multiple ASGI/WSGI applications through a single entry point
|
||||||
|
with process isolation, health monitoring, and auto-restart.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Key Features</h2>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><strong>Process Orchestration</strong> — Run multiple apps in isolated subprocesses with health checks</li>
|
||||||
|
<li><strong>Auto-restart</strong> — Failed processes restart automatically with exponential backoff</li>
|
||||||
|
<li><strong>Multi-worker Support</strong> — Configure workers per application</li>
|
||||||
|
<li><strong>nginx-style Routing</strong> — Regex patterns with exact, prefix, and case-insensitive matching</li>
|
||||||
|
<li><strong>Reverse Proxy</strong> — Forward requests to backend services with header manipulation</li>
|
||||||
|
<li><strong>Static File Serving</strong> — Efficient serving with correct MIME types</li>
|
||||||
|
<li><strong>SPA Support</strong> — Single Page Application fallback routing</li>
|
||||||
|
<li><strong>SSL/HTTPS</strong> — Secure connections with certificate configuration</li>
|
||||||
|
<li><strong>ASGI/WSGI Support</strong> — FastAPI, Flask, Django, Starlette and more</li>
|
||||||
|
<li><strong>Request Tracing</strong> — X-Request-ID propagation through proxied requests</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Documentation</h2>
|
||||||
|
|
||||||
|
<h3>Getting Started</h3>
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="getting-started/installation.html">Installation</a></td>
|
||||||
|
<td class="desc">Download and install pyserve</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="getting-started/quickstart.html">Quick Start</a></td>
|
||||||
|
<td class="desc">Get up and running in 5 minutes</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Guides</h3>
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="guides/process-orchestration.html">Process Orchestration</a></td>
|
||||||
|
<td class="desc">Run apps in isolated processes with health monitoring</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="guides/configuration.html">Configuration</a></td>
|
||||||
|
<td class="desc">Complete configuration reference</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="guides/routing.html">Routing</a></td>
|
||||||
|
<td class="desc">URL routing and regex patterns</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="guides/reverse-proxy.html">Reverse Proxy</a></td>
|
||||||
|
<td class="desc">Proxying requests to backend services</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="guides/asgi-mount.html">ASGI Mounting</a></td>
|
||||||
|
<td class="desc">Mount Python web frameworks in-process</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Reference</h3>
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="reference/cli.html">CLI Reference</a></td>
|
||||||
|
<td class="desc">Command-line interface options</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="reference/extensions.html">Extensions</a></td>
|
||||||
|
<td class="desc">Built-in extension modules</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Resources</h2>
|
||||||
|
<ul class="plain">
|
||||||
|
<li><a href="https://github.com/ShiftyX1/PyServe">GitHub Repository</a></li>
|
||||||
|
<li><a href="https://github.com/ShiftyX1/PyServe/releases">Releases & Downloads</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Version</h2>
|
||||||
|
<p>Current version: <strong>0.9.10</strong></p>
|
||||||
|
|
||||||
|
<h2>Requirements</h2>
|
||||||
|
<ul class="indent">
|
||||||
|
<li>Python 3.12 or higher</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
251
reference/asgi-mount.html
Normal file
251
reference/asgi-mount.html
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>ASGI Mount API - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » <a href="/reference/">Reference</a> » ASGI Mount API
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>ASGI Mount API Reference</h2>
|
||||||
|
|
||||||
|
<p>The <code>pyserve.asgi_mount</code> module provides a Python API for mounting
|
||||||
|
ASGI and WSGI applications programmatically.</p>
|
||||||
|
|
||||||
|
<h3>Classes</h3>
|
||||||
|
|
||||||
|
<h4>ASGIAppLoader</h4>
|
||||||
|
<p>Loads and manages ASGI/WSGI applications from Python import paths.</p>
|
||||||
|
|
||||||
|
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> ASGIAppLoader
|
||||||
|
|
||||||
|
loader = ASGIAppLoader()
|
||||||
|
|
||||||
|
<span class="comment"># Load an ASGI app</span>
|
||||||
|
app = loader.load_app(
|
||||||
|
app_path=<span class="value">"mymodule:app"</span>,
|
||||||
|
app_type=<span class="value">"asgi"</span>,
|
||||||
|
module_path=<span class="value">"/path/to/project"</span>,
|
||||||
|
factory=<span class="value">False</span>,
|
||||||
|
factory_args=<span class="value">None</span>
|
||||||
|
)</pre>
|
||||||
|
|
||||||
|
<h5>Methods</h5>
|
||||||
|
<dl>
|
||||||
|
<dt>load_app(app_path, app_type="asgi", module_path=None, factory=False, factory_args=None)</dt>
|
||||||
|
<dd>
|
||||||
|
Load an application from an import path.
|
||||||
|
<ul class="indent">
|
||||||
|
<li><code>app_path</code>: Import path in format <code>module:attribute</code></li>
|
||||||
|
<li><code>app_type</code>: <code>"asgi"</code> or <code>"wsgi"</code></li>
|
||||||
|
<li><code>module_path</code>: Optional path to add to <code>sys.path</code></li>
|
||||||
|
<li><code>factory</code>: If True, call the attribute as a factory function</li>
|
||||||
|
<li><code>factory_args</code>: Dict of arguments for factory function</li>
|
||||||
|
</ul>
|
||||||
|
Returns the loaded ASGI application or <code>None</code> on error.
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<dt>get_app(app_path)</dt>
|
||||||
|
<dd>Get a previously loaded application by its path.</dd>
|
||||||
|
|
||||||
|
<dt>reload_app(app_path, **kwargs)</dt>
|
||||||
|
<dd>Reload an application, useful for development hot-reloading.</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h4>MountedApp</h4>
|
||||||
|
<p>Represents an application mounted at a specific path.</p>
|
||||||
|
|
||||||
|
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> MountedApp
|
||||||
|
|
||||||
|
mount = MountedApp(
|
||||||
|
path=<span class="value">"/api"</span>,
|
||||||
|
app=my_asgi_app,
|
||||||
|
name=<span class="value">"my-api"</span>,
|
||||||
|
strip_path=<span class="value">True</span>
|
||||||
|
)</pre>
|
||||||
|
|
||||||
|
<h5>Attributes</h5>
|
||||||
|
<dl>
|
||||||
|
<dt>path: str</dt>
|
||||||
|
<dd>The mount path (without trailing slash).</dd>
|
||||||
|
|
||||||
|
<dt>app: ASGIApp</dt>
|
||||||
|
<dd>The ASGI application.</dd>
|
||||||
|
|
||||||
|
<dt>name: str</dt>
|
||||||
|
<dd>Friendly name for logging.</dd>
|
||||||
|
|
||||||
|
<dt>strip_path: bool</dt>
|
||||||
|
<dd>Whether to strip the mount path from requests.</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h5>Methods</h5>
|
||||||
|
<dl>
|
||||||
|
<dt>matches(request_path) → bool</dt>
|
||||||
|
<dd>Check if a request path matches this mount.</dd>
|
||||||
|
|
||||||
|
<dt>get_modified_path(original_path) → str</dt>
|
||||||
|
<dd>Get the modified path after stripping mount prefix.</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h4>ASGIMountManager</h4>
|
||||||
|
<p>Manages multiple mounted applications and routes requests.</p>
|
||||||
|
|
||||||
|
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> ASGIMountManager
|
||||||
|
|
||||||
|
manager = ASGIMountManager()
|
||||||
|
|
||||||
|
<span class="comment"># Mount using app instance</span>
|
||||||
|
manager.mount(path=<span class="value">"/api"</span>, app=my_app)
|
||||||
|
|
||||||
|
<span class="comment"># Mount using import path</span>
|
||||||
|
manager.mount(
|
||||||
|
path=<span class="value">"/flask"</span>,
|
||||||
|
app_path=<span class="value">"myapp:flask_app"</span>,
|
||||||
|
app_type=<span class="value">"wsgi"</span>
|
||||||
|
)</pre>
|
||||||
|
|
||||||
|
<h5>Methods</h5>
|
||||||
|
<dl>
|
||||||
|
<dt>mount(path, app=None, app_path=None, app_type="asgi", module_path=None, factory=False, factory_args=None, name="", strip_path=True) → bool</dt>
|
||||||
|
<dd>
|
||||||
|
Mount an application at a path. Either <code>app</code> or <code>app_path</code> must be provided.
|
||||||
|
Returns <code>True</code> on success.
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<dt>unmount(path) → bool</dt>
|
||||||
|
<dd>Remove a mounted application. Returns <code>True</code> if found and removed.</dd>
|
||||||
|
|
||||||
|
<dt>get_mount(request_path) → Optional[MountedApp]</dt>
|
||||||
|
<dd>Get the mount that matches a request path.</dd>
|
||||||
|
|
||||||
|
<dt>handle_request(scope, receive, send) → bool</dt>
|
||||||
|
<dd>Handle an ASGI request. Returns <code>True</code> if handled by a mounted app.</dd>
|
||||||
|
|
||||||
|
<dt>list_mounts() → List[Dict]</dt>
|
||||||
|
<dd>Get a list of all mounts with their configuration.</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h5>Properties</h5>
|
||||||
|
<dl>
|
||||||
|
<dt>mounts: List[MountedApp]</dt>
|
||||||
|
<dd>Copy of the current mounts list (sorted by path length, longest first).</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Helper Functions</h3>
|
||||||
|
<p>Convenience functions for loading specific framework applications:</p>
|
||||||
|
|
||||||
|
<h4>create_fastapi_app()</h4>
|
||||||
|
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> create_fastapi_app
|
||||||
|
|
||||||
|
app = create_fastapi_app(
|
||||||
|
app_path=<span class="value">"myapp.api:app"</span>,
|
||||||
|
module_path=<span class="value">None</span>,
|
||||||
|
factory=<span class="value">False</span>,
|
||||||
|
factory_args=<span class="value">None</span>
|
||||||
|
)</pre>
|
||||||
|
|
||||||
|
<h4>create_flask_app()</h4>
|
||||||
|
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> create_flask_app
|
||||||
|
|
||||||
|
app = create_flask_app(
|
||||||
|
app_path=<span class="value">"myapp.web:app"</span>,
|
||||||
|
module_path=<span class="value">None</span>,
|
||||||
|
factory=<span class="value">False</span>,
|
||||||
|
factory_args=<span class="value">None</span>
|
||||||
|
)</pre>
|
||||||
|
<p>Automatically wraps the WSGI app for ASGI compatibility.</p>
|
||||||
|
|
||||||
|
<h4>create_django_app()</h4>
|
||||||
|
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> create_django_app
|
||||||
|
|
||||||
|
app = create_django_app(
|
||||||
|
settings_module=<span class="value">"myproject.settings"</span>,
|
||||||
|
module_path=<span class="value">"/path/to/project"</span>
|
||||||
|
)</pre>
|
||||||
|
<p>Sets <code>DJANGO_SETTINGS_MODULE</code> and returns Django's ASGI application.</p>
|
||||||
|
|
||||||
|
<h4>create_starlette_app()</h4>
|
||||||
|
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> create_starlette_app
|
||||||
|
|
||||||
|
app = create_starlette_app(
|
||||||
|
app_path=<span class="value">"myapp:starlette_app"</span>,
|
||||||
|
module_path=<span class="value">None</span>,
|
||||||
|
factory=<span class="value">False</span>,
|
||||||
|
factory_args=<span class="value">None</span>
|
||||||
|
)</pre>
|
||||||
|
|
||||||
|
<h3>Usage Example</h3>
|
||||||
|
<p>Complete example mounting multiple applications:</p>
|
||||||
|
|
||||||
|
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> (
|
||||||
|
PyServeServer,
|
||||||
|
ASGIMountManager,
|
||||||
|
create_fastapi_app,
|
||||||
|
create_flask_app
|
||||||
|
)
|
||||||
|
|
||||||
|
<span class="comment"># Create mount manager</span>
|
||||||
|
mounts = ASGIMountManager()
|
||||||
|
|
||||||
|
<span class="comment"># Mount FastAPI</span>
|
||||||
|
api_app = create_fastapi_app(<span class="value">"myapp.api:app"</span>)
|
||||||
|
<span class="keyword">if</span> api_app:
|
||||||
|
mounts.mount(<span class="value">"/api"</span>, app=api_app, name=<span class="value">"api"</span>)
|
||||||
|
|
||||||
|
<span class="comment"># Mount Flask</span>
|
||||||
|
admin_app = create_flask_app(<span class="value">"myapp.admin:app"</span>)
|
||||||
|
<span class="keyword">if</span> admin_app:
|
||||||
|
mounts.mount(<span class="value">"/admin"</span>, app=admin_app, name=<span class="value">"admin"</span>)
|
||||||
|
|
||||||
|
<span class="comment"># List mounts</span>
|
||||||
|
<span class="keyword">for</span> mount <span class="keyword">in</span> mounts.list_mounts():
|
||||||
|
print(f<span class="value">"Mounted {mount['name']} at {mount['path']}"</span>)</pre>
|
||||||
|
|
||||||
|
<h3>Error Handling</h3>
|
||||||
|
<p>All loader functions return <code>None</code> on failure and log errors.
|
||||||
|
Check the return value before using:</p>
|
||||||
|
|
||||||
|
<pre>app = create_fastapi_app(<span class="value">"nonexistent:app"</span>)
|
||||||
|
<span class="keyword">if</span> app <span class="keyword">is None</span>:
|
||||||
|
<span class="comment"># Handle error - check logs for details</span>
|
||||||
|
print(<span class="value">"Failed to load application"</span>)</pre>
|
||||||
|
|
||||||
|
<h3>WSGI Compatibility</h3>
|
||||||
|
<p>For WSGI applications, pyserve uses adapters in this priority:</p>
|
||||||
|
<ol class="indent">
|
||||||
|
<li><code>a2wsgi.WSGIMiddleware</code> (recommended)</li>
|
||||||
|
<li><code>asgiref.wsgi.WsgiToAsgi</code> (fallback)</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<p>Install an adapter:</p>
|
||||||
|
<pre>pip install a2wsgi <span class="comment"># recommended</span>
|
||||||
|
<span class="comment"># or</span>
|
||||||
|
pip install asgiref</pre>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>See Also:</strong>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><a href="../guides/asgi-mount.html">ASGI Mounting Guide</a> — Configuration-based mounting</li>
|
||||||
|
<li><a href="extensions.html">Extensions</a> — ASGI extension configuration</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
141
reference/cli.html
Normal file
141
reference/cli.html
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>CLI Reference - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » <a href="/reference/">Reference</a> » CLI
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>CLI Reference</h2>
|
||||||
|
|
||||||
|
<p>pyserve provides a command-line interface for server management.</p>
|
||||||
|
|
||||||
|
<h3>Synopsis</h3>
|
||||||
|
<pre>pyserve [OPTIONS]</pre>
|
||||||
|
|
||||||
|
<h3>Options</h3>
|
||||||
|
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td><code>-c, --config FILE</code></td>
|
||||||
|
<td>Path to configuration file</td>
|
||||||
|
<td class="desc">Default: <code>config.yaml</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>--host HOST</code></td>
|
||||||
|
<td>Bind address</td>
|
||||||
|
<td class="desc">Overrides config value</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>--port PORT</code></td>
|
||||||
|
<td>Listen port</td>
|
||||||
|
<td class="desc">Overrides config value</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>--debug</code></td>
|
||||||
|
<td>Enable debug mode</td>
|
||||||
|
<td class="desc">Sets log level to DEBUG</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>--version</code></td>
|
||||||
|
<td>Show version and exit</td>
|
||||||
|
<td class="desc"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>--help</code></td>
|
||||||
|
<td>Show help message and exit</td>
|
||||||
|
<td class="desc"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Examples</h3>
|
||||||
|
|
||||||
|
<p><strong>Start with default configuration:</strong></p>
|
||||||
|
<pre>pyserve</pre>
|
||||||
|
|
||||||
|
<p><strong>Start with custom config file:</strong></p>
|
||||||
|
<pre>pyserve -c /path/to/config.yaml</pre>
|
||||||
|
|
||||||
|
<p><strong>Override host and port:</strong></p>
|
||||||
|
<pre>pyserve --host 127.0.0.1 --port 9000</pre>
|
||||||
|
|
||||||
|
<p><strong>Enable debug mode:</strong></p>
|
||||||
|
<pre>pyserve --debug</pre>
|
||||||
|
|
||||||
|
<p><strong>Show version:</strong></p>
|
||||||
|
<pre>pyserve --version
|
||||||
|
<span class="comment"># Output: pyserve 0.7.0</span></pre>
|
||||||
|
|
||||||
|
<h3>Configuration Priority</h3>
|
||||||
|
<p>Settings are applied in the following order (later overrides earlier):</p>
|
||||||
|
<ol class="indent">
|
||||||
|
<li>Default values</li>
|
||||||
|
<li>Configuration file (<code>config.yaml</code>)</li>
|
||||||
|
<li>Command-line options</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h3>Default Configuration</h3>
|
||||||
|
<p>If no configuration file is found, pyserve uses default settings:</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li>Host: <code>0.0.0.0</code></li>
|
||||||
|
<li>Port: <code>8080</code></li>
|
||||||
|
<li>Log level: <code>INFO</code></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Exit Codes</h3>
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td><code>0</code></td>
|
||||||
|
<td>Success / Clean shutdown</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>1</code></td>
|
||||||
|
<td>Configuration error or startup failure</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Signals</h3>
|
||||||
|
<p>pyserve handles the following signals:</p>
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td><code>SIGINT</code> (Ctrl+C)</td>
|
||||||
|
<td>Graceful shutdown</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>SIGTERM</code></td>
|
||||||
|
<td>Graceful shutdown</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Development Commands (Makefile)</h3>
|
||||||
|
<p>When working with the source repository, use make commands:</p>
|
||||||
|
|
||||||
|
<pre>make run <span class="comment"># Start in development mode</span>
|
||||||
|
make run-prod <span class="comment"># Start in production mode</span>
|
||||||
|
make test <span class="comment"># Run tests</span>
|
||||||
|
make test-cov <span class="comment"># Tests with coverage</span>
|
||||||
|
make lint <span class="comment"># Check code with linters</span>
|
||||||
|
make format <span class="comment"># Format code</span>
|
||||||
|
make build <span class="comment"># Build wheel package</span>
|
||||||
|
make clean <span class="comment"># Clean temporary files</span>
|
||||||
|
make help <span class="comment"># Show all commands</span></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
325
reference/extensions.html
Normal file
325
reference/extensions.html
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Extensions - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » <a href="/reference/">Reference</a> » Extensions
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>Extensions</h2>
|
||||||
|
|
||||||
|
<p>pyserve uses a modular extension system for adding functionality. Extensions
|
||||||
|
are loaded in order and can process requests and modify responses.</p>
|
||||||
|
|
||||||
|
<h3>Built-in Extensions</h3>
|
||||||
|
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td><code>process_orchestration</code></td>
|
||||||
|
<td>Run ASGI/WSGI apps in isolated processes with health monitoring</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>routing</code></td>
|
||||||
|
<td>nginx-style URL routing with regex patterns</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>asgi</code></td>
|
||||||
|
<td>Mount ASGI/WSGI applications in-process</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>security</code></td>
|
||||||
|
<td>Security headers and IP filtering</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>caching</code></td>
|
||||||
|
<td>Response caching (in development)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>monitoring</code></td>
|
||||||
|
<td>Request metrics and statistics</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Extension Configuration</h3>
|
||||||
|
<p>Extensions are configured in the <code>extensions</code> section:</p>
|
||||||
|
|
||||||
|
<pre><span class="directive">extensions:</span>
|
||||||
|
- <span class="directive">type:</span> <span class="value">routing</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="comment"># extension-specific configuration</span>
|
||||||
|
|
||||||
|
- <span class="directive">type:</span> <span class="value">security</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="comment"># ...</span></pre>
|
||||||
|
|
||||||
|
<h3>Routing Extension</h3>
|
||||||
|
<p>The primary extension for URL routing. See <a href="../guides/routing.html">Routing Guide</a> for full documentation.</p>
|
||||||
|
|
||||||
|
<pre><span class="directive">- type:</span> <span class="value">routing</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">regex_locations:</span>
|
||||||
|
<span class="value">"=/health"</span>:
|
||||||
|
<span class="directive">return:</span> <span class="value">"200 OK"</span>
|
||||||
|
<span class="value">"~^/api/"</span>:
|
||||||
|
<span class="directive">proxy_pass:</span> <span class="value">"http://backend:9001"</span>
|
||||||
|
<span class="value">"__default__"</span>:
|
||||||
|
<span class="directive">root:</span> <span class="value">"./static"</span></pre>
|
||||||
|
|
||||||
|
<h3>Security Extension</h3>
|
||||||
|
<p>Adds security headers and IP-based access control.</p>
|
||||||
|
|
||||||
|
<h4>Configuration Options</h4>
|
||||||
|
<dl>
|
||||||
|
<dt>security_headers</dt>
|
||||||
|
<dd>Dictionary of security headers to add to all responses</dd>
|
||||||
|
|
||||||
|
<dt>allowed_ips</dt>
|
||||||
|
<dd>List of allowed IP addresses (whitelist mode)</dd>
|
||||||
|
|
||||||
|
<dt>blocked_ips</dt>
|
||||||
|
<dd>List of blocked IP addresses (blacklist mode)</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<pre><span class="directive">- type:</span> <span class="value">security</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">security_headers:</span>
|
||||||
|
<span class="directive">X-Frame-Options:</span> <span class="value">DENY</span>
|
||||||
|
<span class="directive">X-Content-Type-Options:</span> <span class="value">nosniff</span>
|
||||||
|
<span class="directive">X-XSS-Protection:</span> <span class="value">"1; mode=block"</span>
|
||||||
|
<span class="directive">Strict-Transport-Security:</span> <span class="value">"max-age=31536000"</span>
|
||||||
|
<span class="directive">blocked_ips:</span>
|
||||||
|
- <span class="value">"192.168.1.100"</span>
|
||||||
|
- <span class="value">"10.0.0.50"</span></pre>
|
||||||
|
|
||||||
|
<p>Default security headers if not specified:</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><code>X-Content-Type-Options: nosniff</code></li>
|
||||||
|
<li><code>X-Frame-Options: DENY</code></li>
|
||||||
|
<li><code>X-XSS-Protection: 1; mode=block</code></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Caching Extension</h3>
|
||||||
|
<p>Response caching for improved performance. <em>(Currently in development)</em></p>
|
||||||
|
|
||||||
|
<h4>Configuration Options</h4>
|
||||||
|
<dl>
|
||||||
|
<dt>cache_patterns</dt>
|
||||||
|
<dd>URL patterns to cache</dd>
|
||||||
|
|
||||||
|
<dt>cache_ttl</dt>
|
||||||
|
<dd>Default cache TTL in seconds. Default: <code>3600</code></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<pre><span class="directive">- type:</span> <span class="value">caching</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">cache_ttl:</span> <span class="value">3600</span>
|
||||||
|
<span class="directive">cache_patterns:</span>
|
||||||
|
- <span class="value">"/api/public/*"</span></pre>
|
||||||
|
|
||||||
|
<h3>Monitoring Extension</h3>
|
||||||
|
<p>Collects request metrics and provides statistics.</p>
|
||||||
|
|
||||||
|
<h4>Configuration Options</h4>
|
||||||
|
<dl>
|
||||||
|
<dt>enable_metrics</dt>
|
||||||
|
<dd>Enable metrics collection. Default: <code>true</code></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<pre><span class="directive">- type:</span> <span class="value">monitoring</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">enable_metrics:</span> <span class="value">true</span></pre>
|
||||||
|
|
||||||
|
<p>Collected metrics (available at <code>/metrics</code>):</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><code>request_count</code> — Total number of requests</li>
|
||||||
|
<li><code>error_count</code> — Number of requests with 4xx/5xx status</li>
|
||||||
|
<li><code>error_rate</code> — Error rate (errors / total)</li>
|
||||||
|
<li><code>avg_response_time</code> — Average response time in seconds</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Built-in Endpoints</h3>
|
||||||
|
<p>pyserve provides built-in endpoints regardless of extensions:</p>
|
||||||
|
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td><code>/health</code></td>
|
||||||
|
<td>Health check endpoint, returns <code>200 OK</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>/metrics</code></td>
|
||||||
|
<td>JSON metrics from all extensions</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Extension Processing Order</h3>
|
||||||
|
<p>Extensions process requests in the order they are defined:</p>
|
||||||
|
<ol class="indent">
|
||||||
|
<li>Request comes in</li>
|
||||||
|
<li>Each extension's <code>process_request</code> is called in order</li>
|
||||||
|
<li>First extension to return a response wins</li>
|
||||||
|
<li>Response passes through each extension's <code>process_response</code></li>
|
||||||
|
<li>Response is sent to client</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>Note:</strong> Place the <code>routing</code> extension last if you want
|
||||||
|
other extensions (like security) to process requests first.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>ASGI Extension</h3>
|
||||||
|
<p>Mount external ASGI/WSGI applications (FastAPI, Flask, Django, etc.) at specified paths.</p>
|
||||||
|
|
||||||
|
<h4>Configuration Options</h4>
|
||||||
|
<dl>
|
||||||
|
<dt>mounts</dt>
|
||||||
|
<dd>List of mount configurations (see below)</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h4>Mount Configuration</h4>
|
||||||
|
<dl>
|
||||||
|
<dt>path</dt>
|
||||||
|
<dd>URL path where the app will be mounted. Example: <code>/api</code></dd>
|
||||||
|
|
||||||
|
<dt>app_path</dt>
|
||||||
|
<dd>Python import path. Format: <code>module:attribute</code></dd>
|
||||||
|
|
||||||
|
<dt>app_type</dt>
|
||||||
|
<dd>Application type: <code>asgi</code> or <code>wsgi</code>. Default: <code>asgi</code></dd>
|
||||||
|
|
||||||
|
<dt>module_path</dt>
|
||||||
|
<dd>Optional path to add to <code>sys.path</code></dd>
|
||||||
|
|
||||||
|
<dt>factory</dt>
|
||||||
|
<dd>If <code>true</code>, call as factory function. Default: <code>false</code></dd>
|
||||||
|
|
||||||
|
<dt>factory_args</dt>
|
||||||
|
<dd>Arguments to pass to factory function</dd>
|
||||||
|
|
||||||
|
<dt>name</dt>
|
||||||
|
<dd>Friendly name for logging</dd>
|
||||||
|
|
||||||
|
<dt>strip_path</dt>
|
||||||
|
<dd>Remove mount path from request URL. Default: <code>true</code></dd>
|
||||||
|
|
||||||
|
<dt>django_settings</dt>
|
||||||
|
<dd>Django settings module (for Django apps only)</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<pre><span class="directive">- type:</span> <span class="value">asgi</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">mounts:</span>
|
||||||
|
<span class="comment"># FastAPI application</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">"/api"</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">"myapp.api:app"</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">asgi</span>
|
||||||
|
<span class="directive">name:</span> <span class="value">"api"</span>
|
||||||
|
|
||||||
|
<span class="comment"># Flask application (WSGI)</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">"/admin"</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">"myapp.admin:app"</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">wsgi</span>
|
||||||
|
<span class="directive">name:</span> <span class="value">"admin"</span>
|
||||||
|
|
||||||
|
<span class="comment"># Factory pattern with arguments</span>
|
||||||
|
- <span class="directive">path:</span> <span class="value">"/api/v2"</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">"myapp.api:create_app"</span>
|
||||||
|
<span class="directive">factory:</span> <span class="value">true</span>
|
||||||
|
<span class="directive">factory_args:</span>
|
||||||
|
<span class="directive">debug:</span> <span class="value">true</span>
|
||||||
|
<span class="directive">version:</span> <span class="value">"2.0"</span></pre>
|
||||||
|
|
||||||
|
<p>Supported frameworks:</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li><strong>FastAPI</strong> — Native ASGI (<code>app_type: asgi</code>)</li>
|
||||||
|
<li><strong>Starlette</strong> — Native ASGI (<code>app_type: asgi</code>)</li>
|
||||||
|
<li><strong>Flask</strong> — WSGI, auto-wrapped (<code>app_type: wsgi</code>)</li>
|
||||||
|
<li><strong>Django</strong> — Use <code>django_settings</code> parameter</li>
|
||||||
|
<li><strong>Custom ASGI</strong> — Any ASGI-compatible application</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>Note:</strong> For WSGI applications, install <code>a2wsgi</code> or <code>asgiref</code>:
|
||||||
|
<code>pip install a2wsgi</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>See <a href="../guides/asgi-mount.html">ASGI Mounting Guide</a> for detailed documentation.</p>
|
||||||
|
|
||||||
|
<h3>Process Orchestration Extension</h3>
|
||||||
|
<p>The flagship extension for running apps in isolated subprocesses. <strong>Recommended for production.</strong></p>
|
||||||
|
|
||||||
|
<h4>Key Features</h4>
|
||||||
|
<ul class="indent">
|
||||||
|
<li>Process isolation — each app runs in its own subprocess</li>
|
||||||
|
<li>Health monitoring with automatic restart</li>
|
||||||
|
<li>Multi-worker support per application</li>
|
||||||
|
<li>Dynamic port allocation (9000-9999)</li>
|
||||||
|
<li>Request tracing with X-Request-ID</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<pre><span class="directive">- type:</span> <span class="value">process_orchestration</span>
|
||||||
|
<span class="directive">config:</span>
|
||||||
|
<span class="directive">port_range:</span> <span class="value">[9000, 9999]</span>
|
||||||
|
<span class="directive">health_check_enabled:</span> <span class="value">true</span>
|
||||||
|
<span class="directive">proxy_timeout:</span> <span class="value">60.0</span>
|
||||||
|
<span class="directive">logging:</span>
|
||||||
|
<span class="directive">httpx_level:</span> <span class="value">warning</span>
|
||||||
|
<span class="directive">proxy_logs:</span> <span class="value">true</span>
|
||||||
|
<span class="directive">health_check_logs:</span> <span class="value">false</span>
|
||||||
|
<span class="directive">apps:</span>
|
||||||
|
- <span class="directive">name:</span> <span class="value">api</span>
|
||||||
|
<span class="directive">path:</span> <span class="value">/api</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">myapp.api:app</span>
|
||||||
|
<span class="directive">workers:</span> <span class="value">4</span>
|
||||||
|
<span class="directive">health_check_path:</span> <span class="value">/health</span>
|
||||||
|
|
||||||
|
- <span class="directive">name:</span> <span class="value">admin</span>
|
||||||
|
<span class="directive">path:</span> <span class="value">/admin</span>
|
||||||
|
<span class="directive">app_path:</span> <span class="value">myapp.admin:app</span>
|
||||||
|
<span class="directive">app_type:</span> <span class="value">wsgi</span>
|
||||||
|
<span class="directive">workers:</span> <span class="value">2</span></pre>
|
||||||
|
|
||||||
|
<h4>App Configuration</h4>
|
||||||
|
<dl>
|
||||||
|
<dt>name</dt>
|
||||||
|
<dd>Unique identifier (required)</dd>
|
||||||
|
|
||||||
|
<dt>path</dt>
|
||||||
|
<dd>URL path prefix (required)</dd>
|
||||||
|
|
||||||
|
<dt>app_path</dt>
|
||||||
|
<dd>Python import path (required)</dd>
|
||||||
|
|
||||||
|
<dt>app_type</dt>
|
||||||
|
<dd><code>asgi</code> or <code>wsgi</code>. Default: <code>asgi</code></dd>
|
||||||
|
|
||||||
|
<dt>workers</dt>
|
||||||
|
<dd>Number of uvicorn workers. Default: <code>1</code></dd>
|
||||||
|
|
||||||
|
<dt>health_check_path</dt>
|
||||||
|
<dd>Health endpoint. Default: <code>/health</code></dd>
|
||||||
|
|
||||||
|
<dt>max_restart_count</dt>
|
||||||
|
<dd>Max restart attempts. Default: <code>5</code></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<p>See <a href="../guides/process-orchestration.html">Process Orchestration Guide</a> for full documentation.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
49
reference/index.html
Normal file
49
reference/index.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Reference - pyserve</title>
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1>pyserve</h1>
|
||||||
|
<div class="tagline">python application orchestrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="../index.html">pyserve</a> » Reference
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h2>Reference</h2>
|
||||||
|
|
||||||
|
<p>API and CLI reference documentation.</p>
|
||||||
|
|
||||||
|
<table class="dirindex">
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="cli.html">CLI Reference</a></td>
|
||||||
|
<td class="desc">Command-line interface options</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="extensions.html">Extensions</a></td>
|
||||||
|
<td class="desc">Built-in extension modules</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="icon"><span class="file">📄</span></td>
|
||||||
|
<td><a href="asgi-mount.html">ASGI Mount API</a></td>
|
||||||
|
<td class="desc">Python API for mounting ASGI/WSGI applications</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
237
style.css
Normal file
237
style.css
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #c9c9c9;
|
||||||
|
background: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
|
border-bottom: 2px solid #2e8b57;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #3cb371;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header .tagline {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #e0e0e0;
|
||||||
|
margin: 20px 0 10px 0;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
border-bottom: 1px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #d0d0d0;
|
||||||
|
margin: 15px 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #5fba7d;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #7ccd9a;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: #4a9a6a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Directory listing table */
|
||||||
|
table.dirindex {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.dirindex td {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-bottom: 1px solid #2a2a2a;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.dirindex tr:hover {
|
||||||
|
background: #252525;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.dirindex .icon {
|
||||||
|
width: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.dirindex .desc {
|
||||||
|
color: #888;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Plain list */
|
||||||
|
ul.plain {
|
||||||
|
list-style: none;
|
||||||
|
margin: 10px 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.plain li {
|
||||||
|
padding: 3px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.plain li:before {
|
||||||
|
content: "» ";
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Indented list */
|
||||||
|
ul.indent {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding-left: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.indent li {
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code blocks */
|
||||||
|
pre {
|
||||||
|
background: #0d0d0d;
|
||||||
|
border: 1px solid #333;
|
||||||
|
padding: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin: 10px 0;
|
||||||
|
color: #b0b0b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
background: #0d0d0d;
|
||||||
|
padding: 1px 4px;
|
||||||
|
color: #b0b0b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline code in text */
|
||||||
|
p code, li code, td code {
|
||||||
|
border: 1px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configuration block */
|
||||||
|
.config {
|
||||||
|
background: #1f1f1a;
|
||||||
|
border: 1px solid #3a3a30;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note/warning blocks */
|
||||||
|
.note {
|
||||||
|
background: #1a2a1a;
|
||||||
|
border-left: 3px solid #2e8b57;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note strong {
|
||||||
|
color: #3cb371;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
background: #2a2a1a;
|
||||||
|
border-left: 3px solid #b8860b;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning strong {
|
||||||
|
color: #daa520;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Definition list */
|
||||||
|
dl {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-left: 20px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigation breadcrumb */
|
||||||
|
.breadcrumb {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb a {
|
||||||
|
color: #5fba7d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
#footer {
|
||||||
|
border-top: 1px solid #333;
|
||||||
|
padding-top: 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #666;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Syntax highlighting for config examples */
|
||||||
|
.directive {
|
||||||
|
color: #5fba7d;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
color: #87ceeb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment {
|
||||||
|
color: #666;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user