feat: Tauri desktop shell — Rust backend + config files + build scripts
- desktop/Cargo.toml: Tauri + plugins (dialog, notification, fs, updater) - desktop/tauri.conf.json: Windows (.msi/.nsis) + Linux (.deb/.AppImage) config - desktop/src/main.rs: spawn Python backend, health check, tray icon, commands - desktop/build.rs: Tauri build hook - desktop/python-embed/sidecar.py: uvicorn launcher with port auto-detect - build-windows.bat: automated Windows build with Python embed download - build-linux.sh: automated Linux build with venv - desktop/README.md: architecture doc + usage guide - desktop/validate-structure.sh: CI validation script - ROADMAP.md: #77 Tauri Desktop section added
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "obsigate-desktop"
|
||||||
|
version = "2.0.0"
|
||||||
|
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
||||||
|
authors = ["Bruno Charest"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { version = "2", features = [] }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tauri = { version = "2", features = [] }
|
||||||
|
tauri-plugin-shell = "2"
|
||||||
|
tauri-plugin-dialog = "2"
|
||||||
|
tauri-plugin-notification = "2"
|
||||||
|
tauri-plugin-fs = "2"
|
||||||
|
tauri-plugin-process = "2"
|
||||||
|
tauri-plugin-updater = "2"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["custom-protocol"]
|
||||||
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# ObsiGate Desktop
|
||||||
|
|
||||||
|
Application desktop native pour [ObsiGate](https://git.dracodev.net/Projets/ObsiGate) — construite avec [Tauri](https://tauri.app/).
|
||||||
|
|
||||||
|
> 🚧 **Phase de conception** — les binaires ne sont pas encore disponibles.
|
||||||
|
|
||||||
|
## Prérequis
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
- [Rust](https://rustup.rs/) (stable ≥ 1.75)
|
||||||
|
- Tauri CLI : `cargo install tauri-cli`
|
||||||
|
- NSIS (inclus dans le build)
|
||||||
|
- Python 3.11 embed (téléchargé automatiquement)
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
```bash
|
||||||
|
sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev
|
||||||
|
cargo install tauri-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
```cmd
|
||||||
|
cd desktop
|
||||||
|
build-windows.bat
|
||||||
|
```
|
||||||
|
→ Produit `target/release/bundle/msi/ObsiGate_2.0.0_x64.msi` + `.exe` NSIS
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
```bash
|
||||||
|
cd desktop
|
||||||
|
chmod +x build-linux.sh
|
||||||
|
./build-linux.sh
|
||||||
|
```
|
||||||
|
→ Produit `target/release/bundle/deb/obsigate_2.0.0_amd64.deb` + `.AppImage`
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
desktop/
|
||||||
|
├── Cargo.toml # Dépendances Rust
|
||||||
|
├── tauri.conf.json # Config Tauri (fenêtre, bundle, plugins)
|
||||||
|
├── build.rs # Script build Tauri
|
||||||
|
├── src/
|
||||||
|
│ └── main.rs # Point d'entrée Rust : spawn backend + webview
|
||||||
|
├── python-embed/
|
||||||
|
│ ├── sidecar.py # Lance uvicorn sur 127.0.0.1:17890
|
||||||
|
│ └── ... # Python 3.11 + site-packages (gelés)
|
||||||
|
├── build-windows.bat # Script build Windows
|
||||||
|
├── build-linux.sh # Script build Linux
|
||||||
|
└── icons/ # Icônes desktop (.ico, .png, .icns)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fonctionnalités desktop natives
|
||||||
|
|
||||||
|
| Fonctionnalité | Web | Desktop |
|
||||||
|
|---|---|---|
|
||||||
|
| Accès fichiers local | Via upload | Natif (sélecteur dossier) |
|
||||||
|
| Thème système | Manuel | Auto (suit OS dark/light) |
|
||||||
|
| Notifications | Service Worker | Natif OS |
|
||||||
|
| Associations `.md` | ❌ | ✅ « Ouvrir avec ObsiGate » |
|
||||||
|
| Tray icon | ❌ | ✅ Barre des tâches |
|
||||||
|
| Auto-update | ❌ | ✅ Vérifie releases Gitea |
|
||||||
|
| Mode hors-ligne | Limité | Complet (backend local) |
|
||||||
|
|
||||||
|
## Cycle de vie
|
||||||
|
|
||||||
|
```
|
||||||
|
Démarrage :
|
||||||
|
1. Tauri lance python-embed/python → uvicorn backend.main:app
|
||||||
|
2. Health check GET /api/health (timeout 15s)
|
||||||
|
3. Webview ouvre http://127.0.0.1:17890
|
||||||
|
|
||||||
|
Arrêt (tray → Quitter ou fermeture fenêtre) :
|
||||||
|
1. SIGTERM envoyé au processus Python
|
||||||
|
2. Backend s'arrête proprement (10s timeout)
|
||||||
|
3. Tauri ferme la webview
|
||||||
|
4. Processus nettoyé — aucun résidu
|
||||||
|
```
|
||||||
Executable
+53
@@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================================
|
||||||
|
# ObsiGate Desktop — Build Linux (.deb + .AppImage)
|
||||||
|
# Prérequis : Rust, Tauri CLI, libwebkit2gtk, Python 3.11
|
||||||
|
# ============================================================
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "========================================"
|
||||||
|
echo " ObsiGate Desktop — Build Linux"
|
||||||
|
echo "========================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── 1. Vérifier les prérequis ─────────────────────────────────
|
||||||
|
echo "[1/6] Verifying prerequisites..."
|
||||||
|
command -v cargo >/dev/null 2>&1 || { echo "❌ Rust/Cargo not found. Install: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"; exit 1; }
|
||||||
|
command -v tauri >/dev/null 2>&1 || cargo install tauri-cli
|
||||||
|
|
||||||
|
# Check system deps
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev curl 2>/dev/null
|
||||||
|
fi
|
||||||
|
echo "✅ Dependencies OK"
|
||||||
|
|
||||||
|
# ── 2. Préparer Python 3.11 ────────────────────────────────────
|
||||||
|
echo "[2/6] Preparing Python environment..."
|
||||||
|
python3 -m venv python-embed/venv
|
||||||
|
source python-embed/venv/bin/activate
|
||||||
|
pip install --upgrade pip -q
|
||||||
|
pip install -r ../backend/requirements.txt -q
|
||||||
|
echo "✅ Python environment ready"
|
||||||
|
|
||||||
|
# ── 3. Copier le frontend ─────────────────────────────────────
|
||||||
|
echo "[3/6] Copying frontend..."
|
||||||
|
rm -rf frontend-static
|
||||||
|
cp -r ../frontend frontend-static
|
||||||
|
echo "✅ Frontend copied"
|
||||||
|
|
||||||
|
# ── 4. Icônes ──────────────────────────────────────────────────
|
||||||
|
echo "[4/6] Generating icons..."
|
||||||
|
# Placeholder — tauri icon generator
|
||||||
|
echo "✅ Icons OK"
|
||||||
|
|
||||||
|
# ── 5. Build Tauri ────────────────────────────────────────────
|
||||||
|
echo "[5/6] Building Tauri application..."
|
||||||
|
cargo tauri build --target x86_64-unknown-linux-gnu --bundles deb,appimage
|
||||||
|
echo "✅ Build successful"
|
||||||
|
|
||||||
|
# ── 6. Résultats ───────────────────────────────────────────────
|
||||||
|
echo "[6/6] Results:"
|
||||||
|
ls -lh target/release/bundle/deb/*.deb 2>/dev/null
|
||||||
|
ls -lh target/release/bundle/appimage/*.AppImage 2>/dev/null
|
||||||
|
echo ""
|
||||||
|
echo "✅ Done! Check target/release/bundle/"
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
@echo off
|
||||||
|
REM ============================================================
|
||||||
|
REM ObsiGate Desktop — Build Windows (.msi + .exe)
|
||||||
|
REM Prérequis : Rust, Tauri CLI, NSIS, Python 3.11 embed
|
||||||
|
REM ============================================================
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
echo ========================================
|
||||||
|
echo ObsiGate Desktop — Build Windows
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM ── 1. Vérifier les prérequis ─────────────────────────────────
|
||||||
|
echo [1/6] Verifying prerequisites...
|
||||||
|
where cargo >nul 2>&1 || (echo ❌ Rust/Cargo not found. Install from https://rustup.rs & exit /b 1)
|
||||||
|
where tauri >nul 2>&1 || cargo install tauri-cli
|
||||||
|
echo ✅ Rust + Tauri OK
|
||||||
|
|
||||||
|
REM ── 2. Préparer Python 3.11 embed ─────────────────────────────
|
||||||
|
echo [2/6] Preparing Python embed...
|
||||||
|
if not exist "python-embed\python.exe" (
|
||||||
|
echo 📥 Downloading Python 3.11 embed...
|
||||||
|
curl -L -o python-embed.zip "https://www.python.org/ftp/python/3.11.15/python-3.11.15-embed-amd64.zip"
|
||||||
|
powershell Expand-Archive -Path python-embed.zip -DestinationPath python-embed\
|
||||||
|
del python-embed.zip
|
||||||
|
|
||||||
|
REM Enable pip in embedded Python
|
||||||
|
echo import site >> python-embed\python311._pth
|
||||||
|
|
||||||
|
REM Install dependencies
|
||||||
|
python-embed\python.exe -m pip install --no-cache-dir -r ..\backend\requirements.txt
|
||||||
|
)
|
||||||
|
echo ✅ Python embed ready
|
||||||
|
|
||||||
|
REM ── 3. Copier le frontend ─────────────────────────────────────
|
||||||
|
echo [3/6] Copying frontend...
|
||||||
|
if exist "frontend-static" rmdir /s /q "frontend-static"
|
||||||
|
xcopy /E /I /Q "..\frontend" "frontend-static"
|
||||||
|
echo ✅ Frontend copied
|
||||||
|
|
||||||
|
REM ── 4. Mettre à jour les icônes ───────────────────────────────
|
||||||
|
echo [4/6] Generating icons...
|
||||||
|
REM Placeholder — will use tauri icon generator when icons are ready
|
||||||
|
echo ✅ Icons OK (using defaults)
|
||||||
|
|
||||||
|
REM ── 5. Build Tauri ────────────────────────────────────────────
|
||||||
|
echo [5/6] Building Tauri application...
|
||||||
|
cargo tauri build --target x86_64-pc-windows-msvc --bundles msi,nsis
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo ❌ Build failed!
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo ✅ Build successful
|
||||||
|
|
||||||
|
REM ── 6. Signer (optionnel) ─────────────────────────────────────
|
||||||
|
echo [6/6] Results:
|
||||||
|
dir /b target\release\bundle\msi\*.msi 2>nul
|
||||||
|
dir /b target\release\bundle\nsis\*.exe 2>nul
|
||||||
|
echo.
|
||||||
|
echo ✅ Done! Check target\release\bundle\
|
||||||
|
endlocal
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
tauri_build::build()
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
ObsiGate Desktop — Sidecar Launcher
|
||||||
|
====================================
|
||||||
|
Lancé par le shell Tauri, démarre le backend FastAPI.
|
||||||
|
Utilise le port 17890 (127.0.0.1 uniquement, pas d'exposition réseau).
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import signal
|
||||||
|
|
||||||
|
BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
HOST = "127.0.0.1"
|
||||||
|
PORT = int(os.environ.get("OBSIGATE_PORT", "17890"))
|
||||||
|
|
||||||
|
|
||||||
|
def find_free_port(start: int) -> int:
|
||||||
|
"""Trouve le premier port libre à partir de `start`."""
|
||||||
|
import socket
|
||||||
|
port = start
|
||||||
|
while port < start + 100:
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||||
|
if s.connect_ex((HOST, port)) != 0:
|
||||||
|
return port
|
||||||
|
port += 1
|
||||||
|
raise RuntimeError(f"No free port found in range {start}-{start+99}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
port = find_free_port(PORT)
|
||||||
|
|
||||||
|
# Create log directory
|
||||||
|
if sys.platform == "win32":
|
||||||
|
log_dir = os.path.join(os.environ.get("APPDATA", os.path.expanduser("~")), "ObsiGate", "logs")
|
||||||
|
else:
|
||||||
|
log_dir = os.path.join(os.path.expanduser("~"), ".local", "share", "obsigate", "logs")
|
||||||
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
|
|
||||||
|
log_file = os.path.join(log_dir, "backend.log")
|
||||||
|
print(f"ObsiGate backend starting on {HOST}:{port} — logs: {log_file}")
|
||||||
|
|
||||||
|
# Launch uvicorn
|
||||||
|
cmd = [
|
||||||
|
sys.executable,
|
||||||
|
"-m", "uvicorn",
|
||||||
|
"backend.main:app",
|
||||||
|
"--host", HOST,
|
||||||
|
"--port", str(port),
|
||||||
|
"--log-level", "info",
|
||||||
|
]
|
||||||
|
|
||||||
|
with open(log_file, "a") as log:
|
||||||
|
log.write(f"\n--- ObsiGate started at {time.ctime()} ---\n")
|
||||||
|
proc = subprocess.Popen(cmd, cwd=BACKEND_DIR, stdout=log, stderr=log)
|
||||||
|
|
||||||
|
def handle_signal(signum, frame):
|
||||||
|
print("Shutting down ObsiGate backend...")
|
||||||
|
proc.terminate()
|
||||||
|
proc.wait(timeout=10)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
signal.signal(signal.SIGTERM, handle_signal)
|
||||||
|
signal.signal(signal.SIGINT, handle_signal)
|
||||||
|
|
||||||
|
# Wait for backend to finish
|
||||||
|
proc.wait()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
use tauri::Manager;
|
||||||
|
use std::process::{Child, Command};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
// ── State ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
struct BackendProcess(Mutex<Option<Child>>);
|
||||||
|
|
||||||
|
// ── Sidecar : spawn Python backend ──────────────────────────────────────────
|
||||||
|
|
||||||
|
fn spawn_backend() -> Result<Child, String> {
|
||||||
|
let python = if cfg!(target_os = "windows") {
|
||||||
|
"./python-embed/python.exe"
|
||||||
|
} else {
|
||||||
|
"./python-embed/bin/python3"
|
||||||
|
};
|
||||||
|
|
||||||
|
let child = Command::new(python)
|
||||||
|
.args([
|
||||||
|
"-m", "uvicorn",
|
||||||
|
"backend.main:app",
|
||||||
|
"--host", "127.0.0.1",
|
||||||
|
"--port", "17890",
|
||||||
|
"--log-level", "info",
|
||||||
|
])
|
||||||
|
.spawn()
|
||||||
|
.map_err(|e| format!("Failed to start Python backend: {}", e))?;
|
||||||
|
|
||||||
|
Ok(child)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Health check — wait until backend is ready ──────────────────────────────
|
||||||
|
|
||||||
|
async fn wait_for_backend() -> Result<(), String> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let url = "http://127.0.0.1:17890/api/health";
|
||||||
|
|
||||||
|
for i in 0..30 {
|
||||||
|
match client.get(url).timeout(Duration::from_secs(2)).send().await {
|
||||||
|
Ok(resp) if resp.status().is_success() => {
|
||||||
|
println!("✅ Backend ready (attempt {})", i + 1);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err("Backend did not become ready within 15 seconds".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Tauri Commands ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn get_backend_url() -> String {
|
||||||
|
"http://127.0.0.1:17890".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn pick_vault_folder(app: tauri::AppHandle) -> Result<String, String> {
|
||||||
|
use tauri_plugin_dialog::DialogExt;
|
||||||
|
|
||||||
|
let path = app
|
||||||
|
.dialog()
|
||||||
|
.file()
|
||||||
|
.blocking_pick_folder();
|
||||||
|
|
||||||
|
match path {
|
||||||
|
Some(p) => Ok(p.to_string()),
|
||||||
|
None => Err("No folder selected".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn get_system_theme(app: tauri::AppHandle) -> String {
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
{
|
||||||
|
use tauri::Theme;
|
||||||
|
match app.get_webview_window("main").unwrap().theme() {
|
||||||
|
Ok(Theme::Dark) => "dark".to_string(),
|
||||||
|
_ => "light".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Entry Point ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// 1. Start Python backend
|
||||||
|
println!("🚀 Starting ObsiGate backend...");
|
||||||
|
let backend = spawn_backend().expect("Failed to launch Python backend");
|
||||||
|
let backend_state = BackendProcess(Mutex::new(Some(backend)));
|
||||||
|
|
||||||
|
// 2. Build Tauri app
|
||||||
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_shell::init())
|
||||||
|
.plugin(tauri_plugin_dialog::init())
|
||||||
|
.plugin(tauri_plugin_notification::init())
|
||||||
|
.plugin(tauri_plugin_fs::init())
|
||||||
|
.plugin(tauri_plugin_process::init())
|
||||||
|
.manage(backend_state)
|
||||||
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
get_backend_url,
|
||||||
|
pick_vault_folder,
|
||||||
|
get_system_theme,
|
||||||
|
])
|
||||||
|
.setup(|app| {
|
||||||
|
// 3. Wait for backend health check (non-blocking)
|
||||||
|
let handle = app.handle().clone();
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
match wait_for_backend().await {
|
||||||
|
Ok(()) => {
|
||||||
|
// Navigate to the main app
|
||||||
|
if let Some(window) = handle.get_webview_window("main") {
|
||||||
|
let _ = window.eval("window.location.href = 'http://127.0.0.1:17890'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("❌ Backend failed: {}", e);
|
||||||
|
if let Some(window) = handle.get_webview_window("main") {
|
||||||
|
let _ = window.eval(&format!(
|
||||||
|
"document.body.innerHTML = '<div style=\"padding:40px;text-align:center;font-family:sans-serif;color:red\"><h2>ObsiGate n\\'a pas pu démarrer</h2><p>{}</p><button onclick=\"location.reload()\">Réessayer</button></div>'",
|
||||||
|
e.replace('\'', "\\'")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.on_window_event(|window, event| {
|
||||||
|
if let tauri::WindowEvent::CloseRequested { .. } = event {
|
||||||
|
// Kill the Python backend on close
|
||||||
|
let state = window.state::<BackendProcess>();
|
||||||
|
if let Ok(mut guard) = state.0.lock() {
|
||||||
|
if let Some(ref mut child) = *guard {
|
||||||
|
println!("🛑 Stopping backend...");
|
||||||
|
let _ = child.kill();
|
||||||
|
let _ = child.wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running ObsiGate Desktop");
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
||||||
|
"productName": "ObsiGate",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"identifier": "com.obsigate.desktop",
|
||||||
|
"build": {
|
||||||
|
"frontendDist": "../frontend",
|
||||||
|
"devUrl": "http://localhost:17890",
|
||||||
|
"beforeDevCommand": "cd .. && python -m uvicorn backend.main:app --host 127.0.0.1 --port 17890 --log-level info",
|
||||||
|
"beforeBuildCommand": "echo 'Frontend already built — using static files'"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"withGlobalTauri": true,
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "ObsiGate",
|
||||||
|
"width": 1200,
|
||||||
|
"height": 800,
|
||||||
|
"minWidth": 800,
|
||||||
|
"minHeight": 600,
|
||||||
|
"resizable": true,
|
||||||
|
"fullscreen": false,
|
||||||
|
"center": true,
|
||||||
|
"decorations": true,
|
||||||
|
"transparent": false,
|
||||||
|
"label": "main"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"security": {
|
||||||
|
"csp": "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com https://esm.sh; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob: https:; connect-src 'self' http://127.0.0.1:* ws://127.0.0.1:*;"
|
||||||
|
},
|
||||||
|
"trayIcon": {
|
||||||
|
"iconPath": "icons/icon.png",
|
||||||
|
"iconAsTemplate": true,
|
||||||
|
"tooltip": "ObsiGate",
|
||||||
|
"menuOnLeftClick": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"targets": "all",
|
||||||
|
"icon": [
|
||||||
|
"icons/32x32.png",
|
||||||
|
"icons/128x128.png",
|
||||||
|
"icons/[email protected]",
|
||||||
|
"icons/icon.icns",
|
||||||
|
"icons/icon.ico"
|
||||||
|
],
|
||||||
|
"fileAssociations": [
|
||||||
|
{
|
||||||
|
"ext": ["md", "markdown", "txt"],
|
||||||
|
"name": "ObsiGate Markdown",
|
||||||
|
"description": "Fichier Markdown ObsiGate",
|
||||||
|
"role": "Editor"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"windows": {
|
||||||
|
"wix": null,
|
||||||
|
"nsis": {
|
||||||
|
"installMode": "currentUser",
|
||||||
|
"installerIcon": "icons/icon.ico",
|
||||||
|
"shortcutName": "ObsiGate",
|
||||||
|
"createDesktopShortcut": true,
|
||||||
|
"startMenuFolder": "ObsiGate"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"deb": {
|
||||||
|
"depends": ["libwebkit2gtk-4.1-0", "libgtk-3-0", "libayatana-appindicator3-1"]
|
||||||
|
},
|
||||||
|
"appimage": {
|
||||||
|
"bundleMediaFramework": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"macOS": {
|
||||||
|
"frameworks": [],
|
||||||
|
"minimumSystemVersion": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"updater": {
|
||||||
|
"pubkey": "CHANGE_ME_TO_YOUR_PUBLIC_KEY",
|
||||||
|
"endpoints": [
|
||||||
|
"https://git.dracodev.net/api/v1/repos/Projets/ObsiGate/releases/latest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"shell": {
|
||||||
|
"open": true,
|
||||||
|
"scope": [
|
||||||
|
{
|
||||||
|
"name": "python-backend",
|
||||||
|
"cmd": "./python-embed/python",
|
||||||
|
"args": ["-m", "uvicorn", "backend.main:app", "--host", "127.0.0.1", "--port", "17890"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+50
@@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Quick validation of desktop build structure
|
||||||
|
echo "=== Desktop build structure validation ==="
|
||||||
|
|
||||||
|
errors=0
|
||||||
|
|
||||||
|
# Check required files exist
|
||||||
|
for f in Cargo.toml tauri.conf.json build.rs src/main.rs python-embed/sidecar.py \
|
||||||
|
build-windows.bat build-linux.sh README.md; do
|
||||||
|
if [ -f "desktop/$f" ]; then
|
||||||
|
echo "✅ desktop/$f"
|
||||||
|
else
|
||||||
|
echo "❌ MISSING: desktop/$f"
|
||||||
|
((errors++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check directories
|
||||||
|
for d in desktop/icons desktop/src; do
|
||||||
|
if [ -d "$d" ]; then
|
||||||
|
echo "✅ $d/"
|
||||||
|
else
|
||||||
|
echo "❌ MISSING: $d/"
|
||||||
|
((errors++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check Cargo.toml has Tauri dependency
|
||||||
|
if grep -q 'tauri' desktop/Cargo.toml; then
|
||||||
|
echo "✅ Cargo.toml references Tauri"
|
||||||
|
else
|
||||||
|
echo "❌ Cargo.toml missing Tauri dependency"
|
||||||
|
((errors++))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check tauri.conf.json is valid JSON
|
||||||
|
if python3 -c "import json; json.load(open('desktop/tauri.conf.json'))" 2>/dev/null; then
|
||||||
|
echo "✅ tauri.conf.json is valid JSON"
|
||||||
|
else
|
||||||
|
echo "❌ tauri.conf.json has JSON syntax errors"
|
||||||
|
((errors++))
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [ $errors -eq 0 ]; then
|
||||||
|
echo "✅ All checks passed — build structure is valid"
|
||||||
|
else
|
||||||
|
echo "❌ $errors error(s) found"
|
||||||
|
fi
|
||||||
|
exit $errors
|
||||||
Reference in New Issue
Block a user