diff --git a/.github/workflows/self-hosted-docker-build.yml b/.github/workflows/self-hosted-docker-build.yml
index bce8703a..330ab982 100644
--- a/.github/workflows/self-hosted-docker-build.yml
+++ b/.github/workflows/self-hosted-docker-build.yml
@@ -27,7 +27,7 @@ jobs:
run: |
# 保留 latest 和 previous,删除其他 huntly 镜像
docker images lcomplete/huntly --format "table {{.Repository}}:{{.Tag}}" | grep -v "latest\|previous\|REPOSITORY" | xargs -r docker rmi || true
- continue-on-error: true
+ continue-on-error: false
- name: Ensure data directory exists
run: |
diff --git a/app/client/src/components/SettingModal/BatchOrganizeSetting.tsx b/app/client/src/components/SettingModal/BatchOrganizeSetting.tsx
index ad730f78..851d39a5 100644
--- a/app/client/src/components/SettingModal/BatchOrganizeSetting.tsx
+++ b/app/client/src/components/SettingModal/BatchOrganizeSetting.tsx
@@ -262,11 +262,11 @@ export default function BatchOrganizeSetting() {
{filterResult.items.length > 0 && (
<>
- {filterResult.totalCount > 5 && (
-
- )}
+
>
)}
diff --git a/app/server/huntly-server/src/main/java/com/huntly/server/connector/ConnectorProperties.java b/app/server/huntly-server/src/main/java/com/huntly/server/connector/ConnectorProperties.java
index 57941d73..ea7b479d 100644
--- a/app/server/huntly-server/src/main/java/com/huntly/server/connector/ConnectorProperties.java
+++ b/app/server/huntly-server/src/main/java/com/huntly/server/connector/ConnectorProperties.java
@@ -13,12 +13,16 @@
@Setter
public class ConnectorProperties {
private Instant lastFetchAt;
-
+
private String subscribeUrl;
-
+
private String apiToken;
private Boolean crawlFullContent;
-
+
private ProxySetting proxySetting;
+
+ private String httpEtag;
+
+ private String httpLastModified;
}
diff --git a/app/server/huntly-server/src/main/java/com/huntly/server/connector/FetchPagesResult.java b/app/server/huntly-server/src/main/java/com/huntly/server/connector/FetchPagesResult.java
new file mode 100644
index 00000000..4ddda2f7
--- /dev/null
+++ b/app/server/huntly-server/src/main/java/com/huntly/server/connector/FetchPagesResult.java
@@ -0,0 +1,49 @@
+package com.huntly.server.connector;
+
+import com.huntly.interfaces.external.model.CapturePage;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * Result of fetching pages from a connector, including HTTP cache headers.
+ */
+@Getter
+@Setter
+public class FetchPagesResult {
+ /**
+ * The fetched pages, empty if the feed was not modified.
+ */
+ private List pages;
+
+ /**
+ * Whether the feed was not modified (HTTP 304).
+ */
+ private boolean notModified;
+
+ /**
+ * ETag header from the response.
+ */
+ private String httpEtag;
+
+ /**
+ * Last-Modified header from the response.
+ */
+ private String httpLastModified;
+
+ public static FetchPagesResult notModified() {
+ FetchPagesResult result = new FetchPagesResult();
+ result.setNotModified(true);
+ return result;
+ }
+
+ public static FetchPagesResult of(List pages, String httpEtag, String httpLastModified) {
+ FetchPagesResult result = new FetchPagesResult();
+ result.setPages(pages);
+ result.setNotModified(false);
+ result.setHttpEtag(httpEtag);
+ result.setHttpLastModified(httpLastModified);
+ return result;
+ }
+}
diff --git a/app/server/huntly-server/src/main/java/com/huntly/server/connector/InfoConnector.java b/app/server/huntly-server/src/main/java/com/huntly/server/connector/InfoConnector.java
index e5da9b73..48a516fe 100644
--- a/app/server/huntly-server/src/main/java/com/huntly/server/connector/InfoConnector.java
+++ b/app/server/huntly-server/src/main/java/com/huntly/server/connector/InfoConnector.java
@@ -24,4 +24,15 @@ protected HttpClient buildHttpClient(ConnectorProperties properties) {
public abstract List fetchAllPages();
public abstract CapturePage fetchPageContent(CapturePage capturePage);
+
+ /**
+ * Fetch newest pages with HTTP 304 cache support.
+ * Default implementation delegates to fetchNewestPages() without cache support.
+ *
+ * @return FetchPagesResult containing pages and cache headers
+ */
+ public FetchPagesResult fetchNewestPagesWithCache() {
+ List pages = fetchNewestPages();
+ return FetchPagesResult.of(pages, null, null);
+ }
}
diff --git a/app/server/huntly-server/src/main/java/com/huntly/server/connector/rss/FeedFetchResult.java b/app/server/huntly-server/src/main/java/com/huntly/server/connector/rss/FeedFetchResult.java
new file mode 100644
index 00000000..6e20c10b
--- /dev/null
+++ b/app/server/huntly-server/src/main/java/com/huntly/server/connector/rss/FeedFetchResult.java
@@ -0,0 +1,47 @@
+package com.huntly.server.connector.rss;
+
+import com.rometools.rome.feed.synd.SyndFeed;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Result of fetching a feed, containing the parsed feed and HTTP cache headers.
+ */
+@Getter
+@Setter
+public class FeedFetchResult {
+ /**
+ * The parsed feed, null if the response was 304 Not Modified.
+ */
+ private SyndFeed feed;
+
+ /**
+ * Whether the feed was not modified (HTTP 304).
+ */
+ private boolean notModified;
+
+ /**
+ * ETag header from the response.
+ */
+ private String etag;
+
+ /**
+ * Last-Modified header from the response.
+ */
+ private String lastModified;
+
+ public static FeedFetchResult notModified() {
+ FeedFetchResult result = new FeedFetchResult();
+ result.setNotModified(true);
+ return result;
+ }
+
+ public static FeedFetchResult of(SyndFeed feed, String etag, String lastModified) {
+ FeedFetchResult result = new FeedFetchResult();
+ result.setFeed(feed);
+ result.setNotModified(false);
+ result.setEtag(etag);
+ result.setLastModified(lastModified);
+ return result;
+ }
+}
diff --git a/app/server/huntly-server/src/main/java/com/huntly/server/connector/rss/FeedUtils.java b/app/server/huntly-server/src/main/java/com/huntly/server/connector/rss/FeedUtils.java
index 31704db8..fea28c79 100644
--- a/app/server/huntly-server/src/main/java/com/huntly/server/connector/rss/FeedUtils.java
+++ b/app/server/huntly-server/src/main/java/com/huntly/server/connector/rss/FeedUtils.java
@@ -23,23 +23,55 @@
*/
@UtilityClass
public class FeedUtils {
- public static SyndFeed parseFeedUrl(String feedUrl, OkHttpClient client) {
- Request request = new Request.Builder()
- .url(feedUrl)
- .build();
- try(Response response = client.newCall(request).execute()) {
- byte[] xmlBytes = null;
+
+ private static final int HTTP_NOT_MODIFIED = 304;
+
+ /**
+ * Fetch feed with conditional request support (HTTP 304).
+ *
+ * @param feedUrl The feed URL to fetch
+ * @param client The OkHttp client
+ * @param etag The ETag from previous request (can be null)
+ * @param lastModified The Last-Modified from previous request (can be null)
+ * @return FeedFetchResult containing the feed or notModified flag
+ */
+ public static FeedFetchResult fetchFeed(String feedUrl, OkHttpClient client, String etag, String lastModified) {
+ Request.Builder requestBuilder = new Request.Builder().url(feedUrl);
+
+ // Add conditional request headers if available
+ if (StringUtils.isNotBlank(etag)) {
+ requestBuilder.header("If-None-Match", etag);
+ }
+ if (StringUtils.isNotBlank(lastModified)) {
+ requestBuilder.header("If-Modified-Since", lastModified);
+ }
+
+ Request request = requestBuilder.build();
+
+ try (Response response = client.newCall(request).execute()) {
+ // Check for 304 Not Modified
+ if (response.code() == HTTP_NOT_MODIFIED) {
+ return FeedFetchResult.notModified();
+ }
+
if (response.body() == null) {
throw new ConnectorFetchException("xml response null for url: " + feedUrl);
}
- xmlBytes = response.body().bytes();
+ byte[] xmlBytes = response.body().bytes();
Charset encoding = FeedUtils.guessEncoding(xmlBytes);
String xmlString = XmlUtils.removeInvalidXmlCharacters(new String(xmlBytes, encoding));
if (xmlString == null) {
throw new ConnectorFetchException("xml fetch failed for url: " + feedUrl);
}
- return new SyndFeedInput().build(new StringReader(xmlString));
+
+ SyndFeed feed = new SyndFeedInput().build(new StringReader(xmlString));
+
+ // Extract cache headers from response
+ String responseEtag = response.header("ETag");
+ String responseLastModified = response.header("Last-Modified");
+
+ return FeedFetchResult.of(feed, responseEtag, responseLastModified);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (FeedException e) {
@@ -47,37 +79,48 @@ public static SyndFeed parseFeedUrl(String feedUrl, OkHttpClient client) {
}
}
-// public static SyndFeed parseFeedUrl(String feedUrl, HttpClient client) {
-// HttpRequest request = HttpRequest.newBuilder().GET().uri(URI.create(feedUrl))
-// .build();
-// HttpResponse response = null;
-// try {
-// response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
-// } catch (IOException e) {
-// throw new RuntimeException(e);
-// } catch (InterruptedException e) {
-// throw new RuntimeException(e);
-// }
-// var xmlBytes = response.body();
-// Charset encoding = FeedUtils.guessEncoding(xmlBytes);
-// String xmlString = XmlUtils.removeInvalidXmlCharacters(new String(xmlBytes, encoding));
-// if (xmlString == null) {
-// throw new ConnectorFetchException("xml fetch failed for url: " + feedUrl);
-// }
-//
-// try {
-// SyndFeed feed = new SyndFeedInput().build(new StringReader(xmlString));
-// return feed;
-// } catch (FeedException e) {
-// throw new RuntimeException(e);
-// }
-// }
-
-// public static SyndFeed parseFeedUrl(String feedUrl) {
-// var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(60))
-// .followRedirects(HttpClient.Redirect.ALWAYS).build();
-// return parseFeedUrl(feedUrl, client);
-// }
+ /**
+ * @deprecated Use {@link #fetchFeed(String, OkHttpClient, String, String)} for
+ * HTTP 304 support
+ */
+ @Deprecated
+ public static SyndFeed parseFeedUrl(String feedUrl, OkHttpClient client) {
+ FeedFetchResult result = fetchFeed(feedUrl, client, null, null);
+ return result.getFeed();
+ }
+
+ // public static SyndFeed parseFeedUrl(String feedUrl, HttpClient client) {
+ // HttpRequest request = HttpRequest.newBuilder().GET().uri(URI.create(feedUrl))
+ // .build();
+ // HttpResponse response = null;
+ // try {
+ // response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
+ // } catch (IOException e) {
+ // throw new RuntimeException(e);
+ // } catch (InterruptedException e) {
+ // throw new RuntimeException(e);
+ // }
+ // var xmlBytes = response.body();
+ // Charset encoding = FeedUtils.guessEncoding(xmlBytes);
+ // String xmlString = XmlUtils.removeInvalidXmlCharacters(new String(xmlBytes,
+ // encoding));
+ // if (xmlString == null) {
+ // throw new ConnectorFetchException("xml fetch failed for url: " + feedUrl);
+ // }
+ //
+ // try {
+ // SyndFeed feed = new SyndFeedInput().build(new StringReader(xmlString));
+ // return feed;
+ // } catch (FeedException e) {
+ // throw new RuntimeException(e);
+ // }
+ // }
+
+ // public static SyndFeed parseFeedUrl(String feedUrl) {
+ // var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(60))
+ // .followRedirects(HttpClient.Redirect.ALWAYS).build();
+ // return parseFeedUrl(feedUrl, client);
+ // }
public static Charset guessEncoding(byte[] bytes) {
String extracted = extractDeclaredEncoding(bytes);
diff --git a/app/server/huntly-server/src/main/java/com/huntly/server/connector/rss/RSSConnector.java b/app/server/huntly-server/src/main/java/com/huntly/server/connector/rss/RSSConnector.java
index 03ca2610..b878b5ad 100644
--- a/app/server/huntly-server/src/main/java/com/huntly/server/connector/rss/RSSConnector.java
+++ b/app/server/huntly-server/src/main/java/com/huntly/server/connector/rss/RSSConnector.java
@@ -3,6 +3,7 @@
import com.huntly.common.util.UrlUtils;
import com.huntly.interfaces.external.model.CapturePage;
import com.huntly.server.connector.ConnectorProperties;
+import com.huntly.server.connector.FetchPagesResult;
import com.huntly.server.connector.InfoConnector;
import com.huntly.server.domain.exceptions.ConnectorFetchException;
import com.huntly.server.util.HttpUtils;
@@ -47,31 +48,54 @@ public List fetchAllPages() {
@Override
public List fetchNewestPages() {
+ FetchPagesResult result = fetchNewestPagesWithCache();
+ return result.getPages() != null ? result.getPages() : new ArrayList<>();
+ }
+
+ @Override
+ public FetchPagesResult fetchNewestPagesWithCache() {
if (StringUtils.isBlank(connectorProperties.getSubscribeUrl())) {
- return new ArrayList<>();
+ return FetchPagesResult.of(new ArrayList<>(), null, null);
}
try {
- SyndFeed feed = FeedUtils.parseFeedUrl(connectorProperties.getSubscribeUrl(), okClient);
+ // Use conditional request with cached ETag and Last-Modified
+ FeedFetchResult feedResult = FeedUtils.fetchFeed(
+ connectorProperties.getSubscribeUrl(),
+ okClient,
+ connectorProperties.getHttpEtag(),
+ connectorProperties.getHttpLastModified());
+
+ // If feed was not modified, return early with notModified flag
+ if (feedResult.isNotModified()) {
+ log.debug("Feed not modified (HTTP 304): {}", connectorProperties.getSubscribeUrl());
+ return FetchPagesResult.notModified();
+ }
+
+ SyndFeed feed = feedResult.getFeed();
var entries = feed.getEntries();
List pages = new ArrayList<>();
for (var entry : entries) {
CapturePage capturePage = new CapturePage();
String content = getContent(entry);
- String description = StringUtils.trimToEmpty(entry.getDescription() == null ? null : entry.getDescription().getValue());
+ String description = StringUtils
+ .trimToEmpty(entry.getDescription() == null ? null : entry.getDescription().getValue());
capturePage.setUrl(entry.getLink());
capturePage.setDomain(UrlUtils.getDomainName(entry.getLink()));
capturePage.setContent(content);
capturePage.setDescription(description);
capturePage.setTitle(getTitle(entry));
- capturePage.setConnectedAt(ObjectUtils.firstNonNull(entry.getPublishedDate(), entry.getUpdatedDate(), feed.getPublishedDate(), new Date()).toInstant());
+ capturePage.setConnectedAt(ObjectUtils.firstNonNull(entry.getPublishedDate(), entry.getUpdatedDate(),
+ feed.getPublishedDate(), new Date()).toInstant());
capturePage.setAuthor(StringUtils.trimToEmpty(entry.getAuthor()));
- capturePage.setCategory(entry.getCategories().stream().map(SyndCategory::getName).collect(Collectors.joining(", ")));
+ capturePage.setCategory(
+ entry.getCategories().stream().map(SyndCategory::getName).collect(Collectors.joining(", ")));
capturePage.setNeedFindThumbUrl(true);
pages.add(capturePage);
}
- return pages;
+ // Return pages with cache headers from response
+ return FetchPagesResult.of(pages, feedResult.getEtag(), feedResult.getLastModified());
} catch (Exception e) {
throw new ConnectorFetchException(e);
}
@@ -93,7 +117,8 @@ private String getTitle(SyndEntry item) {
private String getContent(SyndEntry entry) {
String content = null;
if (!entry.getContents().isEmpty()) {
- content = entry.getContents().stream().map(SyndContent::getValue).collect(Collectors.joining(System.lineSeparator()));
+ content = entry.getContents().stream().map(SyndContent::getValue)
+ .collect(Collectors.joining(System.lineSeparator()));
}
return StringUtils.trimToEmpty(content);
}
diff --git a/app/server/huntly-server/src/main/java/com/huntly/server/domain/entity/Connector.java b/app/server/huntly-server/src/main/java/com/huntly/server/domain/entity/Connector.java
index 5fa69286..b916a9ed 100644
--- a/app/server/huntly-server/src/main/java/com/huntly/server/domain/entity/Connector.java
+++ b/app/server/huntly-server/src/main/java/com/huntly/server/domain/entity/Connector.java
@@ -22,7 +22,7 @@ public class Connector implements Serializable {
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
-
+
@Column(name = "type")
private Integer type;
@@ -31,7 +31,7 @@ public class Connector implements Serializable {
@Column(name = "subscribe_url")
private String subscribeUrl;
-
+
@Column(name = "api_token")
private String apiToken;
@@ -64,10 +64,16 @@ public class Connector implements Serializable {
@Column(name = "crawl_full_content")
private Boolean crawlFullContent;
-
+
@Column(name = "is_enabled")
private Boolean enabled;
@Column(name = "created_at")
private Instant createdAt;
+
+ @Column(name = "http_etag")
+ private String httpEtag;
+
+ @Column(name = "http_last_modified")
+ private String httpLastModified;
}
diff --git a/app/server/huntly-server/src/main/java/com/huntly/server/service/ConnectorFetchService.java b/app/server/huntly-server/src/main/java/com/huntly/server/service/ConnectorFetchService.java
index c1f4a2c7..156cc2ce 100644
--- a/app/server/huntly-server/src/main/java/com/huntly/server/service/ConnectorFetchService.java
+++ b/app/server/huntly-server/src/main/java/com/huntly/server/service/ConnectorFetchService.java
@@ -47,7 +47,9 @@ public class ConnectorFetchService {
ThreadPoolExecutor fetchExecutor;
- public ConnectorFetchService(HuntlyProperties huntlyProperties, ConnectorService connectorService, CapturePageService capturePageService, PageArticleContentService pageArticleContentService, EventPublisher eventPublisher, GlobalSettingService globalSettingService, PageService pageService) {
+ public ConnectorFetchService(HuntlyProperties huntlyProperties, ConnectorService connectorService,
+ CapturePageService capturePageService, PageArticleContentService pageArticleContentService,
+ EventPublisher eventPublisher, GlobalSettingService globalSettingService, PageService pageService) {
this.huntlyProperties = huntlyProperties;
this.connectorService = connectorService;
this.capturePageService = capturePageService;
@@ -57,12 +59,13 @@ public ConnectorFetchService(HuntlyProperties huntlyProperties, ConnectorService
inProcessConnectorIds = Collections.synchronizedSet(new HashSet<>());
fetchExecutor = new ThreadPoolExecutor(
- ObjectUtils.defaultIfNull(huntlyProperties.getConnectorFetchCorePoolSize(), AppConstants.DEFAULT_CONNECTOR_FETCH_CORE_POOL_SIZE),
- ObjectUtils.defaultIfNull(huntlyProperties.getConnectorFetchMaxPoolSize(), AppConstants.DEFAULT_CONNECTOR_FETCH_MAX_POOL_SIZE),
+ ObjectUtils.defaultIfNull(huntlyProperties.getConnectorFetchCorePoolSize(),
+ AppConstants.DEFAULT_CONNECTOR_FETCH_CORE_POOL_SIZE),
+ ObjectUtils.defaultIfNull(huntlyProperties.getConnectorFetchMaxPoolSize(),
+ AppConstants.DEFAULT_CONNECTOR_FETCH_MAX_POOL_SIZE),
300, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10000),
- r -> new Thread(r, "connector_fetch_thread")
- );
+ r -> new Thread(r, "connector_fetch_thread"));
}
public void fetchAllConnectPages() {
@@ -118,15 +121,45 @@ public void fetchPagesImmediately(Integer connectorId) {
private void fetchPages(Connector connector) {
var connectorProperties = connectorService.getConnectorProperties(connector.getId());
- InfoConnector infoConnector = InfoConnectorFactory.createInfoConnector(connector.getType(), connectorProperties);
+ InfoConnector infoConnector = InfoConnectorFactory.createInfoConnector(connector.getType(),
+ connectorProperties);
if (infoConnector == null) {
return;
}
- var pages = connector.getLastFetchBeginAt() == null ? infoConnector.fetchAllPages() : infoConnector.fetchNewestPages();
- boolean inboxChangedTriggered = false;
+
boolean isRssFetch = Objects.equals(connector.getType(), ConnectorType.RSS.getCode());
boolean isGithubFetch = Objects.equals(connector.getType(), ConnectorType.GITHUB.getCode());
+ List pages;
+
+ // For RSS feeds, use cache-aware fetching (HTTP 304 support)
+ if (isRssFetch && connector.getLastFetchBeginAt() != null) {
+ var fetchResult = infoConnector.fetchNewestPagesWithCache();
+
+ // If feed was not modified (HTTP 304), we can skip processing
+ if (fetchResult.isNotModified()) {
+ log.info("Feed not modified (HTTP 304), skipping: {}", connector.getName());
+ return;
+ }
+
+ pages = fetchResult.getPages() != null ? fetchResult.getPages() : new ArrayList<>();
+
+ // Update HTTP cache headers for future conditional requests
+ if (StringUtils.isNotBlank(fetchResult.getHttpEtag())
+ || StringUtils.isNotBlank(fetchResult.getHttpLastModified())) {
+ connectorService.updateHttpCacheHeaders(
+ connector.getId(),
+ fetchResult.getHttpEtag(),
+ fetchResult.getHttpLastModified());
+ }
+ } else {
+ // For first fetch or non-RSS connectors, use regular fetch
+ pages = connector.getLastFetchBeginAt() == null ? infoConnector.fetchAllPages()
+ : infoConnector.fetchNewestPages();
+ }
+
+ boolean inboxChangedTriggered = false;
+
for (CapturePage page : pages) {
page.setConnectorId(connector.getId());
String rawContent = page.getContent();
@@ -152,15 +185,18 @@ private void fetchPages(Connector connector) {
}
Page savedPage = null;
- //Avoid frequent updates of RSS articles.
- if (isRssFetch && existPage != null && Objects.equals(existPage.getConnectorId(), page.getConnectorId()) && Objects.equals(existPage.getTitle(), page.getTitle()) && Objects.equals(existPage.getConnectedAt(), page.getConnectedAt())) {
+ // Avoid frequent updates of RSS articles.
+ if (isRssFetch && existPage != null && Objects.equals(existPage.getConnectorId(), page.getConnectorId())
+ && Objects.equals(existPage.getTitle(), page.getTitle())
+ && Objects.equals(existPage.getConnectedAt(), page.getConnectedAt())) {
savedPage = existPage;
} else {
savedPage = capturePageService.save(page);
}
if (isRssFetch && isExecuteFetch) {
- pageArticleContentService.saveContent(savedPage.getId(), rawContent, ArticleContentCategory.RAW_CONTENT);
+ pageArticleContentService.saveContent(savedPage.getId(), rawContent,
+ ArticleContentCategory.RAW_CONTENT);
}
if (savedPage.getMarkRead() == null || Objects.equals(savedPage.getMarkRead(), false)) {
@@ -177,7 +213,8 @@ private void fetchPages(Connector connector) {
// update rss connector site icon
if (isRssFetch) {
if (StringUtils.isBlank(connector.getIconUrl())) {
- var icon = SiteUtils.getFaviconFromHome(connector.getSubscribeUrl(), HttpUtils.buildHttpClient(globalSettingService.getProxySetting(), 10));
+ var icon = SiteUtils.getFaviconFromHome(connector.getSubscribeUrl(),
+ HttpUtils.buildHttpClient(globalSettingService.getProxySetting(), 10));
if (icon != null) {
connectorService.updateIconUrl(connector.getId(), icon.getIconUrl());
}
@@ -199,7 +236,9 @@ private boolean isAtFetchTime(Connector connector) {
if (connector == null) {
return false;
}
- Integer fetchIntervalSeconds = ObjectUtils.defaultIfNull(connector.getFetchIntervalSeconds(), huntlyProperties.getDefaultFeedFetchIntervalSeconds());
- return connector.getLastFetchBeginAt() == null || connector.getLastFetchBeginAt().plusSeconds(fetchIntervalSeconds).isBefore(Instant.now());
+ Integer fetchIntervalSeconds = ObjectUtils.defaultIfNull(connector.getFetchIntervalSeconds(),
+ huntlyProperties.getDefaultFeedFetchIntervalSeconds());
+ return connector.getLastFetchBeginAt() == null
+ || connector.getLastFetchBeginAt().plusSeconds(fetchIntervalSeconds).isBefore(Instant.now());
}
}
diff --git a/app/server/huntly-server/src/main/java/com/huntly/server/service/ConnectorService.java b/app/server/huntly-server/src/main/java/com/huntly/server/service/ConnectorService.java
index 99428300..e3fbab8c 100644
--- a/app/server/huntly-server/src/main/java/com/huntly/server/service/ConnectorService.java
+++ b/app/server/huntly-server/src/main/java/com/huntly/server/service/ConnectorService.java
@@ -42,8 +42,10 @@ public class ConnectorService {
private GlobalSettingService globalSettingService;
- public ConnectorService(HuntlyProperties huntlyProperties, FolderRepository folderRepository, ConnectorSettingRepository connectorSettingRepository,
- ConnectorRepository connectorRepository, PageRepository pageRepository, GlobalSettingService globalSettingService) {
+ public ConnectorService(HuntlyProperties huntlyProperties, FolderRepository folderRepository,
+ ConnectorSettingRepository connectorSettingRepository,
+ ConnectorRepository connectorRepository, PageRepository pageRepository,
+ GlobalSettingService globalSettingService) {
this.huntlyProperties = huntlyProperties;
this.folderRepository = folderRepository;
this.connectorSettingRepository = connectorSettingRepository;
@@ -69,6 +71,8 @@ public ConnectorProperties getConnectorProperties(Integer connectorId) {
properties.setSubscribeUrl(con.getSubscribeUrl());
properties.setLastFetchAt(con.getLastFetchBeginAt());
properties.setProxySetting(globalSettingService.getProxySetting());
+ properties.setHttpEtag(con.getHttpEtag());
+ properties.setHttpLastModified(con.getHttpLastModified());
});
return properties;
}
@@ -95,8 +99,22 @@ public void updateLastFetchEndAt(Integer connectorId, Instant endAt, boolean suc
}
}
+ public void updateHttpCacheHeaders(Integer connectorId, String httpEtag, String httpLastModified) {
+ var connector = connectorRepository.findById(connectorId).orElse(null);
+ if (connector != null) {
+ if (httpEtag != null) {
+ connector.setHttpEtag(httpEtag);
+ }
+ if (httpLastModified != null) {
+ connector.setHttpLastModified(httpLastModified);
+ }
+ connectorRepository.save(connector);
+ }
+ }
+
public Connector saveWhenNotExist(Connector connector) {
- var existsConnector = connectorRepository.findBySubscribeUrlAndType(connector.getSubscribeUrl(), connector.getType());
+ var existsConnector = connectorRepository.findBySubscribeUrlAndType(connector.getSubscribeUrl(),
+ connector.getType());
if (existsConnector.isEmpty()) {
connectorRepository.save(connector);
}
@@ -118,7 +136,8 @@ public FolderConnectorView getFolderConnectorView(boolean onlyEnabled) {
}
private List getFolderFeedConnectors(List folders, List connectors) {
- connectors = connectors.stream().filter(t -> ConnectorType.RSS.getCode().equals(t.getType())).collect(Collectors.toList());
+ connectors = connectors.stream().filter(t -> ConnectorType.RSS.getCode().equals(t.getType()))
+ .collect(Collectors.toList());
List folderConnectorsList = new ArrayList<>();
// add connectors without folder first
@@ -136,7 +155,8 @@ private List getFolderFeedConnectors(List folders, Lis
return folderConnectorsList;
}
- private void fillFolderConnectors(List folderConnectorsList, Folder folder, List childConnectors) {
+ private void fillFolderConnectors(List folderConnectorsList, Folder folder,
+ List childConnectors) {
if (!CollectionUtils.isEmpty(childConnectors)) {
FolderConnectors folderConnectors = new FolderConnectors();
folderConnectors.setId(folder.getId());
@@ -275,8 +295,9 @@ public Connector saveGitHubSetting(GitHubSetting gitHubSetting) {
return null;
}
- var connector = gitHubSetting.getConnectorId() > 0 ?
- connectorRepository.findById(gitHubSetting.getConnectorId()).orElse(null) : null;
+ var connector = gitHubSetting.getConnectorId() > 0
+ ? connectorRepository.findById(gitHubSetting.getConnectorId()).orElse(null)
+ : null;
if (connector == null) {
connector = new Connector();
connector.setCreatedAt(Instant.now());