mirror of
https://github.com/usatiuk/dhfs.git
synced 2025-10-28 12:37:48 +01:00
API draft to manually set peer addresses
This commit is contained in:
@@ -3,11 +3,9 @@ package com.usatiuk.dhfs.repository;
|
||||
import com.usatiuk.dhfs.PeerId;
|
||||
import com.usatiuk.dhfs.repository.peerdiscovery.PeerAddress;
|
||||
import com.usatiuk.dhfs.repository.peerdiscovery.PeerDiscoveryDirectory;
|
||||
import com.usatiuk.dhfs.repository.peersync.PeerInfo;
|
||||
import com.usatiuk.dhfs.repository.peersync.PeerInfoService;
|
||||
import com.usatiuk.dhfs.repository.peersync.api.PeerSyncApiClientDynamic;
|
||||
import com.usatiuk.dhfs.repository.peertrust.PeerTrustManager;
|
||||
import com.usatiuk.dhfs.repository.webapi.AvailablePeerInfo;
|
||||
import com.usatiuk.objects.transaction.Transaction;
|
||||
import com.usatiuk.objects.transaction.TransactionManager;
|
||||
import io.quarkus.logging.Log;
|
||||
@@ -70,7 +68,7 @@ public class PeerManager {
|
||||
if (_heartbeatExecutor == null) return;
|
||||
try {
|
||||
var peers = peerInfoService.getPeersNoSelf();
|
||||
var pids = peers.stream().map(PeerInfo::id).toList();
|
||||
var pids = peers.stream().map(com.usatiuk.dhfs.repository.peersync.PeerInfo::id).toList();
|
||||
|
||||
List<PeerId> stale = _states.keySet().stream().filter(p -> !pids.contains(p)).toList();
|
||||
stale.forEach(_states.keySet()::remove);
|
||||
@@ -98,7 +96,7 @@ public class PeerManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleConnectionSuccess(PeerInfo host, PeerAddress address) {
|
||||
private void handleConnectionSuccess(com.usatiuk.dhfs.repository.peersync.PeerInfo host, PeerAddress address) {
|
||||
boolean wasReachable = isReachable(host);
|
||||
|
||||
boolean shouldSync = !persistentPeerDataService.isInitialSyncDone(host.id());
|
||||
@@ -120,7 +118,7 @@ public class PeerManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void handleConnectionError(PeerInfo host) {
|
||||
public void handleConnectionError(com.usatiuk.dhfs.repository.peersync.PeerInfo host) {
|
||||
boolean wasReachable = isReachable(host);
|
||||
|
||||
if (wasReachable)
|
||||
@@ -134,7 +132,7 @@ public class PeerManager {
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
private boolean pingCheck(PeerInfo host, PeerAddress address) {
|
||||
private boolean pingCheck(com.usatiuk.dhfs.repository.peersync.PeerInfo host, PeerAddress address) {
|
||||
try {
|
||||
return rpcClientFactory.withObjSyncClient(host.id(), address, pingTimeout, (peer, c) -> {
|
||||
c.ping(PingRequest.getDefaultInstance());
|
||||
@@ -150,7 +148,7 @@ public class PeerManager {
|
||||
return _states.containsKey(host);
|
||||
}
|
||||
|
||||
public boolean isReachable(PeerInfo host) {
|
||||
public boolean isReachable(com.usatiuk.dhfs.repository.peersync.PeerInfo host) {
|
||||
return isReachable(host.id());
|
||||
}
|
||||
|
||||
@@ -170,7 +168,7 @@ public class PeerManager {
|
||||
|
||||
public HostStateSnapshot getHostStateSnapshot() {
|
||||
return transactionManager.run(() -> {
|
||||
var partition = peerInfoService.getPeersNoSelf().stream().map(PeerInfo::id)
|
||||
var partition = peerInfoService.getPeersNoSelf().stream().map(com.usatiuk.dhfs.repository.peersync.PeerInfo::id)
|
||||
.collect(Collectors.partitioningBy(this::isReachable));
|
||||
return new HostStateSnapshot(partition.get(true), partition.get(false));
|
||||
});
|
||||
@@ -201,10 +199,9 @@ public class PeerManager {
|
||||
peerTrustManager.reloadTrustManagerHosts(transactionManager.run(() -> peerInfoService.getPeers().stream().toList())); //FIXME:
|
||||
}
|
||||
|
||||
public Collection<AvailablePeerInfo> getSeenButNotAddedHosts() {
|
||||
public Collection<PeerId> getSeenButNotAddedHosts() {
|
||||
return transactionManager.run(() -> {
|
||||
return peerDiscoveryDirectory.getReachablePeers().stream().filter(p -> !peerInfoService.getPeerInfo(p).isPresent())
|
||||
.map(p -> new AvailablePeerInfo(p.toString())).toList();
|
||||
return peerDiscoveryDirectory.getReachablePeers().stream().filter(p -> !peerInfoService.getPeerInfo(p).isPresent()).toList();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.usatiuk.dhfs.repository;
|
||||
|
||||
import com.usatiuk.dhfs.ShutdownChecker;
|
||||
import com.usatiuk.dhfs.PeerId;
|
||||
import com.usatiuk.dhfs.ShutdownChecker;
|
||||
import com.usatiuk.dhfs.repository.peerdiscovery.IpPeerAddress;
|
||||
import com.usatiuk.dhfs.repository.peerdiscovery.PeerAddressType;
|
||||
import com.usatiuk.dhfs.repository.peersync.PeerInfoService;
|
||||
import com.usatiuk.dhfs.repository.peertrust.PeerTrustManager;
|
||||
import com.usatiuk.objects.transaction.Transaction;
|
||||
@@ -13,6 +15,7 @@ import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.enterprise.event.Observes;
|
||||
import jakarta.inject.Inject;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.pcollections.HashTreePMap;
|
||||
import org.pcollections.HashTreePSet;
|
||||
|
||||
import java.io.File;
|
||||
@@ -23,6 +26,7 @@ import java.nio.file.StandardOpenOption;
|
||||
import java.security.KeyPair;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -70,7 +74,7 @@ public class PersistentPeerDataService {
|
||||
_selfKeyPair = CertificateTools.generateKeyPair();
|
||||
_selfCertificate = CertificateTools.generateCertificate(_selfKeyPair, _selfUuid.toString());
|
||||
|
||||
curTx.put(new PersistentRemoteHostsData(_selfUuid, _selfCertificate, _selfKeyPair, HashTreePSet.empty()));
|
||||
curTx.put(new PersistentRemoteHostsData(_selfUuid, _selfCertificate, _selfKeyPair, HashTreePSet.empty(), HashTreePMap.empty()));
|
||||
peerInfoService.putPeer(_selfUuid, _selfCertificate.getEncoded());
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -153,4 +157,39 @@ public class PersistentPeerDataService {
|
||||
return data.initialSyncDone().contains(peerId);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public List<IpPeerAddress> getPersistentPeerAddresses() {
|
||||
return txm.run(() -> {
|
||||
var data = curTx.get(PersistentRemoteHostsData.class, PersistentRemoteHostsData.KEY).orElse(null);
|
||||
if (data == null) throw new IllegalStateException("Self data not found");
|
||||
return data.persistentPeerAddress().values().stream().toList();
|
||||
});
|
||||
}
|
||||
|
||||
public void addPersistentPeerAddress(PeerId peerId, IpPeerAddress address) {
|
||||
txm.run(() -> {
|
||||
var data = curTx.get(PersistentRemoteHostsData.class, PersistentRemoteHostsData.KEY).orElse(null);
|
||||
if (data == null) throw new IllegalStateException("Self data not found");
|
||||
var newData = data.persistentPeerAddress().plus(peerId, address.withType(PeerAddressType.WAN)); //TODO:
|
||||
curTx.put(data.withPersistentPeerAddress(newData));
|
||||
});
|
||||
}
|
||||
|
||||
public void removePersistentPeerAddress(PeerId peerId) {
|
||||
txm.run(() -> {
|
||||
var data = curTx.get(PersistentRemoteHostsData.class, PersistentRemoteHostsData.KEY).orElse(null);
|
||||
if (data == null) throw new IllegalStateException("Self data not found");
|
||||
var newData = data.persistentPeerAddress().minus(peerId);
|
||||
curTx.put(data.withPersistentPeerAddress(newData));
|
||||
});
|
||||
}
|
||||
|
||||
public IpPeerAddress getPersistentPeerAddress(PeerId peerId) {
|
||||
return txm.run(() -> {
|
||||
var data = curTx.get(PersistentRemoteHostsData.class, PersistentRemoteHostsData.KEY).orElse(null);
|
||||
if (data == null) throw new IllegalStateException("Self data not found");
|
||||
return data.persistentPeerAddress().get(peerId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.usatiuk.dhfs.repository;
|
||||
|
||||
import com.usatiuk.dhfs.PeerId;
|
||||
import com.usatiuk.dhfs.repository.peerdiscovery.IpPeerAddress;
|
||||
import com.usatiuk.objects.JData;
|
||||
import com.usatiuk.objects.JObjectKey;
|
||||
import com.usatiuk.dhfs.PeerId;
|
||||
import org.pcollections.PMap;
|
||||
import org.pcollections.PSet;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -12,7 +14,8 @@ import java.security.cert.X509Certificate;
|
||||
public record PersistentRemoteHostsData(PeerId selfUuid,
|
||||
X509Certificate selfCertificate,
|
||||
KeyPair selfKeyPair,
|
||||
PSet<PeerId> initialSyncDone) implements JData, Serializable {
|
||||
PSet<PeerId> initialSyncDone,
|
||||
PMap<PeerId, IpPeerAddress> persistentPeerAddress) implements JData, Serializable {
|
||||
public static final JObjectKey KEY = JObjectKey.of("self_peer_data");
|
||||
|
||||
@Override
|
||||
@@ -20,9 +23,12 @@ public record PersistentRemoteHostsData(PeerId selfUuid,
|
||||
return KEY;
|
||||
}
|
||||
|
||||
|
||||
public PersistentRemoteHostsData withInitialSyncDone(PSet<PeerId> initialSyncDone) {
|
||||
return new PersistentRemoteHostsData(selfUuid, selfCertificate, selfKeyPair, initialSyncDone);
|
||||
return new PersistentRemoteHostsData(selfUuid, selfCertificate, selfKeyPair, initialSyncDone, persistentPeerAddress);
|
||||
}
|
||||
|
||||
public PersistentRemoteHostsData withPersistentPeerAddress(PMap<PeerId, IpPeerAddress> persistentPeerAddress) {
|
||||
return new PersistentRemoteHostsData(selfUuid, selfCertificate, selfKeyPair, initialSyncDone, persistentPeerAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,4 +6,7 @@ import java.net.InetAddress;
|
||||
|
||||
public record IpPeerAddress(PeerId peer, PeerAddressType type,
|
||||
InetAddress address, int port, int securePort) implements PeerAddress {
|
||||
public IpPeerAddress withType(PeerAddressType type) {
|
||||
return new IpPeerAddress(peer, type, address, port, securePort);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.usatiuk.dhfs.repository.peerdiscovery;
|
||||
|
||||
import com.usatiuk.dhfs.PeerId;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Optional;
|
||||
|
||||
public class PeerAddrStringHelper {
|
||||
|
||||
public static Optional<IpPeerAddress> parse(String addr) {
|
||||
if (addr.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
var split = addr.split(":");
|
||||
try {
|
||||
return Optional.of(new IpPeerAddress(PeerId.of(split[0]), PeerAddressType.LAN, InetAddress.getByName(split[1]),
|
||||
Integer.parseInt(split[2]), Integer.parseInt(split[3])));
|
||||
} catch (UnknownHostException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<IpPeerAddress> parseNoPeer(PeerId peerId, String addr) {
|
||||
if (addr.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
var split = addr.split(":");
|
||||
try {
|
||||
return Optional.of(new IpPeerAddress(peerId, PeerAddressType.LAN, InetAddress.getByName(split[0]),
|
||||
Integer.parseInt(split[1]), Integer.parseInt(split[2])));
|
||||
} catch (UnknownHostException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,9 @@ package com.usatiuk.dhfs.repository.peerdiscovery;
|
||||
|
||||
import com.usatiuk.dhfs.PeerId;
|
||||
|
||||
public interface PeerAddress {
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface PeerAddress extends Serializable {
|
||||
PeerId peer();
|
||||
|
||||
PeerAddressType type();
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.usatiuk.dhfs.repository.peerdiscovery;
|
||||
|
||||
import com.usatiuk.dhfs.repository.PersistentPeerDataService;
|
||||
import io.quarkus.scheduler.Scheduled;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
@ApplicationScoped
|
||||
public class PersistentStaticPeerDiscovery {
|
||||
@Inject
|
||||
PeerDiscoveryDirectory peerDiscoveryDirectory;
|
||||
@Inject
|
||||
PersistentPeerDataService persistentPeerDataService;
|
||||
|
||||
@Scheduled(every = "1s", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
|
||||
public void discoverPeers() {
|
||||
var addrs = persistentPeerDataService.getPersistentPeerAddresses();
|
||||
for (var addr : addrs) {
|
||||
peerDiscoveryDirectory.notifyAddr(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,13 @@
|
||||
package com.usatiuk.dhfs.repository.peerdiscovery;
|
||||
|
||||
import com.usatiuk.dhfs.PeerId;
|
||||
import io.quarkus.scheduler.Scheduled;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ApplicationScoped
|
||||
public class StaticPeerDiscovery {
|
||||
@@ -22,18 +18,8 @@ public class StaticPeerDiscovery {
|
||||
public StaticPeerDiscovery(@ConfigProperty(name = "dhfs.peerdiscovery.static-peers") Optional<String> staticPeers) {
|
||||
var peers = staticPeers.orElse("");
|
||||
_peers = Arrays.stream(peers.split(",")).flatMap(e ->
|
||||
{
|
||||
if (e.isEmpty()) {
|
||||
return Stream.of();
|
||||
}
|
||||
var split = e.split(":");
|
||||
try {
|
||||
return Stream.of(new IpPeerAddress(PeerId.of(split[0]), PeerAddressType.LAN, InetAddress.getByName(split[1]),
|
||||
Integer.parseInt(split[2]), Integer.parseInt(split[3])));
|
||||
} catch (UnknownHostException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}).toList();
|
||||
PeerAddrStringHelper.parse(e).stream()
|
||||
).toList();
|
||||
}
|
||||
|
||||
@Scheduled(every = "1s", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
package com.usatiuk.dhfs.repository.webapi;
|
||||
|
||||
public record AvailablePeerInfo(String uuid) {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.usatiuk.dhfs.repository.webapi;
|
||||
|
||||
public record PeerAddressInfo(String uuid, String address) {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.usatiuk.dhfs.repository.webapi;
|
||||
|
||||
public record PeerInfo(String uuid, String address) {
|
||||
}
|
||||
@@ -12,8 +12,8 @@ import jakarta.ws.rs.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Path("/objects-manage")
|
||||
public class ManagementApi {
|
||||
@Path("/peers-manage")
|
||||
public class PeerManagementApi {
|
||||
@Inject
|
||||
PeerInfoService peerInfoService;
|
||||
@Inject
|
||||
@@ -39,7 +39,20 @@ public class ManagementApi {
|
||||
|
||||
@Path("available-peers")
|
||||
@GET
|
||||
public Collection<AvailablePeerInfo> availablePeers() {
|
||||
return peerManager.getSeenButNotAddedHosts();
|
||||
public Collection<KnownPeerInfo> availablePeers() {
|
||||
return peerManager.getSeenButNotAddedHosts().stream().map(p -> new KnownPeerInfo(p.toString())).toList();
|
||||
}
|
||||
|
||||
@Path("peer-state")
|
||||
@GET
|
||||
public Collection<PeerInfo> peerInfos(Collection<String> peerIdStrings) {
|
||||
return peerIdStrings.stream().map(PeerId::of).map(
|
||||
peerId -> {
|
||||
return new PeerInfo(
|
||||
peerId.toString(),
|
||||
peerManager.getAddress(peerId).toString()
|
||||
);
|
||||
}
|
||||
).toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.usatiuk.dhfs.repository.webapi;
|
||||
|
||||
import com.usatiuk.dhfs.PeerId;
|
||||
import com.usatiuk.dhfs.repository.PeerManager;
|
||||
import com.usatiuk.dhfs.repository.PersistentPeerDataService;
|
||||
import com.usatiuk.dhfs.repository.peerdiscovery.PeerAddrStringHelper;
|
||||
import com.usatiuk.dhfs.repository.peersync.PeerInfoService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Path("/peers-addr-manage")
|
||||
public class PersistentPeerAddressApi {
|
||||
@Inject
|
||||
PeerInfoService peerInfoService;
|
||||
@Inject
|
||||
PeerManager peerManager;
|
||||
@Inject
|
||||
PersistentPeerDataService persistentPeerDataService;
|
||||
|
||||
@Path("{peerId}")
|
||||
@PUT
|
||||
public void addPeerAddress(String peerAddr, @PathParam("peerId") String peerId) {
|
||||
if (peerAddr.isEmpty()) {
|
||||
deletePeerAddress(peerId);
|
||||
return;
|
||||
}
|
||||
persistentPeerDataService.addPersistentPeerAddress(PeerId.of(peerId), PeerAddrStringHelper.parseNoPeer(PeerId.of(peerId), peerAddr).orElseThrow(IllegalArgumentException::new));
|
||||
}
|
||||
|
||||
@Path("{peerId}")
|
||||
@DELETE
|
||||
public void deletePeerAddress(@PathParam("peerId") String peerId) {
|
||||
persistentPeerDataService.removePersistentPeerAddress(PeerId.of(peerId));
|
||||
}
|
||||
|
||||
@Path("{peerId}")
|
||||
@GET
|
||||
public String getPeerAddress(@PathParam("peerId") String peerId) {
|
||||
return persistentPeerDataService.getPersistentPeerAddress(PeerId.of(peerId)).toString();
|
||||
}
|
||||
|
||||
@Path("")
|
||||
@GET
|
||||
public Collection<PeerAddressInfo> getPeerAddresses() {
|
||||
return persistentPeerDataService.getPersistentPeerAddresses()
|
||||
.stream()
|
||||
.map(p -> new PeerAddressInfo(p.peer().toString(), p.address().getHostAddress() + ":" + p.port() + ":" + p.securePort()))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -66,13 +66,13 @@ public class DhfsFuseIT {
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||
@@ -246,7 +246,7 @@ public class DhfsFuseIT {
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request DELETE " +
|
||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo rewritten > /root/dhfs_default/fuse/testf1").getExitCode());
|
||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo jioadsd > /root/dhfs_default/fuse/newfile1").getExitCode());
|
||||
@@ -262,7 +262,7 @@ public class DhfsFuseIT {
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||
|
||||
|
||||
@@ -92,25 +92,25 @@ public class DhfsFusex3IT {
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
var c2curl1 = container2.execInContainer("/bin/sh", "-c",
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
var c2curl3 = container2.execInContainer("/bin/sh", "-c",
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c3uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
var c3curl = container3.execInContainer("/bin/sh", "-c",
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
waitingConsumer3.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 2);
|
||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 2);
|
||||
@@ -191,7 +191,7 @@ public class DhfsFusex3IT {
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request DELETE " +
|
||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
Thread.sleep(10000);
|
||||
|
||||
|
||||
@@ -73,13 +73,13 @@ public class ResyncIT {
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||
@@ -113,13 +113,13 @@ public class ResyncIT {
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||
@@ -153,13 +153,13 @@ public class ResyncIT {
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
||||
"curl --header \"Content-Type: application/json\" " +
|
||||
" --request PUT " +
|
||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
||||
" http://localhost:8080/objects-manage/known-peers");
|
||||
" http://localhost:8080/peers-manage/known-peers");
|
||||
|
||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||
|
||||
4868
webui/package-lock.json
generated
4868
webui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,27 +10,31 @@
|
||||
"browserslist": "> 0.5%, last 2 versions, not dead",
|
||||
"dependencies": {
|
||||
"jwt-decode": "^4.0.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.24.0",
|
||||
"zod": "^3.23.8"
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-router": "^7.4.1",
|
||||
"react-router-dom": "^7.4.1",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"@parcel/resolver-default": {
|
||||
"packageExports": true
|
||||
},
|
||||
"devDependencies": {
|
||||
"@parcel/transformer-sass": "^2.12.0",
|
||||
"@parcel/transformer-typescript-tsc": "^2.12.0",
|
||||
"@parcel/validator-typescript": "^2.12.0",
|
||||
"@types/eslint": "^8.56.10",
|
||||
"@parcel/transformer-sass": "^2.14.4",
|
||||
"@parcel/transformer-typescript-tsc": "^2.14.4",
|
||||
"@parcel/validator-typescript": "^2.14.4",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/eslint-config-prettier": "^6.11.3",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.14.1",
|
||||
"@typescript-eslint/parser": "^7.14.1",
|
||||
"eslint": "^8",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-react": "^7.34.3",
|
||||
"parcel": "^2.12.0",
|
||||
"prettier": "^3.3.2",
|
||||
"@types/react": "^19.0.12",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^8.28.0",
|
||||
"@typescript-eslint/parser": "^8.28.0",
|
||||
"eslint": "^9",
|
||||
"eslint-config-prettier": "^10.1.1",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"parcel": "^2.14.4",
|
||||
"prettier": "^3.5.3",
|
||||
"process": "^0.11.10",
|
||||
"typescript": "^5.5.2"
|
||||
"typescript": "^5.8.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ export interface TPeerAvailableCardProps {
|
||||
|
||||
export function PeerAvailableCard({ peerInfo }: TPeerAvailableCardProps) {
|
||||
const fetcher = useFetcher();
|
||||
|
||||
return (
|
||||
<div className="peerAvailableCard">
|
||||
<div className={"peerInfo"}>
|
||||
@@ -22,8 +21,8 @@ export function PeerAvailableCard({ peerInfo }: TPeerAvailableCardProps) {
|
||||
action={"/home/peers"}
|
||||
>
|
||||
<button type="submit">connect</button>
|
||||
<input name="intent" hidden={true} value={"add_peer"} />
|
||||
<input name="uuid" hidden={true} value={peerInfo.uuid} />
|
||||
<input name="intent" hidden={true} defaultValue={"add_peer"} />
|
||||
<input name="uuid" hidden={true} defaultValue={peerInfo.uuid} />
|
||||
</fetcher.Form>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { TKnownPeerInfoTo } from "./api/dto";
|
||||
|
||||
import "./PeerKnownCard.scss";
|
||||
import { useFetcher } from "react-router-dom";
|
||||
import { useFetcher, useLoaderData } from "react-router-dom";
|
||||
import { LoaderToType } from "./commonPlumbing";
|
||||
import { peerStateLoader } from "./PeerStatePlumbing";
|
||||
|
||||
export interface TPeerKnownCardProps {
|
||||
peerInfo: TKnownPeerInfoTo;
|
||||
@@ -9,12 +11,42 @@ export interface TPeerKnownCardProps {
|
||||
|
||||
export function PeerKnownCard({ peerInfo }: TPeerKnownCardProps) {
|
||||
const fetcher = useFetcher();
|
||||
const loaderData = useLoaderData() as LoaderToType<typeof peerStateLoader>;
|
||||
|
||||
const addr = loaderData.peerAddresses.find(
|
||||
(item) => item.uuid === peerInfo.uuid,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="peerKnownCard">
|
||||
<div className={"peerInfo"}>
|
||||
<span>UUID: </span>
|
||||
<span>{peerInfo.uuid}</span>
|
||||
<div>
|
||||
<span>UUID: </span>
|
||||
<span>{peerInfo.uuid}</span>
|
||||
</div>
|
||||
<div>
|
||||
<fetcher.Form
|
||||
className="actions"
|
||||
method="put"
|
||||
action={"/home/peers"}
|
||||
>
|
||||
<input
|
||||
name="intent"
|
||||
hidden={true}
|
||||
defaultValue={"save_addr"}
|
||||
/>
|
||||
<input
|
||||
name="uuid"
|
||||
hidden={true}
|
||||
defaultValue={peerInfo.uuid}
|
||||
/>
|
||||
<input
|
||||
name="address"
|
||||
defaultValue={addr?.address || ""}
|
||||
/>
|
||||
<button type="submit">save</button>
|
||||
</fetcher.Form>
|
||||
</div>
|
||||
</div>
|
||||
<fetcher.Form
|
||||
className="actions"
|
||||
@@ -22,8 +54,12 @@ export function PeerKnownCard({ peerInfo }: TPeerKnownCardProps) {
|
||||
action={"/home/peers"}
|
||||
>
|
||||
<button type="submit">remove</button>
|
||||
<input name="intent" hidden={true} value={"remove_peer"} />
|
||||
<input name="uuid" hidden={true} value={peerInfo.uuid} />
|
||||
<input
|
||||
name="intent"
|
||||
hidden={true}
|
||||
defaultValue={"remove_peer"}
|
||||
/>
|
||||
<input name="uuid" hidden={true} defaultValue={peerInfo.uuid} />
|
||||
</fetcher.Form>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
getAvailablePeers,
|
||||
getKnownPeers,
|
||||
getPeerAddresses,
|
||||
putKnownPeer,
|
||||
putPeerAddress,
|
||||
removeKnownPeer,
|
||||
} from "./api/PeerState";
|
||||
import { ActionFunctionArgs } from "react-router-dom";
|
||||
@@ -10,10 +12,15 @@ export async function peerStateLoader() {
|
||||
return {
|
||||
availablePeers: await getAvailablePeers(),
|
||||
knownPeers: await getKnownPeers(),
|
||||
peerAddresses: await getPeerAddresses(),
|
||||
};
|
||||
}
|
||||
|
||||
export type PeerStateActionType = "add_peer" | "remove_peer" | unknown;
|
||||
export type PeerStateActionType =
|
||||
| "add_peer"
|
||||
| "remove_peer"
|
||||
| "save_addr"
|
||||
| unknown;
|
||||
|
||||
export async function peerStateAction({ request }: ActionFunctionArgs) {
|
||||
const formData = await request.formData();
|
||||
@@ -22,6 +29,11 @@ export async function peerStateAction({ request }: ActionFunctionArgs) {
|
||||
return await putKnownPeer(formData.get("uuid") as string);
|
||||
} else if (intent === "remove_peer") {
|
||||
return await removeKnownPeer(formData.get("uuid") as string);
|
||||
} else if (intent === "save_addr") {
|
||||
return await putPeerAddress(
|
||||
formData.get("uuid") as string,
|
||||
formData.get("address") as string,
|
||||
);
|
||||
} else {
|
||||
throw new Error("Malformed action: " + JSON.stringify(request));
|
||||
}
|
||||
|
||||
@@ -3,36 +3,73 @@ import {
|
||||
AvailablePeerInfoToResp,
|
||||
KnownPeerInfoToResp,
|
||||
NoContentToResp,
|
||||
PeerAddressInfoToResp,
|
||||
TAvailablePeerInfoArrTo,
|
||||
TAvailablePeerInfoToResp,
|
||||
TKnownPeerInfoArrTo,
|
||||
TKnownPeerInfoToResp,
|
||||
TNoContentToResp,
|
||||
TPeerAddressInfoArrTo,
|
||||
TPeerAddressInfoToResp,
|
||||
} from "./dto";
|
||||
|
||||
export async function getAvailablePeers(): Promise<TAvailablePeerInfoArrTo> {
|
||||
return fetchJSON_throws<
|
||||
TAvailablePeerInfoToResp,
|
||||
typeof AvailablePeerInfoToResp
|
||||
>("/objects-manage/available-peers", "GET", AvailablePeerInfoToResp);
|
||||
>("/peers-manage/available-peers", "GET", AvailablePeerInfoToResp);
|
||||
}
|
||||
|
||||
export async function getKnownPeers(): Promise<TKnownPeerInfoArrTo> {
|
||||
return fetchJSON_throws<TKnownPeerInfoToResp, typeof KnownPeerInfoToResp>(
|
||||
"/objects-manage/known-peers",
|
||||
"/peers-manage/known-peers",
|
||||
"GET",
|
||||
KnownPeerInfoToResp,
|
||||
);
|
||||
}
|
||||
|
||||
export async function putKnownPeer(uuid: string): Promise<TNoContentToResp> {
|
||||
return fetchJSON("/objects-manage/known-peers", "PUT", NoContentToResp, {
|
||||
return fetchJSON("/peers-manage/known-peers", "PUT", NoContentToResp, {
|
||||
uuid,
|
||||
});
|
||||
}
|
||||
|
||||
export async function removeKnownPeer(uuid: string): Promise<TNoContentToResp> {
|
||||
return fetchJSON("/objects-manage/known-peers", "DELETE", NoContentToResp, {
|
||||
return fetchJSON("/peers-manage/known-peers", "DELETE", NoContentToResp, {
|
||||
uuid,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getPeerAddresses(): Promise<TPeerAddressInfoArrTo> {
|
||||
return fetchJSON_throws<
|
||||
TPeerAddressInfoToResp,
|
||||
typeof PeerAddressInfoToResp
|
||||
>("/peers-addr-manage", "GET", PeerAddressInfoToResp);
|
||||
}
|
||||
|
||||
export async function putPeerAddress(
|
||||
uuid: string,
|
||||
address: string,
|
||||
): Promise<TNoContentToResp> {
|
||||
return fetchJSON(
|
||||
`/peers-addr-manage/${uuid}`,
|
||||
"PUT",
|
||||
NoContentToResp,
|
||||
address,
|
||||
);
|
||||
}
|
||||
|
||||
export async function removePeerAddress(
|
||||
uuid: string,
|
||||
): Promise<TNoContentToResp> {
|
||||
return fetchJSON(`/peers-addr-manage/${uuid}`, "DELETE", NoContentToResp);
|
||||
}
|
||||
|
||||
export async function getPeerAddress(
|
||||
uuid: string,
|
||||
): Promise<TPeerAddressInfoToResp> {
|
||||
return fetchJSON_throws<
|
||||
TPeerAddressInfoToResp,
|
||||
typeof PeerAddressInfoToResp
|
||||
>(`/peers-addr-manage/${uuid}`, "GET", PeerAddressInfoToResp);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,19 @@ export type TKnownPeerInfoArrTo = z.infer<typeof KnownPeerInfoArrTo>;
|
||||
export const KnownPeerInfoToResp = CreateAPIResponse(KnownPeerInfoArrTo);
|
||||
export type TKnownPeerInfoToResp = z.infer<typeof KnownPeerInfoToResp>;
|
||||
|
||||
// PeerAddressInfo
|
||||
export const PeerAddressInfoTo = z.object({
|
||||
uuid: z.string(),
|
||||
address: z.string(),
|
||||
});
|
||||
export type TPeerAddressInfoTo = z.infer<typeof PeerAddressInfoTo>;
|
||||
|
||||
export const PeerAddressInfoArrTo = z.array(PeerAddressInfoTo);
|
||||
export type TPeerAddressInfoArrTo = z.infer<typeof PeerAddressInfoArrTo>;
|
||||
|
||||
export const PeerAddressInfoToResp = CreateAPIResponse(PeerAddressInfoArrTo);
|
||||
export type TPeerAddressInfoToResp = z.infer<typeof PeerAddressInfoToResp>;
|
||||
|
||||
// KnownPeerPut
|
||||
export const KnownPeerPutTo = z.object({ uuid: z.string() });
|
||||
export type TKnownPeerPutTo = z.infer<typeof KnownPeerPutTo>;
|
||||
|
||||
@@ -44,14 +44,17 @@ export async function fetchJSON<T, P extends { parse: (arg: string) => T }>(
|
||||
body?: string | Record<string, unknown> | File,
|
||||
headers?: Record<string, string>,
|
||||
): Promise<T> {
|
||||
const reqBody = () =>
|
||||
body instanceof File
|
||||
const reqBody = () => {
|
||||
if (typeof body === "string" || body instanceof String)
|
||||
return body.toString();
|
||||
return body instanceof File
|
||||
? (() => {
|
||||
const fd = new FormData();
|
||||
fd.append("file", body);
|
||||
return fd;
|
||||
})()
|
||||
: JSON.stringify(body);
|
||||
};
|
||||
|
||||
const reqHeaders = () =>
|
||||
body instanceof File
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
],
|
||||
"jsx": "react-jsx",
|
||||
"target": "es2015",
|
||||
"moduleResolution": "Node",
|
||||
"moduleResolution": "bundler",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"sourceMap": true,
|
||||
|
||||
Reference in New Issue
Block a user