Files
gala-ki-spielwiese/quarkus-automaton/src/main/java/de/galabau/dateieingang/sftp/SftpService.java

125 lines
4.5 KiB
Java
Raw Normal View History

package de.galabau.dateieingang.sftp;
import de.galabau.dateieingang.exception.SftpException;
import io.quarkus.logging.Log;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.RemoteResourceInfo;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
/** Kapselt alle SFTP-Operationen: Auflisten, Download und Umbenennen. */
@ApplicationScoped
public class SftpService {
@Inject
SftpConfig config;
@PostConstruct
void init() {
try {
Files.createDirectories(Path.of(config.localWorkDir()));
} catch (IOException e) {
throw new RuntimeException("Lokales Arbeitsverzeichnis konnte nicht erstellt werden: "
+ config.localWorkDir(), e);
}
}
@FunctionalInterface
private interface SftpOperation<T> {
T execute(SFTPClient sftp) throws IOException;
}
/**
* Öffnet eine SFTP-Verbindung, führt die Operation aus und trennt danach sauber.
* Credentials und Host-Key-Verifikation werden aus {@link SftpConfig} gelesen.
*/
private <T> T withSftp(SftpOperation<T> operation) throws SftpException {
try (SSHClient ssh = new SSHClient()) {
configureHostKeyVerification(ssh);
ssh.connect(config.host(), config.port());
authenticate(ssh);
try (SFTPClient sftp = ssh.newSFTPClient()) {
return operation.execute(sftp);
}
} catch (IOException e) {
throw new SftpException("SFTP-Operation fehlgeschlagen auf " + config.host()
+ ": " + e.getMessage(), e);
}
}
private void configureHostKeyVerification(SSHClient ssh) {
if (config.hostKeyFingerprint().isPresent()) {
ssh.addHostKeyVerifier(config.hostKeyFingerprint().get());
} else {
Log.warn("SFTP Host-Key-Fingerprint nicht konfiguriert — PromiscuousVerifier aktiv (nur Dev!)");
ssh.addHostKeyVerifier(new PromiscuousVerifier());
}
}
private void authenticate(SSHClient ssh) throws IOException {
if (config.privateKeyPath().isPresent()) {
ssh.authPublickey(config.username(), config.privateKeyPath().get());
} else {
ssh.authPassword(config.username(), config.password());
}
}
/**
* Listet alle {@code *.zip}-Dateien im konfigurierten Remote-Verzeichnis.
*
* @return Liste der Dateinamen (ohne Pfad), z.B. {@code ["export_2026-04-08.zip"]}
* @throws SftpException bei Verbindungs- oder Lesefehler
*/
public List<String> listZipFiles() throws SftpException {
return withSftp(sftp ->
sftp.ls(config.remotePath()).stream()
.filter(RemoteResourceInfo::isRegularFile)
.map(RemoteResourceInfo::getName)
.filter(name -> name.endsWith(".zip"))
.toList()
);
}
/**
* Lädt eine Datei vom SFTP-Server in das lokale Arbeitsverzeichnis herunter.
*
* @param filename Dateiname auf dem Remote-Server, z.B. {@code export_2026-04-08.zip}
* @return Lokaler Pfad der heruntergeladenen Datei
* @throws SftpException bei Verbindungs- oder Downloadfehler
*/
public Path download(String filename) throws SftpException {
Path localFile = Path.of(config.localWorkDir(), filename);
withSftp(sftp -> {
sftp.get(config.remotePath() + "/" + filename, localFile.toString());
return null;
});
return localFile;
}
/**
* Benennt eine Datei auf dem Remote-SFTP-Server um.
* Wird nach Erfolg ({@code .processed}) oder Fehler ({@code .error}) aufgerufen.
*
* @param filename aktueller Dateiname, z.B. {@code export_2026-04-08.zip}
* @param newFilename neuer Dateiname, z.B. {@code export_2026-04-08.zip.processed}
* @throws SftpException bei Verbindungs- oder Umbenennfehler
*/
public void renameRemote(String filename, String newFilename) throws SftpException {
withSftp(sftp -> {
sftp.rename(
config.remotePath() + "/" + filename,
config.remotePath() + "/" + newFilename
);
return null;
});
}
}