add better client sync schemes initialization,
add client sync scheme selects to localdb, start on interrupt sync scheme implementation, change sync endpoints to start with /sync/
This commit is contained in:
parent
aa8148fc16
commit
178ff6168f
@ -87,6 +87,11 @@
|
|||||||
"whitelist": true,
|
"whitelist": true,
|
||||||
"display": true
|
"display": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"clientsync": {
|
||||||
|
"always": true,
|
||||||
|
"hash": true,
|
||||||
|
"interrupt": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
|
@ -7,10 +7,12 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.3.2",
|
"axios": "^1.3.2",
|
||||||
"body-parser": "^1.20.1",
|
"body-parser": "^1.20.1",
|
||||||
|
"cookie": "^0.5.0",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"morgan": "^1.10.0"
|
"morgan": "^1.10.0",
|
||||||
|
"ws": "^8.13.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.43.0",
|
"eslint": "^8.43.0",
|
||||||
|
69
src/clientsync.js
Normal file
69
src/clientsync.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { WebSocketServer } from "ws";
|
||||||
|
import * as cookie from "cookie";
|
||||||
|
|
||||||
|
import { requestPVE } from "./pve.js";
|
||||||
|
import { checkAuth, getObjectHash } from "./utils.js";
|
||||||
|
|
||||||
|
// maps usernames to socket object(s)
|
||||||
|
const userSocketMap = {};
|
||||||
|
// maps proxmox resource ids to user(s) who can access the resource
|
||||||
|
const resourceUserMap = {};
|
||||||
|
|
||||||
|
export function setupClientSync (app, server, schemes) {
|
||||||
|
/**
|
||||||
|
* GET - get list of supported synchronization schemes
|
||||||
|
* responses:
|
||||||
|
* - 200 : {always: boolean, hash: boolean, interrupt: boolean}
|
||||||
|
*/
|
||||||
|
app.get("/api/sync/schemes", async (req, res) => {
|
||||||
|
res.send(schemes);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (schemes.hash) {
|
||||||
|
/**
|
||||||
|
* GET - get hash of current cluster resources states
|
||||||
|
* Client can use this endpoint to check for cluster state changes to avoid costly data transfers to the client.
|
||||||
|
* responses:
|
||||||
|
* - 401: {auth: false}
|
||||||
|
* - 200: string
|
||||||
|
*/
|
||||||
|
app.get("/api/sync/hash", async (req, res) => {
|
||||||
|
// check auth
|
||||||
|
const auth = await checkAuth(req.cookies, res);
|
||||||
|
if (!auth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// get current cluster resources
|
||||||
|
const status = (await requestPVE("/cluster/resources", "GET", req.cookies)).data.data;
|
||||||
|
// filter out just state information of resources that are needed
|
||||||
|
const resources = ["lxc", "qemu", "node"];
|
||||||
|
const state = {};
|
||||||
|
status.forEach((element) => {
|
||||||
|
if (resources.includes(element.type)) {
|
||||||
|
state[element.id] = element.status;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
res.status(200).send(getObjectHash(state));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (schemes.interrupt) {
|
||||||
|
const wsServer = new WebSocketServer({ noServer: true, path: "/api/sync/interrupt" });
|
||||||
|
wsServer.on("connection", (socket, username) => {
|
||||||
|
socket.on("message", (message) => {
|
||||||
|
console.log(message.toString());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
server.on("upgrade", async (req, socket, head) => {
|
||||||
|
const cookies = cookie.parse(req.headers.cookie || "");
|
||||||
|
const auth = (await requestPVE("/version", "GET", cookies)).status === 200;
|
||||||
|
if (!auth) {
|
||||||
|
socket.destroy();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
wsServer.handleUpgrade(req, socket, head, (socket) => {
|
||||||
|
wsServer.emit("connection", socket, cookies.username);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
32
src/main.js
32
src/main.js
@ -6,8 +6,9 @@ import morgan from "morgan";
|
|||||||
|
|
||||||
import { api } from "./package.js";
|
import { api } from "./package.js";
|
||||||
import { requestPVE, handleResponse, getDiskInfo, getDeviceInfo, getNodeAvailDevices } from "./pve.js";
|
import { requestPVE, handleResponse, getDiskInfo, getDeviceInfo, getNodeAvailDevices } from "./pve.js";
|
||||||
import { checkAuth, approveResources, getUserResources, getObjectHash } from "./utils.js";
|
import { checkAuth, approveResources, getUserResources } from "./utils.js";
|
||||||
import { db, pveAPIToken, listenPort, hostname, domain } from "./db.js";
|
import { db, pveAPIToken, listenPort, hostname, domain } from "./db.js";
|
||||||
|
import { setupClientSync } from "./clientsync.js";
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
@ -1196,32 +1197,7 @@ app.delete(`/api/:node(${nodeRegexP})/:type(${typeRegexP})/:vmid(${vmidRegexP})/
|
|||||||
await handleResponse(params.node, result, res);
|
await handleResponse(params.node, result, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
const server = app.listen(listenPort, () => {
|
||||||
* GET - get hash of current cluster resources states
|
|
||||||
* Client can use this endpoint to check for cluster state changes to avoid costly data transfers to the client.
|
|
||||||
* responses:
|
|
||||||
* - 401: {auth: false}
|
|
||||||
* - 200: string
|
|
||||||
*/
|
|
||||||
app.get(`/api/cluster/statushash`, async (req, res) => {
|
|
||||||
// check auth
|
|
||||||
const auth = await checkAuth(req.cookies, res);
|
|
||||||
if (!auth) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// get current cluster resources
|
|
||||||
let status = (await requestPVE("/cluster/resources", "GET", req.cookies)).data.data;
|
|
||||||
// filter out just state information of resources that are needed
|
|
||||||
let resources = ["lxc", "qemu", "node"];
|
|
||||||
let state = {};
|
|
||||||
status.forEach((element) => {
|
|
||||||
if (resources.includes(element.type)) {
|
|
||||||
state[element.id] = element.status;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
res.status(200).send(getObjectHash(state));
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(listenPort, () => {
|
|
||||||
console.log(`proxmoxaas-api v${api.version} listening on port ${listenPort}`);
|
console.log(`proxmoxaas-api v${api.version} listening on port ${listenPort}`);
|
||||||
});
|
});
|
||||||
|
setupClientSync(app, server, db.getGlobalConfig().clientsync);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {createHash} from "crypto";
|
import { createHash } from "crypto";
|
||||||
|
|
||||||
import { getUsedResources, requestPVE } from "./pve.js";
|
import { getUsedResources, requestPVE } from "./pve.js";
|
||||||
import { db } from "./db.js";
|
import { db } from "./db.js";
|
||||||
@ -94,9 +94,8 @@ export async function approveResources (req, username, request) {
|
|||||||
return approved; // if all requested resources pass, allow
|
return approved; // if all requested resources pass, allow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function getObjectHash (object, alg = "sha256", format = "hex") {
|
export function getObjectHash (object, alg = "sha256", format = "hex") {
|
||||||
const hash = createHash(alg);
|
const hash = createHash(alg);
|
||||||
hash.update(JSON.stringify(object, Object.keys(object).sort()));
|
hash.update(JSON.stringify(object, Object.keys(object).sort()));
|
||||||
return hash.digest(format);
|
return hash.digest(format);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user