Spaces:
Running
Running
Restore dashboard at / with Paperclip at /app/ (proper fix)
Browse filesPatch ui/src/main.tsx in the Dockerfile build to add basename="/app"
to BrowserRouter. With this, React Router strips the /app prefix
client-side before matching routes — /app/ becomes /, /app/ABC/issues
becomes /ABC/issues — so the company-prefix extractor never sees "app".
Health server:
- / → HuggingClip dashboard (restored)
- /app/* → proxy to Paperclip with /app prefix stripped
- /api/* → proxy pass-through
- /* catch-all → proxy pass-through (assets, sw.js, etc.)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dockerfile +6 -0
- health-server.js +26 -4
- start.sh +2 -2
Dockerfile
CHANGED
|
@@ -11,6 +11,12 @@ RUN apt-get update && apt-get install -y \
|
|
| 11 |
# Clone Paperclip (depth=1 for speed, uses repo's default branch)
|
| 12 |
RUN git clone --depth=1 https://github.com/paperclipai/paperclip.git .
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
# Patch firstBlockedChainFinding to cap chain depth at 500.
|
| 15 |
# Default 1MB stack overflows on deep recovery-issue chains created by
|
| 16 |
# runaway agents. Cycle detection is already in place via the Set; we
|
|
|
|
| 11 |
# Clone Paperclip (depth=1 for speed, uses repo's default branch)
|
| 12 |
RUN git clone --depth=1 https://github.com/paperclipai/paperclip.git .
|
| 13 |
|
| 14 |
+
# Patch React Router to use /app as basename so the health dashboard owns /
|
| 15 |
+
# Without this, <BrowserRouter> sees the full path "/app/" and treats "app"
|
| 16 |
+
# as a company prefix, showing "Company not found".
|
| 17 |
+
RUN sed -i 's|<BrowserRouter>|<BrowserRouter basename="/app">|' ui/src/main.tsx && \
|
| 18 |
+
grep -q 'basename="/app"' ui/src/main.tsx || (echo "PATCH NOT APPLIED to main.tsx" && exit 1)
|
| 19 |
+
|
| 20 |
# Patch firstBlockedChainFinding to cap chain depth at 500.
|
| 21 |
# Default 1MB stack overflows on deep recovery-issue chains created by
|
| 22 |
# runaway agents. Cycle detection is already in place via the Set; we
|
health-server.js
CHANGED
|
@@ -84,6 +84,10 @@ app.get("/health", async (req, res) => {
|
|
| 84 |
// ============================================================================
|
| 85 |
// Dashboard Route
|
| 86 |
// ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
app.get("/_status", (req, res) => {
|
| 88 |
res.send(getDashboardHTML());
|
| 89 |
});
|
|
@@ -93,7 +97,7 @@ app.get("/_status/", (req, res) => {
|
|
| 93 |
});
|
| 94 |
|
| 95 |
app.get("/dashboard/", (req, res) => {
|
| 96 |
-
res.redirect("/
|
| 97 |
});
|
| 98 |
|
| 99 |
app.get("/dashboard/status", (req, res) => {
|
|
@@ -136,6 +140,24 @@ app.post("/dashboard/uptimerobot/setup", (req, res) => {
|
|
| 136 |
// Reverse Proxy Routes
|
| 137 |
// ============================================================================
|
| 138 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
// Proxy all /api/* requests to Paperclip
|
| 140 |
app.all("/api/*", async (req, res) => {
|
| 141 |
const targetPath = req.path;
|
|
@@ -599,10 +621,10 @@ function getDashboardHTML() {
|
|
| 599 |
</div>
|
| 600 |
<div class="stat">
|
| 601 |
<span class="stat-label">UI URL</span>
|
| 602 |
-
<span class="stat-value"><span class="code">/</span></span>
|
| 603 |
</div>
|
| 604 |
<div class="button-group">
|
| 605 |
-
<a href="/" class="button button-primary" target="_blank">Open Paperclip UI</a>
|
| 606 |
</div>
|
| 607 |
</div>
|
| 608 |
|
|
@@ -660,7 +682,7 @@ function getDashboardHTML() {
|
|
| 660 |
<div class="card">
|
| 661 |
<h2>📚 Resources</h2>
|
| 662 |
<div class="button-group" style="flex-direction: column;">
|
| 663 |
-
<a href="/" class="button button-primary" target="_blank">Paperclip Dashboard</a>
|
| 664 |
<a href="/api/" class="button button-secondary" target="_blank">API Reference</a>
|
| 665 |
</div>
|
| 666 |
<div class="stat" style="margin-top: 16px;">
|
|
|
|
| 84 |
// ============================================================================
|
| 85 |
// Dashboard Route
|
| 86 |
// ============================================================================
|
| 87 |
+
app.get("/", (req, res) => {
|
| 88 |
+
res.send(getDashboardHTML());
|
| 89 |
+
});
|
| 90 |
+
|
| 91 |
app.get("/_status", (req, res) => {
|
| 92 |
res.send(getDashboardHTML());
|
| 93 |
});
|
|
|
|
| 97 |
});
|
| 98 |
|
| 99 |
app.get("/dashboard/", (req, res) => {
|
| 100 |
+
res.redirect("/");
|
| 101 |
});
|
| 102 |
|
| 103 |
app.get("/dashboard/status", (req, res) => {
|
|
|
|
| 140 |
// Reverse Proxy Routes
|
| 141 |
// ============================================================================
|
| 142 |
|
| 143 |
+
// Proxy /app/* to Paperclip, stripping the /app prefix.
|
| 144 |
+
// The SPA is built with BrowserRouter basename="/app" so React Router
|
| 145 |
+
// strips the prefix on the client — Paperclip receives clean paths.
|
| 146 |
+
app.all("/app/*", async (req, res) => {
|
| 147 |
+
const targetPath = req.path.replace("/app", "") || "/";
|
| 148 |
+
const query = req.url.includes("?") ? req.url.slice(req.url.indexOf("?")) : "";
|
| 149 |
+
const targetUrl = `http://${PAPERCLIP_HOST}:${PAPERCLIP_PORT}${targetPath}${query}`;
|
| 150 |
+
|
| 151 |
+
try {
|
| 152 |
+
const response = await proxyRequest(req.method, targetUrl, req.headers, req.body);
|
| 153 |
+
Object.keys(response.headers).forEach((key) => res.setHeader(key, response.headers[key]));
|
| 154 |
+
res.status(response.statusCode).send(response.body);
|
| 155 |
+
} catch (error) {
|
| 156 |
+
console.error(`Proxy error: ${error.message}`);
|
| 157 |
+
res.status(503).json({ error: "Paperclip service unavailable", details: error.message });
|
| 158 |
+
}
|
| 159 |
+
});
|
| 160 |
+
|
| 161 |
// Proxy all /api/* requests to Paperclip
|
| 162 |
app.all("/api/*", async (req, res) => {
|
| 163 |
const targetPath = req.path;
|
|
|
|
| 621 |
</div>
|
| 622 |
<div class="stat">
|
| 623 |
<span class="stat-label">UI URL</span>
|
| 624 |
+
<span class="stat-value"><span class="code">/app/</span></span>
|
| 625 |
</div>
|
| 626 |
<div class="button-group">
|
| 627 |
+
<a href="/app/" class="button button-primary" target="_blank">Open Paperclip UI</a>
|
| 628 |
</div>
|
| 629 |
</div>
|
| 630 |
|
|
|
|
| 682 |
<div class="card">
|
| 683 |
<h2>📚 Resources</h2>
|
| 684 |
<div class="button-group" style="flex-direction: column;">
|
| 685 |
+
<a href="/app/" class="button button-primary" target="_blank">Paperclip Dashboard</a>
|
| 686 |
<a href="/api/" class="button button-secondary" target="_blank">API Reference</a>
|
| 687 |
</div>
|
| 688 |
<div class="stat" style="margin-top: 16px;">
|
start.sh
CHANGED
|
@@ -258,8 +258,8 @@ fi
|
|
| 258 |
|
| 259 |
echo "HuggingClip is ready!"
|
| 260 |
echo ""
|
| 261 |
-
echo " Health dashboard : http://localhost:7861/
|
| 262 |
-
echo " Paperclip UI : http://localhost:7861/"
|
| 263 |
echo " API : http://localhost:7861/api/"
|
| 264 |
echo ""
|
| 265 |
|
|
|
|
| 258 |
|
| 259 |
echo "HuggingClip is ready!"
|
| 260 |
echo ""
|
| 261 |
+
echo " Health dashboard : http://localhost:7861/"
|
| 262 |
+
echo " Paperclip UI : http://localhost:7861/app/"
|
| 263 |
echo " API : http://localhost:7861/api/"
|
| 264 |
echo ""
|
| 265 |
|