125 lines
4.5 KiB
Java
125 lines
4.5 KiB
Java
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;
|
|
});
|
|
}
|
|
}
|