6 Commits

Author SHA1 Message Date
alu 159b08a7ca improve makefile 2026-07-15 23:25:54 +00:00
alu 33db3c15b8 cleanup settings logic 2026-07-15 05:03:07 +00:00
alu 526b2a0ff0 remove update-modules npm script 2026-07-07 23:27:14 +00:00
alu ff1f618ad7 update to go 1.26.5 2026-07-07 23:24:46 +00:00
alu 47415fbdd9 update common lib 2026-07-07 03:39:53 +00:00
alu 3646bc1272 update readme 2026-07-06 23:52:25 +00:00
10 changed files with 95 additions and 89 deletions
+10 -5
View File
@@ -1,20 +1,25 @@
.PHONY: build test clean wfa-js .PHONY: build test clean wfa-js
build: clean wfa-js build: clean build-wfa-js
@echo "======================== Building Binary =======================" @echo "======================== Building Binary ======================="
mkdir -p dist mkdir -p dist
# resolve symbolic links in web by copying it into dist/web/ # resolve symbolic links in web by copying it into dist/web/
cp -rL web/ dist/web/ cp -rL web/ dist/web/
CGO_ENABLED=0 go build -tags release -ldflags="-s -w" -v -o dist/ . CGO_ENABLED=0 go build -tags release -ldflags="-s -w" -v -o dist/ .
wfa-js: build-wfa-js:
$(MAKE) -C WFA-JS $(MAKE) -C WFA-JS
cp -f WFA-JS/dist/* web/modules cp -f WFA-JS/dist/* web/modules
test: clean test: clean test-wfa-js
go run .
clean: test-wfa-js:
$(MAKE) test -C WFA-JS
clean: clean-wfa-js
@echo "======================== Cleaning Project ======================" @echo "======================== Cleaning Project ======================"
go clean go clean
rm -rf dist/* rm -rf dist/*
clean-wfa-js:
$(MAKE) clean -C WFA-JS
+9 -9
View File
@@ -13,7 +13,7 @@ ProxmoxAAS Dashboard provides users of a proxmox based compute on demand service
## ProxmoxAAS System Installation Overview ## ProxmoxAAS System Installation Overview
The ProxmoxAAS project is large and is split into multiple components. There are three required components, the Dashboard, API, and Fabric. There is also an optional LDAP component for organizations that want to use LDAP as their authentication backend. The instalation order should start with the Dashboard and then proceed to the other backend components. This will require some foresight into the setup process. The ProxmoxAAS project is large and is split into multiple components. There are four required components, the Dashboard, API, Fabric, and access-manager-api. The instalation order should start with the Dashboard and then proceed to the other backend components. This will require some foresight into the setup process.
The supported setup is to use a reverse proxy to serve both the original Proxmox web interface and ProxmoxAAS components. It is possible other setups can work. Rather than provide specific steps to duplicate a certain setup, the steps included are intended as a guideline of steps required for proper function in most setups. Consequently, the examples provided are only to highlight key settings and do not represent complete working configurations. The instructions also assume you have your own domain name which will substitute `domain.net` in some of the configs. The supported setup is to use a reverse proxy to serve both the original Proxmox web interface and ProxmoxAAS components. It is possible other setups can work. Rather than provide specific steps to duplicate a certain setup, the steps included are intended as a guideline of steps required for proper function in most setups. Consequently, the examples provided are only to highlight key settings and do not represent complete working configurations. The instructions also assume you have your own domain name which will substitute `domain.net` in some of the configs.
@@ -25,7 +25,7 @@ We will assume different hosts for each component which are accessible by unique
| ProxmoxAAS-Dashboard | dashboard.local | paas.domain.net | | ProxmoxAAS-Dashboard | dashboard.local | paas.domain.net |
| ProxmoxAAS-API | api.local | paas.domain.net/api/ | | ProxmoxAAS-API | api.local | paas.domain.net/api/ |
| ProxmoxAAS-Fabric | fabric.local | N/A | | ProxmoxAAS-Fabric | fabric.local | N/A |
| ProxmoxAAS-LDAP | ldap.local | N/A| | access-manager-api | access.local | N/A|
## Prerequisites - Dashboard ## Prerequisites - Dashboard
- Proxmox VE Cluster (v7.0+) - Proxmox VE Cluster (v7.0+)
@@ -36,11 +36,11 @@ We will assume different hosts for each component which are accessible by unique
1. Initialize any host, which will be the `ProxmoxAAS-Dashboard` component host 1. Initialize any host, which will be the `ProxmoxAAS-Dashboard` component host
2. Download `proxmoxaas-dashboard` binary and `template.config.json` file from [releases](https://git.tronnet.net/tronnet/ProxmoxAAS-LDAP/releases) 2. Download `proxmoxaas-dashboard` binary and `template.config.json` file from [releases](https://git.tronnet.net/tronnet/ProxmoxAAS-LDAP/releases)
Rename `template.config.json` to `config.json` and modify: Rename `template.config.json` to `config.json` and modify:
- listenPort: port for PAAS-Dashboard to bind and listen on - `listenPort`: port for PAAS-Dashboard to bind and listen on
- organization: name of your org which is displayed on the top left corner - `organization`: name of your org which is displayed on the top left corner
- dashurl: url for the dashboard, ie. `https://paas.domain.net` - `dashurl`: url for the dashboard, ie. `https://paas.domain.net`
- apiurl: url for PAAS-API, ie. `https://paas.domain.net/api` - `apiurl`: url for PAAS-API, ie. `https://paas.domain.net/api`
- pveurl: url for the Proxmox endpoint, ie. `https://pve.domain.net` - `pveurl`: url for the Proxmox endpoint, ie. `https://pve.domain.net`
3. Execute the binary or additionally download `proxmoxaas-dashboard.service` from [releases](https://git.tronnet.net/tronnet/ProxmoxAAS-LDAP/releases) to run using systemd 3. Execute the binary or additionally download `proxmoxaas-dashboard.service` from [releases](https://git.tronnet.net/tronnet/ProxmoxAAS-LDAP/releases) to run using systemd
After this step, the Dashboard should be available on the `ProxmoxAAS-Dashboard` host at the configured `listenPort` After this step, the Dashboard should be available on the `ProxmoxAAS-Dashboard` host at the configured `listenPort`
@@ -53,9 +53,9 @@ To install the API component, go to [ProxmoxAAS-API](https://git.tronnet.net/tro
To install the Fabric component, go to [ProxmoxAAS-Fabric](https://git.tronnet.net/tronnet/ProxmoxAAS-Fabric). This is required for the app to function. The Fabric installation will also have steps for setting up the reverse proxy server. To install the Fabric component, go to [ProxmoxAAS-Fabric](https://git.tronnet.net/tronnet/ProxmoxAAS-Fabric). This is required for the app to function. The Fabric installation will also have steps for setting up the reverse proxy server.
## Installation - LDAP ## Installation - access-manager-api
To install the LDAP component, go to [ProxmoxAAS-LDAP](https://git.tronnet.net/tronnet/ProxmoxAAS-LDAP).This is an optional component which adds a lightweight REST API server ontop of a simplified LDAP environment. It is only used by the API as a potential authentication backend. To install the access-manager-api component, go to [access-manager-api](https://git.tronnet.net/tronnet/access-manager-api). This is required for the app to function. The access-manager-api installation will also have steps for setting up the reverse proxy server.
## Installation - Reverse Proxy ## Installation - Reverse Proxy
1. Configure nginx or preferred reverse proxy to reverse proxy the dashboard. The configuration should include at least the following, ensuring that the configured ports are adjusted appropriately: 1. Configure nginx or preferred reverse proxy to reverse proxy the dashboard. The configuration should include at least the following, ensuring that the configured ports are adjusted appropriately:
+1 -1
View File
@@ -1,6 +1,6 @@
module proxmoxaas-dashboard module proxmoxaas-dashboard
go 1.26.4 go 1.26.5
require ( require (
github.com/gerow/go-color v0.0.0-20140219113758-125d37f527f1 github.com/gerow/go-color v0.0.0-20140219113758-125d37f527f1
+1 -2
View File
@@ -4,8 +4,7 @@
"description": "Front-end for ProxmoxAAS", "description": "Front-end for ProxmoxAAS",
"type": "module", "type": "module",
"scripts": { "scripts": {
"lint": "html-validate --config dev_config/.htmlvalidate.json web/html/*; stylelint --config dev_config/.stylelintrc.json --formatter verbose --fix web/css/*.css; DEBUG=eslint:cli-engine eslint --config dev_config/eslint.config.mjs --fix web/scripts/", "lint": "html-validate --config dev_config/.htmlvalidate.json web/html/*; stylelint --config dev_config/.stylelintrc.json --formatter verbose --fix web/css/*.css; DEBUG=eslint:cli-engine eslint --config dev_config/eslint.config.mjs --fix web/scripts/"
"update-modules": "rm -rf web/modules/wfa.js web/modules/wfa.wasm; curl https://git.tronnet.net/alu/WFA-JS/releases/download/latest/wfa.js -o web/modules/wfa.js; curl https://git.tronnet.net/alu/WFA-JS/releases/download/latest/wfa.wasm -o web/modules/wfa.wasm"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3.3.5", "@eslint/eslintrc": "^3.3.5",
+7 -1
View File
@@ -20,6 +20,12 @@
p:last-child { p:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
#save.disabled {
display: none;
}
#save.enabled {
display: unset;
}
</style> </style>
</head> </head>
<body> <body>
@@ -70,7 +76,7 @@
</fieldset> </fieldset>
</section> </section>
<div class="w3-container w3-center" id="form-actions"> <div class="w3-container w3-center" id="form-actions">
<button class="w3-button w3-margin" id="save" type="submit">SAVE</button> <button class="w3-button w3-margin disabled" id="save" type="submit">SAVE</button>
</div> </div>
</form> </form>
</main> </main>
+3 -2
View File
@@ -1,7 +1,8 @@
import { getSyncSettings, requestAPI } from "./utils.js"; import { getSetting, requestAPI } from "./utils.js";
export async function setupClientSync (callback) { export async function setupClientSync (callback) {
const { scheme, rate } = getSyncSettings(); const scheme = getSetting("sync-scheme");
const rate = getSetting("sync-rate");
if (scheme === "never") { if (scheme === "never") {
return; return;
} }
+2 -2
View File
@@ -1,4 +1,4 @@
import { requestPVE, requestAPI, setAppearance, getSearchSettings, requestDash, setIconSrc, setIconAlt } from "./utils.js"; import { requestPVE, requestAPI, setAppearance, getSetting, requestDash, setIconSrc, setIconAlt } from "./utils.js";
import { alert, dialog, error } from "./dialog.js"; import { alert, dialog, error } from "./dialog.js";
import { setupClientSync } from "./clientsync.js"; import { setupClientSync } from "./clientsync.js";
import wfaInit from "../modules/wfa.js"; import wfaInit from "../modules/wfa.js";
@@ -243,7 +243,7 @@ function initInstances () {
} }
function sortInstances () { function sortInstances () {
const searchCriteria = getSearchSettings(); const searchCriteria = getSetting("search-criteria");
const searchQuery = document.querySelector("#search").value || null; const searchQuery = document.querySelector("#search").value || null;
let criteria; let criteria;
if (!searchQuery) { if (!searchQuery) {
+45 -19
View File
@@ -1,36 +1,62 @@
import { setAppearance, getSyncSettings, getSearchSettings, getThemeSettings, setSyncSettings, setSearchSettings, setThemeSettings } from "./utils.js"; import { setAppearance, getSetting, setSetting } from "./utils.js";
window.addEventListener("DOMContentLoaded", init); window.addEventListener("DOMContentLoaded", init);
function init () { function init () {
setAppearance(); setAppearance();
const { scheme, rate } = getSyncSettings(); document.querySelectorAll("[id^=sync-]").forEach((v) => {
if (scheme) { v.addEventListener("change", handleSettingsChange);
document.querySelector(`#sync-${scheme}`).checked = true; });
} document.querySelector(`#sync-${getSetting("sync-scheme")}`).checked = true;
if (rate) { document.querySelector("#sync-rate").value = getSetting("sync-rate");
document.querySelector("#sync-rate").value = rate;
}
const search = getSearchSettings(); document.querySelectorAll("[id^=search-]").forEach((v) => {
if (search) { v.addEventListener("change", handleSettingsChange);
document.querySelector(`#search-${search}`).checked = true; });
} document.querySelector(`#search-${getSetting("search-criteria")}`).checked = true;
const theme = getThemeSettings(); document.querySelector("#appearance-theme").addEventListener("change", handleSettingsChange);
if (theme) { document.querySelector("#appearance-theme").value = getSetting("appearance-theme");
document.querySelector("#appearance-theme").value = theme;
}
document.querySelector("#settings").addEventListener("submit", handleSaveSettings, false); document.querySelector("#settings").addEventListener("submit", handleSaveSettings, false);
} }
function handleSettingsChange (event) {
event.preventDefault();
const form = new FormData(document.querySelector("#settings"));
const saveBtn = document.querySelector("#save");
if (getSetting("sync-scheme") !== form.get("sync-scheme")) {
saveBtn.classList.remove("disabled");
saveBtn.classList.add("enabled");
}
else if (getSetting("sync-rate") !== Number(form.get("sync-rate"))) {
saveBtn.classList.remove("disabled");
saveBtn.classList.add("enabled");
}
else if (getSetting("search-criteria") !== form.get("search-criteria")) {
saveBtn.classList.remove("disabled");
saveBtn.classList.add("enabled");
}
else if (getSetting("appearance-theme") !== form.get("appearance-theme")) {
saveBtn.classList.remove("disabled");
saveBtn.classList.add("enabled");
}
else {
saveBtn.classList.remove("enabled");
saveBtn.classList.add("disabled");
}
}
function handleSaveSettings (event) { function handleSaveSettings (event) {
event.preventDefault(); event.preventDefault();
const form = new FormData(document.querySelector("#settings")); const form = new FormData(document.querySelector("#settings"));
setSyncSettings(form.get("sync-scheme"), form.get("sync-rate")); const saveBtn = document.querySelector("#save");
setSearchSettings(form.get("search-criteria")); setSetting("sync-scheme", form.get("sync-scheme"));
setThemeSettings(form.get("appearance-theme")); setSetting("sync-rate", Number(form.get("sync-rate")));
setSetting("search-criteria", form.get("search-criteria"));
setSetting("appearance-theme", form.get("appearance-theme"));
saveBtn.classList.remove("enabled");
saveBtn.classList.add("disabled");
init(); init();
} }
+15 -46
View File
@@ -125,60 +125,30 @@ export function getURIData () {
return Object.fromEntries(url.searchParams); return Object.fromEntries(url.searchParams);
} }
const settingsDefault = { const settings = {
"sync-scheme": "always", "sync-scheme": {"type": String, "default": "always"},
"sync-rate": 5, "sync-rate": {"type": Number, "default": 5},
"search-criteria": "fuzzy", "search-criteria": {"type": String, "default": "fuzzy"},
"appearance-theme": "auto" "appearance-theme": {"type": String, "default": "auto"}
}; };
export function getSyncSettings () { export function getSetting (key) {
let scheme = localStorage.getItem("sync-scheme"); const meta = settings[key];
let rate = Number(localStorage.getItem("sync-rate")); let value = localStorage.getItem(key);
if (!scheme) { if (value === null || meta === null) {
scheme = settingsDefault["sync-scheme"]; value = meta.default;
localStorage.setItem("sync-scheme", scheme); localStorage.setItem(key, meta.default);
}
if (!rate) {
rate = settingsDefault["sync-rate"];
localStorage.setItem("sync-rate", rate);
}
return { scheme, rate };
} }
export function getSearchSettings () { return meta.type(value);
let searchCriteria = localStorage.getItem("search-criteria");
if (!searchCriteria) {
searchCriteria = settingsDefault["search-criteria"];
localStorage.setItem("search-criteria", searchCriteria);
}
return searchCriteria;
} }
export function getThemeSettings () { export function setSetting (key, value) {
let theme = localStorage.getItem("appearance-theme"); localStorage.setItem(key, value);
if (!theme) {
theme = settingsDefault["appearance-theme"];
localStorage.setItem("appearance-theme", theme);
}
return theme;
}
export function setSyncSettings (scheme, rate) {
localStorage.setItem("sync-scheme", scheme);
localStorage.setItem("sync-rate", rate);
}
export function setSearchSettings (criteria) {
localStorage.setItem("search-criteria", criteria);
}
export function setThemeSettings (theme) {
localStorage.setItem("appearance-theme", theme);
} }
export function setAppearance () { export function setAppearance () {
const theme = getThemeSettings(); const theme = getSetting("appearance-theme");
if (theme === "auto") { if (theme === "auto") {
document.querySelector(":root").classList.remove("dark-theme", "light-theme"); document.querySelector(":root").classList.remove("dark-theme", "light-theme");
} }
@@ -192,7 +162,6 @@ export function setAppearance () {
} }
} }
// assumes href is path to svg, and id to grab is #symb
export function setIconSrc (icon, path) { export function setIconSrc (icon, path) {
icon.setAttribute("src", path); icon.setAttribute("src", path);
} }