harden consumer
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -402,3 +402,4 @@ FodyWeavers.xsd
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
|
||||
dist/
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"name": "jackettio-addon",
|
||||
"version": "0.0.1",
|
||||
"exports": "./index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "node esbuild.js",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"name": "selfhostio-addon",
|
||||
"version": "0.0.1",
|
||||
"exports": "./index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "node esbuild.js",
|
||||
|
||||
1
src/node/consumer/.eslintignore
Normal file
1
src/node/consumer/.eslintignore
Normal file
@@ -0,0 +1 @@
|
||||
*.ts
|
||||
39
src/node/consumer/.eslintrc.cjs
Normal file
39
src/node/consumer/.eslintrc.cjs
Normal 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",
|
||||
},
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
CURRENT_IP="$(curl -s http://whatismyip.akamai.com)"
|
||||
clear
|
||||
echo "Current IP: $CURRENT_IP"
|
||||
14
src/node/consumer/ecosystem.config.cjs
Normal file
14
src/node/consumer/ecosystem.config.cjs
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: "consumer",
|
||||
script: "npm start",
|
||||
cwd: "/app",
|
||||
watch: ["./dist/index.cjs"],
|
||||
autorestart: true,
|
||||
env: {
|
||||
...process.env
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
51
src/node/consumer/esbuild.js
Normal file
51
src/node/consumer/esbuild.js
Normal 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);
|
||||
}
|
||||
21
src/node/consumer/jsconfig.json
Normal file
21
src/node/consumer/jsconfig.json
Normal 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"]
|
||||
}
|
||||
2008
src/node/consumer/package-lock.json
generated
2008
src/node/consumer/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
})();
|
||||
@@ -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 }
|
||||
@@ -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`;
|
||||
@@ -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}`)
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
@@ -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 });
|
||||
Reference in New Issue
Block a user