From 104e7ba0bbb45af0f5a3e99468ee0d5c5dbb6a08 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Fri, 15 Sep 2023 15:50:06 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20Parameterized=20connect?= =?UTF-8?q?=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 7 + package.json | 1 + services/api.service.ts | 220 +++++++++----------------------- services/qbittorrent.service.ts | 28 +++- 4 files changed, 89 insertions(+), 167 deletions(-) diff --git a/package-lock.json b/package-lock.json index cb2d742..ceaeba2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "jest": "^29.3.1", "moleculer-repl": "^0.7.3", "prettier": "^2.8.0", + "qbittorrent-api-v2": "^1.2.2", "ts-jest": "^29.0.3", "ts-node": "^10.9.1", "typescript": "^4.9.3" @@ -6285,6 +6286,12 @@ } ] }, + "node_modules/qbittorrent-api-v2": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/qbittorrent-api-v2/-/qbittorrent-api-v2-1.2.2.tgz", + "integrity": "sha512-v9nkeikj8EjZJ2Ud/QAkTr038PAM4Xk8WXtR8scVb3jidFGs8gMOrZIL05gy4JNo4b0/vxKlwcC2Zu4IpuMslw==", + "dev": true + }, "node_modules/qs": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", diff --git a/package.json b/package.json index b86abcd..30479f9 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "jest": "^29.3.1", "moleculer-repl": "^0.7.3", "prettier": "^2.8.0", + "qbittorrent-api-v2": "^1.2.2", "ts-jest": "^29.0.3", "ts-node": "^10.9.1", "typescript": "^4.9.3" diff --git a/services/api.service.ts b/services/api.service.ts index fc864ce..ab918aa 100644 --- a/services/api.service.ts +++ b/services/api.service.ts @@ -1,168 +1,68 @@ -import type { Context, ServiceSchema } from "moleculer"; -import type { ApiSettingsSchema, GatewayResponse, IncomingRequest, Route } from "moleculer-web"; +import fs from "fs"; +import { Service, ServiceBroker } 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); + 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, + routes: [ + { + path: "/api", + whitelist: ["**"], + cors: { + origin: "*", + methods: ["GET", "OPTIONS", "POST", "PUT", "DELETE"], + allowedHeaders: ["*"], + exposedHeaders: [], + credentials: false, + maxAge: 3600, + }, + use: [], + mergeParams: true, + authentication: false, + authorization: false, + autoAliases: true, + aliases: {}, + callingOptions: {}, -const ApiService: ServiceSchema = { - name: "api", - mixins: [ApiGateway], - - // 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, - - // Exposed IP - ip: "0.0.0.0", - - // Global Express middlewares. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Middlewares - use: [], - - 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", + bodyParsers: { + json: { + strict: false, + limit: "1MB", + }, + urlencoded: { + extended: true, + limit: "1MB", + }, + }, + mappingPolicy: "all", // Available values: "all", "restrict" + logging: true, }, - urlencoded: { - extended: true, - limit: "1MB", + + { + path: "/logs", + use: [ApiGateway.serveStatic("logs")], }, + ], + log4XXResponses: false, + logRequestParams: true, + logResponseData: true, + assets: { + folder: "public", + // Options to `server-static` module + 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, }, - ], + events: {}, - // 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, - - // Serve assets from "public" folder. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Serve-static-files - assets: { - folder: "public", - - // Options to `server-static` module - options: {}, - }, - }, - - 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; - - if (auth && auth.startsWith("Bearer")) { - const token = auth.slice(7); - - // 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; - } - }, - - /** - * 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; - - // 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; + methods: {}, + started(): any {}, + }); + } +} diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index ccc6a56..add4fb5 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -16,14 +16,28 @@ export default class QBittorrentService extends Service { actions: { connect: { rest: "POST /connect", - handler: async (ctx: Context<{}>) => { - const client = new qBittorrentClient( - "http://127.0.0.1:8080", - "admin", - "adminadmin", + handler: async ( + ctx: Context<{ + username: string; + password: string; + hostname: string; + port: string; + protocol: string; + name: string; + }>, + ) => { + const { username, password, hostname, port, protocol } = ctx.params; + this.meta = new qBittorrentClient( + `${protocol}://${hostname}:${port}`, + `${username}`, + `${password}`, ); - console.log(client); - return { foo: "bar" }; + }, + }, + getClientInfo: { + rest: "GET /getClientInfo", + handler: async (ctx: Context<{}>) => { + return await this.meta.app.buildInfo(); }, }, },