diff --git a/.gitignore b/.gitignore index 8af52058..7d12aa29 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,8 @@ package-lock.json # secrets .env -# esbuild -min - # page build +min build # stuff i already made but delayed @@ -16,3 +14,6 @@ future # docker docker-compose.yml + +# vscode +.vscode diff --git a/docker/docker-compose.yml.example b/docker-compose.yml.example similarity index 54% rename from docker/docker-compose.yml.example rename to docker-compose.yml.example index 9284e1d3..cb50b9f7 100644 --- a/docker/docker-compose.yml.example +++ b/docker-compose.yml.example @@ -2,38 +2,35 @@ version: '3.5' services: cobalt-api: - build: - context: . - dockerfile: ./docker/dockerfile_api + build: . restart: unless-stopped container_name: cobalt-api ports: - 9000:9000/tcp environment: - apiPort=9000 - - apiURL='https://co.wuk.sh/' + - apiURL=https://co.wuk.sh/ + - apiName=eu-nl - cors=1 cobalt-web: - build: - context: . - dockerfile: ./docker/dockerfile_web + build: . restart: unless-stopped container_name: cobalt-web ports: - 9000:9000/tcp environment: + - webPort=9000 + - webURL=https://co.wukko.me/ - apiPort=9000 - - apiURL='https://co.wuk.sh/' + - apiURL=https://co.wuk.sh/ - cors=1 - cobalt-full: - build: - context: . - dockerfile: ./dockerfile + cobalt-both: + build: . restart: unless-stopped - container_name: cobalt-full + container_name: cobalt-both ports: - 9000:9000/tcp environment: - - apiPort=9000 - - apiURL='https://co.wuk.sh/' + - port=9000 + - selfURL=https://co.wukko.me/ - cors=1 diff --git a/docker/dockerfile_api b/docker/dockerfile_api deleted file mode 100644 index 60fdbec8..00000000 --- a/docker/dockerfile_api +++ /dev/null @@ -1,15 +0,0 @@ -FROM node:18-bullseye-slim -WORKDIR /app - -RUN apt-get update -RUN apt-get install -y git -RUN rm -rf /var/lib/apt/lists/* - -COPY package*.json ./ -RUN npm install - -RUN git clone -n https://github.com/wukko/cobalt.git --depth 1 && mv cobalt/.git ./ && rm -rf cobalt - -COPY . . -EXPOSE 9000 -CMD [ "node", "src/api" ] diff --git a/docker/dockerfile_web b/docker/dockerfile_web deleted file mode 100644 index e20460c3..00000000 --- a/docker/dockerfile_web +++ /dev/null @@ -1,15 +0,0 @@ -FROM node:18-bullseye-slim -WORKDIR /app - -RUN apt-get update -RUN apt-get install -y git -RUN rm -rf /var/lib/apt/lists/* - -COPY package*.json ./ -RUN npm install - -RUN git clone -n https://github.com/wukko/cobalt.git --depth 1 && mv cobalt/.git ./ && rm -rf cobalt - -COPY . . -EXPOSE 9000 -CMD [ "node", "src/web" ] diff --git a/package.json b/package.json index 2c0f7258..9db76a44 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,7 @@ "start": "node src/cobalt", "setup": "node src/modules/setup", "test": "node src/test/test", - "build": "node src/modules/buildStatic", - "api": "node src/api", - "web": "node src/web" + "build": "node src/modules/buildStatic" }, "repository": { "type": "git", diff --git a/src/cobalt.js b/src/cobalt.js index 066385a7..6f3dd795 100644 --- a/src/cobalt.js +++ b/src/cobalt.js @@ -1,213 +1,36 @@ import "dotenv/config"; import express from "express"; -import cors from "cors"; -import rateLimit from "express-rate-limit"; -import { randomBytes } from "crypto"; -const ipSalt = randomBytes(64).toString('hex'); +import { Bright, Green, Red } from "./modules/sub/consoleText.js"; +import { getCurrentBranch, shortCommit } from "./modules/sub/currentCommit.js"; +import { loadLoc } from "./localization/manager.js"; import path from 'path'; import { fileURLToPath } from 'url'; + +import { runWeb } from "./core/web.js"; +import { runAPI } from "./core/api.js"; +import { runBoth } from "./core/both.js"; + +const app = express(); + +const gitCommit = shortCommit(); +const gitBranch = getCurrentBranch(); + const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename).slice(0, -4); // go up another level (get rid of src/) -import { getCurrentBranch, shortCommit } from "./modules/sub/currentCommit.js"; -import { appName, genericUserAgent, version } from "./modules/config.js"; -import { getJSON } from "./modules/api.js"; -import { apiJSON, checkJSONPost, getIP, languageCode } from "./modules/sub/utils.js"; -import { Bright, Cyan, Green, Red } from "./modules/sub/consoleText.js"; -import stream from "./modules/stream/stream.js"; -import loc, { loadLoc } from "./localization/manager.js"; -import { buildFront } from "./modules/build.js"; -import { changelogHistory } from "./modules/pageRender/onDemand.js"; -import { sha256 } from "./modules/sub/crypto.js"; -import findRendered from "./modules/pageRender/findRendered.js"; -import { celebrationsEmoji } from "./modules/pageRender/elements.js"; +app.disable('x-powered-by'); -if (process.env.selfURL && process.env.port) { - const commitHash = shortCommit(); - const branch = getCurrentBranch(); - const app = express(); +await loadLoc(); // preload localization - app.disable('x-powered-by'); - - const corsConfig = process.env.cors === '0' ? { origin: process.env.selfURL, optionsSuccessStatus: 200 } : {}; - - const apiLimiter = rateLimit({ - windowMs: 60000, - max: 25, - standardHeaders: false, - legacyHeaders: false, - keyGenerator: (req, res) => sha256(getIP(req), ipSalt), - handler: (req, res, next, opt) => { - res.status(429).json({ "status": "error", "text": loc(languageCode(req), 'ErrorRateLimit') }); - return; - } - }); - const apiLimiterStream = rateLimit({ - windowMs: 60000, - max: 28, - standardHeaders: false, - legacyHeaders: false, - keyGenerator: (req, res) => sha256(getIP(req), ipSalt), - handler: (req, res, next, opt) => { - res.status(429).json({ "status": "error", "text": loc(languageCode(req), 'ErrorRateLimit') }); - return; - } - }); - - const startTime = new Date(); - const startTimestamp = Math.floor(startTime.getTime()); - - // preload localization files and build static pages - await loadLoc(); - await buildFront(commitHash, branch); - - app.use('/api/:type', cors(corsConfig)); - app.use('/api/json', apiLimiter); - app.use('/api/stream', apiLimiterStream); - app.use('/api/onDemand', apiLimiter); - - app.use('/', express.static('./build/min')); - app.use('/', express.static('./src/front')); - - app.use((req, res, next) => { - try { decodeURIComponent(req.path) } catch (e) { return res.redirect('/') } - next(); - }); - app.use((req, res, next) => { - if (req.header("user-agent") && req.header("user-agent").includes("Trident")) res.destroy(); - next(); - }); - app.use('/api/json', express.json({ - verify: (req, res, buf) => { - try { - JSON.parse(buf); - if (buf.length > 720) throw new Error(); - if (String(req.header('Content-Type')) !== "application/json") { - res.status(400).json({ 'status': 'error', 'text': 'invalid content type header' }); - return; - } - if (String(req.header('Accept')) !== "application/json") { - res.status(400).json({ 'status': 'error', 'text': 'invalid accept header' }); - return; - } - } catch(e) { - res.status(400).json({ 'status': 'error', 'text': 'invalid json body.' }); - return; - } - } - })); - - app.post('/api/json', async (req, res) => { - try { - let ip = sha256(getIP(req), ipSalt); - let lang = languageCode(req); - let j = apiJSON(0, { t: "Bad request" }); - try { - let request = req.body; - if (request.url) { - request.dubLang = request.dubLang ? lang : false; - let chck = checkJSONPost(request); - if (chck) chck["ip"] = ip; - j = chck ? await getJSON(chck["url"], lang, chck) : apiJSON(0, { t: loc(lang, 'ErrorCouldntFetch') }); - } else { - j = apiJSON(0, { t: loc(lang, 'ErrorNoLink') }); - } - } catch (e) { - j = apiJSON(0, { t: loc(lang, 'ErrorCantProcess') }); - } - res.status(j.status).json(j.body); - return; - } catch (e) { - res.destroy(); - return - } - }); - - app.get('/api/:type', (req, res) => { - try { - let ip = sha256(getIP(req), ipSalt); - switch (req.params.type) { - case 'stream': - if (req.query.p) { - res.status(200).json({ "status": "continue" }); - return; - } else if (req.query.t && req.query.h && req.query.e) { - stream(res, ip, req.query.t, req.query.h, req.query.e); - } else { - let j = apiJSON(0, { t: "no stream id" }) - res.status(j.status).json(j.body); - return; - } - break; - case 'onDemand': - if (req.query.blockId) { - let blockId = req.query.blockId.slice(0, 3); - let r, j; - switch(blockId) { - case "0": // changelog history - r = changelogHistory(); - j = r ? apiJSON(3, { t: r }) : apiJSON(0, { t: "couldn't render this block" }) - break; - case "1": // celebrations emoji - r = celebrationsEmoji(); - j = r ? apiJSON(3, { t: r }) : false - break; - default: - j = apiJSON(0, { t: "couldn't find a block with this id" }) - break; - } - if (j.body) { - res.status(j.status).json(j.body) - } else { - res.status(204).end() - } - } else { - let j = apiJSON(0, { t: "no block id" }); - res.status(j.status).json(j.body) - } - break; - case 'serverInfo': - res.status(200).json({ - version: version, - commit: commitHash, - branch: branch, - url: process.env.apiURL, - cors: process.env.cors, - startTime: `${startTimestamp}` - }); - break; - default: - let j = apiJSON(0, { t: "unknown response type" }) - res.status(j.status).json(j.body); - break; - } - } catch (e) { - res.status(500).json({ 'status': 'error', 'text': loc(languageCode(req), 'ErrorCantProcess') }); - return; - } - }); - app.get("/api/status", (req, res) => { - res.status(200).end() - }); - app.get("/api", (req, res) => { - res.redirect('/api/json') - }); - app.get("/", (req, res) => { - res.sendFile(`${__dirname}/${findRendered(languageCode(req), req.header('user-agent') ? req.header('user-agent') : genericUserAgent)}`); - }); - app.get("/favicon.ico", (req, res) => { - res.redirect('/icons/favicon.ico'); - }); - app.get("/*", (req, res) => { - res.redirect('/') - }); - - app.listen(process.env.port, () => { - console.log(`\n${Cyan(appName)} ${Bright(`v.${version}-${commitHash} (${branch})`)}\nStart time: ${Bright(`${startTime.toUTCString()} (${Math.floor(startTimestamp)})`)}\n\nURL: ${Cyan(`${process.env.selfURL}`)}\nPort: ${process.env.port}\n`) - }) +if (process.env.apiURL && process.env.apiPort && !((process.env.webURL && process.env.webPort) || (process.env.selfURL && process.env.port))) { + await runAPI(express, app, gitCommit, gitBranch, __dirname); +} else if (process.env.webURL && process.env.webPort && !((process.env.apiURL && process.env.apiPort) || (process.env.selfURL && process.env.port))) { + await runWeb(express, app, gitCommit, gitBranch, __dirname); +} else if (process.env.selfURL && process.env.port && !((process.env.apiURL && process.env.apiPort) || (process.env.webURL && process.env.webPort))) { + await runBoth(express, app, gitCommit, gitBranch, __dirname) } else { console.log(Red(`cobalt hasn't been configured yet or configuration is invalid.\n`) + Bright(`please run the setup script to fix this: `) + Green(`npm run setup`)); } diff --git a/src/api.js b/src/core/api.js similarity index 78% rename from src/api.js rename to src/core/api.js index 74c33e1f..0c9bfdbb 100644 --- a/src/api.js +++ b/src/core/api.js @@ -1,30 +1,21 @@ -import "dotenv/config"; - -import express from "express"; import cors from "cors"; import rateLimit from "express-rate-limit"; import { randomBytes } from "crypto"; const ipSalt = randomBytes(64).toString('hex'); -import { getCurrentBranch, shortCommit } from "./modules/sub/currentCommit.js"; -import { appName, version } from "./modules/config.js"; -import { getJSON } from "./modules/api.js"; -import { apiJSON, checkJSONPost, getIP, languageCode } from "./modules/sub/utils.js"; -import { Bright, Cyan, Green, Red } from "./modules/sub/consoleText.js"; -import stream from "./modules/stream/stream.js"; -import loc, { loadLoc } from "./localization/manager.js"; -import { changelogHistory } from "./modules/pageRender/onDemand.js"; -import { sha256 } from "./modules/sub/crypto.js"; -import { celebrationsEmoji } from "./modules/pageRender/elements.js"; - -if (process.env.apiURL && process.env.apiPort) { - const commitHash = shortCommit(); - const branch = getCurrentBranch(); - const app = express(); - - app.disable('x-powered-by'); +import { appName, version } from "../modules/config.js"; +import { getJSON } from "../modules/api.js"; +import { apiJSON, checkJSONPost, getIP, languageCode } from "../modules/sub/utils.js"; +import { Bright, Cyan } from "../modules/sub/consoleText.js"; +import stream from "../modules/stream/stream.js"; +import loc from "../localization/manager.js"; +import { changelogHistory } from "../modules/pageRender/onDemand.js"; +import { sha256 } from "../modules/sub/crypto.js"; +import { celebrationsEmoji } from "../modules/pageRender/elements.js"; +import { verifyStream } from "../modules/stream/manage.js"; +export async function runAPI(express, app, gitCommit, gitBranch, __dirname) { const corsConfig = process.env.cors === '0' ? { origin: process.env.webURL, optionsSuccessStatus: 200 } : {}; const apiLimiter = rateLimit({ @@ -53,9 +44,6 @@ if (process.env.apiURL && process.env.apiPort) { const startTime = new Date(); const startTimestamp = Math.floor(startTime.getTime()); - // preload localization files - await loadLoc(); - app.use('/api/:type', cors(corsConfig)); app.use('/api/json', apiLimiter); app.use('/api/stream', apiLimiterStream); @@ -65,10 +53,6 @@ if (process.env.apiURL && process.env.apiPort) { try { decodeURIComponent(req.path) } catch (e) { return res.redirect('/') } next(); }); - app.use((req, res, next) => { - if (req.header("user-agent") && req.header("user-agent").includes("Trident")) res.destroy(); - next(); - }); app.use('/api/json', express.json({ verify: (req, res, buf) => { try { @@ -120,6 +104,12 @@ if (process.env.apiURL && process.env.apiPort) { let ip = sha256(getIP(req), ipSalt); switch (req.params.type) { case 'stream': + let streamInfo = verifyStream(ip, req.query.t, req.query.h, req.query.e); + if (streamInfo.error) { + res.status(streamInfo.status).json(apiJSON(0, { t: streamInfo.error }).body); + return; + } + if (req.query.p) { res.status(200).json({ "status": "continue" }); return; @@ -161,8 +151,9 @@ if (process.env.apiURL && process.env.apiPort) { case 'serverInfo': res.status(200).json({ version: version, - commit: commitHash, - branch: branch, + commit: gitCommit, + branch: gitBranch, + name: process.env.apiName ? process.env.apiName : "unknown", url: process.env.apiURL, cors: process.env.cors, startTime: `${startTimestamp}` @@ -178,19 +169,17 @@ if (process.env.apiURL && process.env.apiPort) { return; } }); - app.get("/api/status", (req, res) => { + app.get('/api/status', (req, res) => { res.status(200).end() }); - app.get("/favicon.ico", (req, res) => { - res.redirect('/icons/favicon.ico'); + app.get('/favicon.ico', (req, res) => { + res.sendFile(`${__dirname}/src/front/icons/favicon.ico`) }); - app.get("/*", (req, res) => { + app.get('/*', (req, res) => { res.redirect('/api/json') }); app.listen(process.env.apiPort, () => { - console.log(`\n${Cyan(appName)} API ${Bright(`v.${version}-${commitHash} (${branch})`)}\nStart time: ${Bright(`${startTime.toUTCString()} (${startTimestamp})`)}\n\nURL: ${Cyan(`${process.env.apiURL}`)}\nPort: ${process.env.apiPort}\n`) - }) -} else { - console.log(Red(`cobalt api hasn't been configured yet or configuration is invalid.\n`) + Bright(`please run the setup script to fix this: `) + Green(`npm run setup`)); + console.log(`\n${Cyan(appName)} API ${Bright(`v.${version}-${gitCommit} (${gitBranch})`)}\nStart time: ${Bright(`${startTime.toUTCString()} (${startTimestamp})`)}\n\nURL: ${Cyan(`${process.env.apiURL}`)}\nPort: ${process.env.apiPort}\n`) + }); } diff --git a/src/core/both.js b/src/core/both.js new file mode 100644 index 00000000..4b14592f --- /dev/null +++ b/src/core/both.js @@ -0,0 +1,197 @@ +import cors from "cors"; +import rateLimit from "express-rate-limit"; +import { randomBytes } from "crypto"; + +const ipSalt = randomBytes(64).toString('hex'); + +import { appName, genericUserAgent, version } from "../modules/config.js"; +import { getJSON } from "../modules/api.js"; +import { apiJSON, checkJSONPost, getIP, languageCode } from "../modules/sub/utils.js"; +import { Bright, Cyan, Green, Red } from "../modules/sub/consoleText.js"; +import stream from "../modules/stream/stream.js"; +import loc from "../localization/manager.js"; +import { buildFront } from "../modules/build.js"; +import { changelogHistory } from "../modules/pageRender/onDemand.js"; +import { sha256 } from "../modules/sub/crypto.js"; +import findRendered from "../modules/pageRender/findRendered.js"; +import { celebrationsEmoji } from "../modules/pageRender/elements.js"; + +export async function runBoth(express, app, gitCommit, gitBranch, __dirname) { + const corsConfig = process.env.cors === '0' ? { origin: process.env.selfURL, optionsSuccessStatus: 200 } : {}; + + const apiLimiter = rateLimit({ + windowMs: 60000, + max: 25, + standardHeaders: false, + legacyHeaders: false, + keyGenerator: (req, res) => sha256(getIP(req), ipSalt), + handler: (req, res, next, opt) => { + res.status(429).json({ "status": "error", "text": loc(languageCode(req), 'ErrorRateLimit') }); + return; + } + }); + const apiLimiterStream = rateLimit({ + windowMs: 60000, + max: 28, + standardHeaders: false, + legacyHeaders: false, + keyGenerator: (req, res) => sha256(getIP(req), ipSalt), + handler: (req, res, next, opt) => { + res.status(429).json({ "status": "error", "text": loc(languageCode(req), 'ErrorRateLimit') }); + return; + } + }); + + const startTime = new Date(); + const startTimestamp = Math.floor(startTime.getTime()); + + // preload localization files and build static pages + await buildFront(gitCommit, gitBranch); + + app.use('/api/:type', cors(corsConfig)); + app.use('/api/json', apiLimiter); + app.use('/api/stream', apiLimiterStream); + app.use('/api/onDemand', apiLimiter); + + app.use('/', express.static('./build/min')); + app.use('/', express.static('./src/front')); + + app.use((req, res, next) => { + try { decodeURIComponent(req.path) } catch (e) { return res.redirect('/') } + next(); + }); + app.use((req, res, next) => { + if (req.header("user-agent") && req.header("user-agent").includes("Trident")) res.destroy(); + next(); + }); + app.use('/api/json', express.json({ + verify: (req, res, buf) => { + try { + JSON.parse(buf); + if (buf.length > 720) throw new Error(); + if (String(req.header('Content-Type')) !== "application/json") { + res.status(400).json({ 'status': 'error', 'text': 'invalid content type header' }); + return; + } + if (String(req.header('Accept')) !== "application/json") { + res.status(400).json({ 'status': 'error', 'text': 'invalid accept header' }); + return; + } + } catch(e) { + res.status(400).json({ 'status': 'error', 'text': 'invalid json body.' }); + return; + } + } + })); + + app.post('/api/json', async (req, res) => { + try { + let ip = sha256(getIP(req), ipSalt); + let lang = languageCode(req); + let j = apiJSON(0, { t: "Bad request" }); + try { + let request = req.body; + if (request.url) { + request.dubLang = request.dubLang ? lang : false; + let chck = checkJSONPost(request); + if (chck) chck["ip"] = ip; + j = chck ? await getJSON(chck["url"], lang, chck) : apiJSON(0, { t: loc(lang, 'ErrorCouldntFetch') }); + } else { + j = apiJSON(0, { t: loc(lang, 'ErrorNoLink') }); + } + } catch (e) { + j = apiJSON(0, { t: loc(lang, 'ErrorCantProcess') }); + } + res.status(j.status).json(j.body); + return; + } catch (e) { + res.destroy(); + return + } + }); + + app.get('/api/:type', (req, res) => { + try { + let ip = sha256(getIP(req), ipSalt); + switch (req.params.type) { + case 'stream': + if (req.query.p) { + res.status(200).json({ "status": "continue" }); + return; + } else if (req.query.t && req.query.h && req.query.e) { + stream(res, ip, req.query.t, req.query.h, req.query.e); + } else { + let j = apiJSON(0, { t: "no stream id" }) + res.status(j.status).json(j.body); + return; + } + break; + case 'onDemand': + if (req.query.blockId) { + let blockId = req.query.blockId.slice(0, 3); + let r, j; + switch(blockId) { + case "0": // changelog history + r = changelogHistory(); + j = r ? apiJSON(3, { t: r }) : apiJSON(0, { t: "couldn't render this block" }) + break; + case "1": // celebrations emoji + r = celebrationsEmoji(); + j = r ? apiJSON(3, { t: r }) : false + break; + default: + j = apiJSON(0, { t: "couldn't find a block with this id" }) + break; + } + if (j.body) { + res.status(j.status).json(j.body) + } else { + res.status(204).end() + } + } else { + let j = apiJSON(0, { t: "no block id" }); + res.status(j.status).json(j.body) + } + break; + case 'serverInfo': + res.status(200).json({ + version: version, + commit: gitCommit, + branch: gitBranch, + name: process.env.apiName ? process.env.apiName : "unknown", + url: process.env.apiURL, + cors: process.env.cors, + startTime: `${startTimestamp}` + }); + break; + default: + let j = apiJSON(0, { t: "unknown response type" }) + res.status(j.status).json(j.body); + break; + } + } catch (e) { + res.status(500).json({ 'status': 'error', 'text': loc(languageCode(req), 'ErrorCantProcess') }); + return; + } + }); + app.get("/api/status", (req, res) => { + res.status(200).end() + }); + app.get("/api", (req, res) => { + res.redirect('/api/json') + }); + app.get("/", (req, res) => { + res.sendFile(`${__dirname}/${findRendered(languageCode(req), req.header('user-agent') ? req.header('user-agent') : genericUserAgent)}`); + }); + app.get("/favicon.ico", (req, res) => { + res.redirect('/icons/favicon.ico'); + }); + app.get("/*", (req, res) => { + res.redirect('/') + }); + + app.listen(process.env.port, () => { + console.log(`${Red("⚠️ This way of running cobalt has been deprecated and will be removed soon.\nCheck the docs and get ready: ")}${Green("WIP")}`) + console.log(`\n${Cyan(appName)} ${Bright(`v.${version}-${gitCommit} (${gitBranch})`)}\nStart time: ${Bright(`${startTime.toUTCString()} (${Math.floor(startTimestamp)})`)}\n\nURL: ${Cyan(`${process.env.selfURL}`)}\nPort: ${process.env.port}\n`) + }) +} diff --git a/src/core/web.js b/src/core/web.js new file mode 100644 index 00000000..95f1fd4a --- /dev/null +++ b/src/core/web.js @@ -0,0 +1,34 @@ +import { appName, genericUserAgent, version } from "../modules/config.js"; +import { languageCode } from "../modules/sub/utils.js"; +import { Bright, Cyan } from "../modules/sub/consoleText.js"; +import { buildFront } from "../modules/build.js"; +import findRendered from "../modules/pageRender/findRendered.js"; + +export async function runWeb(express, app, gitCommit, gitBranch, __dirname) { + await buildFront(gitCommit, gitBranch); + + app.use('/', express.static('./build/min')); + app.use('/', express.static('./src/front')); + + app.use((req, res, next) => { + try { decodeURIComponent(req.path) } catch (e) { return res.redirect('/') } + next(); + }); + app.get("/status", (req, res) => { + res.status(200).end() + }); + app.get("/", (req, res) => { + res.sendFile(`${__dirname}/${findRendered(languageCode(req), req.header('user-agent') ? req.header('user-agent') : genericUserAgent)}`) + }); + app.get("/favicon.ico", (req, res) => { + res.sendFile(`${__dirname}/src/front/icons/favicon.ico`) + }); + app.get("/*", (req, res) => { + res.redirect('/') + }); + + app.listen(process.env.webPort, () => { + let startTime = new Date(); + console.log(`\n${Cyan(appName)} WEB ${Bright(`v.${version}-${gitCommit} (${gitBranch})`)}\nStart time: ${Bright(`${startTime.toUTCString()} (${Math.floor(new Date().getTime())})`)}\n\nURL: ${Cyan(`${process.env.webURL}`)}\nPort: ${process.env.webPort}\n`) + }) +} diff --git a/src/front/cobalt.css b/src/front/cobalt.css index 338f0dc1..749ff065 100644 --- a/src/front/cobalt.css +++ b/src/front/cobalt.css @@ -664,10 +664,6 @@ button:active, #pd-share { display: none; } -#hop-attribution { - display: block; - text-align: right; -} #about-donate-footer::before { content: ""; position: absolute; diff --git a/src/front/cobalt.js b/src/front/cobalt.js index 59ec949a..f801a704 100644 --- a/src/front/cobalt.js +++ b/src/front/cobalt.js @@ -19,8 +19,6 @@ const exceptions = { // used for mobile devices "vQuality": "720" }; -let apiURL = ''; - let store = {}; function changeAPI(url) { diff --git a/src/modules/changelog/changelog.json b/src/modules/changelog/changelog.json index 0636d947..aa573457 100644 --- a/src/modules/changelog/changelog.json +++ b/src/modules/changelog/changelog.json @@ -1,9 +1,9 @@ { "current": { "version": "5.4", - "title": "instagram support, hop, docker, and more!", + "title": "instagram support, docker, and more!", "banner": "catphonestand.webp", - "content": "something many of you've been waiting for is finally here! try it out and let me know what you think :)\n\ntl;dr:\n*; added experimental instagram support! download any reels or videos you like, and make sure to report any issues you encounter. yes, you can convert either to audio.\n*; fixed support for on.soundcloud links.\n*; added share button to \"how to save?\" popup.\n*; added docker support.\n*; main instance is now powered by hop.io.\n\nservice improvements:\n*; added experimental support for videos from instagram. currently only reels and post videos are downloadable, but i'm looking into ways to save high resolution photos too. if you experience any issues, please report them on either of support platforms.\n*; fixed support for on.soundcloud share links. should work just as well as other versions!\n*; fixed an issue that made some youtube videos impossible to download.\n\ninterface improvements:\n*; new css-only checkmark! yes, i can't stop tinkering with it because slight flashing on svg load annoyed me. now it loads instantly (and also looks slightly better).\n*; fixed copy animation.\n*; minor localization improvements.\n*; fixed the embed logo that i broke somewhere in between 5.3 and 5.4.\n\ninternal improvements:\n*; now using nanoid for live render stream ids.\n*; added support for docker. it's kind of clumsy because of how i get .git folder inside the container, but if you know how to do it better, feel free to make a pr.\n*; cobalt now checks only for existence of environment variables, not exactly the .env file.\n*; changed the way user ip address is retrieved for instances using cloudflare.\n*; added ability to disable cors, both to setup script and environment variables.\n*; moved main instance to hop.io infra. there should no longer be random downtimes. huge shout out to the hop team for being so nice and helping me out :D\n\ni can't believe how diverse and widespread cobalt has become. it's used in all fields: music production, education, content creation, and even game development. thank you. this is absolutely nuts.\nif you don't mind sharing, please tell me about your use case. i'd really love to hear how you use cobalt and how i could make it even more useful for you." + "content": "something many of you've been waiting for is finally here! try it out and let me know what you think :)\n\ntl;dr:\n*; added experimental instagram support! download any reels or videos you like, and make sure to report any issues you encounter. yes, you can convert either to audio.\n*; fixed support for on.soundcloud links.\n*; added share button to \"how to save?\" popup.\n*; added docker support.\n\nservice improvements:\n*; added experimental support for videos from instagram. currently only reels and post videos are downloadable, but i'm looking into ways to save high resolution photos too. if you experience any issues, please report them on either of support platforms.\n*; fixed support for on.soundcloud share links. should work just as well as other versions!\n*; fixed an issue that made some youtube videos impossible to download.\n\ninterface improvements:\n*; new css-only checkmark! yes, i can't stop tinkering with it because slight flashing on svg load annoyed me. now it loads instantly (and also looks slightly better).\n*; fixed copy animation.\n*; minor localization improvements.\n*; fixed the embed logo that i broke somewhere in between 5.3 and 5.4.\n\ninternal improvements:\n*; now using nanoid for live render stream ids.\n*; added support for docker. it's kind of clumsy because of how i get .git folder inside the container, but if you know how to do it better, feel free to make a pr.\n*; cobalt now checks only for existence of environment variables, not exactly the .env file.\n*; changed the way user ip address is retrieved for instances using cloudflare.\n*; added ability to disable cors, both to setup script and environment variables.\n\ni can't believe how diverse and widespread cobalt has become. it's used in all fields: music production, education, content creation, and even game development. thank you. this is absolutely nuts.\nif you don't mind sharing, please tell me about your use case. i'd really love to hear how you use cobalt and how i could make it even more useful for you." }, "history": [{ "version": "5.3", diff --git a/src/modules/pageRender/page.js b/src/modules/pageRender/page.js index d506890a..f4046e80 100644 --- a/src/modules/pageRender/page.js +++ b/src/modules/pageRender/page.js @@ -48,10 +48,10 @@ export default function(obj) { ${appName} - + - + @@ -106,7 +106,6 @@ export default function(obj) { "title": t("CollapsePrivacy"), "body": t("PrivacyPolicy") }]) - + `${process.env.DEPLOYMENT_ID && process.env.INTERNAL_IP ? 'powered by hop.io' : ''}` }] }) }, { @@ -408,7 +407,8 @@ export default function(obj) { }])} - + }; + let apiURL = '${process.env.apiURL ? process.env.apiURL.slice(0, -1) : ''}'; + `; } catch (err) { diff --git a/src/web.js b/src/web.js deleted file mode 100644 index 4f298975..00000000 --- a/src/web.js +++ /dev/null @@ -1,59 +0,0 @@ -import "dotenv/config"; - -import express from "express"; - -import path from 'path'; -import { fileURLToPath } from 'url'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename).slice(0, -4); // go up another level (get rid of src/) - -import { getCurrentBranch, shortCommit } from "./modules/sub/currentCommit.js"; -import { appName, genericUserAgent, version } from "./modules/config.js"; -import { languageCode } from "./modules/sub/utils.js"; -import { Bright, Cyan, Green, Red } from "./modules/sub/consoleText.js"; -import { loadLoc } from "./localization/manager.js"; -import { buildFront } from "./modules/build.js"; -import findRendered from "./modules/pageRender/findRendered.js"; - -if (process.env.webURL && process.env.webPort) { - const commitHash = shortCommit(); - const branch = getCurrentBranch(); - const app = express(); - - app.disable('x-powered-by'); - - // preload localization files and build static pages - await loadLoc(); - await buildFront(commitHash, branch); - - app.use('/', express.static('./build/min')); - app.use('/', express.static('./src/front')); - - app.use((req, res, next) => { - try { decodeURIComponent(req.path) } catch (e) { return res.redirect('/') } - next(); - }); - app.use((req, res, next) => { - if (req.header("user-agent") && req.header("user-agent").includes("Trident")) res.destroy(); - next(); - }); - app.get("/status", (req, res) => { - res.status(200).end() - }); - app.get("/", (req, res) => { - res.sendFile(`${__dirname}/${findRendered(languageCode(req), req.header('user-agent') ? req.header('user-agent') : genericUserAgent)}`); - }); - app.get("/favicon.ico", (req, res) => { - res.redirect('/icons/favicon.ico'); - }); - app.get("/*", (req, res) => { - res.redirect('/') - }); - - app.listen(process.env.webPort, () => { - let startTime = new Date(); - console.log(`\n${Cyan(appName)} WEB ${Bright(`v.${version}-${commitHash} (${branch})`)}\nStart time: ${Bright(`${startTime.toUTCString()} (${Math.floor(new Date().getTime())})`)}\n\nURL: ${Cyan(`${process.env.webURL}`)}\nPort: ${process.env.webPort}\n`) - }) -} else { - console.log(Red(`cobalt web hasn't been configured yet or configuration is invalid.\n`) + Bright(`please run the setup script to fix this: `) + Green(`npm run setup`)); -}