diff --git a/Caddyfile b/Caddyfile
index eb42a99..aea8b6c 100644
--- a/Caddyfile
+++ b/Caddyfile
@@ -10,6 +10,7 @@
}
handle @staticpaths {
+ respond /favicon.ico 204
root ./etydFrontend
file_server
}
diff --git a/etydFrontend/_static/etyd.js b/etydFrontend/_static/etyd.js
index 5879b9d..79483e7 100644
--- a/etydFrontend/_static/etyd.js
+++ b/etydFrontend/_static/etyd.js
@@ -1,11 +1,22 @@
-//Firefox check
window.onload = function() {
- document.getElementById('resultfeed').value = "hii :3"
if (navigator.userAgent.includes("Firefox")) {
- document.getElementById('resultfeed').value += `\nClipboard functionality does not work on Firefox.`
- document.getElementById('clipboard1').disabled = true
- document.getElementById('clipboard2').disabled = true
+ document.getElementById('resultfeed').value += `\nClipboard buttons only work on Firefox >127.`
}
+
+ // Event listeners can only be added after the page is loaded
+ document.getElementById("actiondropdown").addEventListener("change", function() {
+ if (document.getElementById("actiondropdown").value === "POST") {
+ document.getElementById("randomizationtoggle").disabled = false
+ document.getElementById("valuefield").disabled = false
+ } else if (document.getElementById("actiondropdown").value === "DELETE") {
+ document.getElementById("randomizationtoggle").disabled = true
+ document.getElementById("randomizationtoggle").checked = false
+ randomUrlTick()
+ document.getElementById("valuefield").disabled = true
+ } else {
+ console.error("UI Code Error: Action dropdown event listener function reached impossible state")
+ }
+ })
}
function makeRandomHex(amount) {
@@ -19,6 +30,8 @@ function makeRandomHex(amount) {
return result
}
+
+
function randomUrlTick() {
if (document.getElementById("randomizationtoggle").checked == true) {
document.getElementById("targetfield").disabled = true
@@ -29,9 +42,9 @@ function randomUrlTick() {
}
}
-function buttonCopyResult() {
- navigator.clipboard.writeText(`${document.location.href}${document.getElementById("urlfield").value}`)
-}
+// function buttonCopyResult() {
+// navigator.clipboard.writeText(`${document.location.href}${document.getElementById("urlfield").value}`)
+// }
function buttonFillFromClipboard() {
navigator.clipboard.readText().then(res => {
@@ -39,9 +52,29 @@ function buttonFillFromClipboard() {
})
}
-function postData() {
- fetch("http://nrdesktop:8081/etydwrite", {
- method: "POST",
+// Changes the buttons text to OK for 500ms for action feedback
+// "internal" in this context just means not called from the page
+function internalButtonConfirmation(element) {
+ let normalValue = document.getElementById(element).innerHTML
+ document.getElementById(element).innerHTML = "Ok"
+ setTimeout(function() {
+ document.getElementById(element).innerHTML = normalValue
+ }, 500)
+}
+
+function buttonCopyUrl() {
+ navigator.clipboard.writeText(`this doesn't work rn lol`)
+ internalButtonConfirmation("buttonCopyUrl")
+}
+
+function buttonClearLog() {
+ document.getElementById("resultfeed").value = ""
+ internalButtonConfirmation("buttonClearLog")
+}
+
+function submitData() {
+ fetch(`http://nrdesktop:8081/etyd${document.getElementById("targetfield").value}`, {
+ method: document.getElementById("actiondropdown").value,
mode: "cors",
headers: {
"Authorization": document.getElementById("authfield").value
@@ -57,5 +90,4 @@ function postData() {
}).catch(error => {
document.getElementById("resultfeed").value += `\nError: ${error}`
})
-}
-
+}
\ No newline at end of file
diff --git a/etydFrontend/_static/index.css b/etydFrontend/_static/index.css
index 66ddd02..b4b4698 100644
--- a/etydFrontend/_static/index.css
+++ b/etydFrontend/_static/index.css
@@ -11,6 +11,10 @@ body {
margin-right: 1em;
}
+.marginbottom1em {
+ margin-bottom: 1em;
+}
+
.resultfeed {
height: 100%;
}
@@ -30,7 +34,8 @@ body {
input, select, textarea, button {
background: none;
color: white;
- border: 2px solid white;
+ border: 1px solid white;
+ padding: 1px 2px;
}
input:disabled, button:disabled {
diff --git a/etydFrontend/index.html b/etydFrontend/index.html
index 4b9026d..64e4571 100644
--- a/etydFrontend/index.html
+++ b/etydFrontend/index.html
@@ -1,3 +1,4 @@
+
@@ -7,66 +8,44 @@
etyd.cc
-
etyd.cc URL Shortener
-
-
+
+
-
-
-
+
+
+
+
+
+
+
-
-
+
+
+
+
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
Instructions
-
- 1. Enter your API Key in the 'Authorization' field
- 2. Enter the shortened URL you want to act upon under the 'URL' field
- 3. Enter the URL that the user will be redirected to under the 'Value' field
- 4. Change 'Action' depending if you want to create or delete a URL
- 5. Press 'POST Data' to submit the form to the server
-
-
-
-
-
Status Code Reference
-
- 400: Bad Request - You will see this if you try and delete a non-existent URL
- 401: Unauthorized - Did you enter your API key?
- 409: Conflict - The entered URL already exists, tick 'Random' and try again
- 500: Internal Server Error - If this happens something has gone very wrong
- 502: Bad Gateway - If you see this the backend is down/unreachable by Caddy
-
-
diff --git a/index.js b/index.js
index 831794d..fbb7efb 100644
--- a/index.js
+++ b/index.js
@@ -14,7 +14,7 @@ function criticalFileLoader(file) {
const globalConfig = JSON.parse(criticalFileLoader('config.json'))
const globalVersion = criticalFileLoader('GITVERSION').split(" ")[0]
-module.exports = { app, globalConfig, fs } // Export express app and fs objects and globalconfig
+module.exports = { app, globalConfig, fs, globalVersion } // Export express app and fs objects and globalconfig
app.use(express.json()) // Allows receiving JSON bodies
// see important note: https://expressjs.com/en/api.html#express.json
@@ -39,9 +39,5 @@ fs.readdir(globalConfig.startup.routesDir, (err, files) => {
}
})
-app.get("/", (rreq,rres) => {
- rres.send(`Enstrayed API | Version: ${globalVersion} | Documentation: etyd.cc/apidocs`)
-})
-
console.log(`Enstrayed API | Version: ${globalVersion} | Port: ${globalConfig.startup.apiPort}`)
app.listen(globalConfig.startup.apiPort)
\ No newline at end of file
diff --git a/liberals/auth.js b/liberals/auth.js
new file mode 100644
index 0000000..07ff077
--- /dev/null
+++ b/liberals/auth.js
@@ -0,0 +1,32 @@
+const { globalConfig } = require("../index.js")
+
+async function checkToken(token,scope) {
+ return await fetch(`${globalConfig.couchdbHost}/auth/sessions`).then(fetchRes => {
+
+ // CouchDB should only ever return 200/304 for success so this should work
+ // https://docs.couchdb.org/en/stable/api/document/common.html#get--db-docid
+ if (fetchRes.status !== 200 || fetchRes.status !== 304) {
+ console.log(`ERROR: auth.js: Database request returned ${fetchRes.status}`)
+ return false
+ } else {
+
+ return fetchRes.json().then(dbRes => {
+
+ if (dbRes.sessions[token] == undefined) { // If the token is not on the sessions list then reject
+ return false
+ } else if (dbRes.sessions[token].scopes.includes(scope)) { // If the token is on the seesions list and includes the scope then accept
+ return true
+ } else { // Otherwise reject
+ return false
+ }
+
+ })
+ }
+
+ }).catch(error => {
+ console.log("ERROR: auth.js: " + error)
+ return false
+ })
+}
+
+module.exports = {checkToken}
\ No newline at end of file
diff --git a/routes/etyd.js b/routes/etyd.js
index 60b9cd8..b381c2c 100644
--- a/routes/etyd.js
+++ b/routes/etyd.js
@@ -1,5 +1,5 @@
const { app, globalConfig } = require("../index.js") // Get globals from index
-const { checkAuthorization } = require("../liberals/authorization.js")
+const { checkToken } = require("../liberals/auth.js")
app.get("/etyd*", (rreq,rres) => {
fetch(`${globalConfig.couchdbHost}/etyd${rreq.path.replace("/etyd","")}`).then(dbRes => {
@@ -26,9 +26,8 @@ app.delete("/etyd*", (rreq,rres) => {
if (rreq.get("Authorization") === undefined) {
rres.sendStatus(400)
} else {
- checkAuthorization(globalConfig.etyd.authKeysDoc,rreq.get("Authorization")).then(authRes => {
+ checkToken(rreq.get("Authorization"),"etyd").then(authRes => {
if (authRes === false) {
- console.log(`${rreq.get("cf-connecting-ip")} DELETE ${rreq.path} returned 401`) // Log unauthorized requests
rres.sendStatus(401)
} else if (authRes === true) { // Authorization successful
@@ -73,14 +72,12 @@ app.post("/etyd*", (rreq,rres) => {
if (rreq.get("Authorization") === undefined) {
rres.sendStatus(400)
} else {
- checkAuthorization(globalConfig.etyd.authKeysDoc,rreq.get("Authorization")).then(authRes => {
+ checkToken(rreq.get("Authorization"),"etyd").then(authRes => {
if (authRes === false) {
- console.log(`${rreq.get("cf-connecting-ip")} POST ${rreq.path} returned 401`) // Log unauthorized requests
rres.sendStatus(401)
} else if (authRes === true) { // Authorization successful
if (rreq.body["url"] == undefined) {
- console.log(`${rreq.get("cf-connecting-ip")} POST ${rreq.path} returned 400 KEY: ${rreq.get("Authorization")}`)
rres.sendStatus(400)
} else {
fetch(`${globalConfig.couchdbHost}/etyd${rreq.path.replace("/etyd", "")}`, {
@@ -94,12 +91,10 @@ app.post("/etyd*", (rreq,rres) => {
switch(dbRes.status) {
case 409:
- console.log(`${rreq.get("cf-connecting-ip")} POST ${rreq.path} returned 409 KEY: ${rreq.get("Authorization")}`)
rres.sendStatus(409)
break;
case 201:
- console.log(`${rreq.get("cf-connecting-ip")} POST ${rreq.path} returned 200 KEY: ${rreq.get("Authorization")}`)
rres.status(200).send(rreq.path.replace("/etyd", ""))
break;
@@ -120,5 +115,4 @@ app.post("/etyd*", (rreq,rres) => {
})
-
module.exports = {app} // export routes to be imported by index for execution
\ No newline at end of file
diff --git a/routes/frontpage.js b/routes/frontpage.js
new file mode 100644
index 0000000..7c2297f
--- /dev/null
+++ b/routes/frontpage.js
@@ -0,0 +1,44 @@
+const { app, globalConfig, fs, globalVersion } = require("../index.js") // Get globals from index
+
+var timeSinceLastQuery = Date.now()-10000
+var cachedResult = ""
+
+app.get("/static/*", (rreq,rres) => {
+ rres.sendFile(globalConfig.frontpage.frontpageDir+"/static/"+rreq.url.replace("/static/",""))
+})
+
+app.get("/posts/*", (rreq,rres) => {
+ rres.sendFile(globalConfig.frontpage.frontpageDir+"/posts/"+rreq.url.replace("/posts/",""))
+})
+
+app.get("/", (rreq, rres) => {
+ if (Date.now() < timeSinceLastQuery+10000) {
+ rres.send(cachedResult)
+ } else {
+ let indexFile = fs.readFileSync(globalConfig.frontpage.frontpageDir+"/index.html","utf-8")
+ cachedResult = indexFile.replace("",parseFiles()).replace("",`API Version ${globalVersion}`)
+ rres.send(cachedResult)
+ }
+})
+
+function parseFiles() {
+ let files = fs.readdirSync(globalConfig.frontpage.frontpageDir+"/posts/")
+ let result = ""
+
+ for (x in files) {
+ if (files[x].endsWith(".html") === false) { break } // If file/dir is not .html then ignore
+
+ let date = files[x].split("-")[0]
+ if (date < 10000000 || date > 99999999) { break } // If date does not fit ISO8601 format then ignore
+
+ date = date.replace(/.{2}/g,"$&-").replace("-","").slice(0,-1) // Insert a dash every 2 characters, remove the first dash, remove the last character
+
+ let name = files[x].slice(9).replace(/-/g," ").replace(".html","") // Strip Date, replace seperator with space & remove file extension
+
+ result = `${date} ${name}`+result
+ }
+
+ return result
+}
+
+module.exports = {app}
\ No newline at end of file
diff --git a/routes/mailjet.js b/routes/mailjet.js
index b26e9d5..19d5e35 100644
--- a/routes/mailjet.js
+++ b/routes/mailjet.js
@@ -1,9 +1,9 @@
const { app, globalConfig } = require("../index.js") // Get globals from index
-const { checkAuthorization } = require("../liberals/authorization.js")
+const { checkToken } = require("../liberals/auth.js")
app.post("/sendemail", (rreq,rres) => {
- checkAuthorization(globalConfig.mailjet.authKeysDoc,rreq.get("Authorization")).then(authRes => {
+ checkToken(rreq.get("Authorization"),"mailjet").then(authRes => {
if (authRes === false) { // If the supplied authorization is invalid or an error occured
console.log(`${rreq.get("cf-connecting-ip")} POST /sendemail returned 401`) // Log the request
@@ -21,8 +21,7 @@ app.post("/sendemail", (rreq,rres) => {
"Messages": [
{
"From": {
- "Email": globalConfig.mailjet.senderAddress,
- "Name": globalConfig.mailjet.senderName,
+ "Email": globalConfig.mailjet.senderAddress
},
"To": [
{