""refactor"" basically everything
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,4 @@
|
||||
node_modules/
|
||||
config.json
|
||||
bun.lockb
|
||||
GITVERSION
|
||||
todo.txt
|
||||
proto.js
|
||||
22
Caddyfile
22
Caddyfile
@@ -1,22 +0,0 @@
|
||||
:8082 {
|
||||
reverse_proxy localhost:8081
|
||||
}
|
||||
|
||||
:8083 {
|
||||
@staticpaths {
|
||||
path /
|
||||
path /_static*
|
||||
path /favicon.ico
|
||||
}
|
||||
|
||||
handle @staticpaths {
|
||||
respond /favicon.ico 204
|
||||
root ./etydFrontend
|
||||
file_server
|
||||
}
|
||||
|
||||
handle /* {
|
||||
rewrite * /etyd{uri}
|
||||
reverse_proxy localhost:8081
|
||||
}
|
||||
}
|
||||
28
README.md
28
README.md
@@ -8,37 +8,32 @@ This file contains documentation relevant for development and deployment, but no
|
||||
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.
|
||||
The configuration is downloaded from CouchDB on startup, however two environment variables must be set to specify the URL of the CouchDB server and the credentials for accessing it:
|
||||
| Variable | Required? | Purpose |
|
||||
|--------------|----------------------|-----------------------------------------------------------------------------------------------------|
|
||||
| `API_PORT` | No, defaults to 8081 | Sets the port the server will listen on |
|
||||
| `API_DBHOST` | Yes | Complete URL of the CouchDB instance, including port and protocol |
|
||||
| `API_DBCRED` | Yes | Credentials to access the CouchDB instance, in Basic Authentication format e.g. `username:password` |
|
||||
|
||||
<details> <summary>Configuration Example</summary>
|
||||
|
||||
* `couchdbHhost`: URL of CouchDB server.
|
||||
* `frontpage.directory`: Directory of frontpage, will be served at root with modifications.
|
||||
* `mailjet.apiKey`: Mailjet API Key.
|
||||
* `mailjet.senderAddress`: Email address that emails will be received from, must be verified in Mailjet admin panel.
|
||||
* `frontpage.frontpageDir`: Directory of frontpage, will be served at root with modifications.
|
||||
* `nowplaying.*.apiKey`: API key of respective service.
|
||||
* `nowplaying.*.target`: User that should be queried to retrieve playback information.
|
||||
|
||||
```json
|
||||
{
|
||||
"startup": {
|
||||
"apiPort": 8081,
|
||||
"routesDir": "./routes"
|
||||
"frontpage": {
|
||||
"directory": ""
|
||||
},
|
||||
|
||||
"couchdbHost": "",
|
||||
|
||||
"mailjet": {
|
||||
"apiKey": "",
|
||||
"senderAddress": ""
|
||||
},
|
||||
|
||||
"frontpage": {
|
||||
"frontpageDir": ""
|
||||
},
|
||||
|
||||
"nowplaying": {
|
||||
"lastfm": {
|
||||
"apiKey": "",
|
||||
@@ -67,14 +62,13 @@ In production, this application is designed to be run in Docker, and the contain
|
||||
> 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
|
||||
FROM node:22
|
||||
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
|
||||
|
||||
USER node
|
||||
ENTRYPOINT [ "node", "index.js" ]
|
||||
```
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const { globalConfig, app } = require("../index.js")
|
||||
const { logRequest } = require("../liberals/logging.js")
|
||||
import { globalConfig, app } from "../index.js"
|
||||
import { logRequest } from "../liberals/logging.js"
|
||||
|
||||
app.delete("/api/token", (rreq,rres) => {
|
||||
fetch(`${globalConfig.couchdbHost}/auth/sessions`).then(res => res.json()).then(fetchRes => {
|
||||
@@ -31,4 +31,4 @@ app.delete("/api/token", (rreq,rres) => {
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = {app}
|
||||
export default {app}
|
||||
@@ -1,6 +1,6 @@
|
||||
const { app, globalConfig } = require("../index.js") // Get globals from index
|
||||
const { checkToken } = require("../liberals/auth.js")
|
||||
const { logRequest } = require("../liberals/logging.js")
|
||||
import { app, globalConfig } from "../index.js" // Get globals from index
|
||||
import { checkToken } from "../liberals/auth.js"
|
||||
import { logRequest } from "../liberals/logging.js"
|
||||
|
||||
app.get("/api/etyd*", (rreq,rres) => {
|
||||
fetch(`${globalConfig.couchdbHost}/etyd${rreq.path.replace("/api/etyd","")}`).then(dbRes => {
|
||||
@@ -119,4 +119,4 @@ app.post("/api/etyd*", (rreq,rres) => {
|
||||
|
||||
})
|
||||
|
||||
module.exports = {app} // export routes to be imported by index for execution
|
||||
export {app} // export routes to be imported by index for execution
|
||||
@@ -1,6 +1,6 @@
|
||||
const { app, globalConfig } = require("../index.js") // Get globals from index
|
||||
const { checkToken } = require("../liberals/auth.js")
|
||||
const { logRequest } = require("../liberals/logging.js")
|
||||
import { app, globalConfig } from "../index.js" // Get globals from index
|
||||
import { checkToken } from "../liberals/auth.js"
|
||||
import { logRequest } from "../liberals/logging.js"
|
||||
|
||||
app.post("/api/sendemail", (rreq,rres) => {
|
||||
checkToken(rreq.get("Authorization"),"mailjet").then(authRes => {
|
||||
@@ -40,4 +40,4 @@ app.post("/api/sendemail", (rreq,rres) => {
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = {app}
|
||||
export {app}
|
||||
@@ -1,5 +1,5 @@
|
||||
const { app, globalConfig } = require("../index.js")
|
||||
const { queryLastfm } = require("../liberals/libnowplaying.js")
|
||||
import { app, globalConfig } from "../index.js"
|
||||
import { queryLastfm } from "../liberals/libnowplaying.js"
|
||||
|
||||
var timeSinceLastLastfmQuery = Date.now()-5000
|
||||
var cachedLastfmResult = {}
|
||||
@@ -30,4 +30,4 @@ app.get("/api/nowplaying", (rreq,rres) => {
|
||||
|
||||
})
|
||||
|
||||
module.exports = {app}
|
||||
export {app}
|
||||
43
index.js
43
index.js
@@ -1,22 +1,28 @@
|
||||
const fs = require('fs'); // Filesystem Access
|
||||
const express = require('express');
|
||||
const app = express(); // Init Express
|
||||
import * as fs from 'fs'
|
||||
import { execSync } from 'child_process'
|
||||
import express, { json } from 'express'
|
||||
const app = express()
|
||||
|
||||
function criticalFileLoader(file) {
|
||||
try {
|
||||
return fs.readFileSync(file, 'utf-8')
|
||||
} catch {
|
||||
console.error(`FATAL: Failed to load ${file}`)
|
||||
if (!process.env.API_DBHOST || !process.env.API_DBCRED) {
|
||||
console.log("FATAL: API_DBHOST and API_DBCRED must be set")
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
const globalConfig = JSON.parse(criticalFileLoader('config.json'))
|
||||
const globalVersion = criticalFileLoader('GITVERSION').split(" ")[0]
|
||||
const globalConfig = await fetch(`${process.env.API_DBHOST}/config/${process.env.API_DBCRED.split(":")[0]}`,{
|
||||
headers: { "Authorization": `Basic ${btoa(process.env.API_DBCRED)}`}
|
||||
}).then(response => {
|
||||
if (response.status !== 200) {
|
||||
console.log(`FATAL: Failed to download configuration: ${response.status} ${response.statusText}`)
|
||||
process.exit(1)
|
||||
} else {
|
||||
return response.json()
|
||||
}
|
||||
})
|
||||
const globalVersion = execSync(`git show --oneline -s`).toString().split(" ")[0]
|
||||
|
||||
module.exports = { app, globalConfig, fs, globalVersion } // Export express app and fs objects and globalconfig
|
||||
export { app, fs, globalConfig, globalVersion}
|
||||
|
||||
app.use(express.json()) // Allows receiving JSON bodies
|
||||
app.use(json()) // Allows receiving JSON bodies
|
||||
// see important note: https://expressjs.com/en/api.html#express.json
|
||||
|
||||
process.on('SIGTERM', function() {
|
||||
@@ -24,20 +30,19 @@ process.on('SIGTERM', function() {
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
// Import Routes
|
||||
fs.readdir(globalConfig.startup.routesDir, (err, files) => {
|
||||
fs.readdir("./routes", (err, files) => {
|
||||
if (err) {
|
||||
console.log(`FATAL: Unable to read ${globalConfig.startup.routesDir}`)
|
||||
console.log(`FATAL: Unable to import routes: ${err}`)
|
||||
process.exit(1)
|
||||
} else {
|
||||
let importedRoutes = []
|
||||
files.forEach(file => {
|
||||
require(`${globalConfig.startup.routesDir}/${file}`)
|
||||
import(`./routes/${file}`)
|
||||
importedRoutes.push(file.slice(0,-3))
|
||||
})
|
||||
console.log(`Imported Routes: ${importedRoutes}`)
|
||||
}
|
||||
})
|
||||
|
||||
console.log(`Enstrayed API | Version: ${globalVersion} | Port: ${globalConfig.startup.apiPort}`)
|
||||
app.listen(globalConfig.startup.apiPort)
|
||||
console.log(`Enstrayed API | Version: ${globalVersion} | Port: ${process.env.API_PORT ?? 8081}`)
|
||||
app.listen(process.env.API_PORT ?? 8081)
|
||||
@@ -1,4 +1,4 @@
|
||||
const { globalConfig } = require("../index.js")
|
||||
import { globalConfig } from "../index.js"
|
||||
|
||||
/**
|
||||
* Checks if a token exists in the sessions file (authentication) and if it has the correct permissions (authorization)
|
||||
@@ -27,4 +27,4 @@ async function checkToken(token,scope) {
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {checkToken}
|
||||
export {checkToken}
|
||||
@@ -1,4 +1,4 @@
|
||||
const { globalConfig } = require("../index.js")
|
||||
import { globalConfig } from "../index.js"
|
||||
|
||||
/**
|
||||
* Queries LastFM for user set in config file and returns formatted result
|
||||
@@ -30,4 +30,17 @@ async function queryLastfm() {
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { queryLastfm }
|
||||
// async function queryJellyfin() {
|
||||
// return await fetch(`${globalConfig.nowplaying.jellyfin.host}/Sessions`, {
|
||||
// headers: {
|
||||
// "Authorization": `MediaBrowser Token=${globalConfig.nowplaying.jellyfin.apiKey}`
|
||||
// }
|
||||
// }).then(response => response.json()).then(response => {
|
||||
// for (x in response) {
|
||||
// if (response[x].UserName !== globalConfig.nowplaying.jellyfin.target) { break }
|
||||
// if (response[x].)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
export { queryLastfm }
|
||||
@@ -23,4 +23,4 @@ function logRequest(response,request,code,extra) {
|
||||
console.log(`${request.get("cf-connecting-ip") ?? request.ip}${actualAuth}${request.method} ${request.path} returned ${code}${actualExtra}`)
|
||||
}
|
||||
|
||||
module.exports = { logRequest }
|
||||
export { logRequest }
|
||||
@@ -15,6 +15,7 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/enstrayed/enstrayedapi/issues"
|
||||
},
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.0.12",
|
||||
"@types/node": "^20.12.3"
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
const { app, globalConfig, fs, globalVersion } = require("../index.js") // Get globals from index
|
||||
import { app, globalConfig, fs, globalVersion } from "../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/",""))
|
||||
rres.sendFile(globalConfig.frontpage.directory+"static/"+rreq.url.replace("/static/",""))
|
||||
})
|
||||
|
||||
app.get("/posts/*", (rreq,rres) => {
|
||||
rres.sendFile(globalConfig.frontpage.frontpageDir+"/posts/"+rreq.url.replace("/posts/",""))
|
||||
rres.sendFile(globalConfig.frontpage.directory+"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")
|
||||
let indexFile = fs.readFileSync(globalConfig.frontpage.directory+"index.html","utf-8")
|
||||
cachedResult = indexFile.replace("<!--SSR_BLOGPOSTS-->",parseFiles()).replace("<!--SSR_APIVERSION-->",`<sup>API Version ${globalVersion}</sup>`)
|
||||
rres.send(cachedResult)
|
||||
}
|
||||
})
|
||||
|
||||
function parseFiles() {
|
||||
let files = fs.readdirSync(globalConfig.frontpage.frontpageDir+"/posts/")
|
||||
let files = fs.readdirSync(globalConfig.frontpage.directory+"posts/")
|
||||
let result = ""
|
||||
|
||||
for (x in files) {
|
||||
for (let x in files) {
|
||||
if (files[x].endsWith(".html") === false) { break } // If file/dir is not .html then ignore
|
||||
|
||||
let date = files[x].split("-")[0]
|
||||
@@ -41,4 +41,4 @@ function parseFiles() {
|
||||
return result
|
||||
}
|
||||
|
||||
module.exports = {app}
|
||||
export {app}
|
||||
@@ -1,4 +1,4 @@
|
||||
const { app } = require("../index.js")
|
||||
import { app } from "../index.js"
|
||||
|
||||
app.get("/api/ip", (rreq,rres) => {
|
||||
let jsonResponse = {
|
||||
@@ -6,7 +6,6 @@ app.get("/api/ip", (rreq,rres) => {
|
||||
"Country": rreq.get("cf-ipcountry") || "not_cloudflare",
|
||||
"CfRay": rreq.get("cf-ray") || "not_cloudflare"
|
||||
}
|
||||
|
||||
rres.send(jsonResponse)
|
||||
})
|
||||
|
||||
@@ -14,4 +13,4 @@ app.get("/api/headers", (rreq,rres) => {
|
||||
rres.send(rreq.headers)
|
||||
})
|
||||
|
||||
module.exports = {app}
|
||||
export { app }
|
||||
Reference in New Issue
Block a user