continuous-integration/drone/push Build encountered an error
- Provider-controlled strings (channel names, groups, file names, log lines, in-place mapping edits, client info) are rendered with textContent instead of innerHTML. PopupContent.description() takes an explicit isHTML flag that only the static help texts pass. - Websocket: drop the always-true CheckOrigin so gorilla's same-origin check applies; read the session token from the HttpOnly cookie sent with the handshake (the ?Token= query parameter is still accepted for older clients); the client no longer puts the token in the URL, rewrites the cookie, or console-logs request/response payloads. - Session cookie is HttpOnly, SameSite=Strict, Path=/, session-scoped (expiry stays server side) and is cleared on logout. - Serving the first-run wizard no longer sets Settings.AuthenticationWEB to false; the wizard page simply bypasses login while it is active. - Upgrade failures no longer write a second error response. - Tests: src/websocket_test.go covers cross-origin refusal, same-origin and no-Origin clients, missing/unknown/legacy tokens, and cookie flags.
203 lines
4.6 KiB
TypeScript
203 lines
4.6 KiB
TypeScript
class Server {
|
|
protocol:string
|
|
cmd:string
|
|
|
|
constructor(cmd:string) {
|
|
this.cmd = cmd
|
|
}
|
|
|
|
request(data:Object):any {
|
|
|
|
if (SERVER_CONNECTION == true) {
|
|
return
|
|
}
|
|
|
|
SERVER_CONNECTION = true
|
|
|
|
if (this.cmd != "updateLog") {
|
|
showElement("loading", true)
|
|
UNDO = new Object()
|
|
setConnectionState("busy")
|
|
}
|
|
|
|
switch(window.location.protocol) {
|
|
case "http:":
|
|
this.protocol = "ws://"
|
|
break
|
|
case "https:":
|
|
this.protocol = "wss://"
|
|
break
|
|
}
|
|
|
|
var wsHost:string = window.location.host
|
|
if (wsHost == undefined || wsHost.length < 1) {
|
|
wsHost = window.location.hostname
|
|
}
|
|
// The session cookie (HttpOnly) is sent with the websocket handshake.
|
|
var url = this.protocol + wsHost + "/data/"
|
|
|
|
data["cmd"] = this.cmd
|
|
var requestCmd:string = data["cmd"]
|
|
var ws = new WebSocket(url)
|
|
var isLogUpdate:boolean = data["cmd"] == "updateLog"
|
|
var responseReceived:boolean = false
|
|
var requestFinished:boolean = false
|
|
var timeoutMs:number = 12000
|
|
var requestTimeout:number
|
|
|
|
var finishRequest = function(state:string, responseSuccess:boolean = false):void {
|
|
if (requestFinished == true) {
|
|
return
|
|
}
|
|
|
|
requestFinished = true
|
|
SERVER_CONNECTION = false
|
|
window.clearTimeout(requestTimeout)
|
|
|
|
if (responseSuccess == true) {
|
|
if (state == "online") {
|
|
WS_FAILURE_COUNT = 0
|
|
}
|
|
} else {
|
|
WS_FAILURE_COUNT++
|
|
}
|
|
|
|
if (isLogUpdate == false) {
|
|
showElement("loading", false)
|
|
}
|
|
|
|
if (state != "") {
|
|
setConnectionState(state)
|
|
}
|
|
}
|
|
|
|
requestTimeout = window.setTimeout(function() {
|
|
console.log("Websocket request timed out.")
|
|
var timeoutState:string = "offline"
|
|
if (isLogUpdate == true && WS_FAILURE_COUNT < 2) {
|
|
timeoutState = "idle"
|
|
}
|
|
finishRequest(timeoutState, false)
|
|
try {
|
|
ws.close()
|
|
} catch (err) {
|
|
console.log(err)
|
|
}
|
|
}, timeoutMs)
|
|
|
|
ws.onopen = function() {
|
|
|
|
WS_AVAILABLE = true
|
|
if (data["cmd"] != "updateLog") {
|
|
setConnectionState("busy")
|
|
}
|
|
|
|
this.send(JSON.stringify(data));
|
|
|
|
}
|
|
|
|
ws.onerror = function(e) {
|
|
|
|
console.log("No websocket connection to xTeVe could be established. Check your network configuration.")
|
|
var errorState:string = "offline"
|
|
if (isLogUpdate == true && WS_FAILURE_COUNT < 2) {
|
|
errorState = "idle"
|
|
}
|
|
finishRequest(errorState, false)
|
|
|
|
if (WS_AVAILABLE == false && isLogUpdate == false && requestCmd != "getServerConfig") {
|
|
alert("No websocket connection to xTeVe could be established. Check your network configuration.")
|
|
}
|
|
|
|
}
|
|
|
|
|
|
ws.onmessage = function (e) {
|
|
responseReceived = true
|
|
finishRequest("online", true)
|
|
|
|
var response = JSON.parse(e.data);
|
|
|
|
if (response["status"] == false) {
|
|
setConnectionState("offline")
|
|
|
|
alert(response["err"])
|
|
|
|
if (response.hasOwnProperty("reload")) {
|
|
location.reload()
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
|
|
if (response.hasOwnProperty("logoURL")) {
|
|
var div = (document.getElementById("channel-icon") as HTMLInputElement)
|
|
div.value = response["logoURL"]
|
|
div.className = "changed"
|
|
return
|
|
}
|
|
|
|
switch (data["cmd"]) {
|
|
case "updateLog":
|
|
SERVER["log"] = response["log"]
|
|
if (document.getElementById("content_log")) {
|
|
showLogs(false)
|
|
}
|
|
return
|
|
break;
|
|
|
|
default:
|
|
SERVER = new Object()
|
|
SERVER = response
|
|
break;
|
|
}
|
|
|
|
if (response.hasOwnProperty("openMenu")) {
|
|
var menu = document.getElementById(response["openMenu"])
|
|
menu.click()
|
|
showElement("popup", false)
|
|
}
|
|
|
|
if (response.hasOwnProperty("openLink")) {
|
|
window.location = response["openLink"]
|
|
}
|
|
|
|
if (response.hasOwnProperty("alert")) {
|
|
alert(response["alert"])
|
|
}
|
|
|
|
if (response.hasOwnProperty("reload")) {
|
|
location.reload()
|
|
}
|
|
|
|
|
|
if (response.hasOwnProperty("wizard")) {
|
|
createLayout()
|
|
configurationWizard[response["wizard"]].createWizard()
|
|
return
|
|
}
|
|
|
|
createLayout()
|
|
|
|
}
|
|
|
|
ws.onclose = function() {
|
|
if (responseReceived == true) {
|
|
return
|
|
}
|
|
|
|
var closeState:string = "offline"
|
|
if (isLogUpdate == true && WS_FAILURE_COUNT < 2) {
|
|
closeState = "idle"
|
|
}
|
|
finishRequest(closeState, false)
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var WS_FAILURE_COUNT:number = 0
|
|
|