From 922299e2a5a6f2c303fb6198a990ca2ec091c18c Mon Sep 17 00:00:00 2001 From: Labstream Date: Thu, 18 Jun 2026 14:14:03 -0500 Subject: [PATCH] Envoltorio de escritorio Windows (Tauri 2) para Labstream OS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cliente delgado que abre os.labstreamsas.com en su propia ventana. Incluye build por GitHub Actions y guía de versionado. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/build-windows.yml | 57 ++++++++++++ .gitignore | 17 ++++ README.md | 135 ++++++++++++++++++++++++++++ app-icon.png | Bin 0 -> 52946 bytes dist/index.html | 44 +++++++++ package.json | 15 ++++ src-tauri/Cargo.toml | 20 +++++ src-tauri/build.rs | 3 + src-tauri/capabilities/default.json | 10 +++ src-tauri/icons/.gitkeep | 0 src-tauri/src/lib.rs | 12 +++ src-tauri/src/main.rs | 6 ++ src-tauri/tauri.conf.json | 50 +++++++++++ 13 files changed, 369 insertions(+) create mode 100644 .github/workflows/build-windows.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app-icon.png create mode 100644 dist/index.html create mode 100644 package.json create mode 100644 src-tauri/Cargo.toml create mode 100644 src-tauri/build.rs create mode 100644 src-tauri/capabilities/default.json create mode 100644 src-tauri/icons/.gitkeep create mode 100644 src-tauri/src/lib.rs create mode 100644 src-tauri/src/main.rs create mode 100644 src-tauri/tauri.conf.json diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml new file mode 100644 index 0000000..387af02 --- /dev/null +++ b/.github/workflows/build-windows.yml @@ -0,0 +1,57 @@ +name: Build instalador Windows + +# Se dispara al publicar una etiqueta de versión (ej. v1.0.0) y también +# a mano desde la pestaña Actions (workflow_dispatch) para pruebas. +on: + push: + tags: + - "v*" + workflow_dispatch: + +permissions: + contents: write # necesario para crear/actualizar el Release con el .exe + +jobs: + build: + runs-on: windows-latest + steps: + - name: Clonar repo + uses: actions/checkout@v4 + + - name: Instalar Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Instalar Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache de Rust + uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + + - name: Instalar dependencias + run: npm install + + - name: Generar iconos + run: npm run icons + + - name: Construir instalador + run: npm run tauri build + + - name: Publicar instalador en el Release + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v2 + with: + files: | + src-tauri/target/release/bundle/nsis/*-setup.exe + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Subir instalador como artefacto (para pruebas sin tag) + if: ${{ !startsWith(github.ref, 'refs/tags/') }} + uses: actions/upload-artifact@v4 + with: + name: labstream-os-setup + path: src-tauri/target/release/bundle/nsis/*-setup.exe diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f5ea43 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Dependencias +node_modules/ + +# Build de Rust / Tauri +src-tauri/target/ + +# Iconos generados por `tauri icon` (se regeneran desde app-icon.png) +src-tauri/icons/*.png +src-tauri/icons/*.ico +src-tauri/icons/*.icns +!src-tauri/icons/.gitkeep + +# Esquemas/IPC generados por Tauri +src-tauri/gen/ + +# Sistema +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..0bca474 --- /dev/null +++ b/README.md @@ -0,0 +1,135 @@ +# Labstream OS — App de escritorio (Windows) + +Envoltorio de escritorio para **Labstream OS**. Es un cliente delgado hecho con +[Tauri 2](https://tauri.app): abre una ventana propia (sin barra de navegador) +que carga el servidor de Labstream OS que corre en el NAS +(`https://os.labstreamsas.com`). + +No contiene la app: el backend, la base de datos, OnlyOffice, etc. siguen en el +NAS. Esto solo le da al equipo un ícono y una ventana propia en Windows. + +El servidor objetivo se configura en `src-tauri/tauri.conf.json` → +`app.windows[0].url`. + +--- + +## Prerrequisitos (solo para construir) + +Quien instala el `.exe` **no necesita nada** (WebView2 ya viene en Windows 10/11). +Para *construir* el instalador hacen falta: + +- [Node.js 20+](https://nodejs.org) +- [Rust](https://rustup.rs) (incluye `cargo`) +- En Windows: las "Visual Studio Build Tools" con el componente de C++. + +> ⚠️ **Importante:** el `.exe` de Windows se construye **en Windows**. Tauri no +> compila de forma cruzada desde macOS de manera fiable. Por eso lo normal es +> dejar que **GitHub Actions** lo construya (ver más abajo). Si quieres hacerlo +> a mano, usa una PC o máquina virtual con Windows. + +--- + +## Construir el instalador con GitHub Actions (recomendado) + +El flujo no requiere tener Windows a mano: + +1. Sube este proyecto a su propio repositorio (ej. `Labstream2026/labstream-desktop`). +2. Cambia la versión en **dos** sitios (deben coincidir): + - `src-tauri/tauri.conf.json` → `"version"` + - `src-tauri/Cargo.toml` → `version` + - (opcional) `package.json` → `"version"` +3. Crea y publica la etiqueta de versión: + ```bash + git commit -am "Versión 1.1.0" + git tag v1.1.0 + git push origin main --tags + ``` +4. GitHub Actions construye el `.exe` y lo publica en **Releases**. El equipo lo + descarga desde ahí: `https://github.com/Labstream2026/labstream-desktop/releases`. + +También puedes lanzarlo a mano sin etiqueta desde la pestaña **Actions → +Build instalador Windows → Run workflow** (el `.exe` queda como *artifact*). + +--- + +## Construir en local (en una PC Windows) + +```bash +npm install +npm run build # genera iconos + compila el instalador +``` + +El instalador queda en: +``` +src-tauri/target/release/bundle/nsis/Labstream OS__x64-setup.exe +``` + +Para desarrollo con recarga en caliente: `npm run dev`. + +--- + +## Versiones (SemVer) + +Se usa `MAYOR.MENOR.PARCHE`: + +| Cambio | Ejemplo | +| ------------------------------ | --------------- | +| Corrección / arreglo | 1.0.0 → 1.0.1 | +| Función nueva compatible | 1.0.1 → 1.1.0 | +| Cambio grande / incompatible | 1.1.0 → 2.0.0 | + +La versión del **envoltorio** es independiente de la de la app web +(`labstream-os`). Como la ventana siempre apunta al mismo servidor, casi nunca +chocan; documenta aquí si en algún momento una versión del envoltorio exige una +versión mínima del backend. + +--- + +## Actualización automática (opcional, recomendable a futuro) + +Hoy, para actualizar, el equipo descarga el nuevo `.exe` del Release y lo +reinstala encima. Para que las apps ya instaladas se actualicen solas, Tauri +trae un *updater*. Para activarlo (paso futuro): + +1. Genera el par de claves de firma: + ```bash + npm run tauri signer generate -- -w ~/.tauri/labstream.key + ``` +2. Añade el `plugin-updater` y la clave pública en `tauri.conf.json`, y guarda la + clave privada como **secret** en GitHub (`TAURI_SIGNING_PRIVATE_KEY`). +3. Apunta el updater al `latest.json` que publica el Release. + +Se deja sin activar a propósito para que la **primera** versión compile sin +configuración extra. + +--- + +## Aviso de SmartScreen (instalación interna) + +Como el instalador **no está firmado** con un certificado de pago, Windows +mostrará una pantalla azul *"Windows protegió su PC"* la primera vez. Es normal +para apps internas: + +> **Más información → Ejecutar de todos modos** + +Si en el futuro se distribuye a clientes externos, conviene un certificado de +*code signing* (~150–400 USD/año) para quitar ese aviso. + +--- + +## Estructura + +``` +labstream-desktop/ +├── app-icon.png fuente del ícono (1024×1024) +├── dist/index.html pantalla de respaldo (requerida por Tauri) +├── package.json scripts de build +├── src-tauri/ +│ ├── tauri.conf.json nombre, versión, URL del servidor, instalador +│ ├── Cargo.toml dependencias Rust + versión +│ ├── build.rs +│ ├── capabilities/default.json +│ ├── icons/ se generan desde app-icon.png +│ └── src/{main.rs,lib.rs} +└── .github/workflows/build-windows.yml build automático del .exe +``` diff --git a/app-icon.png b/app-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5d71e6a2e93c5b73b5fd498efdf1c7a9e253265f GIT binary patch literal 52946 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 MM(=d)Ny|I`3%ynUp8x;= literal 0 HcmV?d00001 diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 0000000..06f17ab --- /dev/null +++ b/dist/index.html @@ -0,0 +1,44 @@ + + + + + + Labstream OS + + + + +

