From 44f612c52fd139954210c9687dd0a00d56b7f065 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Tue, 5 Sep 2023 21:53:42 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Fixes=20to=20getList=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/api.service.ts | 273 ++++++++++++++------------------ services/qbittorrent.service.ts | 31 ++-- 2 files changed, 140 insertions(+), 164 deletions(-) diff --git a/services/api.service.ts b/services/api.service.ts index fc864ce..32768f1 100644 --- a/services/api.service.ts +++ b/services/api.service.ts @@ -1,168 +1,141 @@ -import type { Context, ServiceSchema } from "moleculer"; -import type { ApiSettingsSchema, GatewayResponse, IncomingRequest, Route } from "moleculer-web"; +import { IncomingMessage } from "http"; +import { Service, ServiceBroker, Context } from "moleculer"; import ApiGateway from "moleculer-web"; -interface Meta { - userAgent?: string | null | undefined; - user?: object | null | undefined; -} +export default class ApiService extends Service { + public constructor(broker: ServiceBroker) { + super(broker); + // @ts-ignore + this.parseServiceSchema({ + name: "api", + mixins: [ApiGateway], + // More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html + settings: { + port: process.env.PORT || 3060, -const ApiService: ServiceSchema = { - name: "api", - mixins: [ApiGateway], + routes: [ + { + path: "/api", + whitelist: ["**"], + use: [], + mergeParams: true, + cors: { + origin: "*", + methods: [ + "GET", + "OPTIONS", + "POST", + "PUT", + "DELETE", + ], + allowedHeaders: ["*"], + exposedHeaders: [], + credentials: false, + maxAge: 3600, + }, - // More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html - settings: { - // Exposed port - port: process.env.PORT != null ? Number(process.env.PORT) : 3060, + authentication: false, + authorization: false, + autoAliases: true, - // Exposed IP - ip: "0.0.0.0", + aliases: {}, + callingOptions: {}, - // Global Express middlewares. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Middlewares - use: [], + bodyParsers: { + json: { + strict: false, + limit: "1MB", + }, + urlencoded: { + extended: true, + limit: "1MB", + }, + }, + mappingPolicy: "all", // Available values: "all", "restrict" - routes: [ - { - path: "/api", - - whitelist: ["**"], - - // Route-level Express middlewares. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Middlewares - use: [], - - // Enable/disable parameter merging method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Disable-merging - mergeParams: true, - - // Enable authentication. Implement the logic into `authenticate` method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Authentication - authentication: false, - - // Enable authorization. Implement the logic into `authorize` method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Authorization - authorization: false, - - // The auto-alias feature allows you to declare your route alias directly in your services. - // The gateway will dynamically build the full routes from service schema. - autoAliases: true, - - aliases: {}, - - /** - * Before call hook. You can check the request. - * - onBeforeCall( - ctx: Context, - route: Route, - req: IncomingRequest, - res: GatewayResponse, - ): void { - // Set request headers to context meta - ctx.meta.userAgent = req.headers["user-agent"]; - }, */ - - /** - * After call hook. You can modify the data. - * - onAfterCall( - ctx: Context, - route: Route, - req: IncomingRequest, - res: GatewayResponse, - data: unknown, - ): unknown { - // Async function which return with Promise - // return this.doSomething(ctx, res, data); - return data; - }, */ - - // Calling options. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Calling-options - // callingOptions: {}, - - bodyParsers: { - json: { - strict: false, - limit: "1MB", - }, - urlencoded: { - extended: true, - limit: "1MB", + // Enable/disable logging + logging: true, }, + ], + // Do not log client side errors (does not log an error response when the error.code is 400<=X<500) + log4XXResponses: false, + // Logging the request parameters. Set to any log level to enable it. E.g. "info" + logRequestParams: null, + logResponseData: null, + assets: { + folder: "public", + options: {}, }, - - // Mapping policy setting. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Mapping-policy - mappingPolicy: "all", // Available values: "all", "restrict" - - // Enable/disable logging - logging: true, }, - ], - // Do not log client side errors (does not log an error response when the error.code is 400<=X<500) - log4XXResponses: false, - // Logging the request parameters. Set to any log level to enable it. E.g. "info" - logRequestParams: null, - // Logging the response data. Set to any log level to enable it. E.g. "info" - logResponseData: null, + methods: { + /** + * Authenticate the request. It checks the `Authorization` token value in the request header. + * Check the token value & resolve the user by the token. + * The resolved user will be available in `ctx.meta.user` + * + * PLEASE NOTE, IT'S JUST AN EXAMPLE IMPLEMENTATION. DO NOT USE IN PRODUCTION! + * + * @param {Context} ctx + * @param {any} route + * @param {IncomingMessage} req + * @returns {Promise} - // Serve assets from "public" folder. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Serve-static-files - assets: { - folder: "public", + async authenticate (ctx: Context, route: any, req: IncomingMessage): Promise < any > => { + // Read the token from header + const auth = req.headers.authorization; - // Options to `server-static` module - options: {}, - }, - }, + if (auth && auth.startsWith("Bearer")) { + const token = auth.slice(7); - methods: { - /** - * Authenticate the request. It check the `Authorization` token value in the request header. - * Check the token value & resolve the user by the token. - * The resolved user will be available in `ctx.meta.user` - * - * PLEASE NOTE, IT'S JUST AN EXAMPLE IMPLEMENTATION. DO NOT USE IN PRODUCTION! - */ - authenticate( - ctx: Context, - route: Route, - req: IncomingRequest, - ): Record | null { - // Read the token from header - const auth = req.headers.authorization; + // Check the token. Tip: call a service which verify the token. E.g. `accounts.resolveToken` + if (token === "123456") { + // Returns the resolved user. It will be set to the `ctx.meta.user` + return { + id: 1, + name: "John Doe", + }; - if (auth && auth.startsWith("Bearer")) { - const token = auth.slice(7); + } else { + // Invalid token + throw new ApiGateway.Errors.UnAuthorizedError(ApiGateway.Errors.ERR_INVALID_TOKEN, { + error: "Invalid Token", + }); + } - // Check the token. Tip: call a service which verify the token. E.g. `accounts.resolveToken` - if (token === "123456") { - // Returns the resolved user. It will be set to the `ctx.meta.user` - return { id: 1, name: "John Doe" }; - } - // Invalid token - throw new ApiGateway.Errors.UnAuthorizedError( - ApiGateway.Errors.ERR_INVALID_TOKEN, - null, - ); - } else { - // No token. Throw an error or do nothing if anonymous access is allowed. - // throw new E.UnAuthorizedError(E.ERR_NO_TOKEN); - return null; - } - }, + } else { + // No token. Throw an error or do nothing if anonymous access is allowed. + // Throw new E.UnAuthorizedError(E.ERR_NO_TOKEN); + return null; + } + }, + */ + /** + * Authorize the request. Check that the authenticated user has right to access the resource. + * + * PLEASE NOTE, IT'S JUST AN EXAMPLE IMPLEMENTATION. DO NOT USE IN PRODUCTION! + * + * @param {Context} ctx + * @param {Object} route + * @param {IncomingMessage} req + * @returns {Promise} - /** - * Authorize the request. Check that the authenticated user has right to access the resource. - * - * PLEASE NOTE, IT'S JUST AN EXAMPLE IMPLEMENTATION. DO NOT USE IN PRODUCTION! - */ - authorize(ctx: Context, route: Route, req: IncomingRequest) { - // Get the authenticated user. - const { user } = ctx.meta; + async authorize (ctx: Context < any, { + user: string; + } > , route: Record, req: IncomingMessage): Promise < any > => { + // Get the authenticated user. + const user = ctx.meta.user; - // It check the `auth` property in action schema. - if (req.$action.auth === "required" && !user) { - throw new ApiGateway.Errors.UnAuthorizedError("NO_RIGHTS", null); - } - }, - }, -}; - -export default ApiService; + // It check the `auth` property in action schema. + // @ts-ignore + if (req.$action.auth === "required" && !user) { + throw new ApiGateway.Errors.UnAuthorizedError("NO_RIGHTS", { + error: "Unauthorized", + }); + } + }, + */ + }, + }); + } +} diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index 9e031d4..137e6fb 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -1,6 +1,7 @@ "use strict"; import { Context, Service, ServiceBroker, ServiceSchema, Errors } from "moleculer"; -import { qBittorrentClient} from "@robertklep/qbittorrent"; +import { qBittorrentClient } from "@robertklep/qbittorrent"; +const { MoleculerError } = require("moleculer").Errors; export default class QBittorrentService extends Service { // @ts-ignore @@ -14,23 +15,25 @@ export default class QBittorrentService extends Service { mixins: [], hooks: {}, actions: { - connect: { - rest: "POST /connect", - handler: async (ctx: Context<{}>) => { - const torrentClient = new qBittorrentClient("http://192.168.1.183:8089", "admin", "adminadmin"); - const info = await torrentClient.torrents.info(); - return { foo: info }; - }, - }, - getList : { - rest: "GET /getList", + getList: { + rest: "GET /getTorrents", handler: async (ctx: Context<{}>) => { - + return await this.torrentClient.torrents.info() } } - }, - methods: {}, + }, methods: {}, + async started(): Promise { + try { + this.torrentClient = new qBittorrentClient("http://192.168.1.183:8089", "admin", "adminadmin"); + + } catch (err) { + throw new MoleculerError(err, 500, "QBITTORRENT_CONNECTION_ERROR", { + data: err, + }); + } + + } }); } }