diff --git a/src/dummy.js b/src/dummy.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/navigation/admin.js b/src/navigation/admin.js index df8241c8ba..851b81f058 100644 --- a/src/navigation/admin.js +++ b/src/navigation/admin.js @@ -1,104 +1,128 @@ -'use strict'; - -const validator = require('validator'); -const winston = require('winston'); - -const plugins = require('../plugins'); -const db = require('../database'); -const pubsub = require('../pubsub'); - -const admin = module.exports; +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +/* eslint-disable import/no-import-module-exports */ +const validator_1 = __importDefault(require("validator")); +const winston_1 = __importDefault(require("winston")); +const plugins_1 = __importDefault(require("../plugins")); +const database_1 = __importDefault(require("../database")); +const pubsub_1 = __importDefault(require("../pubsub")); +const promisify_1 = __importDefault(require("../promisify")); +const navigation_json_1 = __importDefault(require("../../install/data/navigation.json")); +const admin = {}; let cache = null; - -pubsub.on('admin:navigation:save', () => { - cache = null; +pubsub_1.default.on('admin:navigation:save', () => { + cache = null; }); - -admin.save = async function (data) { - const order = Object.keys(data); - const bulkSet = []; - data.forEach((item, index) => { - item.order = order[index]; - if (item.hasOwnProperty('groups')) { - item.groups = JSON.stringify(item.groups); - } - bulkSet.push([`navigation:enabled:${item.order}`, item]); - }); - - cache = null; - pubsub.publish('admin:navigation:save'); - const ids = await db.getSortedSetRange('navigation:enabled', 0, -1); - await db.deleteAll(ids.map(id => `navigation:enabled:${id}`)); - await db.setObjectBulk(bulkSet); - await db.delete('navigation:enabled'); - await db.sortedSetAdd('navigation:enabled', order, order); +admin.save = function (data) { + return __awaiter(this, void 0, void 0, function* () { + const order = Object.keys(data); + const bulkSet = []; + data.forEach((item, index) => { + item.order = order[index]; + if (item.hasOwnProperty('groups')) { + item.groups = JSON.stringify(item.groups); + } + bulkSet.push([`navigation:enabled:${item.order}`, item]); + }); + cache = null; + pubsub_1.default.publish('admin:navigation:save'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + const ids = yield database_1.default.getSortedSetRange('navigation:enabled', 0, -1); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + yield database_1.default.deleteAll(ids.map(id => `navigation:enabled:${id}`)); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + yield database_1.default.setObjectBulk(bulkSet); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + yield database_1.default.delete('navigation:enabled'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + yield database_1.default.sortedSetAdd('navigation:enabled', order, order); + }); }; - -admin.getAdmin = async function () { - const [enabled, available] = await Promise.all([ - admin.get(), - getAvailable(), - ]); - return { enabled: enabled, available: available }; -}; - const fieldsToEscape = ['iconClass', 'class', 'route', 'id', 'text', 'textClass', 'title']; - -admin.escapeFields = navItems => toggleEscape(navItems, true); -admin.unescapeFields = navItems => toggleEscape(navItems, false); - function toggleEscape(navItems, flag) { - navItems.forEach((item) => { - if (item) { - fieldsToEscape.forEach((field) => { - if (item.hasOwnProperty(field)) { - item[field] = validator[flag ? 'escape' : 'unescape'](String(item[field])); - } - }); - } - }); + navItems.forEach((item) => { + if (item) { + fieldsToEscape.forEach((field) => { + if (item.hasOwnProperty(field)) { + item[field] = validator_1.default[flag ? 'escape' : 'unescape'](String(item[field])); + } + }); + } + }); } - -admin.get = async function () { - if (cache) { - return cache.map(item => ({ ...item })); - } - const ids = await db.getSortedSetRange('navigation:enabled', 0, -1); - const data = await db.getObjects(ids.map(id => `navigation:enabled:${id}`)); - cache = data.filter(Boolean).map((item) => { - if (item.hasOwnProperty('groups')) { - try { - item.groups = JSON.parse(item.groups); - } catch (err) { - winston.error(err.stack); - item.groups = []; - } - } - item.groups = item.groups || []; - if (item.groups && !Array.isArray(item.groups)) { - item.groups = [item.groups]; - } - return item; - }); - admin.escapeFields(cache); - - return cache.map(item => ({ ...item })); +admin.escapeFields = navItems => toggleEscape(navItems, true); +admin.unescapeFields = navItems => toggleEscape(navItems, false); +admin.get = function () { + return __awaiter(this, void 0, void 0, function* () { + if (cache) { + return cache.map(item => (Object.assign({}, item))); + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + const ids = yield database_1.default.getSortedSetRange('navigation:enabled', 0, -1); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + const data = yield database_1.default.getObjects(ids.map(id => `navigation:enabled:${id}`)); + cache = data.filter(Boolean).map((item) => { + if (item.hasOwnProperty('groups')) { + try { + item.groups = JSON.parse(item.groups); + } + catch (err) { + if (err instanceof Error) { + winston_1.default.error(err.stack); + } + else { + winston_1.default.error('Unknown error', err); + } + item.groups = []; + } + } + item.groups = item.groups || []; + if (item.groups && !Array.isArray(item.groups)) { + item.groups = [item.groups]; + } + return item; + }); + admin.escapeFields(cache); + return cache.map(item => (Object.assign({}, item))); + }); }; - -async function getAvailable() { - const core = require('../../install/data/navigation.json').map((item) => { - item.core = true; - item.id = item.id || ''; - return item; - }); - - const navItems = await plugins.hooks.fire('filter:navigation.available', core); - navItems.forEach((item) => { - if (item && !item.hasOwnProperty('enabled')) { - item.enabled = true; - } - }); - return navItems; +function getAvailable() { + return __awaiter(this, void 0, void 0, function* () { + const core = navigation_json_1.default.map((item) => { + item.core = true; + item.id = item.id || ''; + return item; + }); + const navItems = yield plugins_1.default.hooks.fire('filter:navigation.available', core); + navItems.forEach((item) => { + if (item && !item.hasOwnProperty('enabled')) { + item.enabled = true; + } + }); + return navItems; + }); } - -require('../promisify')(admin); +admin.getAdmin = function () { + return __awaiter(this, void 0, void 0, function* () { + const [enabled, available] = yield Promise.all([ + admin.get(), + getAvailable(), + ]); + return { enabled: enabled, available: available }; + }); +}; +// eslint-disable-next-line @typescript-eslint/no-unsafe-call +(0, promisify_1.default)(admin); +module.exports = admin; diff --git a/src/navigation/admin.ts b/src/navigation/admin.ts new file mode 100644 index 0000000000..2ee1aceadf --- /dev/null +++ b/src/navigation/admin.ts @@ -0,0 +1,144 @@ +/* eslint-disable import/no-import-module-exports */ +import validator from 'validator'; +import winston from 'winston'; +import plugins from '../plugins'; +import db from '../database'; +import pubsub from '../pubsub'; +import promisify from '../promisify'; +import navigationData from '../../install/data/navigation.json'; + +// Interface for the structure of navigation items +interface NavigationItem { + order?: string; + groups?: string | string[]; + iconClass?: string; + class?: string; + route?: string; + id?: string; + text?: string; + textClass?: string; + title?: string; + core?: boolean; + enabled?: boolean; + [key: string]: string | string[] | boolean | undefined; +} + +// Interface for the Admin +interface Admin { + save: (data: NavigationItem[]) => Promise; + getAdmin: () => Promise<{ enabled: NavigationItem[], available: NavigationItem[] }>; + escapeFields: (navItems: NavigationItem[]) => void; + unescapeFields: (navItems: NavigationItem[]) => void; + get: () => Promise; +} + +const admin: Admin = {} as Admin; +let cache: NavigationItem[] | null = null; + +pubsub.on('admin:navigation:save', () => { + cache = null; +}); + +admin.save = async function (data: NavigationItem[]): Promise { + const order: string[] = Object.keys(data); + const bulkSet: [string, NavigationItem][] = []; + data.forEach((item, index) => { + item.order = order[index]; + if (item.hasOwnProperty('groups')) { + item.groups = JSON.stringify(item.groups); + } + bulkSet.push([`navigation:enabled:${item.order}`, item]); + }); + + cache = null; + pubsub.publish('admin:navigation:save'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + const ids = await db.getSortedSetRange('navigation:enabled', 0, -1) as string[]; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + await db.deleteAll(ids.map(id => `navigation:enabled:${id}`)); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + await db.setObjectBulk(bulkSet); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + await db.delete('navigation:enabled'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + await db.sortedSetAdd('navigation:enabled', order, order); +}; + +const fieldsToEscape = ['iconClass', 'class', 'route', 'id', 'text', 'textClass', 'title']; + +function toggleEscape(navItems: NavigationItem[], flag: boolean): void { + navItems.forEach((item) => { + if (item) { + fieldsToEscape.forEach((field) => { + if (item.hasOwnProperty(field)) { + item[field] = validator[flag ? 'escape' : 'unescape'](String(item[field])); + } + }); + } + }); +} + +admin.escapeFields = navItems => toggleEscape(navItems, true); +admin.unescapeFields = navItems => toggleEscape(navItems, false); + +admin.get = async function (): Promise { + if (cache) { + return cache.map(item => ({ ...item })); + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + const ids = await db.getSortedSetRange('navigation:enabled', 0, -1) as string[]; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + const data = await db.getObjects(ids.map(id => `navigation:enabled:${id}`)) as NavigationItem[]; + cache = data.filter(Boolean).map((item) => { + if (item.hasOwnProperty('groups')) { + try { + item.groups = JSON.parse(item.groups as string) as string[]; + } catch (err) { + if (err instanceof Error) { + winston.error(err.stack); + } else { + winston.error('Unknown error', err); + } + item.groups = []; + } + } + item.groups = item.groups || []; + if (item.groups && !Array.isArray(item.groups)) { + item.groups = [item.groups]; + } + return item; + }); + admin.escapeFields(cache); + + return cache.map(item => ({ ...item })); +}; + +async function getAvailable(): Promise { + const core: NavigationItem[] = navigationData.map((item: NavigationItem) => { + item.core = true; + item.id = item.id || ''; + return item; + }); + + const navItems = await plugins.hooks.fire('filter:navigation.available', core) as NavigationItem[]; + navItems.forEach((item) => { + if (item && !item.hasOwnProperty('enabled')) { + item.enabled = true; + } + }); + return navItems; +} + +admin.getAdmin = async function () { + const [enabled, available] = await Promise.all([ + admin.get(), + getAvailable(), + ]); + return { enabled: enabled, available: available }; +}; + + +// eslint-disable-next-line @typescript-eslint/no-unsafe-call +promisify(admin); + +module.exports = admin; diff --git a/tsconfig.json b/tsconfig.json index 10aeeef7e6..5fce0e7548 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,13 +5,14 @@ "module": "commonjs", "moduleResolution": "node", "esModuleInterop": true, + "resolveJsonModule": true, }, "include": [ "public/src/**/*", - "src/**/*", + "src/**/*", "test/**/*", ], - "exclude":[ + "exclude": [ "node_modules", ] } \ No newline at end of file