+ 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?
+ 405: Method Not Allowed - You will see this if you try a request with no arguments
+ 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
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 21f47ad..43da3b4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,20 @@
"version": "1.0.0",
"license": "UNLICENSED",
"dependencies": {
- "express": "^4.18.2"
+ "express": "^4.18.2",
+ "typescript": "^5.4.3"
+ },
+ "devDependencies": {
+ "@types/node": "^20.12.3"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.12.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.3.tgz",
+ "integrity": "sha512-sD+ia2ubTeWrOu+YMF+MTAB7E+O7qsMqAbMfW7DG3K1URwhZ5hN1pLlRVGbf4wDFzSfikL05M17EyorS86jShw==",
+ "dev": true,
+ "dependencies": {
+ "undici-types": "~5.26.4"
}
},
"node_modules/accepts": {
@@ -666,6 +679,24 @@
"node": ">= 0.6"
}
},
+ "node_modules/typescript": {
+ "version": "5.4.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
+ "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true
+ },
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
diff --git a/package.json b/package.json
index 9b134b7..01392d7 100644
--- a/package.json
+++ b/package.json
@@ -18,5 +18,9 @@
"bugs": {
"url": "https://github.com/enstrayed/enstrayedapi/issues"
},
- "homepage": "https://api.enstrayed.com"
+ "homepage": "https://api.enstrayed.com",
+ "devDependencies": {
+ "@types/bun": "^1.0.12",
+ "@types/node": "^20.12.3"
+ }
}
diff --git a/routes/etyd.js b/routes/etyd.js
index 8c011ec..140eb16 100644
--- a/routes/etyd.js
+++ b/routes/etyd.js
@@ -1,108 +1,108 @@
const { app, db, globalConfig } = require("../index.js") // Get globals from index
-function makeRandomHex() {
- const characters = "1234567890abcdef"
- let counter = 0
- let result = ""
- while (counter < globalConfig.etyd.randomHexLength) {
- result += characters.charAt(Math.floor(Math.random() * characters.length))
- counter += 1
- }
- return result
-}
+// 2024-04-05: Unused because trying to put randomization server side just made no sense
+// function makeRandomHex() {
+// const characters = "1234567890abcdef"
+// let counter = 0
+// let result = ""
+// while (counter < globalConfig.etyd.randomHexLength) {
+// result += characters.charAt(Math.floor(Math.random() * characters.length))
+// counter += 1
+// }
+// return result
+// }
-app.options("/etydwrite", (rreq,rres) => {
- rres.set("Access-Control-Allow-Headers","Authorization")
- rres.set("Access-Control-Allow-Origin","*")
- rres.sendStatus(204)
-})
+// 2024-04-05: Defining OPTIONS for browser prefetch is no longer necessary as CORS is not going to be used
+// app.options("/etydwrite", (rreq,rres) => {
+// rres.set("Access-Control-Allow-Headers","Authorization")
+// rres.set("Access-Control-Allow-Origin","*")
+// rres.sendStatus(204)
+// })
-app.post("/etydwrite", (rreq,rres) => {
- db.get(globalConfig.etyd.authKeyInDb).then(dbres => {
-
- if (dbres == null) { // If authkey key doesnt exist in redis then error out
- console.log("ERROR: Configured key containing etyd authkeys is null")
- rres.sendStatus(500)
- } else { // if it does exist
- let validKeys = dbres.split(",") // split the string into an array
- if (validKeys.includes(rreq.get("Authorization"))) { // check if authorization header key exists in that array
-
- console.log(rreq.body)
-
- switch(rreq.body.action) {
- case "set": // Write to db
- if (rreq.body.random == true) {
-
- let workingTarget = makeRandomHex() // Make a random URL
- db.get(`/${workingTarget}`).then(dbres => { // Check if it exists
- if (dbres != null) { // If it does
- let workingTarget = makeRandomHex() // Make a new one
- db.get(`/${workingTarget}`).then(dbres => { // Check if *that* exists
- if (dbres != null) { // If it does
- // Then everything is dumb and pointless so just give up
- console.log(`${rreq.get("cf-connecting-ip")} POST /etydwrite ACTION set returned 409 (Two attempts to find an open key failed)`)
- rres.sendStatus(409)
- } else { // if it doesnt then set the stupid key I hate this code so much why did I do this serverside this is so dumb
- console.log(`${rreq.get("cf-connecting-ip")} POST /etydwrite ACTION set returned 200 KEY:${rreq.get("Authorization")} TARGET: ${workingTarget}`)
- db.set(`/${workingTarget}`,rreq.body.value)
- rres.send(`https://etyd.cc/${workingTarget}`)
- }
- })
-
- } else {
- console.log(`${rreq.get("cf-connecting-ip")} POST /etydwrite ACTION set returned 200 KEY:${rreq.get("Authorization")} TARGET: ${workingTarget}`)
- db.set(`/${workingTarget}`,rreq.body.value)
- rres.send(`https://etyd.cc/${workingTarget}`)
- }
- })
-
- } else {
-
- db.get(rreq.body.target).then(dbres => { // check if key already exists
- if (dbres != null) { // if it does then send 409 conflict
- console.log(`${rreq.get("cf-connecting-ip")} POST /etydwrite ACTION set returned 409 KEY:${rreq.get("Authorization")}`)
- rres.sendStatus(409)
- } else {
- db.set(`/${rreq.body.target}`,rreq.body.value)
- rres.send(`https://etyd.cc/${rreq.body.target}`)
- }
- })
-
- }
- break;
-
-
- case "delete":
- let workingTarget = rreq.body.target.replace("https://etyd.cc/","") // Sanitize input
- if (workingTarget.startsWith("/")) {
- workingTarget = workingTarget.slice(1)
- }
-
- db.get(`/${workingTarget}`).then(dbres => {
- if (dbres == null) { //if key doesnt exist then log and return 400
- console.log(`${rreq.get("cf-connecting-ip")} POST /etydwrite ACTION delete returned 404 KEY:${rreq.get("Authorization")} TARGET: ${workingTarget}`)
- rres.sendStatus(404)
- } else {
- console.log(`${rreq.get("cf-connecting-ip")} POST /etydwrite ACTION delete returned 200 KEY:${rreq.get("Authorization")} TARGET: ${workingTarget}`)
- db.del(`/${workingTarget}`)
- rres.sendStatus(200)
- }
- })
- break;
-
-
- default:
- console.log(`${rreq.get("cf-connecting-ip")} POST /etydwrite ACTION default returned 400 KEY:${rreq.get("Authorization")}`)
- rres.sendStatus(400) // request json didnt include a valid action
- break;
- }
-
- } else { // if it doesnt then its a unauthorized request
- console.log(`${rreq.get("cf-connecting-ip")} POST /etydwrite returned 401`)
- rres.sendStatus(401)
- }
+app.get("/etyd*", (rreq,rres) => {
+ fetch(`http://${globalConfig.couchdb.host}/etyd${rreq.path.replace("/etyd","")}`, {
+ headers: {
+ "Authorization": `Basic ${btoa(globalConfig.couchdb.authorization)}`
}
+ }).then(dbRes => {
+ if (dbRes.status == 404) {
+ rres.sendStatus(404)
+ } else {
+ dbRes.json().then(dbRes => {
+ rres.redirect(dbRes.content.url)
+ })
+ }
+ }).catch(fetchError => {
+ rres.sendStatus(500)
+ console.log(`${rres.get("cf-connecting-ip")} GET ${rreq.path} returned 500: ${fetchError}`)
})
})
+app.delete("/etyd*", (rreq,rres) => {
+
+ fetch(`http://${globalConfig.couchdb.host}/apiauthkeys/${globalConfig.etyd.authKeysDoc}`, {
+ headers: {
+ "Authorization": `Basic ${btoa(globalConfig.couchdb.authorization)}`
+ }
+ }).then(dbRes => dbRes.json()).then(dbRes => {
+
+ if (dbRes.status == 404) { // If document containing cider auth keys does not exist
+ console.log(`ERROR: Could not find apiauthkeys/${globalConfig.etyd.authKeysDoc}`)
+ rres.sendStatus(500) // Refuse request
+ } else {
+ if (rreq.get("Authorization") == null) { // If authorization header is not supplied
+ rres.sendStatus(400) // then return bad request (would return 500 otherwise)
+ } else {
+ if (dbRes["content"][rreq.get("Authorization").split("_")[0]] === rreq.get("Authorization").split("_")[1]) {
+
+ fetch(`http://${globalConfig.couchdb.host}/etyd${rreq.path.replace("/etyd", "")}`, {
+ headers: {
+ "Authorization": `Basic ${btoa(globalConfig.couchdb.authorization)}`
+ }
+ }).then(dbRes => {
+
+ if (dbRes.status == 404) {
+ rres.sendStatus(404)
+ } else {
+ dbRes.json().then(dbRes => {
+
+ fetch(`http://${globalConfig.couchdb.host}/etyd${rreq.path.replace("/etyd", "")}`, {
+ method: "DELETE",
+ headers: {
+ "Authorization": `Basic ${btoa(globalConfig.couchdb.authorization)}`,
+ "If-Match": dbRes["_rev"]
+ }
+ }).then(fetchRes => {
+ if (fetchRes.status == 200) {
+ console.log(`${rres.get("cf-connecting-ip")} DELETE ${rreq.path} returned 200 KEY: ${rreq.get("Authorization")}`)
+ rres.sendStatus(200)
+ }
+ }).catch(fetchError => {
+ rres.sendStatus(500)
+ console.log(`${rres.get("cf-connecting-ip")} DELETE ${rreq.path} returned 500: ${fetchError}`)
+ })
+
+ })
+ }
+
+ }).catch(fetchError => {
+ rres.sendStatus(500)
+ console.log(`${rres.get("cf-connecting-ip")} DELETE ${rreq.path} returned 500: ${fetchError}`)
+ })
+
+ } else {
+ console.log(`${rreq.get("cf-connecting-ip")} DELETE ${rreq.path} returned 401`) // log ip of unauthorized requests
+ rres.sendStatus(401) // received auth key was not in database
+ }
+ }
+ }
+ }).catch(fetchError => {
+ rres.sendStatus(500)
+ console.log(`${rres.get("cf-connecting-ip")} DELETE ${rreq.path} returned 500: ${fetchError}`)
+ })
+
+})
+
+
+
module.exports = {app} // export routes to be imported by index for execution
\ No newline at end of file