- {categories.map(
+ {categories.filter((category : Category) => !config.hideEmptyCategories || category.apps.length > 0).map(
(category: Category): JSX.Element => (
{
// Get Redux action creators
const dispatch = useDispatch();
- const { getCategories, setEditCategory, setEditApp } =
+ const { setEditCategory, setEditApp } =
bindActionCreators(actionCreators, dispatch);
- // Load categories if array is empty
- useEffect(() => {
- if (!categories.length) {
- getCategories();
- }
- }, []);
-
// Form
const [modalIsOpen, setModalIsOpen] = useState(false);
const [formContentType, setFormContentType] = useState(ContentType.category);
diff --git a/client/src/components/Bookmarks/BookmarkGrid/BookmarkGrid.tsx b/client/src/components/Bookmarks/BookmarkGrid/BookmarkGrid.tsx
index 6ed99b9..5ab2f88 100644
--- a/client/src/components/Bookmarks/BookmarkGrid/BookmarkGrid.tsx
+++ b/client/src/components/Bookmarks/BookmarkGrid/BookmarkGrid.tsx
@@ -1,6 +1,8 @@
+import { useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import { Category } from '../../../interfaces';
+import { State } from '../../../store/reducers';
import { Message } from '../../UI';
import { BookmarkCard } from '../BookmarkCard/BookmarkCard';
import classes from './BookmarkGrid.module.css';
@@ -20,6 +22,10 @@ export const BookmarkGrid = (props: Props): JSX.Element => {
fromHomepage = false,
} = props;
+ const {
+ config: { config }
+ } = useSelector((state: State) => state);
+
let bookmarks: JSX.Element;
if (categories.length) {
@@ -28,7 +34,7 @@ export const BookmarkGrid = (props: Props): JSX.Element => {
} else {
bookmarks = (
- {categories.map(
+ {categories.filter((category : Category) => !config.hideEmptyCategories || category.apps.length > 0).map(
(category: Category): JSX.Element => (
{
// Get Redux action creators
const dispatch = useDispatch();
- const { getCategories, setEditCategory, setEditBookmark } =
+ const { setEditCategory, setEditBookmark } =
bindActionCreators(actionCreators, dispatch);
- // Load categories if array is empty
- useEffect(() => {
- if (!categories.length) {
- getCategories();
- }
- }, []);
-
// Form
const [modalIsOpen, setModalIsOpen] = useState(false);
const [formContentType, setFormContentType] = useState(ContentType.category);
diff --git a/client/src/components/Settings/UISettings/UISettings.tsx b/client/src/components/Settings/UISettings/UISettings.tsx
index 7843998..a78f475 100644
--- a/client/src/components/Settings/UISettings/UISettings.tsx
+++ b/client/src/components/Settings/UISettings/UISettings.tsx
@@ -283,7 +283,7 @@ export const UISettings = (): JSX.Element => {
- {/* HIDE CATEGORIES */}
+ {/* HIDE BOOKMARKS */}
+ {/* HIDE EMPTY CATEGORIES */}
+
+
+
+
+
);
diff --git a/client/src/interfaces/Config.ts b/client/src/interfaces/Config.ts
index 825535b..97523c3 100644
--- a/client/src/interfaces/Config.ts
+++ b/client/src/interfaces/Config.ts
@@ -16,6 +16,7 @@ export interface Config {
searchSameTab: boolean;
hideApps: boolean;
hideBookmarks: boolean;
+ hideEmptyCategories: boolean;
hideSearch: boolean;
defaultSearchProvider: string;
dockerApps: boolean;
diff --git a/client/src/interfaces/Forms.ts b/client/src/interfaces/Forms.ts
index 8b961d3..74244a3 100644
--- a/client/src/interfaces/Forms.ts
+++ b/client/src/interfaces/Forms.ts
@@ -23,6 +23,7 @@ export interface OtherSettingsForm {
hideHeader: boolean;
hideApps: boolean;
hideBookmarks: boolean;
+ hideEmptyCategories: boolean;
useOrdering: string;
appsSameTab: boolean;
bookmarksSameTab: boolean;
diff --git a/client/src/store/reducers/app.ts b/client/src/store/reducers/app.ts
index 6bbccea..9ebd16a 100644
--- a/client/src/store/reducers/app.ts
+++ b/client/src/store/reducers/app.ts
@@ -5,8 +5,6 @@ import { Action } from '../actions';
import { categoriesReducer, CategoriesState } from './category';
interface AppsState extends CategoriesState {
- loading: boolean;
- errors: string | undefined;
categories: Category[];
categoryInEdit: Category | null;
appInEdit: App | null;
diff --git a/client/src/store/reducers/bookmark.ts b/client/src/store/reducers/bookmark.ts
index 1a7638d..7accfe3 100644
--- a/client/src/store/reducers/bookmark.ts
+++ b/client/src/store/reducers/bookmark.ts
@@ -5,8 +5,6 @@ import { Action } from '../actions';
import { categoriesReducer, CategoriesState } from './category';
interface BookmarksState extends CategoriesState {
- loading: boolean;
- errors: string | undefined;
categories: Category[];
categoryInEdit: Category | null;
bookmarkInEdit: Bookmark | null;
diff --git a/client/src/utility/templateObjects/configTemplate.ts b/client/src/utility/templateObjects/configTemplate.ts
index b1bc91c..c59412d 100644
--- a/client/src/utility/templateObjects/configTemplate.ts
+++ b/client/src/utility/templateObjects/configTemplate.ts
@@ -16,6 +16,7 @@ export const configTemplate: Config = {
searchSameTab: false,
hideApps: false,
hideBookmarks: false,
+ hideEmptyCategories: true,
hideSearch: false,
defaultSearchProvider: 'l',
dockerApps: false,
diff --git a/client/src/utility/templateObjects/settingsTemplate.ts b/client/src/utility/templateObjects/settingsTemplate.ts
index db3e689..1a898e0 100644
--- a/client/src/utility/templateObjects/settingsTemplate.ts
+++ b/client/src/utility/templateObjects/settingsTemplate.ts
@@ -8,6 +8,7 @@ export const otherSettingsTemplate: OtherSettingsForm = {
hideHeader: false,
hideApps: false,
hideBookmarks: false,
+ hideEmptyCategories: true,
useOrdering: 'createdAt',
appsSameTab: false,
bookmarksSameTab: false,
diff --git a/controllers/apps/docker/useDocker.js b/controllers/apps/docker/useDocker.js
index 7d5d715..4b1b641 100644
--- a/controllers/apps/docker/useDocker.js
+++ b/controllers/apps/docker/useDocker.js
@@ -115,7 +115,7 @@ const useDocker = async (apps) => {
const icons = labels['flame.icon'] ? labels['flame.icon'].split(';') : [];
for (let i = 0; i < names.length; i++) {
- const category = categoriesLabels[i] ? categories.find(category => category.name.toUpperCase() === categoriesLabels[i].toUpperCase()) : dockerDefaultCategory;
+ let category = categoriesLabels[i] ? categories.find(category => category.name.toUpperCase() === categoriesLabels[i].toUpperCase()) : dockerDefaultCategory;
if (!category) {
category = await createNewCategory(categoriesLabels[i]);
if (category) {
@@ -129,7 +129,7 @@ const useDocker = async (apps) => {
name: names[i] || names[0],
url: urls[i] || urls[0],
icon: icons[i] || 'docker',
- category: category.id,
+ categoryId: category.id,
orderId: orders[i] || 500,
});
}
@@ -141,7 +141,6 @@ const useDocker = async (apps) => {
await app.update({ isPinned: false });
}
}
-
for (const item of dockerApps) {
// If app already exists, update it
if (apps.some((app) => app.name === item.name)) {
diff --git a/controllers/apps/docker/useKubernetes.js b/controllers/apps/docker/useKubernetes.js
index 2bb5f83..a70ed81 100644
--- a/controllers/apps/docker/useKubernetes.js
+++ b/controllers/apps/docker/useKubernetes.js
@@ -67,7 +67,7 @@ const useKubernetes = async (apps) => {
const icons = annotations['flame.pawelmalak/icon'] ? annotations['flame.pawelmalak/icon'].split(';') : [];
for (let i = 0; i < names.length; i++) {
- const category = categoriesLabels[i] ? categories.find(category => category.name.toUpperCase() === categoriesLabels[i].toUpperCase()) : kubernetesDefaultCategory;
+ let category = categoriesLabels[i] ? categories.find(category => category.name.toUpperCase() === categoriesLabels[i].toUpperCase()) : kubernetesDefaultCategory;
if (!category) {
category = await createNewCategory(categoriesLabels[i]);
if (category) {
@@ -81,7 +81,7 @@ const useKubernetes = async (apps) => {
name: names[i] || names[0],
url: urls[i] || urls[0],
icon: icons[i] || 'kubernetes',
- category: category.id,
+ categoryId: category.id,
orderId: orders[i] || 500,
});
}
diff --git a/controllers/apps/getAllApps.js b/controllers/apps/getAllApps.js
index 846850b..59f9b60 100644
--- a/controllers/apps/getAllApps.js
+++ b/controllers/apps/getAllApps.js
@@ -10,12 +10,11 @@ const { useKubernetes, useDocker } = require('./docker');
// @access Public
const getAllApps = asyncWrapper(async (req, res, next) => {
const {
- useOrdering: orderType,
- dockerApps: useDockerAPI,
- kubernetesApps: useKubernetesAPI,
+ useOrdering: orderType
} = await loadConfig();
- let apps = await loadIntegrationsApps();
+ // Load apps to create/update apps from integrations (Docker, Kubernetes, etc.)
+ await initIntegrationsApps();
// apps visibility
const where = req.isAuthenticated ? {} : { isPublic: true };
@@ -44,26 +43,4 @@ const getAllApps = asyncWrapper(async (req, res, next) => {
});
});
-const loadIntegrationsApps = asyncWrapper(async () => {
- const {
- dockerApps: useDockerAPI,
- kubernetesApps: useKubernetesAPI,
- } = await loadConfig();
-
- let apps;
-
- if (useDockerAPI) {
- await useDocker(apps);
- }
-
- if (useKubernetesAPI) {
- await useKubernetes(apps);
- }
-
- return apps;
-});
-
-module.exports = {
- getAllApps,
- loadIntegrationsApps
-}
+module.exports = getAllApps;
diff --git a/controllers/apps/getIntegrationsApps.js b/controllers/apps/getIntegrationsApps.js
new file mode 100644
index 0000000..b71b608
--- /dev/null
+++ b/controllers/apps/getIntegrationsApps.js
@@ -0,0 +1,42 @@
+const asyncWrapper = require('../../middleware/asyncWrapper');
+const App = require('../../models/App');
+const { Sequelize } = require('sequelize');
+const loadConfig = require('../../utils/loadConfig');
+
+const { useKubernetes, useDocker } = require('./docker');
+
+// @desc Get all apps
+// @route GET /api/integrationsApps
+// @access Public
+const getIntegrationsApps = asyncWrapper(async (req, res, next) => {
+ const {
+ useOrdering: orderType,
+ dockerApps: useDockerAPI,
+ kubernetesApps: useKubernetesAPI,
+ } = await loadConfig();
+
+ let apps;
+
+ if (useDockerAPI) {
+ await useDocker(apps);
+ }
+
+ if (useKubernetesAPI) {
+ await useKubernetes(apps);
+ }
+
+ if (process.env.NODE_ENV === 'production') {
+ // Set header to fetch containers info every time
+ return res.status(200).setHeader('Cache-Control', 'no-store').json({
+ success: true,
+ data: apps,
+ });
+ }
+
+ res.status(200).json({
+ success: true,
+ data: apps,
+ });
+});
+
+module.exports = getIntegrationsApps;
diff --git a/controllers/apps/index.js b/controllers/apps/index.js
index 91f7f00..01873b3 100644
--- a/controllers/apps/index.js
+++ b/controllers/apps/index.js
@@ -4,5 +4,5 @@ module.exports = {
deleteApp: require('./deleteApp'),
updateApp: require('./updateApp'),
reorderApps: require('./reorderApps'),
- getAllApps: require('./getAllApps').getAllApps,
+ getAllApps: require('./getAllApps'),
};
diff --git a/controllers/categories/getAllCategories.js b/controllers/categories/getAllCategories.js
index 3ec1cb5..8cc13ec 100644
--- a/controllers/categories/getAllCategories.js
+++ b/controllers/categories/getAllCategories.js
@@ -4,7 +4,7 @@ const App = require('../../models/App');
const Bookmark = require('../../models/Bookmark');
const { Sequelize } = require('sequelize');
const loadConfig = require('../../utils/loadConfig');
-const loadIntegrationsApps = require('../apps/getAllApps').loadIntegrationsApps;
+const initIntegrationsApps = require('../../utils/init/initIntegrationsApps');
// @desc Get all categories
// @route GET /api/categories
@@ -12,8 +12,8 @@ const loadIntegrationsApps = require('../apps/getAllApps').loadIntegrationsApps;
const getAllCategories = asyncWrapper(async (req, res, next) => {
const { useOrdering: orderType } = await loadConfig();
- // Load apps to create apps from integrations (Docker, Kubernetes, etc.)
- await loadIntegrationsApps();
+ // Load apps to create/update apps from integrations (Docker, Kubernetes, etc.)
+ await initIntegrationsApps();
let categories;
let output;
diff --git a/server.js b/server.js
index c6f25f9..7302fdc 100644
--- a/server.js
+++ b/server.js
@@ -13,6 +13,7 @@ const Sockets = require('./Sockets');
// Utils
const initApp = require('./utils/init');
+const initIntegrationsApps = require('./utils/init/initIntegrationsApps');
const Logger = require('./utils/Logger');
const logger = new Logger();
@@ -22,7 +23,10 @@ const logger = new Logger();
// Init app
await initApp();
await connectDB();
- await associateModels();
+ await associateModels();
+
+ // Load apps to create/update apps from integrations (Docker, Kubernetes, etc.)
+ await initIntegrationsApps();
// Create server for Express API and WebSockets
const server = http.createServer();
diff --git a/utils/init/initIntegrationsApps.js b/utils/init/initIntegrationsApps.js
new file mode 100644
index 0000000..e04282f
--- /dev/null
+++ b/utils/init/initIntegrationsApps.js
@@ -0,0 +1,24 @@
+const { useKubernetes, useDocker } = require('../../controllers/apps/docker');
+const asyncWrapper = require('../../middleware/asyncWrapper');
+const loadConfig = require('../loadConfig');
+
+const loadIntegrationsApps = asyncWrapper(async () => {
+ const {
+ dockerApps: useDockerAPI,
+ kubernetesApps: useKubernetesAPI,
+ } = await loadConfig();
+
+ let apps;
+
+ if (useDockerAPI) {
+ await useDocker(apps);
+ }
+
+ if (useKubernetesAPI) {
+ await useKubernetes(apps);
+ }
+
+ return apps;
+});
+
+module.exports = loadIntegrationsApps;
diff --git a/utils/init/initialConfig.json b/utils/init/initialConfig.json
index 3f7625e..a01ff1e 100644
--- a/utils/init/initialConfig.json
+++ b/utils/init/initialConfig.json
@@ -14,6 +14,7 @@
"searchSameTab": false,
"hideApps": false,
"hideBookmarks": false,
+ "hideEmptyCategories": true,
"hideSearch": false,
"defaultSearchProvider": "l",
"dockerApps": false,