mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 11:13:43 +08:00
95 lines
3.9 KiB
Java
95 lines
3.9 KiB
Java
package vip.mate.wiki.service;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
|
|
import org.springframework.scheduling.annotation.Scheduled;
|
|
import org.springframework.stereotype.Service;
|
|
import vip.mate.wiki.WikiProperties;
|
|
import vip.mate.wiki.model.WikiKnowledgeBaseEntity;
|
|
|
|
/**
|
|
* Watches each KB's configured source directory and auto-ingests new files.
|
|
*
|
|
* <p>Implemented as a periodic, single-owner scan rather than per-node OS file
|
|
* watchers: {@link SchedulerLock} (ShedLock) ensures exactly one instance runs
|
|
* a cycle, so a multi-instance deployment never double-ingests, and a periodic
|
|
* scan is inherently restart-safe (it picks up anything missed while down). The
|
|
* underlying {@link WikiDirectoryScanService} dedups by source path, so a
|
|
* re-scan only ingests genuinely new files; deletes are never propagated.
|
|
* Path validation (symlink resolution + allowed roots) is enforced by the
|
|
* shared validator inside the scan.
|
|
*
|
|
* @author MateClaw Team
|
|
*/
|
|
@Slf4j
|
|
@Service
|
|
public class WikiSourceWatcherService {
|
|
|
|
private final WikiKnowledgeBaseService kbService;
|
|
private final WikiProperties properties;
|
|
private final java.util.List<vip.mate.wiki.source.WikiIngestSourceProvider> sourceProviders;
|
|
|
|
public WikiSourceWatcherService(WikiKnowledgeBaseService kbService,
|
|
WikiProperties properties,
|
|
java.util.List<vip.mate.wiki.source.WikiIngestSourceProvider> sourceProviders) {
|
|
this.kbService = kbService;
|
|
this.properties = properties;
|
|
this.sourceProviders = sourceProviders;
|
|
}
|
|
|
|
/** The registered source-provider types (filesystem ships; api/mq pluggable later). */
|
|
public java.util.List<String> availableSourceTypes() {
|
|
return sourceProviders.stream().map(vip.mate.wiki.source.WikiIngestSourceProvider::sourceType).toList();
|
|
}
|
|
|
|
/** Scheduled entry point — gated by config, serialized across instances. */
|
|
@Scheduled(fixedDelayString = "${mate.wiki.watcher-interval-ms:300000}", initialDelay = 60_000)
|
|
@SchedulerLock(name = "wiki-source-watcher", lockAtMostFor = "PT10M", lockAtLeastFor = "PT30S")
|
|
public void scheduledScan() {
|
|
if (!properties.isWatcherEnabled()) {
|
|
return;
|
|
}
|
|
int added = runScanCycle();
|
|
if (added > 0) {
|
|
log.info("[WikiWatcher] scan cycle ingested {} new file(s)", added);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scan every KB that has a source directory configured and return the total
|
|
* number of new files ingested. Per-KB failures are logged and skipped so
|
|
* one bad directory cannot stall the others.
|
|
*/
|
|
public int runScanCycle() {
|
|
int totalAdded = 0;
|
|
for (WikiKnowledgeBaseEntity kb : kbService.listAll()) {
|
|
vip.mate.wiki.source.WikiIngestSourceProvider provider = providerFor(kb);
|
|
if (provider == null) {
|
|
continue;
|
|
}
|
|
try {
|
|
WikiDirectoryScanService.ScanResult result = provider.sync(kb);
|
|
totalAdded += result.added();
|
|
if (!result.errors().isEmpty()) {
|
|
log.warn("[WikiWatcher] KB {} ({}) sync reported issues: {}",
|
|
kb.getId(), provider.sourceType(), result.errors());
|
|
}
|
|
} catch (Exception e) {
|
|
log.warn("[WikiWatcher] sync failed for KB {} ({}): {}",
|
|
kb.getId(), provider.sourceType(), e.getMessage());
|
|
}
|
|
}
|
|
return totalAdded;
|
|
}
|
|
|
|
/** The first registered provider that supports the KB, or null. */
|
|
public vip.mate.wiki.source.WikiIngestSourceProvider providerFor(WikiKnowledgeBaseEntity kb) {
|
|
for (vip.mate.wiki.source.WikiIngestSourceProvider p : sourceProviders) {
|
|
if (p.supports(kb)) {
|
|
return p;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|