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:
wukko 2023-05-22 01:13:05 +06:00
parent 19bc17b1dd
commit 9edc4bd61b
14 changed files with 304 additions and 358 deletions

7
.gitignore vendored
View file

@ -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

View file

@ -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

View file

@ -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" ]

View file

@ -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" ]

View file

@ -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",

View file

@ -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";
if (process.env.selfURL && process.env.port) {
const commitHash = shortCommit();
const branch = getCurrentBranch();
const app = express();
app.disable('x-powered-by');
const corsConfig = process.env.cors === '0' ? { origin: process.env.selfURL, optionsSuccessStatus: 200 } : {};
await loadLoc(); // preload localization
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`));
}

View file

@ -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
View 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
View 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`)
})
}

View file

@ -664,10 +664,6 @@ button:active,
#pd-share {
display: none;
}
#hop-attribution {
display: block;
text-align: right;
}
#about-donate-footer::before {
content: "";
position: absolute;

View file

@ -19,8 +19,6 @@ const exceptions = { // used for mobile devices
"vQuality": "720"
};
let apiURL = '';
let store = {};
function changeAPI(url) {

View file

@ -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",

View file

@ -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) {

View file

@ -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`));
}