Files
threetwo-core-service/services/settings.service.ts
Rishi Ghan 83f905ebb6
Some checks failed
Docker Image CI / build (push) Has been cancelled
getDirectoryStatus endpoint
2026-04-14 17:28:44 -04:00

259 lines
7.2 KiB
TypeScript

"use strict";
import {
Context,
Service,
ServiceBroker,
ServiceSchema,
Errors,
} from "moleculer";
import { DbMixin } from "../mixins/db.mixin";
import Settings from "../models/settings.model";
import { isEmpty, pickBy, identity, map, isNil } from "lodash";
import fs from "fs";
import path from "path";
import { COMICS_DIRECTORY, USERDATA_DIRECTORY } from "../constants/directories";
const ObjectId = require("mongoose").Types.ObjectId;
export default class SettingsService extends Service {
// @ts-ignore
public constructor(
public broker: ServiceBroker,
schema: ServiceSchema<{}> = { name: "settings" }
) {
super(broker);
this.parseServiceSchema({
name: "settings",
mixins: [DbMixin("settings", Settings)],
settings: {},
hooks: {},
actions: {
getEnvironmentVariables: {
rest: "GET /getEnvironmentVariables",
params: {},
handler: async (ctx: Context<{}>) => {
return {
comicsDirectory: process.env.COMICS_DIRECTORY,
userdataDirectory: process.env.USERDATA_DIRECTORY,
redisURI: process.env.REDIS_URI,
elasticsearchURI: process.env.ELASTICSEARCH_URI,
mongoURI: process.env.MONGO_URI,
kafkaBroker: process.env.KAFKA_BROKER,
unrarBinPath: process.env.UNRAR_BIN_PATH,
sevenzBinPath: process.env.SEVENZ_BINARY_PATH,
comicvineAPIKey: process.env.COMICVINE_API_KEY,
}
}
},
getDirectoryStatus: {
rest: "GET /getDirectoryStatus",
params: {},
handler: async (ctx: Context<{}>) => {
const comicsDirectoryEnvSet = !!process.env.COMICS_DIRECTORY;
const userdataDirectoryEnvSet = !!process.env.USERDATA_DIRECTORY;
const resolvedComicsDirectory = path.resolve(COMICS_DIRECTORY);
const resolvedUserdataDirectory = path.resolve(USERDATA_DIRECTORY);
let comicsDirectoryExists = false;
let userdataDirectoryExists = false;
try {
await fs.promises.access(resolvedComicsDirectory, fs.constants.F_OK);
comicsDirectoryExists = true;
} catch {
comicsDirectoryExists = false;
}
try {
await fs.promises.access(resolvedUserdataDirectory, fs.constants.F_OK);
userdataDirectoryExists = true;
} catch {
userdataDirectoryExists = false;
}
const issues: string[] = [];
if (!comicsDirectoryEnvSet) {
issues.push("COMICS_DIRECTORY environment variable is not set");
}
if (!userdataDirectoryEnvSet) {
issues.push("USERDATA_DIRECTORY environment variable is not set");
}
if (!comicsDirectoryExists) {
issues.push(`Comics directory does not exist: ${resolvedComicsDirectory}`);
}
if (!userdataDirectoryExists) {
issues.push(`Userdata directory does not exist: ${resolvedUserdataDirectory}`);
}
return {
comicsDirectory: {
path: resolvedComicsDirectory,
envSet: comicsDirectoryEnvSet,
exists: comicsDirectoryExists,
isValid: comicsDirectoryEnvSet && comicsDirectoryExists,
},
userdataDirectory: {
path: resolvedUserdataDirectory,
envSet: userdataDirectoryEnvSet,
exists: userdataDirectoryExists,
isValid: userdataDirectoryEnvSet && userdataDirectoryExists,
},
isValid: comicsDirectoryEnvSet && userdataDirectoryEnvSet && comicsDirectoryExists && userdataDirectoryExists,
issues,
};
}
},
getSettings: {
rest: "GET /getAllSettings",
params: {},
async handler(ctx: Context<{ settingsKey: string }>) {
const { settingsKey } = ctx.params;
// Initialize a projection object. Include everything by default.
let projection = settingsKey
? { _id: 0, [settingsKey]: 1 }
: {};
// Find the settings with the dynamic projection
const settings = await Settings.find({}, projection);
if (settings.length === 0) {
return {};
}
// If settingsKey is provided, return the specific part of the settings.
// Otherwise, return the entire settings document.
if (settingsKey) {
// Check if the specific key exists in the settings document.
// Since `settings` is an array, we access the first element.
// Then, we use the settingsKey to return only that part of the document.
return settings[0][settingsKey] || {};
} else {
// Return the entire settings document
return settings[0];
}
},
},
saveSettings: {
rest: "POST /saveSettings",
params: {},
async handler(
ctx: Context<{
settingsPayload?: {
protocol: string;
hostname: string;
port: string;
username: string;
password: string;
_id?: string;
airDCPPUserSettings?: object;
hubs?: [];
};
settingsObjectId?: string;
settingsKey: string;
}>
) {
try {
console.log(ctx.params);
let query = {};
const { settingsKey, settingsObjectId } =
ctx.params;
const {
hostname,
protocol,
port,
username,
password,
} = ctx.params.settingsPayload;
const host = {
hostname,
protocol,
port,
username,
password,
};
const undefinedPropsInHostname = Object.values(
host
).filter((value) => value === undefined);
// Update, depending what key was passed in params
// 1. Construct the update query
switch (settingsKey) {
case "bittorrent":
console.log(
`Recieved settings for ${settingsKey}, building query...`
);
query = {
...(undefinedPropsInHostname.length ===
0 && {
$set: {
"bittorrent.client.host": host,
},
}),
};
break;
case "directConnect":
console.log(
`Recieved settings for ${settingsKey}, building query...`
);
const { hubs, airDCPPUserSettings } =
ctx.params.settingsPayload;
query = {
...(undefinedPropsInHostname.length ===
0 && {
$set: {
"directConnect.client.host":
host,
},
}),
...(!isNil(hubs) && {
$set: {
"directConnect.client.hubs":
hubs,
},
}),
};
console.log(JSON.stringify(query, null, 4));
break;
default:
return false;
}
// 2. Set up options, filters
const options = {
upsert: true,
setDefaultsOnInsert: true,
returnDocument: "after",
};
const filter = settingsObjectId
? { _id: settingsObjectId }
: {};
// 3. Execute the mongo query
const result = await Settings.findOneAndUpdate(
filter,
query,
options
);
return result;
} catch (err) {
return err;
}
},
},
deleteSettings: {
rest: "POST /deleteSettings",
params: {},
async handler(ctx: Context<{}>) {
return await Settings.remove({}, (result) => result);
},
},
},
methods: {},
});
}
}