From afead56a7482a4146a2e12d82e2e9eff1fa5bf12 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Wed, 15 Apr 2026 11:35:11 -0400 Subject: [PATCH] Fixed eslint errors --- services/api.service.ts | 45 ++++++-- services/autodownload.service.ts | 27 +++-- services/comicprocessor.service.ts | 158 ++++++++++++++++---------- services/graphql.service.ts | 18 ++- services/prowlarr.service.ts | 68 ++++++----- services/qbittorrent.service.ts | 174 +++++++++++++++++++---------- tsconfig.eslint.json | 14 +++ 7 files changed, 322 insertions(+), 182 deletions(-) create mode 100644 tsconfig.eslint.json diff --git a/services/api.service.ts b/services/api.service.ts index 0e84b58..b28c625 100644 --- a/services/api.service.ts +++ b/services/api.service.ts @@ -1,6 +1,28 @@ -import { Service, ServiceBroker } from "moleculer"; +import type { IncomingMessage, ServerResponse } from "http"; +import type { ServiceBroker } from "moleculer"; +import { Service } from "moleculer"; import ApiGateway from "moleculer-web"; +interface GraphQLRequest extends IncomingMessage { + body: { + query: string; + variables?: Record; + operationName?: string; + }; + $params: { + query?: string; + variables?: string; + operationName?: string; + }; + $ctx: { + broker: ServiceBroker; + }; +} + +interface GraphQLError { + message: string; +} + export default class ApiService extends Service { constructor(broker: ServiceBroker) { super(broker); @@ -55,7 +77,8 @@ export default class ApiService extends Service { maxAge: 3600, }, aliases: { - "POST /": async (req: any, res: any) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + "POST /": async (req: GraphQLRequest, res: ServerResponse) => { try { const { query, variables, operationName } = req.body; const result = await req.$ctx.broker.call("acquisition-graphql.query", { @@ -65,17 +88,17 @@ export default class ApiService extends Service { }); res.setHeader("Content-Type", "application/json"); res.end(JSON.stringify(result)); - } catch (error: any) { + } catch (error: unknown) { res.statusCode = 500; res.setHeader("Content-Type", "application/json"); - res.end(JSON.stringify({ errors: [{ message: error.message }] })); + res.end(JSON.stringify({ errors: [{ message: (error as GraphQLError).message }] })); } }, - "GET /": async (req: any, res: any) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + "GET /": async (req: GraphQLRequest, res: ServerResponse) => { try { - const query = req.$params.query; - const variables = req.$params.variables ? JSON.parse(req.$params.variables) : undefined; - const operationName = req.$params.operationName; + const { query, variables: variablesStr, operationName } = req.$params; + const variables = variablesStr ? JSON.parse(variablesStr) : undefined; const result = await req.$ctx.broker.call("acquisition-graphql.query", { query, variables, @@ -83,10 +106,10 @@ export default class ApiService extends Service { }); res.setHeader("Content-Type", "application/json"); res.end(JSON.stringify(result)); - } catch (error: any) { + } catch (error: unknown) { res.statusCode = 500; res.setHeader("Content-Type", "application/json"); - res.end(JSON.stringify({ errors: [{ message: error.message }] })); + res.end(JSON.stringify({ errors: [{ message: (error as GraphQLError).message }] })); } }, }, @@ -112,7 +135,7 @@ export default class ApiService extends Service { events: {}, methods: {}, - started(): any {}, + started(): void {}, }); } } diff --git a/services/autodownload.service.ts b/services/autodownload.service.ts index 44b2ecc..e312843 100644 --- a/services/autodownload.service.ts +++ b/services/autodownload.service.ts @@ -1,12 +1,17 @@ -"use strict"; +import type { Producer } from "kafkajs"; import { Kafka } from "kafkajs"; -import type { Context, ServiceBroker, ServiceSchema } from "moleculer"; +import type { Context, ServiceBroker } from "moleculer"; import { Errors, Service } from "moleculer"; +interface Issue { + id: string; + number: number; +} + interface Comic { wanted: { markEntireVolumeWanted?: boolean; - issues?: any[]; + issues?: Issue[]; volume: { id: string; name: string; @@ -15,22 +20,20 @@ interface Comic { } export default class AutoDownloadService extends Service { - private kafkaProducer: any; + private kafkaProducer!: Producer; - private readonly BATCH_SIZE = 100; // Adjust based on your system capacity + private readonly BATCH_SIZE = 100; - // @ts-ignore - constructor( - public broker: ServiceBroker, - schema: ServiceSchema<{}> = { name: "autodownload" }, - ) { + // @ts-ignore -- Moleculer requires this constructor signature for service instantiation + constructor(broker: ServiceBroker) { super(broker); this.parseServiceSchema({ name: "autodownload", actions: { searchWantedComics: { rest: "POST /searchWantedComics", - handler: async (ctx: Context<{}>) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + handler: async (ctx: Context>) => { try { /* eslint-disable no-await-in-loop */ let page = 1; @@ -99,7 +102,7 @@ export default class AutoDownloadService extends Service { async started() { const kafka = new Kafka({ clientId: "comic-search-service", - brokers: [process.env.KAFKA_BROKER_URI], + brokers: [process.env.KAFKA_BROKER_URI || "localhost:9092"], }); this.kafkaProducer = kafka.producer(); await this.kafkaProducer.connect(); diff --git a/services/comicprocessor.service.ts b/services/comicprocessor.service.ts index db31d53..289c9de 100644 --- a/services/comicprocessor.service.ts +++ b/services/comicprocessor.service.ts @@ -1,30 +1,90 @@ -import type { EachMessagePayload } from "kafkajs"; +import type { Consumer, EachMessagePayload, Producer } from "kafkajs"; import { Kafka, logLevel } from "kafkajs"; -import { isNil, isUndefined } from "lodash"; +import { isNil } from "lodash"; import type { ServiceBroker, ServiceSchema } from "moleculer"; import { Service } from "moleculer"; +import type { Socket } from "socket.io-client"; import io from "socket.io-client"; import stringSimilarity from "string-similarity-alg"; +interface SearchPayload { + id: string; + name: string; +} + interface SearchResult { - groupedResult: { entityId: number; payload: any }; - updatedResult: { entityId: number; payload: any }; + groupedResult: { entityId: number; payload: SearchPayload }; + updatedResult: { entityId: number; payload: SearchPayload }; +} + +interface Issue { + issueNumber?: string; + issue_number?: string; + coverDate?: string; + year?: number; +} + +interface Volume { + id: string; + name: string; +} + +interface Comic { + wanted: { + volume: Volume; + issues?: Issue[]; + markEntireVolumeWanted?: boolean; + }; +} + +interface Job { + comic: Comic; +} + +interface Hub { + value: string; +} + +interface DirectConnectSettings { + client: { + hubs: Hub[]; + }; +} + +interface SearchInfo { + query: { + pattern: string; + }; +} + +interface SearchesSentData { + searchInfo: SearchInfo; +} + +interface RankedResult extends SearchPayload { + similarity: number; } export default class ComicProcessorService extends Service { - private kafkaConsumer: any; - private socketIOInstance: any; - private kafkaProducer: any; - private prowlarrResultsMap: Map = new Map(); - private airDCPPSearchResults: Map = new Map(); - private issuesToSearch: any = []; + private kafkaConsumer!: Consumer; - // @ts-ignore: schema parameter is required by Service constructor + private socketIOInstance!: Socket; + + private kafkaProducer!: Producer; + + private prowlarrResultsMap: Map = new Map(); + + private airDCPPSearchResults: Map = new Map(); + + private issuesToSearch: Issue[] = []; + + // @ts-ignore -- Moleculer requires this constructor signature for service instantiation constructor( - public broker: ServiceBroker, + broker: ServiceBroker, + // eslint-disable-next-line @typescript-eslint/no-unused-vars schema: ServiceSchema = { name: "comicProcessor" }, ) { - super(broker, schema); + super(broker); this.parseServiceSchema({ name: "comicProcessor", methods: { @@ -36,9 +96,9 @@ export default class ComicProcessorService extends Service { day: date.getDate(), }; }, - rankSearchResults: async (results: Map, query: string) => { + rankSearchResults: (results: Map, query: string): RankedResult | null => { // Find the highest-ranked response based on similarity to the search string - let highestRankedResult = null; + let highestRankedResult: RankedResult | null = null; let highestSimilarity = -1; results.forEach((resultArray) => { @@ -56,24 +116,25 @@ export default class ComicProcessorService extends Service { return highestRankedResult; }, - processJob: async (job: any) => { + processJob: async (job: Job) => { try { this.logger.info("Processing job:", JSON.stringify(job, null, 2)); // Get the hub to search on - const settings: any = await this.broker.call("settings.getSettings", { + const settings: DirectConnectSettings = await this.broker.call("settings.getSettings", { settingsKey: "directConnect", }); - const hubs = settings.client.hubs.map((hub: any) => hub.value); + const hubs = settings.client.hubs.map((hub: Hub) => hub.value); const { comic } = job; const { volume, issues, markEntireVolumeWanted } = comic.wanted; // If entire volume is marked as wanted, get their details from CV if (markEntireVolumeWanted) { - this.issuesToSearch = await this.broker.call( + const fetchedIssues: Issue[] = await this.broker.call( "comicvine.getIssuesForVolume", { volumeId: volume.id }, ); + this.issuesToSearch = fetchedIssues; this.logger.info( `The entire volume with id: ${volume.id} was marked as wanted.`, ); @@ -81,17 +142,18 @@ export default class ComicProcessorService extends Service { this.logger.info(`${this.issuesToSearch.length} issues to search`); } else { // Or proceed with `issues` from the wanted object. - this.issuesToSearch = issues; + this.issuesToSearch = issues || []; } + /* eslint-disable no-await-in-loop */ for (const issue of this.issuesToSearch) { // Query builder for DC++ // 1. issue number - const inferredIssueNumber = + const issueNumber = issue.issueNumber || issue.issue_number || ""; // 2. year - const { year } = this.parseStringDate(issue.coverDate); - const inferredYear = year || issue.year || ""; + const { year } = this.parseStringDate(issue.coverDate || ""); + const issueYear = year || issue.year || ""; // 3. Orchestrate the query const dcppSearchQuery = { @@ -109,6 +171,7 @@ export default class ComicProcessorService extends Service { "DC++ search query:", JSON.stringify(dcppSearchQuery, null, 4), ); + this.logger.debug(`Issue number: ${issueNumber}, Year: ${issueYear}`); await this.broker.call("socket.search", { query: dcppSearchQuery, @@ -120,40 +183,18 @@ export default class ComicProcessorService extends Service { }, namespace: "/automated", }); - - // const prowlarrResults = await this.broker.call("prowlarr.search", { - // prowlarrQuery: { - // port: "9696", - // apiKey: "c4f42e265fb044dc81f7e88bd41c3367", - // offset: 0, - // categories: [7030], - // query: `${volume.name} ${issue.issueNumber} ${year}`, - // host: "localhost", - // limit: 100, - // type: "search", - // indexerIds: [2], - // }, - // }); - // - // this.logger.info( - // "Prowlarr search results:", - // JSON.stringify(prowlarrResults, null, 4), - // ); - - // Store prowlarr results in map using unique key - // const key = `${volume.name}-${issue.issueNumber}-${year}`; - // this.prowlarrResultsMap.set(key, prowlarrResults); } + /* eslint-enable no-await-in-loop */ } catch (error) { this.logger.error("Error processing job:", error); } }, - produceResultsToKafka: async (query: string, result: any[]): Promise => { + produceResultsToKafka: async (query: string): Promise => { try { /* Match and rank */ - const finalResult = await this.rankSearchResults( + const finalResult = this.rankSearchResults( this.airDCPPSearchResults, query, ); @@ -191,13 +232,13 @@ export default class ComicProcessorService extends Service { async started() { const kafka = new Kafka({ clientId: "comic-processor-service", - brokers: [process.env.KAFKA_BROKER_URI], + brokers: [process.env.KAFKA_BROKER_URI || "localhost:9092"], logLevel: logLevel.INFO, }); this.kafkaConsumer = kafka.consumer({ groupId: "comic-processor-group" }); this.kafkaProducer = kafka.producer(); - this.kafkaConsumer.on("consumer.crash", (event: any) => { + this.kafkaConsumer.on("consumer.crash", (event: { payload: Error }) => { this.logger.error("Consumer crash:", event); }); this.kafkaConsumer.on("consumer.connect", () => { @@ -219,9 +260,9 @@ export default class ComicProcessorService extends Service { }); await this.kafkaConsumer.run({ - eachMessage: async ({ topic, partition, message }: EachMessagePayload) => { + eachMessage: async ({ message }: EachMessagePayload) => { if (message.value) { - const job = JSON.parse(message.value.toString()); + const job = JSON.parse(message.value.toString()) as Job; await this.processJob(job); } else { this.logger.warn("Received message with null value"); @@ -250,14 +291,17 @@ export default class ComicProcessorService extends Service { this.airDCPPSearchResults.set(entityId, []); } if (!isNil(payload)) { - this.airDCPPSearchResults.get(entityId).push(payload); + const results = this.airDCPPSearchResults.get(entityId); + if (results) { + results.push(payload); + } } - console.log( + this.logger.info( "Updated airDCPPSearchResults:", JSON.stringify(Array.from(this.airDCPPSearchResults.entries()), null, 4), ); - console.log(JSON.stringify(payload, null, 4)); + this.logger.info(JSON.stringify(payload, null, 4)); }); // Handle searchResultUpdated event @@ -268,7 +312,7 @@ export default class ComicProcessorService extends Service { const resultsForInstance = this.airDCPPSearchResults.get(entityId); if (resultsForInstance) { - const toReplaceIndex = resultsForInstance.findIndex((element: any) => { + const toReplaceIndex = resultsForInstance.findIndex((element: SearchPayload) => { this.logger.info("search result updated!"); this.logger.info(JSON.stringify(element, null, 4)); return element.id === payload.id; @@ -284,7 +328,7 @@ export default class ComicProcessorService extends Service { }); // Handle searchComplete event - this.socketIOInstance.on("searchesSent", async (data: any) => { + this.socketIOInstance.on("searchesSent", async (data: SearchesSentData) => { this.logger.info( `Search complete for query: "${data.searchInfo.query.pattern}"`, ); diff --git a/services/graphql.service.ts b/services/graphql.service.ts index 9fea1f0..507d0f7 100644 --- a/services/graphql.service.ts +++ b/services/graphql.service.ts @@ -1,13 +1,21 @@ -import { Service, ServiceBroker } from "moleculer"; -import { graphql, GraphQLSchema } from "graphql"; import { makeExecutableSchema } from "@graphql-tools/schema"; -import { typeDefs } from "../models/graphql/typedef"; +import type { GraphQLSchema } from "graphql"; +import { graphql } from "graphql"; +import type { Context, ServiceBroker } from "moleculer"; +import { Service } from "moleculer"; import { resolvers } from "../models/graphql/resolvers"; +import { typeDefs } from "../models/graphql/typedef"; + +interface GraphQLParams { + query: string; + variables?: Record; + operationName?: string; +} export default class GraphQLService extends Service { private graphqlSchema!: GraphQLSchema; - public constructor(broker: ServiceBroker) { + constructor(broker: ServiceBroker) { super(broker); this.parseServiceSchema({ name: "acquisition-graphql", @@ -19,7 +27,7 @@ export default class GraphQLService extends Service { variables: { type: "object", optional: true }, operationName: { type: "string", optional: true }, }, - async handler(ctx: any) { + async handler(ctx: Context) { const { query, variables, operationName } = ctx.params; return graphql({ schema: this.graphqlSchema, diff --git a/services/prowlarr.service.ts b/services/prowlarr.service.ts index 83412c7..37df168 100644 --- a/services/prowlarr.service.ts +++ b/services/prowlarr.service.ts @@ -1,13 +1,32 @@ - -import { Context, Service, ServiceBroker, ServiceSchema, Errors } from "moleculer"; import axios from "axios"; +import type { Context, ServiceBroker } from "moleculer"; +import { Service } from "moleculer"; + +interface ConnectParams { + host: string; + port: string; + apiKey: string; +} + +interface ProwlarrQuery { + host: string; + port: string; + apiKey: string; + query: string; + type: string; + indexerIds: number[]; + categories: number[]; + limit: number; + offset: number; +} + +interface SearchParams { + prowlarrQuery: ProwlarrQuery; +} export default class ProwlarrService extends Service { - // @ts-ignore - constructor( - public broker: ServiceBroker, - schema: ServiceSchema<{}> = { name: "prowlarr" }, - ) { + // @ts-ignore -- Moleculer requires this constructor signature for service instantiation + constructor(broker: ServiceBroker) { super(broker); this.parseServiceSchema({ name: "prowlarr", @@ -16,13 +35,7 @@ export default class ProwlarrService extends Service { actions: { connect: { rest: "POST /connect", - handler: async ( - ctx: Context<{ - host: string; - port: string; - apiKey: string; - }>, - ) => { + handler: async (ctx: Context) => { const { host, port, apiKey } = ctx.params; const result = await axios.request({ url: `http://${host}:${port}/api`, @@ -31,14 +44,12 @@ export default class ProwlarrService extends Service { "X-Api-Key": apiKey, }, }); - console.log(result.data); + this.logger.info(result.data); }, }, getIndexers: { rest: "GET /indexers", - handler: async ( - ctx: Context<{ host: string; port: string; apiKey: string }>, - ) => { + handler: async (ctx: Context) => { const { host, port, apiKey } = ctx.params; const result = await axios.request({ url: `http://${host}:${port}/api/v1/indexer`, @@ -52,21 +63,7 @@ export default class ProwlarrService extends Service { }, search: { rest: "GET /search", - handler: async ( - ctx: Context<{ - prowlarrQuery: { - host: string; - port: string; - apiKey: string; - query: string; - type: string; - indexerIds: [number]; - categories: [number]; - limit: number; - offset: number; - }; - }>, - ) => { + handler: async (ctx: Context) => { const { prowlarrQuery: { indexerIds, @@ -103,7 +100,8 @@ export default class ProwlarrService extends Service { }, ping: { rest: "GET /ping", - handler: async (ctx: Context<{}>) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + handler: async (ctx: Context>) => { const foo = await axios.request({ url: "http://192.168.1.183:9696/ping", method: "GET", @@ -112,7 +110,7 @@ export default class ProwlarrService extends Service { "X-Api-Key": "163ef9a683874f65b53c7be87354b38b", }, }); - console.log(foo.data); + this.logger.info(foo.data); return true; }, }, diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index 8239b7a..58b7b7a 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -1,12 +1,67 @@ import { readFileSync, writeFileSync } from "fs"; import { qBittorrentClient } from "@robertklep/qbittorrent"; -import type { Context, ServiceBroker, ServiceSchema } from "moleculer"; -import { Errors, Service } from "moleculer"; +import type { Context, ServiceBroker } from "moleculer"; +import { Service } from "moleculer"; import parseTorrent from "parse-torrent"; +interface QBittorrentCredentials { + client: { + host: { + username: string; + password: string; + hostname: string; + port: string; + protocol: string; + }; + }; +} + +interface ConnectParams { + username: string; + password: string; + hostname: string; + port: string; + protocol: string; + name?: string; +} + +interface AddTorrentParams { + torrentToDownload: string; + comicObjectId: string; +} + +interface InfoHashesParams { + infoHashes: string[]; +} + +interface TorrentRealTimeStatsParams { + infoHashes: { _id: string; infoHashes: string[] }[]; +} + +interface TorrentDetail { + torrent: Record; +} + +interface TorrentDetailsGroup { + _id: string; + details: TorrentDetail[]; +} + +interface SyncMaindata { + torrents: Record>; + rid?: number; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type QBittorrentMeta = any; + export default class QBittorrentService extends Service { - // @ts-ignore - constructor(public broker: ServiceBroker, schema: ServiceSchema<{}> = { name: "qbittorrent" }) { + private meta!: QBittorrentMeta; + + private rid = 0; + + // @ts-ignore -- Moleculer requires this constructor signature for service instantiation + constructor(broker: ServiceBroker) { super(broker); this.parseServiceSchema({ name: "qbittorrent", @@ -16,42 +71,35 @@ export default class QBittorrentService extends Service { actions: { fetchQbittorrentCredentials: { rest: "GET /fetchQbittorrentCredentials", - handler: async (ctx: Context<{}>) => { - return await this.broker.call("settings.getSettings", { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + handler: async (ctx: Context>) => this.broker.call("settings.getSettings", { settingsKey: "bittorrent", - }); - }, + }), }, connect: { rest: "POST /connect", - handler: async ( - ctx: Context<{ - username: string; - password: string; - hostname: string; - port: string; - protocol: string; - name?: string; - }>, - ) => { + handler: (ctx: Context) => { const { username, password, hostname, port, protocol } = ctx.params; + // eslint-disable-next-line new-cap this.meta = new qBittorrentClient( `${protocol}://${hostname}:${port}`, `${username}`, `${password}`, ); - console.log(this.meta); + this.logger.info(this.meta); if (this.meta) { return { success: true, message: "Logged in successfully" }; } + return { success: false, message: "Failed to connect" }; }, }, loginWithStoredCredentials: { rest: "POST /loginWithStoredCredentials", - handler: async (ctx: Context<{}>) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + handler: async (ctx: Context>) => { try { - const result: any = await this.broker.call( + const result: QBittorrentCredentials | undefined = await this.broker.call( "qbittorrent.fetchQbittorrentCredentials", {}, ); @@ -69,10 +117,14 @@ export default class QBittorrentService extends Service { port, protocol, }); - console.log("qbittorrent connection details:"); - console.log(JSON.stringify(connection, null, 4)); + this.logger.info("qbittorrent connection details:"); + this.logger.info(JSON.stringify(connection, null, 4)); return connection; } + return { + error: null, + message: "Qbittorrent credentials not found.", + }; } catch (err) { return { error: err, @@ -85,7 +137,8 @@ export default class QBittorrentService extends Service { getClientInfo: { rest: "GET /getClientInfo", - handler: async (ctx: Context<{}>) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + handler: async (ctx: Context>) => { await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); return { buildInfo: await this.meta.app.buildInfo(), @@ -96,22 +149,17 @@ export default class QBittorrentService extends Service { }, addTorrent: { rest: "POST /addTorrent", - handler: async ( - ctx: Context<{ - torrentToDownload: any; - comicObjectId: string; - }>, - ) => { + handler: async (ctx: Context) => { try { await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); const { torrentToDownload, comicObjectId } = ctx.params; - console.log(torrentToDownload); + this.logger.info(torrentToDownload); const response = await fetch(torrentToDownload, { method: "GET", }); // Read the buffer to a file const buffer = await response.arrayBuffer(); - writeFileSync(`mithrandir.torrent`, Buffer.from(buffer)); + writeFileSync(`mithrandir.torrent`, new Uint8Array(buffer)); // Add the torrent to qbittorrent's queue, paused. const result = await this.meta.torrents.add({ torrents: { @@ -134,28 +182,33 @@ export default class QBittorrentService extends Service { result, }; } catch (err) { - console.error(err); + this.logger.error(err); + return { + error: err, + message: "Failed to add torrent", + }; } }, }, getTorrents: { rest: "POST /getTorrents", - handler: async (ctx: Context<{}>) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + handler: async (ctx: Context>) => { await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); - return await this.meta.torrents.info(); + return this.meta.torrents.info(); }, }, getTorrentProperties: { rest: "POST /getTorrentProperties", - handler: async (ctx: Context<{ infoHashes: string[] }>) => { + handler: async (ctx: Context) => { try { const { infoHashes } = ctx.params; await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); - return await this.meta.torrents.info({ + return this.meta.torrents.info({ hashes: infoHashes, }); } catch (err) { - console.error("An error occurred:", err); + this.logger.error("An error occurred:", err); // Consider handling the error more gracefully here, possibly returning an error response throw err; // or return a specific error object/message } @@ -163,23 +216,21 @@ export default class QBittorrentService extends Service { }, getTorrentRealTimeStats: { rest: "POST /getTorrentRealTimeStats", - handler: async ( - ctx: Context<{ infoHashes: { _id: string; infoHashes: string[] }[] }>, - ) => { + handler: async (ctx: Context) => { const { infoHashes } = ctx.params; await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); try { // Increment rid for each call this.rid = typeof this.rid === "number" ? this.rid + 1 : 0; - const data = await this.meta.sync.maindata(this.rid); - const torrentDetails: any = []; + const data: SyncMaindata = await this.meta.sync.maindata(this.rid); + const torrentDetails: TorrentDetailsGroup[] = []; - infoHashes.forEach(({ _id, infoHashes }) => { + infoHashes.forEach(({ _id, infoHashes: hashes }) => { // Initialize an object to hold details for this _id - const details: any = []; + const details: TorrentDetail[] = []; - infoHashes.forEach((hash) => { + hashes.forEach((hash) => { // Assuming 'data.torrents[hash]' retrieves the details for the hash const torrent = data.torrents[hash]; if (torrent) { @@ -201,9 +252,9 @@ export default class QBittorrentService extends Service { // Assuming `data.rid` contains the latest rid from the server if (data.rid !== undefined) { this.rid = data.rid; - console.log(`rid is ${this.rid}`); + this.logger.info(`rid is ${this.rid}`); } - console.log(JSON.stringify(torrentDetails, null, 4)); + this.logger.info(JSON.stringify(torrentDetails, null, 4)); return torrentDetails; } catch (err) { this.logger.error(err); @@ -213,24 +264,23 @@ export default class QBittorrentService extends Service { }, determineDownloadApps: { rest: "", - handler: async () => { - // 1. Parse the incoming search query - // to make sure that it is well-formed - // At the very least, it should have name, year, number - // 2. Choose between download mediums based on user-preference? - // possible choices are: DC++, Torrent - // 3. Perform the search on those media with the aforementioned search query - // 4. Choose a subset of relevant search results, - // and score them - // 5. Download the highest-scoring, relevant result - }, + // 1. Parse the incoming search query + // to make sure that it is well-formed + // At the very least, it should have name, year, number + // 2. Choose between download mediums based on user-preference? + // possible choices are: DC++, Torrent + // 3. Perform the search on those media with the aforementioned search query + // 4. Choose a subset of relevant search results, + // and score them + // 5. Download the highest-scoring, relevant result + handler: () => undefined, }, }, methods: {}, - async started() { - console.log(`Initializing rid...`); + started() { + this.logger.info(`Initializing rid...`); this.rid = 0; - console.log(`rid is ${this.rid}`); + this.logger.info(`rid is ${this.rid}`); }, }); } diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 0000000..4313c71 --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": [ + "./**/*.ts", + "./**/*.js" + ], + "exclude": [ + "node_modules", + "dist" + ] +}