harden consumer

This commit is contained in:
iPromKnight
2024-02-02 14:04:53 +00:00
parent 68edaba308
commit 8ad6cf731c
29 changed files with 693 additions and 1567 deletions

1
.gitignore vendored
View File

@@ -402,3 +402,4 @@ FodyWeavers.xsd
# JetBrains Rider
*.sln.iml
dist/

View File

@@ -1,7 +1,6 @@
{
"name": "jackettio-addon",
"version": "0.0.1",
"exports": "./index.js",
"type": "module",
"scripts": {
"build": "node esbuild.js",

View File

@@ -1,7 +1,6 @@
{
"name": "selfhostio-addon",
"version": "0.0.1",
"exports": "./index.js",
"type": "module",
"scripts": {
"build": "node esbuild.js",

View File

@@ -0,0 +1 @@
*.ts

View File

@@ -0,0 +1,39 @@
/** @type {import("eslint").ESLint.Options} */
module.exports = {
env: {
es2024: true,
node: true,
},
globals: {
Atomics: "readonly",
SharedArrayBuffer: "readonly",
},
parserOptions: {
sourceType: "module",
},
plugins: ["import", "import-helpers"],
rules: {
"default-case": "off",
"import/no-duplicates": "off",
"import/no-extraneous-dependencies": ["off", { devDependencies: ["backend", "frontend", "mobile"] }],
"import/order": "off",
"import-helpers/order-imports": [
"warn",
{
alphabetize: {
order: "asc",
},
},
],
"lines-between-class-members": ["error", "always", { exceptAfterSingleLine: true }],
"no-continue": "off",
"no-param-reassign": "off",
"no-plusplus": ["error", { allowForLoopAfterthoughts: true }],
"no-restricted-syntax": "off",
"no-unused-expressions": ["off", { allowShortCircuit: true }],
"no-unused-vars": "off",
"no-use-before-define": "off",
"one-var": ["error", { uninitialized: "consecutive" }],
"prefer-destructuring": "off",
},
};

View File

@@ -1,16 +0,0 @@
env:
es2021: true
node: true
extends: eslint:recommended
plugins:
- import
rules:
import/no-unresolved: 2
import/no-commonjs: 2
import/extensions:
- 2
- ignorePackages
parserOptions:
ecmaVersion: latest
sourceType: module

View File

@@ -1,18 +1,29 @@
FROM node:lts-buster-slim
# RUN apk update && apk upgrade && \
# apk add --no-cache git curl
FROM node:lts-buster-slim as builder
RUN apt-get update && \
apt-get install -y curl git && \
apt-get clean && \
apt-get install -y git && \
rm -rf /var/lib/apt/lists/*
WORKDIR /home/node/app
WORKDIR /app
COPY package*.json ./
RUN npm ci --only-production
RUN npm install
COPY . .
RUN chmod a+x ./check-ip.sh
RUN npm run build
CMD [ "node", "--no-warnings=ExperimentalWarning", "index.js" ]
# --- Runtime Stage ---
FROM node:lts-buster-slim
# Install pm2
RUN npm install pm2 -g
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app ./
RUN npm prune --omit=dev
EXPOSE 7001
ENTRYPOINT [ "pm2-runtime", "start", "ecosystem.config.cjs"]

View File

@@ -1,5 +0,0 @@
#!/bin/sh
CURRENT_IP="$(curl -s http://whatismyip.akamai.com)"
clear
echo "Current IP: $CURRENT_IP"

View File

@@ -0,0 +1,14 @@
module.exports = {
apps: [
{
name: "consumer",
script: "npm start",
cwd: "/app",
watch: ["./dist/index.cjs"],
autorestart: true,
env: {
...process.env
},
},
],
};

View File

@@ -0,0 +1,51 @@
import { build } from "esbuild";
import { readFileSync, rmSync } from "fs";
const { devDependencies } = JSON.parse(readFileSync("./package.json", "utf8"));
const start = Date.now();
try {
const outdir = "dist";
rmSync(outdir, { recursive: true, force: true });
build({
bundle: true,
entryPoints: [
"./src/index.js",
],
external: [...(devDependencies && Object.keys(devDependencies))],
keepNames: true,
minify: true,
outbase: "./src",
outdir,
outExtension: {
".js": ".cjs",
},
platform: "node",
plugins: [
{
name: "populate-import-meta",
setup: ({ onLoad }) => {
onLoad({ filter: new RegExp(`${import.meta.dirname}/src/.*.(js|ts)$`) }, args => {
const contents = readFileSync(args.path, "utf8");
const transformedContents = contents
.replace(/import\.meta/g, `{dirname:__dirname,filename:__filename}`)
.replace(/import\.meta\.filename/g, "__filename")
.replace(/import\.meta\.dirname/g, "__dirname");
return { contents: transformedContents, loader: "default" };
});
},
}
],
}).then(() => {
// biome-ignore lint/style/useTemplate: <explanation>
console.log("⚡ " + "\x1b[32m" + `Done in ${Date.now() - start}ms`);
});
} catch (e) {
console.log(e);
process.exit(1);
}

View File

@@ -0,0 +1,21 @@
{
"compilerOptions": {
"baseUrl": "./src",
"checkJs": true,
"isolatedModules": true,
"lib": ["es6"],
"module": "ESNext",
"moduleResolution": "node",
"outDir": "./dist",
"pretty": true,
"removeComments": true,
"resolveJsonModule": true,
"rootDir": "./src",
"skipLibCheck": true,
"sourceMap": true,
"target": "ES6",
"types": ["node"],
"typeRoots": ["node_modules/@types", "src/@types"]
},
"exclude": ["node_modules"]
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,12 @@
{
"name": "consumer",
"version": "1.0.0",
"exports": "./index.js",
"version": "0.0.1",
"type": "module",
"scripts": {
"start": "node index.js",
"lint": "eslint . --ext .js"
"build": "node esbuild.js",
"dev": "tsx watch --ignore node_modules src/index.js",
"start": "node dist/index.cjs",
"lint": "eslint . --ext .ts,.js"
},
"author": "A Dude",
"license": "MIT",
@@ -23,12 +24,18 @@
"name-to-imdb": "^3.0.4",
"parse-torrent-title": "git://github.com/TheBeastLT/parse-torrent-title.git#022408972c2a040f846331a912a6a8487746a654",
"pg": "^8.11.3",
"pg-hstore": "^2.3.4",
"sequelize": "^6.31.1",
"torrent-stream": "^1.2.1",
"user-agents": "^1.0.1444"
},
"devDependencies": {
"@types/node": "^20.11.6",
"@types/stremio-addon-sdk": "^1.6.10",
"esbuild": "^0.19.12",
"eslint": "^8.56.0",
"eslint-plugin-import": "^2.29.1"
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-import-helpers": "^1.3.1",
"tsx": "^4.7.0"
}
}

View File

@@ -1,11 +1,13 @@
import { getTrackers } from "./lib/trackerService.js";
import { connect } from './lib/repository.js';
import { listenToQueue } from './jobs/processTorrents.js';
import { jobConfig } from "./lib/config.js";
import { connect } from './lib/repository.js';
import { getTrackers } from "./lib/trackerService.js";
await getTrackers();
await connect();
(async () => {
await getTrackers();
await connect();
if (jobConfig.JOBS_ENABLED) {
await listenToQueue();
}
if (jobConfig.JOBS_ENABLED) {
await listenToQueue();
}
})();

View File

@@ -1,7 +1,7 @@
import { rabbitConfig, jobConfig } from '../lib/config.js'
import { processTorrentRecord } from "../lib/ingestedTorrent.js";
import amqp from 'amqplib'
import amqp from 'amqplib'
import Promise from 'bluebird'
import { rabbitConfig, jobConfig } from '../lib/config.js'
import { processTorrentRecord } from "../lib/ingestedTorrent.js";
const assertQueueOptions = { durable: true }
const consumeQueueOptions = { noAck: false }

View File

@@ -1,6 +1,6 @@
import { cacheConfig } from './config.js';
import cacheManager from 'cache-manager';
import mangodbStore from 'cache-manager-mongodb';
import { cacheConfig } from './config.js';
const GLOBAL_KEY_PREFIX = 'selfhostio-consumer';
const IMDB_ID_PREFIX = `${GLOBAL_KEY_PREFIX}|imdb_id`;

View File

@@ -1,9 +1,9 @@
import { Type } from './types.js';
import { createTorrentEntry, checkAndUpdateTorrent } from './torrentEntries.js';
import {getTrackers} from "./trackerService.js";
import { Type } from './types.js';
export async function processTorrentRecord(torrent) {
const category = torrent.category;
const {category} = torrent;
const type = category === 'tv' ? Type.SERIES : Type.MOVIE;
const torrentInfo = await parseTorrent(torrent, type);
console.log(`Processing torrent ${torrentInfo.title} with infoHash ${torrentInfo.infoHash}`)

View File

@@ -1,6 +1,6 @@
import axios from 'axios';
import nameToImdb from 'name-to-imdb';
import { search } from 'google-sr';
import nameToImdb from 'name-to-imdb';
import { cacheWrapImdbId, cacheWrapKitsuId, cacheWrapMetadata } from './cache.js';
import { Type } from './types.js';

View File

@@ -1,7 +1,7 @@
import moment from 'moment';
import * as Promises from './promises.js';
import { Sequelize, Op, DataTypes, fn, col, literal } from 'sequelize';
import { databaseConfig } from './config.js';
import * as Promises from './promises.js';
const database = new Sequelize(
databaseConfig.DATABASE_URI,

View File

@@ -1,7 +1,7 @@
import torrentStream from 'torrent-stream';
import {isSubtitle, isVideo} from './extension.js';
import { torrentConfig } from './config.js';
import { decode } from 'magnet-uri';
import torrentStream from 'torrent-stream';
import { torrentConfig } from './config.js';
import {isSubtitle, isVideo} from './extension.js';
export async function torrentFiles(torrent, timeout) {
return filesFromTorrentStream(torrent, timeout)

View File

@@ -1,11 +1,11 @@
import { parse } from 'parse-torrent-title';
import { Type } from './types.js';
import { getImdbId, getKitsuId } from './metadata.js';
import { isPackTorrent } from './parseHelper.js';
import * as Promises from './promises.js';
import * as repository from './repository.js';
import { getImdbId, getKitsuId } from './metadata.js';
import { parseTorrentFiles } from './torrentFiles.js';
import { assignSubtitles } from './torrentSubtitles.js';
import { isPackTorrent } from './parseHelper.js';
import { Type } from './types.js';
export async function createTorrentEntry(torrent, overwrite = false) {
const titleInfo = parse(torrent.title);

View File

@@ -1,14 +1,14 @@
import moment from 'moment';
import Bottleneck from 'bottleneck';
import distance from 'jaro-winkler';
import moment from 'moment';
import { parse } from 'parse-torrent-title';
import * as Promises from './promises.js';
import { metadataConfig } from './config.js';
import { isDisk } from './extension.js';
import { getMetadata, getImdbId, getKitsuId } from './metadata.js';
import { parseSeriesVideos, isPackTorrent } from './parseHelper.js';
import { Type } from './types.js';
import { isDisk } from './extension.js';
import * as Promises from './promises.js';
import {torrentFiles} from "./torrent.js";
import { metadataConfig } from './config.js';
import { Type } from './types.js';
const MIN_SIZE = 5 * 1024 * 1024; // 5 MB
const imdb_limiter = new Bottleneck({ maxConcurrent: metadataConfig.IMDB_CONCURRENT, minTime: metadataConfig.IMDB_INTERVAL_MS });