Desktop app dynamic config of API Port (#40)
* added dynamic configuration of Port in Desktop app * added dynamic configuration of API_URL to docker deployments. Now it works with editing only .env file.
This commit is contained in:
@@ -13,6 +13,13 @@ dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Runtime configuration (use config.json.example as template)
|
||||
public/config.json
|
||||
|
||||
# Docker entrypoint scripts (copied from ../docker/ during build)
|
||||
docker-entrypoint.sh
|
||||
docker-dev-entrypoint.sh
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"apiBaseURL": "http://localhost:8180",
|
||||
"_comment": "This file can be modified at deployment without rebuilding. Copy to config.json and adjust apiBaseURL for your environment."
|
||||
}
|
||||
@@ -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,134 @@
|
||||
/**
|
||||
* 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 runtime config.json or auto-detection:
|
||||
* 1. Try to load /config.json (can be modified at deployment without rebuild)
|
||||
* 2. Try to detect backend at same origin (production reverse proxy setup)
|
||||
* 3. Fall back to VITE_API_URL (development) or same origin
|
||||
*/
|
||||
|
||||
let cachedAPIBaseURL = null
|
||||
|
||||
/**
|
||||
* Try to load configuration from /config.json
|
||||
*/
|
||||
async function loadConfigFile() {
|
||||
try {
|
||||
const response = await fetch('/config.json', {
|
||||
cache: 'no-cache',
|
||||
headers: { 'Accept': 'application/json' }
|
||||
})
|
||||
if (response.ok) {
|
||||
// Check if response is actually JSON (not HTML from SPA fallback)
|
||||
const contentType = response.headers.get('content-type')
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
const config = await response.json()
|
||||
if (config.apiBaseURL) {
|
||||
console.log('Loaded API URL from config.json:', config.apiBaseURL)
|
||||
return config.apiBaseURL
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// config.json doesn't exist or is invalid, that's okay
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to detect backend at same origin (production setup)
|
||||
*/
|
||||
async function detectBackendAtOrigin() {
|
||||
try {
|
||||
const origin = window.location.origin
|
||||
const response = await fetch(`${origin}/api/public/version`, {
|
||||
method: 'GET',
|
||||
cache: 'no-cache',
|
||||
signal: AbortSignal.timeout(2000) // 2 second timeout
|
||||
})
|
||||
if (response.ok) {
|
||||
console.log('Detected backend at same origin:', origin)
|
||||
return origin
|
||||
}
|
||||
} catch (error) {
|
||||
// Backend not at same origin, that's okay
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the API base URL dynamically
|
||||
* - In Wails desktop app: calls Go backend to get configured URL
|
||||
* - In web app: tries config.json, auto-detection, or VITE_API_URL
|
||||
*/
|
||||
export async function getAPIBaseURL() {
|
||||
// Return cached value if available
|
||||
if (cachedAPIBaseURL) {
|
||||
return cachedAPIBaseURL
|
||||
}
|
||||
|
||||
// Try Wails desktop app first (with timeout)
|
||||
if (typeof window !== 'undefined' && window['go']) {
|
||||
// Wait up to 3 seconds for Wails bindings to be ready
|
||||
for (let i = 0; i < 30; i++) {
|
||||
try {
|
||||
if (window['go']?.['main']?.['App']?.['GetAPIBaseURL']) {
|
||||
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)
|
||||
break
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
}
|
||||
|
||||
// Wails detected but binding failed - use desktop fallback
|
||||
cachedAPIBaseURL = 'http://localhost:8185'
|
||||
console.log('Desktop app using fallback:', cachedAPIBaseURL)
|
||||
return cachedAPIBaseURL
|
||||
}
|
||||
|
||||
// Web app - try multiple strategies
|
||||
|
||||
// Strategy 1: Load from config.json (can be modified at deployment)
|
||||
const configFileURL = await loadConfigFile()
|
||||
if (configFileURL) {
|
||||
cachedAPIBaseURL = configFileURL
|
||||
return cachedAPIBaseURL
|
||||
}
|
||||
|
||||
// Strategy 2: Check if backend is at same origin (production reverse proxy)
|
||||
if (window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1') {
|
||||
const sameOriginURL = await detectBackendAtOrigin()
|
||||
if (sameOriginURL) {
|
||||
cachedAPIBaseURL = sameOriginURL
|
||||
return cachedAPIBaseURL
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 3: Use VITE_API_URL (development) or fallback to same origin
|
||||
if (import.meta.env.VITE_API_URL) {
|
||||
cachedAPIBaseURL = import.meta.env.VITE_API_URL
|
||||
console.log('Web app using VITE_API_URL:', cachedAPIBaseURL)
|
||||
} else {
|
||||
// Final fallback: assume same origin for production
|
||||
cachedAPIBaseURL = window.location.origin
|
||||
console.log('Web app using same origin:', cachedAPIBaseURL)
|
||||
}
|
||||
|
||||
return cachedAPIBaseURL
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if running in desktop mode (Wails)
|
||||
*/
|
||||
export function isDesktopMode() {
|
||||
return isWailsApp()
|
||||
}typeof window !== 'undefined' &&
|
||||
window['go']?.['main']?.['App'] !== undefined
|
||||
@@ -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