mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
websocket can now be disabled, the websocket ping interval and the tree reload interval can now be configured (#1132)
This commit is contained in:
@@ -190,6 +190,9 @@ export interface ServerInfo {
|
||||
googleAnalyticsCode?: string
|
||||
smtpEnabled: boolean
|
||||
demoAccountEnabled: boolean
|
||||
websocketEnabled: boolean
|
||||
websocketPingInterval: number
|
||||
treeReloadInterval: number
|
||||
}
|
||||
|
||||
export interface Settings {
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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 = (
|
||||
<Center>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user