added dynamic configuration of Port in Desktop app
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
# Desktop Runtime Configuration
|
||||
|
||||
The BaMoRT desktop app now uses **runtime configuration** for the API port, meaning you can change the port in the `.env` file without rebuilding the app.
|
||||
|
||||
## How It Works
|
||||
|
||||
### Backend (Go)
|
||||
- **[desktop/main.go](desktop/main.go)**: The `App.GetAPIBaseURL()` method reads the configured port from `config.Cfg` and returns the full API URL
|
||||
- This method is bound to the frontend via Wails, making it callable from JavaScript
|
||||
|
||||
### Frontend (Vue/JS)
|
||||
- **[frontend/src/utils/config.js](frontend/src/utils/config.js)**: Detects if running in Wails and calls the Go backend to get the API URL at runtime
|
||||
- **[frontend/src/utils/api.js](frontend/src/utils/api.js)**: Uses the dynamic config instead of a hardcoded VITE_API_URL
|
||||
- The API URL is cached after first retrieval for performance
|
||||
|
||||
### Build Process
|
||||
- **[frontend/package.json](frontend/package.json)**: The `build:desktop` script no longer hardcodes `VITE_API_URL`
|
||||
- The frontend bundle is now port-agnostic
|
||||
|
||||
## Usage
|
||||
|
||||
### Change the API Port
|
||||
|
||||
1. Edit `desktop/.env`:
|
||||
```env
|
||||
API_PORT=8185 # Change to any port you want
|
||||
```
|
||||
|
||||
2. Run the app - no rebuild needed!
|
||||
```bash
|
||||
./desktop/build/bin/bamort
|
||||
```
|
||||
|
||||
The frontend will automatically connect to the configured port.
|
||||
|
||||
### Environment Detection
|
||||
|
||||
The config system automatically detects the environment:
|
||||
|
||||
- **Desktop (Wails)**: Calls `window.go.main.App.GetAPIBaseURL()` to get the URL from Go backend
|
||||
- **Web (Development)**: Uses `import.meta.env.VITE_API_URL` from Vite
|
||||
- **Web (Production)**: Defaults to `https://bamort-api.trokan.de`
|
||||
|
||||
## Fallback Behavior
|
||||
|
||||
If the desktop app can't reach the Go backend (shouldn't happen), it falls back to `http://localhost:8185`.
|
||||
|
||||
## Benefits
|
||||
|
||||
✅ Change API port without rebuilding
|
||||
✅ Faster iteration during development
|
||||
✅ Same binary works with different configurations
|
||||
✅ Cleaner separation of config from code
|
||||
|
||||
## Technical Details
|
||||
|
||||
**Request Flow:**
|
||||
1. Frontend makes API request
|
||||
2. Axios interceptor checks if `baseURL` is set
|
||||
3. If not set, calls `getAPIBaseURL()` from config.js
|
||||
4. Config.js detects Wails environment via `window.go`
|
||||
5. Calls Go backend's `GetAPIBaseURL()` method
|
||||
6. Caches the result for subsequent requests
|
||||
7. Request proceeds with correct baseURL
|
||||
+13
-3
@@ -1,6 +1,6 @@
|
||||
// BaMoRT Desktop application entry point.
|
||||
// Uses Wails v2 to wrap the existing Gin HTTP server in a native desktop window.
|
||||
// The backend runs on localhost:8180 (SQLite); the WebView loads the bundled frontend.
|
||||
// The backend runs on localhost:8185 (SQLite); the WebView loads the bundled frontend.
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -56,8 +56,15 @@ func (a *App) startup(ctx context.Context) {
|
||||
startGinServer()
|
||||
}
|
||||
|
||||
// GetAPIBaseURL returns the HTTP server address for the frontend to connect to.
|
||||
// This allows the API port to be configured at runtime via .env without rebuilding.
|
||||
func (a *App) GetAPIBaseURL() string {
|
||||
// GetServerAddress returns ":port", so we need to add localhost
|
||||
return "http://localhost" + config.Cfg.GetServerAddress()
|
||||
}
|
||||
|
||||
// startGinServer initialises the database, runs migrations, and starts the
|
||||
// Gin HTTP server on the configured port (default 8180) in a goroutine.
|
||||
// Gin HTTP server on the configured port (default 8185) in a goroutine.
|
||||
func startGinServer() {
|
||||
cfg := config.Cfg
|
||||
|
||||
@@ -131,7 +138,7 @@ func main() {
|
||||
_ = os.Setenv("ENVIRONMENT", "desktop")
|
||||
}
|
||||
if os.Getenv("API_PORT") == "" {
|
||||
_ = os.Setenv("API_PORT", "8180")
|
||||
_ = os.Setenv("API_PORT", "8185")
|
||||
}
|
||||
if os.Getenv("TEMPLATES_DIR") == "" {
|
||||
_ = os.Setenv("TEMPLATES_DIR", "./templates")
|
||||
@@ -158,6 +165,9 @@ func main() {
|
||||
MinWidth: 1024,
|
||||
MinHeight: 768,
|
||||
OnStartup: app.startup,
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
},
|
||||
AssetServer: &assetserver.Options{
|
||||
Assets: frontendFS,
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"build:desktop": "DESKTOP_BUILD=1 VITE_API_URL=http://localhost:8180 vite build",
|
||||
"build:desktop": "DESKTOP_BUILD=1 vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1 +1 @@
|
||||
a98925179cb067ef9c9e5871faa6f8c7
|
||||
705a38c2f133892600ec967917c4d556
|
||||
@@ -1,12 +1,33 @@
|
||||
import axios from 'axios'
|
||||
import { getAPIBaseURL } from './config'
|
||||
|
||||
const API = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL || 'https://bamort-api.trokan.de', // Use env variable with fallback
|
||||
})
|
||||
// Create API instance without baseURL - will be set dynamically
|
||||
const API = axios.create({})
|
||||
|
||||
// Request interceptor to add auth token
|
||||
let baseURLPromise = null
|
||||
let baseURLResolved = false
|
||||
|
||||
// Get base URL (cached after first call)
|
||||
async function ensureBaseURL() {
|
||||
if (baseURLResolved) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!baseURLPromise) {
|
||||
baseURLPromise = getAPIBaseURL()
|
||||
}
|
||||
|
||||
const baseURL = await baseURLPromise
|
||||
API.defaults.baseURL = baseURL
|
||||
baseURLResolved = true
|
||||
}
|
||||
|
||||
// Request interceptor to add auth token and ensure baseURL is set
|
||||
API.interceptors.request.use(
|
||||
(config) => {
|
||||
async (config) => {
|
||||
// Ensure baseURL is set before request
|
||||
await ensureBaseURL()
|
||||
|
||||
const token = localStorage.getItem('token')
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Runtime configuration for BaMoRT frontend
|
||||
*
|
||||
* For desktop builds (Wails), reads the API base URL from the Go backend
|
||||
* to allow runtime configuration via .env without rebuilding.
|
||||
*
|
||||
* For web builds, uses VITE_API_URL environment variable.
|
||||
*/
|
||||
|
||||
let cachedAPIBaseURL = null
|
||||
|
||||
/**
|
||||
* Check if we're running in a Wails desktop app
|
||||
*/
|
||||
function isWailsApp() {
|
||||
return typeof window !== 'undefined' &&
|
||||
window['go'] !== undefined &&
|
||||
window['go']['main'] !== undefined &&
|
||||
window['go']['main']['App'] !== undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for Wails to initialize (max 3 seconds)
|
||||
*/
|
||||
async function waitForWails(maxAttempts = 30) {
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
if (isWailsApp()) {
|
||||
return true
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the API base URL dynamically
|
||||
* - In Wails desktop app: calls Go backend to get configured URL
|
||||
* - In web app: uses VITE_API_URL or defaults to production API
|
||||
*/
|
||||
export async function getAPIBaseURL() {
|
||||
// Return cached value if available
|
||||
if (cachedAPIBaseURL) {
|
||||
return cachedAPIBaseURL
|
||||
}
|
||||
|
||||
// Check if this looks like a desktop environment
|
||||
const isDesktop = typeof window !== 'undefined' &&
|
||||
(window.location.protocol === 'wails:' ||
|
||||
window.location.hostname === 'wails.localhost')
|
||||
|
||||
// Wails desktop app - wait for Wails to be ready
|
||||
if (isDesktop || typeof window !== 'undefined' && window['go']) {
|
||||
const wailsReady = await waitForWails()
|
||||
|
||||
if (wailsReady) {
|
||||
try {
|
||||
// Access Wails Go bindings via window object
|
||||
const url = await window['go']['main']['App']['GetAPIBaseURL']()
|
||||
cachedAPIBaseURL = url
|
||||
console.log('Desktop app using API URL from config:', url)
|
||||
return url
|
||||
} catch (error) {
|
||||
console.error('Failed to get API URL from Wails:', error)
|
||||
// Fallback to localhost for desktop
|
||||
cachedAPIBaseURL = 'http://localhost:8185'
|
||||
return cachedAPIBaseURL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Web app - use environment variable or production default
|
||||
cachedAPIBaseURL = import.meta.env.VITE_API_URL || 'https://bamort-api.trokan.de'
|
||||
console.log('Web app using API URL:', cachedAPIBaseURL)
|
||||
return cachedAPIBaseURL
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if running in desktop mode (Wails)
|
||||
*/
|
||||
export function isDesktopMode() {
|
||||
return isWailsApp()
|
||||
}
|
||||
@@ -44,6 +44,7 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { getAPIBaseURL } from '../utils/config'
|
||||
import axios from 'axios'
|
||||
import { getVersion, getGitCommit } from '../version'
|
||||
|
||||
@@ -80,7 +81,7 @@ export default {
|
||||
methods: {
|
||||
async fetchBackendVersion() {
|
||||
try {
|
||||
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:8180'
|
||||
const apiUrl = await getAPIBaseURL()
|
||||
const response = await axios.get(`${apiUrl}/api/public/version`)
|
||||
|
||||
if (response.data) {
|
||||
|
||||
@@ -115,6 +115,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAPIBaseURL } from '../utils/config'
|
||||
import axios from 'axios'
|
||||
import { getVersion, getGitCommit } from '../version'
|
||||
|
||||
@@ -157,7 +158,7 @@ export default {
|
||||
methods: {
|
||||
async fetchBackendVersion() {
|
||||
try {
|
||||
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:8180'
|
||||
const apiUrl = await getAPIBaseURL()
|
||||
const response = await axios.get(`${apiUrl}/api/public/systeminfo`)
|
||||
|
||||
if (response.data) {
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export function GetAPIBaseURL():Promise<string>;
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export function GetAPIBaseURL() {
|
||||
return window['go']['main']['App']['GetAPIBaseURL']();
|
||||
}
|
||||
Reference in New Issue
Block a user