new inner layout
- one main controlling script (cobalt.js). - added api server name to serverInfo endpoint. - one dockerfile. - less mess.
This commit is contained in:
parent
19bc17b1dd
commit
9edc4bd61b
14 changed files with 304 additions and 358 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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" ]
|
|
@ -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" ]
|
|
@ -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",
|
||||
|
|
219
src/cobalt.js
219
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`));
|
||||
}
|
||||
|
|
|
@ -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`)
|
||||
});
|
||||
}
|
197
src/core/both.js
Normal file
197
src/core/both.js
Normal file
|
@ -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`)
|
||||
})
|
||||
}
|
34
src/core/web.js
Normal file
34
src/core/web.js
Normal file
|
@ -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`)
|
||||
})
|
||||
}
|
|
@ -664,10 +664,6 @@ button:active,
|
|||
#pd-share {
|
||||
display: none;
|
||||
}
|
||||
#hop-attribution {
|
||||
display: block;
|
||||
text-align: right;
|
||||
}
|
||||
#about-donate-footer::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
|
|
@ -19,8 +19,6 @@ const exceptions = { // used for mobile devices
|
|||
"vQuality": "720"
|
||||
};
|
||||
|
||||
let apiURL = '';
|
||||
|
||||
let store = {};
|
||||
|
||||
function changeAPI(url) {
|
||||
|
|
|
@ -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\n<span class='text-backdrop'>tl;dr:</span>\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 <a class=\"text-backdrop italic\" href=\"https://hop.io/\" target=\"_blank\">hop.io</a>.\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 <a class=\"text-backdrop italic\" href=\"https://hop.io/\" target=\"_blank\">hop.io</a> 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. <span class='text-backdrop'>thank you</span>. 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\n<span class='text-backdrop'>tl;dr:</span>\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. <span class='text-backdrop'>thank you</span>. 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",
|
||||
|
|
|
@ -48,10 +48,10 @@ export default function(obj) {
|
|||
|
||||
<title>${appName}</title>
|
||||
|
||||
<meta property="og:url" content="${process.env.selfURL}" />
|
||||
<meta property="og:url" content="${process.env.webURL || process.env.selfURL}" />
|
||||
<meta property="og:title" content="${appName}" />
|
||||
<meta property="og:description" content="${t('EmbedBriefDescription')}" />
|
||||
<meta property="og:image" content="${process.env.selfURL}icons/generic.png" />
|
||||
<meta property="og:image" content="${process.env.webURL || process.env.selfURL}icons/generic.png" />
|
||||
<meta name="title" content="${appName}" />
|
||||
<meta name="description" content="${t('AboutSummary')}" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
|
@ -106,7 +106,6 @@ export default function(obj) {
|
|||
"title": t("CollapsePrivacy"),
|
||||
"body": t("PrivacyPolicy")
|
||||
}])
|
||||
+ `${process.env.DEPLOYMENT_ID && process.env.INTERNAL_IP ? '<a id="hop-attribution" class="explanation" href="https://hop.io/" target="_blank">powered by hop.io</a>' : ''}`
|
||||
}]
|
||||
})
|
||||
}, {
|
||||
|
@ -408,7 +407,8 @@ export default function(obj) {
|
|||
}])}
|
||||
</footer>
|
||||
</body>
|
||||
<script type="text/javascript">const loc = {
|
||||
<script type="text/javascript">
|
||||
const loc = {
|
||||
noInternet: ` + "`" + t('ErrorNoInternet') + "`" + `,
|
||||
noURLReturned: ` + "`" + t('ErrorNoUrlReturned') + "`" + `,
|
||||
unknownStatus: ` + "`" + t('ErrorUnknownStatus') + "`" + `,
|
||||
|
@ -417,7 +417,9 @@ export default function(obj) {
|
|||
pickerImages: ` + "`" + t('ImagePickerTitle') + "`" + `,
|
||||
pickerImagesExpl: ` + "`" + t(`ImagePickerExplanation${isMobile ? "Phone" : "PC"}`) + "`" + `,
|
||||
pickerDefaultExpl: ` + "`" + t(`MediaPickerExplanation${isMobile ? `Phone${isIOS ? "IOS" : ""}` : "PC"}`) + "`" + `,
|
||||
};</script>
|
||||
};
|
||||
let apiURL = '${process.env.apiURL ? process.env.apiURL.slice(0, -1) : ''}';
|
||||
</script>
|
||||
<script type="text/javascript" src="cobalt.js"></script>
|
||||
</html>`;
|
||||
} catch (err) {
|
||||
|
|
59
src/web.js
59
src/web.js
|
@ -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`));
|
||||
}
|
Loading…
Reference in a new issue