feat(desktop): OpenMES Desktop app (Tauri) + unattended install preset#186
Conversation
Brings the desktop platform app to develop alongside mobile/. A Tauri (Rust + webview) shell that bundles PHP + the backend and runs OpenMES as a local desktop app / LAN MES server, with an in-app self-updater. - desktop/: Tauri app (src-tauri Rust, TS control panel, icons, build scripts for self-contained Windows .exe and Linux .deb/.rpm) - Backend INSTALLER_PRESET mode: unattended install (skips env/db wizard steps, straight to admin creation) that the desktop shell drives — InstallController + install views + bootstrap/app.php - InstallPresetTest covers the preset behaviour The shop-floor workstation feature lands separately in #175.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThe PR adds a Tauri desktop application with embedded PHP/database runtime management, remote database setup, self-updating, platform build scripts, and a complete frontend. It also adds an ChangesTauri desktop application
Unattended backend installation
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant DesktopUI
participant TauriBackend
participant PHPServer
participant Database
User->>DesktopUI: launch desktop application
DesktopUI->>TauriBackend: ensure runtime and database mode
TauriBackend->>Database: discover or validate database
DesktopUI->>TauriBackend: start server
TauriBackend->>PHPServer: launch artisan server
PHPServer->>Database: connect using selected configuration
DesktopUI->>TauriBackend: poll server status
TauriBackend-->>DesktopUI: return ready URL
DesktopUI->>PHPServer: navigate to local application
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (5)
desktop/README.md (3)
12-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdd language tag to fenced code block.
Specifying a language tag improves rendering and resolves markdownlint warnings.
📝 Proposed change
-``` +```text🤖 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 `@desktop/README.md` at line 12, Update the fenced code block in the README to include the text language tag, changing the opening fence from an unannotated block to a text-labeled fence while preserving its contents and closing fence.Source: Linters/SAST tools
75-77: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse correct Polish typography for closing quotes.
Consider using the standard Polish closing quote (
”) instead of the straight double quote (").📝 Proposed change
- 2015–2022 x64*. Instalator i `postgres.exe` są niepodpisane (SmartScreen → - „Więcej informacji → Uruchom mimo to"). Nie uruchamiać jako Administrator - (Postgres odmawia startu z konta admina). + 2015–2022 x64*. Instalator i `postgres.exe` są niepodpisane (SmartScreen → + „Więcej informacji → Uruchom mimo to”). Nie uruchamiać jako Administrator + (Postgres odmawia startu z konta admina).🤖 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 `@desktop/README.md` around lines 75 - 77, Update the Polish quotation in the installer warning within the README text to use the standard closing quote character (”) instead of the straight double quote, preserving the surrounding wording and formatting.Source: Linters/SAST tools
22-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse correct Polish typography for closing quotes.
Consider using the standard Polish closing quote (
”) instead of the straight double quote (").📝 Proposed change
Zamknięcie okna **nie wyłącza serwera** — aplikacja chowa się do traya. -Pełne wyłączenie: tray → „Zakończ". +Pełne wyłączenie: tray → „Zakończ”.🤖 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 `@desktop/README.md` around lines 22 - 24, Update the Polish quoted tray label in the README text to use the standard Polish closing quotation mark (”) instead of the straight double quote, preserving the existing wording and formatting.Source: Linters/SAST tools
desktop/index.html (1)
36-71: 🎯 Functional Correctness | 🔵 TrivialMissing explicit database driver selection.
This form only collects host/port/database/username/password; there's no control to pick Postgres vs. MySQL explicitly, even though the copy on Line 27 advertises support for both. See related comment on
desktop/src/main.tswhere the driver is inferred purely from the port number.🤖 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 `@desktop/index.html` around lines 36 - 71, Update the db-remote-form to add an explicit database-driver selector for Postgres and MySQL alongside the existing connection fields. Ensure the selected driver is exposed through the form’s existing submission flow so the connection logic can use it instead of relying solely on the port number.backend/app/Http/Controllers/InstallController.php (1)
46-61: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
installerPreset()silently breaks underconfig:cache.The docblock itself flags the caveat: "Note: requires an uncached config (env() returns null after
config:cache), which is the case for desktop/dev runs." If the packaged desktop/container runtime (or any future deployment path) ever runsconfig:cacheorartisan optimize,INSTALLER_PRESETdetection silently degrades to the normal interactive wizard with no error — a hard-to-diagnose regression for the entire unattended-install feature.Prefer routing this through a dedicated config key (e.g.
config('install.preset')backed byenv('INSTALLER_PRESET')in a config file) so it survives caching, consistent with Laravel's general guidance against callingenv()outside config files.Please confirm the desktop/build staging scripts (
desktop/scripts/*.sh) never runphp artisan config:cacheagainst the bundled backend before first launch.🤖 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/app/Http/Controllers/InstallController.php` around lines 46 - 61, Update installerPreset() to read the preset through a dedicated cached configuration key such as config('install.preset'), with that key defined in a configuration file and backed by env('INSTALLER_PRESET') there; preserve validation against self::DB_DRIVERS. Also inspect desktop/scripts/*.sh and confirm the build or staging flow does not run php artisan config:cache against the bundled backend before first launch.
🤖 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/app/Http/Controllers/InstallController.php`:
- Around line 442-473: Validate db_database before it is consumed by the runtime
configuration flow, including the setupDatabase() validation path and the
preset-gated block in the install flow. Reject traversal segments such as “..”
or resolve the path and require it to remain under storage_path(), then only
pass the sanitized value to storage_path() and SQLite configuration.
- Around line 411-423: Move the validation currently performed by
`$request->validate()` in the install flow into a `CreateAdminRequest` Form
Request, adding a `rules()` method that reads the preset value from
`session('install_database_config')['preset']` and preserves the conditional
`site_name` and `site_url` rules. Update the controller action to type-hint and
use `CreateAdminRequest`, while retaining the existing validated defaults for
`site_name` and `site_url`.
- Around line 102-110: Update the preconfigured database connection failure
handling in InstallController so it does not flash $e->getMessage() to
unauthenticated users. Reuse the same PDO exception sanitization/classification
behavior and user-facing error wording established by setupDatabase(), while
preserving the redirect to the install.database route.
In `@desktop/index.html`:
- Around line 51-55: Correct the default database value in the `db-database`
input from `openmmes` to `openmes`, and update the corresponding `ServerConfig`
field `db_database` to use the same corrected default.
In `@desktop/scripts/fetch-runtimes.sh`:
- Around line 11-40: Update the runtime download flow in fetch-runtimes.sh to
pin PHP_WIN_URL to a fixed Windows PHP release instead of latest, define the
expected SHA-256 values for the PHP_LINUX_URL, PHP_WIN_URL, and PG_WIN_URL
archives, and verify each downloaded file’s checksum before tar or unzip
extraction; abort on any mismatch and remove the invalid temporary archive.
In `@desktop/src-tauri/src/lib.rs`:
- Around line 663-664: Update the command construction around Command::new and
the PHP -r invocation so config.db_password is no longer passed through args as
a positional CLI argument. Provide the password via a safer environment variable
or stdin, and adjust the PHP code to read it from that channel while preserving
the existing DSN, username, and database connection behavior.
- Around line 351-374: The PostgreSQL initialization currently enables trust
authentication and creates an unused password file. Update the initdb flow
around the `Command::new(...pg_exe("initdb"))` invocation to use SCRAM
authentication with a securely generated, persisted password, pass it via
`--pwfile`, and remove the temporary `.pgpw` write/delete logic; also update the
corresponding `DB_PASSWORD` configuration to use the same password instead of an
empty value.
- Around line 1428-1465: Update the update flow around migrate_on,
swap_in_place, and rollback_swap so rollback never starts old code against a
database already migrated to the new schema: create and retain a database
snapshot/backup before migration and restore it before each do_start rollback
path, or defer migration until the new version has booted successfully. Preserve
cleanup and failure reporting, and document the chosen forward-only migration
assumption if rollback cannot restore the schema.
In `@desktop/src-tauri/tauri.conf.json`:
- Around line 12-27: Update the Tauri app configuration by removing the
withGlobalTauri setting and replacing the null security.csp value with a
restrictive Content Security Policy that permits only the frontend’s required
resources and Tauri module usage. Keep the existing window configuration
unchanged.
In `@desktop/src/main.ts`:
- Around line 388-401: Replace the port-based driver inference in the
db-remote-form submit handler in desktop/src/main.ts (lines 388-401) with the
selected value from an explicit db-driver control. Add a Postgres/MySQL driver
select to the db-remote-form in desktop/index.html (lines 36-71), ensuring its
value populates the ServerConfig db_driver field.
- Around line 5-7: Update desktop/src/main.ts so all comments and source-level
text are written in English, including the areas around isSettingsWindow,
showMessage, humanizeError, and bootError. Introduce or reuse an i18n layer with
English keys and a Polish locale so user-facing messages remain Polish at
runtime. Replace inline Polish strings throughout the file with localized
lookups while preserving the existing UI behavior and error messaging.
- Around line 9-23: Update ServerConfig and the save_config serialization flow
so db_password is excluded from the JSON file and stored/retrieved through the
operating system keychain or credential store. Keep the remaining ServerConfig
fields persisted as before, and ensure runtime configuration still receives the
password from the secure store.
- Around line 363-418: Update ensureDbMode so its static controls receive event
listeners only once, while each invocation rebinds the current cfg and Promise
resolve state used by those handlers. Preserve the existing local, remote, back,
and submit behavior, but prevent stale closures from issuing duplicate IPC calls
or overwriting newer configuration after bootFlow retries.
---
Nitpick comments:
In `@backend/app/Http/Controllers/InstallController.php`:
- Around line 46-61: Update installerPreset() to read the preset through a
dedicated cached configuration key such as config('install.preset'), with that
key defined in a configuration file and backed by env('INSTALLER_PRESET') there;
preserve validation against self::DB_DRIVERS. Also inspect desktop/scripts/*.sh
and confirm the build or staging flow does not run php artisan config:cache
against the bundled backend before first launch.
In `@desktop/index.html`:
- Around line 36-71: Update the db-remote-form to add an explicit
database-driver selector for Postgres and MySQL alongside the existing
connection fields. Ensure the selected driver is exposed through the form’s
existing submission flow so the connection logic can use it instead of relying
solely on the port number.
In `@desktop/README.md`:
- Line 12: Update the fenced code block in the README to include the text
language tag, changing the opening fence from an unannotated block to a
text-labeled fence while preserving its contents and closing fence.
- Around line 75-77: Update the Polish quotation in the installer warning within
the README text to use the standard closing quote character (”) instead of the
straight double quote, preserving the surrounding wording and formatting.
- Around line 22-24: Update the Polish quoted tray label in the README text to
use the standard Polish closing quotation mark (”) instead of the straight
double quote, preserving the existing wording and formatting.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: fd514032-d7b3-4734-89e3-e201b07d063d
⛔ Files ignored due to path filters (21)
desktop/package-lock.jsonis excluded by!**/package-lock.jsondesktop/src-tauri/Cargo.lockis excluded by!**/*.lockdesktop/src-tauri/icons/128x128.pngis excluded by!**/*.pngdesktop/src-tauri/icons/128x128@2x.pngis excluded by!**/*.pngdesktop/src-tauri/icons/32x32.pngis excluded by!**/*.pngdesktop/src-tauri/icons/64x64.pngis excluded by!**/*.pngdesktop/src-tauri/icons/Square107x107Logo.pngis excluded by!**/*.pngdesktop/src-tauri/icons/Square142x142Logo.pngis excluded by!**/*.pngdesktop/src-tauri/icons/Square150x150Logo.pngis excluded by!**/*.pngdesktop/src-tauri/icons/Square284x284Logo.pngis excluded by!**/*.pngdesktop/src-tauri/icons/Square30x30Logo.pngis excluded by!**/*.pngdesktop/src-tauri/icons/Square310x310Logo.pngis excluded by!**/*.pngdesktop/src-tauri/icons/Square44x44Logo.pngis excluded by!**/*.pngdesktop/src-tauri/icons/Square71x71Logo.pngis excluded by!**/*.pngdesktop/src-tauri/icons/Square89x89Logo.pngis excluded by!**/*.pngdesktop/src-tauri/icons/StoreLogo.pngis excluded by!**/*.pngdesktop/src-tauri/icons/icon.icois excluded by!**/*.icodesktop/src-tauri/icons/icon.pngis excluded by!**/*.pngdesktop/src/assets/tauri.svgis excluded by!**/*.svgdesktop/src/assets/typescript.svgis excluded by!**/*.svgdesktop/src/assets/vite.svgis excluded by!**/*.svg
📒 Files selected for processing (29)
.gitignorebackend/app/Http/Controllers/InstallController.phpbackend/bootstrap/app.phpbackend/resources/views/install/admin.blade.phpbackend/resources/views/install/complete.blade.phpbackend/resources/views/install/database.blade.phpbackend/resources/views/install/environment.blade.phpbackend/resources/views/install/welcome.blade.phpbackend/tests/Feature/InstallPresetTest.phpdesktop/.gitignoredesktop/README.mddesktop/index.htmldesktop/package.jsondesktop/scripts/build-linux.shdesktop/scripts/build-windows.shdesktop/scripts/bundle-resources.shdesktop/scripts/fetch-runtimes.shdesktop/src-tauri/.gitignoredesktop/src-tauri/Cargo.tomldesktop/src-tauri/build.rsdesktop/src-tauri/capabilities/default.jsondesktop/src-tauri/icons/icon.icnsdesktop/src-tauri/src/lib.rsdesktop/src-tauri/src/main.rsdesktop/src-tauri/tauri.conf.jsondesktop/src/main.tsdesktop/src/styles.cssdesktop/tsconfig.jsondesktop/vite.config.ts
| try { | ||
| // No purge here: the connection is already configured by real env | ||
| // vars (and purging would destroy in-memory test databases). | ||
| config(['database.default' => $driver]); | ||
| DB::connection($driver)->getPdo(); | ||
| } catch (\Exception $e) { | ||
| return redirect()->route('install.database') | ||
| ->with('error', 'Preconfigured database is not reachable: '.$e->getMessage()); | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Raw exception message exposed to an unauthenticated visitor.
$e->getMessage() from DB::connection($driver)->getPdo() is flashed directly into the redirect error, unlike setupDatabase() (Lines 272-288) which sanitizes/classifies PDO exceptions before display. Since install routes are reachable pre-auth (before an admin exists), this can leak internal connection details (host/port/driver internals) to anyone probing the not-yet-installed instance.
🛡️ Proposed fix
} catch (\Exception $e) {
- return redirect()->route('install.database')
- ->with('error', 'Preconfigured database is not reachable: '.$e->getMessage());
+ return redirect()->route('install.database')
+ ->with('error', 'Preconfigured database is not reachable. Check the server logs for details.');
}📝 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.
| try { | |
| // No purge here: the connection is already configured by real env | |
| // vars (and purging would destroy in-memory test databases). | |
| config(['database.default' => $driver]); | |
| DB::connection($driver)->getPdo(); | |
| } catch (\Exception $e) { | |
| return redirect()->route('install.database') | |
| ->with('error', 'Preconfigured database is not reachable: '.$e->getMessage()); | |
| } | |
| try { | |
| // No purge here: the connection is already configured by real env | |
| // vars (and purging would destroy in-memory test databases). | |
| config(['database.default' => $driver]); | |
| DB::connection($driver)->getPdo(); | |
| } catch (\Exception $e) { | |
| return redirect()->route('install.database') | |
| ->with('error', 'Preconfigured database is not reachable. Check the server logs for details.'); | |
| } |
🤖 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/app/Http/Controllers/InstallController.php` around lines 102 - 110,
Update the preconfigured database connection failure handling in
InstallController so it does not flash $e->getMessage() to unauthenticated
users. Reuse the same PDO exception sanitization/classification behavior and
user-facing error wording established by setupDatabase(), while preserving the
redirect to the install.database route.
| $isPreset = (bool) (session('install_database_config')['preset'] ?? false); | ||
|
|
||
| $validated = $request->validate([ | ||
| 'admin_username' => 'required|string|max:255|unique:users,username', | ||
| 'admin_email' => 'required|email|max:255|unique:users,email', | ||
| 'admin_password' => 'required|string|min:8|confirmed', | ||
| 'site_name' => 'required|string|max:255', | ||
| 'site_url' => 'required|url', | ||
| 'site_name' => [$isPreset ? 'nullable' : 'required', 'string', 'max:255'], | ||
| 'site_url' => [$isPreset ? 'nullable' : 'required', 'url'], | ||
| ]); | ||
|
|
||
| $validated['site_name'] = $validated['site_name'] ?? 'OpenMES'; | ||
| $validated['site_url'] = $validated['site_url'] ?? 'http://localhost'; | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Inline $request->validate() in the controller.
Lines 413-419 add new conditional rules directly inline in the controller rather than in a Form Request. As per coding guidelines, backend/app/Http/{Controllers,Requests}/**/*.php: "Validation must use Form Requests, never validate inline in controllers." Moving this to a CreateAdminRequest (with a rules() method reading session('install_database_config')['preset']) would restore compliance and improve testability.
🤖 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/app/Http/Controllers/InstallController.php` around lines 411 - 423,
Move the validation currently performed by `$request->validate()` in the install
flow into a `CreateAdminRequest` Form Request, adding a `rules()` method that
reads the preset value from `session('install_database_config')['preset']` and
preserves the conditional `site_name` and `site_url` rules. Update the
controller action to type-hint and use `CreateAdminRequest`, while retaining the
existing validated defaults for `site_name` and `site_url`.
Source: Coding guidelines
| // Re-apply runtime DB config so Eloquent uses the correct connection. | ||
| // Preset installs skip this: their connection comes straight from real | ||
| // environment variables and is already active — re-applying it from | ||
| // session would mangle non-path values like ":memory:". | ||
| if (empty($dbConfig['preset'])) { | ||
| if ($driver === 'sqlite') { | ||
| $dbPath = $dbConfig['db_database']; | ||
| if (! str_starts_with($dbPath, '/')) { | ||
| $dbPath = storage_path($dbPath); | ||
| } | ||
| config(["database.connections.{$driver}.database" => $dbPath]); | ||
| } else { | ||
| config([ | ||
| "database.connections.{$driver}.host" => $dbConfig['db_host'], | ||
| "database.connections.{$driver}.port" => $dbConfig['db_port'], | ||
| "database.connections.{$driver}.database" => $dbConfig['db_database'], | ||
| "database.connections.{$driver}.username" => $dbConfig['db_username'], | ||
| "database.connections.{$driver}.password" => $dbConfig['db_password'], | ||
| ]); | ||
| } | ||
| config(["database.connections.{$driver}.database" => $dbPath]); | ||
| } else { | ||
| config([ | ||
| "database.connections.{$driver}.host" => $dbConfig['db_host'], | ||
| "database.connections.{$driver}.port" => $dbConfig['db_port'], | ||
| "database.connections.{$driver}.database" => $dbConfig['db_database'], | ||
| "database.connections.{$driver}.username" => $dbConfig['db_username'], | ||
| "database.connections.{$driver}.password" => $dbConfig['db_password'], | ||
| ]); | ||
|
|
||
| config(['database.default' => $driver]); | ||
|
|
||
| DB::purge($driver); | ||
| DB::reconnect($driver); | ||
| } | ||
|
|
||
| config([ | ||
| 'database.default' => $driver, | ||
| 'app.name' => $validated['site_name'], | ||
| 'app.url' => $validated['site_url'], | ||
| ]); | ||
|
|
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Unsanitized db_database flows into storage_path() — path traversal risk, reachable pre-auth.
Static analysis flags Line 449: "Avoid building paths from unsanitized input ... [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')." $dbConfig['db_database'] originates from setupDatabase()'s validation ('db_database' => 'required|string', no traversal check), so a value like ../../../../tmp/x reaches storage_path($dbPath) unfiltered. Because install routes are open to any visitor before the install marker exists, this is exploitable pre-auth, not just by a trusted admin. The underlying gap predates this diff (same pattern in setupDatabase() Lines 243-248) but is now duplicated into this newly-added preset-gated block.
Add a traversal check where db_database is validated/consumed (e.g. reject values containing .. or resolve+confirm the final path stays under storage_path()).
🧰 Tools
🪛 ast-grep (0.44.1)
[error] 449-449: Avoid building paths from unsanitized input
Context: storage_path($dbPath)
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
(laravel-path-traversal-storage)
🤖 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/app/Http/Controllers/InstallController.php` around lines 442 - 473,
Validate db_database before it is consumed by the runtime configuration flow,
including the setupDatabase() validation path and the preset-gated block in the
install flow. Reject traversal segments such as “..” or resolve the path and
require it to remain under storage_path(), then only pass the sanitized value to
storage_path() and SQLite configuration.
Source: Linters/SAST tools
| <div class="row"> | ||
| <label> | ||
| Baza | ||
| <input id="db-database" type="text" value="openmmes" /> | ||
| </label> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Default database name likely has a typo (openmmes).
The suggested default value has a duplicated "m" — matches the app name "OpenMES" with an extra letter. This same typo appears in the Rust-side ServerConfig default (db_database: "openmmes".into()), so it's consistently wrong rather than intentional branding.
✏️ Suggested fix
- <input id="db-database" type="text" value="openmmes" />
+ <input id="db-database" type="text" value="openmes" />📝 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.
| <div class="row"> | |
| <label> | |
| Baza | |
| <input id="db-database" type="text" value="openmmes" /> | |
| </label> | |
| <div class="row"> | |
| <label> | |
| Baza | |
| <input id="db-database" type="text" value="openmes" /> | |
| </label> |
🤖 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 `@desktop/index.html` around lines 51 - 55, Correct the default database value
in the `db-database` input from `openmmes` to `openmes`, and update the
corresponding `ServerConfig` field `db_database` to use the same corrected
default.
| PHP_LINUX_URL="https://dl.static-php.dev/static-php-cli/common/php-8.3.31-cli-linux-x86_64.tar.gz" | ||
| PHP_WIN_URL="https://windows.php.net/downloads/releases/latest/php-8.3-nts-Win32-vs16-x64-latest.zip" | ||
| PG_WIN_URL="https://repo1.maven.org/maven2/io/zonky/test/postgres/embedded-postgres-binaries-windows-amd64/16.4.0/embedded-postgres-binaries-windows-amd64-16.4.0.jar" | ||
|
|
||
| # ── Linux PHP (do buildu Linux) ────────────────────────────────────────────── | ||
| echo "[1/3] Linux static PHP…" | ||
| mkdir -p src-tauri/resources/php | ||
| if [ ! -f src-tauri/resources/php/php ]; then | ||
| curl -fL --retry 2 -o /tmp/php-linux.tar.gz "$PHP_LINUX_URL" | ||
| tar xzf /tmp/php-linux.tar.gz -C src-tauri/resources/php && rm /tmp/php-linux.tar.gz | ||
| chmod +x src-tauri/resources/php/php | ||
| fi | ||
|
|
||
| # ── Windows PHP (do buildu Windows) ────────────────────────────────────────── | ||
| echo "[2/3] Windows PHP…" | ||
| mkdir -p winphp-staging | ||
| if [ ! -f winphp-staging/php/php.exe ]; then | ||
| curl -fL --retry 2 -o /tmp/php-win.zip "$PHP_WIN_URL" | ||
| rm -rf winphp-staging/php && unzip -q /tmp/php-win.zip -d winphp-staging/php && rm /tmp/php-win.zip | ||
| fi | ||
|
|
||
| # ── Windows PostgreSQL (zonky) ─────────────────────────────────────────────── | ||
| echo "[3/3] Windows PostgreSQL…" | ||
| if [ ! -f winphp-staging/pgsql/bin/postgres.exe ]; then | ||
| curl -fL --retry 2 -o /tmp/pg-win.jar "$PG_WIN_URL" | ||
| unzip -o -q /tmp/pg-win.jar postgres-windows-x86_64.txz -d /tmp | ||
| mkdir -p winphp-staging/pgsql | ||
| tar xf /tmp/postgres-windows-x86_64.txz -C winphp-staging/pgsql | ||
| rm /tmp/pg-win.jar /tmp/postgres-windows-x86_64.txz | ||
| fi |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file map =="
git ls-files desktop/scripts/fetch-runtimes.sh desktop | sed -n '1,120p'
echo
echo "== outline =="
ast-grep outline desktop/scripts/fetch-runtimes.sh --view expanded || true
echo
echo "== script contents with line numbers =="
cat -n desktop/scripts/fetch-runtimes.sh | sed -n '1,220p'
echo
echo "== search for runtime download / hash verification usages =="
rg -n "sha256|checksum|verify|windows.php.net|static-php.dev|embedded-postgres-binaries-windows-amd64|fetch-runtimes" desktop . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**'Repository: Mes-Open/OpenMes
Length of output: 50373
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== inspect nearby files that consume the staged runtimes =="
rg -n "src-tauri/resources/php|winphp-staging|postgres.exe|php.exe|desktop/scripts/fetch-runtimes.sh" desktop -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**'
echo
echo "== list likely packaging files =="
git ls-files desktop | rg 'tauri|cargo|build|package|script|workflow|yml|yaml' || trueRepository: Mes-Open/OpenMes
Length of output: 4413
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== fetch-runtimes.sh =="
cat -n desktop/scripts/fetch-runtimes.sh | sed -n '1,220p'
echo
echo "== search for staging/resource paths in desktop tree =="
rg -n "src-tauri/resources/php|winphp-staging|fetch-runtimes.sh|php-8.3.31|embedded-postgres-binaries-windows-amd64" desktop -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**'
echo
echo "== files mentioning packaging/build =="
git ls-files desktop | rg '^(desktop/)?(package|Cargo|tauri|src-tauri|scripts|\.github/)' || trueRepository: Mes-Open/OpenMes
Length of output: 6839
Pin the Windows PHP download and add checksum checks for the runtime archives.
These runtime archives are pulled into the desktop installer bundle, but none are integrity-checked before extraction. PHP_WIN_URL also points at latest, so the build can drift over time. Pin that URL to a fixed release and verify a known SHA-256 for each download before unpacking.
🧰 Tools
🪛 ast-grep (0.44.1)
[warning] 18-18: Writing to or reading from a hardcoded, predictable path under /tmp is vulnerable to symlink and TOCTOU attacks: a local attacker can pre-create the file (or a symlink pointing elsewhere) and hijack or corrupt the contents. Generate a unique, unpredictable temporary file with mktemp instead, e.g. tmpfile="$(mktemp)" (or mktemp -d for directories) and reference "$tmpfile".
Context: /tmp/php-linux.tar.gz
Note: [CWE-377] Insecure Temporary File.
(predictable-tmp-file-bash)
[warning] 19-19: Writing to or reading from a hardcoded, predictable path under /tmp is vulnerable to symlink and TOCTOU attacks: a local attacker can pre-create the file (or a symlink pointing elsewhere) and hijack or corrupt the contents. Generate a unique, unpredictable temporary file with mktemp instead, e.g. tmpfile="$(mktemp)" (or mktemp -d for directories) and reference "$tmpfile".
Context: /tmp/php-linux.tar.gz
Note: [CWE-377] Insecure Temporary File.
(predictable-tmp-file-bash)
[warning] 19-19: Writing to or reading from a hardcoded, predictable path under /tmp is vulnerable to symlink and TOCTOU attacks: a local attacker can pre-create the file (or a symlink pointing elsewhere) and hijack or corrupt the contents. Generate a unique, unpredictable temporary file with mktemp instead, e.g. tmpfile="$(mktemp)" (or mktemp -d for directories) and reference "$tmpfile".
Context: /tmp/php-linux.tar.gz
Note: [CWE-377] Insecure Temporary File.
(predictable-tmp-file-bash)
[warning] 27-27: Writing to or reading from a hardcoded, predictable path under /tmp is vulnerable to symlink and TOCTOU attacks: a local attacker can pre-create the file (or a symlink pointing elsewhere) and hijack or corrupt the contents. Generate a unique, unpredictable temporary file with mktemp instead, e.g. tmpfile="$(mktemp)" (or mktemp -d for directories) and reference "$tmpfile".
Context: /tmp/php-win.zip
Note: [CWE-377] Insecure Temporary File.
(predictable-tmp-file-bash)
[warning] 28-28: Writing to or reading from a hardcoded, predictable path under /tmp is vulnerable to symlink and TOCTOU attacks: a local attacker can pre-create the file (or a symlink pointing elsewhere) and hijack or corrupt the contents. Generate a unique, unpredictable temporary file with mktemp instead, e.g. tmpfile="$(mktemp)" (or mktemp -d for directories) and reference "$tmpfile".
Context: /tmp/php-win.zip
Note: [CWE-377] Insecure Temporary File.
(predictable-tmp-file-bash)
[warning] 28-28: Writing to or reading from a hardcoded, predictable path under /tmp is vulnerable to symlink and TOCTOU attacks: a local attacker can pre-create the file (or a symlink pointing elsewhere) and hijack or corrupt the contents. Generate a unique, unpredictable temporary file with mktemp instead, e.g. tmpfile="$(mktemp)" (or mktemp -d for directories) and reference "$tmpfile".
Context: /tmp/php-win.zip
Note: [CWE-377] Insecure Temporary File.
(predictable-tmp-file-bash)
[warning] 34-34: Writing to or reading from a hardcoded, predictable path under /tmp is vulnerable to symlink and TOCTOU attacks: a local attacker can pre-create the file (or a symlink pointing elsewhere) and hijack or corrupt the contents. Generate a unique, unpredictable temporary file with mktemp instead, e.g. tmpfile="$(mktemp)" (or mktemp -d for directories) and reference "$tmpfile".
Context: /tmp/pg-win.jar
Note: [CWE-377] Insecure Temporary File.
(predictable-tmp-file-bash)
[warning] 35-35: Writing to or reading from a hardcoded, predictable path under /tmp is vulnerable to symlink and TOCTOU attacks: a local attacker can pre-create the file (or a symlink pointing elsewhere) and hijack or corrupt the contents. Generate a unique, unpredictable temporary file with mktemp instead, e.g. tmpfile="$(mktemp)" (or mktemp -d for directories) and reference "$tmpfile".
Context: /tmp/pg-win.jar
Note: [CWE-377] Insecure Temporary File.
(predictable-tmp-file-bash)
[warning] 37-37: Writing to or reading from a hardcoded, predictable path under /tmp is vulnerable to symlink and TOCTOU attacks: a local attacker can pre-create the file (or a symlink pointing elsewhere) and hijack or corrupt the contents. Generate a unique, unpredictable temporary file with mktemp instead, e.g. tmpfile="$(mktemp)" (or mktemp -d for directories) and reference "$tmpfile".
Context: /tmp/postgres-windows-x86_64.txz
Note: [CWE-377] Insecure Temporary File.
(predictable-tmp-file-bash)
[warning] 38-38: Writing to or reading from a hardcoded, predictable path under /tmp is vulnerable to symlink and TOCTOU attacks: a local attacker can pre-create the file (or a symlink pointing elsewhere) and hijack or corrupt the contents. Generate a unique, unpredictable temporary file with mktemp instead, e.g. tmpfile="$(mktemp)" (or mktemp -d for directories) and reference "$tmpfile".
Context: /tmp/pg-win.jar
Note: [CWE-377] Insecure Temporary File.
(predictable-tmp-file-bash)
[warning] 38-38: Writing to or reading from a hardcoded, predictable path under /tmp is vulnerable to symlink and TOCTOU attacks: a local attacker can pre-create the file (or a symlink pointing elsewhere) and hijack or corrupt the contents. Generate a unique, unpredictable temporary file with mktemp instead, e.g. tmpfile="$(mktemp)" (or mktemp -d for directories) and reference "$tmpfile".
Context: /tmp/postgres-windows-x86_64.txz
Note: [CWE-377] Insecure Temporary File.
(predictable-tmp-file-bash)
🤖 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 `@desktop/scripts/fetch-runtimes.sh` around lines 11 - 40, Update the runtime
download flow in fetch-runtimes.sh to pin PHP_WIN_URL to a fixed Windows PHP
release instead of latest, define the expected SHA-256 values for the
PHP_LINUX_URL, PHP_WIN_URL, and PG_WIN_URL archives, and verify each downloaded
file’s checksum before tar or unzip extraction; abort on any mismatch and remove
the invalid temporary archive.
| "app": { | ||
| "withGlobalTauri": true, | ||
| "windows": [ | ||
| { | ||
| "label": "main", | ||
| "title": "OpenMES Desktop — panel sterowania", | ||
| "width": 940, | ||
| "height": 700, | ||
| "minWidth": 760, | ||
| "minHeight": 560 | ||
| } | ||
| ], | ||
| "security": { | ||
| "csp": null | ||
| } | ||
| }, |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Secure the Tauri configuration by removing the global API and enabling CSP.
Disabling the Content Security Policy ("csp": null) and exposing the Tauri API globally ("withGlobalTauri": true) unnecessarily increases the attack surface in the event of an XSS vulnerability.
Since the frontend imports Tauri APIs directly as modules (e.g., import { invoke } from "@tauri-apps/api/core";), the global window.__TAURI__ is not needed. Consider removing it and enabling a restrictive CSP to mitigate potential security risks.
🛡️ Proposed security hardening
"app": {
- "withGlobalTauri": true,
"windows": [
{
"label": "main",
"title": "OpenMES Desktop — panel sterowania",
"width": 940,
"height": 700,
"minWidth": 760,
"minHeight": 560
}
],
"security": {
- "csp": null
+ "csp": "default-src 'self'; style-src 'self' 'unsafe-inline';"
}
},📝 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.
| "app": { | |
| "withGlobalTauri": true, | |
| "windows": [ | |
| { | |
| "label": "main", | |
| "title": "OpenMES Desktop — panel sterowania", | |
| "width": 940, | |
| "height": 700, | |
| "minWidth": 760, | |
| "minHeight": 560 | |
| } | |
| ], | |
| "security": { | |
| "csp": null | |
| } | |
| }, | |
| "app": { | |
| "windows": [ | |
| { | |
| "label": "main", | |
| "title": "OpenMES Desktop — panel sterowania", | |
| "width": 940, | |
| "height": 700, | |
| "minWidth": 760, | |
| "minHeight": 560 | |
| } | |
| ], | |
| "security": { | |
| "csp": "default-src 'self'; style-src 'self' 'unsafe-inline';" | |
| } | |
| }, |
🤖 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 `@desktop/src-tauri/tauri.conf.json` around lines 12 - 27, Update the Tauri app
configuration by removing the withGlobalTauri setting and replacing the null
security.csp value with a restrictive Content Security Policy that permits only
the frontend’s required resources and Tauri module usage. Keep the existing
window configuration unchanged.
| // Okno "settings" pokazuje panel od razu; okno "main" pokazuje splash i po | ||
| // starcie serwera przełącza się na UI OpenMES - panel widzi tylko przy błędzie. | ||
| const isSettingsWindow = getCurrentWindow().label === "settings"; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Comments and UI strings throughout the file are in Polish.
Repo guideline requires English for code, text, and comments in .ts files, but this file's comments (e.g. Line 6: "Okno "main" pokazuje splash i po starcie serwera przełącza się na UI OpenMES") and all user-facing strings (showMessage, humanizeError, bootError, etc.) are Polish. This is pervasive across the file rather than isolated instances.
Given the target audience is clearly Polish-speaking operators, consider introducing an i18n layer (English keys + a Polish locale file) so the source code/comments are in English per guidelines while still shipping localized UI text.
As per coding guidelines, "All code, Blade/JSX text, validation messages, seeders, and comments must be in English" for **/*.{php,js,jsx,ts,tsx,blade.php}.
Also applies to: 271-296, 298-308, 361-363, 420-421, 459-460
🤖 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 `@desktop/src/main.ts` around lines 5 - 7, Update desktop/src/main.ts so all
comments and source-level text are written in English, including the areas
around isSettingsWindow, showMessage, humanizeError, and bootError. Introduce or
reuse an i18n layer with English keys and a Polish locale so user-facing
messages remain Polish at runtime. Replace inline Polish strings throughout the
file with localized lookups while preserving the existing UI behavior and error
messaging.
Source: Coding guidelines
| interface ServerConfig { | ||
| php_binary: string; | ||
| backend_dir: string; | ||
| host: string; | ||
| port: number; | ||
| autostart: boolean; | ||
| extra_env: string; | ||
| db_mode: string; | ||
| db_driver: string; | ||
| db_host: string; | ||
| db_port: number; | ||
| db_database: string; | ||
| db_username: string; | ||
| db_password: string; | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Locate the Rust persistence logic for ServerConfig/save_config to check for encryption.
rg -n -A5 -B5 'fn save_config' desktop/src-tauri/src/lib.rs
rg -n -A5 'db_password' desktop/src-tauri/src/lib.rsRepository: Mes-Open/OpenMes
Length of output: 1434
Store db_password outside the JSON config file. save_config serializes the full ServerConfig to disk, so db_password is written in plaintext. Move just this field to the OS keychain/credential store instead of flat-file storage.
🤖 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 `@desktop/src/main.ts` around lines 9 - 23, Update ServerConfig and the
save_config serialization flow so db_password is excluded from the JSON file and
stored/retrieved through the operating system keychain or credential store. Keep
the remaining ServerConfig fields persisted as before, and ensure runtime
configuration still receives the password from the secure store.
| function ensureDbMode(cfg: ServerConfig): Promise<void> { | ||
| if (cfg.db_mode) return Promise.resolve(); | ||
| show("splash", false); | ||
| show("db-choice", true); | ||
|
|
||
| return new Promise((resolve) => { | ||
| $("btn-db-local").addEventListener("click", async () => { | ||
| cfg.db_mode = "local"; | ||
| await invoke("save_config", { config: cfg }); | ||
| show("db-choice", false); | ||
| show("splash", true); | ||
| resolve(); | ||
| }); | ||
|
|
||
| $("btn-db-remote").addEventListener("click", () => { | ||
| show("db-choice", false); | ||
| show("db-remote", true); | ||
| scanAndFillDbForm(); | ||
| }); | ||
|
|
||
| $("btn-db-back").addEventListener("click", () => { | ||
| show("db-remote", false); | ||
| show("db-choice", true); | ||
| }); | ||
|
|
||
| $<HTMLFormElement>("db-remote-form").addEventListener("submit", async (e) => { | ||
| e.preventDefault(); | ||
| const msg = $("db-message"); | ||
| const port = Number($<HTMLInputElement>("db-port").value) || 5432; | ||
| const candidate: ServerConfig = { | ||
| ...cfg, | ||
| db_mode: "remote", | ||
| db_driver: port === 3306 ? "mysql" : "pgsql", | ||
| db_host: $<HTMLInputElement>("db-host").value.trim(), | ||
| db_port: port, | ||
| db_database: $<HTMLInputElement>("db-database").value.trim(), | ||
| db_username: $<HTMLInputElement>("db-username").value.trim(), | ||
| db_password: $<HTMLInputElement>("db-password").value, | ||
| }; | ||
| msg.classList.remove("error"); | ||
| msg.textContent = "Sprawdzam połączenie…"; | ||
| try { | ||
| await invoke("test_db_connection", { config: candidate }); | ||
| } catch (err) { | ||
| msg.classList.add("error"); | ||
| msg.textContent = `Nie udało się połączyć: ${err}`; | ||
| return; | ||
| } | ||
| Object.assign(cfg, candidate); | ||
| await invoke("save_config", { config: cfg }); | ||
| show("db-remote", false); | ||
| show("splash", true); | ||
| resolve(); | ||
| }); | ||
| }); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Listener accumulation in ensureDbMode across boot retries.
ensureDbMode is invoked every time bootFlow() runs (Line 436), which happens again on every retryBoot() (Lines 460-471, triggered from be-db/be-local/be-retry). Each call adds fresh addEventListener handlers to the same static elements (btn-db-local, btn-db-remote, btn-db-back, db-remote-form) without ever removing the previous ones. After N retries, a single click fires N handlers, each closing over a different (potentially stale) cfg/resolve. Consequences: duplicate save_config/test_db_connection IPC calls, and a stale handler can overwrite freshly-saved settings (host/port/php_binary/backend_dir) with an older snapshot via Object.assign(cfg, candidate) + save_config.
🛠️ Suggested fix — wire listeners once, rebind state per call
+let dbModeResolve: (() => void) | null = null;
+let dbModeCfg: ServerConfig | null = null;
+let dbModeListenersWired = false;
+
+function wireDbModeListeners() {
+ if (dbModeListenersWired) return;
+ dbModeListenersWired = true;
+
+ $("btn-db-local").addEventListener("click", async () => {
+ if (!dbModeCfg) return;
+ dbModeCfg.db_mode = "local";
+ await invoke("save_config", { config: dbModeCfg });
+ show("db-choice", false);
+ show("splash", true);
+ dbModeResolve?.();
+ });
+
+ $("btn-db-remote").addEventListener("click", () => {
+ show("db-choice", false);
+ show("db-remote", true);
+ scanAndFillDbForm();
+ });
+
+ $("btn-db-back").addEventListener("click", () => {
+ show("db-remote", false);
+ show("db-choice", true);
+ });
+
+ $<HTMLFormElement>("db-remote-form").addEventListener("submit", async (e) => {
+ e.preventDefault();
+ if (!dbModeCfg) return;
+ // ... existing body, using dbModeCfg instead of cfg, dbModeResolve?.() instead of resolve()
+ });
+}
+
function ensureDbMode(cfg: ServerConfig): Promise<void> {
if (cfg.db_mode) return Promise.resolve();
+ wireDbModeListeners();
+ dbModeCfg = cfg;
show("splash", false);
show("db-choice", true);
- return new Promise((resolve) => { ... });
+ return new Promise((resolve) => { dbModeResolve = resolve; });
}🤖 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 `@desktop/src/main.ts` around lines 363 - 418, Update ensureDbMode so its
static controls receive event listeners only once, while each invocation rebinds
the current cfg and Promise resolve state used by those handlers. Preserve the
existing local, remote, back, and submit behavior, but prevent stale closures
from issuing duplicate IPC calls or overwriting newer configuration after
bootFlow retries.
| $<HTMLFormElement>("db-remote-form").addEventListener("submit", async (e) => { | ||
| e.preventDefault(); | ||
| const msg = $("db-message"); | ||
| const port = Number($<HTMLInputElement>("db-port").value) || 5432; | ||
| const candidate: ServerConfig = { | ||
| ...cfg, | ||
| db_mode: "remote", | ||
| db_driver: port === 3306 ? "mysql" : "pgsql", | ||
| db_host: $<HTMLInputElement>("db-host").value.trim(), | ||
| db_port: port, | ||
| db_database: $<HTMLInputElement>("db-database").value.trim(), | ||
| db_username: $<HTMLInputElement>("db-username").value.trim(), | ||
| db_password: $<HTMLInputElement>("db-password").value, | ||
| }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Database driver is guessed from port number instead of being explicitly selected. Root cause: the remote-DB form has no driver control, so main.ts falls back to a port === 3306 heuristic that misclassifies MySQL/Postgres on non-default ports.
desktop/src/main.ts#L388-L401: replace theport === 3306 ? "mysql" : "pgsql"heuristic with a value read from an explicit driver control.desktop/index.html#L36-L71: add a driver<select>(Postgres/MySQL) todb-remote-formsodb-driveris set by user choice rather than inferred from port.
📍 Affects 2 files
desktop/src/main.ts#L388-L401(this comment)desktop/index.html#L36-L71
🤖 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 `@desktop/src/main.ts` around lines 388 - 401, Replace the port-based driver
inference in the db-remote-form submit handler in desktop/src/main.ts (lines
388-401) with the selected value from an explicit db-driver control. Add a
Postgres/MySQL driver select to the db-remote-form in desktop/index.html (lines
36-71), ensuring its value populates the ServerConfig db_driver field.
- installer: stop leaking raw PDO exception messages to pre-auth visitors (log instead); reject path-traversal in sqlite db_database before it hits storage_path(); move admin-step validation into a CreateAdminRequest Form Request (per project convention) - tauri: harden config — withGlobalTauri=false + a CSP (permissive only for localhost so the served MES app's websockets/SSE keep working) - panel: AbortController in ensureDbMode so boot retries don't stack duplicate DB-choice listeners - updater: snapshot the SQLite DB before migrating and restore it on rollback, so restored old code never runs against the migrated schema
|
Thanks for the review — addressed in Fixed
Intentional / not changed
Full backend suite stays green (1896 tests). @coderabbitai review |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
git show --stat --oneline --decorate --no-renames 3fd73aa6
git diff 3fd73aa6^ 3fd73aa6 -- \
backend/app/Http/Controllers/InstallController.php \
backend/app/Http/Requests/Web/Install/CreateAdminRequest.php \
backend/tests/Feature/InstallPresetTest.php \
desktop/src-tauri/tauri.conf.json \
desktop/src/main.ts \
desktop/src-tauri/src/lib.rs
printf '\n--- PHP outlines ---\n'
ast-grep outline backend/app/Http/Controllers/InstallController.php --items all --type method,function --view expanded || true
if [ -f backend/app/Http/Requests/Web/Install/CreateAdminRequest.php ]; then
ast-grep outline backend/app/Http/Requests/Web/Install/CreateAdminRequest.php --items all --view expanded || true
fi
printf '\n--- Rust updater-related locations ---\n'
rg -n -C 3 'snapshot|rollback|migrat|swap|backup' desktop/src-tauri/src/lib.rs
printf '\n--- TypeScript database choice listener locations ---\n'
rg -n -C 4 'ensureDbMode|AbortController|addEventListener|abort' desktop/src/main.tsLength of output: 30246 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- Rust: database/runtime environment and update flow ---'
sed -n '430,570p' desktop/src-tauri/src/lib.rs
sed -n '1180,1250p' desktop/src-tauri/src/lib.rs
sed -n '1340,1510p' desktop/src-tauri/src/lib.rs
printf '%s\n' '--- TypeScript: DB scanning and boot/retry lifecycle ---'
sed -n '300,530p' desktop/src/main.ts
printf '%s\n' '--- References to SQLite DB filenames and DB_DATABASE ---'
rg -n -C 3 'openmes\.sqlite|DB_DATABASE|db_database|LARAVEL_STORAGE_PATH' \
desktop/src-tauri/src/lib.rs \
backend/app/Http/Controllers/InstallController.php \
backend/config/database.php \
backend/.env.example 2>/dev/null || true
printf '%s\n' '--- Tests related to updates and installer input validation ---'
rg -n -C 5 'run_update_apply|pre-update|snapshot|rollback|Invalid database path|CreateAdminRequest' \
desktop/src-tauri/src/lib.rs \
backend/tests 2>/dev/null || true
printf '%s\n' '--- Routes invoking admin creation ---'
rg -n -C 3 'createAdmin|install\.admin|install/admin' backend/routes backend/app 2>/dev/null || trueLength of output: 149086
|
* feat: required operators * fix: styles * test: fix e2e disposition flow and create English scenario variant * [feat] Per-order machines to run supervisor view #89 * Update tests and localization * fix to test can capture all step from multi users by logout and login * fix test error * clean: remove temporary development files and compiled output * translate: translate Operator Workstation and Work Order details to Vietnamese * fix: address code review comments for i18n localization and backend logic * fix: address remaining PR comments on docker-compose mount, reset script fail-fast, and e2e spec safety * fix: adjust lot pick layout font sizes and make E2E dropdown lot selection robust * fix: localize Start and Complete step buttons under Operator panel * style: polish LotPickModal row spacing and fonts to avoid vertical overlapping * fix: wait for start API response before expecting step Complete button in vi spec * fix: refine confirmBtn locator in E2E spec to avoid partial text matching * fix: unconditionally scan and fill selects in E2E lot picking modal * fix: refine createBtn button locator regex pattern * fix: use explicit workstation button label in E2E spec to avoid strict createBtn helper mismatches * fix: streamline startBtn visibility and click handler logic in E2E spec * fix: add robust re-click startBtn fallback to start batch step in E2E spec * translate: localize work order operator modals and actions to support vietnamese * fix(materials): make material_type_id optional in StoreMaterialRequest * chore: remove reset-test cmd files * fix(i18n): address PR reviews for operator controllers translation gaps * chore: remove car-production-buildout spec files * fix(i18n): align media type badge localization mapping key with select options * feat: mobile * fix: restore removed migration 2026_06_18_120000 (make material_type_id nullable) Also remove composer.phar and empty .rr.yaml from git tracking. * Address order machines review feedback * fix: logs * feat: accepnt informations * feat: finish up whole feature * fix: address CodeRabbit review on customer/priority PR - PriorityRuleRequest: reject non-"equals" conditions for the customer.tier source (a numeric comparison casts the tier string to 0 and matches nonsense). - CustomerMetricsService: lock and re-check the work order inside the transaction so concurrent completions can't double-count revenue/orders. - Priority Settings band editor: block save when any threshold is blank instead of silently coercing an empty field to 0. Adds tests and en/pl strings. Full suite green (1690). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(workstation): shop-floor device registration + live admin roster (#175) * feat(workstation): shop-floor device registration + live admin roster Workstation client PCs self-register against the MAIN app by IP and heartbeat; Admin → Structure → Workstation Devices shows them live (online/offline derived from last_seen_at) via an Electric shape. - Public, rate-limited API: POST /api/workstations/register + /heartbeat - WorkstationDevice model (soft-delete + audit), migration with a partial unique index on device_uuid, factory - Web admin roster page (ResourceTable) + forget (soft-delete) - Registered in SoftDeleteRegistry + ShapeRegistry; i18n en/pl parity - Tests: registration/heartbeat/validation, admin authz + soft-delete * docs(roadmap): add workflow/UX orchestration items from field feedback Operator/supervisor/scheduler workflow items surfaced by a high-volume plant's feedback (unified production+scrap reporting, role-based entry points, machine-counter reporting, shift board, start-of-shift + multi-day Gantt scheduling, made-to-order vs stock order entry). Tagged 'probable'. * ci(release): build desktop installers (win/mac/linux) + Android apk (#187) Extends the release workflow so a tag/dispatch produces installers for every platform, all attached to the same GitHub Release: - web: existing self-contained ZIP (unchanged) — creates the release - desktop: Tauri matrix (windows .exe/NSIS, macOS .dmg, linux .deb/.rpm), each bundling PHP + the backend; PHP runtime fetched per-OS, backend built (composer + vite) and injected via bundle-resources + --config - mobile: Expo prebuild + Gradle assembleRelease -> Android .apk Jobs fan out from the web job (needs: web) and upload to the release by tag, so a desktop/mobile failure never blocks the web artifact. * feat(desktop): OpenMES Desktop app (Tauri) + unattended install preset (#186) * feat(desktop): OpenMES Desktop app (Tauri) + unattended install preset Brings the desktop platform app to develop alongside mobile/. A Tauri (Rust + webview) shell that bundles PHP + the backend and runs OpenMES as a local desktop app / LAN MES server, with an in-app self-updater. - desktop/: Tauri app (src-tauri Rust, TS control panel, icons, build scripts for self-contained Windows .exe and Linux .deb/.rpm) - Backend INSTALLER_PRESET mode: unattended install (skips env/db wizard steps, straight to admin creation) that the desktop shell drives — InstallController + install views + bootstrap/app.php - InstallPresetTest covers the preset behaviour The shop-floor workstation feature lands separately in #175. * fix(desktop): address CodeRabbit review on #186 - installer: stop leaking raw PDO exception messages to pre-auth visitors (log instead); reject path-traversal in sqlite db_database before it hits storage_path(); move admin-step validation into a CreateAdminRequest Form Request (per project convention) - tauri: harden config — withGlobalTauri=false + a CSP (permissive only for localhost so the served MES app's websockets/SSE keep working) - panel: AbortController in ensureDbMode so boot retries don't stack duplicate DB-choice listeners - updater: snapshot the SQLite DB before migrating and restore it on rollback, so restored old code never runs against the migrated schema * feat(work-orders): add elapsed-time "Age" column to work-order lists (#185) * feat(work-orders): add elapsed-time "Age" column to work-order lists Add an "Age" column (now - created_at) to the admin and supervisor work-order lists, showing a compact human-readable duration (just now / 5m / 3h / 2d / 1y) with the exact creation time on hover. - New reusable elapsed() formatter in lib/i18n.js - Opt-in `live: true` column flag on ResourceTable: a shared 30s clock ticks live cells via context, so tables without one schedule no timer - Opt-in `sortAccessor` column flag so "Age" sorts by actual age (ascending = youngest first) while global search keeps using created_at - Vitest setup covering the duration formatting; wired into CI + pre-commit created_at was already synced on the work-order Electric shapes, so no schema or shape change. Closes #100 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(work-orders): isolate live-clock ticks to a provider component Move the 30s clock state out of ResourceTable into a small LiveClockProvider wrapper, so a tick re-renders only the provider and the NowContext consumers (the live cells) rather than the whole ResourceTable body. Previously the tick re-ran the live query/filter and handed DataTable freshly allocated props (visibleRows, the inline rangeLabel), defeating its memoization and reconciling the entire grid every 30s. Addresses CodeRabbit review on #185. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(routes): drop dangling __e2e__ block; 404 (not 500) on non-numeric report ids (#188) - Remove the WIP `__e2e__` test-control route group and its import: it referenced App\Http\Controllers\TestControl\TenantController, the EnsureE2eEnabled middleware and config/e2e.php — none of which exist in the repo (leaked from 'feat: custom fields wip'). It broke `php artisan route:list` and would 500 on /__e2e__/* instead of the intended 404. No spec references it; reintroduce complete (routes + controller + middleware + config) if the isolated-tenant harness is wanted. - Constrain reports.show / cost-reports.show to `whereNumber('workOrder')` so a non-numeric id (e.g. /admin/reports/net-requirements) 404s instead of hitting the controller with a string id and 500ing on an invalid-integer query. * feat(bom): select multi bom in orders (#183) * feat(bom): select multi bom in orders * fix(refactor): code fixes reported in code review by CodeRabbit --------- Co-authored-by: jakub-przepiora <jakub.przepioraa@gmail.com> * fix(e2e): close unterminated test.describe in material-hold-release spec (#189) The spec was missing the final `});` closing its `test.describe(...)` block, so Playwright failed to parse it — and because collection aborts on the first unparseable file, this broke the ENTIRE E2E suite (0 specs could run). E2E isn't part of the CI test gate, so it slipped onto develop unnoticed. Adds the missing close; the file now parses and lists its test. * chore(release): v0.17.0 --------- Co-authored-by: Mateusz Łuczyński <mateusz.luczynski01@gmail.com> Co-authored-by: AlexAnh <soibien@gmail.com> Co-authored-by: ElNinio978 <Kacperwos2005@gmail.com> Co-authored-by: JanKolo04 <jankolodziej04@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Mateusz Łuczyński <100802495+Svannte@users.noreply.github.com> Co-authored-by: Jan Kołodziej <76879087+JanKolo04@users.noreply.github.com>
Summary
Brings the OpenMES Desktop app to
develop, alongsidemobile/. It's a Tauri (Rust + system webview) shell that bundles PHP + the backend and runs OpenMES as a local desktop app / LAN MES server — no separate PHP/Postgres install on the client.What's included
desktop/— the Tauri app:src-tauri/(Rust): resolves/serves the backend via bundled PHP, manages a local DB (SQLite on Linux, bundled PostgreSQL on Windows), tray + control panel, and an in-app self-updater (staged atomic swap of the managed backend copy; self-contained release ZIP, no composer/npm on the client).src/), OpenMES icons, and build scripts producing self-contained installers: Windows.exe(NSIS) and Linux.deb/.rpm.INSTALLER_PRESETmode — an unattended install path the desktop shell drives: skips the env/database wizard steps and goes straight to admin creation. TouchesInstallController, theinstall/*views andbootstrap/app.php; covered byInstallPresetTest.Testing
InstallPresetTest: 6 passed on the develop base..exe(86 MB) and Linux.deb(65 MB) both produced and self-contained; the in-app updater ran a full download → verify → migrate → atomic swap → restart cycle.Notes
desktop/src-tauri/resources/,winphp-staging/) are gitignored and fetched viascripts/fetch-runtimes.sh.🤖 Generated with claude-flow
Summary by CodeRabbit
New Features
Documentation
Style