sync changes

This commit is contained in:
Enstrayed
2025-10-01 09:50:32 -07:00
parent 4de76c34c2
commit b1ac3d6f58
16 changed files with 114 additions and 60 deletions

View File

@@ -6,8 +6,8 @@ import { randomStringBase62, getHumanReadableUserAgent } from "../liberals/misc.
app.get("/api/auth/whoami", (rreq,rres) => { app.get("/api/auth/whoami", (rreq,rres) => {
if (!rreq.cookies["APIToken"] && !rreq.get("Authorization")) { if (!rreq.cookies["APIToken"] && !rreq.get("Authorization")) {
rres.send({ "loggedIn": false, "username": "", "scopes": "" }) rres.send({ "loggedIn": false, "username": "", "scopes": "" })
} else { } else { // select
db`select s.scopes, u.username from sessions s join users u on s.owner = u.id where s.token = ${rreq.cookies["APIToken"] ?? rreq.get("Authorization")}`.then(dbRes => { db`select scopes,username from sessions where token = ${rreq.cookies["APIToken"] ?? rreq.get("Authorization")}`.then(dbRes => {
if (dbRes.length > 0 && dbRes.length < 2) { if (dbRes.length > 0 && dbRes.length < 2) {
rres.send({ "loggedIn": true, "username": dbRes[0]?.username, "scopes": dbRes[0]?.scopes.split(",") }) rres.send({ "loggedIn": true, "username": dbRes[0]?.username, "scopes": dbRes[0]?.scopes.split(",") })
} else { } else {
@@ -75,7 +75,7 @@ app.get("/api/auth/callback", (rreq,rres) => {
let newExpiration = Date.now() + 86400 let newExpiration = Date.now() + 86400
let newComment = `Login token for ${getHumanReadableUserAgent(rreq.get("User-Agent"))} on ${rreq.get("cf-connecting-ip") ?? rreq.ip}` let newComment = `Login token for ${getHumanReadableUserAgent(rreq.get("User-Agent"))} on ${rreq.get("cf-connecting-ip") ?? rreq.ip}`
db`insert into sessions (token,owner,scopes,expires,comment) values (${newToken},(select id from users where oidc_username = ${fetchRes2.username}),${fetchRes2.enstrayedapi_scopes},${newExpiration},${newComment});`.then(dbRes1 => { db`insert into sessions (token,username,scopes,expires,comment) values (${newToken},${fetchRes2.username},${fetchRes2.enstrayedapi_scopes},${newExpiration},${newComment});`.then(dbRes1 => {
if (rreq.query.state.split("_")[0] === "redirect") { if (rreq.query.state.split("_")[0] === "redirect") {
let newDestination = atob(rreq.query.state.split("_")[1].replace("-","/")) let newDestination = atob(rreq.query.state.split("_")[1].replace("-","/"))
rres.setHeader("Set-Cookie", `APIToken=${newToken}; Domain=${rreq.hostname}; Expires=${new Date(newExpiration).toUTCString()}; Path=/`).redirect(newDestination) rres.setHeader("Set-Cookie", `APIToken=${newToken}; Domain=${rreq.hostname}; Expires=${new Date(newExpiration).toUTCString()}; Path=/`).redirect(newDestination)

View File

@@ -9,3 +9,11 @@ app.get("/dynamic/icon/*", (rreq, rres) => {
rres.sendStatus(404) rres.sendStatus(404)
} }
}) })
app.get("/dynamic/background/*", (rreq,rres) => {
if (rreq.headers["accept"].includes("image/jxl")) {
rres.setHeader("Content-Type", "image/jxl").sendFile( `${process.cwd()}/website/dynamic/backgrounds/${rreq.path.split("/")[3]}.jxl`)
} else {
rres.setHeader("Content-Type", "image/jpeg").sendFile( `${process.cwd()}/website/dynamic/backgrounds/${rreq.path.split("/")[3]}.jpg`)
}
})

View File

@@ -26,7 +26,14 @@ app.get("/helpdesk/ticket/new", (rreq,rres) => {
}) })
app.get("/api/helpdesk/forms/*", (rreq, rres) => { app.get("/api/helpdesk/forms/*", (rreq, rres) => {
rres.sendFile(process.cwd()+"/website/helpdesk/forms/"+rreq.url.replace("/api/helpdesk/forms/","")) fs.readFile(process.cwd()+"/website/helpdesk/forms/"+rreq.url.replace("/api/helpdesk/forms/","")+".json","utf-8", (error, data) => {
if (error) {
rres.status(400).send("Unable to retrieve requested form")
} else {
rres.type('json').send(data)
}
})
}) })
app.get("/helpdesk/static/*", (rreq,rres) => { app.get("/helpdesk/static/*", (rreq,rres) => {

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

View File

@@ -1,14 +0,0 @@
{
"anonymousSubmission": false,
"form": {
"description": {
"type": "span",
"content": "Important: <ul> <li>Your Enstrayed Cloud quota does NOT affect your EOES mailbox quota.</li> <li>Your request will be reviewed and may be denied for any reason.</li> </ul>"
},
"newquota": {
"type": "text",
"content": "New quota (in GB)",
"required": true
}
}
}

View File

@@ -4,10 +4,6 @@
"type": "text", "type": "text",
"content": "What is your name?" "content": "What is your name?"
}, },
"email": {
"type": "text",
"content": "What is a good email address to reach you at?"
},
"message": { "message": {
"type": "bigtext", "type": "bigtext",
"content": "Please enter your message below." "content": "Please enter your message below."

View File

@@ -5,6 +5,5 @@
"general_techsupport": "General Technical Support", "general_techsupport": "General Technical Support",
"ecls_deleteaccount": "ECLS: Delete Account", "ecls_deleteaccount": "ECLS: Delete Account",
"ecls_passwordreset": "ECLS: Password Reset", "ecls_passwordreset": "ECLS: Password Reset",
"ecls_personalinfochange": "ECLS: Personal Information Change", "ecls_personalinfochange": "ECLS: Personal Information Change"
"enstrayedcloud_quotachange": "Enstrayed Cloud: Quota Change"
} }

View File

@@ -57,23 +57,8 @@ body {
margin: 0 0 0.5em 0; margin: 0 0 0.5em 0;
} }
.articlecontent { .helpdeskScopeOnly {
background-color: #fff; display: none;
padding: 2em;
margin: 2em 0 2em;
max-width: 100ch;
}
.articlecontent > h1 {
margin: 0 0 0.5em;
}
.articlecontent > ol > li {
margin: 0 0 0.2em;
}
.articlecontent img {
max-width: 100%;
} }
.linkColumn { .linkColumn {
@@ -107,3 +92,30 @@ dialog::backdrop {
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.5);
} }
#formarea > div > input {
width: 50%;
}
#formarea > div > textarea {
width: 100%;
}
/* Article Specific */
.articlecontent {
background-color: #fff;
padding: 2em;
margin: 2em 0 2em;
max-width: 100ch;
}
.articlecontent > h1 {
margin: 0 0 0.5em;
}
.articlecontent > ol > li {
margin: 0 0 0.2em;
}
.articlecontent img {
max-width: 100%;
}

View File

@@ -12,6 +12,14 @@ document.addEventListener("DOMContentLoaded", function () {
if (jsonRes.loggedIn) { if (jsonRes.loggedIn) {
globalLoggedIn = true globalLoggedIn = true
document.getElementById("loginButton").innerText = `Logout ${jsonRes.username}` document.getElementById("loginButton").innerText = `Logout ${jsonRes.username}`
if (jsonRes.scopes.includes("helpdesk")) {
let targetElements = document.getElementsByClassName("helpdeskScopeOnly")
// cant do a for loop since targetElements dynamically updates, this is easier but stupid
while (targetElements.length > 0) {
targetElements[0].classList.toggle("helpdeskScopeOnly")
}
}
} else { } else {
globalLoggedIn = false globalLoggedIn = false
document.getElementById("loginButton").innerText = `Login` document.getElementById("loginButton").innerText = `Login`
@@ -24,8 +32,7 @@ function loginFunction() {
if (globalLoggedIn === true) { if (globalLoggedIn === true) {
fetch(`/api/auth/logout`).then(fetchRes => { fetch(`/api/auth/logout`).then(fetchRes => {
if (fetchRes.status === 200) { if (fetchRes.status === 200) {
globalLoggedIn = false location.reload()
document.getElementById("loginButton").innerText = `Login`
} else { } else {
fetchRes.text().then(textRes => { fetchRes.text().then(textRes => {
useGlobalDialog("Error", `An error occurred during logout: ${textRes}`) useGlobalDialog("Error", `An error occurred during logout: ${textRes}`)
@@ -41,6 +48,13 @@ function loginFunction() {
if (jsonRes.loggedIn) { if (jsonRes.loggedIn) {
globalLoggedIn = true globalLoggedIn = true
document.getElementById("loginButton").innerText = `Logout ${jsonRes.username}` document.getElementById("loginButton").innerText = `Logout ${jsonRes.username}`
if (jsonRes.scopes.includes("helpdesk")) {
let targetElements = document.getElementsByClassName("helpdeskScopeOnly")
while (targetElements.length > 0) {
targetElements[0].classList.toggle("helpdeskScopeOnly")
}
}
} else { } else {
useGlobalDialog("Error", `You are not logged in. Please try logging in again.`) useGlobalDialog("Error", `You are not logged in. Please try logging in again.`)
} }

View File

@@ -10,8 +10,8 @@
<body> <body>
<div class="headerbar"> <div class="headerbar">
<h1>Enstrayed Helpdesk</h1> <a href="/helpdesk" class="headerbarTitle">Enstrayed Helpdesk</a>
<a href="/helpdesk/ticket/new">Open Ticket</a> <a href="/helpdesk/ticket/new">New Request</a>
<a href="/helpdesk/articles">Knowledgebase</a> <a href="/helpdesk/articles">Knowledgebase</a>
<div class="headerbarright"> <div class="headerbarright">

View File

@@ -11,8 +11,9 @@
<div class="headerbar"> <div class="headerbar">
<a href="/helpdesk" class="headerbarTitle">Enstrayed Helpdesk</a> <a href="/helpdesk" class="headerbarTitle">Enstrayed Helpdesk</a>
<a href="/helpdesk/ticket/new">Open Ticket</a> <a href="/helpdesk/ticket/new">New Request</a>
<a href="/helpdesk/articles">Knowledgebase</a> <a href="/helpdesk/articles">Knowledgebase</a>
<a href="/helpdesk/ticket/list" class="helpdeskScopeOnly">Ticket List</a>
<div class="headerbarright"> <div class="headerbarright">
<button id="loginButton" onclick="loginFunction()">Login</button> <button id="loginButton" onclick="loginFunction()">Login</button>

View File

@@ -9,11 +9,9 @@
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
document.getElementById('formSelection').value = 'none' document.getElementById('formSelection').value = 'none'
document.getElementById('formSelection').addEventListener('change', function() {
useGlobalDialog(`Info`, `You selected ${this.value}`)
})
fetch(`/api/helpdesk/forms/manifest.json`).then(fetchRes => {
fetch(`/api/helpdesk/forms/manifest`).then(fetchRes => {
fetchRes.json().then(jsonRes => { fetchRes.json().then(jsonRes => {
for (let x in jsonRes) { for (let x in jsonRes) {
let newElement = document.createElement('option') let newElement = document.createElement('option')
@@ -23,6 +21,33 @@
} }
}) })
}) })
document.getElementById('formSelection').addEventListener('change', function() {
document.getElementById("formarea").innerHTML = ""
fetch(`/api/helpdesk/forms/${this.value}`).then(fetchRes => {
fetchRes.json().then(jsonRes => {
for (x in jsonRes.form) {
let newElem
switch (jsonRes.form[x].type) {
case "text":
newElem = document.createElement('div')
newElem.innerHTML = `<label for="${"formarea_"+x}">${jsonRes.form[x].content}</label><br><input type="text" id="${"formarea_"+x}">`
document.getElementById("formarea").appendChild(newElem)
break;
case "bigtext":
newElem = document.createElement('div')
newElem.innerHTML = `<label for="${"formarea_"+x}">${jsonRes.form[x].content}</label><br><textarea id="${"formarea_"+x}" ></textarea>`
document.getElementById("formarea").appendChild(newElem)
break;
}
}
})
})
})
}) })
</script> </script>
</head> </head>
@@ -30,23 +55,31 @@
<div class="headerbar"> <div class="headerbar">
<a href="/helpdesk" class="headerbarTitle">Enstrayed Helpdesk</a> <a href="/helpdesk" class="headerbarTitle">Enstrayed Helpdesk</a>
<a>Open Ticket</a> <a>New Request</a>
<a href="/helpdesk/articles">Knowledgebase</a> <a href="/helpdesk/articles">Knowledgebase</a>
<div class="headerbarright"> <div class="headerbarright">
<button id="loginButton" onclick="loginFunction()">Login</button> <button id="loginButton" onclick="loginFunction()">Login</button>
</div> </div>
</div> </div>
<div class="maincontent"> <div class="maincontent">
<div class="newticketmaincontent"> <div class="newticketmaincontent">
<h2>New Ticket</h2> <h2>New Request</h2>
<div>
<label for="formSelection">Please select a form: </label> <label for="formSelection">Please select a form: </label>
<select name="Form Selection" id="formSelection"> <select name="Form Selection" id="formSelection">
<option value="none" disabled selected>-- Choose From List --</option> <option value="none" disabled selected>-- Choose From List --</option>
</select> </select>
</div>
<hr> <hr>
<div id="formarea">
</div> </div>
</div> </div>
</div>
<dialog id="globalDialog"> <dialog id="globalDialog">
<h2 id="globalDialogHeader">Warning</h2> <h2 id="globalDialogHeader">Warning</h2>
<p id="globalDialogText">This is a warning</p> <p id="globalDialogText">This is a warning</p>

View File

@@ -1,9 +1,8 @@
html { html {
background-color: #0f0f0f; background-color: #0f0f0f;
background-image: url('/static/bridge2.jpg'); background-image: url('/dynamic/background/main');
/*! background-size: 100%; */ background-size: cover;
background-position-y: 50%; background-position-y: 50%;
background-position-x: center;
height: 100%; height: 100%;
} }
@@ -64,7 +63,6 @@ body {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
gap: 0.2em; gap: 0.2em;
max-width: 14em;
} }
#nowplaying .nowPlayingLine2 { #nowplaying .nowPlayingLine2 {