+ + diff --git a/package.json b/package.json new file mode 100644 index 0000000..78af926 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "labstream-desktop", + "version": "1.0.0", + "private": true, + "description": "App de escritorio (Windows) que envuelve Labstream OS — cliente del servidor del NAS.", + "scripts": { + "icons": "tauri icon app-icon.png", + "dev": "npm run icons && tauri dev", + "build": "npm run icons && tauri build", + "tauri": "tauri" + }, + "devDependencies": { + "@tauri-apps/cli": "^2" + } +} diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..5315537 --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "labstream-desktop" +version = "1.0.0" +description = "Cliente de escritorio de Labstream OS" +authors = ["Labstream SAS"] +edition = "2021" +rust-version = "1.77.2" + +[lib] +name = "labstream_desktop_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } +tauri-plugin-opener = "2" +serde = { version = "1", features = ["derive"] } +serde_json = "1" diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..d860e1e --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 0000000..adc7b23 --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,10 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "Permisos por defecto del envoltorio de escritorio.", + "windows": ["main"], + "permissions": [ + "core:default", + "opener:default" + ] +} diff --git a/src-tauri/icons/.gitkeep b/src-tauri/icons/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..04ba033 --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1,12 @@ +// Punto de entrada de la app de escritorio de Labstream OS. +// Es un "envoltorio": la ventana carga directamente el servidor del NAS +// (ver `app.windows[].url` en tauri.conf.json), así que aquí no hay lógica de UI. +// El plugin `opener` permite que los enlaces externos abran el navegador del sistema. + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_opener::init()) + .run(tauri::generate_context!()) + .expect("error al arrancar Labstream OS"); +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..03e5c36 --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,6 @@ +// Evita que se abra una consola de Windows detrás de la ventana en la build de release. +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + labstream_desktop_lib::run() +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..6e32a6f --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,50 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "Labstream OS", + "version": "1.0.0", + "identifier": "co.labstream.os", + "build": { + "frontendDist": "../dist" + }, + "app": { + "withGlobalTauri": false, + "windows": [ + { + "label": "main", + "title": "Labstream OS", + "url": "https://os.labstreamsas.com", + "width": 1400, + "height": 900, + "minWidth": 1024, + "minHeight": 700, + "resizable": true, + "center": true, + "decorations": true + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": ["nsis"], + "publisher": "Labstream SAS", + "category": "Productivity", + "shortDescription": "Espacio de trabajo de Labstream", + "longDescription": "Cliente de escritorio de Labstream OS para Windows. Abre el espacio de trabajo de Labstream en su propia ventana.", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "windows": { + "nsis": { + "installMode": "currentUser", + "languages": ["Spanish", "SpanishInternational", "English"] + } + } + } +}