🏗️ Parameterized connect endpoint

This commit is contained in:
2023-09-15 15:50:06 -04:00
parent e49c9c985a
commit 104e7ba0bb
4 changed files with 89 additions and 167 deletions

7
package-lock.json generated
View File

@@ -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",

View File

@@ -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"

View File

@@ -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<ApiSettingsSchema> = {
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<unknown, Meta>,
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<string, unknown> | 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<null, Meta>, 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 {},
});
}
}

View File

@@ -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();
},
},
},