Compare commits
7 Commits
main
...
35796c0c3b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35796c0c3b | ||
|
|
0351e5c7a6 | ||
|
|
3df1465bf5 | ||
|
|
dcc5191ba3 | ||
|
|
220d3dc5a6 | ||
|
|
fa7bbe3953 | ||
|
|
ed1c8f6c96 |
@@ -1,16 +1,143 @@
|
|||||||
{
|
{
|
||||||
"managed": {
|
|
||||||
"name": "sample",
|
|
||||||
"repos": {
|
|
||||||
"frontend": "/path/to/your/frontend/repo",
|
|
||||||
"backend": "/path/to/your/backend/repo"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"framework": {
|
"framework": {
|
||||||
"name": "soleprint"
|
"name": "soleprint",
|
||||||
|
"slug": "soleprint",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Development workflow and documentation system",
|
||||||
|
"tagline": "Mapping development footprints",
|
||||||
|
"icon": "",
|
||||||
|
"hub_port": 12030
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"enabled": false
|
"enabled": true,
|
||||||
|
"provider": "google",
|
||||||
|
"allowed_domains": [],
|
||||||
|
"allowed_emails": [],
|
||||||
|
"session_secret": "ENV:AUTH_SESSION_SECRET"
|
||||||
},
|
},
|
||||||
"veins": []
|
"veins": ["google"],
|
||||||
|
"managed": {
|
||||||
|
"name": "sample"
|
||||||
|
},
|
||||||
|
"systems": [
|
||||||
|
{
|
||||||
|
"key": "data_flow",
|
||||||
|
"name": "artery",
|
||||||
|
"slug": "artery",
|
||||||
|
"title": "Artery",
|
||||||
|
"tagline": "Todo lo vital",
|
||||||
|
"icon": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "documentation",
|
||||||
|
"name": "atlas",
|
||||||
|
"slug": "atlas",
|
||||||
|
"title": "Atlas",
|
||||||
|
"tagline": "Documentacion accionable",
|
||||||
|
"icon": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "execution",
|
||||||
|
"name": "station",
|
||||||
|
"slug": "station",
|
||||||
|
"title": "Station",
|
||||||
|
"tagline": "Monitores, Entornos y Herramientas",
|
||||||
|
"icon": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"components": {
|
||||||
|
"shared": {
|
||||||
|
"config": {
|
||||||
|
"name": "room",
|
||||||
|
"title": "Room",
|
||||||
|
"description": "Runtime environment configuration",
|
||||||
|
"plural": "rooms"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"name": "depot",
|
||||||
|
"title": "Depot",
|
||||||
|
"description": "Data storage / provisions",
|
||||||
|
"plural": "depots"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data_flow": {
|
||||||
|
"connector": {
|
||||||
|
"name": "vein",
|
||||||
|
"title": "Vein",
|
||||||
|
"description": "Stateless API connector",
|
||||||
|
"plural": "veins"
|
||||||
|
},
|
||||||
|
"mock": {
|
||||||
|
"name": "shunt",
|
||||||
|
"title": "Shunt",
|
||||||
|
"description": "Fake connector for testing",
|
||||||
|
"plural": "shunts"
|
||||||
|
},
|
||||||
|
"composed": {
|
||||||
|
"name": "pulse",
|
||||||
|
"title": "Pulse",
|
||||||
|
"description": "Composed data flow",
|
||||||
|
"plural": "pulses",
|
||||||
|
"formula": "Vein + Room + Depot"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"name": "plexus",
|
||||||
|
"title": "Plexus",
|
||||||
|
"description": "Full app with backend, frontend and DB",
|
||||||
|
"plural": "plexus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"documentation": {
|
||||||
|
"pattern": {
|
||||||
|
"name": "template",
|
||||||
|
"title": "Template",
|
||||||
|
"description": "Documentation pattern",
|
||||||
|
"plural": "templates"
|
||||||
|
},
|
||||||
|
"library": {
|
||||||
|
"name": "book",
|
||||||
|
"title": "Book",
|
||||||
|
"description": "Documentation library"
|
||||||
|
},
|
||||||
|
"composed": {
|
||||||
|
"name": "book",
|
||||||
|
"title": "Book",
|
||||||
|
"description": "Composed documentation",
|
||||||
|
"plural": "books",
|
||||||
|
"formula": "Template + Depot"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"execution": {
|
||||||
|
"utility": {
|
||||||
|
"name": "tool",
|
||||||
|
"title": "Tool",
|
||||||
|
"description": "Execution utility",
|
||||||
|
"plural": "tools"
|
||||||
|
},
|
||||||
|
"watcher": {
|
||||||
|
"name": "monitor",
|
||||||
|
"title": "Monitor",
|
||||||
|
"description": "Service monitor",
|
||||||
|
"plural": "monitors"
|
||||||
|
},
|
||||||
|
"container": {
|
||||||
|
"name": "cabinet",
|
||||||
|
"title": "Cabinet",
|
||||||
|
"description": "Tool container",
|
||||||
|
"plural": "cabinets"
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"name": "desk",
|
||||||
|
"title": "Desk",
|
||||||
|
"description": "Execution workspace"
|
||||||
|
},
|
||||||
|
"composed": {
|
||||||
|
"name": "desk",
|
||||||
|
"title": "Desk",
|
||||||
|
"description": "Composed execution bundle",
|
||||||
|
"plural": "desks",
|
||||||
|
"formula": "Cabinet + Room + Depots"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,102 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Sample App</title>
|
<title>Sample - Public Demo</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
* {
|
||||||
font-family: system-ui, -apple-system, sans-serif;
|
box-sizing: border-box;
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: #f5f5f5;
|
padding: 0;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family:
|
||||||
|
system-ui,
|
||||||
|
-apple-system,
|
||||||
|
sans-serif;
|
||||||
|
background: linear-gradient(135deg, #1e3a5f 0%, #0d1b2a 100%);
|
||||||
|
color: #e5e5e5;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
||||||
}
|
}
|
||||||
h1 { color: #333; }
|
h1 {
|
||||||
p { color: #666; }
|
font-size: 3rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
background: linear-gradient(135deg, #3a86ff, #8338ec);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #a3a3a3;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.status {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background: rgba(58, 134, 255, 0.2);
|
||||||
|
border: 1px solid #3a86ff;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #3a86ff;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
margin-top: 3rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 8px;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
.info h3 {
|
||||||
|
color: #8338ec;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.info ul {
|
||||||
|
list-style: none;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.info li {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
.info li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #3a86ff;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Sample App</h1>
|
<h1>Sample</h1>
|
||||||
<p>This is a sample managed room for Soleprint.</p>
|
<p>Public demo - open to any Gmail account</p>
|
||||||
<p>Replace this with your actual frontend.</p>
|
<span class="status">Public Demo</span>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<h3>Soleprint Managed Room</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
With sidebar:
|
||||||
|
<a href="https://sample.spr.mcrn.ar"
|
||||||
|
>sample.spr.mcrn.ar</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Standalone:
|
||||||
|
<a href="https://sample.mcrn.ar">sample.mcrn.ar</a>
|
||||||
|
</li>
|
||||||
|
<li>Login with any Google account</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# SOLEPRINT - Sample Room Configuration
|
# Sample Soleprint Configuration
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Copy this to cfg/<your-room>/soleprint/.env and customize
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# DEPLOYMENT
|
# DEPLOYMENT
|
||||||
@@ -9,32 +8,24 @@
|
|||||||
DEPLOYMENT_NAME=sample_spr
|
DEPLOYMENT_NAME=sample_spr
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# NETWORK (unique per room to allow concurrent operation)
|
# NETWORK
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
NETWORK_NAME=sample_network
|
NETWORK_NAME=sample_network
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# PORTS (choose unique ports for each room)
|
# PORTS (unique per room)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
SOLEPRINT_PORT=12020
|
SOLEPRINT_PORT=12030
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# GOOGLE OAUTH (optional - for auth)
|
# GOOGLE OAUTH
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
GOOGLE_CLIENT_ID=
|
GOOGLE_CLIENT_ID=1076380473867-k6gvdg8etujj2e51bqejve78ft99hnqd.apps.googleusercontent.com
|
||||||
GOOGLE_CLIENT_SECRET=
|
GOOGLE_CLIENT_SECRET=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
|
||||||
GOOGLE_REDIRECT_URI=http://sample.spr.local.ar/spr/artery/google/oauth/callback
|
GOOGLE_REDIRECT_URI=http://sample.spr.local.ar/artery/google/oauth/callback
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# AUTH
|
# AUTH
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
AUTH_SESSION_SECRET=change-this-in-production
|
AUTH_BYPASS=true
|
||||||
|
AUTH_SESSION_SECRET=sample-dev-secret-change-in-production
|
||||||
# =============================================================================
|
|
||||||
# DATABASE (optional - if your app uses a database)
|
|
||||||
# =============================================================================
|
|
||||||
DB_HOST=sample_db
|
|
||||||
DB_PORT=5432
|
|
||||||
DB_NAME=sampledb
|
|
||||||
DB_USER=postgres
|
|
||||||
DB_PASSWORD=localdev123
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"showcase_url": "https://sample.spr.mcrn.ar",
|
||||||
"framework": {
|
"framework": {
|
||||||
"name": "soleprint",
|
"name": "soleprint",
|
||||||
"slug": "soleprint",
|
"slug": "soleprint",
|
||||||
|
|||||||
@@ -43,9 +43,9 @@
|
|||||||
"system": "artery"
|
"system": "artery"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "vnc",
|
"name": "vpn",
|
||||||
"slug": "vnc",
|
"slug": "vpn",
|
||||||
"title": "VNC",
|
"title": "VPN",
|
||||||
"status": "planned",
|
"status": "planned",
|
||||||
"system": "artery"
|
"system": "artery"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
<g id="clust6" class="cluster">
|
<g id="clust6" class="cluster">
|
||||||
<title>cluster_room</title>
|
<title>cluster_room</title>
|
||||||
<polygon fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" points="642,-92.75 642,-184.38 952,-184.38 952,-92.75 642,-92.75"/>
|
<polygon fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" points="642,-92.75 642,-184.38 952,-184.38 952,-92.75 642,-92.75"/>
|
||||||
<text xml:space="preserve" text-anchor="middle" x="797" y="-165.18" font-family="Helvetica,sans-Serif" font-size="16.00">Managed Room (e.g., AMAR)</text>
|
<text xml:space="preserve" text-anchor="middle" x="797" y="-165.18" font-family="Helvetica,sans-Serif" font-size="16.00">Managed Room</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- hub -->
|
<!-- hub -->
|
||||||
<g id="node1" class="node">
|
<g id="node1" class="node">
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
191
docs/architecture/sidebar-injection.html
Normal file
191
docs/architecture/sidebar-injection.html
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Sidebar Injection - Soleprint</title>
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
<style>
|
||||||
|
pre {
|
||||||
|
background: #1a1a1a;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
font-family: monospace;
|
||||||
|
background: #2a2a2a;
|
||||||
|
padding: 0.15rem 0.4rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #3f3f3f;
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background: #1a1a1a;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="../lang-toggle.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div id="lang-toggle"></div>
|
||||||
|
<h1>Sidebar Injection</h1>
|
||||||
|
<p class="subtitle">
|
||||||
|
<span class="lang-en">How managed room sidebar works</span>
|
||||||
|
<span class="lang-es">Como funciona el sidebar del managed room</span>
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>Overview</h2>
|
||||||
|
<p class="lang-en">The soleprint sidebar is injected into managed app pages using a hybrid nginx + JavaScript approach. This allows any frontend framework (React, Next.js, static HTML) to receive the sidebar without code modifications.</p>
|
||||||
|
<p class="lang-es">El sidebar de soleprint se inyecta en las paginas de apps manejadas usando un enfoque hibrido nginx + JavaScript. Esto permite que cualquier framework frontend (React, Next.js, HTML estatico) reciba el sidebar sin modificaciones de codigo.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">How It Works</span>
|
||||||
|
<span class="lang-es">Como Funciona</span>
|
||||||
|
</h2>
|
||||||
|
<pre>
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Browser Request │
|
||||||
|
│ http://room.spr.local.ar/ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Nginx │
|
||||||
|
│ │
|
||||||
|
│ 1. Routes /spr/* → soleprint:PORT (sidebar assets + API) │
|
||||||
|
│ 2. Routes /* → frontend:PORT (app pages) │
|
||||||
|
│ 3. Injects CSS+JS into HTML responses via sub_filter │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Browser Renders │
|
||||||
|
│ │
|
||||||
|
│ 1. Page loads with injected CSS (sidebar styles ready) │
|
||||||
|
│ 2. sidebar.js executes (deferred, after DOM ready) │
|
||||||
|
│ 3. JS fetches /spr/api/sidebar/config │
|
||||||
|
│ 4. JS creates sidebar DOM elements and injects into page │
|
||||||
|
│ 5. Sidebar appears on left side, pushes content with margin │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Key Design Decisions</span>
|
||||||
|
<span class="lang-es">Decisiones de Diseno Clave</span>
|
||||||
|
</h2>
|
||||||
|
<div class="findings-grid">
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3><span class="lang-en">Why nginx sub_filter?</span><span class="lang-es">Por que nginx sub_filter?</span></h3>
|
||||||
|
<p class="lang-en"><strong>Framework agnostic</strong>: Works with any frontend. No app changes needed. Easy to disable.</p>
|
||||||
|
<p class="lang-es"><strong>Agnostico de framework</strong>: Funciona con cualquier frontend. Sin cambios en la app. Facil de deshabilitar.</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3><span class="lang-en">Why inject into </head>?</span><span class="lang-es">Por que inyectar en </head>?</span></h3>
|
||||||
|
<p class="lang-en">Next.js and streaming SSR may not include </body> in initial response. </head> is always present.</p>
|
||||||
|
<p class="lang-es">Next.js y SSR streaming pueden no incluir </body> en la respuesta inicial. </head> siempre esta presente.</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3><span class="lang-en">Why JS instead of iframe?</span><span class="lang-es">Por que JS en vez de iframe?</span></h3>
|
||||||
|
<p class="lang-en">No isolation issues, better UX (no double scrollbars), simpler CSS with margin-left.</p>
|
||||||
|
<p class="lang-es">Sin problemas de aislamiento, mejor UX (sin doble scrollbar), CSS mas simple con margin-left.</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Nginx Configuration</span>
|
||||||
|
<span class="lang-es">Configuracion de Nginx</span>
|
||||||
|
</h2>
|
||||||
|
<p class="lang-en">The nginx config injects CSS+JS into HTML responses:</p>
|
||||||
|
<p class="lang-es">La config de nginx inyecta CSS+JS en las respuestas HTML:</p>
|
||||||
|
<pre><code>location / {
|
||||||
|
proxy_pass http://frontend:PORT;
|
||||||
|
proxy_set_header Accept-Encoding ""; # Required for sub_filter
|
||||||
|
|
||||||
|
# Inject sidebar
|
||||||
|
sub_filter '</head>'
|
||||||
|
'<link rel="stylesheet" href="/spr/sidebar.css">
|
||||||
|
<script src="/spr/sidebar.js" defer></script></head>';
|
||||||
|
sub_filter_once off;
|
||||||
|
sub_filter_types text/html;
|
||||||
|
}</code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Port Allocation</span>
|
||||||
|
<span class="lang-es">Asignacion de Puertos</span>
|
||||||
|
</h2>
|
||||||
|
<p class="lang-en">Each room uses unique ports for concurrent operation:</p>
|
||||||
|
<p class="lang-es">Cada room usa puertos unicos para operacion concurrente:</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Room</th><th>Soleprint</th><th>Frontend</th><th>Backend</th></tr>
|
||||||
|
<tr><td>amar</td><td>12000</td><td>3000</td><td>8001</td></tr>
|
||||||
|
<tr><td>dlt</td><td>12010</td><td>3010</td><td>-</td></tr>
|
||||||
|
<tr><td>sample</td><td>12020</td><td>3020</td><td>8020</td></tr>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Sidebar Config API</span>
|
||||||
|
<span class="lang-es">API de Config del Sidebar</span>
|
||||||
|
</h2>
|
||||||
|
<p class="lang-en">The sidebar JS fetches configuration from <code>/spr/api/sidebar/config</code>:</p>
|
||||||
|
<p class="lang-es">El JS del sidebar obtiene configuracion de <code>/spr/api/sidebar/config</code>:</p>
|
||||||
|
<pre><code>{
|
||||||
|
"room": "amar",
|
||||||
|
"soleprint_base": "/spr",
|
||||||
|
"auth_enabled": true,
|
||||||
|
"tools": {
|
||||||
|
"artery": "/spr/artery",
|
||||||
|
"atlas": "/spr/atlas",
|
||||||
|
"station": "/spr/station"
|
||||||
|
}
|
||||||
|
}</code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>Troubleshooting</h2>
|
||||||
|
<div class="findings-grid">
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3><span class="lang-en">Sidebar not appearing</span><span class="lang-es">Sidebar no aparece</span></h3>
|
||||||
|
<p class="lang-en">Check if soleprint is running. Verify nginx has <code>Accept-Encoding ""</code>. Hard refresh (Ctrl+Shift+R).</p>
|
||||||
|
<p class="lang-es">Verificar que soleprint esta corriendo. Verificar que nginx tiene <code>Accept-Encoding ""</code>. Refresco forzado (Ctrl+Shift+R).</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3><span class="lang-en">sub_filter not working</span><span class="lang-es">sub_filter no funciona</span></h3>
|
||||||
|
<p class="lang-en">Ensure <code>proxy_set_header Accept-Encoding ""</code> is set. Check response is <code>text/html</code>.</p>
|
||||||
|
<p class="lang-es">Asegurar que <code>proxy_set_header Accept-Encoding ""</code> esta seteado. Verificar que la respuesta es <code>text/html</code>.</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p><a href="../"><span class="lang-en">← Back to index</span><span class="lang-es">← Volver al indice</span></a></p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
314
docs/artery/index.html
Normal file
314
docs/artery/index.html
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Artery - Soleprint</title>
|
||||||
|
<link rel="stylesheet" href="../architecture/styles.css" />
|
||||||
|
<style>
|
||||||
|
.composition {
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: 2px solid #b91c1c;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.composition h3 {
|
||||||
|
margin: 0 0 0.75rem 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #fca5a5;
|
||||||
|
}
|
||||||
|
.composition > p {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #a3a3a3;
|
||||||
|
}
|
||||||
|
.components {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
.component {
|
||||||
|
background: #0a0a0a;
|
||||||
|
border: 1px solid #3f3f3f;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.component h4 {
|
||||||
|
margin: 0 0 0.25rem 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: #fca5a5;
|
||||||
|
}
|
||||||
|
.component p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #a3a3a3;
|
||||||
|
}
|
||||||
|
.model-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="../lang-toggle.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div id="lang-toggle"></div>
|
||||||
|
<h1>Artery</h1>
|
||||||
|
<p class="subtitle">Todo lo vital</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Model</span
|
||||||
|
><span class="lang-es">Modelo</span>
|
||||||
|
</h2>
|
||||||
|
<div class="model-grid">
|
||||||
|
<div class="composition">
|
||||||
|
<h3>Pulse</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Composed data flow: a vein configured for a room
|
||||||
|
with storage
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Flujo de datos compuesto: vein configurado para un
|
||||||
|
room con almacenamiento
|
||||||
|
</p>
|
||||||
|
<div class="components">
|
||||||
|
<div class="component">
|
||||||
|
<h4>Vein</h4>
|
||||||
|
<p class="lang-en">API connector</p>
|
||||||
|
<p class="lang-es">Conector API</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>Room</h4>
|
||||||
|
<p class="lang-en">Config/env</p>
|
||||||
|
<p class="lang-es">Config/entorno</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>Depot</h4>
|
||||||
|
<p class="lang-en">Data storage</p>
|
||||||
|
<p class="lang-es">Almacenamiento</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="composition">
|
||||||
|
<h3>Shunt</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Mock connector for testing - same interface, fake
|
||||||
|
data
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Conector mock para testing - misma interfaz, datos
|
||||||
|
falsos
|
||||||
|
</p>
|
||||||
|
<div class="components">
|
||||||
|
<div class="component">
|
||||||
|
<h4>Vein Interface</h4>
|
||||||
|
<p class="lang-en">Same API shape</p>
|
||||||
|
<p class="lang-es">Misma forma de API</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>Mock Data</h4>
|
||||||
|
<p class="lang-en">Fake responses</p>
|
||||||
|
<p class="lang-es">Respuestas falsas</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="composition">
|
||||||
|
<h3>Plexus</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Full application when you need more than data flow
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Aplicacion completa cuando necesitas mas que flujo
|
||||||
|
de datos
|
||||||
|
</p>
|
||||||
|
<div class="components">
|
||||||
|
<div class="component">
|
||||||
|
<h4>Backend</h4>
|
||||||
|
<p>FastAPI server</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>Frontend</h4>
|
||||||
|
<p>Web UI</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>Infra</h4>
|
||||||
|
<p class="lang-en">DB, queues, etc</p>
|
||||||
|
<p class="lang-es">DB, colas, etc</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Architecture</span
|
||||||
|
><span class="lang-es">Arquitectura</span>
|
||||||
|
</h2>
|
||||||
|
<img
|
||||||
|
src="../architecture/02-artery-hierarchy.svg"
|
||||||
|
alt="Artery Hierarchy"
|
||||||
|
style="
|
||||||
|
max-width: 100%;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Components</span
|
||||||
|
><span class="lang-es">Componentes</span>
|
||||||
|
</h2>
|
||||||
|
<div class="findings-grid">
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Vein</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Stateless API connector. Connects to external
|
||||||
|
services like Google Sheets, Jira, Slack. Pure data
|
||||||
|
flow - no state, no storage.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Conector API sin estado. Conecta a servicios
|
||||||
|
externos como Google Sheets, Jira, Slack. Flujo de
|
||||||
|
datos puro.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Shunt</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Mock connector for testing. Same interface as a vein
|
||||||
|
but returns fake data.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Conector mock para testing. Misma interfaz que un
|
||||||
|
vein pero devuelve datos falsos.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Pulse</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Composed data flow. Formula:
|
||||||
|
<strong>Vein + Room + Depot</strong>.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Flujo de datos compuesto. Formula:
|
||||||
|
<strong>Vein + Room + Depot</strong>.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Plexus</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Full application with backend, frontend, and
|
||||||
|
infrastructure.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Aplicacion completa con backend, frontend e
|
||||||
|
infraestructura.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Shared Components</span
|
||||||
|
><span class="lang-es">Componentes Compartidos</span>
|
||||||
|
</h2>
|
||||||
|
<div class="findings-grid">
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Room</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Runtime environment configuration. Each room is an
|
||||||
|
isolated instance with its own config and
|
||||||
|
credentials.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Configuracion del entorno. Cada room es una
|
||||||
|
instancia aislada con su propia config y
|
||||||
|
credenciales.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Depot</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Data storage / provisions. JSON files, configs,
|
||||||
|
cached responses.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Almacenamiento de datos. Archivos JSON, configs,
|
||||||
|
respuestas cacheadas.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Available Veins</span
|
||||||
|
><span class="lang-es">Veins Disponibles</span>
|
||||||
|
</h2>
|
||||||
|
<div class="findings-grid">
|
||||||
|
<a
|
||||||
|
href="../veins/index.html"
|
||||||
|
class="finding-card"
|
||||||
|
style="text-decoration: none"
|
||||||
|
>
|
||||||
|
<h3>Google</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Google Sheets API. OAuth authentication, read/write
|
||||||
|
spreadsheets.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Google Sheets API. Autenticacion OAuth,
|
||||||
|
leer/escribir hojas de calculo.
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="../veins/index.html"
|
||||||
|
class="finding-card"
|
||||||
|
style="text-decoration: none"
|
||||||
|
>
|
||||||
|
<h3>Jira</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Jira Cloud API. Query issues, projects, sprints.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Jira Cloud API. Consultar issues, proyectos,
|
||||||
|
sprints.
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="../veins/index.html"
|
||||||
|
class="finding-card"
|
||||||
|
style="text-decoration: none"
|
||||||
|
>
|
||||||
|
<h3>Slack</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Slack API. Channels, messages, users.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Slack API. Canales, mensajes, usuarios.
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>
|
||||||
|
<a href="../"
|
||||||
|
><span class="lang-en">← Back to index</span
|
||||||
|
><span class="lang-es">← Volver al indice</span></a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
209
docs/atlas/index.html
Normal file
209
docs/atlas/index.html
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Atlas - Soleprint</title>
|
||||||
|
<link rel="stylesheet" href="../architecture/styles.css" />
|
||||||
|
<style>
|
||||||
|
.composition {
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: 2px solid #15803d;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.composition h3 {
|
||||||
|
margin: 0 0 0.75rem 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #86efac;
|
||||||
|
}
|
||||||
|
.composition > p {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #a3a3a3;
|
||||||
|
}
|
||||||
|
.components {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
.component {
|
||||||
|
background: #0a0a0a;
|
||||||
|
border: 1px solid #3f3f3f;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.component h4 {
|
||||||
|
margin: 0 0 0.25rem 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: #86efac;
|
||||||
|
}
|
||||||
|
.component p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #a3a3a3;
|
||||||
|
}
|
||||||
|
.model-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="../lang-toggle.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div id="lang-toggle"></div>
|
||||||
|
<h1>Atlas</h1>
|
||||||
|
<p class="subtitle">
|
||||||
|
<span class="lang-en">Actionable Documentation</span
|
||||||
|
><span class="lang-es">Documentacion Accionable</span>
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Model</span
|
||||||
|
><span class="lang-es">Modelo</span>
|
||||||
|
</h2>
|
||||||
|
<div class="model-grid">
|
||||||
|
<div class="composition">
|
||||||
|
<h3>Plain Book</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Static documentation with an index page
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Documentacion estatica con una pagina indice
|
||||||
|
</p>
|
||||||
|
<div class="components">
|
||||||
|
<div class="component">
|
||||||
|
<h4>index.html</h4>
|
||||||
|
<p class="lang-en">Entry point</p>
|
||||||
|
<p class="lang-es">Punto de entrada</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>Depot</h4>
|
||||||
|
<p class="lang-en">Static content</p>
|
||||||
|
<p class="lang-es">Contenido estatico</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="composition">
|
||||||
|
<h3>Templated Book</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Dynamic docs from template + data elements
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Docs dinamicos desde template + elementos de datos
|
||||||
|
</p>
|
||||||
|
<div class="components">
|
||||||
|
<div class="component">
|
||||||
|
<h4>Template</h4>
|
||||||
|
<p class="lang-en">Jinja2 pattern</p>
|
||||||
|
<p class="lang-es">Patron Jinja2</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>Depot</h4>
|
||||||
|
<p class="lang-en">Data elements</p>
|
||||||
|
<p class="lang-es">Elementos de datos</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Architecture</span
|
||||||
|
><span class="lang-es">Arquitectura</span>
|
||||||
|
</h2>
|
||||||
|
<img
|
||||||
|
src="../architecture/01-system-overview.svg"
|
||||||
|
alt="System Overview"
|
||||||
|
style="
|
||||||
|
max-width: 100%;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Components</span
|
||||||
|
><span class="lang-es">Componentes</span>
|
||||||
|
</h2>
|
||||||
|
<div class="findings-grid">
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Book</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Collection of related documentation. Can be plain
|
||||||
|
(static HTML) or templated (template + depot
|
||||||
|
elements).
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Coleccion de documentacion relacionada. Puede ser
|
||||||
|
plain (HTML estatico) o templated (template +
|
||||||
|
elementos de depot).
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Template</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Jinja2 templates that generate documentation. Define
|
||||||
|
the structure once, fill with data from depot.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Templates Jinja2 que generan documentacion. Definen
|
||||||
|
la estructura una vez, llenan con datos del depot.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Shared Components</span
|
||||||
|
><span class="lang-es">Componentes Compartidos</span>
|
||||||
|
</h2>
|
||||||
|
<div class="findings-grid">
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Room</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Runtime environment configuration. Each room can
|
||||||
|
have its own atlas with project-specific books.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Configuracion del entorno. Cada room puede tener su
|
||||||
|
propio atlas con books especificos del proyecto.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Depot</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Data storage. For plain books: static files. For
|
||||||
|
templated books: elements that fill the template.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Almacenamiento de datos. Para plain books: archivos
|
||||||
|
estaticos. Para templated books: elementos que
|
||||||
|
llenan el template.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>
|
||||||
|
<a href="../"
|
||||||
|
><span class="lang-en">← Back to index</span
|
||||||
|
><span class="lang-es">← Volver al indice</span></a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
265
docs/index.html
265
docs/index.html
@@ -5,39 +5,151 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Soleprint - Documentation</title>
|
<title>Soleprint - Documentation</title>
|
||||||
<link rel="stylesheet" href="architecture/styles.css" />
|
<link rel="stylesheet" href="architecture/styles.css" />
|
||||||
|
<style>
|
||||||
|
.one-liner {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
.demo-link {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--bg-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.demo-link:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
.systems-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
.system-card {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
border-left: 3px solid var(--accent);
|
||||||
|
}
|
||||||
|
.system-card h3 {
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
.system-card p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
.arch-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
.arch-card {
|
||||||
|
display: block;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: transform 0.15s;
|
||||||
|
}
|
||||||
|
.arch-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
.arch-card h4 {
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
.arch-card p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="lang-toggle.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
|
<div id="lang-toggle"></div>
|
||||||
<h1>Soleprint</h1>
|
<h1>Soleprint</h1>
|
||||||
<p class="subtitle">
|
<p class="subtitle">Cada paso deja huella</p>
|
||||||
Cada paso deja huella / Each step leaves a mark
|
<p class="one-liner">
|
||||||
|
<span class="lang-en"
|
||||||
|
>Pluggable stuff to tackle any challenge</span
|
||||||
|
>
|
||||||
|
<span class="lang-es"
|
||||||
|
>Piezas enchufables para cualquier desafio</span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
|
<a href="https://sample.spr.mcrn.ar" class="demo-link">
|
||||||
|
<span class="lang-en">Try the Demo</span>
|
||||||
|
<span class="lang-es">Ver Demo</span>
|
||||||
|
</a>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<section class="findings-section">
|
<section class="findings-section">
|
||||||
<h2>Documentation</h2>
|
<h2>
|
||||||
<div class="findings-grid">
|
<span class="lang-en">The Three Systems</span>
|
||||||
|
<span class="lang-es">Los Tres Sistemas</span>
|
||||||
|
</h2>
|
||||||
|
<div class="systems-grid">
|
||||||
<a
|
<a
|
||||||
href="architecture/index.html"
|
href="artery/"
|
||||||
class="finding-card"
|
class="system-card"
|
||||||
style="text-decoration: none"
|
style="text-decoration: none"
|
||||||
>
|
>
|
||||||
<h3>Architecture</h3>
|
<h3>Artery</h3>
|
||||||
<p>
|
<p class="lang-en">
|
||||||
System overview, connector hierarchy, build flow,
|
API connectors and data flow. Veins for real APIs,
|
||||||
and room configuration diagrams.
|
shunts for mocks.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Conectores y flujo de datos. Veins para APIs reales,
|
||||||
|
shunts para mocks.
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="veins/index.html"
|
href="atlas/"
|
||||||
class="finding-card"
|
class="system-card"
|
||||||
style="text-decoration: none"
|
style="text-decoration: none"
|
||||||
>
|
>
|
||||||
<h3>Veins & Shunts</h3>
|
<h3>Atlas</h3>
|
||||||
<p>
|
<p class="lang-en">
|
||||||
API connectors (Jira, Slack, Google) and mock
|
Actionable documentation. Templates that generate
|
||||||
connectors for testing.
|
living docs.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Documentacion accionable. Templates que generan docs
|
||||||
|
vivos.
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="station/"
|
||||||
|
class="system-card"
|
||||||
|
style="text-decoration: none"
|
||||||
|
>
|
||||||
|
<h3>Station</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Tools and monitors. Everything to run, test, and
|
||||||
|
observe.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Herramientas y monitores. Todo para correr, testear,
|
||||||
|
y observar.
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -45,53 +157,106 @@
|
|||||||
|
|
||||||
<section class="findings-section">
|
<section class="findings-section">
|
||||||
<h2>Quick Start</h2>
|
<h2>Quick Start</h2>
|
||||||
<div class="finding-card">
|
<pre>
|
||||||
<h3>Build & Run</h3>
|
# Clone
|
||||||
<pre
|
git clone https://git.mcrn.ar/soleprint
|
||||||
style="
|
cd soleprint
|
||||||
background: var(--bg-primary);
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow-x: auto;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
# Build standalone
|
|
||||||
python build.py
|
|
||||||
cd gen/standalone && .venv/bin/python run.py
|
|
||||||
|
|
||||||
# Build with room config
|
# Setup (once)
|
||||||
python build.py --cfg amar
|
python -m venv .venv && source .venv/bin/activate
|
||||||
cd gen/amar && .venv/bin/python run.py
|
pip install -r requirements.txt
|
||||||
|
|
||||||
# Visit http://localhost:12000</pre
|
# Option 1: Build standalone (soleprint only)
|
||||||
|
python build.py --cfg standalone
|
||||||
|
cd gen/standalone && ./ctrl/start.sh
|
||||||
|
# Visit http://localhost:12000
|
||||||
|
|
||||||
|
# Option 2: Build managed room (soleprint + your app)
|
||||||
|
python build.py --cfg myroom
|
||||||
|
cd gen/myroom && ./ctrl/start.sh
|
||||||
|
# Visit http://myroom.spr.local.ar
|
||||||
|
|
||||||
|
# Option 3: Use the installer (coming soon)</pre
|
||||||
|
>
|
||||||
|
|
||||||
|
<p style="margin-top: 1rem">
|
||||||
|
<span class="lang-en">Minimal config example:</span>
|
||||||
|
<span class="lang-es">Ejemplo de config minima:</span>
|
||||||
|
</p>
|
||||||
|
<pre>
|
||||||
|
// cfg/myroom/config.json
|
||||||
|
{
|
||||||
|
"room": "myroom",
|
||||||
|
"artery": {
|
||||||
|
"veins": ["google", "jira", "slack"],
|
||||||
|
"shunts": ["mercadopago"]
|
||||||
|
},
|
||||||
|
"atlas": {
|
||||||
|
"books": ["gherkin", "feature-flow"]
|
||||||
|
},
|
||||||
|
"station": {
|
||||||
|
"tools": ["tester", "modelgen"],
|
||||||
|
"monitors": ["databrowse"]
|
||||||
|
}
|
||||||
|
}</pre
|
||||||
>
|
>
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="findings-section">
|
<section class="findings-section">
|
||||||
<h2>What Soleprint Solves</h2>
|
<h2>
|
||||||
|
<span class="lang-en">Architecture</span>
|
||||||
|
<span class="lang-es">Arquitectura</span>
|
||||||
|
</h2>
|
||||||
|
<p class="lang-en">Deep dive into how soleprint works.</p>
|
||||||
|
<p class="lang-es">Profundizando en como funciona soleprint.</p>
|
||||||
<div class="findings-grid">
|
<div class="findings-grid">
|
||||||
<article class="finding-card">
|
<a
|
||||||
<h3>Freelance Standardization</h3>
|
href="architecture/"
|
||||||
<p>Consistent framework across projects.</p>
|
class="finding-card"
|
||||||
</article>
|
style="text-decoration: none"
|
||||||
<article class="finding-card">
|
>
|
||||||
<h3>Missing Infrastructure</h3>
|
<h3>
|
||||||
<p>
|
<span class="lang-en">Architecture Diagrams →</span
|
||||||
Mock systems not ready yet - DBs, APIs, Kubernetes.
|
><span class="lang-es"
|
||||||
|
>Diagramas de Arquitectura →</span
|
||||||
|
>
|
||||||
|
</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
System overview, artery hierarchy, build flow, room
|
||||||
|
configuration.
|
||||||
</p>
|
</p>
|
||||||
</article>
|
<p class="lang-es">
|
||||||
<article class="finding-card">
|
Vista general, jerarquia de artery, flujo de build,
|
||||||
<h3>Reliable Testing</h3>
|
configuracion de rooms.
|
||||||
<p>BDD -> Gherkin -> Tests.</p>
|
</p>
|
||||||
</article>
|
</a>
|
||||||
|
<a
|
||||||
|
href="architecture/sidebar-injection.html"
|
||||||
|
class="finding-card"
|
||||||
|
style="text-decoration: none"
|
||||||
|
>
|
||||||
|
<h3>
|
||||||
|
<span class="lang-en">Sidebar Injection →</span
|
||||||
|
><span class="lang-es">Inyeccion de Sidebar →</span>
|
||||||
|
</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
How the managed room sidebar works with nginx +
|
||||||
|
JavaScript.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Como funciona el sidebar del managed room con nginx
|
||||||
|
+ JavaScript.
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p>Soleprint - Development Workflow Platform</p>
|
<p>
|
||||||
|
<a href="https://soleprint.mcrn.ar">soleprint.mcrn.ar</a> ·
|
||||||
|
<a href="https://sample.spr.mcrn.ar">Live Demo</a>
|
||||||
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
54
docs/lang-toggle.js
Normal file
54
docs/lang-toggle.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// Language toggle for soleprint docs
|
||||||
|
// Include this script and add: <div id="lang-toggle"></div> in header
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
function setLang(lang) {
|
||||||
|
localStorage.setItem("spr-docs-lang", lang);
|
||||||
|
document.documentElement.lang = lang;
|
||||||
|
document.querySelectorAll(".lang-toggle button").forEach((btn) => {
|
||||||
|
btn.classList.toggle("active", btn.dataset.lang === lang);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const currentLang = localStorage.getItem("spr-docs-lang") || "en";
|
||||||
|
|
||||||
|
// Inject toggle HTML
|
||||||
|
const container = document.getElementById("lang-toggle");
|
||||||
|
if (container) {
|
||||||
|
container.className = "lang-toggle";
|
||||||
|
|
||||||
|
const btnEn = document.createElement("button");
|
||||||
|
btnEn.textContent = "EN";
|
||||||
|
btnEn.dataset.lang = "en";
|
||||||
|
btnEn.addEventListener("click", () => setLang("en"));
|
||||||
|
|
||||||
|
const btnEs = document.createElement("button");
|
||||||
|
btnEs.textContent = "ES";
|
||||||
|
btnEs.dataset.lang = "es";
|
||||||
|
btnEs.addEventListener("click", () => setLang("es"));
|
||||||
|
|
||||||
|
container.appendChild(btnEn);
|
||||||
|
container.appendChild(btnEs);
|
||||||
|
}
|
||||||
|
|
||||||
|
setLang(currentLang);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inject styles
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.textContent = `
|
||||||
|
.lang-toggle { position: absolute; top: 1.5rem; right: 2rem; display: flex; border: 1px solid #888; border-radius: 4px; overflow: hidden; }
|
||||||
|
.lang-toggle button { background: #1a1a1a; border: none; color: #888; padding: 0.4rem 0.8rem; font-family: inherit; font-size: 0.75rem; cursor: pointer; }
|
||||||
|
.lang-toggle button:first-child { border-right: 1px solid #888; }
|
||||||
|
.lang-toggle button:hover { background: #0a0a0a; color: #fff; }
|
||||||
|
.lang-toggle button.active { background: var(--accent, #b91c1c); color: #fff; }
|
||||||
|
header { position: relative; }
|
||||||
|
.lang-en, .lang-es { display: none; }
|
||||||
|
html[lang="en"] .lang-en { display: block; }
|
||||||
|
html[lang="es"] .lang-es { display: block; }
|
||||||
|
html[lang="en"] span.lang-en { display: inline; }
|
||||||
|
html[lang="es"] span.lang-es { display: inline; }
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
})();
|
||||||
336
docs/station/index.html
Normal file
336
docs/station/index.html
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Station - Soleprint</title>
|
||||||
|
<link rel="stylesheet" href="../architecture/styles.css" />
|
||||||
|
<style>
|
||||||
|
.composition {
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: 2px solid #7c3aed;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.composition h3 {
|
||||||
|
margin: 0 0 0.75rem 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #c4b5fd;
|
||||||
|
}
|
||||||
|
.composition > p {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #a3a3a3;
|
||||||
|
}
|
||||||
|
.components {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
.component {
|
||||||
|
background: #0a0a0a;
|
||||||
|
border: 1px solid #3f3f3f;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.component h4 {
|
||||||
|
margin: 0 0 0.25rem 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: #c4b5fd;
|
||||||
|
}
|
||||||
|
.component p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #a3a3a3;
|
||||||
|
}
|
||||||
|
.model-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="../lang-toggle.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div id="lang-toggle"></div>
|
||||||
|
<h1>Station</h1>
|
||||||
|
<p class="subtitle">
|
||||||
|
<span class="lang-en">Monitors, Environments & Tools</span
|
||||||
|
><span class="lang-es">Monitores, Entornos y Herramientas</span>
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Model</span
|
||||||
|
><span class="lang-es">Modelo</span>
|
||||||
|
</h2>
|
||||||
|
<div class="model-grid">
|
||||||
|
<div class="composition">
|
||||||
|
<h3>Desk</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Control center - collection of monitors
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Centro de control - coleccion de monitores
|
||||||
|
</p>
|
||||||
|
<div class="components">
|
||||||
|
<div class="component">
|
||||||
|
<h4>Monitor</h4>
|
||||||
|
<p>Web UI</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>Monitor</h4>
|
||||||
|
<p>Web UI</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>...</h4>
|
||||||
|
<p></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="composition">
|
||||||
|
<h3>Monitor</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Web interface - always running, always watching
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Interfaz web - siempre corriendo, siempre observando
|
||||||
|
</p>
|
||||||
|
<div class="components">
|
||||||
|
<div class="component">
|
||||||
|
<h4>Web UI</h4>
|
||||||
|
<p>Dashboard</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>Room</h4>
|
||||||
|
<p class="lang-en">Config/env</p>
|
||||||
|
<p class="lang-es">Config/entorno</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>Depot</h4>
|
||||||
|
<p class="lang-en">Data source</p>
|
||||||
|
<p class="lang-es">Fuente de datos</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="composition">
|
||||||
|
<h3>Tool</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
CLI utility - run once, get results
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Utilidad CLI - ejecutar una vez, obtener resultados
|
||||||
|
</p>
|
||||||
|
<div class="components">
|
||||||
|
<div class="component">
|
||||||
|
<h4>CLI</h4>
|
||||||
|
<p class="lang-en">Command interface</p>
|
||||||
|
<p class="lang-es">Interfaz de comandos</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>Room</h4>
|
||||||
|
<p class="lang-en">Config/env</p>
|
||||||
|
<p class="lang-es">Config/entorno</p>
|
||||||
|
</div>
|
||||||
|
<div class="component">
|
||||||
|
<h4>Depot</h4>
|
||||||
|
<p class="lang-en">Output storage</p>
|
||||||
|
<p class="lang-es">Almacenamiento de salida</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Architecture</span
|
||||||
|
><span class="lang-es">Arquitectura</span>
|
||||||
|
</h2>
|
||||||
|
<img
|
||||||
|
src="../architecture/01-system-overview.svg"
|
||||||
|
alt="System Overview"
|
||||||
|
style="
|
||||||
|
max-width: 100%;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Components</span
|
||||||
|
><span class="lang-es">Componentes</span>
|
||||||
|
</h2>
|
||||||
|
<div class="findings-grid">
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Desk</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Collection of monitors. Your control center with all
|
||||||
|
the views you need.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Coleccion de monitores. Tu centro de control con
|
||||||
|
todas las vistas que necesitas.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Monitor</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Web interfaces for observation. Data browsers,
|
||||||
|
dashboards, log viewers. Always running.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Interfaces web para observacion. Navegadores de
|
||||||
|
datos, dashboards, visores de logs. Siempre
|
||||||
|
corriendo.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Tool</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
CLI utilities and scripts. Code generators, test
|
||||||
|
runners, infra provisioners. Run once, get results.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Utilidades CLI y scripts. Generadores de codigo,
|
||||||
|
test runners, provisioners de infra. Ejecutar una
|
||||||
|
vez, obtener resultados.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Shared Components</span
|
||||||
|
><span class="lang-es">Componentes Compartidos</span>
|
||||||
|
</h2>
|
||||||
|
<div class="findings-grid">
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Room</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Runtime environment configuration. Tools and
|
||||||
|
monitors are configured per-room for isolation.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Configuracion del entorno. Tools y monitors se
|
||||||
|
configuran por room para aislamiento.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Depot</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Data storage. For tools: output files, results. For
|
||||||
|
monitors: data to display.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Almacenamiento de datos. Para tools: archivos de
|
||||||
|
salida, resultados. Para monitors: datos a mostrar.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Available Tools</span
|
||||||
|
><span class="lang-es">Tools Disponibles</span>
|
||||||
|
</h2>
|
||||||
|
<div class="findings-grid">
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Tester</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
API and Playwright test runner. Discover tests, run
|
||||||
|
them, collect artifacts.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Test runner de API y Playwright. Descubrir tests,
|
||||||
|
ejecutarlos, recolectar artefactos.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>ModelGen</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Generate model diagrams from code. Introspect
|
||||||
|
soleprint structure, output SVG.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Generar diagramas de modelo desde codigo.
|
||||||
|
Introspeccionar estructura de soleprint, generar
|
||||||
|
SVG.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>Infra</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Infrastructure provisioners. AWS, GCP, DigitalOcean
|
||||||
|
deployment helpers.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Provisioners de infraestructura. Helpers de deploy
|
||||||
|
para AWS, GCP, DigitalOcean.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>DataGen</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Test data generation. Create realistic fake data for
|
||||||
|
development.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Generacion de datos de test. Crear datos falsos
|
||||||
|
realistas para desarrollo.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>DataBrowse</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Navigable data model graphs generated from existing
|
||||||
|
models.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Grafos de modelo de datos navegables generados desde
|
||||||
|
modelos existentes.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="findings-section">
|
||||||
|
<h2>
|
||||||
|
<span class="lang-en">Available Monitors</span
|
||||||
|
><span class="lang-es">Monitors Disponibles</span>
|
||||||
|
</h2>
|
||||||
|
<div class="findings-grid">
|
||||||
|
<article class="finding-card">
|
||||||
|
<h3>DataBrowse</h3>
|
||||||
|
<p class="lang-en">
|
||||||
|
Navigable data model graphs generated from existing
|
||||||
|
models.
|
||||||
|
</p>
|
||||||
|
<p class="lang-es">
|
||||||
|
Grafos de modelo de datos navegables generados desde
|
||||||
|
modelos existentes.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>
|
||||||
|
<a href="../"
|
||||||
|
><span class="lang-en">← Back to index</span
|
||||||
|
><span class="lang-es">← Volver al indice</span></a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -17,4 +17,4 @@ COPY . .
|
|||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
CMD ["uvicorn", "run:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
|
|||||||
@@ -419,6 +419,21 @@
|
|||||||
<div class="component"><h4>Depot</h4></div>
|
<div class="component"><h4>Depot</h4></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="composition" style="margin-top: 1rem">
|
||||||
|
<h3>Shunt</h3>
|
||||||
|
<div class="components">
|
||||||
|
<div class="component"><h4>Vein Interface</h4></div>
|
||||||
|
<div class="component"><h4>Mock Data</h4></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="composition" style="margin-top: 1rem">
|
||||||
|
<h3>Plexus</h3>
|
||||||
|
<div class="components">
|
||||||
|
<div class="component"><h4>Backend</h4></div>
|
||||||
|
<div class="component"><h4>Frontend</h4></div>
|
||||||
|
<div class="component"><h4>Infra</h4></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
|||||||
0
soleprint/artery/shunts/__init__.py
Normal file → Executable file
0
soleprint/artery/shunts/__init__.py
Normal file → Executable file
0
soleprint/artery/shunts/example/README.md
Normal file → Executable file
0
soleprint/artery/shunts/example/README.md
Normal file → Executable file
0
soleprint/artery/shunts/example/depot/responses.json
Normal file → Executable file
0
soleprint/artery/shunts/example/depot/responses.json
Normal file → Executable file
0
soleprint/artery/shunts/example/main.py
Normal file → Executable file
0
soleprint/artery/shunts/example/main.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/.env.example
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/.env.example
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/README.md
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/README.md
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/__init__.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/__init__.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/api/__init__.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/api/__init__.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/api/routes.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/api/routes.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/core/__init__.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/core/__init__.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/core/config.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/core/config.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/main.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/main.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/requirements.txt
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/requirements.txt
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/run.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/run.py
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/templates/index.html
Normal file → Executable file
0
soleprint/artery/shunts/mercadopago/templates/index.html
Normal file → Executable file
@@ -144,6 +144,9 @@ class GoogleOAuth:
|
|||||||
"name": userinfo.get("name"),
|
"name": userinfo.get("name"),
|
||||||
"picture": userinfo.get("picture"),
|
"picture": userinfo.get("picture"),
|
||||||
"hd": userinfo.get("hd"), # Hosted domain (Google Workspace)
|
"hd": userinfo.get("hd"), # Hosted domain (Google Workspace)
|
||||||
|
# Include tokens for storage
|
||||||
|
"access_token": credentials.token,
|
||||||
|
"refresh_token": credentials.refresh_token,
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh_access_token(self, refresh_token: str) -> dict:
|
def refresh_access_token(self, refresh_token: str) -> dict:
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class AuthConfig(BaseModel):
|
|||||||
enabled: bool = False
|
enabled: bool = False
|
||||||
provider: str = "google" # Vein name to use for auth
|
provider: str = "google" # Vein name to use for auth
|
||||||
allowed_domains: list[str] = [] # Empty = allow any domain
|
allowed_domains: list[str] = [] # Empty = allow any domain
|
||||||
|
allowed_emails: list[str] = [] # Specific emails to allow
|
||||||
session_secret: str = "" # Required if enabled, can be "ENV:VAR_NAME"
|
session_secret: str = "" # Required if enabled, can be "ENV:VAR_NAME"
|
||||||
session_timeout_hours: int = 24
|
session_timeout_hours: int = 24
|
||||||
login_redirect: str = "/"
|
login_redirect: str = "/"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ Authentication middleware for route protection.
|
|||||||
Generic middleware, provider-agnostic.
|
Generic middleware, provider-agnostic.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from starlette.middleware.base import BaseHTTPMiddleware
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
@@ -11,6 +12,10 @@ from starlette.responses import JSONResponse, RedirectResponse
|
|||||||
|
|
||||||
from .config import AuthConfig
|
from .config import AuthConfig
|
||||||
|
|
||||||
|
# Local dev bypass - set via environment variable only, can't be triggered remotely
|
||||||
|
AUTH_BYPASS = os.environ.get("AUTH_BYPASS", "").lower() == "true"
|
||||||
|
AUTH_BYPASS_USER = os.environ.get("AUTH_BYPASS_USER", "dev@local")
|
||||||
|
|
||||||
|
|
||||||
class AuthMiddleware(BaseHTTPMiddleware):
|
class AuthMiddleware(BaseHTTPMiddleware):
|
||||||
"""
|
"""
|
||||||
@@ -31,6 +36,15 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|||||||
async def dispatch(self, request, call_next):
|
async def dispatch(self, request, call_next):
|
||||||
path = request.url.path
|
path = request.url.path
|
||||||
|
|
||||||
|
# Local dev bypass - auto-authenticate
|
||||||
|
if AUTH_BYPASS:
|
||||||
|
request.state.user = {
|
||||||
|
"email": AUTH_BYPASS_USER,
|
||||||
|
"name": "Dev User",
|
||||||
|
"domain": "local",
|
||||||
|
}
|
||||||
|
return await call_next(request)
|
||||||
|
|
||||||
# Check if route is public
|
# Check if route is public
|
||||||
if self._is_public(path):
|
if self._is_public(path):
|
||||||
return await call_next(request)
|
return await call_next(request)
|
||||||
@@ -49,14 +63,19 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|||||||
session.clear()
|
session.clear()
|
||||||
return self._unauthorized(request, "Session expired")
|
return self._unauthorized(request, "Session expired")
|
||||||
|
|
||||||
# Check domain restriction
|
# Check domain/email restriction
|
||||||
user_domain = session.get("domain")
|
user_domain = session.get("domain")
|
||||||
if self.config.allowed_domains:
|
email_allowed = user_email in self.config.allowed_emails
|
||||||
if not user_domain or user_domain not in self.config.allowed_domains:
|
domain_allowed = user_domain and user_domain in self.config.allowed_domains
|
||||||
|
no_restrictions = (
|
||||||
|
not self.config.allowed_domains and not self.config.allowed_emails
|
||||||
|
)
|
||||||
|
|
||||||
|
if not (email_allowed or domain_allowed or no_restrictions):
|
||||||
session.clear()
|
session.clear()
|
||||||
return self._unauthorized(
|
return self._unauthorized(
|
||||||
request,
|
request,
|
||||||
f"Access restricted to: {', '.join(self.config.allowed_domains)}",
|
f"Access restricted",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Attach user to request state for downstream use
|
# Attach user to request state for downstream use
|
||||||
|
|||||||
@@ -111,14 +111,17 @@ async def callback(
|
|||||||
except httpx.RequestError as e:
|
except httpx.RequestError as e:
|
||||||
raise HTTPException(500, f"Failed to contact provider: {e}")
|
raise HTTPException(500, f"Failed to contact provider: {e}")
|
||||||
|
|
||||||
# Verify domain if restricted
|
# Verify domain/email restriction
|
||||||
|
user_email = user_info.get("email")
|
||||||
user_domain = user_info.get("hd")
|
user_domain = user_info.get("hd")
|
||||||
if auth_config.allowed_domains:
|
email_allowed = user_email in auth_config.allowed_emails
|
||||||
if not user_domain or user_domain not in auth_config.allowed_domains:
|
domain_allowed = user_domain and user_domain in auth_config.allowed_domains
|
||||||
|
no_restrictions = not auth_config.allowed_domains and not auth_config.allowed_emails
|
||||||
|
|
||||||
|
if not (email_allowed or domain_allowed or no_restrictions):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
403,
|
403,
|
||||||
f"Access restricted to: {', '.join(auth_config.allowed_domains)}. "
|
f"Access restricted. Your account ({user_email}) is not authorized.",
|
||||||
f"Your account is from: {user_domain or 'personal Gmail'}",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create session
|
# Create session
|
||||||
|
|||||||
@@ -214,6 +214,36 @@
|
|||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.showcase-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.showcase-link {
|
||||||
|
display: inline-block;
|
||||||
|
background: linear-gradient(135deg, #d4a574, #b8956a);
|
||||||
|
color: #0a0a0a;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: transform 0.15s, box-shadow 0.15s;
|
||||||
|
}
|
||||||
|
.showcase-link:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(212, 165, 116, 0.3);
|
||||||
|
}
|
||||||
|
.showcase-hint {
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.showcase-hint:hover {
|
||||||
|
color: #d4a574;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% if managed %}
|
{% if managed %}
|
||||||
<link rel="stylesheet" href="/sidebar.css">
|
<link rel="stylesheet" href="/sidebar.css">
|
||||||
@@ -244,6 +274,14 @@
|
|||||||
</header>
|
</header>
|
||||||
<p class="tagline">Cada paso deja huella</p>
|
<p class="tagline">Cada paso deja huella</p>
|
||||||
|
|
||||||
|
{% if showcase_url %}
|
||||||
|
<div class="showcase-container">
|
||||||
|
<a href="{{ showcase_url }}" class="showcase-link">Managed Room Demo</a>
|
||||||
|
<a href="/artery" class="showcase-hint">what's a room?</a>
|
||||||
|
<a href="https://mariano.mcrn.ar/docs/soleprint/" class="showcase-hint">see docs</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<p class="mission" style="display: none">
|
<p class="mission" style="display: none">
|
||||||
<!-- placeholder for session alerts -->
|
<!-- placeholder for session alerts -->
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -351,6 +351,10 @@ def artery_index(request: Request):
|
|||||||
|
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
|
|
||||||
|
# Load rooms and depots
|
||||||
|
rooms = load_data("rooms.json") or []
|
||||||
|
depots = load_data("depots.json") or []
|
||||||
|
|
||||||
template = Template(html_path.read_text())
|
template = Template(html_path.read_text())
|
||||||
return HTMLResponse(
|
return HTMLResponse(
|
||||||
template.render(
|
template.render(
|
||||||
@@ -361,6 +365,8 @@ def artery_index(request: Request):
|
|||||||
pulses=pulses,
|
pulses=pulses,
|
||||||
shunts=shunts,
|
shunts=shunts,
|
||||||
plexuses=plexuses,
|
plexuses=plexuses,
|
||||||
|
rooms=rooms,
|
||||||
|
depots=depots,
|
||||||
soleprint_url="/",
|
soleprint_url="/",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -601,6 +607,7 @@ def index(request: Request):
|
|||||||
config = load_config()
|
config = load_config()
|
||||||
managed = config.get("managed", {})
|
managed = config.get("managed", {})
|
||||||
managed_url = get_managed_url(request, managed)
|
managed_url = get_managed_url(request, managed)
|
||||||
|
showcase_url = config.get("showcase_url")
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"index.html",
|
"index.html",
|
||||||
@@ -611,6 +618,7 @@ def index(request: Request):
|
|||||||
"station": "/station",
|
"station": "/station",
|
||||||
"managed": managed,
|
"managed": managed,
|
||||||
"managed_url": managed_url,
|
"managed_url": managed_url,
|
||||||
|
"showcase_url": showcase_url,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -92,18 +92,19 @@
|
|||||||
function renderSystemSection(systemKey, title) {
|
function renderSystemSection(systemKey, title) {
|
||||||
const veins = config.veins || [];
|
const veins = config.veins || [];
|
||||||
|
|
||||||
// System icon - ALWAYS shown as non-clickable separator
|
// System link with icon and label (same as soleprint)
|
||||||
let html = `
|
let html = `
|
||||||
<div class="spr-sidebar-icon" title="${title}">
|
<a href="${SPR_BASE}/${systemKey}" class="spr-sidebar-item" title="${title}">
|
||||||
${icons[systemKey]}
|
${icons[systemKey]}
|
||||||
</div>
|
<span class="label">${title}</span>
|
||||||
|
<span class="tooltip">${title}</span>
|
||||||
|
</a>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Nested elements below icon - auth-gated
|
// Nested elements below - auth-gated
|
||||||
if (user) {
|
if (user) {
|
||||||
// Logged in
|
// Logged in - show vein links under artery
|
||||||
if (systemKey === "artery") {
|
if (systemKey === "artery") {
|
||||||
// Show vein links under artery
|
|
||||||
for (const vein of veins) {
|
for (const vein of veins) {
|
||||||
html += `
|
html += `
|
||||||
<a href="${SPR_BASE}/artery/${vein}" class="spr-sidebar-item spr-vein-item" title="${vein}">
|
<a href="${SPR_BASE}/artery/${vein}" class="spr-sidebar-item spr-vein-item" title="${vein}">
|
||||||
@@ -112,14 +113,6 @@
|
|||||||
</a>
|
</a>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Atlas/Station: clickable link to main page
|
|
||||||
html += `
|
|
||||||
<a href="${SPR_BASE}/${systemKey}" class="spr-sidebar-item spr-system-link" title="${title}">
|
|
||||||
<span class="label">${title}</span>
|
|
||||||
<span class="tooltip">${title}</span>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
} else if (config.auth_enabled) {
|
} else if (config.auth_enabled) {
|
||||||
// Not logged in: show "login to access" below each system icon
|
// Not logged in: show "login to access" below each system icon
|
||||||
@@ -159,7 +152,7 @@
|
|||||||
|
|
||||||
<div class="spr-sidebar-divider"></div>
|
<div class="spr-sidebar-divider"></div>
|
||||||
|
|
||||||
<a href="${SPR_BASE}/" class="spr-sidebar-item active" title="Soleprint">
|
<a href="/spr/" class="spr-sidebar-item active" title="Soleprint">
|
||||||
${icons.soleprint}
|
${icons.soleprint}
|
||||||
<span class="label">Soleprint</span>
|
<span class="label">Soleprint</span>
|
||||||
<span class="tooltip">Soleprint</span>
|
<span class="tooltip">Soleprint</span>
|
||||||
@@ -204,9 +197,9 @@
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
// Include current page as redirect target after login
|
// Include current path as redirect target after login (relative, not full URL)
|
||||||
const redirectUrl = encodeURIComponent(window.location.href);
|
const redirectPath = window.location.pathname + window.location.search;
|
||||||
const loginUrl = `${config.auth.login_url}?redirect=${redirectUrl}`;
|
const loginUrl = `${config.auth.login_url}?redirect=${encodeURIComponent(redirectPath)}`;
|
||||||
return `
|
return `
|
||||||
<div class="spr-sidebar-user">
|
<div class="spr-sidebar-user">
|
||||||
<a href="${loginUrl}" class="spr-sidebar-item" title="Login with Google">
|
<a href="${loginUrl}" class="spr-sidebar-item" title="Login with Google">
|
||||||
|
|||||||
Reference in New Issue
Block a user