api: flatten code directories, better filenames, remove old files

This commit is contained in:
wukko
2024-08-03 14:47:13 +06:00
parent 5ce208f1a5
commit dd831e13e8
53 changed files with 83 additions and 249 deletions

View 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];
}
}

View 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
View 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() ])
}

View 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
}

View 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
}
}

View 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
View 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
View 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;
}