api: flatten code directories, better filenames, remove old files
This commit is contained in:
23
api/src/misc/alias-envs.js
Normal file
23
api/src/misc/alias-envs.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Red } from "./console-text.js";
|
||||
|
||||
const mapping = {
|
||||
apiPort: 'API_PORT',
|
||||
apiURL: 'API_URL',
|
||||
apiName: 'API_NAME',
|
||||
cors: 'CORS_WILDCARD',
|
||||
cookiePath: 'COOKIE_PATH',
|
||||
webPort: 'WEB_PORT',
|
||||
webURL: 'WEB_URL',
|
||||
showSponsors: 'SHOW_SPONSORS',
|
||||
isBeta: 'IS_BETA'
|
||||
}
|
||||
|
||||
for (const [ oldEnv, newEnv ] of Object.entries(mapping)) {
|
||||
if (process.env[oldEnv] && !process.env[newEnv]) {
|
||||
process.env[newEnv] = process.env[oldEnv];
|
||||
console.error(`${Red('[!]')} ${oldEnv} is deprecated and will be removed in a future version.`);
|
||||
console.error(` You should use ${newEnv} instead.`);
|
||||
console.error();
|
||||
delete process.env[oldEnv];
|
||||
}
|
||||
}
|
||||
16
api/src/misc/console-text.js
Normal file
16
api/src/misc/console-text.js
Normal file
@@ -0,0 +1,16 @@
|
||||
function t(color, tt) {
|
||||
return color + tt + "\x1b[0m"
|
||||
}
|
||||
|
||||
export function Bright(tt) {
|
||||
return t("\x1b[1m", tt)
|
||||
}
|
||||
export function Red(tt) {
|
||||
return t("\x1b[31m", tt)
|
||||
}
|
||||
export function Green(tt) {
|
||||
return t("\x1b[32m", tt)
|
||||
}
|
||||
export function Cyan(tt) {
|
||||
return t("\x1b[36m", tt)
|
||||
}
|
||||
27
api/src/misc/crypto.js
Normal file
27
api/src/misc/crypto.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import { createHmac, createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
||||
|
||||
const algorithm = "aes256";
|
||||
|
||||
export function generateSalt() {
|
||||
return randomBytes(64).toString('hex');
|
||||
}
|
||||
|
||||
export function generateHmac(str, salt) {
|
||||
return createHmac("sha256", salt).update(str).digest("base64url");
|
||||
}
|
||||
|
||||
export function encryptStream(plaintext, iv, secret) {
|
||||
const buff = Buffer.from(JSON.stringify(plaintext));
|
||||
const key = Buffer.from(secret, "base64url");
|
||||
const cipher = createCipheriv(algorithm, key, Buffer.from(iv, "base64url"));
|
||||
|
||||
return Buffer.concat([ cipher.update(buff), cipher.final() ])
|
||||
}
|
||||
|
||||
export function decryptStream(ciphertext, iv, secret) {
|
||||
const buff = Buffer.from(ciphertext);
|
||||
const key = Buffer.from(secret, "base64url");
|
||||
const decipher = createDecipheriv(algorithm, key, Buffer.from(iv, "base64url"));
|
||||
|
||||
return Buffer.concat([ decipher.update(buff), decipher.final() ])
|
||||
}
|
||||
23
api/src/misc/current-commit.js
Normal file
23
api/src/misc/current-commit.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { execSync } from "child_process";
|
||||
|
||||
let commit, commitInfo, branch;
|
||||
|
||||
export function shortCommit() {
|
||||
if (commit) return commit;
|
||||
let c = execSync('git rev-parse --short HEAD').toString().trim();
|
||||
commit = c;
|
||||
return c
|
||||
}
|
||||
export function getCommitInfo() {
|
||||
if (commitInfo) return commitInfo;
|
||||
let d = execSync(`git show -s --format='%s;;;%B'`).toString().trim().replace(/[\r\n]/gm, '\n').split(';;;');
|
||||
d[1] = d[1].replace(d[0], '').trim().toString().replace(/[\r\n]/gm, '<br>');
|
||||
commitInfo = d;
|
||||
return d
|
||||
}
|
||||
export function getCurrentBranch() {
|
||||
if (branch) return branch;
|
||||
let b = execSync('git branch --show-current').toString().trim();
|
||||
branch = b;
|
||||
return b
|
||||
}
|
||||
16
api/src/misc/load-from-fs.js
Normal file
16
api/src/misc/load-from-fs.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import * as fs from "fs";
|
||||
|
||||
export function loadJSON(path) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(path, 'utf-8'))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
export function loadFile(path) {
|
||||
try {
|
||||
return fs.readFileSync(path, 'utf-8')
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
28
api/src/misc/randomize-ciphers.js
Normal file
28
api/src/misc/randomize-ciphers.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import tls from 'node:tls';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
|
||||
const ORIGINAL_CIPHERS = tls.DEFAULT_CIPHERS;
|
||||
|
||||
// How many ciphers from the top of the list to shuffle.
|
||||
// The remaining ciphers are left in the original order.
|
||||
const TOP_N_SHUFFLE = 8;
|
||||
|
||||
// Modified variation of https://stackoverflow.com/a/12646864
|
||||
const shuffleArray = (array) => {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = randomBytes(4).readUint32LE() % array.length;
|
||||
[array[i], array[j]] = [array[j], array[i]];
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
export const randomizeCiphers = () => {
|
||||
do {
|
||||
const cipherList = ORIGINAL_CIPHERS.split(':');
|
||||
const shuffled = shuffleArray(cipherList.slice(0, TOP_N_SHUFFLE));
|
||||
const retained = cipherList.slice(TOP_N_SHUFFLE);
|
||||
|
||||
tls.DEFAULT_CIPHERS = [ ...shuffled, ...retained ].join(':');
|
||||
} while (tls.DEFAULT_CIPHERS === ORIGINAL_CIPHERS);
|
||||
}
|
||||
42
api/src/misc/run-test.js
Normal file
42
api/src/misc/run-test.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import { normalizeRequest } from "../processing/request.js";
|
||||
import match from "../processing/match.js";
|
||||
import { extract } from "../processing/url.js";
|
||||
|
||||
export async function runTest(url, params, expect) {
|
||||
const normalized = normalizeRequest({ url, ...params });
|
||||
if (!normalized) {
|
||||
throw "invalid request";
|
||||
}
|
||||
|
||||
const parsed = extract(normalized.url);
|
||||
if (parsed === null) {
|
||||
throw `invalid url: ${normalized.url}`;
|
||||
}
|
||||
|
||||
const result = await match(
|
||||
parsed.host, parsed.patternMatch, "en", normalized
|
||||
);
|
||||
|
||||
let error = [];
|
||||
if (expect.status !== result.body.status) {
|
||||
const detail = `${expect.status} (expected) != ${result.body.status} (actual)`;
|
||||
error.push(`status mismatch: ${detail}`);
|
||||
}
|
||||
|
||||
if (expect.code !== result.status) {
|
||||
const detail = `${expect.code} (expected) != ${result.status} (actual)`;
|
||||
error.push(`status code mismatch: ${detail}`);
|
||||
}
|
||||
|
||||
if (error.length) {
|
||||
if (result.body.text) {
|
||||
error.push(`error message: ${result.body.text}`);
|
||||
}
|
||||
|
||||
throw error.join('\n');
|
||||
}
|
||||
|
||||
if (result.body.status === 'stream') {
|
||||
// TODO: stream testing
|
||||
}
|
||||
}
|
||||
67
api/src/misc/utils.js
Normal file
67
api/src/misc/utils.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const forbiddenCharsString = ['}', '{', '%', '>', '<', '^', ';', '`', '$', '"', "@", '='];
|
||||
|
||||
export function metadataManager(obj) {
|
||||
const keys = Object.keys(obj);
|
||||
const tags = [
|
||||
"album",
|
||||
"copyright",
|
||||
"title",
|
||||
"artist",
|
||||
"track",
|
||||
"date"
|
||||
]
|
||||
let commands = []
|
||||
|
||||
for (const i in keys) {
|
||||
if (tags.includes(keys[i]))
|
||||
commands.push('-metadata', `${keys[i]}=${obj[keys[i]]}`)
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
|
||||
export function cleanString(string) {
|
||||
for (const i in forbiddenCharsString) {
|
||||
string = string.replaceAll("/", "_")
|
||||
.replaceAll(forbiddenCharsString[i], '')
|
||||
}
|
||||
return string;
|
||||
}
|
||||
export function verifyLanguageCode(code) {
|
||||
const langCode = String(code.slice(0, 2).toLowerCase());
|
||||
if (RegExp(/[a-z]{2}/).test(code)) {
|
||||
return langCode
|
||||
}
|
||||
return "en"
|
||||
}
|
||||
export function languageCode(req) {
|
||||
if (req.header('Accept-Language')) {
|
||||
return verifyLanguageCode(req.header('Accept-Language'))
|
||||
}
|
||||
return "en"
|
||||
}
|
||||
export function cleanHTML(html) {
|
||||
let clean = html.replace(/ {4}/g, '');
|
||||
clean = clean.replace(/\n/g, '');
|
||||
return clean
|
||||
}
|
||||
|
||||
export function getRedirectingURL(url) {
|
||||
return fetch(url, { redirect: 'manual' }).then((r) => {
|
||||
if ([301, 302, 303].includes(r.status) && r.headers.has('location'))
|
||||
return r.headers.get('location');
|
||||
}).catch(() => null);
|
||||
}
|
||||
|
||||
export function merge(a, b) {
|
||||
for (const k of Object.keys(b)) {
|
||||
if (Array.isArray(b[k])) {
|
||||
a[k] = [...(a[k] ?? []), ...b[k]];
|
||||
} else if (typeof b[k] === 'object') {
|
||||
a[k] = merge(a[k], b[k]);
|
||||
} else {
|
||||
a[k] = b[k];
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
Reference in New Issue
Block a user