2022-07-08 19:17:56 +01:00
|
|
|
import NodeCache from "node-cache";
|
2023-04-29 16:30:59 +01:00
|
|
|
import { randomBytes } from "crypto";
|
2023-04-08 17:55:44 +01:00
|
|
|
import { nanoid } from 'nanoid';
|
2022-07-08 19:17:56 +01:00
|
|
|
|
2024-03-05 14:55:17 +00:00
|
|
|
import { decryptStream, encryptStream, generateHmac } from "../sub/crypto.js";
|
2022-07-08 19:17:56 +01:00
|
|
|
import { streamLifespan } from "../config.js";
|
|
|
|
|
2023-12-02 14:44:19 +00:00
|
|
|
const streamCache = new NodeCache({
|
|
|
|
stdTTL: streamLifespan/1000,
|
|
|
|
checkperiod: 10,
|
|
|
|
deleteOnExpire: true
|
|
|
|
})
|
2022-07-08 19:17:56 +01:00
|
|
|
|
2023-01-13 18:34:48 +00:00
|
|
|
streamCache.on("expired", (key) => {
|
|
|
|
streamCache.del(key);
|
2023-12-02 14:44:19 +00:00
|
|
|
})
|
|
|
|
|
2024-03-05 12:14:26 +00:00
|
|
|
const hmacSalt = randomBytes(64).toString('hex');
|
2023-01-13 18:34:48 +00:00
|
|
|
|
2022-07-08 19:17:56 +01:00
|
|
|
export function createStream(obj) {
|
2024-03-05 12:14:26 +00:00
|
|
|
const streamID = nanoid(),
|
2024-03-05 15:15:13 +00:00
|
|
|
iv = randomBytes(16).toString('base64url'),
|
2024-03-05 16:49:00 +00:00
|
|
|
secret = randomBytes(32).toString('base64url'),
|
2024-03-05 14:41:08 +00:00
|
|
|
exp = new Date().getTime() + streamLifespan,
|
2024-03-05 14:55:17 +00:00
|
|
|
hmac = generateHmac(`${streamID},${exp},${iv},${secret}`, hmacSalt),
|
2024-03-05 12:14:26 +00:00
|
|
|
streamData = {
|
2024-03-05 14:45:54 +00:00
|
|
|
exp: exp,
|
2023-01-13 18:34:48 +00:00
|
|
|
type: obj.type,
|
|
|
|
urls: obj.u,
|
2024-03-05 14:45:54 +00:00
|
|
|
service: obj.service,
|
2023-01-13 18:34:48 +00:00
|
|
|
filename: obj.filename,
|
|
|
|
audioFormat: obj.audioFormat,
|
2024-03-05 12:14:26 +00:00
|
|
|
isAudioOnly: !!obj.isAudioOnly,
|
2023-01-13 18:34:48 +00:00
|
|
|
copy: !!obj.copy,
|
|
|
|
mute: !!obj.mute,
|
2024-03-05 12:14:26 +00:00
|
|
|
metadata: obj.fileMetadata || false
|
|
|
|
};
|
|
|
|
|
|
|
|
streamCache.set(
|
|
|
|
streamID,
|
|
|
|
encryptStream(streamData, iv, secret)
|
|
|
|
)
|
|
|
|
|
|
|
|
let streamLink = new URL('/api/stream', process.env.apiURL);
|
|
|
|
|
|
|
|
const params = {
|
|
|
|
't': streamID,
|
|
|
|
'e': exp,
|
|
|
|
'h': hmac,
|
|
|
|
's': secret,
|
|
|
|
'i': iv
|
2023-01-13 18:34:48 +00:00
|
|
|
}
|
2024-03-05 12:14:26 +00:00
|
|
|
|
|
|
|
for (const [key, value] of Object.entries(params)) {
|
|
|
|
streamLink.searchParams.append(key, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return streamLink.toString();
|
2022-07-08 19:17:56 +01:00
|
|
|
}
|
|
|
|
|
2024-03-05 12:14:26 +00:00
|
|
|
export function verifyStream(id, hmac, exp, secret, iv) {
|
2022-07-08 19:17:56 +01:00
|
|
|
try {
|
2024-03-05 14:55:17 +00:00
|
|
|
const ghmac = generateHmac(`${id},${exp},${iv},${secret}`, hmacSalt);
|
2024-03-05 12:14:26 +00:00
|
|
|
|
|
|
|
if (ghmac !== String(hmac)) {
|
|
|
|
return {
|
|
|
|
error: "i couldn't verify if you have access to this stream. go back and try again!",
|
|
|
|
status: 401
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const streamInfo = JSON.parse(decryptStream(streamCache.get(id.toString()), iv, secret));
|
|
|
|
|
2023-12-02 14:44:19 +00:00
|
|
|
if (!streamInfo) return {
|
|
|
|
error: "this download link has expired or doesn't exist. go back and try again!",
|
|
|
|
status: 400
|
|
|
|
}
|
2023-06-27 14:56:15 +01:00
|
|
|
|
2024-03-05 14:41:08 +00:00
|
|
|
if (String(exp) === String(streamInfo.exp) && Number(exp) > new Date().getTime()) {
|
2023-06-27 14:56:15 +01:00
|
|
|
return streamInfo;
|
2023-02-09 14:45:17 +00:00
|
|
|
}
|
2023-12-02 14:44:19 +00:00
|
|
|
return {
|
|
|
|
error: "i couldn't verify if you have access to this stream. go back and try again!",
|
|
|
|
status: 401
|
|
|
|
}
|
2022-07-08 19:17:56 +01:00
|
|
|
} catch (e) {
|
2024-03-05 12:14:26 +00:00
|
|
|
return { status: 500, body: { status: "error", text: "couldn't verify this stream. request a new one!" } };
|
2022-07-08 19:17:56 +01:00
|
|
|
}
|
2022-08-01 16:48:37 +01:00
|
|
|
}
|