Feature/geral/switch javafx to tauri - #29
Conversation
…nds, and automatic version synchronization script
…R and removing legacy Java desktop components.
📝 WalkthroughWalkthroughA aplicação deixa de usar JavaFX e jpackage como shell desktop e passa a usar Tauri. O backend expõe porta e token de sessão, enquanto o shell Rust inicia o JAR, integra essas informações ao frontend e encerra o processo ao fechar. ChangesMigração do backend
Aplicativo Tauri
Estimated code review effort: 5 (Critical) | ~90 minutes Sequence Diagram(s)sequenceDiagram
participant Tauri
participant SpringBoot
participant React
Tauri->>SpringBoot: inicia backend.jar
SpringBoot-->>Tauri: envia PORT e TOKEN no stdout
React->>Tauri: invoke get_backend_info
Tauri-->>React: retorna BackendInfo
React->>SpringBoot: executa health-check com token
React->>Tauri: invoke close_splash
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/api/client.ts (1)
35-53: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick winSessão do backend é assíncrona, mas o app a consome como se já estivesse disponível. Porta e token só chegam via
invoke("get_backend_info")depois do mount, enquanto o cliente HTTP e as primeiras queries já foram inicializados — em produção a porta é dinâmica, então essas chamadas vão paralocalhost:8080e/ou sem token.
frontend/src/api/client.ts#L35-L53: não congelarbaseURLnoaxios.create; resolvê-la por requisição em umrequestinterceptor.frontend/src/components/RootLayout.tsx#L211-L236: bloquear a renderização dos filhos (e deuseCheckUpdatesQuery) até a sessão nativa estar resolvida — por exemplo, com um estadosessionReadyque só então montaNavigationSidebar/Outlet.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/api/client.ts` around lines 35 - 53, Atualize frontend/src/api/client.ts nas linhas 35-53 para não congelar getApiBaseUrl() em axios.create; resolva a URL atual em um interceptor de request antes de cada chamada, preservando a configuração de headers. Em frontend/src/components/RootLayout.tsx nas linhas 211-236, adicione um estado sessionReady que só seja definido após invoke("get_backend_info") concluir e bloqueie a montagem de NavigationSidebar, Outlet e useCheckUpdatesQuery até a sessão nativa estar pronta.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@backend/src/main/java/com/devaulty/backend/infrastructure/lifecycle/RuntimeSessionLogger.java`:
- Around line 17-27: Restrinja o tratamento em onApplicationEvent de
RuntimeSessionLogger ao contexto do servidor principal, ignorando
WebServerInitializedEvent originado pelo contexto de management do Actuator
antes de publicar SESSION_PREFIX, port e token. Use a identificação já
disponível no evento ou contexto da aplicação para distinguir o servidor de
management, preservando a emissão única da sessão para a porta principal.
In `@backend/src/main/resources/application-dev.yml`:
- Around line 12-14: Atualize a configuração dev em devaulty.dev.token para usar
dev-session-token, alinhando-a ao valor retornado por
frontend/src-tauri/src/lib.rs e preservando a validação das chamadas Tauri.
Atualize também a documentação correspondente para refletir o novo token de
desenvolvimento.
In `@docs/security/local-development-tokens.md`:
- Around line 22-33: Atualize a documentação de sessão em “Production Mode” para
refletir o contrato consumido por `frontend/src-tauri/src/lib.rs`: o shell
aguarda no stdout uma linha contendo `[DEVAULTY_SESSION]`, `PORT=` e `TOKEN=`.
Remova ou corrija as afirmações de que `RuntimeSessionWriter`, `session.json` e
`DEVAULTY_PORT` são mecanismos de descoberta usados pelo shell, incluindo o
trecho aplicável às linhas 46–61.
In `@frontend/scripts/build-all.js`:
- Around line 24-39: Update the build flow around runCommand and sourceJarPath
so it cannot select an arbitrary executable JAR via jarFiles[0]. Clean the
backend build/libs output before running bootJar, or otherwise validate and
identify the newly generated expected artifact; preserve the existing failure
handling when no valid JAR is available.
- Around line 54-56: Update the Step 3 frontend build command in build-all.js to
invoke the package’s configured build script via npm run build instead of npx
vite build, ensuring the TypeScript project check runs before bundling.
In `@frontend/scripts/sync-version.js`:
- Around line 31-38: Atualize o fluxo de sincronização em torno de rawVersion e
cleanVersion para preservar o sufixo de pré-lançamento ao atualizar
package.json, Cargo.toml e tauri.conf.json. Remova ou ajuste a limpeza que
elimina o trecho após “-”, garantindo que versões como 0.1.6-alpha permaneçam
idênticas em todos os manifests.
In `@frontend/src-tauri/src/lib.rs`:
- Around line 190-200: Estenda a limpeza do processo filho além do evento
CloseRequested da janela main, adicionando um handler de saída da aplicação em
.build(...) que cubra RunEvent::Exit/ExitRequested e reutilize SessionState. Em
ambos os caminhos, extraia o child de child_process, execute kill quando
necessário e depois chame wait() para recolher o processo; preserve a limpeza
atual da janela principal e evite duplicação insegura.
- Around line 135-185: Atualize o fluxo de inicialização em torno de
resolve_java_binary e Command::new para registrar explicitamente falhas ao
resolver o Java e ao executar spawn, marcando is_jar_mode como ativo somente
após a inicialização válida. Remova o jar_path.to_str().unwrap(), tratando
caminhos não UTF-8 sem pânico, e capture stderr do processo Java para encaminhar
suas linhas ao mecanismo de logging existente. Garanta que cada falha seja
propagada ou registrada imediatamente, em vez de aguardar o timeout de
get_backend_info.
- Around line 28-36: Atualize o fallback de modo dev no fluxo que verifica
`is_jar` para retornar o token aceito pelo backend, usando `dev-secret-token` em
vez de `dev-session-token`. Mantenha a porta 8080 e o comportamento de retorno
imediato inalterados.
In `@frontend/src-tauri/tauri.conf.json`:
- Around line 36-38: Substitua o valor nulo de security.csp em tauri.conf.json
por uma política CSP restritiva, permitindo recursos próprios, conexões a
http://localhost:* e imagens self/data conforme necessário para Vite/Tailwind.
Remova apenas o desligamento da CSP e valide que o aplicativo continua
carregando seus recursos locais e comunicando-se com o servidor localhost.
- Around line 24-34: Enable app.macOSPrivateApi in tauri.conf.json and add the
macos-private-api feature in Cargo.toml so the splash window’s transparent
setting works on macOS while preserving the existing splash configuration.
In `@frontend/src/components/RootLayout.tsx`:
- Around line 228-232: Update the initialization error handling around the
RootLayout startup flow so only the expected non-Tauri case is silently ignored;
surface backend startup failures, including the 60-second timeout, through the
existing error state or toast mechanism before closing the splash screen. Keep
the finally block’s close_splash cleanup intact.
---
Outside diff comments:
In `@frontend/src/api/client.ts`:
- Around line 35-53: Atualize frontend/src/api/client.ts nas linhas 35-53 para
não congelar getApiBaseUrl() em axios.create; resolva a URL atual em um
interceptor de request antes de cada chamada, preservando a configuração de
headers. Em frontend/src/components/RootLayout.tsx nas linhas 211-236, adicione
um estado sessionReady que só seja definido após invoke("get_backend_info")
concluir e bloqueie a montagem de NavigationSidebar, Outlet e
useCheckUpdatesQuery até a sessão nativa estar pronta.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 6a3e1718-66cf-4a51-8144-21873d239d5f
⛔ Files ignored due to path filters (21)
frontend/package-lock.jsonis excluded by!**/package-lock.jsonfrontend/public/devaulty-splash-screen-logo.pngis excluded by!**/*.pngfrontend/src-tauri/Cargo.lockis excluded by!**/*.lockfrontend/src-tauri/icons/128x128.pngis excluded by!**/*.pngfrontend/src-tauri/icons/128x128@2x.pngis excluded by!**/*.pngfrontend/src-tauri/icons/32x32.pngis excluded by!**/*.pngfrontend/src-tauri/icons/64x64.pngis excluded by!**/*.pngfrontend/src-tauri/icons/Square107x107Logo.pngis excluded by!**/*.pngfrontend/src-tauri/icons/Square142x142Logo.pngis excluded by!**/*.pngfrontend/src-tauri/icons/Square150x150Logo.pngis excluded by!**/*.pngfrontend/src-tauri/icons/Square284x284Logo.pngis excluded by!**/*.pngfrontend/src-tauri/icons/Square30x30Logo.pngis excluded by!**/*.pngfrontend/src-tauri/icons/Square310x310Logo.pngis excluded by!**/*.pngfrontend/src-tauri/icons/Square44x44Logo.pngis excluded by!**/*.pngfrontend/src-tauri/icons/Square71x71Logo.pngis excluded by!**/*.pngfrontend/src-tauri/icons/Square89x89Logo.pngis excluded by!**/*.pngfrontend/src-tauri/icons/StoreLogo.pngis excluded by!**/*.pngfrontend/src-tauri/icons/devaulty-icon.pngis excluded by!**/*.pngfrontend/src-tauri/icons/icon.icois excluded by!**/*.icofrontend/src-tauri/icons/icon.pngis excluded by!**/*.pngfrontend/src-tauri/resources/backend.jaris excluded by!**/*.jar
📒 Files selected for processing (27)
backend/build.gradle.ktsbackend/src/main/java/com/devaulty/backend/desktop/DevaultyDesktop.javabackend/src/main/java/com/devaulty/backend/desktop/DevaultyMainLauncher.javabackend/src/main/java/com/devaulty/backend/desktop/listener/ServerPortListener.javabackend/src/main/java/com/devaulty/backend/infrastructure/lifecycle/RuntimeSessionLogger.javabackend/src/main/java/com/devaulty/backend/infrastructure/security/WebConfig.javabackend/src/main/resources/application-dev.ymlbackend/src/main/resources/application.yamlbackend/src/main/resources/jpackage/linux/devaulty.desktopbackend/src/main/resources/jpackage/macos/Info.plistdocs/security/local-development-tokens.mdfrontend/.gitignorefrontend/VM.native_memoryfrontend/package.jsonfrontend/public/splash.htmlfrontend/scripts/build-all.jsfrontend/scripts/sync-version.jsfrontend/src-tauri/.gitignorefrontend/src-tauri/Cargo.tomlfrontend/src-tauri/build.rsfrontend/src-tauri/capabilities/default.jsonfrontend/src-tauri/icons/icon.icnsfrontend/src-tauri/src/lib.rsfrontend/src-tauri/src/main.rsfrontend/src-tauri/tauri.conf.jsonfrontend/src/api/client.tsfrontend/src/components/RootLayout.tsx
💤 Files with no reviewable changes (5)
- backend/src/main/java/com/devaulty/backend/desktop/listener/ServerPortListener.java
- backend/src/main/java/com/devaulty/backend/desktop/DevaultyMainLauncher.java
- backend/src/main/resources/jpackage/linux/devaulty.desktop
- backend/src/main/java/com/devaulty/backend/desktop/DevaultyDesktop.java
- backend/src/main/resources/jpackage/macos/Info.plist
| @Override | ||
| public void onApplicationEvent(WebServerInitializedEvent event) { | ||
| int port = event.getWebServer().getPort(); | ||
| String token = AppTokenContext.PROCESS_TOKEN; | ||
|
|
||
| String sessionPayload = String.format("%s PORT=%d TOKEN=%s", SESSION_PREFIX, port, token); | ||
|
|
||
| System.out.println(sessionPayload); | ||
|
|
||
| log.info("Devaulty Backend initialized on dynamic port: {}", port); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Filtre o contexto de management antes de publicar a sessão.
WebServerInitializedEvent também é disparado pelo contexto do servidor de management (Actuator em porta própria). Nesse cenário a segunda emissão sobrescreve port/token no SessionState do shell Rust (frontend/src-tauri/src/lib.rs, linhas 174-177 guardam sempre o último valor), fazendo o frontend apontar para a porta de management.
🛡️ Correção sugerida
`@Override`
public void onApplicationEvent(WebServerInitializedEvent event) {
+ if (event.getApplicationContext().getServerNamespace() != null) {
+ return; // ignora o contexto de management
+ }
int port = event.getWebServer().getPort();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Override | |
| public void onApplicationEvent(WebServerInitializedEvent event) { | |
| int port = event.getWebServer().getPort(); | |
| String token = AppTokenContext.PROCESS_TOKEN; | |
| String sessionPayload = String.format("%s PORT=%d TOKEN=%s", SESSION_PREFIX, port, token); | |
| System.out.println(sessionPayload); | |
| log.info("Devaulty Backend initialized on dynamic port: {}", port); | |
| } | |
| `@Override` | |
| public void onApplicationEvent(WebServerInitializedEvent event) { | |
| if (event.getApplicationContext().getServerNamespace() != null) { | |
| return; // ignora o contexto de management | |
| } | |
| int port = event.getWebServer().getPort(); | |
| String token = AppTokenContext.PROCESS_TOKEN; | |
| String sessionPayload = String.format("%s PORT=%d TOKEN=%s", SESSION_PREFIX, port, token); | |
| System.out.println(sessionPayload); | |
| log.info("Devaulty Backend initialized on dynamic port: {}", port); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@backend/src/main/java/com/devaulty/backend/infrastructure/lifecycle/RuntimeSessionLogger.java`
around lines 17 - 27, Restrinja o tratamento em onApplicationEvent de
RuntimeSessionLogger ao contexto do servidor principal, ignorando
WebServerInitializedEvent originado pelo contexto de management do Actuator
antes de publicar SESSION_PREFIX, port e token. Use a identificação já
disponível no evento ou contexto da aplicação para distinguir o servidor de
management, preservando a emissão única da sessão para a porta principal.
| devaulty: | ||
| dev: | ||
| token: "dev-secret-token" No newline at end of file | ||
| token: "dev-secret-token" |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Alinhe o token de desenvolvimento com o token retornado pelo shell.
Em desenvolvimento, frontend/src-tauri/src/lib.rs retorna dev-session-token, mas o filtro aceita o valor configurado aqui (dev-secret-token) ou o UUID do processo. As chamadas da interface Tauri receberão 403. Use o mesmo valor nos dois lados e atualize a documentação correspondente.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/src/main/resources/application-dev.yml` around lines 12 - 14,
Atualize a configuração dev em devaulty.dev.token para usar dev-session-token,
alinhando-a ao valor retornado por frontend/src-tauri/src/lib.rs e preservando a
validação das chamadas Tauri. Atualize também a documentação correspondente para
refletir o novo token de desenvolvimento.
| - Written to session.json - Accepts random UUID as fallback | ||
| for Tauri to read - Enables Swagger UI & cURL testing | ||
| - Port also written to - Fixed port 8080 | ||
| session.json | ||
| ``` | ||
|
|
||
| ### 1. Production Mode (`application-prod.yml`) | ||
| - `AppTokenContext.PROCESS_TOKEN` generates a cryptographically unique UUID (`UUID.randomUUID()`) in-memory when the JVM boots. | ||
| - `DevaultyDesktop.java` injects this token directly into the JavaFX `WebView` JS context (`window.DEVAULTY_INTERNAL_TOKEN`) during the `Worker.State.RUNNING` state before React components mount. | ||
| - External requests without this exact in-memory UUID receive `HTTP 403 Forbidden`. | ||
| - `RuntimeSessionWriter` writes `~/.config/devaulty/session.json` containing both the ephemeral port and process token. The Tauri (Rust) shell reads this file to discover the backend's address and authenticate its requests. | ||
| - The session file is automatically deleted when the Spring Boot application shuts down (`DisposableBean.destroy()`). | ||
| - Additionally, `DEVAULTY_PORT=<port>` is printed to stdout as a fallback mechanism for Tauri to read from the child process output. | ||
| - External requests without the exact in-memory UUID receive `HTTP 403 Forbidden`. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Documente o contrato de sessão realmente consumido pelo shell.
O shell em frontend/src-tauri/src/lib.rs não lê ~/.config/devaulty/session.json: ele aguarda no stdout uma linha contendo [DEVAULTY_SESSION], PORT= e TOKEN=. Documentar RuntimeSessionWriter, o arquivo de sessão e DEVAULTY_PORT como mecanismo de descoberta induz diagnósticos incorretos e pode mascarar o motivo do timeout de inicialização.
Also applies to: 46-61
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/security/local-development-tokens.md` around lines 22 - 33, Atualize a
documentação de sessão em “Production Mode” para refletir o contrato consumido
por `frontend/src-tauri/src/lib.rs`: o shell aguarda no stdout uma linha
contendo `[DEVAULTY_SESSION]`, `PORT=` e `TOKEN=`. Remova ou corrija as
afirmações de que `RuntimeSessionWriter`, `session.json` e `DEVAULTY_PORT` são
mecanismos de descoberta usados pelo shell, incluindo o trecho aplicável às
linhas 46–61.
| runCommand(`${gradlewCmd} bootJar`, backendDir); | ||
|
|
||
| // 2. Locate generated JAR in backend/build/libs/ | ||
| const backendLibsDir = path.join(backendDir, "build/libs"); | ||
| if (!fs.existsSync(backendLibsDir)) { | ||
| console.error(`Backend build libs directory not found: ${backendLibsDir}`); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| const jarFiles = fs.readdirSync(backendLibsDir).filter((file) => file.endsWith(".jar") && !file.endsWith("-plain.jar")); | ||
| if (jarFiles.length === 0) { | ||
| console.error("No executable JAR file found in backend/build/libs/"); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| const sourceJarPath = path.join(backendLibsDir, jarFiles[0]); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Não selecione o JAR pelo primeiro item do diretório.
build/libs pode reter JARs executáveis de versões anteriores, e a ordem de readdirSync() não identifica o artefato recém-gerado. Isso pode empacotar um backend antigo junto ao frontend atual. Limpe a saída antes do bootJar ou valide que há exatamente um JAR esperado antes de copiá-lo.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/scripts/build-all.js` around lines 24 - 39, Update the build flow
around runCommand and sourceJarPath so it cannot select an arbitrary executable
JAR via jarFiles[0]. Clean the backend build/libs output before running bootJar,
or otherwise validate and identify the newly generated expected artifact;
preserve the existing failure handling when no valid JAR is available.
| // 5. Build Frontend assets (Vite) | ||
| console.log("\nStep 3: Building React/Vite web bundle..."); | ||
| runCommand("npx vite build", frontendDir); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Execute a checagem de tipos no build de empacotamento.
npx vite build não executa o tsc -b definido no script build. Assim, erros TypeScript podem não bloquear o bundle distribuído. Use npm run build nesta etapa.
Correção proposta
- runCommand("npx vite build", frontendDir);
+ runCommand("npm run build", frontendDir);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 5. Build Frontend assets (Vite) | |
| console.log("\nStep 3: Building React/Vite web bundle..."); | |
| runCommand("npx vite build", frontendDir); | |
| // 5. Build Frontend assets (Vite) | |
| console.log("\nStep 3: Building React/Vite web bundle..."); | |
| runCommand("npm run build", frontendDir); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/scripts/build-all.js` around lines 54 - 56, Update the Step 3
frontend build command in build-all.js to invoke the package’s configured build
script via npm run build instead of npx vite build, ensuring the TypeScript
project check runs before bundling.
| if jar_path.exists() { | ||
| // Mark as production JAR mode BEFORE spawning Java | ||
| *state_clone.is_jar_mode.lock().unwrap() = true; | ||
|
|
||
| if let Some(java_bin) = resolve_java_binary() { | ||
| if let Ok(mut child) = Command::new(&java_bin) | ||
| .env("SPRING_PROFILES_ACTIVE", "prod") | ||
| .args([ | ||
| "-Xms64m", | ||
| "-Xmx256m", | ||
| "-XX:MetaspaceSize=96m", | ||
| "-XX:MaxMetaspaceSize=192m", | ||
| "-XX:ParallelGCThreads=2", | ||
| "-XX:ConcGCThreads=1", | ||
| "-XX:+UseG1GC", | ||
| "-XX:MaxGCPauseMillis=100", | ||
| "-jar", | ||
| jar_path.to_str().unwrap(), | ||
| "--spring.profiles.active=prod", | ||
| ]) | ||
| .stdout(Stdio::piped()) | ||
| .spawn() | ||
| { | ||
| if let Some(stdout) = child.stdout.take() { | ||
| let state_inner = Arc::clone(&state_clone); | ||
| std::thread::spawn(move || { | ||
| let reader = BufReader::new(stdout); | ||
| for line in reader.lines().flatten() { | ||
| if line.contains("[DEVAULTY_SESSION]") { | ||
| let parts: Vec<&str> = line.split_whitespace().collect(); | ||
| let mut port_val = None; | ||
| let mut token_val = None; | ||
| for part in parts { | ||
| if part.starts_with("PORT=") { | ||
| port_val = part.trim_start_matches("PORT=").parse::<u16>().ok(); | ||
| } else if part.starts_with("TOKEN=") { | ||
| token_val = Some(part.trim_start_matches("TOKEN=").to_string()); | ||
| } | ||
| } | ||
| if let (Some(p), Some(t)) = (port_val, token_val) { | ||
| *state_inner.port.lock().unwrap() = Some(p); | ||
| *state_inner.token.lock().unwrap() = Some(t); | ||
| } | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| *state_clone.child_process.lock().unwrap() = Some(child); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Falhas ao iniciar o Java são silenciosas e degradam a inicialização para um erro de 60s.
is_jar_mode é marcado como true antes de resolver o binário; se resolve_java_binary() retornar None ou o spawn falhar, nada é registrado e get_backend_info só falha após o timeout de 60s — o frontend engole o erro e abre a janela principal sem backend. Além disso, jar_path.to_str().unwrap() (linha 152) pode entrar em pânico em caminhos não-UTF8, e stderr não é capturado, perdendo o stacktrace do Spring Boot.
🛠️ Correção sugerida
- if let Some(java_bin) = resolve_java_binary() {
- if let Ok(mut child) = Command::new(&java_bin)
+ let Some(java_bin) = resolve_java_binary() else {
+ log::error!("Java runtime não encontrado; backend não será iniciado");
+ return Ok(());
+ };
+ let Some(jar_str) = jar_path.to_str() else {
+ log::error!("Caminho do JAR não é UTF-8: {:?}", jar_path);
+ return Ok(());
+ };
+ match Command::new(&java_bin)
.env("SPRING_PROFILES_ACTIVE", "prod")
.args([
...
- jar_path.to_str().unwrap(),
+ jar_str,
"--spring.profiles.active=prod",
])
.stdout(Stdio::piped())
+ .stderr(Stdio::piped())
.spawn()
- {
+ {
+ Ok(mut child) => { /* ... leitura do stdout ... */ }
+ Err(e) => log::error!("Falha ao iniciar o backend: {e}"),
+ }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src-tauri/src/lib.rs` around lines 135 - 185, Atualize o fluxo de
inicialização em torno de resolve_java_binary e Command::new para registrar
explicitamente falhas ao resolver o Java e ao executar spawn, marcando
is_jar_mode como ativo somente após a inicialização válida. Remova o
jar_path.to_str().unwrap(), tratando caminhos não UTF-8 sem pânico, e capture
stderr do processo Java para encaminhar suas linhas ao mecanismo de logging
existente. Garanta que cada falha seja propagada ou registrada imediatamente, em
vez de aguardar o timeout de get_backend_info.
| .on_window_event(|window, event| { | ||
| if let tauri::WindowEvent::CloseRequested { .. } = event { | ||
| if window.label() == "main" { | ||
| let state = window.state::<Arc<SessionState>>(); | ||
| let mut guard = state.child_process.lock().unwrap(); | ||
| if let Some(mut child) = guard.take() { | ||
| let _ = child.kill(); | ||
| } | ||
| } | ||
| } | ||
| }) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
O backend Java pode ficar órfão e o kill não colhe o processo.
O encerramento só acontece em CloseRequested da janela main. Saídas por outros caminhos (fechar a app pela splash, Cmd+Q no macOS, app.exit(), pânico no shell) deixam o JVM rodando com ~450-500 MB. Também falta wait() após kill(), deixando zumbi em Unix.
🛠️ Correção sugerida — encerrar também no evento de saída da aplicação
- if let Some(mut child) = guard.take() {
- let _ = child.kill();
- }
+ if let Some(mut child) = guard.take() {
+ let _ = child.kill();
+ let _ = child.wait(); // evita processo zumbi
+ }Além disso, replique a limpeza em .build(...) + RunEvent::Exit/ExitRequested para cobrir os demais caminhos de encerramento.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .on_window_event(|window, event| { | |
| if let tauri::WindowEvent::CloseRequested { .. } = event { | |
| if window.label() == "main" { | |
| let state = window.state::<Arc<SessionState>>(); | |
| let mut guard = state.child_process.lock().unwrap(); | |
| if let Some(mut child) = guard.take() { | |
| let _ = child.kill(); | |
| } | |
| } | |
| } | |
| }) | |
| .on_window_event(|window, event| { | |
| if let tauri::WindowEvent::CloseRequested { .. } = event { | |
| if window.label() == "main" { | |
| let state = window.state::<Arc<SessionState>>(); | |
| let mut guard = state.child_process.lock().unwrap(); | |
| if let Some(mut child) = guard.take() { | |
| let _ = child.kill(); | |
| let _ = child.wait(); // evita processo zumbi | |
| } | |
| } | |
| } | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src-tauri/src/lib.rs` around lines 190 - 200, Estenda a limpeza do
processo filho além do evento CloseRequested da janela main, adicionando um
handler de saída da aplicação em .build(...) que cubra
RunEvent::Exit/ExitRequested e reutilize SessionState. Em ambos os caminhos,
extraia o child de child_process, execute kill quando necessário e depois chame
wait() para recolher o processo; preserve a limpeza atual da janela principal e
evite duplicação insegura.
| { | ||
| "label": "splash", | ||
| "url": "/splash.html", | ||
| "title": "Devaulty", | ||
| "width": 550, | ||
| "height": 400, | ||
| "center": true, | ||
| "decorations": false, | ||
| "transparent": true, | ||
| "alwaysOnTop": true | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Tauri v2 transparent window macOSPrivateApi requirement
💡 Result:
To achieve a transparent window in Tauri v2 on macOS, you must enable the macOS private API, as it is required to unlock system-level transparency [1][2]. To implement this, you need to configure your application in two places: 1. Tauri Configuration (tauri.conf.json): Enable the private API in your configuration [3][1]. { "app": { "macOSPrivateApi": true } } Note that enabling this flag will cause apps distributed via the Mac App Store to be rejected, as it relies on private Apple APIs [1][4]. 2. Cargo Dependencies: You must also enable the corresponding feature in your Cargo.toml file to make the transparent method available for macOS [3][2]: [dependencies] tauri = { version = "2.0.0", features = ["macos-private-api"] } If the transparent method is missing from your WebviewWindowBuilder or WindowBuilder during compilation on macOS, it is almost always because one of these two configurations is missing [3][2]. Recent updates to Tauri v2 have introduced more granular control over these APIs, allowing you to use features like macos-private-api-transparent independently of other private APIs, though the legacy umbrella setting macOSPrivateApi remains for backward compatibility [5]. For production-grade transparent effects that avoid private API rejection, some developers look toward using native Metal rendering, though this requires more advanced implementation [4].
Citations:
- 1: https://txz.cool/blog/m36p2ntvjgaazq07pg08wix5
- 2: https://docs.rs/tauri/2.0.0/tauri/window/struct.WindowBuilder.html
- 3: [bug][v2][only MacOS] no method named
transparentfound for structWebviewWindowBuilderin the current scope tauri-apps/tauri#10057 - 4: [feat] Using macOS API to achieve transparent effect tauri-apps/tauri#13680
- 5: feat(config): allow macOS fullscreen and transparent APIs to be enabled independently tauri-apps/tauri#15640
Ative macOSPrivateApi para a splash translúcida no macOS
Se a splash precisa ser translúcida no macOS, habilite app.macOSPrivateApi e o feature macos-private-api no Cargo.toml; sem isso, transparent: true não surte efeito no macOS e a build pode ser rejeitada na App Store.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src-tauri/tauri.conf.json` around lines 24 - 34, Enable
app.macOSPrivateApi in tauri.conf.json and add the macos-private-api feature in
Cargo.toml so the splash window’s transparent setting works on macOS while
preserving the existing splash configuration.
| "security": { | ||
| "csp": null | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
csp: null desativa a Content Security Policy do webview.
Com o app carregando conteúdo local e falando com http://localhost:<porta>, uma CSP restritiva é viável e reduz muito a superfície de XSS. Considere algo como default-src 'self'; connect-src 'self' http://localhost:*; img-src 'self' data: e ajustar conforme o Vite/Tailwind exigirem.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src-tauri/tauri.conf.json` around lines 36 - 38, Substitua o valor
nulo de security.csp em tauri.conf.json por uma política CSP restritiva,
permitindo recursos próprios, conexões a http://localhost:* e imagens self/data
conforme necessário para Vite/Tailwind. Remova apenas o desligamento da CSP e
valide que o aplicativo continua carregando seus recursos locais e
comunicando-se com o servidor localhost.
| } catch { | ||
| // Silently ignore if running outside Tauri | ||
| } finally { | ||
| await invoke("close_splash").catch(() => {}); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Erro de inicialização do backend fica invisível para o usuário.
O catch vazio trata igualmente "rodando fora do Tauri" e "timeout de 60s do backend" (frontend/src-tauri/src/lib.rs, linha 56). No segundo caso a splash fecha e a janela principal abre sem backend, sem qualquer mensagem. Diferencie os casos e exiba um estado de erro/toast.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/RootLayout.tsx` around lines 228 - 232, Update the
initialization error handling around the RootLayout startup flow so only the
expected non-Tauri case is silently ignored; surface backend startup failures,
including the 60-second timeout, through the existing error state or toast
mechanism before closing the splash screen. Keep the finally block’s
close_splash cleanup intact.
Pull Request: Switch Desktop Frontend from JavaFX + WebView to Tauri v2
📌 Summary
This PR replaces the legacy JavaFX + Embedded WebView application shell with a modern Tauri v2 (Rust) desktop shell hosting a React frontend.
The backend continues to run as a headless Spring Boot service spawned and managed asynchronously by the Tauri desktop process.
🚀 Key Changes & Architecture
src-tauri).backend.jar) on application startup and manages process lifecycle & IPC token verification..deb,.appimage,.rpm,.msi, and.dmgbundles (npm run build:all,npm run build:deb).application.yaml,package.json,Cargo.toml, andtauri.conf.json.1. Backend RAM Consumption (WON'T FIX)
Warning
High Backend RAM Usage (JVM Footprint):
The Spring Boot backend process still consumes ~450–500 MB of RAM (RSS) at runtime due to JVM metaspace, Spring Data JPA/Hibernate, Liquibase, and thread stack overhead.
WON'T FIXin this PR.2. CI/CD Release Pipeline Status (CRITICAL)
Caution
CI/CD Releases Currently Non-Functional:
The automated GitHub Actions release and build pipeline is currently non-functional/disabled for Tauri packaging on this branch.
npm run build:deb) function as expected.🧪 Verification & Testing
npm run build:debSummary by CodeRabbit
Novos Recursos
Melhorias
Documentação