Compare commits
4 Commits
main
...
bugfix-0.2.5
| Author | SHA1 | Date | |
|---|---|---|---|
| 57ff19b8ee | |||
| 261a6294cb | |||
| bb9ef4f77e | |||
| c6539d17f4 |
@@ -0,0 +1,174 @@
|
||||
# Runtime Configuration Implementation Summary
|
||||
|
||||
## What Was Implemented
|
||||
|
||||
Enhanced the BaMoRT frontend to support runtime configuration for **both desktop (Wails) and web production** deployments, eliminating the need to rebuild when changing API URLs.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Desktop (Wails)
|
||||
- Uses Go bindings to read API URL from `.env` file at runtime
|
||||
- Method: `window['go']['main']['App']['GetAPIBaseURL']()`
|
||||
- Configuration: `desktop/.env` → `API_PORT` variable
|
||||
- No rebuild needed when changing port
|
||||
|
||||
### Web Production (NEW)
|
||||
Uses a **multi-strategy fallback system**:
|
||||
|
||||
1. **config.json** (Priority 1)
|
||||
- Loads `/config.json` from web root
|
||||
- Can be modified after deployment
|
||||
- Example: `{"apiBaseURL": "https://api.yourdomain.com"}`
|
||||
|
||||
2. **Auto-detection** (Priority 2)
|
||||
- Probes `/api/public/version` at same origin
|
||||
- Works automatically with reverse proxy setups
|
||||
- Detects if backend and frontend share same domain
|
||||
|
||||
3. **VITE_API_URL** (Priority 3)
|
||||
- Environment variable at build time
|
||||
- Used for development: `VITE_API_URL=http://localhost:8180`
|
||||
|
||||
4. **Same Origin Fallback** (Priority 4)
|
||||
- Uses `window.location.origin`
|
||||
- Assumes backend and frontend on same domain
|
||||
|
||||
## Files Modified
|
||||
|
||||
### Core Implementation
|
||||
- `/data/dev/bamort/frontend/src/utils/config.js` - Enhanced with multi-strategy config loading
|
||||
- `/data/dev/bamort/frontend/src/utils/api.js` - Uses dynamic baseURL (already done for desktop)
|
||||
- `/data/dev/bamort/desktop/main.go` - GetAPIBaseURL() Go binding (already done for desktop)
|
||||
|
||||
### Documentation
|
||||
- `/data/dev/bamort/frontend/RUNTIME_CONFIG.md` - Complete web configuration guide
|
||||
- `/data/dev/bamort/desktop/RUNTIME_CONFIG.md` - Desktop configuration guide (existing)
|
||||
|
||||
### Configuration Files
|
||||
- `/data/dev/bamort/frontend/public/config.json.example` - Template for deployment
|
||||
- `/data/dev/bamort/frontend/.gitignore` - Excludes `public/config.json` from git
|
||||
|
||||
## How It Works
|
||||
|
||||
### For Web Development
|
||||
```bash
|
||||
cd frontend
|
||||
VITE_API_URL=http://localhost:8180 npm run dev
|
||||
```
|
||||
|
||||
### For Web Production Deployment
|
||||
|
||||
**Option A: Using config.json (Recommended)**
|
||||
```bash
|
||||
# 1. Build once
|
||||
cd frontend && npm run build
|
||||
|
||||
# 2. Deploy dist/ to web server
|
||||
|
||||
# 3. Create config.json in web root
|
||||
cat > /var/www/bamort/config.json <<EOF
|
||||
{
|
||||
"apiBaseURL": "https://api.production.com"
|
||||
}
|
||||
EOF
|
||||
|
||||
# 4. Change API URL anytime without rebuild!
|
||||
```
|
||||
|
||||
**Option B: Reverse Proxy Setup**
|
||||
Example nginx configuration:
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name yourdomain.com;
|
||||
|
||||
# Frontend
|
||||
location / {
|
||||
root /var/www/bamort;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Backend API
|
||||
location /api {
|
||||
proxy_pass http://localhost:8180;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
```
|
||||
Frontend auto-detects and uses same origin.
|
||||
|
||||
**Option C: Build with Embedded URL**
|
||||
```bash
|
||||
VITE_API_URL=https://api.production.com npm run build
|
||||
```
|
||||
(Not recommended - requires rebuild for URL changes)
|
||||
|
||||
### For Desktop (Wails)
|
||||
```bash
|
||||
# 1. Build once
|
||||
cd desktop && wails build
|
||||
|
||||
# 2. Change port in .env (no rebuild!)
|
||||
echo "API_PORT=9000" > desktop/.env
|
||||
|
||||
# 3. Run app - uses new port automatically
|
||||
./desktop/build/bin/bamort
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
Open browser console when loading the app. Look for one of these messages:
|
||||
|
||||
**Desktop:**
|
||||
```
|
||||
Desktop app using API URL from config: http://localhost:8185
|
||||
```
|
||||
|
||||
**Web:**
|
||||
```
|
||||
Loaded API URL from config.json: https://api.yourdomain.com
|
||||
```
|
||||
or
|
||||
```
|
||||
Detected backend at same origin: https://yourdomain.com
|
||||
```
|
||||
or
|
||||
```
|
||||
Web app using VITE_API_URL: http://localhost:8180
|
||||
```
|
||||
or
|
||||
```
|
||||
Web app using same origin: https://yourdomain.com
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
✅ **No rebuild needed** for API URL changes in production web deployments
|
||||
✅ **Same build artifact** can be deployed to dev/staging/production
|
||||
✅ **Infrastructure-friendly** - works with reverse proxies, Docker, static hosting
|
||||
✅ **Flexible** - multiple configuration strategies with sensible fallbacks
|
||||
✅ **Dev-friendly** - VITE_API_URL still works for development
|
||||
✅ **Desktop-friendly** - reads .env at runtime (already implemented)
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [x] Frontend builds successfully (`npm run build`)
|
||||
- [x] Desktop app reads runtime config from .env
|
||||
- [ ] Web dev mode works with VITE_API_URL
|
||||
- [ ] Web production with config.json works
|
||||
- [ ] Web production with reverse proxy auto-detection works
|
||||
- [ ] Web production with same-origin fallback works
|
||||
|
||||
## Migration Path
|
||||
|
||||
**For existing deployments:**
|
||||
1. No changes needed! Current builds continue working
|
||||
2. To enable runtime config: Just add `config.json` to your web root
|
||||
3. Old builds with VITE_API_URL still work as fallback
|
||||
|
||||
**For new deployments:**
|
||||
1. Build once: `npm run build`
|
||||
2. Deploy `dist/` contents
|
||||
3. Add `config.json` with your API URL
|
||||
4. Done!
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
// Version is the application version
|
||||
const Version = "0.2.4"
|
||||
const Version = "0.2.5"
|
||||
|
||||
var (
|
||||
// GitCommit will be set by build flags or detected at runtime
|
||||
|
||||
@@ -147,7 +147,17 @@ func UpdateCharacter(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, character)
|
||||
// Reload character to get updated data
|
||||
var updatedCharacter models.Char
|
||||
err = updatedCharacter.FirstID(id)
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Failed to reload character")
|
||||
return
|
||||
}
|
||||
|
||||
// Return as FeChar with categorized skills
|
||||
feChar := ToFeChar(&updatedCharacter)
|
||||
c.JSON(http.StatusOK, feChar)
|
||||
}
|
||||
func DeleteCharacter(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
+31
-17
@@ -8,23 +8,37 @@ import (
|
||||
)
|
||||
|
||||
func SetupGin(r *gin.Engine) {
|
||||
// Build allowed origins list from configuration
|
||||
allowedOrigins := []string{
|
||||
config.Cfg.FrontendURL,
|
||||
"http://localhost:5173", // Development frontend
|
||||
"http://192.168.0.48:5173", // Development frontend
|
||||
"http://192.168.0.36:5173", // Development frontend
|
||||
"https://bamort.trokan.de", // Production frontend
|
||||
var corsConfig cors.Config
|
||||
|
||||
// Desktop: the Wails WebView origin varies by platform/version (wails.localhost,
|
||||
// localhost, null …). Since the server is local, allow all origins.
|
||||
if config.Cfg.Environment == "desktop" {
|
||||
corsConfig = cors.Config{
|
||||
AllowOriginFunc: func(_ string) bool { return true },
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
||||
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * 3600,
|
||||
}
|
||||
} else {
|
||||
allowedOrigins := []string{
|
||||
config.Cfg.FrontendURL,
|
||||
"http://localhost:5173", // Development frontend
|
||||
"http://192.168.0.48:5173", // Development frontend
|
||||
"http://192.168.0.36:5173", // Development frontend
|
||||
"https://bamort.trokan.de", // Production frontend
|
||||
"http://wails.localhost", // Wails desktop WebView
|
||||
}
|
||||
corsConfig = cors.Config{
|
||||
AllowOrigins: allowedOrigins,
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
||||
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * 3600,
|
||||
}
|
||||
}
|
||||
|
||||
// Add CORS middleware
|
||||
r.Use(cors.New(cors.Config{
|
||||
//AllowOrigins: []string{"http://localhost:3000"}, // Replace with your frontend's URL
|
||||
AllowOrigins: allowedOrigins,
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
|
||||
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * 3600, // Cache preflight for 12 hours
|
||||
}))
|
||||
r.Use(cors.New(corsConfig))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Generated by `wails build` – do not commit built artefacts
|
||||
frontend/dist/*
|
||||
!frontend/dist/.gitkeep
|
||||
|
||||
# Wails build outputs
|
||||
build/bin/
|
||||
@@ -0,0 +1,24 @@
|
||||
# Desktop App for BaMoRT
|
||||
|
||||
To use it as a local Desktop App we packaged it into Wails.
|
||||
Frontend and Backend are still the same code but the database, for now, is limited to SQLite
|
||||
|
||||
# How to build
|
||||
preinstall
|
||||
in Debian 13
|
||||
|
||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
|
||||
|
||||
## Install Wails CLI once
|
||||
|
||||
go install github.com/wailsapp/wails/v2/cmd/wails@latest
|
||||
|
||||
## Development (Wails proxies the Vite dev server, run Vite separately first)
|
||||
|
||||
cd desktop && wails dev
|
||||
|
||||
## Production desktop binary
|
||||
|
||||
cd desktop && wails build
|
||||
|
||||
## Output: desktop/build/bin/bamort
|
||||
@@ -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
|
||||
Vendored
@@ -0,0 +1,86 @@
|
||||
module bamort-desktop
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
bamort v0.0.0
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/wailsapp/wails/v2 v2.11.0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/bep/debounce v1.2.1 // indirect
|
||||
github.com/bytedance/sonic v1.12.8 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.3 // indirect
|
||||
github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d // indirect
|
||||
github.com/chromedp/chromedp v0.14.2 // indirect
|
||||
github.com/chromedp/sysutil v1.1.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gin-contrib/cors v1.7.3 // indirect
|
||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
||||
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.24.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.4.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/hhrutter/lzw v1.0.0 // indirect
|
||||
github.com/hhrutter/pkcs7 v0.2.0 // indirect
|
||||
github.com/hhrutter/tiff v1.0.2 // indirect
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/labstack/echo/v4 v4.13.3 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
|
||||
github.com/leaanthony/gosod v1.0.4 // indirect
|
||||
github.com/leaanthony/slicer v1.6.0 // indirect
|
||||
github.com/leaanthony/u v1.1.1 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pdfcpu/pdfcpu v0.11.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/samber/lo v1.49.1 // indirect
|
||||
github.com/tkrajina/go-reflector v0.5.8 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/wailsapp/go-webview2 v1.0.22 // indirect
|
||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||
golang.org/x/arch v0.13.0 // indirect
|
||||
golang.org/x/crypto v0.43.0 // indirect
|
||||
golang.org/x/image v0.32.0 // indirect
|
||||
golang.org/x/net v0.45.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
google.golang.org/protobuf v1.36.4 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.5.7 // indirect
|
||||
gorm.io/driver/sqlite v1.5.7 // indirect
|
||||
gorm.io/gorm v1.25.12 // indirect
|
||||
)
|
||||
|
||||
replace bamort => ../backend
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||
github.com/bytedance/sonic v1.12.8 h1:4xYRVRlXIgvSZ4e8iVTlMF5szgpXd4AfvuWgA8I8lgs=
|
||||
github.com/bytedance/sonic v1.12.8/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
|
||||
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d h1:ZtA1sedVbEW7EW80Iz2GR3Ye6PwbJAJXjv7D74xG6HU=
|
||||
github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
|
||||
github.com/chromedp/chromedp v0.14.2 h1:r3b/WtwM50RsBZHMUm9fsNhhzRStTHrKdr2zmwbZSzM=
|
||||
github.com/chromedp/chromedp v0.14.2/go.mod h1:rHzAv60xDE7VNy/MYtTUrYreSc0ujt2O1/C3bzctYBo=
|
||||
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
|
||||
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/gin-contrib/cors v1.7.3 h1:hV+a5xp8hwJoTw7OY+a70FsL8JkVVFTXw9EcfrYUdns=
|
||||
github.com/gin-contrib/cors v1.7.3/go.mod h1:M3bcKZhxzsvI+rlRSkkxHyljJt1ESd93COUvemZ79j4=
|
||||
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 h1:iizUGZ9pEquQS5jTGkh4AqeeHCMbfbjeb0zMt0aEFzs=
|
||||
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
|
||||
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
|
||||
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0=
|
||||
github.com/hhrutter/lzw v1.0.0/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo=
|
||||
github.com/hhrutter/pkcs7 v0.2.0 h1:i4HN2XMbGQpZRnKBLsUwO3dSckzgX142TNqY/KfXg+I=
|
||||
github.com/hhrutter/pkcs7 v0.2.0/go.mod h1:aEzKz0+ZAlz7YaEMY47jDHL14hVWD6iXt0AgqgAvWgE=
|
||||
github.com/hhrutter/tiff v1.0.2 h1:7H3FQQpKu/i5WaSChoD1nnJbGx4MxU5TlNqqpxw55z8=
|
||||
github.com/hhrutter/tiff v1.0.2/go.mod h1:pcOeuK5loFUE7Y/WnzGw20YxUdnqjY1P0Jlcieb/cCw=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
||||
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
|
||||
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
|
||||
github.com/leaanthony/gosod v1.0.4 h1:YLAbVyd591MRffDgxUOU1NwLhT9T1/YiwjKZpkNFeaI=
|
||||
github.com/leaanthony/gosod v1.0.4/go.mod h1:GKuIL0zzPj3O1SdWQOdgURSuhkF+Urizzxh26t9f1cw=
|
||||
github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js=
|
||||
github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8=
|
||||
github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M=
|
||||
github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
|
||||
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||
github.com/pdfcpu/pdfcpu v0.11.1 h1:htHBSkGH5jMKWC6e0sihBFbcKZ8vG1M67c8/dJxhjas=
|
||||
github.com/pdfcpu/pdfcpu v0.11.1/go.mod h1:pP3aGga7pRvwFWAm9WwFvo+V68DfANi9kxSQYioNYcw=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ=
|
||||
github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/wailsapp/go-webview2 v1.0.22 h1:YT61F5lj+GGaat5OB96Aa3b4QA+mybD0Ggq6NZijQ58=
|
||||
github.com/wailsapp/go-webview2 v1.0.22/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
|
||||
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
||||
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
|
||||
github.com/wailsapp/wails/v2 v2.11.0 h1:seLacV8pqupq32IjS4Y7V8ucab0WZwtK6VvUVxSBtqQ=
|
||||
github.com/wailsapp/wails/v2 v2.11.0/go.mod h1:jrf0ZaM6+GBc1wRmXsM8cIvzlg0karYin3erahI4+0k=
|
||||
golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA=
|
||||
golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ=
|
||||
golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc=
|
||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
||||
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
|
||||
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
|
||||
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
// 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:8185 (SQLite); the WebView loads the bundled frontend.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"bamort/appsystem"
|
||||
"bamort/character"
|
||||
"bamort/config"
|
||||
"bamort/database"
|
||||
"bamort/equipment"
|
||||
"bamort/gamesystem"
|
||||
"bamort/gsmaster"
|
||||
"bamort/importer"
|
||||
"bamort/logger"
|
||||
"bamort/maintenance"
|
||||
"bamort/models"
|
||||
"bamort/pdfrender"
|
||||
"bamort/router"
|
||||
"bamort/transfer"
|
||||
"bamort/user"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
||||
)
|
||||
|
||||
// frontend/dist is populated by `wails build` running the frontend:build command
|
||||
// in wails.json (outputs to ../desktop/frontend/dist via DESKTOP_BUILD=1).
|
||||
//
|
||||
//go:embed all:frontend/dist
|
||||
var assets embed.FS
|
||||
|
||||
// App is the Wails application struct. Lifecycle hooks are attached to it.
|
||||
type App struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewApp creates the Wails application instance.
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
// startup is called by Wails just before the WebView window is shown.
|
||||
// It starts the Gin HTTP server in the background so the frontend can reach it.
|
||||
func (a *App) startup(ctx context.Context) {
|
||||
a.ctx = ctx
|
||||
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 8185) in a goroutine.
|
||||
func startGinServer() {
|
||||
cfg := config.Cfg
|
||||
|
||||
logger.Info("BaMoRT Desktop Server wird gestartet...")
|
||||
logger.Info("Environment: %s", cfg.Environment)
|
||||
logger.Info("Database: %s @ %s", cfg.DatabaseType, cfg.DatabaseURL)
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
// Connect to SQLite database.
|
||||
database.ConnectDatabase()
|
||||
|
||||
// Auto-migrate all modules so a fresh SQLite file gets a complete schema.
|
||||
if err := models.MigrateStructure(); err != nil {
|
||||
logger.Warn("models migration: %s", err.Error())
|
||||
}
|
||||
if err := user.MigrateStructure(); err != nil {
|
||||
logger.Warn("user migration: %s", err.Error())
|
||||
}
|
||||
if err := gamesystem.MigrateStructure(); err != nil {
|
||||
logger.Warn("gamesystem migration: %s", err.Error())
|
||||
}
|
||||
if err := database.MigrateStructure(); err != nil {
|
||||
logger.Warn("database migration: %s", err.Error())
|
||||
}
|
||||
|
||||
// Initialise PDF templates (source from ./default_templates if present).
|
||||
if err := pdfrender.InitializeTemplates("./default_templates", cfg.TemplatesDir); err != nil {
|
||||
logger.Warn("template init: %s", err.Error())
|
||||
}
|
||||
|
||||
r := gin.Default()
|
||||
router.SetupGin(r)
|
||||
|
||||
protected := router.BaseRouterGrp(r)
|
||||
user.RegisterRoutes(protected)
|
||||
gsmaster.RegisterRoutes(protected)
|
||||
character.RegisterRoutes(protected)
|
||||
equipment.RegisterRoutes(protected)
|
||||
maintenance.RegisterRoutes(protected)
|
||||
importer.RegisterRoutes(protected)
|
||||
pdfrender.RegisterRoutes(protected)
|
||||
transfer.RegisterRoutes(protected)
|
||||
appsystem.RegisterRoutes(protected)
|
||||
|
||||
pdfrender.RegisterPublicRoutes(r)
|
||||
appsystem.RegisterPublicRoutes(r)
|
||||
|
||||
logger.Info("Desktop API server starting on %s", cfg.GetServerAddress())
|
||||
go func() {
|
||||
if err := r.Run(cfg.GetServerAddress()); err != nil {
|
||||
log.Printf("Gin server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Brief pause to let the HTTP server bind before the WebView starts loading.
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Apply desktop defaults BEFORE anything uses config.Cfg.
|
||||
// config.init() already ran (at import time) and may have picked up .env;
|
||||
// here we ensure the critical SQLite settings exist even without a .env file.
|
||||
if os.Getenv("DATABASE_TYPE") == "" {
|
||||
_ = os.Setenv("DATABASE_TYPE", "sqlite")
|
||||
}
|
||||
if os.Getenv("DATABASE_URL") == "" {
|
||||
_ = os.Setenv("DATABASE_URL", "./bamort-desktop.db")
|
||||
}
|
||||
if os.Getenv("ENVIRONMENT") == "" {
|
||||
_ = os.Setenv("ENVIRONMENT", "desktop")
|
||||
}
|
||||
if os.Getenv("API_PORT") == "" {
|
||||
_ = os.Setenv("API_PORT", "8185")
|
||||
}
|
||||
if os.Getenv("TEMPLATES_DIR") == "" {
|
||||
_ = os.Setenv("TEMPLATES_DIR", "./templates")
|
||||
}
|
||||
if os.Getenv("EXPORT_TEMP_DIR") == "" {
|
||||
_ = os.Setenv("EXPORT_TEMP_DIR", "./export_temp")
|
||||
}
|
||||
|
||||
// Reload config so the above env vars take effect (init() may have run first).
|
||||
config.Cfg = config.LoadConfig()
|
||||
|
||||
// Sub-FS rooted at frontend/dist so Wails serves it from /.
|
||||
frontendFS, err := fs.Sub(assets, "frontend/dist")
|
||||
if err != nil {
|
||||
log.Fatal("frontend/dist embed error:", err)
|
||||
}
|
||||
|
||||
app := NewApp()
|
||||
|
||||
if err := wails.Run(&options.App{
|
||||
Title: "BaMoRT",
|
||||
Width: 1400,
|
||||
Height: 900,
|
||||
MinWidth: 1024,
|
||||
MinHeight: 768,
|
||||
OnStartup: app.startup,
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
},
|
||||
AssetServer: &assetserver.Options{
|
||||
Assets: frontendFS,
|
||||
},
|
||||
}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "https://wails.io/schemas/config.v2.json",
|
||||
"name": "bamort",
|
||||
"outputfilename": "bamort",
|
||||
"frontend:dir": "../frontend",
|
||||
"frontend:install": "npm install",
|
||||
"frontend:build": "npm run build:desktop",
|
||||
"frontend:dev:watcher": "npm run dev",
|
||||
"frontend:dev:serverUrl": "http://localhost:5173",
|
||||
"build:tags": "webkit2_41",
|
||||
"author": {
|
||||
"name": "Bamort"
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -1,9 +1,5 @@
|
||||
# Environment variables for Bamort development environment
|
||||
|
||||
# API Configuration
|
||||
# API_URL=http://localhost:8180
|
||||
|
||||
# Database Configuration (for development)
|
||||
DATABASE_TYPE=mysql
|
||||
DATABASE_URL=bamort:bG4)efozrc@tcp(mariadb-dev:3306)/bamort?charset=utf8mb4&parseTime=True&loc=Local
|
||||
|
||||
@@ -13,9 +9,13 @@ MARIADB_PASSWORD=bG4)efozrc
|
||||
MARIADB_DATABASE=bamort
|
||||
MARIADB_USER=bamort
|
||||
|
||||
# Frontend Configuration
|
||||
# API Configuration
|
||||
# API_URL is used by the frontend to connect to the backend
|
||||
# For dev environment with Vite dev server, this goes to VITE_API_URL
|
||||
API_URL=http://192.168.0.48:8180
|
||||
API_PORT=8180
|
||||
|
||||
# Frontend Configuration
|
||||
BASE_URL=http://localhost:5173
|
||||
TEMPLATES_DIR=./templates
|
||||
EXPORT_TEMP_DIR=./export_temp
|
||||
|
||||
+16
-3
@@ -1,9 +1,16 @@
|
||||
# Environment variables for Bamort production deployment
|
||||
# Copy this file to .env and adjust the values as needed
|
||||
# Copy this file to .env.dev or .env.prd and adjust the values as needed
|
||||
|
||||
#- API Configuration
|
||||
# ===== FRONTEND API CONFIGURATION =====
|
||||
# API_URL: The backend API endpoint that the frontend will use
|
||||
# For production: Use your public API domain (https://api.yourdomain.com)
|
||||
# For development: Use your local network IP (http://192.168.x.x:8180)
|
||||
#
|
||||
# IMPORTANT: After changing API_URL, just restart containers - NO REBUILD NEEDED!
|
||||
# Production: docker-compose -f docker-compose.yml restart frontend
|
||||
# Development: docker-compose -f docker-compose.dev.yml restart frontend-dev
|
||||
API_URL=https://backend.domain.de
|
||||
VITE_API_URL=https://backend.domain.de
|
||||
API_PORT=8180
|
||||
|
||||
#- Database Configuration Backend
|
||||
DATABASE_TYPE=mysql
|
||||
@@ -20,3 +27,9 @@ BASE_URL=https://frontend.domain.de
|
||||
TEMPLATES_DIR=./templates
|
||||
EXPORT_TEMP_DIR=./export_temp
|
||||
COMPOSE_PROJECT_NAME=bamort
|
||||
|
||||
# Mail Configuration (for development)
|
||||
MAIL_HOST=mail.server.de
|
||||
MAIL_PORT=465
|
||||
MAIL_USERNAME=bamort@server.de
|
||||
MAIL_PASSWORD=XXXZZZZZ.YYYXXX
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
# Environment variables for Bamort production environment
|
||||
|
||||
# API Configuration
|
||||
# API_URL is used by the frontend to generate config.json at container startup
|
||||
# Change this value and restart containers (no rebuild needed!)
|
||||
API_URL=https://bamort-api.trokan.de
|
||||
VITE_API_URL=https://bamort-api.trokan.de
|
||||
|
||||
# Database Configuration Backend
|
||||
API_PORT=8180
|
||||
DATABASE_TYPE=mysql
|
||||
#DATABASE_URL=bamort:bG4)efozrc@tcp(mariadb:3306)/bamort?charset=utf8mb4&parseTime=True&loc=Local
|
||||
|
||||
@@ -14,7 +14,7 @@ MARIADB_PASSWORD=bG4)efozrc
|
||||
MARIADB_DATABASE=bamort
|
||||
MARIADB_USER=bamort
|
||||
|
||||
API_PORT=8180
|
||||
# Frontend Configuration
|
||||
BASE_URL=https://bamort.trokan.de
|
||||
TEMPLATES_DIR=./templates
|
||||
EXPORT_TEMP_DIR=./export_temp
|
||||
|
||||
+12
-17
@@ -1,42 +1,37 @@
|
||||
# =========== 1) Build stage ===========
|
||||
FROM node:22-alpine AS build
|
||||
|
||||
# Accept build arguments for Vite environment variables
|
||||
ARG VITE_API_URL
|
||||
ARG VITE_BASE_URL
|
||||
ARG VITE_API_PORT
|
||||
|
||||
# Set them as environment variables for the build process
|
||||
ENV VITE_API_URL=$VITE_API_URL
|
||||
ENV VITE_BASE_URL=$VITE_BASE_URL
|
||||
ENV VITE_API_PORT=$VITE_API_PORT
|
||||
|
||||
# No build args needed - using runtime configuration instead
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy the rest of the frontend code
|
||||
COPY . .
|
||||
|
||||
# Build the production bundle
|
||||
# Build the production bundle WITHOUT baked-in API URL
|
||||
# Runtime configuration will be generated from environment variables
|
||||
RUN npm run build
|
||||
|
||||
# =========== 2) Serve stage ===========
|
||||
FROM nginx:alpine
|
||||
|
||||
# Copy production build to Nginx html folder.
|
||||
# Adjust /usr/src/app/build -> /usr/src/app/dist if you’re using Angular/Vue
|
||||
#COPY --from=build /usr/src/app/build /usr/share/nginx/html
|
||||
# Copy production build to Nginx html folder
|
||||
COPY --from=build /usr/src/app/dist /usr/share/nginx/html
|
||||
|
||||
# Copy custom nginx configuration for SPA routing
|
||||
COPY --from=build /usr/src/app/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Copy entrypoint script that generates runtime config
|
||||
COPY --from=build /usr/src/app/docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
# Expose HTTP port
|
||||
EXPOSE 80
|
||||
|
||||
# Run Nginx in foreground
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
# Use custom entrypoint that generates config.json from environment
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
|
||||
@@ -9,8 +9,12 @@ COPY package*.json ./
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy entrypoint script that generates config.json
|
||||
COPY docker-dev-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
# Expose Vite dev server port
|
||||
EXPOSE 5173
|
||||
|
||||
# Start development server with host binding for Docker
|
||||
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
|
||||
# Use entrypoint that generates config.json before starting Vite
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
|
||||
@@ -25,18 +25,12 @@ services:
|
||||
build:
|
||||
context: ../frontend
|
||||
dockerfile: ../docker/Dockerfile.frontend
|
||||
args:
|
||||
VITE_API_URL: ${API_URL:-https://bamort-api.trokan.de}
|
||||
VITE_BASE_URL: ${BASE_URL:-https://bamort.trokan.de}
|
||||
VITE_API_PORT: ${API_PORT:-443}
|
||||
container_name: bamort-frontend
|
||||
ports:
|
||||
- "8181:80"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- VITE_API_URL=${API_URL:-https://bamort.trokan.de:8180}
|
||||
- VITE_BASE_URL=${BASE_URL:-https://bamort.trokan.de}
|
||||
- VITE_API_PORT=${API_PORT:-8180}
|
||||
- API_URL=${API_URL:-https://bamort-api.trokan.de}
|
||||
depends_on:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Generate config.json from VITE_API_URL for development
|
||||
# This makes the dev environment behave the same as production
|
||||
|
||||
CONFIG_FILE="/app/public/config.json"
|
||||
|
||||
echo "🔧 Generating development config.json..."
|
||||
|
||||
# Use VITE_API_URL from environment
|
||||
API_BASE_URL="${VITE_API_URL:-}"
|
||||
|
||||
if [ -z "$API_BASE_URL" ]; then
|
||||
echo "⚠️ VITE_API_URL not set, creating minimal config"
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"_comment": "Development mode - VITE_API_URL will be used as fallback"
|
||||
}
|
||||
EOF
|
||||
else
|
||||
echo "✅ API URL configured: $API_BASE_URL"
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"apiBaseURL": "$API_BASE_URL"
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo "📄 Created config.json:"
|
||||
cat "$CONFIG_FILE"
|
||||
|
||||
# Start Vite dev server
|
||||
echo "🚀 Starting Vite dev server..."
|
||||
exec npm run dev -- --host 0.0.0.0
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Generate config.json from environment variables at container startup
|
||||
# This allows runtime configuration without rebuilding the container
|
||||
|
||||
CONFIG_FILE="/usr/share/nginx/html/config.json"
|
||||
|
||||
echo "🔧 Generating frontend runtime configuration..."
|
||||
|
||||
# Use API_URL from environment, or fallback to same origin
|
||||
API_BASE_URL="${API_URL:-}"
|
||||
|
||||
if [ -z "$API_BASE_URL" ]; then
|
||||
echo "⚠️ API_URL not set, frontend will auto-detect or use same origin"
|
||||
# Create minimal config that triggers auto-detection
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"_comment": "API_URL not configured, using auto-detection"
|
||||
}
|
||||
EOF
|
||||
else
|
||||
echo "✅ API URL configured: $API_BASE_URL"
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"apiBaseURL": "$API_BASE_URL"
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo "📄 Generated config.json:"
|
||||
cat "$CONFIG_FILE"
|
||||
|
||||
# Start nginx
|
||||
echo "🚀 Starting nginx..."
|
||||
exec nginx -g "daemon off;"
|
||||
+18
-1
@@ -11,16 +11,33 @@ fi
|
||||
# Gehe ins Docker-Verzeichnis
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Load development environment variables
|
||||
if [ -f .env.dev ]; then
|
||||
echo "📝 Loading configuration from .env.dev"
|
||||
export $(grep -v '^#' .env.dev | xargs)
|
||||
else
|
||||
if [ -f .env ]; then
|
||||
echo "📝 Loading configuration from .env"
|
||||
export $(grep -v '^#' .env | xargs)
|
||||
else
|
||||
echo "⚠️ Warning: .env not found, using defaults"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get current git commit
|
||||
export GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
||||
echo "📝 Git Commit: $GIT_COMMIT"
|
||||
|
||||
echo "📦 Building and starting development containers..."
|
||||
echo "🔧 Frontend will use API: ${API_URL:-http://localhost:8180}"
|
||||
|
||||
# Stoppe vorhandene Container
|
||||
docker-compose -f docker-compose.dev.yml down
|
||||
|
||||
# Baue und starte die Container
|
||||
docker-compose -f docker-compose.dev.yml up --build -d
|
||||
docker-compose -f docker-compose.dev.yml --env-file .env.dev up --build -d
|
||||
|
||||
echo "✅ Development environment started."
|
||||
echo "📱 Frontend: http://localhost:5173"
|
||||
echo "🔌 Backend: http://localhost:8180"
|
||||
echo "🗄️ phpMyAdmin: http://localhost:8081"
|
||||
|
||||
+23
-2
@@ -11,14 +11,35 @@ fi
|
||||
# Gehe ins Docker-Verzeichnis
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Load production environment variables
|
||||
if [ -f .env.prd ]; then
|
||||
echo "📝 Loading configuration from .env.prd"
|
||||
export $(grep -v '^#' .env.prd | xargs)
|
||||
else
|
||||
if [ -f .env ]; then
|
||||
echo "📝 Loading configuration from .env"
|
||||
export $(grep -v '^#' .env | xargs)
|
||||
else
|
||||
echo "⚠️ Warning: .env not found, using defaults"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "📦 Building and starting production containers..."
|
||||
echo "🔧 Frontend will use API: ${API_URL:-https://bamort-api.trokan.de}"
|
||||
|
||||
# Build before stopping existing containers
|
||||
docker-compose -f docker-compose.yml build
|
||||
docker-compose -f docker-compose.yml --env-file .env.prd build
|
||||
|
||||
# Stoppe vorhandene Container
|
||||
docker-compose -f docker-compose.yml down
|
||||
|
||||
# Baue und starte die Container
|
||||
docker-compose -f docker-compose.yml up --build -d
|
||||
docker-compose -f docker-compose.yml --env-file .env.prd up -d
|
||||
|
||||
echo "✅ Production environment started."
|
||||
echo "📱 Frontend: http://localhost:8181"
|
||||
echo "🔌 Backend: http://localhost:8182"
|
||||
echo ""
|
||||
echo "💡 To change API URL: Edit .env.prd and run:"
|
||||
echo " docker-compose -f docker-compose.yml restart frontend"
|
||||
echo " (No rebuild needed!)"
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Frontend Version Management
|
||||
|
||||
## Current Version: 0.2.3
|
||||
## Current Version: 0.2.4
|
||||
|
||||
The frontend version is managed independently from the backend.
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "bamort-frontend",
|
||||
"version": "0.1.29",
|
||||
"version": "0.2.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bamort-frontend",
|
||||
"version": "0.1.29",
|
||||
"version": "0.2.4",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "bamort-frontend",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.4",
|
||||
"private": true,
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"build:desktop": "DESKTOP_BUILD=1 vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
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."
|
||||
}
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<!-- Submenu Content -->
|
||||
<!-- <div class="character-aspect"> -->
|
||||
<component :is="currentView" :character="character" :isOwner="isOwner" @character-updated="refreshCharacter"/>
|
||||
<component :is="currentView" :character="character" :isOwner="isOwner" @character-updated="refreshCharacter" @character-data-updated="updateCharacterData"/>
|
||||
<!-- </div> -->
|
||||
|
||||
<!-- Submenu -->
|
||||
@@ -156,6 +156,12 @@ export default {
|
||||
alert('Fehler beim Aktualisieren der Charakterdaten: ' + (error.response?.data?.error || error.message));
|
||||
}
|
||||
},
|
||||
|
||||
updateCharacterData(updatedData) {
|
||||
// Update character data directly without reloading from server
|
||||
this.character = updatedData;
|
||||
console.log('Character data updated directly from response');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -55,6 +55,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Warning message when editing skill name -->
|
||||
<div v-if="showNameEditWarning" class="warning-message">
|
||||
<span class="warning-icon">⚠️</span>
|
||||
<span class="warning-text">
|
||||
{{ $t('characters.datasheet.editnamewarning') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<table class="cd-table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -384,6 +393,29 @@
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.warning-message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 16px;
|
||||
margin: 10px 0;
|
||||
background-color: #fff3cd;
|
||||
border: 1px solid #ffc107;
|
||||
border-radius: 6px;
|
||||
color: #856404;
|
||||
animation: slideIn 0.3s ease;
|
||||
}
|
||||
|
||||
.warning-icon {
|
||||
font-size: 1.2rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.warning-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@@ -431,6 +463,7 @@ export default {
|
||||
editingSkillId: null,
|
||||
editingField: null,
|
||||
editValue: '',
|
||||
showNameEditWarning: false,
|
||||
|
||||
isLoading: false
|
||||
};
|
||||
@@ -752,6 +785,11 @@ export default {
|
||||
this.editingField = field;
|
||||
this.editValue = skill[field] || '';
|
||||
|
||||
// Show warning when editing skill name
|
||||
if (field === 'name') {
|
||||
this.showNameEditWarning = true;
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
const input = this.$refs.editInput;
|
||||
if (input) {
|
||||
@@ -779,19 +817,50 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
// Update local character object
|
||||
skill[field] = newValue;
|
||||
// Find and update the original skill in character.fertigkeiten or character.waffenfertigkeiten
|
||||
let originalSkillFound = false;
|
||||
|
||||
// Check in fertigkeiten
|
||||
if (this.character.fertigkeiten) {
|
||||
const originalSkill = this.character.fertigkeiten.find(s => s.name === skill.name);
|
||||
if (originalSkill) {
|
||||
originalSkill[field] = newValue;
|
||||
originalSkillFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check in waffenfertigkeiten if not found in fertigkeiten
|
||||
if (!originalSkillFound && this.character.waffenfertigkeiten) {
|
||||
const originalWeaponSkill = this.character.waffenfertigkeiten.find(s => s.name === skill.name);
|
||||
if (originalWeaponSkill) {
|
||||
originalWeaponSkill[field] = newValue;
|
||||
originalSkillFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!originalSkillFound) {
|
||||
console.warn('Original skill not found in character data:', skill.name);
|
||||
alert('Warnung: Originalfertigkeit nicht gefunden.');
|
||||
this.cancelEditSkill();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Save to backend
|
||||
await API.put(`/api/characters/${this.character.id}`, this.character);
|
||||
const response = await API.put(`/api/characters/${this.character.id}`, this.character);
|
||||
|
||||
console.log('Skill updated successfully:', skill.name, field, newValue);
|
||||
|
||||
// Update the parent component with the response data
|
||||
// The backend now returns the full FeChar with categorizedskills
|
||||
this.$emit('character-data-updated', response.data);
|
||||
|
||||
this.$emit('character-updated');
|
||||
this.cancelEditSkill();
|
||||
} catch (error) {
|
||||
console.error('Failed to update skill:', error);
|
||||
alert('Fehler beim Speichern: ' + (error.response?.data?.error || error.message));
|
||||
this.cancelEditSkill();
|
||||
// Revert changes on error by reloading
|
||||
this.$emit('character-updated');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -799,6 +868,7 @@ export default {
|
||||
this.editingSkillId = null;
|
||||
this.editingField = null;
|
||||
this.editValue = '';
|
||||
this.showNameEditWarning = false;
|
||||
},
|
||||
|
||||
isEditingSkill(skill, field) {
|
||||
|
||||
@@ -563,7 +563,8 @@ export default {
|
||||
bRollTooltip: 'B würfeln: 1d6 + Modifikator je nach Rasse'
|
||||
},
|
||||
datasheet: {
|
||||
editHelp: 'Doppelklicken auf ein Feld, um es zu bearbeiten.'
|
||||
editHelp: 'Doppelklicken auf ein Feld, um es zu bearbeiten.',
|
||||
editnamewarning: 'Wenn der Name der Fertigkeit geändert wird kann diese nicht mehr nach den Regeln verbessert werden. Auch beim Export können Boni und andere Werte fehlen!'
|
||||
}
|
||||
},
|
||||
audit: {
|
||||
|
||||
@@ -559,7 +559,8 @@ export default {
|
||||
bRollTooltip: 'Roll B: 1d6 + modifier based on race'
|
||||
},
|
||||
datasheet: {
|
||||
editHelp: 'Click twice on a field to edit it.'
|
||||
editHelp: 'Click twice on a field to edit it.',
|
||||
editnamewarning: 'If the name of the skill is changed, it can no longer be improved according to the rules. Also, bonuses and other values may be missing when exporting!'
|
||||
}
|
||||
},
|
||||
audit: {
|
||||
|
||||
@@ -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
|
||||
@@ -1,5 +1,5 @@
|
||||
// Frontend version information
|
||||
export const VERSION = '0.2.3'
|
||||
export const VERSION = '0.2.4'
|
||||
|
||||
// Git commit will be injected at build time or detected from env
|
||||
export const GIT_COMMIT = import.meta.env.VITE_GIT_COMMIT || 'unknown'
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -19,8 +19,11 @@ export default defineConfig({
|
||||
//port: 8080,
|
||||
host: ['bamort.trokan.de','192.168.0.48', 'localhost','terra.local'],
|
||||
},
|
||||
//build: {
|
||||
// minify: false,
|
||||
// sourcemap: true,
|
||||
//},
|
||||
build: {
|
||||
// When DESKTOP_BUILD=1, output into the Wails embed path so `wails build`
|
||||
// can embed the frontend directly into the desktop binary.
|
||||
outDir: process.env.DESKTOP_BUILD ? '../desktop/frontend/dist' : 'dist',
|
||||
//minify: false,
|
||||
//sourcemap: true,
|
||||
},
|
||||
})
|
||||
|
||||
+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']();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "@wailsapp/runtime",
|
||||
"version": "2.0.0",
|
||||
"description": "Wails Javascript runtime library",
|
||||
"main": "runtime.js",
|
||||
"types": "runtime.d.ts",
|
||||
"scripts": {
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/wailsapp/wails.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Wails",
|
||||
"Javascript",
|
||||
"Go"
|
||||
],
|
||||
"author": "Lea Anthony <lea.anthony@gmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/wailsapp/wails/issues"
|
||||
},
|
||||
"homepage": "https://github.com/wailsapp/wails#readme"
|
||||
}
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
_ __ _ __
|
||||
| | / /___ _(_) /____
|
||||
| | /| / / __ `/ / / ___/
|
||||
| |/ |/ / /_/ / / (__ )
|
||||
|__/|__/\__,_/_/_/____/
|
||||
The electron alternative for Go
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
|
||||
export interface Position {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface Size {
|
||||
w: number;
|
||||
h: number;
|
||||
}
|
||||
|
||||
export interface Screen {
|
||||
isCurrent: boolean;
|
||||
isPrimary: boolean;
|
||||
width : number
|
||||
height : number
|
||||
}
|
||||
|
||||
// Environment information such as platform, buildtype, ...
|
||||
export interface EnvironmentInfo {
|
||||
buildType: string;
|
||||
platform: string;
|
||||
arch: string;
|
||||
}
|
||||
|
||||
// [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit)
|
||||
// emits the given event. Optional data may be passed with the event.
|
||||
// This will trigger any event listeners.
|
||||
export function EventsEmit(eventName: string, ...data: any): void;
|
||||
|
||||
// [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name.
|
||||
export function EventsOn(eventName: string, callback: (...data: any) => void): () => void;
|
||||
|
||||
// [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple)
|
||||
// sets up a listener for the given event name, but will only trigger a given number times.
|
||||
export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void;
|
||||
|
||||
// [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce)
|
||||
// sets up a listener for the given event name, but will only trigger once.
|
||||
export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void;
|
||||
|
||||
// [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff)
|
||||
// unregisters the listener for the given event name.
|
||||
export function EventsOff(eventName: string, ...additionalEventNames: string[]): void;
|
||||
|
||||
// [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall)
|
||||
// unregisters all listeners.
|
||||
export function EventsOffAll(): void;
|
||||
|
||||
// [LogPrint](https://wails.io/docs/reference/runtime/log#logprint)
|
||||
// logs the given message as a raw message
|
||||
export function LogPrint(message: string): void;
|
||||
|
||||
// [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace)
|
||||
// logs the given message at the `trace` log level.
|
||||
export function LogTrace(message: string): void;
|
||||
|
||||
// [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug)
|
||||
// logs the given message at the `debug` log level.
|
||||
export function LogDebug(message: string): void;
|
||||
|
||||
// [LogError](https://wails.io/docs/reference/runtime/log#logerror)
|
||||
// logs the given message at the `error` log level.
|
||||
export function LogError(message: string): void;
|
||||
|
||||
// [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal)
|
||||
// logs the given message at the `fatal` log level.
|
||||
// The application will quit after calling this method.
|
||||
export function LogFatal(message: string): void;
|
||||
|
||||
// [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo)
|
||||
// logs the given message at the `info` log level.
|
||||
export function LogInfo(message: string): void;
|
||||
|
||||
// [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning)
|
||||
// logs the given message at the `warning` log level.
|
||||
export function LogWarning(message: string): void;
|
||||
|
||||
// [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload)
|
||||
// Forces a reload by the main application as well as connected browsers.
|
||||
export function WindowReload(): void;
|
||||
|
||||
// [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp)
|
||||
// Reloads the application frontend.
|
||||
export function WindowReloadApp(): void;
|
||||
|
||||
// [WindowSetAlwaysOnTop](https://wails.io/docs/reference/runtime/window#windowsetalwaysontop)
|
||||
// Sets the window AlwaysOnTop or not on top.
|
||||
export function WindowSetAlwaysOnTop(b: boolean): void;
|
||||
|
||||
// [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme)
|
||||
// *Windows only*
|
||||
// Sets window theme to system default (dark/light).
|
||||
export function WindowSetSystemDefaultTheme(): void;
|
||||
|
||||
// [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme)
|
||||
// *Windows only*
|
||||
// Sets window to light theme.
|
||||
export function WindowSetLightTheme(): void;
|
||||
|
||||
// [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme)
|
||||
// *Windows only*
|
||||
// Sets window to dark theme.
|
||||
export function WindowSetDarkTheme(): void;
|
||||
|
||||
// [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter)
|
||||
// Centers the window on the monitor the window is currently on.
|
||||
export function WindowCenter(): void;
|
||||
|
||||
// [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle)
|
||||
// Sets the text in the window title bar.
|
||||
export function WindowSetTitle(title: string): void;
|
||||
|
||||
// [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen)
|
||||
// Makes the window full screen.
|
||||
export function WindowFullscreen(): void;
|
||||
|
||||
// [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen)
|
||||
// Restores the previous window dimensions and position prior to full screen.
|
||||
export function WindowUnfullscreen(): void;
|
||||
|
||||
// [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen)
|
||||
// Returns the state of the window, i.e. whether the window is in full screen mode or not.
|
||||
export function WindowIsFullscreen(): Promise<boolean>;
|
||||
|
||||
// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize)
|
||||
// Sets the width and height of the window.
|
||||
export function WindowSetSize(width: number, height: number): void;
|
||||
|
||||
// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize)
|
||||
// Gets the width and height of the window.
|
||||
export function WindowGetSize(): Promise<Size>;
|
||||
|
||||
// [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize)
|
||||
// Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions.
|
||||
// Setting a size of 0,0 will disable this constraint.
|
||||
export function WindowSetMaxSize(width: number, height: number): void;
|
||||
|
||||
// [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize)
|
||||
// Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions.
|
||||
// Setting a size of 0,0 will disable this constraint.
|
||||
export function WindowSetMinSize(width: number, height: number): void;
|
||||
|
||||
// [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition)
|
||||
// Sets the window position relative to the monitor the window is currently on.
|
||||
export function WindowSetPosition(x: number, y: number): void;
|
||||
|
||||
// [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition)
|
||||
// Gets the window position relative to the monitor the window is currently on.
|
||||
export function WindowGetPosition(): Promise<Position>;
|
||||
|
||||
// [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide)
|
||||
// Hides the window.
|
||||
export function WindowHide(): void;
|
||||
|
||||
// [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow)
|
||||
// Shows the window, if it is currently hidden.
|
||||
export function WindowShow(): void;
|
||||
|
||||
// [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise)
|
||||
// Maximises the window to fill the screen.
|
||||
export function WindowMaximise(): void;
|
||||
|
||||
// [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise)
|
||||
// Toggles between Maximised and UnMaximised.
|
||||
export function WindowToggleMaximise(): void;
|
||||
|
||||
// [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise)
|
||||
// Restores the window to the dimensions and position prior to maximising.
|
||||
export function WindowUnmaximise(): void;
|
||||
|
||||
// [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised)
|
||||
// Returns the state of the window, i.e. whether the window is maximised or not.
|
||||
export function WindowIsMaximised(): Promise<boolean>;
|
||||
|
||||
// [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise)
|
||||
// Minimises the window.
|
||||
export function WindowMinimise(): void;
|
||||
|
||||
// [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise)
|
||||
// Restores the window to the dimensions and position prior to minimising.
|
||||
export function WindowUnminimise(): void;
|
||||
|
||||
// [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised)
|
||||
// Returns the state of the window, i.e. whether the window is minimised or not.
|
||||
export function WindowIsMinimised(): Promise<boolean>;
|
||||
|
||||
// [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal)
|
||||
// Returns the state of the window, i.e. whether the window is normal or not.
|
||||
export function WindowIsNormal(): Promise<boolean>;
|
||||
|
||||
// [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour)
|
||||
// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels.
|
||||
export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void;
|
||||
|
||||
// [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall)
|
||||
// Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
|
||||
export function ScreenGetAll(): Promise<Screen[]>;
|
||||
|
||||
// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl)
|
||||
// Opens the given URL in the system browser.
|
||||
export function BrowserOpenURL(url: string): void;
|
||||
|
||||
// [Environment](https://wails.io/docs/reference/runtime/intro#environment)
|
||||
// Returns information about the environment
|
||||
export function Environment(): Promise<EnvironmentInfo>;
|
||||
|
||||
// [Quit](https://wails.io/docs/reference/runtime/intro#quit)
|
||||
// Quits the application.
|
||||
export function Quit(): void;
|
||||
|
||||
// [Hide](https://wails.io/docs/reference/runtime/intro#hide)
|
||||
// Hides the application.
|
||||
export function Hide(): void;
|
||||
|
||||
// [Show](https://wails.io/docs/reference/runtime/intro#show)
|
||||
// Shows the application.
|
||||
export function Show(): void;
|
||||
|
||||
// [ClipboardGetText](https://wails.io/docs/reference/runtime/clipboard#clipboardgettext)
|
||||
// Returns the current text stored on clipboard
|
||||
export function ClipboardGetText(): Promise<string>;
|
||||
|
||||
// [ClipboardSetText](https://wails.io/docs/reference/runtime/clipboard#clipboardsettext)
|
||||
// Sets a text on the clipboard
|
||||
export function ClipboardSetText(text: string): Promise<boolean>;
|
||||
|
||||
// [OnFileDrop](https://wails.io/docs/reference/runtime/draganddrop#onfiledrop)
|
||||
// OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings.
|
||||
export function OnFileDrop(callback: (x: number, y: number ,paths: string[]) => void, useDropTarget: boolean) :void
|
||||
|
||||
// [OnFileDropOff](https://wails.io/docs/reference/runtime/draganddrop#dragandddropoff)
|
||||
// OnFileDropOff removes the drag and drop listeners and handlers.
|
||||
export function OnFileDropOff() :void
|
||||
|
||||
// Check if the file path resolver is available
|
||||
export function CanResolveFilePaths(): boolean;
|
||||
|
||||
// Resolves file paths for an array of files
|
||||
export function ResolveFilePaths(files: File[]): void
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
_ __ _ __
|
||||
| | / /___ _(_) /____
|
||||
| | /| / / __ `/ / / ___/
|
||||
| |/ |/ / /_/ / / (__ )
|
||||
|__/|__/\__,_/_/_/____/
|
||||
The electron alternative for Go
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
|
||||
export function LogPrint(message) {
|
||||
window.runtime.LogPrint(message);
|
||||
}
|
||||
|
||||
export function LogTrace(message) {
|
||||
window.runtime.LogTrace(message);
|
||||
}
|
||||
|
||||
export function LogDebug(message) {
|
||||
window.runtime.LogDebug(message);
|
||||
}
|
||||
|
||||
export function LogInfo(message) {
|
||||
window.runtime.LogInfo(message);
|
||||
}
|
||||
|
||||
export function LogWarning(message) {
|
||||
window.runtime.LogWarning(message);
|
||||
}
|
||||
|
||||
export function LogError(message) {
|
||||
window.runtime.LogError(message);
|
||||
}
|
||||
|
||||
export function LogFatal(message) {
|
||||
window.runtime.LogFatal(message);
|
||||
}
|
||||
|
||||
export function EventsOnMultiple(eventName, callback, maxCallbacks) {
|
||||
return window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks);
|
||||
}
|
||||
|
||||
export function EventsOn(eventName, callback) {
|
||||
return EventsOnMultiple(eventName, callback, -1);
|
||||
}
|
||||
|
||||
export function EventsOff(eventName, ...additionalEventNames) {
|
||||
return window.runtime.EventsOff(eventName, ...additionalEventNames);
|
||||
}
|
||||
|
||||
export function EventsOffAll() {
|
||||
return window.runtime.EventsOffAll();
|
||||
}
|
||||
|
||||
export function EventsOnce(eventName, callback) {
|
||||
return EventsOnMultiple(eventName, callback, 1);
|
||||
}
|
||||
|
||||
export function EventsEmit(eventName) {
|
||||
let args = [eventName].slice.call(arguments);
|
||||
return window.runtime.EventsEmit.apply(null, args);
|
||||
}
|
||||
|
||||
export function WindowReload() {
|
||||
window.runtime.WindowReload();
|
||||
}
|
||||
|
||||
export function WindowReloadApp() {
|
||||
window.runtime.WindowReloadApp();
|
||||
}
|
||||
|
||||
export function WindowSetAlwaysOnTop(b) {
|
||||
window.runtime.WindowSetAlwaysOnTop(b);
|
||||
}
|
||||
|
||||
export function WindowSetSystemDefaultTheme() {
|
||||
window.runtime.WindowSetSystemDefaultTheme();
|
||||
}
|
||||
|
||||
export function WindowSetLightTheme() {
|
||||
window.runtime.WindowSetLightTheme();
|
||||
}
|
||||
|
||||
export function WindowSetDarkTheme() {
|
||||
window.runtime.WindowSetDarkTheme();
|
||||
}
|
||||
|
||||
export function WindowCenter() {
|
||||
window.runtime.WindowCenter();
|
||||
}
|
||||
|
||||
export function WindowSetTitle(title) {
|
||||
window.runtime.WindowSetTitle(title);
|
||||
}
|
||||
|
||||
export function WindowFullscreen() {
|
||||
window.runtime.WindowFullscreen();
|
||||
}
|
||||
|
||||
export function WindowUnfullscreen() {
|
||||
window.runtime.WindowUnfullscreen();
|
||||
}
|
||||
|
||||
export function WindowIsFullscreen() {
|
||||
return window.runtime.WindowIsFullscreen();
|
||||
}
|
||||
|
||||
export function WindowGetSize() {
|
||||
return window.runtime.WindowGetSize();
|
||||
}
|
||||
|
||||
export function WindowSetSize(width, height) {
|
||||
window.runtime.WindowSetSize(width, height);
|
||||
}
|
||||
|
||||
export function WindowSetMaxSize(width, height) {
|
||||
window.runtime.WindowSetMaxSize(width, height);
|
||||
}
|
||||
|
||||
export function WindowSetMinSize(width, height) {
|
||||
window.runtime.WindowSetMinSize(width, height);
|
||||
}
|
||||
|
||||
export function WindowSetPosition(x, y) {
|
||||
window.runtime.WindowSetPosition(x, y);
|
||||
}
|
||||
|
||||
export function WindowGetPosition() {
|
||||
return window.runtime.WindowGetPosition();
|
||||
}
|
||||
|
||||
export function WindowHide() {
|
||||
window.runtime.WindowHide();
|
||||
}
|
||||
|
||||
export function WindowShow() {
|
||||
window.runtime.WindowShow();
|
||||
}
|
||||
|
||||
export function WindowMaximise() {
|
||||
window.runtime.WindowMaximise();
|
||||
}
|
||||
|
||||
export function WindowToggleMaximise() {
|
||||
window.runtime.WindowToggleMaximise();
|
||||
}
|
||||
|
||||
export function WindowUnmaximise() {
|
||||
window.runtime.WindowUnmaximise();
|
||||
}
|
||||
|
||||
export function WindowIsMaximised() {
|
||||
return window.runtime.WindowIsMaximised();
|
||||
}
|
||||
|
||||
export function WindowMinimise() {
|
||||
window.runtime.WindowMinimise();
|
||||
}
|
||||
|
||||
export function WindowUnminimise() {
|
||||
window.runtime.WindowUnminimise();
|
||||
}
|
||||
|
||||
export function WindowSetBackgroundColour(R, G, B, A) {
|
||||
window.runtime.WindowSetBackgroundColour(R, G, B, A);
|
||||
}
|
||||
|
||||
export function ScreenGetAll() {
|
||||
return window.runtime.ScreenGetAll();
|
||||
}
|
||||
|
||||
export function WindowIsMinimised() {
|
||||
return window.runtime.WindowIsMinimised();
|
||||
}
|
||||
|
||||
export function WindowIsNormal() {
|
||||
return window.runtime.WindowIsNormal();
|
||||
}
|
||||
|
||||
export function BrowserOpenURL(url) {
|
||||
window.runtime.BrowserOpenURL(url);
|
||||
}
|
||||
|
||||
export function Environment() {
|
||||
return window.runtime.Environment();
|
||||
}
|
||||
|
||||
export function Quit() {
|
||||
window.runtime.Quit();
|
||||
}
|
||||
|
||||
export function Hide() {
|
||||
window.runtime.Hide();
|
||||
}
|
||||
|
||||
export function Show() {
|
||||
window.runtime.Show();
|
||||
}
|
||||
|
||||
export function ClipboardGetText() {
|
||||
return window.runtime.ClipboardGetText();
|
||||
}
|
||||
|
||||
export function ClipboardSetText(text) {
|
||||
return window.runtime.ClipboardSetText(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for OnFileDrop returns a slice of file path strings when a drop is finished.
|
||||
*
|
||||
* @export
|
||||
* @callback OnFileDropCallback
|
||||
* @param {number} x - x coordinate of the drop
|
||||
* @param {number} y - y coordinate of the drop
|
||||
* @param {string[]} paths - A list of file paths.
|
||||
*/
|
||||
|
||||
/**
|
||||
* OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings.
|
||||
*
|
||||
* @export
|
||||
* @param {OnFileDropCallback} callback - Callback for OnFileDrop returns a slice of file path strings when a drop is finished.
|
||||
* @param {boolean} [useDropTarget=true] - Only call the callback when the drop finished on an element that has the drop target style. (--wails-drop-target)
|
||||
*/
|
||||
export function OnFileDrop(callback, useDropTarget) {
|
||||
return window.runtime.OnFileDrop(callback, useDropTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* OnFileDropOff removes the drag and drop listeners and handlers.
|
||||
*/
|
||||
export function OnFileDropOff() {
|
||||
return window.runtime.OnFileDropOff();
|
||||
}
|
||||
|
||||
export function CanResolveFilePaths() {
|
||||
return window.runtime.CanResolveFilePaths();
|
||||
}
|
||||
|
||||
export function ResolveFilePaths(files) {
|
||||
return window.runtime.ResolveFilePaths(files);
|
||||
}
|
||||
Reference in New Issue
Block a user