Documentacion tecnica — Workspace

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

CapaTecnologiaVersion
FrameworkAstro6.1.5
UI IslandsReact19.x
CSSTailwind v4 (CSS-first)4.2.2
TipografiaOswald (logo), Plus Jakarta Sans (body), JetBrains Mono (code)@fontsource
BusquedaFuse.js7.3.0
Buildpnpm10.x
HostingCloudflare Pages-
DatabaseCloudflare D1SQLite
SecretsCloudflare 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

EndpointMetodosDescripcion
/api/tasksGET, POSTTareas. Filtros: ?assignee, ?status, ?priority, ?limit
/api/tasks/:idGET, PUT, DELETETarea individual. PUT usa COALESCE para update parcial
/api/notesGET, POSTNotas. Filtros: ?author, ?limit
/api/notes/:idGET, PUT, DELETENota individual
/api/alertsGET, POSTAlertas. ?active=1 filtra activas. POST desactiva previas
/api/alerts/:idGET, PUT, DELETEAlerta individual. PUT puede toggle active
/api/newsGET, POSTNoticias. ?limit, ?category
/api/news/:idGET, PUT, DELETENoticia individual

Proxy APIs

EndpointDestinoSecretCache
/api/metricshttps://crearack.com/api/workspace/metricsWORKSPACE_API_KEY300s
/api/uptimehttps://api.uptimerobot.com/v2/getMonitorsUPTIMEROBOT_API_KEY60s
/api/servershttps://api.hetzner.cloud/v1/servers + metricsHETZNER_API_TOKEN60s

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

EstrategiaUsoEjemplo
client:loadInteractivo inmediatoTaskList, AlertBanner, SearchBox
client:idleCarga diferidaNewsWidget, MetricsWidget
client:only="react"Sin SSR (evita hydration mismatch)HeaderClock

Componentes principales

ComponenteArchivoFuncion
SearchBoxcomponents/SearchDialog.tsxBusqueda Fuse.js con historial localStorage
HeaderClockcomponents/HeaderClock.tsxReloj live, tabular-nums, ancho fijo
ServerStatusHeadercomponents/ServerStatusHeader.tsx3 LEDs con glow animation
TaskListcomponents/tasks/TaskList.tsxLista CRUD con filtros
TaskFormcomponents/tasks/TaskForm.tsxFormulario crear/editar
NotesGridcomponents/notes/NotesGrid.tsxGrid coloreada CRUD
AlertAdmincomponents/alerts/AlertAdmin.tsxGestion alertas
AlertBannercomponents/dashboard/AlertBanner.tsxBanner condicional dashboard
TasksWidgetcomponents/dashboard/TasksWidget.tsxWidget compacto tareas
MetricsWidgetcomponents/dashboard/MetricsWidget.tsxMetricas CreaRack Pro
ServerStatusWidgetcomponents/dashboard/ServerStatusWidget.tsxUptime + 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-brandLOCKED — 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

SecretDondeUso
UPTIMEROBOT_API_KEYCloudflare PagesProxy a UptimeRobot API
WORKSPACE_API_KEYCloudflare Pages + DokployAutenticacion proxy metricas
HETZNER_API_TOKENCloudflare PagesHetzner 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

ProblemaSolucion
Widget CreaRack Pro a 0Verificar WORKSPACE_API_KEY en Dokploy Y en compose.prod.yml
Busqueda sin resultadosEjecutar node scripts/build-search-index.mjs
Sidebar scrollea con contenidoVerificar que body no tenga position:fixed hijos
Vite “Outdated Optimize Dep”rm -rf node_modules/.vite + reiniciar dev
Hydration mismatch en relojUsar client:only="react" en vez de client:load
Tailwind clases no generan en .astroUsar CSS centralizado en globals.css
LED animation no funcionaVerificar @keyframes pulse/led-refresh en globals.css
D1 “already exists” en migracionLa tabla ya existe, ignorar el error
Staging rojo en UptimeRobotPuerto 8000 no publico. Monitor pausado, se chequea directo
Env var no llega al contenedorVerificar que esta en compose.prod.yml + redeploy Dokploy