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
|
googleAnalyticsCode?: string
|
||||||
smtpEnabled: boolean
|
smtpEnabled: boolean
|
||||||
demoAccountEnabled: boolean
|
demoAccountEnabled: boolean
|
||||||
|
websocketEnabled: boolean
|
||||||
|
websocketPingInterval: number
|
||||||
|
treeReloadInterval: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Settings {
|
export interface Settings {
|
||||||
|
|||||||
@@ -1,32 +1,37 @@
|
|||||||
import { setWebSocketConnected } from "app/slices/server"
|
import { setWebSocketConnected } from "app/slices/server"
|
||||||
import { reloadTree } from "app/slices/tree"
|
import { reloadTree } from "app/slices/tree"
|
||||||
import { useAppDispatch } from "app/store"
|
import { useAppDispatch, useAppSelector } from "app/store"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
import WebsocketHeartbeatJs from "websocket-heartbeat-js"
|
import WebsocketHeartbeatJs from "websocket-heartbeat-js"
|
||||||
|
|
||||||
export const useWebSocket = () => {
|
export const useWebSocket = () => {
|
||||||
|
const websocketEnabled = useAppSelector(state => state.server.serverInfos?.websocketEnabled)
|
||||||
|
const websocketPingInterval = useAppSelector(state => state.server.serverInfos?.websocketPingInterval)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentUrl = new URL(window.location.href)
|
let ws: WebsocketHeartbeatJs | undefined
|
||||||
const wsProtocol = currentUrl.protocol === "http:" ? "ws" : "wss"
|
|
||||||
const wsUrl = `${wsProtocol}://${currentUrl.hostname}:${currentUrl.port}/ws`
|
|
||||||
|
|
||||||
const ws = new WebsocketHeartbeatJs({
|
if (websocketEnabled && websocketPingInterval) {
|
||||||
url: wsUrl,
|
const currentUrl = new URL(window.location.href)
|
||||||
pingMsg: "ping",
|
const wsProtocol = currentUrl.protocol === "http:" ? "ws" : "wss"
|
||||||
// ping interval, just under a minute to prevent firewalls from closing idle connections
|
const wsUrl = `${wsProtocol}://${currentUrl.hostname}:${currentUrl.port}/ws`
|
||||||
pingTimeout: 55000,
|
|
||||||
})
|
ws = new WebsocketHeartbeatJs({
|
||||||
ws.onopen = () => dispatch(setWebSocketConnected(true))
|
url: wsUrl,
|
||||||
ws.onclose = () => dispatch(setWebSocketConnected(false))
|
pingMsg: "ping",
|
||||||
ws.onmessage = event => {
|
pingTimeout: websocketPingInterval,
|
||||||
const { data } = event
|
})
|
||||||
if (typeof data === "string") {
|
ws.onopen = () => dispatch(setWebSocketConnected(true))
|
||||||
if (data.startsWith("new-feed-entries:")) dispatch(reloadTree())
|
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()
|
return () => ws?.close()
|
||||||
}, [dispatch])
|
}, [dispatch, websocketEnabled, websocketPingInterval])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ export default function Layout(props: LayoutProps) {
|
|||||||
const mobile = useMobile()
|
const mobile = useMobile()
|
||||||
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
|
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
|
||||||
const webSocketConnected = useAppSelector(state => state.server.webSocketConnected)
|
const webSocketConnected = useAppSelector(state => state.server.webSocketConnected)
|
||||||
|
const treeReloadInterval = useAppSelector(state => state.server.serverInfos?.treeReloadInterval)
|
||||||
const sidebarHidden = props.sidebarWidth === 0
|
const sidebarHidden = props.sidebarWidth === 0
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
useWebSocket()
|
useWebSocket()
|
||||||
@@ -110,12 +111,15 @@ export default function Layout(props: LayoutProps) {
|
|||||||
}, [dispatch])
|
}, [dispatch])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// reload tree periodically if not receiving websocket events
|
let timer: number | undefined
|
||||||
const timer = setInterval(() => {
|
|
||||||
if (!webSocketConnected) dispatch(reloadTree())
|
if (!webSocketConnected && treeReloadInterval) {
|
||||||
}, 30000)
|
// reload tree periodically if not receiving websocket events
|
||||||
|
timer = window.setInterval(() => dispatch(reloadTree()), treeReloadInterval)
|
||||||
|
}
|
||||||
|
|
||||||
return () => clearInterval(timer)
|
return () => clearInterval(timer)
|
||||||
}, [dispatch, webSocketConnected])
|
}, [dispatch, webSocketConnected, treeReloadInterval])
|
||||||
|
|
||||||
const burger = (
|
const burger = (
|
||||||
<Center>
|
<Center>
|
||||||
|
|||||||
@@ -78,6 +78,15 @@ app:
|
|||||||
# user-agent string that will be used by the http client, leave empty for the default one
|
# user-agent string that will be used by the http client, leave empty for the default one
|
||||||
userAgent:
|
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
|
# Database connection
|
||||||
# -------------------
|
# -------------------
|
||||||
# for MariaDB
|
# for MariaDB
|
||||||
|
|||||||
@@ -79,6 +79,15 @@ app:
|
|||||||
# user-agent string that will be used by the http client, leave empty for the default one
|
# user-agent string that will be used by the http client, leave empty for the default one
|
||||||
userAgent:
|
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
|
# Database connection
|
||||||
# -------------------
|
# -------------------
|
||||||
# for MariaDB
|
# for MariaDB
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import be.tomcools.dropwizard.websocket.WebsocketBundleConfiguration;
|
|||||||
import be.tomcools.dropwizard.websocket.WebsocketConfiguration;
|
import be.tomcools.dropwizard.websocket.WebsocketConfiguration;
|
||||||
import io.dropwizard.core.Configuration;
|
import io.dropwizard.core.Configuration;
|
||||||
import io.dropwizard.db.DataSourceFactory;
|
import io.dropwizard.db.DataSourceFactory;
|
||||||
|
import io.dropwizard.util.Duration;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import jakarta.validation.constraints.Min;
|
import jakarta.validation.constraints.Min;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
@@ -62,8 +63,7 @@ public class CommaFeedConfiguration extends Configuration implements WebsocketBu
|
|||||||
@Override
|
@Override
|
||||||
public WebsocketConfiguration getWebsocketConfiguration() {
|
public WebsocketConfiguration getWebsocketConfiguration() {
|
||||||
WebsocketConfiguration config = new WebsocketConfiguration();
|
WebsocketConfiguration config = new WebsocketConfiguration();
|
||||||
// the client sends ping messages every minute, so we can close idle connections a little bit after that
|
config.setMaxSessionIdleTimeout(getApplicationSettings().getWebsocketPingInterval().toMilliseconds() + 10000);
|
||||||
config.setMaxSessionIdleTimeout(90000L);
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,6 +164,12 @@ public class CommaFeedConfiguration extends Configuration implements WebsocketBu
|
|||||||
|
|
||||||
private String userAgent;
|
private String userAgent;
|
||||||
|
|
||||||
|
private Boolean websocketEnabled = true;
|
||||||
|
|
||||||
|
private Duration websocketPingInterval = Duration.minutes(15);
|
||||||
|
|
||||||
|
private Duration treeReloadInterval = Duration.seconds(30);
|
||||||
|
|
||||||
public Date getUnreadThreshold() {
|
public Date getUnreadThreshold() {
|
||||||
int keepStatusDays = getKeepStatusDays();
|
int keepStatusDays = getKeepStatusDays();
|
||||||
return keepStatusDays > 0 ? DateUtils.addDays(new Date(), -1 * keepStatusDays) : null;
|
return keepStatusDays > 0 ? DateUtils.addDays(new Date(), -1 * keepStatusDays) : null;
|
||||||
|
|||||||
@@ -32,4 +32,13 @@ public class ServerInfo implements Serializable {
|
|||||||
@Schema(requiredMode = RequiredMode.REQUIRED)
|
@Schema(requiredMode = RequiredMode.REQUIRED)
|
||||||
private boolean demoAccountEnabled;
|
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.setGoogleAnalyticsCode(config.getApplicationSettings().getGoogleAnalyticsTrackingCode());
|
||||||
infos.setSmtpEnabled(StringUtils.isNotBlank(config.getApplicationSettings().getSmtpHost()));
|
infos.setSmtpEnabled(StringUtils.isNotBlank(config.getApplicationSettings().getSmtpHost()));
|
||||||
infos.setDemoAccountEnabled(config.getApplicationSettings().getCreateDemoAccount());
|
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();
|
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
|
# user-agent string that will be used by the http client, leave empty for the default one
|
||||||
userAgent:
|
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
|
# Database connection
|
||||||
# -------------------
|
# -------------------
|
||||||
# for MariaDB
|
# for MariaDB
|
||||||
|
|||||||
Reference in New Issue
Block a user