Merge pull request #4 from Enstrayed/main
Merge changes into new auth branch
This commit was merged in pull request #4.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,3 +2,5 @@ node_modules/
|
|||||||
config.json
|
config.json
|
||||||
bun.lockb
|
bun.lockb
|
||||||
GITVERSION
|
GITVERSION
|
||||||
|
todo.txt
|
||||||
|
proto.js
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
FROM node:20
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
RUN git clone https://github.com/enstrayed/enstrayedapi .
|
|
||||||
RUN git config --global --add safe.directory /app
|
|
||||||
RUN git show --oneline -s >> GITVERSION
|
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
ENTRYPOINT [ "node", "index.js" ]
|
|
||||||
105
README.md
Normal file
105
README.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# Enstrayed API
|
||||||
|
This repository contains the code for my personal web API written in JavaScript using the Express framework.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
This file contains documentation relevant for development and deployment, but not necessarily usage. Information for all endpoints is available [on my website](https://enstrayed.com/posts/20240409-API-Documentation.html).
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
If you would like to report a bug or security issue, please open a GitHub issue. If you are the operator of a service this application accesses, use the contact information provided during registration with your service to contact me directly.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
On startup, this application will look for two files. If either cannot be read, it will exit with an error code.
|
||||||
|
1. `config.json` contains settings required for operation and API keys used for calling external services.
|
||||||
|
2. `GITVERSION` contains the commit that was cloned when the container was built.
|
||||||
|
|
||||||
|
<details> <summary>Configuration Example</summary>
|
||||||
|
|
||||||
|
* `couchdb.host`: Hostname/IP address and port of a CouchDB server.
|
||||||
|
* `couchdb.authorization`: Username & password used to access the CouchDB server, in HTTP Basic authentication format, e.g. `username:password`.
|
||||||
|
* `blog.postsDirectory`: Directory that will be parsed when calling /blogposts. If running in Docker this directory will need to be mounted to the container.
|
||||||
|
* `blog.postsDirUrl`: Location of the posts directory on the web server.
|
||||||
|
* `nowplaying.*.target`: Set to the Last.fm/Jellyfin username to query for playback information.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"startup": {
|
||||||
|
"apiPort": 8081,
|
||||||
|
"routesDir": "./routes"
|
||||||
|
},
|
||||||
|
|
||||||
|
"couchdb": {
|
||||||
|
"host": "hazeldale:5984",
|
||||||
|
"authorization": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"mailjet": {
|
||||||
|
"apiKey": "",
|
||||||
|
"senderAddress": "apinotifications@enstrayed.com",
|
||||||
|
"senderName": "API Notifications",
|
||||||
|
|
||||||
|
"authKeysDoc": "mailjet"
|
||||||
|
},
|
||||||
|
|
||||||
|
"blog": {
|
||||||
|
"postsDirectory": "C:/Users/natha/Downloads/proto/posts",
|
||||||
|
"postsDirUrl": "/posts"
|
||||||
|
},
|
||||||
|
|
||||||
|
"nowplaying": {
|
||||||
|
"lastfm": {
|
||||||
|
"apiKey": "",
|
||||||
|
"target": "enstrayed"
|
||||||
|
},
|
||||||
|
"jellyfin": {
|
||||||
|
"apiKey": "",
|
||||||
|
"host": "",
|
||||||
|
"target": ""
|
||||||
|
},
|
||||||
|
"cider": {
|
||||||
|
"apiKeys": [],
|
||||||
|
"hosts": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
In production, this application is designed to be run in Docker, and the container built by pulling the latest commit from the main branch. As such, deploying this application is just a matter of creating a directory and copying the Dockerfile:
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Please review the Configuration section of this document for important information. By default, the `config.json` file is expected to be mounted into the container at `/app/config.json`.
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM node:20
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN git clone https://github.com/enstrayed/enstrayedapi .
|
||||||
|
RUN git config --global --add safe.directory /app
|
||||||
|
RUN git show --oneline -s >> GITVERSION
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
ENTRYPOINT [ "node", "index.js" ]
|
||||||
|
```
|
||||||
|
|
||||||
|
<details> <summary>Docker Compose File</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
services:
|
||||||
|
enstrayedapi:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
image: enstrayedapi
|
||||||
|
container_name: enstrayedapi
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./config.json:/app/config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## License
|
||||||
|
If for whatever reason you want to, you are free to adapt this code for your own projects or as reference. However, this software is provided as-is with no warranty or agreement to support it.
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
"startup": {
|
|
||||||
"apiPort": 8081,
|
|
||||||
"routesDir": "./routes"
|
|
||||||
},
|
|
||||||
|
|
||||||
"couchdb": {
|
|
||||||
"host": "hazeldale:5984",
|
|
||||||
"authorization": ""
|
|
||||||
},
|
|
||||||
|
|
||||||
"cider": {
|
|
||||||
"targetHosts": ["localhost:10769"],
|
|
||||||
|
|
||||||
"authKeysDoc": "cider"
|
|
||||||
},
|
|
||||||
|
|
||||||
"mailjet": {
|
|
||||||
"apiKey": "",
|
|
||||||
"senderAddress": "apinotifications@enstrayed.com",
|
|
||||||
"senderName": "API Notifications",
|
|
||||||
|
|
||||||
"authKeysDoc": "mailjet"
|
|
||||||
},
|
|
||||||
|
|
||||||
"etyd": {
|
|
||||||
"randomHexLength": 6,
|
|
||||||
"authKeyInDb": "apiAuthKeys.etyd"
|
|
||||||
},
|
|
||||||
|
|
||||||
"blog": {
|
|
||||||
"postsDirectory": "C:/Users/natha/Downloads/proto/posts",
|
|
||||||
"postsDirUrl": "/posts"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
const { app, db, globalConfig } = require("../index.js") // Get globals from index
|
|
||||||
|
|
||||||
var timeSinceLastCiderQuery = Date.now()-2000;
|
|
||||||
var currentListening = {}
|
|
||||||
var currentListeningHtml = ""
|
|
||||||
|
|
||||||
app.get("/cider", (rreq,rres) => {
|
|
||||||
|
|
||||||
rres.send("<span>Cider endpoint is temporarily unavailable.</span>")
|
|
||||||
})
|
|
||||||
|
|
||||||
// app.get("/cider", (rreq,rres) => { // GET current listening from target
|
|
||||||
|
|
||||||
// if (Date.now() < timeSinceLastCiderQuery+2000) {
|
|
||||||
// rres.send(currentListening); // If it has been <2 seconds since the last request, return the cached result.
|
|
||||||
// } else {
|
|
||||||
// getCurrentListening(globalConfig.cider.targetHosts[0],"json").then(funcRes => {
|
|
||||||
// if (funcRes == 1) {
|
|
||||||
// rres.sendStatus(503) // If there was a problem getting the upstream JSON, return 503 Service Unavailable.
|
|
||||||
// } else {
|
|
||||||
// // Required (I think?) because of CORS.
|
|
||||||
// currentListening = funcRes
|
|
||||||
// rres.send(funcRes)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// })
|
|
||||||
|
|
||||||
// app.post("/cider", (rreq,rres) => { // POST stop listening on cider target
|
|
||||||
|
|
||||||
// fetch(`http://${globalConfig.couchdb.host}/apiauthkeys/${globalConfig.cider.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.mailjet.authKeysDoc}`)
|
|
||||||
// rres.sendStatus(500) // Refuse request
|
|
||||||
// } else {
|
|
||||||
// if (dbRes["content"][rreq.get("Authorization").split("_")[0]] === rreq.get("Authorization").split("_")[1]) {
|
|
||||||
|
|
||||||
// fetch(`http://${globalConfig.cider.targetHosts[0]}/stop`).then(fres => { // send GET /stop to cider target
|
|
||||||
// if (fres.status == 204) {
|
|
||||||
// console.log(`${rreq.get("cf-connecting-ip")} POST /cider returned 200 KEY:${rreq.get("Authorization")}`)
|
|
||||||
// rres.sendStatus(200) // if that works then 200
|
|
||||||
// } else {
|
|
||||||
// rres.sendStatus(500) // otherwise lol
|
|
||||||
// }
|
|
||||||
// }).catch(ferror => {
|
|
||||||
// rres.sendStatus(503) // and if a problem happens its probably cause cider target is unavailable
|
|
||||||
// })
|
|
||||||
|
|
||||||
// } else {
|
|
||||||
// console.log(`${rreq.get("cf-connecting-ip")} POST /cider returned 401`) // log ip of unauthorized requests
|
|
||||||
// rres.sendStatus(401) // received auth key was not in database
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
// })
|
|
||||||
|
|
||||||
// 2024-04-10: Retrieves currentPlayingSong JSON from specified Cider host and
|
|
||||||
// returns JSON/HTML containing the useful bits if successful, returning 1 if not.
|
|
||||||
|
|
||||||
// async function getCurrentListening(host,contentType) { // Host should be hostname/ip & port only.
|
|
||||||
// timeSinceLastCiderQuery = Date.now(); // Save last time function was run, used to indicate when the cache needs refreshed.
|
|
||||||
// return await fetch(`http://${host}/currentPlayingSong`).then(fetchRes => {
|
|
||||||
// if (fetchRes.status == 502) {
|
|
||||||
// return 1 // If the upstream server returns 502 (Bad Gateway) then internally return 1, indicating error.
|
|
||||||
// } else {
|
|
||||||
// return fetchRes.json().then(jsonRes => {
|
|
||||||
// if (jsonRes.info.name == undefined) {
|
|
||||||
// return 1 // If Cider is running but not playing a song this check prevents an undefined variable error.
|
|
||||||
// } else {
|
|
||||||
// if (contentType === "json") {
|
|
||||||
// return {
|
|
||||||
// "songName": jsonRes.info.name,
|
|
||||||
// "artistName": jsonRes.info.artistName,
|
|
||||||
// "albumName": jsonRes.info.albumName,
|
|
||||||
// "songLinkUrl": jsonRes.info.url.songLink,
|
|
||||||
// "endtimeEpochInMs": jsonRes.info.endTime,
|
|
||||||
// "artworkUrl": jsonRes.info.artwork.url.replace("{w}", jsonRes.info.artwork.width).replace("{h}", jsonRes.info.artwork.height)
|
|
||||||
// }
|
|
||||||
// } else if (contentType === "html") {
|
|
||||||
// return `<img src="${jsonRes.info.artwork.url.replace("{w}", jsonRes.info.artwork.width).replace("{h}", jsonRes.info.artwork.height)}" alt="Album Art" id="nowplaying-albumart" style="width: 10em;"> <div class="textlist"><p>I'm listening to</p><h3>${`${jsonRes.info.name} by ${jsonRes.info.artistName}`}</h3><p>from ${jsonRes.info.albumName}</p><a href="${jsonRes.info.url.songLink}" class="noindent">song.link</a></div>`
|
|
||||||
// } else {
|
|
||||||
// return 1
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }).catch(fetchError => {
|
|
||||||
// console.error("Error fetch()ing upstream Cider host: "+fetchError)
|
|
||||||
// return 1 // If something else happens then log it and return 1, indicating error.
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
module.exports = {app} // export routes to be imported by index for execution
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
services:
|
|
||||||
enstrayedapi:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
image: enstrayedapi
|
|
||||||
container_name: enstrayedapi
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- ./config.json:/app/config.json
|
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
const { globalConfig } = require("../index.js")
|
const { globalConfig } = require("../index.js")
|
||||||
|
|
||||||
async function checkAuthorization(documentToUse,keyToCheck) {
|
async function checkAuthorization(documentToUse,keyToCheck) {
|
||||||
return await fetch(`http://${globalConfig.couchdb.host}/apiauthkeys/${documentToUse}`, {
|
return await fetch(`${globalConfig.couchdbHost}/apiauthkeys/${documentToUse}`).then(fetchRes => {
|
||||||
headers: {
|
|
||||||
"Authorization": `Basic ${btoa(globalConfig.couchdb.authorization)}`
|
|
||||||
}
|
|
||||||
}).then(fetchRes => {
|
|
||||||
|
|
||||||
if (fetchRes.status === 404) { // If document doesnt exist fail gracefully
|
if (fetchRes.status === 404) { // If document doesnt exist fail gracefully
|
||||||
|
|
||||||
|
|||||||
26
liberals/nowplaying.js
Normal file
26
liberals/nowplaying.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
const { globalConfig } = require("../index.js")
|
||||||
|
|
||||||
|
async function queryLastfm() {
|
||||||
|
return await fetch(`https://ws.audioscrobbler.com/2.0/?format=json&method=user.getrecenttracks&limit=1&api_key=${globalConfig.nowplaying.lastfm.apiKey}&user=${globalConfig.nowplaying.lastfm.target}`).then(response => response.json()).then(response => {
|
||||||
|
if (response["recenttracks"] == undefined) {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
if (response.recenttracks.track[0]["@attr"] == undefined) {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
"json": {
|
||||||
|
"songName": response.recenttracks.track[0].name,
|
||||||
|
"artistName": response.recenttracks.track[0].artist["#text"],
|
||||||
|
"albumName": response.recenttracks.track[0].album["#text"],
|
||||||
|
"artUrl": response.recenttracks.track[0].image[3]["#text"],
|
||||||
|
"link": response.recenttracks.track[0].url
|
||||||
|
},
|
||||||
|
"html": `<img src="${response.recenttracks.track[0].image[3]["#text"]}" alt="Album Art" style="width: 10em;"> <div class="textlist"> <p>I'm listening to</p> <h3>${response.recenttracks.track[0].name} by ${response.recenttracks.track[0].artist["#text"]}</h3> <p>from ${response.recenttracks.track[0].album["#text"]}</p> <a href="${response.recenttracks.track[0].url}" class="noindent">View on Last.fm</a></div>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { queryLastfm }
|
||||||
56
package-lock.json
generated
56
package-lock.json
generated
@@ -9,22 +9,43 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2"
|
||||||
"typescript": "^5.4.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/bun": "^1.0.12",
|
||||||
"@types/node": "^20.12.3"
|
"@types/node": "^20.12.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/bun": {
|
||||||
"version": "20.12.3",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.1.5.tgz",
|
||||||
"integrity": "sha512-sD+ia2ubTeWrOu+YMF+MTAB7E+O7qsMqAbMfW7DG3K1URwhZ5hN1pLlRVGbf4wDFzSfikL05M17EyorS86jShw==",
|
"integrity": "sha512-7RprVDMF+1o+EWSo7F1+iJpkfNz+Ikw9K//vwambcY+D1QHXfb9l7jWY1hSBfuFEkW9yFAhkMzP2uTi1pQXoqw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bun-types": "1.1.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "20.12.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.14.tgz",
|
||||||
|
"integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~5.26.4"
|
"undici-types": "~5.26.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/ws": {
|
||||||
|
"version": "8.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
|
||||||
|
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
"version": "1.3.8",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
@@ -65,6 +86,17 @@
|
|||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bun-types": {
|
||||||
|
"version": "1.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.1.14.tgz",
|
||||||
|
"integrity": "sha512-esfxOvECTkjEuUEHBOoOo590Qggf4b9cz5h29AOB2SKt3yZwG3LbAX4iIYwWZX7GnO7vaY5hIdcQygwN0xGdNw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "~20.12.8",
|
||||||
|
"@types/ws": "~8.5.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
@@ -679,18 +711,6 @@
|
|||||||
"node": ">= 0.6"
|
"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": {
|
"node_modules/undici-types": {
|
||||||
"version": "5.26.5",
|
"version": "5.26.5",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
|||||||
@@ -2,13 +2,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2"
|
"express": "^4.18.2"
|
||||||
},
|
},
|
||||||
"name": "api",
|
"name": "enstrayedapi",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "api.enstrayed.com",
|
"description": "EnstrayedAPI",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/enstrayed/enstrayedapi.git"
|
"url": "git+https://github.com/enstrayed/enstrayedapi.git"
|
||||||
@@ -18,7 +15,6 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/enstrayed/enstrayedapi/issues"
|
"url": "https://github.com/enstrayed/enstrayedapi/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://api.enstrayed.com",
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.0.12",
|
"@types/bun": "^1.0.12",
|
||||||
"@types/node": "^20.12.3"
|
"@types/node": "^20.12.3"
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ const { app, globalConfig } = require("../index.js") // Get globals from index
|
|||||||
const { checkToken } = require("../liberals/auth.js")
|
const { checkToken } = require("../liberals/auth.js")
|
||||||
|
|
||||||
app.get("/etyd*", (rreq,rres) => {
|
app.get("/etyd*", (rreq,rres) => {
|
||||||
fetch(`http://${globalConfig.couchdb.host}/etyd${rreq.path.replace("/etyd","")}`, {
|
fetch(`${globalConfig.couchdbHost}/etyd${rreq.path.replace("/etyd","")}`).then(dbRes => {
|
||||||
headers: {
|
|
||||||
"Authorization": `Basic ${btoa(globalConfig.couchdb.authorization)}`
|
|
||||||
}
|
|
||||||
}).then(dbRes => {
|
|
||||||
if (dbRes.status == 404) {
|
if (dbRes.status == 404) {
|
||||||
rres.sendStatus(404)
|
rres.sendStatus(404)
|
||||||
} else {
|
} else {
|
||||||
@@ -35,21 +31,16 @@ app.delete("/etyd*", (rreq,rres) => {
|
|||||||
rres.sendStatus(401)
|
rres.sendStatus(401)
|
||||||
} else if (authRes === true) { // Authorization successful
|
} else if (authRes === true) { // Authorization successful
|
||||||
|
|
||||||
fetch(`http://${globalConfig.couchdb.host}/etyd${rreq.path.replace("/etyd", "")}`, {
|
fetch(`${globalConfig.couchdbHost}/etyd${rreq.path.replace("/etyd", "")}`).then(dbRes => {
|
||||||
headers: {
|
|
||||||
"Authorization": `Basic ${btoa(globalConfig.couchdb.authorization)}`
|
|
||||||
}
|
|
||||||
}).then(dbRes => {
|
|
||||||
|
|
||||||
if (dbRes.status == 404) {
|
if (dbRes.status == 404) {
|
||||||
rres.sendStatus(404)
|
rres.sendStatus(404)
|
||||||
} else {
|
} else {
|
||||||
dbRes.json().then(dbRes => {
|
dbRes.json().then(dbRes => {
|
||||||
|
|
||||||
fetch(`http://${globalConfig.couchdb.host}/etyd${rreq.path.replace("/etyd", "")}`, {
|
fetch(`${globalConfig.couchdbHost}/etyd${rreq.path.replace("/etyd", "")}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `Basic ${btoa(globalConfig.couchdb.authorization)}`,
|
|
||||||
"If-Match": dbRes["_rev"] // Using the If-Match header is easiest for deleting entries in couchdb
|
"If-Match": dbRes["_rev"] // Using the If-Match header is easiest for deleting entries in couchdb
|
||||||
}
|
}
|
||||||
}).then(fetchRes => {
|
}).then(fetchRes => {
|
||||||
@@ -89,10 +80,7 @@ app.post("/etyd*", (rreq,rres) => {
|
|||||||
if (rreq.body["url"] == undefined) {
|
if (rreq.body["url"] == undefined) {
|
||||||
rres.sendStatus(400)
|
rres.sendStatus(400)
|
||||||
} else {
|
} else {
|
||||||
fetch(`http://${globalConfig.couchdb.host}/etyd${rreq.path.replace("/etyd", "")}`, {
|
fetch(`${globalConfig.couchdbHost}/etyd${rreq.path.replace("/etyd", "")}`, {
|
||||||
headers: {
|
|
||||||
"Authorization": `Basic ${btoa(globalConfig.couchdb.authorization)}`
|
|
||||||
},
|
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
"content": {
|
"content": {
|
||||||
|
|||||||
@@ -1,11 +1,33 @@
|
|||||||
const { app, globalConfig } = require("../index.js")
|
const { app, globalConfig } = require("../index.js")
|
||||||
|
const { queryLastfm } = require("../liberals/nowplaying.js")
|
||||||
|
|
||||||
|
var timeSinceLastLastfmQuery = Date.now()-5000
|
||||||
|
var cachedLastfmResult = {}
|
||||||
|
|
||||||
|
const notPlayingAnythingPlaceholder = {
|
||||||
|
"json": {
|
||||||
|
"playing": false
|
||||||
|
},
|
||||||
|
"html": `<span>I'm not currently listening to anything.</span>`
|
||||||
|
}
|
||||||
|
|
||||||
app.get("/nowplaying", (rreq,rres) => {
|
app.get("/nowplaying", (rreq,rres) => {
|
||||||
if (rreq.query.format === "html") {
|
|
||||||
rres.send("<span>The /nowplaying endpoint is currently under construction.</span>")
|
if (Date.now() < timeSinceLastLastfmQuery+5000) {
|
||||||
|
rres.send(cachedLastfmResult[rreq.query.format] ?? cachedLastfmResult.json)
|
||||||
} else {
|
} else {
|
||||||
rres.send({"message":"The /nowplaying endpoint is currently under construction."})
|
timeSinceLastLastfmQuery = Date.now()
|
||||||
|
queryLastfm().then(response => {
|
||||||
|
if (response == 1) {
|
||||||
|
cachedLastfmResult = notPlayingAnythingPlaceholder
|
||||||
|
} else {
|
||||||
|
cachedLastfmResult = response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rres.send(cachedLastfmResult[rreq.query.format] ?? cachedLastfmResult.json)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = {app}
|
module.exports = {app}
|
||||||
Reference in New Issue
Block a user