🏗️ Parameterized connect endpoint
This commit is contained in:
7
package-lock.json
generated
7
package-lock.json
generated
@@ -30,6 +30,7 @@
|
|||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
"moleculer-repl": "^0.7.3",
|
"moleculer-repl": "^0.7.3",
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.0",
|
||||||
|
"qbittorrent-api-v2": "^1.2.2",
|
||||||
"ts-jest": "^29.0.3",
|
"ts-jest": "^29.0.3",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.9.3"
|
"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": {
|
"node_modules/qs": {
|
||||||
"version": "6.11.2",
|
"version": "6.11.2",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
"moleculer-repl": "^0.7.3",
|
"moleculer-repl": "^0.7.3",
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.0",
|
||||||
|
"qbittorrent-api-v2": "^1.2.2",
|
||||||
"ts-jest": "^29.0.3",
|
"ts-jest": "^29.0.3",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.9.3"
|
"typescript": "^4.9.3"
|
||||||
|
|||||||
@@ -1,168 +1,68 @@
|
|||||||
import type { Context, ServiceSchema } from "moleculer";
|
import fs from "fs";
|
||||||
import type { ApiSettingsSchema, GatewayResponse, IncomingRequest, Route } from "moleculer-web";
|
import { Service, ServiceBroker } from "moleculer";
|
||||||
import ApiGateway from "moleculer-web";
|
import ApiGateway from "moleculer-web";
|
||||||
|
|
||||||
interface Meta {
|
export default class ApiService extends Service {
|
||||||
userAgent?: string | null | undefined;
|
public constructor(broker: ServiceBroker) {
|
||||||
user?: object | null | undefined;
|
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> = {
|
bodyParsers: {
|
||||||
name: "api",
|
json: {
|
||||||
mixins: [ApiGateway],
|
strict: false,
|
||||||
|
limit: "1MB",
|
||||||
// More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html
|
},
|
||||||
settings: {
|
urlencoded: {
|
||||||
// Exposed port
|
extended: true,
|
||||||
port: process.env.PORT != null ? Number(process.env.PORT) : 3060,
|
limit: "1MB",
|
||||||
|
},
|
||||||
// Exposed IP
|
},
|
||||||
ip: "0.0.0.0",
|
mappingPolicy: "all", // Available values: "all", "restrict"
|
||||||
|
logging: true,
|
||||||
// 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",
|
|
||||||
},
|
},
|
||||||
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)
|
methods: {},
|
||||||
log4XXResponses: false,
|
started(): any {},
|
||||||
// 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;
|
|
||||||
|
|||||||
@@ -16,14 +16,28 @@ export default class QBittorrentService extends Service {
|
|||||||
actions: {
|
actions: {
|
||||||
connect: {
|
connect: {
|
||||||
rest: "POST /connect",
|
rest: "POST /connect",
|
||||||
handler: async (ctx: Context<{}>) => {
|
handler: async (
|
||||||
const client = new qBittorrentClient(
|
ctx: Context<{
|
||||||
"http://127.0.0.1:8080",
|
username: string;
|
||||||
"admin",
|
password: string;
|
||||||
"adminadmin",
|
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();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user