Move OpenClaw, Palmr, MinIO, JSX publishing, MCP configs, and migration candidates into dedicated docs/ files. Keep only DevOps-essential content inline (deployment rules, DNS, router, credentials, troubleshooting). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
100 lines
3.8 KiB
Markdown
100 lines
3.8 KiB
Markdown
# Publishing JSX/React Artifacts Online
|
|
|
|
Single-file React components (JSX) can be published as standalone web pages via the NUC's artifacts infrastructure.
|
|
|
|
## How It Works
|
|
|
|
```
|
|
Public Internet → Tailscale Funnel (:443) → Traefik → artifacts-web (nginx) → /opt/artifacts/
|
|
```
|
|
|
|
- **Funnel URL:** `https://alezmad-nuc.tail58f5ad.ts.net/artifacts/<name>/`
|
|
- **LAN URL:** `https://artifacts.nuc.lan/<name>/`
|
|
- **Nginx container:** `artifacts-web` (image: `nginx:alpine`, read-only mount of `/opt/artifacts`)
|
|
- **Traefik public route:** `Host(alezmad-nuc.tail58f5ad.ts.net) && PathPrefix(/artifacts)` → strips `/artifacts` → `artifacts-web:80`
|
|
- **Config file:** `/traefik/dynamic/nuc-services-public.yaml` (inside `coolify-proxy` container)
|
|
|
|
## Quick Publish Steps
|
|
|
|
```bash
|
|
# 1. Build self-contained HTML from JSX
|
|
# - Replace `import { useState, ... } from "react"` with `const { useState, ... } = React;`
|
|
# - Remove `export default ComponentName;`
|
|
# - Wrap in HTML with React 18 CDN + Babel standalone
|
|
# - Add `ReactDOM.createRoot(root).render(<Component />)` at the end
|
|
|
|
# 2. Copy to NUC artifacts directory
|
|
ssh nuc "echo '7vXHpSTD.' | sudo -S mkdir -p /opt/artifacts/<name>"
|
|
scp /tmp/build/index.html nuc:~/tmp-artifact.html
|
|
ssh nuc "echo '7vXHpSTD.' | sudo -S mv ~/tmp-artifact.html /opt/artifacts/<name>/index.html"
|
|
ssh nuc "echo '7vXHpSTD.' | sudo -S chmod 644 /opt/artifacts/<name>/index.html"
|
|
|
|
# 3. Done! No server restart needed — nginx serves it immediately.
|
|
```
|
|
|
|
## HTML Template for JSX Files
|
|
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>TITLE</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { background: #08090d; overflow-x: hidden; }
|
|
/* Add any @keyframes or global styles here */
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="text/babel">
|
|
const { useState, useEffect, useRef, useCallback } = React;
|
|
|
|
// ... paste JSX component code here (without import/export lines) ...
|
|
|
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
|
root.render(React.createElement(ComponentName));
|
|
</script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
## Build Script (for large JSX files)
|
|
|
|
```bash
|
|
# Automated: strips import/export, wraps in HTML
|
|
cat > /tmp/build.html << 'HEADER'
|
|
<!DOCTYPE html>
|
|
<html><head>
|
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
|
</head><body><div id="root"></div>
|
|
<script type="text/babel">
|
|
const { useState, useEffect, useRef, useCallback, useMemo, useReducer } = React;
|
|
HEADER
|
|
|
|
# Strip first line (import) and last line (export), append body
|
|
sed -n '2,$p' source.jsx | sed '$d' >> /tmp/build.html
|
|
|
|
# Add render footer (replace COMPONENT_NAME)
|
|
cat >> /tmp/build.html << 'FOOTER'
|
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
|
root.render(React.createElement(COMPONENT_NAME));
|
|
</script></body></html>
|
|
FOOTER
|
|
```
|
|
|
|
## Currently Published
|
|
|
|
| Path | Source | Public URL |
|
|
|------|--------|------------|
|
|
| `/opt/artifacts/checkin/` | `arrio/.scratch/checkin_demo_v1.jsx` | `https://alezmad-nuc.tail58f5ad.ts.net/artifacts/checkin/` |
|