Documentacion tecnica — CreaRackSL Workspace
Referencia tecnica completa de la plataforma workspace.crearack.com.
Arquitectura
Browser → Cloudflare CDN → Cloudflare Pages (static HTML/JS)
→ Pages Functions (API) → D1 Database
→ Pages Functions (proxy) → CreaRack Pro API
→ Pages Functions (proxy) → UptimeRobot API
- Frontend: Astro 6 static output (pre-rendered HTML + React hydration islands)
- API: Cloudflare Pages Functions (serverless, TypeScript)
- Database: Cloudflare D1 (SQLite on edge, region WEUR)
- Auth: Cloudflare Access (email-based, zero-trust)
Stack detallado
| Capa | Tecnologia | Version |
|---|---|---|
| Framework | Astro | 6.1.5 |
| UI Islands | React | 19.x |
| CSS | Tailwind v4 (CSS-first) | 4.2.2 |
| Tipografia | Oswald (logo), Plus Jakarta Sans (body), JetBrains Mono (code) | @fontsource |
| Busqueda | Fuse.js | 7.3.0 |
| Build | pnpm | 10.x |
| Hosting | Cloudflare Pages | - |
| Database | Cloudflare D1 | SQLite |
| Secrets | Cloudflare Pages Secrets | - |
Base de datos D1
Database: crearacksl-workspace-db
Region: WEUR (Western Europe)
Tablas
news
id INTEGER PRIMARY KEY AUTOINCREMENT
title TEXT NOT NULL
content TEXT NOT NULL
author TEXT NOT NULL
category TEXT DEFAULT 'general'
pinned INTEGER DEFAULT 0
created_at TEXT DEFAULT datetime('now')
updated_at TEXT DEFAULT datetime('now')
tasks
id INTEGER PRIMARY KEY AUTOINCREMENT
title TEXT NOT NULL
description TEXT DEFAULT ''
assignee TEXT NOT NULL DEFAULT 'Equipo' -- Txell, Dani, Edu, Equipo
status TEXT NOT NULL DEFAULT 'pending' -- pending, in-progress, done
priority TEXT NOT NULL DEFAULT 'normal' -- low, normal, high, urgent
due_date TEXT -- ISO date or NULL
created_by TEXT NOT NULL DEFAULT 'Equipo'
created_at TEXT DEFAULT datetime('now')
updated_at TEXT DEFAULT datetime('now')
Indices: assignee, status, priority, due_date
notes
id INTEGER PRIMARY KEY AUTOINCREMENT
title TEXT NOT NULL
content TEXT NOT NULL
color TEXT NOT NULL DEFAULT 'blue' -- blue, green, yellow, pink, purple, cyan
author TEXT NOT NULL DEFAULT 'Equipo'
created_at TEXT DEFAULT datetime('now')
updated_at TEXT DEFAULT datetime('now')
Indices: author, created_at DESC
alerts
id INTEGER PRIMARY KEY AUTOINCREMENT
message TEXT NOT NULL
type TEXT NOT NULL DEFAULT 'info' -- info, warning, critical
active INTEGER NOT NULL DEFAULT 1 -- 0=inactive, 1=active
author TEXT NOT NULL DEFAULT 'Equipo'
created_at TEXT DEFAULT datetime('now')
updated_at TEXT DEFAULT datetime('now')
Indice: active. Regla: solo 1 alerta activa (POST desactiva las anteriores).
Migraciones
migrations/0001_create_news.sql
migrations/0002_create_tasks.sql
migrations/0003_create_notes.sql
migrations/0004_create_alerts.sql
Ejecutar con: npx wrangler d1 execute crearacksl-workspace-db --remote --file=./migrations/XXXX.sql
API Endpoints (Pages Functions)
Todas las APIs siguen el mismo patron:
functions/api/{resource}/index.ts→ GET (list) + POST (create)functions/api/{resource}/[id].ts→ GET (one) + PUT (update) + DELETE
CRUD APIs
| Endpoint | Metodos | Descripcion |
|---|---|---|
/api/tasks | GET, POST | Tareas. Filtros: ?assignee, ?status, ?priority, ?limit |
/api/tasks/:id | GET, PUT, DELETE | Tarea individual. PUT usa COALESCE para update parcial |
/api/notes | GET, POST | Notas. Filtros: ?author, ?limit |
/api/notes/:id | GET, PUT, DELETE | Nota individual |
/api/alerts | GET, POST | Alertas. ?active=1 filtra activas. POST desactiva previas |
/api/alerts/:id | GET, PUT, DELETE | Alerta individual. PUT puede toggle active |
/api/news | GET, POST | Noticias. ?limit, ?category |
/api/news/:id | GET, PUT, DELETE | Noticia individual |
Proxy APIs
| Endpoint | Destino | Secret | Cache |
|---|---|---|---|
/api/metrics | https://crearack.com/api/workspace/metrics | WORKSPACE_API_KEY | 300s |
/api/uptime | https://api.uptimerobot.com/v2/getMonitors | UPTIMEROBOT_API_KEY | 60s |
/api/servers | https://api.hetzner.cloud/v1/servers + metrics | HETZNER_API_TOKEN | 60s |
Monitoring architecture:
- Hetzner API: Estado real (running/off), CPU%, network in/out, specs (cores/RAM/disk) para PROD y Staging. Token read-only.
- UptimeRobot: Uptime% y response time para PROD (“CreaRack Pro”), SSL (“CreaRack SSL”) y Workspace. Staging pausado (puerto 8000 no publico).
- El widget de Servidores fusiona ambas fuentes: Hetzner data + UptimeRobot data para PROD, solo Hetzner para Staging, solo UptimeRobot para SSL y Workspace.
Patron de codigo (Pages Functions)
interface Env {
DB: D1Database;
}
export const onRequestGet: PagesFunction<Env> = async ({ env, request }) => {
const { results } = await env.DB.prepare('SELECT * FROM table LIMIT ?')
.bind(limit).all();
return Response.json(results);
};
Deteccion localhost
Todos los React components detectan window.location.hostname === 'localhost' y muestran placeholder en vez de hacer fetch. Las Pages Functions solo funcionan en Cloudflare (no en astro dev).
Componentes React
Hydration strategies
| Estrategia | Uso | Ejemplo |
|---|---|---|
client:load | Interactivo inmediato | TaskList, AlertBanner, SearchBox |
client:idle | Carga diferida | NewsWidget, MetricsWidget |
client:only="react" | Sin SSR (evita hydration mismatch) | HeaderClock |
Componentes principales
| Componente | Archivo | Funcion |
|---|---|---|
| SearchBox | components/SearchDialog.tsx | Busqueda Fuse.js con historial localStorage |
| HeaderClock | components/HeaderClock.tsx | Reloj live, tabular-nums, ancho fijo |
| ServerStatusHeader | components/ServerStatusHeader.tsx | 3 LEDs con glow animation |
| TaskList | components/tasks/TaskList.tsx | Lista CRUD con filtros |
| TaskForm | components/tasks/TaskForm.tsx | Formulario crear/editar |
| NotesGrid | components/notes/NotesGrid.tsx | Grid coloreada CRUD |
| AlertAdmin | components/alerts/AlertAdmin.tsx | Gestion alertas |
| AlertBanner | components/dashboard/AlertBanner.tsx | Banner condicional dashboard |
| TasksWidget | components/dashboard/TasksWidget.tsx | Widget compacto tareas |
| MetricsWidget | components/dashboard/MetricsWidget.tsx | Metricas CreaRack Pro |
| ServerStatusWidget | components/dashboard/ServerStatusWidget.tsx | Uptime + health check |
Layout CSS
App Shell (flex layout)
body (flex column, h-100vh, overflow:hidden)
Header (flex-shrink:0)
div.app-body (flex row, flex:1, overflow:hidden)
Sidebar (w-16rem, flex-shrink:0, overflow-y:auto)
main (flex:1, overflow-y:auto) ← esto scrollea
Importante: No usar position:fixed en elementos dentro de body. El Shine toggle aplica filter:brightness() al body, que rompe fixed positioning. El layout flex evita este problema.
Clases centralizadas (globals.css)
.app-body— Flex row container.dash-card— Tarjeta dashboard (padding 2rem, border-radius 1rem).dash-card-title— Titulo widget (14px uppercase).dash-entry— Sub-tarjeta/entrada.logo-brand— LOCKED — Oswald 700, letter-spacing -0.05em.glass— Backdrop blur para header.sidebar-responsive— Sidebar con colapsable mobile
Animaciones (globals.css)
@keyframes pulse— LED pulsante continuo@keyframes led-refresh— Glow flash al refrescar@keyframes fade-in— Entrada de contenido
Secrets y variables de entorno
| Secret | Donde | Uso |
|---|---|---|
UPTIMEROBOT_API_KEY | Cloudflare Pages | Proxy a UptimeRobot API |
WORKSPACE_API_KEY | Cloudflare Pages + Dokploy | Autenticacion proxy metricas |
HETZNER_API_TOKEN | Cloudflare Pages | Hetzner Cloud API (read-only) |
Configurar secrets
# Cloudflare
npx wrangler pages secret put UPTIMEROBOT_API_KEY
npx wrangler pages secret put WORKSPACE_API_KEY
# Dokploy (CreaRack Pro)
# Anadir WORKSPACE_API_KEY en Environment variables
# Y anadir a compose.prod.yml: - WORKSPACE_API_KEY=${WORKSPACE_API_KEY:-}
Despliegue
Cloudflare Pages (workspace)
- Auto-deploy: Push a
main→ Cloudflare detecta y despliega - Build command:
node scripts/sync-content.mjs && node scripts/build-search-index.mjs && astro build - Output:
./dist
CreaRack Pro (endpoint metricas)
- Archivo:
core/workspace_api.py - Ruta:
GET /api/workspace/metrics - Auth: Header
X-Workspace-Key - Deploy: Manual via Dokploy
Troubleshooting
| Problema | Solucion |
|---|---|
| Widget CreaRack Pro a 0 | Verificar WORKSPACE_API_KEY en Dokploy Y en compose.prod.yml |
| Busqueda sin resultados | Ejecutar node scripts/build-search-index.mjs |
| Sidebar scrollea con contenido | Verificar que body no tenga position:fixed hijos |
| Vite “Outdated Optimize Dep” | rm -rf node_modules/.vite + reiniciar dev |
| Hydration mismatch en reloj | Usar client:only="react" en vez de client:load |
| Tailwind clases no generan en .astro | Usar CSS centralizado en globals.css |
| LED animation no funciona | Verificar @keyframes pulse/led-refresh en globals.css |
| D1 “already exists” en migracion | La tabla ya existe, ignorar el error |
| Staging rojo en UptimeRobot | Puerto 8000 no publico. Monitor pausado, se chequea directo |
| Env var no llega al contenedor | Verificar que esta en compose.prod.yml + redeploy Dokploy |