diff --git a/commafeed-client/src/app/types.ts b/commafeed-client/src/app/types.ts
index c4900b0a..88b6d50d 100644
--- a/commafeed-client/src/app/types.ts
+++ b/commafeed-client/src/app/types.ts
@@ -190,6 +190,9 @@ export interface ServerInfo {
googleAnalyticsCode?: string
smtpEnabled: boolean
demoAccountEnabled: boolean
+ websocketEnabled: boolean
+ websocketPingInterval: number
+ treeReloadInterval: number
}
export interface Settings {
diff --git a/commafeed-client/src/hooks/useWebSocket.ts b/commafeed-client/src/hooks/useWebSocket.ts
index 1615aed7..1d449ecd 100644
--- a/commafeed-client/src/hooks/useWebSocket.ts
+++ b/commafeed-client/src/hooks/useWebSocket.ts
@@ -1,32 +1,37 @@
import { setWebSocketConnected } from "app/slices/server"
import { reloadTree } from "app/slices/tree"
-import { useAppDispatch } from "app/store"
+import { useAppDispatch, useAppSelector } from "app/store"
import { useEffect } from "react"
import WebsocketHeartbeatJs from "websocket-heartbeat-js"
export const useWebSocket = () => {
+ const websocketEnabled = useAppSelector(state => state.server.serverInfos?.websocketEnabled)
+ const websocketPingInterval = useAppSelector(state => state.server.serverInfos?.websocketPingInterval)
const dispatch = useAppDispatch()
useEffect(() => {
- const currentUrl = new URL(window.location.href)
- const wsProtocol = currentUrl.protocol === "http:" ? "ws" : "wss"
- const wsUrl = `${wsProtocol}://${currentUrl.hostname}:${currentUrl.port}/ws`
+ let ws: WebsocketHeartbeatJs | undefined
- const ws = new WebsocketHeartbeatJs({
- url: wsUrl,
- pingMsg: "ping",
- // ping interval, just under a minute to prevent firewalls from closing idle connections
- pingTimeout: 55000,
- })
- ws.onopen = () => dispatch(setWebSocketConnected(true))
- ws.onclose = () => dispatch(setWebSocketConnected(false))
- ws.onmessage = event => {
- const { data } = event
- if (typeof data === "string") {
- if (data.startsWith("new-feed-entries:")) dispatch(reloadTree())
+ if (websocketEnabled && websocketPingInterval) {
+ const currentUrl = new URL(window.location.href)
+ const wsProtocol = currentUrl.protocol === "http:" ? "ws" : "wss"
+ const wsUrl = `${wsProtocol}://${currentUrl.hostname}:${currentUrl.port}/ws`
+
+ ws = new WebsocketHeartbeatJs({
+ url: wsUrl,
+ pingMsg: "ping",
+ pingTimeout: websocketPingInterval,
+ })
+ ws.onopen = () => dispatch(setWebSocketConnected(true))
+ ws.onclose = () => dispatch(setWebSocketConnected(false))
+ ws.onmessage = event => {
+ const { data } = event
+ if (typeof data === "string") {
+ if (data.startsWith("new-feed-entries:")) dispatch(reloadTree())
+ }
}
}
- return () => ws.close()
- }, [dispatch])
+ return () => ws?.close()
+ }, [dispatch, websocketEnabled, websocketPingInterval])
}
diff --git a/commafeed-client/src/pages/app/Layout.tsx b/commafeed-client/src/pages/app/Layout.tsx
index 783e6fbb..ad95fa13 100644
--- a/commafeed-client/src/pages/app/Layout.tsx
+++ b/commafeed-client/src/pages/app/Layout.tsx
@@ -95,6 +95,7 @@ export default function Layout(props: LayoutProps) {
const mobile = useMobile()
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
const webSocketConnected = useAppSelector(state => state.server.webSocketConnected)
+ const treeReloadInterval = useAppSelector(state => state.server.serverInfos?.treeReloadInterval)
const sidebarHidden = props.sidebarWidth === 0
const dispatch = useAppDispatch()
useWebSocket()
@@ -110,12 +111,15 @@ export default function Layout(props: LayoutProps) {
}, [dispatch])
useEffect(() => {
- // reload tree periodically if not receiving websocket events
- const timer = setInterval(() => {
- if (!webSocketConnected) dispatch(reloadTree())
- }, 30000)
+ let timer: number | undefined
+
+ if (!webSocketConnected && treeReloadInterval) {
+ // reload tree periodically if not receiving websocket events
+ timer = window.setInterval(() => dispatch(reloadTree()), treeReloadInterval)
+ }
+
return () => clearInterval(timer)
- }, [dispatch, webSocketConnected])
+ }, [dispatch, webSocketConnected, treeReloadInterval])
const burger = (
diff --git a/commafeed-server/config.dev.yml b/commafeed-server/config.dev.yml
index f99b2490..76f44e88 100644
--- a/commafeed-server/config.dev.yml
+++ b/commafeed-server/config.dev.yml
@@ -78,6 +78,15 @@ app:
# user-agent string that will be used by the http client, leave empty for the default one
userAgent:
+ # enable websocket connection so the server can notify the web client that there are new entries for your feeds
+ websocketEnabled: true
+
+ # interval at which the client will send a ping message on the websocket to keep the connection alive
+ websocketPingInterval: 15m
+
+ # if websocket is disabled or the connection is lost, the client will reload the feed tree at this interval
+ treeReloadInterval: 30s
+
# Database connection
# -------------------
# for MariaDB
diff --git a/commafeed-server/config.yml.example b/commafeed-server/config.yml.example
index 9a0051ba..680e35df 100644
--- a/commafeed-server/config.yml.example
+++ b/commafeed-server/config.yml.example
@@ -79,6 +79,15 @@ app:
# user-agent string that will be used by the http client, leave empty for the default one
userAgent:
+ # enable websocket connection so the server can notify the web client that there are new entries for your feeds
+ websocketEnabled: true
+
+ # interval at which the client will send a ping message on the websocket to keep the connection alive
+ websocketPingInterval: 15m
+
+ # if websocket is disabled or the connection is lost, the client will reload the feed tree at this interval
+ treeReloadInterval: 30s
+
# Database connection
# -------------------
# for MariaDB
diff --git a/commafeed-server/src/main/java/com/commafeed/CommaFeedConfiguration.java b/commafeed-server/src/main/java/com/commafeed/CommaFeedConfiguration.java
index 0e3be9c2..1134081a 100644
--- a/commafeed-server/src/main/java/com/commafeed/CommaFeedConfiguration.java
+++ b/commafeed-server/src/main/java/com/commafeed/CommaFeedConfiguration.java
@@ -13,6 +13,7 @@ import be.tomcools.dropwizard.websocket.WebsocketBundleConfiguration;
import be.tomcools.dropwizard.websocket.WebsocketConfiguration;
import io.dropwizard.core.Configuration;
import io.dropwizard.db.DataSourceFactory;
+import io.dropwizard.util.Duration;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
@@ -62,8 +63,7 @@ public class CommaFeedConfiguration extends Configuration implements WebsocketBu
@Override
public WebsocketConfiguration getWebsocketConfiguration() {
WebsocketConfiguration config = new WebsocketConfiguration();
- // the client sends ping messages every minute, so we can close idle connections a little bit after that
- config.setMaxSessionIdleTimeout(90000L);
+ config.setMaxSessionIdleTimeout(getApplicationSettings().getWebsocketPingInterval().toMilliseconds() + 10000);
return config;
}
@@ -164,6 +164,12 @@ public class CommaFeedConfiguration extends Configuration implements WebsocketBu
private String userAgent;
+ private Boolean websocketEnabled = true;
+
+ private Duration websocketPingInterval = Duration.minutes(15);
+
+ private Duration treeReloadInterval = Duration.seconds(30);
+
public Date getUnreadThreshold() {
int keepStatusDays = getKeepStatusDays();
return keepStatusDays > 0 ? DateUtils.addDays(new Date(), -1 * keepStatusDays) : null;
diff --git a/commafeed-server/src/main/java/com/commafeed/frontend/model/ServerInfo.java b/commafeed-server/src/main/java/com/commafeed/frontend/model/ServerInfo.java
index f28d79d0..0eee703b 100644
--- a/commafeed-server/src/main/java/com/commafeed/frontend/model/ServerInfo.java
+++ b/commafeed-server/src/main/java/com/commafeed/frontend/model/ServerInfo.java
@@ -32,4 +32,13 @@ public class ServerInfo implements Serializable {
@Schema(requiredMode = RequiredMode.REQUIRED)
private boolean demoAccountEnabled;
+ @Schema(requiredMode = RequiredMode.REQUIRED)
+ private boolean websocketEnabled;
+
+ @Schema(requiredMode = RequiredMode.REQUIRED)
+ private long websocketPingInterval;
+
+ @Schema(requiredMode = RequiredMode.REQUIRED)
+ private long treeReloadInterval;
+
}
diff --git a/commafeed-server/src/main/java/com/commafeed/frontend/resource/ServerREST.java b/commafeed-server/src/main/java/com/commafeed/frontend/resource/ServerREST.java
index 0307a3bc..7847f155 100644
--- a/commafeed-server/src/main/java/com/commafeed/frontend/resource/ServerREST.java
+++ b/commafeed-server/src/main/java/com/commafeed/frontend/resource/ServerREST.java
@@ -56,6 +56,9 @@ public class ServerREST {
infos.setGoogleAnalyticsCode(config.getApplicationSettings().getGoogleAnalyticsTrackingCode());
infos.setSmtpEnabled(StringUtils.isNotBlank(config.getApplicationSettings().getSmtpHost()));
infos.setDemoAccountEnabled(config.getApplicationSettings().getCreateDemoAccount());
+ infos.setWebsocketEnabled(config.getApplicationSettings().getWebsocketEnabled());
+ infos.setWebsocketPingInterval(config.getApplicationSettings().getWebsocketPingInterval().toMilliseconds());
+ infos.setTreeReloadInterval(config.getApplicationSettings().getTreeReloadInterval().toMilliseconds());
return Response.ok(infos).build();
}
diff --git a/commafeed-server/src/test/java/com/commafeed/integration/rest/ServerIT.java b/commafeed-server/src/test/java/com/commafeed/integration/rest/ServerIT.java
new file mode 100644
index 00000000..5ec64966
--- /dev/null
+++ b/commafeed-server/src/test/java/com/commafeed/integration/rest/ServerIT.java
@@ -0,0 +1,22 @@
+package com.commafeed.integration.rest;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import com.commafeed.frontend.model.ServerInfo;
+import com.commafeed.integration.BaseIT;
+
+public class ServerIT extends BaseIT {
+
+ @Test
+ void getServerInfos() {
+ ServerInfo serverInfos = getClient().target(getApiBaseUrl() + "server/get").request().get(ServerInfo.class);
+ Assertions.assertTrue(serverInfos.isAllowRegistrations());
+ Assertions.assertTrue(serverInfos.isSmtpEnabled());
+ Assertions.assertTrue(serverInfos.isDemoAccountEnabled());
+ Assertions.assertTrue(serverInfos.isWebsocketEnabled());
+ Assertions.assertEquals(900000, serverInfos.getWebsocketPingInterval());
+ Assertions.assertEquals(30000, serverInfos.getTreeReloadInterval());
+
+ }
+}
diff --git a/commafeed-server/src/test/resources/config.test.yml b/commafeed-server/src/test/resources/config.test.yml
index 2fddc93e..2615483f 100644
--- a/commafeed-server/src/test/resources/config.test.yml
+++ b/commafeed-server/src/test/resources/config.test.yml
@@ -78,6 +78,15 @@ app:
# user-agent string that will be used by the http client, leave empty for the default one
userAgent:
+ # enable websocket connection so the server can notify the web client that there are new entries for your feeds
+ websocketEnabled: true
+
+ # interval at which the client will send a ping message on the websocket to keep the connection alive
+ websocketPingInterval: 15m
+
+ # if websocket is disabled or the connection is lost, the client will reload the feed tree at this interval
+ treeReloadInterval: 30s
+
# Database connection
# -------------------
# for MariaDB