diff --git a/.run/Reposilite.run.xml b/.run/Reposilite.run.xml
index 180746d2b..5e96fd15b 100644
--- a/.run/Reposilite.run.xml
+++ b/.run/Reposilite.run.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/Reposilite.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/Reposilite.java
index e393c6cd1..c730bae77 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/Reposilite.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/Reposilite.java
@@ -54,7 +54,7 @@ public final class Reposilite {
private final Path configurationFile;
private final Path workingDirectory;
private final boolean testEnvEnabled;
- private final StorageProvider storageProvider;
+ private final FileSystemStorageProvider storageProvider;
private final Configuration configuration;
private final ReposiliteContextFactory contextFactory;
private final ReposiliteExecutor executor;
@@ -92,11 +92,11 @@ public final class Reposilite {
this.executor = new ReposiliteExecutor(testEnvEnabled, failureService);
this.tokenService = new TokenService(workingDirectory, storageProvider);
this.statsService = new StatsService(workingDirectory, failureService, storageProvider);
- this.repositoryService = new RepositoryService(workingDirectory, storageProvider);
- this.metadataService = new MetadataService(failureService, storageProvider);
+ this.repositoryService = new RepositoryService();
+ this.metadataService = new MetadataService(failureService);
this.authenticator = new Authenticator(repositoryService, tokenService);
- this.repositoryAuthenticator = new RepositoryAuthenticator(configuration.rewritePathsEnabled, authenticator, repositoryService, storageProvider);
+ this.repositoryAuthenticator = new RepositoryAuthenticator(configuration.rewritePathsEnabled, authenticator, repositoryService);
this.authService = new AuthService(authenticator);
this.deployService = new DeployService(configuration.deployEnabled, configuration.rewritePathsEnabled, authenticator, repositoryService, metadataService, storageProvider);
this.lookupService = new LookupService(repositoryAuthenticator, metadataService, repositoryService, storageProvider);
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/ReposiliteHttpServer.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/ReposiliteHttpServer.java
index e2a8b8d99..3691b053d 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/ReposiliteHttpServer.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/ReposiliteHttpServer.java
@@ -63,11 +63,9 @@ void start(Configuration configuration, Runnable onStart) {
reposilite.getFailureService());
LookupApiEndpoint lookupApiEndpoint = new LookupApiEndpoint(
- configuration.rewritePathsEnabled,
reposilite.getContextFactory(),
reposilite.getRepositoryAuthenticator(),
- reposilite.getRepositoryService(),
- reposilite.getStorageProvider());
+ reposilite.getRepositoryService());
CliController cliController = new CliController(
reposilite.getContextFactory(),
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/ReposiliteUtils.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/ReposiliteUtils.java
index 35c075140..feec075d9 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/ReposiliteUtils.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/ReposiliteUtils.java
@@ -16,6 +16,7 @@
package org.panda_lang.reposilite;
+import org.jetbrains.annotations.Nullable;
import org.panda_lang.reposilite.repository.Repository;
import org.panda_lang.reposilite.repository.RepositoryService;
import org.panda_lang.utilities.commons.StringUtils;
@@ -30,14 +31,12 @@ private ReposiliteUtils() { }
*
* - Remove root slash
* - Remove illegal path modifiers like .. and ~
- * - Insert repository name if missing
*
*
- * @param rewritePathsEnabled determines if path reqriting is enabled
* @param uri the uri to process
* @return the normalized uri
*/
- public static String normalizeUri(boolean rewritePathsEnabled, RepositoryService repositoryService, String uri) {
+ public static String normalizeUri(String uri) {
if (uri.startsWith("/")) {
uri = uri.substring(1);
}
@@ -46,21 +45,29 @@ public static String normalizeUri(boolean rewritePathsEnabled, RepositoryService
return StringUtils.EMPTY;
}
- if (!rewritePathsEnabled) {
- return uri;
+ return uri;
+ }
+
+ public static @Nullable Repository getRepository(boolean rewritePathsEnabled, RepositoryService repositoryService, String uri) {
+ String repositoryName = uri;
+
+ if (repositoryName.startsWith("/")) {
+ repositoryName = repositoryName.substring(1);
}
- if (StringUtils.countOccurrences(uri, "/") <= 1) {
- return uri;
+ if (repositoryName.contains("..") || repositoryName.contains("~") || repositoryName.contains(":") || repositoryName.contains("\\")) {
+ return null;
}
- for (Repository repository : repositoryService.getRepositories()) {
- if (uri.startsWith(repository.getName())) {
- return uri;
- }
+
+ String repository = StringUtils.countOccurrences(repositoryName, "/") > 0
+ ? repositoryName.substring(0, repositoryName.indexOf('/'))
+ : repositoryName;
+
+ if (rewritePathsEnabled && repositoryService.getRepository(repository) == null) {
+ repository = repositoryService.getPrimaryRepository().getName();
}
- return repositoryService.getPrimaryRepository().getName() + "/" + uri;
+ return repositoryService.getRepository(repository);
}
-
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/auth/Session.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/auth/Session.java
index 43ea5f87e..8b8670f93 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/auth/Session.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/auth/Session.java
@@ -19,6 +19,7 @@
import org.panda_lang.reposilite.repository.Repository;
import org.panda_lang.utilities.commons.StringUtils;
+import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
@@ -27,9 +28,9 @@ public final class Session {
public static final String WILDCARD = "*";
private final Token token;
- private final List repositories;
+ private final Collection repositories;
- public Session(Token token, List repositories) {
+ public Session(Token token, Collection repositories) {
this.token = token;
this.repositories = repositories;
}
@@ -59,7 +60,7 @@ public boolean hasPermissionTo(String path) {
return path.startsWith(tokenPath) || path.startsWith(tokenPath + "/");
}
- public List getRepositories() {
+ public Collection getRepositories() {
return repositories;
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/config/Configuration.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/config/Configuration.java
index 5990cdf5d..c658ef523 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/config/Configuration.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/config/Configuration.java
@@ -69,8 +69,8 @@ public final class Configuration implements Serializable {
public String diskQuota = "10GB";
@Description("# List of supported Maven repositories.")
@Description("# First directory on the list is the main (primary) repository.")
- @Description("# Tu mark repository as private, prefix its name with a dot, e.g. \".private\"")
- public List repositories = Arrays.asList("releases", "snapshots");
+ @Description("# Tu mark repository as private, add the \"--private\" flag")
+ public List repositories = Arrays.asList("releases --no-redeploy", "snapshots");
@Description("# Allow to omit name of the main repository in request")
@Description("# e.g. /org/panda-lang/reposilite will be redirected to /releases/org/panda-lang/reposilite")
public Boolean rewritePathsEnabled = true;
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/config/RepositoryConfig.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/config/RepositoryConfig.java
new file mode 100644
index 000000000..7fb16980b
--- /dev/null
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/config/RepositoryConfig.java
@@ -0,0 +1,38 @@
+package org.panda_lang.reposilite.config;
+
+import java.util.regex.Pattern;
+
+public final class RepositoryConfig {
+ private static final Pattern UNESCAPED_SPACE = Pattern.compile("(? T get(RepositoryOption option) {
+ return option.get(this);
+ }
+
+ public static RepositoryConfig parse(String string) {
+ String[] strings = UNESCAPED_SPACE.split(string);
+ String repositoryName = strings[0].startsWith(".") ? strings[0].substring(1) : strings[0];
+ RepositoryConfig config = new RepositoryConfig(repositoryName);
+
+ for (RepositoryOption> option : RepositoryOption.getOptions()) {
+ option.parse(config, strings);
+ }
+
+ return config;
+ }
+}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/config/RepositoryOption.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/config/RepositoryOption.java
new file mode 100644
index 000000000..a0cb6e3c1
--- /dev/null
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/config/RepositoryOption.java
@@ -0,0 +1,118 @@
+package org.panda_lang.reposilite.config;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.panda_lang.reposilite.storage.FileSystemStorageProvider;
+import org.panda_lang.reposilite.storage.S3StorageProvider;
+import org.panda_lang.reposilite.storage.StorageProvider;
+
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public final class RepositoryOption {
+ private static final Map> OPTIONS = new HashMap<>();
+
+ public static final RepositoryOption PRIVATE = create("private", () -> false, strings -> strings[0].startsWith(".") || contains("--private", strings));
+ public static final RepositoryOption NO_REDEPLOY = create("no-redeploy", () -> false, strings -> contains("--no-redeploy", strings)); // TODO Actually prevent redeploy
+ public static final RepositoryOption STORAGE_PROVIDER = create("storage-provider", () -> null, strings -> {
+ String provider = getValue("--storage-provider", strings);
+
+ if (provider != null) {
+ if (provider.equalsIgnoreCase("files")) {
+ String diskQuota = getValue("--disk-quota", strings);
+
+ if (diskQuota == null) {
+ throw new UnsupportedOperationException("'--disk-quota' cannot be null");
+ } else {
+ return FileSystemStorageProvider.of(Paths.get("repositories").resolve(strings[0]), diskQuota);
+ }
+ } else if (provider.equalsIgnoreCase("s3")) {
+ String s3BucketName = getValue("--s3-bucket", strings);
+ String region = getValue("--s3-region", strings);
+
+ if (s3BucketName == null) {
+ throw new UnsupportedOperationException("'--s3-bucket' cannot be null");
+ } else if (region == null) {
+ throw new UnsupportedOperationException("'--s3-region' cannot be null");
+ } else {
+ return new S3StorageProvider(s3BucketName, region);
+ }
+ } else if (provider.equalsIgnoreCase("rest")) {
+ // TODO REST API storage endpoint
+ }
+ }
+
+ throw new UnsupportedOperationException("Storage provider specifier is required");
+ });
+
+ private final String name;
+ private final Map values = new HashMap<>();
+ private final Function defaultValue;
+ private final Function parser;
+
+ private RepositoryOption(String name, Supplier defaultValue, Function parser) {
+ this.name = name;
+ this.defaultValue = config -> defaultValue.get();
+ this.parser = parser;
+ }
+
+ @Override
+ public String toString() {
+ return "RepositoryOption[" + this.name + "]";
+ }
+
+ public T get(RepositoryConfig config) {
+ return this.values.computeIfAbsent(config, this.defaultValue);
+ }
+
+ public void parse(RepositoryConfig config, String[] args) {
+ this.values.put(config, this.parser.apply(args));
+ }
+
+ void put(RepositoryConfig config, T value) {
+ this.values.put(config, value);
+ }
+
+ public static RepositoryOption create(@NotNull String name, Supplier defaultValue, Function parser) {
+ RepositoryOption option = new RepositoryOption<>(name, defaultValue, parser);
+
+ OPTIONS.put(name, option);
+
+ return option;
+ }
+
+ public static RepositoryOption> get(String name) {
+ return OPTIONS.get(name);
+ }
+
+ public static Collection> getOptions() {
+ return OPTIONS.values();
+ }
+
+ private static boolean contains(String name, String[] strings) {
+ // The first arg is the repository name, so we skip it.
+ for (int i = 1; i < strings.length; ++i) {
+ if (strings[i].equals(name)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static @Nullable String getValue(String name, String[] strings) {
+ String key = name + '=';
+ // The first arg is the repository name, so we skip it.
+ for (int i = 1; i < strings.length; ++i) {
+ if (strings[i].startsWith(key)) {
+ return strings[i].substring(key.length());
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/metadata/MetadataService.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/metadata/MetadataService.java
index 5ed729943..37713853d 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/metadata/MetadataService.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/metadata/MetadataService.java
@@ -18,13 +18,15 @@
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import org.apache.http.HttpStatus;
import org.panda_lang.reposilite.error.ErrorDto;
import org.panda_lang.reposilite.error.FailureService;
+import org.panda_lang.reposilite.repository.FileDetailsDto;
import org.panda_lang.reposilite.repository.Repository;
-import org.panda_lang.reposilite.storage.StorageProvider;
import org.panda_lang.reposilite.utils.ArrayUtils;
import org.panda_lang.reposilite.utils.FilesUtils;
import org.panda_lang.utilities.commons.StringUtils;
+import org.panda_lang.utilities.commons.collection.Pair;
import org.panda_lang.utilities.commons.function.Lazy;
import org.panda_lang.utilities.commons.function.Result;
@@ -40,75 +42,73 @@ public final class MetadataService {
.defaultUseWrapper(false)
.build());
- private final Map metadataCache = new HashMap<>();
+ private final Map> metadataCache = new HashMap<>();
private final FailureService failureService;
- private final StorageProvider storageProvider;
- public MetadataService(FailureService failureService, StorageProvider storageProvider) {
+ public MetadataService(FailureService failureService) {
this.failureService = failureService;
- this.storageProvider = storageProvider;
}
- public Result generateMetadata(Repository repository, String[] requested) throws IOException {
- Path metadataFile = repository.getFile(requested);
-
- if (!metadataFile.getFileName().toString().equals("maven-metadata.xml")) {
- return Result.error("Bad request");
+ public Result, ErrorDto> getMetadata(Repository repository, Path requested) throws IOException {
+ if (!requested.getFileName().toString().equals("maven-metadata.xml")) {
+ return Result.error(new ErrorDto(HttpStatus.SC_BAD_REQUEST, "Bad request"));
}
- String cachedContent = metadataCache.get(metadataFile.toString());
+ Pair cachedContent = metadataCache.get(requested);
if (cachedContent != null) {
return Result.ok(cachedContent);
}
- Path artifactDirectory = metadataFile.getParent();
+ Path artifactDirectory = requested.getParent();
- if (storageProvider.exists(artifactDirectory)) {
- return Result.error("Bad request");
+ if (repository.exists(artifactDirectory)) {
+ return Result.error(new ErrorDto(HttpStatus.SC_BAD_REQUEST, "Bad request"));
}
- Result versions = MetadataUtils.toSortedVersions(storageProvider, artifactDirectory);
+ Result versions = MetadataUtils.toSortedVersions(repository, artifactDirectory);
- if (versions.isErr()) return versions.map(p -> "").mapErr(ErrorDto::getMessage);
+ if (versions.isErr()) return versions.map(p -> null);
if (versions.get().length > 0) {
- return generateArtifactMetadata(metadataFile, MetadataUtils.toGroup(requested, 2), artifactDirectory, versions.get());
+ return generateArtifactMetadata(repository, requested, MetadataUtils.toGroup(requested), artifactDirectory, versions.get());
}
- return generateBuildMetadata(metadataFile, MetadataUtils.toGroup(requested, 3), artifactDirectory);
+ return generateBuildMetadata(repository, requested, MetadataUtils.toGroup(requested), artifactDirectory);
}
- private Result generateArtifactMetadata(Path metadataFile, String groupId, Path artifactDirectory, Path[] versions) throws IOException {
+ private Result, ErrorDto> generateArtifactMetadata(Repository repository, Path metadataFile, String groupId, Path artifactDirectory, Path[] versions) throws IOException {
Path latest = Objects.requireNonNull(ArrayUtils.getFirst(versions));
- Versioning versioning = new Versioning(latest.getFileName().toString(), latest.getFileName().toString(), FilesUtils.toNames(versions), null, null, MetadataUtils.toUpdateTime(storageProvider, latest));
+ Versioning versioning = new Versioning(latest.getFileName().toString(), latest.getFileName().toString(), FilesUtils.toNames(versions), null, null, MetadataUtils.toUpdateTime(repository, latest));
Metadata metadata = new Metadata(groupId, artifactDirectory.getFileName().toString(), null, versioning);
- return toMetadataFile(metadataFile, metadata);
+ return toMetadataFile(repository, metadataFile, metadata);
}
- private Result generateBuildMetadata(Path metadataFile, String groupId, Path versionDirectory) throws IOException {
+ private Result, ErrorDto> generateBuildMetadata(Repository repository, Path metadataFile, String groupId, Path versionDirectory) throws IOException {
Path artifactDirectory = versionDirectory.getParent();
- Result builds = MetadataUtils.toSortedBuilds(storageProvider, versionDirectory);
- if (builds.isErr()) return builds.map(p -> "").mapErr(ErrorDto::getMessage);
+ Result builds = MetadataUtils.toSortedBuilds(repository, versionDirectory);
+
+ if (builds.isErr()) return builds.map(p -> null);
Path latestBuild = ArrayUtils.getFirst(builds.get());
if (latestBuild == null) {
- return Result.error("Latest build not found");
+ return Result.error(new ErrorDto(HttpStatus.SC_NOT_FOUND, "Latest build not found"));
}
String name = artifactDirectory.getFileName().toString();
String version = StringUtils.replace(versionDirectory.getFileName().toString(), "-SNAPSHOT", StringUtils.EMPTY);
- String[] identifiers = MetadataUtils.toSortedIdentifiers(name, version, builds.get());
+ String[] identifiers = MetadataUtils.toSortedIdentifiers(repository, name, version, builds.get());
String latestIdentifier = Objects.requireNonNull(ArrayUtils.getFirst(identifiers));
int buildSeparatorIndex = latestIdentifier.lastIndexOf("-");
Versioning versioning;
// snapshot requests
if (buildSeparatorIndex != -1) {
+
// format: timestamp-buildNumber
String latestTimestamp = latestIdentifier.substring(0, buildSeparatorIndex);
String latestBuildNumber = latestIdentifier.substring(buildSeparatorIndex + 1);
@@ -117,12 +117,12 @@ private Result generateBuildMetadata(Path metadataFile, String g
Collection snapshotVersions = new ArrayList<>(builds.get().length);
for (String identifier : identifiers) {
- Path[] buildFiles = MetadataUtils.toBuildFiles(storageProvider, versionDirectory, identifier);
+ Path[] buildFiles = MetadataUtils.toBuildFiles(repository, versionDirectory, identifier);
for (Path buildFile : buildFiles) {
String fileName = buildFile.getFileName().toString();
String value = version + "-" + identifier;
- String updated = MetadataUtils.toUpdateTime(storageProvider, buildFile);
+ String updated = MetadataUtils.toUpdateTime(repository, buildFile);
String extension = fileName
.replace(name + "-", StringUtils.EMPTY)
.replace(value + ".", StringUtils.EMPTY);
@@ -132,31 +132,36 @@ private Result generateBuildMetadata(Path metadataFile, String g
}
}
- versioning = new Versioning(null, null, null, snapshot, snapshotVersions, MetadataUtils.toUpdateTime(storageProvider, latestBuild));
+ versioning = new Versioning(null, null, null, snapshot, snapshotVersions, MetadataUtils.toUpdateTime(repository, latestBuild));
}
else {
String fullVersion = version + "-SNAPSHOT";
- versioning = new Versioning(fullVersion, fullVersion, Collections.singletonList(fullVersion), null, null, MetadataUtils.toUpdateTime(storageProvider, latestBuild));
+ versioning = new Versioning(fullVersion, fullVersion, Collections.singletonList(fullVersion), null, null, MetadataUtils.toUpdateTime(repository, latestBuild));
}
- return toMetadataFile(metadataFile, new Metadata(groupId, name, versionDirectory.getFileName().toString(), versioning));
+ return toMetadataFile(repository, metadataFile, new Metadata(groupId, name, versionDirectory.getFileName().toString(), versioning));
}
- private Result toMetadataFile(Path metadataFile, Metadata metadata) {
+ private Result, ErrorDto> toMetadataFile(Repository repository, Path metadataFile, Metadata metadata) {
try {
String serializedMetadata = XML_MAPPER.get().writeValueAsString(metadata);
- storageProvider.putFile(metadataFile, serializedMetadata.getBytes(StandardCharsets.UTF_8));
- FilesUtils.writeFileChecksums(storageProvider, metadataFile);
- metadataCache.put(metadataFile.toString(), serializedMetadata);
- return Result.ok(serializedMetadata);
+ byte[] bytes = serializedMetadata.getBytes(StandardCharsets.UTF_8);
+ Result result = repository.putFile(metadataFile, bytes);
+
+ if (result.isOk()) {
+ FilesUtils.writeFileChecksums(repository, metadataFile, bytes);
+ metadataCache.put(metadataFile, new Pair<>(result.get(), serializedMetadata));
+ }
+
+ return result.map(fileDetailsDto -> new Pair<>(fileDetailsDto, serializedMetadata));
} catch (IOException e) {
failureService.throwException(metadataFile.toAbsolutePath().toString(), e);
- return Result.error("Cannot generate metadata");
+ return Result.error(new ErrorDto(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Cannot generate metadata"));
}
}
public void clearMetadata(Path metadataFile) {
- metadataCache.remove(metadataFile.toString());
+ metadataCache.remove(metadataFile);
}
public int purgeCache() {
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/metadata/MetadataUtils.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/metadata/MetadataUtils.java
index 3fc09c0b8..c6d5733fc 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/metadata/MetadataUtils.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/metadata/MetadataUtils.java
@@ -17,10 +17,9 @@
package org.panda_lang.reposilite.metadata;
import org.panda_lang.reposilite.error.ErrorDto;
-import org.panda_lang.reposilite.storage.StorageProvider;
+import org.panda_lang.reposilite.repository.Repository;
import org.panda_lang.reposilite.utils.FilesUtils;
import org.panda_lang.utilities.commons.StringUtils;
-import org.panda_lang.utilities.commons.collection.Pair;
import org.panda_lang.utilities.commons.function.PandaStream;
import org.panda_lang.utilities.commons.function.Result;
import org.panda_lang.utilities.commons.text.Joiner;
@@ -31,11 +30,7 @@
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
-import java.util.Arrays;
-import java.util.Locale;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.stream.Stream;
+import java.util.*;
public final class MetadataUtils {
@@ -47,37 +42,37 @@ public final class MetadataUtils {
private MetadataUtils() { }
- public static Result toSortedBuilds(StorageProvider storageProvider, Path directory) {
- return storageProvider.getFiles(directory).map(list -> toSorted(list.stream()
- .filter(path -> path.getParent().equals(directory))
- .filter(storageProvider::exists)
- .filter(path -> path.getFileName().endsWith(".pom")),
- path -> path.getFileName().toString(), storageProvider::isDirectory)
- .toArray(Path[]::new));
- }
+ public static Result toSortedBuilds(Repository repository, Path directory) {
+ Result, ErrorDto> result = repository.getFiles(directory);
+
+ if (result.isOk()) {
+ Collection paths = new TreeSet<>(repository);
+
+ for (Path path : result.get()) {
+ paths.add(directory.resolve(path.getName(0)));
+ }
+
+ return Result.ok(paths.toArray(new Path[0]));
+ }
- public static Result toFiles(StorageProvider storageProvider, Path directory) {
- return storageProvider.getFiles(directory).map(list -> toSorted(list.stream()
- .filter(path -> path.getParent().equals(directory))
- .filter(storageProvider::exists),
- path -> path.getFileName().toString(), storageProvider::isDirectory)
- .toArray(Path[]::new));
+ return result.map(p -> null);
}
- public static Result toSortedVersions(StorageProvider storageProvider, Path directory) throws IOException {
- return storageProvider.getFiles(directory).map(list -> toSorted(list.stream()
- .filter(path -> path.getParent().equals(directory))
- .filter(storageProvider::isDirectory),
- path -> path.getFileName().toString(), storageProvider::isDirectory)
- .toArray(Path[]::new));
+ public static Result toSortedVersions(Repository repository, Path directory) {
+ return repository.getFiles(directory).map(list -> list.stream()
+ .filter(path -> path.getParent().endsWith(directory))
+ .filter(repository::isDirectory)
+ .sorted(repository)
+ .toArray(Path[]::new)
+ );
}
- protected static String[] toSortedIdentifiers(String artifact, String version, Path[] builds) {
+ protected static String[] toSortedIdentifiers(Repository repository, String artifact, String version, Path[] builds) {
return PandaStream.of(builds)
+ .sorted(repository)
.map(build -> toIdentifier(artifact, version, build))
.filterNot(StringUtils::isEmpty)
.distinct()
- .transform(stream -> toSorted(stream, Function.identity(), identifier -> true))
.toArray(String[]::new);
}
@@ -89,21 +84,15 @@ protected static boolean isNotChecksum(Path path, String identifier) {
&& (name.contains(identifier + ".") || name.contains(identifier + "-"));
}
- protected static Path[] toBuildFiles(StorageProvider storageProvider, Path directory, String identifier) {
- return toSorted(storageProvider.getFiles(directory).get().stream()
- .filter(path -> path.getParent().equals(directory))
- .filter(path -> isNotChecksum(path, identifier)),
- path -> path.getFileName().toString(), storageProvider::isDirectory)
+ protected static Path[] toBuildFiles(Repository repository, Path directory, String identifier) {
+ return repository.getFiles(directory).get().stream()
+ .filter(path -> path.getParent().equals(directory))
+ .filter(path -> isNotChecksum(path, identifier))
+ .filter(repository::exists)
+ .sorted(repository)
.toArray(Path[]::new);
}
- public static Stream toSorted(Stream stream, Function mapper, Predicate isDirectory) {
- return stream
- .map(object -> new Pair<>(object, mapper.apply(object).split("[-.]")))
- .sorted(new MetadataComparator<>(pair -> mapper.apply(pair.getKey()), Pair::getValue, pair -> isDirectory.test(pair.getKey())))
- .map(Pair::getKey);
- }
-
public static String toIdentifier(String artifact, String version, Path build) {
String identifier = build.getFileName().toString();
identifier = StringUtils.replace(identifier, "." + FilesUtils.getExtension(identifier), StringUtils.EMPTY);
@@ -133,8 +122,8 @@ private static String declassifyIdentifier(String identifier) {
: identifier.substring(0, occurrence);
}
- protected static String toUpdateTime(StorageProvider storageProvider, Path file) throws IOException {
- Result result = storageProvider.getLastModifiedTime(file);
+ protected static String toUpdateTime(Repository repository, Path file) throws IOException {
+ Result result = repository.getLastModifiedTime(file);
if (result.isOk()) {
return TIMESTAMP_FORMATTER.format(Instant.ofEpochMilli(result.get().toMillis()));
@@ -157,6 +146,21 @@ private static String[] shrinkGroup(String[] elements, int toShrink) {
return Arrays.copyOfRange(elements, 0, elements.length - toShrink);
}
+ public static String toGroup(Path metadataFilePath) {
+ StringBuilder builder = new StringBuilder();
+
+ for (Iterator iterator = metadataFilePath.getParent().getParent().iterator(); iterator.hasNext(); ) {
+ Path path = iterator.next();
+ builder.append(path.getFileName().toString());
+
+ if (iterator.hasNext()) {
+ builder.append('.');
+ }
+ }
+
+ return builder.toString();
+ }
+
private static boolean isBuildNumber(String content) {
for (char character : content.toCharArray()) {
if (!Character.isDigit(character)) {
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/Artifact.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/Artifact.java
index 5a9afc7cc..de6669d3f 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/Artifact.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/Artifact.java
@@ -21,7 +21,6 @@
import java.nio.file.Path;
final class Artifact {
-
private final Repository repository;
private final String group;
private final String artifact;
@@ -34,20 +33,6 @@ final class Artifact {
this.version = version;
}
- public Path getFile(String fileName) {
- return repository.getFile(getLocalPath() + fileName);
- }
-
- public String getLocalPath() {
- return getGroupPath() + "/" + artifact + "/" + version + "/";
- }
-
- private String getGroupPath() {
- return group
- .replaceAll("([^`])\\.([^`])", "$1/$2")
- .replace(MetadataUtils.ESCAPE_DOT, ".");
- }
-
public Repository getRepository() {
return repository;
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/DeployEndpoint.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/DeployEndpoint.java
index 50995fb07..7d3d8adb0 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/DeployEndpoint.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/DeployEndpoint.java
@@ -23,6 +23,7 @@
import io.javalin.plugin.openapi.annotations.OpenApiContent;
import io.javalin.plugin.openapi.annotations.OpenApiParam;
import io.javalin.plugin.openapi.annotations.OpenApiResponse;
+import org.jetbrains.annotations.NotNull;
import org.panda_lang.reposilite.Reposilite;
import org.panda_lang.reposilite.ReposiliteContext;
import org.panda_lang.reposilite.ReposiliteContextFactory;
@@ -69,19 +70,14 @@ public DeployEndpoint(ReposiliteContextFactory contextFactory, DeployService dep
}
)
@Override
- public void handle(Context ctx) {
+ public void handle(@NotNull Context ctx) {
ReposiliteContext context = contextFactory.create(ctx);
Reposilite.getLogger().info("DEPLOY " + context.uri() + " from " + context.address());
deployService.deploy(context)
- .map(future -> ctx.result(future.thenAccept(result -> result
- .map(ctx::json)
- .onError(error -> Reposilite.getLogger().debug("Cannot deploy artifact due to: (future) " + error.getMessage()))
- .mapErr(error -> ResponseUtils.errorResponse(ctx, error)))))
- .onError(error -> {
- Reposilite.getLogger().debug("Cannot deploy artifact due to: " + error.getMessage());
- ResponseUtils.errorResponse(ctx, error);
- });
+ .map(ctx::json)
+ .onError(error -> Reposilite.getLogger().debug("Cannot deploy artifact due to: " + error.getMessage()))
+ .mapErr(error -> ResponseUtils.errorResponse(ctx, error));
}
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/DeployService.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/DeployService.java
index abe676d26..cb42c16c9 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/DeployService.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/DeployService.java
@@ -26,16 +26,11 @@
import org.panda_lang.reposilite.error.ErrorDto;
import org.panda_lang.reposilite.error.ResponseUtils;
import org.panda_lang.reposilite.metadata.MetadataService;
-import org.panda_lang.reposilite.storage.StorageProvider;
+import org.panda_lang.utilities.commons.collection.Pair;
import org.panda_lang.utilities.commons.function.Result;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Spliterator;
-import java.util.Spliterators;
-import java.util.concurrent.CompletableFuture;
-import java.util.stream.StreamSupport;
+import java.nio.file.Paths;
public final class DeployService {
@@ -44,29 +39,28 @@ public final class DeployService {
private final Authenticator authenticator;
private final RepositoryService repositoryService;
private final MetadataService metadataService;
- private final StorageProvider storageProvider;
public DeployService(
boolean deployEnabled,
boolean rewritePathsEnabled,
Authenticator authenticator,
RepositoryService repositoryService,
- MetadataService metadataService, StorageProvider storageProvider) {
+ MetadataService metadataService) {
this.deployEnabled = deployEnabled;
this.rewritePathsEnabled = rewritePathsEnabled;
this.authenticator = authenticator;
this.repositoryService = repositoryService;
this.metadataService = metadataService;
- this.storageProvider = storageProvider;
}
- public Result>, ErrorDto> deploy(ReposiliteContext context) {
+ public Result deploy(ReposiliteContext context) {
if (!deployEnabled) {
return ResponseUtils.error(HttpStatus.SC_METHOD_NOT_ALLOWED, "Artifact deployment is disabled");
}
- String uri = ReposiliteUtils.normalizeUri(rewritePathsEnabled, repositoryService, context.uri());
+ String uri = ReposiliteUtils.normalizeUri(context.uri());
+ Repository repository = ReposiliteUtils.getRepository(rewritePathsEnabled, repositoryService, uri);
Result authResult = this.authenticator.authByUri(context.headers(), uri);
if (authResult.isErr()) {
@@ -79,39 +73,33 @@ public Result>, ErrorDto> dep
return ResponseUtils.error(HttpStatus.SC_UNAUTHORIZED, "Cannot deploy artifact without write permission");
}
- if (storageProvider.isFull()) {
+ if (repository == null) {
+ return ResponseUtils.error(HttpStatus.SC_NOT_FOUND, "Repository not found");
+ }
+
+ if (repository.isFull()) {
return ResponseUtils.error(HttpStatus.SC_INSUFFICIENT_STORAGE, "Not enough storage space available");
}
- Path path = repositoryService.getFile(uri);
- Result fileDetails = storageProvider.getFileDetails(path);
+ Path path = Paths.get(uri);
Path metadataFile = path.resolveSibling("maven-metadata.xml");
metadataService.clearMetadata(metadataFile);
- Reposilite.getLogger().info("DEPLOY " + authResult.isOk() + " successfully deployed " + path + " from " + context.address());
-
try {
- if (path.getFileName().toString().contains("maven-metadata")) {
- if (!storageProvider.exists(path)) {
- ArrayList list = new ArrayList<>();
-
- path.forEach(p -> list.add(p.toString()));
+ Result result;
- String[] strarr = list.toArray(new String[0]);
-
- // remove repository name from path
- String[] requestPath = Arrays.copyOfRange(strarr, 1, strarr.length);
-
- metadataService.generateMetadata(repositoryService.getRepository(path.getName(1).toString()), requestPath);
-
- fileDetails = storageProvider.getFileDetails(path);
- }
+ if (path.getFileName().toString().contains("maven-metadata")) {
+ result = metadataService.getMetadata(repository, metadataFile).map(Pair::getKey);
+ } else {
+ result = repository.putFile(path, context.input());
+ }
- return Result.ok(CompletableFuture.completedFuture(Result.ok(fileDetails.get())));
+ if (result.isOk()) {
+ Reposilite.getLogger().info("DEPLOY " + authResult.isOk() + " successfully deployed " + path + " from " + context.address());
}
- return Result.ok(CompletableFuture.completedFuture(storageProvider.putFile(path, context.input())));
+ return result;
} catch (Exception e) {
return Result.error(new ErrorDto(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Failed to upload artifact"));
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/FileListDto.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/ListDto.java
similarity index 71%
rename from reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/FileListDto.java
rename to reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/ListDto.java
index ff04b19df..09cbb3958 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/FileListDto.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/ListDto.java
@@ -19,16 +19,11 @@
import java.io.Serializable;
import java.util.List;
-final class FileListDto implements Serializable {
+final class ListDto implements Serializable {
- private final List extends FileDetailsDto> files;
+ public final List files;
- FileListDto(List extends FileDetailsDto> files) {
- this.files = files;
+ ListDto(List items) {
+ this.files = items;
}
-
- public List extends FileDetailsDto> getFiles() {
- return files;
- }
-
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/LookupApiEndpoint.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/LookupApiEndpoint.java
index 362e0b717..c244609f0 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/LookupApiEndpoint.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/LookupApiEndpoint.java
@@ -30,36 +30,28 @@
import org.panda_lang.reposilite.ReposiliteUtils;
import org.panda_lang.reposilite.error.ErrorDto;
import org.panda_lang.reposilite.error.ResponseUtils;
-import org.panda_lang.reposilite.storage.StorageProvider;
import org.panda_lang.utilities.commons.StringUtils;
import org.panda_lang.utilities.commons.collection.Pair;
import org.panda_lang.utilities.commons.function.Result;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
+import java.util.*;
public final class LookupApiEndpoint implements Handler {
- private final boolean rewritePathsEnabled;
private final ReposiliteContextFactory contextFactory;
private final RepositoryAuthenticator repositoryAuthenticator;
private final RepositoryService repositoryService;
- private final StorageProvider storageProvider;
public LookupApiEndpoint(
- boolean rewritePathsEnabled,
ReposiliteContextFactory contextFactory,
RepositoryAuthenticator repositoryAuthenticator,
- RepositoryService repositoryService, StorageProvider storageProvider) {
+ RepositoryService repositoryService) {
- this.rewritePathsEnabled = rewritePathsEnabled;
this.contextFactory = contextFactory;
this.repositoryAuthenticator = repositoryAuthenticator;
this.repositoryService = repositoryService;
- this.storageProvider = storageProvider;
}
@OpenApi(
@@ -76,7 +68,7 @@ public LookupApiEndpoint(
description = "Returns document (different for directory and file) that describes requested resource",
content = {
@OpenApiContent(from = FileDetailsDto.class),
- @OpenApiContent(from = FileListDto.class)
+ @OpenApiContent(from = ListDto.class)
}
),
@OpenApiResponse(
@@ -95,21 +87,22 @@ public void handle(@NotNull Context ctx) {
ReposiliteContext context = contextFactory.create(ctx);
Reposilite.getLogger().info("API " + context.uri() + " from " + context.address());
- String uri = ReposiliteUtils.normalizeUri(rewritePathsEnabled, repositoryService, StringUtils.replaceFirst(context.uri(), "/api", ""));
+ String uri = ReposiliteUtils.normalizeUri(StringUtils.replaceFirst(context.uri(), "/api", ""));
if (StringUtils.isEmpty(uri) || "/".equals(uri)) {
ctx.json(repositoryAuthenticator.findAvailableRepositories(context.headers()));
return;
}
- Result, ErrorDto> result = repositoryAuthenticator.authRepository(context.headers(), uri);
+ Result, ErrorDto> result = repositoryAuthenticator.authRepository(context.headers(), uri);
if (result.isErr()) {
ResponseUtils.errorResponse(ctx, result.getError().getStatus(), result.getError().getMessage());
return;
}
- Path requestedFile = repositoryService.getFile(uri);
+ Repository repository = result.get().getValue();
+ Path requestedFile = result.get().getKey(); // The first element is the repository name
Optional latest = Optional.empty();
try {
@@ -123,27 +116,36 @@ public void handle(@NotNull Context ctx) {
return;
}
- if (!storageProvider.exists(requestedFile) && !storageProvider.isDirectory(requestedFile)) {
+ if (!repository.exists(requestedFile) && !repository.isDirectory(requestedFile)) {
ResponseUtils.errorResponse(ctx, HttpStatus.SC_NOT_FOUND, "File not found");
return;
}
- if (storageProvider.exists(requestedFile)) {
- ctx.json(storageProvider.getFileDetails(requestedFile).get());
+ if (repository.exists(requestedFile)) {
+ ctx.json(repository.getFileDetails(requestedFile).get());
return;
}
+ Collection paths = new HashSet<>();
List list = new ArrayList<>();
try {
- for (Path directory : storageProvider.getFiles(requestedFile).get()) {
- list.add(storageProvider.getFileDetails(directory).get());
+ for (Path path : repository.getFiles(requestedFile).get()) {
+ Path next = path.getName(0);
+
+ if (!paths.contains(next)) {
+ paths.add(next);
+
+ list.add(repository.isDirectory(next)
+ ? new FileDetailsDto(FileDetailsDto.DIRECTORY, next.toString(), "", "application/octet-stream", 0)
+ : repository.getFileDetails(next).get());
+ }
}
} catch (Exception ignored) {
}
- ctx.json(new FileListDto(list));
+ ctx.json(new ListDto<>(list));
}
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/LookupController.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/LookupController.java
index 4afcf743e..b698cdfa2 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/LookupController.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/LookupController.java
@@ -18,11 +18,7 @@
import io.javalin.http.Context;
import io.javalin.http.Handler;
-import io.javalin.plugin.openapi.annotations.ContentType;
-import io.javalin.plugin.openapi.annotations.OpenApi;
-import io.javalin.plugin.openapi.annotations.OpenApiContent;
-import io.javalin.plugin.openapi.annotations.OpenApiParam;
-import io.javalin.plugin.openapi.annotations.OpenApiResponse;
+import io.javalin.plugin.openapi.annotations.*;
import org.apache.http.HttpStatus;
import org.jetbrains.annotations.NotNull;
import org.panda_lang.reposilite.Reposilite;
@@ -35,7 +31,6 @@
import org.panda_lang.utilities.commons.function.Result;
import java.io.IOException;
-import java.util.concurrent.CompletableFuture;
public final class LookupController implements Handler {
@@ -91,17 +86,17 @@ public void handle(@NotNull Context ctx) {
Result response;
- try {
- response = lookupService.findLocal(context);
- } catch (IOException e) {
- e.printStackTrace();
- return;
- }
-
- if (isProxied(response)) {
+ if (lookupService.exists(context)) {
+ try {
+ response = lookupService.find(context);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return;
+ }
+ } else if (this.hasProxied) {
response = proxyService.findProxied(context);
} else {
- response = response.mapErr(proxiedError -> new ErrorDto(HttpStatus.SC_NOT_FOUND, proxiedError.getMessage()));
+ response = Result.error(new ErrorDto(HttpStatus.SC_NOT_FOUND, "File not found"));
}
handleResult(ctx, context, response);
@@ -144,8 +139,4 @@ private void handleError(Context ctx, ErrorDto error) {
.contentType("text/html")
.res.setCharacterEncoding("UTF-8");
}
-
- private boolean isProxied(Result, ErrorDto> lookupResponse) {
- return this.hasProxied && lookupResponse.isErr() && lookupResponse.getError().getStatus() == HttpStatus.SC_USE_PROXY;
- }
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/LookupService.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/LookupService.java
index 103c0c3f8..3ac6c02be 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/LookupService.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/LookupService.java
@@ -21,9 +21,7 @@
import org.panda_lang.reposilite.ReposiliteContext;
import org.panda_lang.reposilite.error.ErrorDto;
import org.panda_lang.reposilite.error.ResponseUtils;
-import org.panda_lang.reposilite.metadata.MetadataService;
import org.panda_lang.reposilite.metadata.MetadataUtils;
-import org.panda_lang.reposilite.storage.StorageProvider;
import org.panda_lang.reposilite.utils.ArrayUtils;
import org.panda_lang.utilities.commons.collection.Pair;
import org.panda_lang.utilities.commons.function.Result;
@@ -31,31 +29,49 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
-import java.util.Objects;
-import java.util.Optional;
public final class LookupService {
private final RepositoryAuthenticator repositoryAuthenticator;
- private final MetadataService metadataService;
private final RepositoryService repositoryService;
- private final StorageProvider storageProvider;
public LookupService(
RepositoryAuthenticator repositoryAuthenticator,
- MetadataService metadataService,
- RepositoryService repositoryService,
- StorageProvider storageProvider) {
+ RepositoryService repositoryService) {
this.repositoryAuthenticator = repositoryAuthenticator;
- this.metadataService = metadataService;
this.repositoryService = repositoryService;
- this.storageProvider = storageProvider;
}
- Result findLocal(ReposiliteContext context) throws IOException {
+ boolean exists(ReposiliteContext context) {
String uri = context.uri();
- Result, ErrorDto> result = this.repositoryAuthenticator.authDefaultRepository(context.headers(), uri);
+ Result, ErrorDto> result = this.repositoryAuthenticator.authDefaultRepository(context.headers(), uri);
+
+ if (result.isErr()) {
+ // Maven requests maven-metadata.xml file during deploy for snapshot releases without specifying credentials
+ // https://github.com/dzikoysk/reposilite/issues/184
+ if (uri.contains("-SNAPSHOT") && uri.endsWith("maven-metadata.xml")) {
+ return false;
+ }
+
+ return false;
+ }
+
+ Path path = result.get().getKey();
+
+ // discard invalid requests (less than 'group/(artifact OR metadata)')
+ if (path.getNameCount() < 2) {
+ return false;
+ }
+
+ Repository repository = result.get().getValue();
+
+ return repository.exists(path);
+ }
+
+ Result find(ReposiliteContext context) throws IOException {
+ String uri = context.uri();
+ Result, ErrorDto> result = this.repositoryAuthenticator.authDefaultRepository(context.headers(), uri);
if (result.isErr()) {
// Maven requests maven-metadata.xml file during deploy for snapshot releases without specifying credentials
@@ -67,29 +83,24 @@ Result findLocal(ReposiliteContext context) throws IOE
return Result.error(result.getError());
}
- String[] path = result.get().getKey();
- // remove repository name from path
- String[] requestPath = Arrays.copyOfRange(path, 1, path.length);
+ Path path = result.get().getKey();
// discard invalid requests (less than 'group/(artifact OR metadata)')
- if (requestPath.length < 2) {
+ if (path.getNameCount() < 2) {
return ResponseUtils.error(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION, "Missing artifact identifier");
}
Repository repository = result.get().getValue();
- String requestedFileName = Objects.requireNonNull(ArrayUtils.getLast(requestPath));
+ String requestedFileName = path.getFileName().toString();
if (requestedFileName.equals("maven-metadata.xml")) {
- return metadataService
- .generateMetadata(repository, requestPath)
- .mapErr(error -> new ErrorDto(HttpStatus.SC_USE_PROXY, error))
- .map(metadataContent -> new LookupResponse("text/xml", metadataContent));
+ return repository.getFile(path).map(bytes -> new LookupResponse("text/xml", Arrays.toString(bytes)));
}
// resolve requests for latest version of artifact
if (requestedFileName.equalsIgnoreCase("latest")) {
- Path requestDirectory = repository.getFile(requestPath).getParent();
- Result versions = MetadataUtils.toSortedVersions(storageProvider, requestDirectory);
+ Path requestDirectory = path.getParent();
+ Result versions = MetadataUtils.toSortedVersions(repository, requestDirectory);
if (versions.isErr()) return versions.map(p -> null);
@@ -104,41 +115,36 @@ Result findLocal(ReposiliteContext context) throws IOE
// resolve snapshot requests
if (requestedFileName.contains("-SNAPSHOT")) {
- repositoryService.resolveSnapshot(repository, requestPath);
- // update requested file name in case of snapshot request
- requestedFileName = requestPath[requestPath.length - 1];
- }
+ path = repositoryService.resolveSnapshot(repository, path);
- Path repositoryFile = repository.getFile(requestPath);
+ if (path == null) {
+ return Result.error(new ErrorDto(HttpStatus.SC_NOT_FOUND, "Latest version not found"));
+ }
+ }
- if (storageProvider.isDirectory(repositoryFile)) {
+ if (repository.isDirectory(path)) {
return ResponseUtils.error(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION, "Directory access");
}
- Optional artifact = repository.find(requestPath);
+ Result bytes = repository.getFile(path);
- if (!artifact.isPresent()) {
- return ResponseUtils.error(HttpStatus.SC_USE_PROXY, "Artifact " + requestedFileName + " not found");
+ if (bytes.isErr()) {
+ return bytes.map(b -> null);
}
- Path file = artifact.get().getFile(requestedFileName);
- Result fileDetailsResult = storageProvider.getFileDetails(file);
+ Result fileDetailsResult = repository.getFileDetails(path);
if (fileDetailsResult.isOk()) {
FileDetailsDto fileDetails = fileDetailsResult.get();
if (!context.method().equals("HEAD")) {
- context.result(outputStream -> {
- byte[] bytes = storageProvider.getFile(file).get();
- outputStream.write(bytes);
- });
+ context.result(outputStream -> outputStream.write(bytes.get()));
}
- Reposilite.getLogger().info("RESOLVED " + file + "; mime: " + fileDetails.getContentType() + "; size: " + storageProvider.getFileSize(file).get());
+ Reposilite.getLogger().info("RESOLVED " + path + "; mime: " + fileDetails.getContentType() + "; size: " + repository.getFileSize(path).get());
return Result.ok(new LookupResponse(fileDetails));
} else {
return Result.error(fileDetailsResult.getError());
}
}
-
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/ProxyService.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/ProxyService.java
index 279f1aa6d..ea698d7df 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/ProxyService.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/ProxyService.java
@@ -35,6 +35,7 @@
import java.io.IOException;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -147,7 +148,7 @@ private Result store(String uri, HttpResponse remoteRe
uri = repositoryService.getPrimaryRepository().getName() + uri;
}
- Path proxiedFile = repositoryService.getFile(uri);
+ Path proxiedFile = Paths.get(uri);
try {
Result result = this.storageProvider.putFile(proxiedFile, remoteResponse.getContent());
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/Repository.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/Repository.java
index ff480b1d1..e71141db2 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/Repository.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/Repository.java
@@ -16,62 +16,118 @@
package org.panda_lang.reposilite.repository;
-import org.panda_lang.reposilite.metadata.MetadataUtils;
+import org.panda_lang.reposilite.config.RepositoryConfig;
+import org.panda_lang.reposilite.config.RepositoryOption;
+import org.panda_lang.reposilite.error.ErrorDto;
import org.panda_lang.reposilite.storage.StorageProvider;
-import org.panda_lang.utilities.commons.text.Joiner;
+import org.panda_lang.utilities.commons.function.Result;
-import java.io.File;
-import java.nio.file.Files;
+import java.io.InputStream;
import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.Optional;
+import java.nio.file.Paths;
+import java.nio.file.attribute.FileTime;
+import java.util.Comparator;
+import java.util.List;
-public final class Repository {
+public final class Repository implements Comparator {
+ private static final Path REPOSITORIES = Paths.get("repositories");
- private final Path directory;
+ private final RepositoryConfig config;
private final String name;
- private final boolean hidden;
private final StorageProvider storageProvider;
- Repository(Path rootDirectory, String name, boolean hidden, StorageProvider storageProvider) {
- this.storageProvider = storageProvider;
- this.directory = rootDirectory.resolve(name);
- this.name = name;
- this.hidden = hidden;
+ Repository(RepositoryConfig config) {
+ this.config = config;
+ this.name = config.getRepositoryName();
+ this.storageProvider = config.get(RepositoryOption.STORAGE_PROVIDER);
}
- public Optional find(String... path) {
- Path targetFile = getFile(path);
+ public boolean isPublic() {
+ return !isPrivate();
+ }
- if (!storageProvider.exists(targetFile) || storageProvider.isDirectory(targetFile) || path.length < 3) {
- return Optional.empty();
- }
+ public boolean isPrivate() {
+ return this.config.get(RepositoryOption.PRIVATE);
+ }
- String groupId = MetadataUtils.toGroup(Arrays.copyOfRange(path, 0, path.length - 3));
- String artifactId = path[path.length - 3];
- String version = path[path.length - 2];
+ public String getName() {
+ return this.name;
+ }
- return Optional.of(new Artifact(this, groupId, artifactId, version));
+ public Result putFile(Path file, byte[] bytes) {
+ return this.storageProvider.putFile(this.relativize(file), bytes);
}
- public boolean isPublic() {
- return !isHidden();
+ public Result putFile(Path file, InputStream inputStream) {
+ return this.storageProvider.putFile(this.relativize(file), inputStream);
}
- public boolean isHidden() {
- return hidden;
+ public Result getFile(Path file) {
+ return this.storageProvider.getFile(this.relativize(file));
}
- public Path getFile(String... path) {
- return directory.resolve(Joiner.on(File.separator).join(path).toString());
+ public Result getFileDetails(Path file) {
+ return this.storageProvider.getFileDetails(this.relativize(file));
}
- public String getUri() {
- return "/" + getName();
+ public Result removeFile(Path file) {
+ return this.storageProvider.removeFile(this.relativize(file));
}
- public String getName() {
- return name;
+ public Result, ErrorDto> getFiles(Path directory) {
+ return this.storageProvider.getFiles(this.relativize(directory));
+ }
+
+ public Result getLastModifiedTime(Path file) {
+ return this.storageProvider.getLastModifiedTime(this.relativize(file));
+ }
+
+ public Result getFileSize(Path file) {
+ return this.storageProvider.getFileSize(this.relativize(file));
+ }
+
+ public boolean exists(Path file) {
+ return this.storageProvider.exists(this.relativize(file));
+ }
+
+ public boolean isDirectory(Path file) {
+ return this.storageProvider.isDirectory(this.relativize(file));
}
+ public boolean isFull() {
+ return this.storageProvider.isFull();
+ }
+
+ public long getUsage() {
+ return this.storageProvider.getUsage();
+ }
+
+ public boolean canHold(long contentLength) {
+ return this.storageProvider.canHold(contentLength);
+ }
+
+ public void shutdown() {
+ this.storageProvider.shutdown();
+ }
+
+ public Path relativize(Path path) {
+ if (path == null) return null;
+
+ if (!path.startsWith(REPOSITORIES)) {
+ if (!path.startsWith(this.name)) {
+ path = Paths.get(this.name).resolve(path);
+ }
+
+ path = REPOSITORIES.resolve(path);
+ } else if (path.startsWith(this.name)) {
+ path = REPOSITORIES.relativize(path);
+ }
+
+ return path;
+ }
+
+ @Override
+ public int compare(Path o1, Path o2) {
+ return this.relativize(o1).compareTo(this.relativize(o2));
+ }
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/RepositoryAuthenticator.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/RepositoryAuthenticator.java
index 004b2c4e5..5f227f8e8 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/RepositoryAuthenticator.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/RepositoryAuthenticator.java
@@ -22,12 +22,13 @@
import org.panda_lang.reposilite.auth.Session;
import org.panda_lang.reposilite.error.ErrorDto;
import org.panda_lang.reposilite.error.ResponseUtils;
-import org.panda_lang.reposilite.storage.StorageProvider;
import org.panda_lang.utilities.commons.StringUtils;
import org.panda_lang.utilities.commons.collection.Pair;
import org.panda_lang.utilities.commons.function.Option;
import org.panda_lang.utilities.commons.function.Result;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Map;
import java.util.stream.Collectors;
@@ -36,22 +37,20 @@ public final class RepositoryAuthenticator {
private final boolean rewritePathsEnabled;
private final Authenticator authenticator;
private final RepositoryService repositoryService;
- private final StorageProvider storageProvider;
- public RepositoryAuthenticator(boolean rewritePathsEnabled, Authenticator authenticator, RepositoryService repositoryService, StorageProvider storageProvider) {
+ public RepositoryAuthenticator(boolean rewritePathsEnabled, Authenticator authenticator, RepositoryService repositoryService) {
this.rewritePathsEnabled = rewritePathsEnabled;
this.authenticator = authenticator;
this.repositoryService = repositoryService;
- this.storageProvider = storageProvider;
}
- public Result, ErrorDto> authDefaultRepository(Map headers, String uri) {
- return authRepository(headers, ReposiliteUtils.normalizeUri(rewritePathsEnabled, repositoryService, uri));
+ public Result, ErrorDto> authDefaultRepository(Map headers, String uri) {
+ return authRepository(headers, ReposiliteUtils.normalizeUri(uri));
}
- public Result, ErrorDto> authRepository(Map headers, String normalizedUri) {
- String[] path = StringUtils.split(normalizedUri, "/");
- String repositoryName = path[0];
+ public Result, ErrorDto> authRepository(Map headers, String normalizedUri) {
+ String[] split = StringUtils.split(normalizedUri, "/");
+ String repositoryName = split[0];
if (StringUtils.isEmpty(repositoryName)) {
return ResponseUtils.error(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION, "Unsupported request");
@@ -60,11 +59,15 @@ public Result, ErrorDto> authRepository(Map authResult = authenticator.authByUri(headers, normalizedUri);
if (authResult.isErr()) {
@@ -72,16 +75,21 @@ public Result, ErrorDto> authRepository(Map 1 ? split[1] : "");
+
+ for (int i = 2; i < split.length; ++i) {
+ path = path.resolve(split[i]);
+ }
+
return Result.ok(new Pair<>(path, repository));
}
- FileListDto findAvailableRepositories(Map headers) {
+ ListDto findAvailableRepositories(Map headers) {
Option session = authenticator.authByHeader(headers).toOption();
- return new FileListDto(repositoryService.getRepositories().stream()
+ return new ListDto<>(repositoryService.getRepositories().stream()
.filter(repository -> repository.isPublic() || session.map(value -> value.getRepositoryNames().contains(repository.getName())).orElseGet(false))
- .map(Repository::getFile)
- .map(path -> storageProvider.getFileDetails(path).get())
+ .map(repository -> new FileDetailsDto(FileDetailsDto.DIRECTORY, repository.getName(), "", "application/octet-stream", 0))
.collect(Collectors.toList()));
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/RepositoryService.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/RepositoryService.java
index 2dcb168ef..622c4241b 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/RepositoryService.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/RepositoryService.java
@@ -16,68 +16,81 @@
package org.panda_lang.reposilite.repository;
+import org.jetbrains.annotations.Nullable;
+import org.panda_lang.reposilite.Reposilite;
import org.panda_lang.reposilite.auth.Token;
import org.panda_lang.reposilite.config.Configuration;
+import org.panda_lang.reposilite.config.RepositoryConfig;
+import org.panda_lang.reposilite.config.RepositoryOption;
import org.panda_lang.reposilite.error.ErrorDto;
import org.panda_lang.reposilite.metadata.MetadataUtils;
-import org.panda_lang.reposilite.storage.StorageProvider;
import org.panda_lang.reposilite.utils.ArrayUtils;
import org.panda_lang.utilities.commons.StringUtils;
import org.panda_lang.utilities.commons.function.Result;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
+import java.util.*;
public final class RepositoryService {
+ private final Map repositories = new LinkedHashMap<>(4);
+ private Repository primaryRepository;
- private final RepositoryStorage repositoryStorage;
- private final StorageProvider storageProvider;
+ public void load(Configuration configuration) {
+ Reposilite.getLogger().info("--- Loading repositories");
- public RepositoryService(
- Path workingDirectory,
- StorageProvider storageProvider) {
+ for (String repositoryString : configuration.repositories) {
+ boolean primary = primaryRepository == null;
+ RepositoryConfig config = RepositoryConfig.parse(repositoryString);
- this.repositoryStorage = new RepositoryStorage(workingDirectory, storageProvider);
- this.storageProvider = storageProvider;
- }
+ Repository repository = new Repository(
+ config
+ );
+
+ repositories.put(repository.getName(), repository);
- public void load(Configuration configuration) throws IOException {
- repositoryStorage.load(configuration);
+ if (primary) {
+ this.primaryRepository = repository;
+ }
+
+ Reposilite.getLogger().info("+ " + (config.get(RepositoryOption.PRIVATE) ? " (hidden)" : "") + (primary ? " (primary)" : ""));
+ }
+
+ Reposilite.getLogger().info(repositories.size() + " repositories have been found");
}
- public void resolveSnapshot(Repository repository, String[] requestPath) {
- Path artifactFile = repository.getFile(requestPath);
+ public @Nullable Path resolveSnapshot(Repository repository, Path requestPath) {
+ Path artifactFile = repository.relativize(requestPath);
Path versionDirectory = artifactFile.getParent();
- Path[] builds = MetadataUtils.toSortedBuilds(storageProvider, versionDirectory).get();
+ Path[] builds = MetadataUtils.toSortedBuilds(repository, versionDirectory).get();
Path latestBuild = ArrayUtils.getFirst(builds);
if (latestBuild == null) {
- return;
+ return null;
}
String version = StringUtils.replace(versionDirectory.getFileName().toString(), "-SNAPSHOT", StringUtils.EMPTY);
Path artifactDirectory = versionDirectory.getParent();
String identifier = MetadataUtils.toIdentifier(artifactDirectory.getFileName().toString(), version, latestBuild);
- requestPath[requestPath.length - 1] = StringUtils.replace(requestPath[requestPath.length - 1], "SNAPSHOT", identifier);
+ return requestPath.getParent().resolve(requestPath.getFileName().toString().replace("SNAPSHOT", identifier));
}
public Optional findLatest(Path requestedFile) throws IOException {
if (requestedFile.getFileName().toString().equals("latest")) {
Path parent = requestedFile.getParent();
- if (parent != null && storageProvider.isDirectory(parent)) {
- Result files = MetadataUtils.toSortedVersions(storageProvider, parent);
+ for (Repository repository : repositories.values()) {
+ if (parent != null && repository.isDirectory(parent)) {
+ Result files = MetadataUtils.toSortedVersions(repository, parent);
- if (files.isOk()) {
- Path latest = ArrayUtils.getFirst(files.get());
+ if (files.isOk()) {
+ Path latest = ArrayUtils.getFirst(files.get());
- if (latest != null) {
- return Optional.ofNullable(storageProvider.getFileDetails(latest).orElseGet(e -> null));
+ if (latest != null) {
+ return Optional.ofNullable(repository.getFileDetails(latest).orElseGet(e -> null));
+ }
}
}
}
@@ -86,7 +99,7 @@ public Optional findLatest(Path requestedFile) throws IOExceptio
return Optional.empty();
}
- public List getRepositories(Token token) {
+ public Collection getRepositories(Token token) {
if (token.hasMultiaccess()) {
return getRepositories();
}
@@ -102,20 +115,15 @@ public List getRepositories(Token token) {
return Collections.emptyList();
}
- public List getRepositories() {
- return repositoryStorage.getRepositories();
+ public Collection getRepositories() {
+ return this.repositories.values();
}
public Repository getRepository(String repositoryName) {
- return repositoryStorage.getRepository(repositoryName);
+ return this.repositories.get(repositoryName);
}
public Repository getPrimaryRepository() {
- return repositoryStorage.getPrimaryRepository();
- }
-
- public Path getFile(String path) {
- return repositoryStorage.getFile(path);
+ return this.primaryRepository;
}
-
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/RepositoryStorage.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/RepositoryStorage.java
deleted file mode 100644
index 2a0e10159..000000000
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/repository/RepositoryStorage.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2020 Dzikoysk
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.panda_lang.reposilite.repository;
-
-import org.panda_lang.reposilite.Reposilite;
-import org.panda_lang.reposilite.config.Configuration;
-import org.panda_lang.reposilite.storage.StorageProvider;
-
-import java.io.InputStream;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-
-final class RepositoryStorage {
- private final Map repositories = new LinkedHashMap<>(4);
-
- private final StorageProvider storageProvider;
- private final Path rootDirectory;
- private Repository primaryRepository;
-
- RepositoryStorage(Path workingDirectory, StorageProvider storageProvider) {
- this.storageProvider = storageProvider;
- this.rootDirectory = workingDirectory.resolve("repositories");
- }
-
- void load(Configuration configuration) {
- Reposilite.getLogger().info("--- Loading repositories");
-
- for (String repositoryName : configuration.repositories) {
- boolean hidden = repositoryName.startsWith(".");
- boolean primary = primaryRepository == null;
-
- if (hidden) {
- repositoryName = repositoryName.substring(1);
- }
-
- Path repositoryDirectory = rootDirectory.resolve(repositoryName);
- Repository repository = new Repository(rootDirectory, repositoryName, hidden, storageProvider);
- repositories.put(repository.getName(), repository);
-
- if (primary) {
- this.primaryRepository = repository;
- }
-
- Reposilite.getLogger().info("+ " + repositoryDirectory.getFileName() + (hidden ? " (hidden)" : "") + (primary ? " (primary)" : ""));
- }
-
- Reposilite.getLogger().info(repositories.size() + " repositories have been found");
- }
-
- CompletableFuture storeFile(InputStream source, Path targetFile) {
- return storeFile(new CompletableFuture<>(), source, targetFile);
- }
-
- private CompletableFuture storeFile(CompletableFuture task, InputStream source, Path targetFile) {
- this.storageProvider.putFile(targetFile, source);
- task.complete(targetFile);
- return task;
- }
-
- Path getFile(String path) {
- return rootDirectory.resolve(path);
- }
-
- Repository getRepository(String repositoryName) {
- return repositories.get(repositoryName);
- }
-
- List getRepositories() {
- return new ArrayList<>(repositories.values());
- }
-
- Repository getPrimaryRepository() {
- return primaryRepository;
- }
-
- Path getRootDirectory() {
- return rootDirectory;
- }
-
- StorageProvider getStorageProvider() {
- return storageProvider;
- }
-
-}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/resource/FrontendHandler.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/resource/FrontendHandler.java
index b361c7b27..e9b9785be 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/resource/FrontendHandler.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/resource/FrontendHandler.java
@@ -45,5 +45,4 @@ public void handle(Context context) {
.header("Content-Type", "application/javascript")
.res.setCharacterEncoding("UTF-8");
}
-
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/resource/FrontendProvider.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/resource/FrontendProvider.java
index 482fdc799..8e2537d85 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/resource/FrontendProvider.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/resource/FrontendProvider.java
@@ -55,5 +55,4 @@ public static FrontendProvider load(Configuration configuration) {
() -> formatter.format(FilesUtils.getResource("/static/js/app.js"))
);
}
-
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/storage/FileSystemStorageProvider.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/storage/FileSystemStorageProvider.java
index 96811ed65..d9aae34e4 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/storage/FileSystemStorageProvider.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/storage/FileSystemStorageProvider.java
@@ -86,17 +86,15 @@ private Result putFile(Path file, T input, Throwin
return Result.error(new ErrorDto(HttpStatus.SC_INSUFFICIENT_STORAGE, "Not enough storage space available"));
}
- Path destination = this.rootDirectory.resolve(file);
-
- if (destination.getParent() != null && !Files.exists(destination.getParent())) {
- Files.createDirectories(destination.getParent());
+ if (file.getParent() != null && !Files.exists(file.getParent())) {
+ Files.createDirectories(file.getParent());
}
- if (!Files.exists(destination)) {
- Files.createFile(destination);
+ if (!Files.exists(file)) {
+ Files.createFile(file);
}
- FileChannel fileChannel = FileChannel.open(destination, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
+ FileChannel fileChannel = FileChannel.open(file, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
fileChannel.lock();
long bytesWritten = writer.apply(input, fileChannel);
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/storage/S3StorageProvider.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/storage/S3StorageProvider.java
index 1f70c74c6..75f82e937 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/storage/S3StorageProvider.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/storage/S3StorageProvider.java
@@ -1,12 +1,15 @@
package org.panda_lang.reposilite.storage;
import org.apache.http.HttpStatus;
+import org.jetbrains.annotations.Nullable;
import org.panda_lang.reposilite.error.ErrorDto;
import org.panda_lang.reposilite.repository.FileDetailsDto;
import org.panda_lang.reposilite.utils.FilesUtils;
import org.panda_lang.utilities.commons.function.Result;
+import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.sync.RequestBody;
+import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
@@ -19,24 +22,36 @@
import java.util.List;
public class S3StorageProvider implements StorageProvider {
- private final S3Client s3 = S3Client.create();
+ private final S3Client s3;
private final String bucket;
- public S3StorageProvider(String bucketName) {
+ public S3StorageProvider(String bucketName, String region) {
this.bucket = bucketName;
+ this.s3 = S3Client.builder().region(Region.of(region)).credentialsProvider(AnonymousCredentialsProvider.create()).build();
}
@Override
public Result putFile(Path file, byte[] bytes) {
PutObjectRequest.Builder builder = PutObjectRequest.builder();
builder.bucket(bucket);
- builder.key(file.toString());
+ builder.key(file.toString().replace('\\', '/'));
builder.contentType(FilesUtils.getMimeType(file.toString(), "text/plain"));
builder.contentLength((long) bytes.length);
- PutObjectResponse response = this.s3.putObject(builder.build(), RequestBody.fromBytes(bytes));
+ try {
+ this.s3.putObject(builder.build(), RequestBody.fromBytes(bytes));
- return Result.error(new ErrorDto(HttpStatus.SC_INTERNAL_SERVER_ERROR, "S3 file test"));
+ return Result.ok(new FileDetailsDto(
+ FileDetailsDto.FILE,
+ file.getFileName().toString(),
+ FileDetailsDto.DATE_FORMAT.format(System.currentTimeMillis()),
+ FilesUtils.getMimeType(file.getFileName().toString(), "application/octet-stream"),
+ bytes.length
+ ));
+ } catch (Exception e) {
+ e.printStackTrace();
+ return Result.error(new ErrorDto(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Failed to write " + file.toString()));
+ }
}
@Override
@@ -44,15 +59,25 @@ public Result putFile(Path file, InputStream inputStre
try {
PutObjectRequest.Builder builder = PutObjectRequest.builder();
builder.bucket(bucket);
- builder.key(file.toString());
+ builder.key(file.toString().replace('\\', '/'));
builder.contentType(FilesUtils.getMimeType(file.toString(), "text/plain"));
- builder.contentLength((long) inputStream.available());
- PutObjectResponse response = this.s3.putObject(builder.build(), RequestBody.fromInputStream(inputStream, inputStream.available()));
+ long length = inputStream.available();
+ builder.contentLength(length);
+
+ this.s3.putObject(builder.build(), RequestBody.fromInputStream(inputStream, inputStream.available()));
+
+ return Result.ok(new FileDetailsDto(
+ FileDetailsDto.FILE,
+ file.getFileName().toString(),
+ FileDetailsDto.DATE_FORMAT.format(System.currentTimeMillis()),
+ FilesUtils.getMimeType(file.getFileName().toString(), "application/octet-stream"),
+ length
+ ));
- return Result.error(new ErrorDto(HttpStatus.SC_INTERNAL_SERVER_ERROR, "S3 file test"));
} catch (IOException e) {
- return Result.error(new ErrorDto(HttpStatus.SC_INTERNAL_SERVER_ERROR, "S3 file test 2"));
+ e.printStackTrace();
+ return Result.error(new ErrorDto(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Failed to write " + file.toString()));
}
}
@@ -62,35 +87,45 @@ public Result getFile(Path file) {
GetObjectRequest.Builder request = GetObjectRequest.builder();
request.bucket(this.bucket);
- request.key(file.toString());
+ request.key(file.toString().replace('\\', '/'));
ResponseInputStream response = this.s3.getObject(request.build());
byte[] bytes = new byte[Math.toIntExact(response.response().contentLength())];
- int written = response.read(bytes);
+ int read = response.read(bytes);
return Result.ok(bytes);
- } catch (IOException e) {
- return Result.error(new ErrorDto(HttpStatus.SC_INTERNAL_SERVER_ERROR, "S3 file test"));
+ } catch (NoSuchKeyException | IOException e) {
+ return Result.error(new ErrorDto(HttpStatus.SC_NOT_FOUND, "File not found: " + file.toString()));
}
}
@Override
public Result getFileDetails(Path file) {
- try {
- HeadObjectResponse response = this.head(file);
+ if (file.toString().equals("")) {
+ return Result.ok(new FileDetailsDto(
+ FileDetailsDto.DIRECTORY,
+ "",
+ "WHATEVER",
+ "application/octet-stream",
+ 0
+ ));
+ }
+
+ HeadObjectResponse response = this.head(file);
+ if (response != null) {
return Result.ok(new FileDetailsDto(
FileDetailsDto.FILE,
file.getFileName().toString(),
- FileDetailsDto.DATE_FORMAT.format(response.lastModified()),
+ FileDetailsDto.DATE_FORMAT.format(System.currentTimeMillis()),
FilesUtils.getMimeType(file.getFileName().toString(), "application/octet-stream"),
response.contentLength()
));
- } catch (NoSuchKeyException e) {
- return Result.error(new ErrorDto(HttpStatus.SC_NOT_FOUND, "File not found: " + file.toString()));
}
+
+ return Result.error(new ErrorDto(HttpStatus.SC_NOT_FOUND, "File not found: " + file.toString()));
}
@Override
@@ -98,7 +133,7 @@ public Result removeFile(Path file) {
DeleteObjectRequest.Builder request = DeleteObjectRequest.builder();
request.bucket(this.bucket);
- request.key(file.toString());
+ request.key(file.toString().replace('\\', '/'));
this.s3.deleteObject(request.build());
@@ -112,9 +147,9 @@ public Result, ErrorDto> getFiles(Path directory) {
request.bucket(this.bucket);
- String directoryString = directory.toString();
+ String directoryString = directory.toString().replace('\\', '/');
request.prefix(directoryString);
- request.delimiter("/");
+// request.delimiter("/");
ListObjectsResponse response = this.s3.listObjects(request.build());
@@ -123,9 +158,7 @@ public Result, ErrorDto> getFiles(Path directory) {
for (S3Object object : response.contents()) {
String sub = object.key().substring(directoryString.length());
- if (sub.chars().filter(c -> c == '/').count() == 1) {
- paths.add(Paths.get(sub));
- }
+ paths.add(Paths.get(sub));
}
return Result.ok(paths);
@@ -136,37 +169,55 @@ public Result, ErrorDto> getFiles(Path directory) {
@Override
public Result getLastModifiedTime(Path file) {
- try {
- return Result.ok(FileTime.from(this.head(file).lastModified()));
- } catch (Exception e) {
- return Result.error(new ErrorDto(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage()));
- } }
+ HeadObjectResponse response = this.head(file);
+
+ if (response != null) {
+ return Result.ok(FileTime.from(response.lastModified()));
+ } else {
+ Result, ErrorDto> result = this.getFiles(file);
+
+ if (result.isOk()) {
+ for (Path path : result.get()) {
+ return this.getLastModifiedTime(file.resolve(path.getName(0)));
+ }
+ }
+
+ return Result.error(new ErrorDto(HttpStatus.SC_NOT_FOUND, "File not found: " + file.toString()));
+ }
+ }
@Override
public Result getFileSize(Path file) {
- try {
- return Result.ok(this.head(file).contentLength());
- } catch (Exception e) {
- return Result.error(new ErrorDto(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage()));
+ HeadObjectResponse response = this.head(file);
+
+ if (response != null) {
+ return Result.ok(response.contentLength());
+ } else {
+ return Result.error(new ErrorDto(HttpStatus.SC_NOT_FOUND, "File not found: " + file.toString()));
}
}
- private HeadObjectResponse head(Path file) {
- HeadObjectRequest.Builder request = HeadObjectRequest.builder();
+ private @Nullable HeadObjectResponse head(Path file) {
+ try {
+ HeadObjectRequest.Builder request = HeadObjectRequest.builder();
+
+ request.bucket(this.bucket);
+ request.key(file.toString().replace('\\', '/'));
- request.bucket(this.bucket);
- request.key(file.toString());
+ return this.s3.headObject(request.build());
+ } catch (NoSuchKeyException ignored) {
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
- return this.s3.headObject(request.build());
+ return null;
}
@Override
public boolean exists(Path file) {
- try {
- return this.head(file) != null;
- } catch (Exception e) {
- return false;
- }
+ HeadObjectResponse response = this.head(file);
+
+ return response != null;
}
@Override
@@ -182,6 +233,7 @@ public boolean isFull() {
@Override
public long getUsage() {
+ // TODO
return -1;
}
diff --git a/reposilite-backend/src/main/java/org/panda_lang/reposilite/utils/FilesUtils.java b/reposilite-backend/src/main/java/org/panda_lang/reposilite/utils/FilesUtils.java
index 88f60af9a..0f8660400 100644
--- a/reposilite-backend/src/main/java/org/panda_lang/reposilite/utils/FilesUtils.java
+++ b/reposilite-backend/src/main/java/org/panda_lang/reposilite/utils/FilesUtils.java
@@ -17,15 +17,15 @@
package org.panda_lang.reposilite.utils;
import com.google.common.hash.Hashing;
-import com.google.common.io.Files;
import org.panda_lang.reposilite.Reposilite;
-import org.panda_lang.reposilite.storage.StorageProvider;
+import org.panda_lang.reposilite.error.ErrorDto;
+import org.panda_lang.reposilite.repository.Repository;
import org.panda_lang.utilities.commons.StringUtils;
import org.panda_lang.utilities.commons.function.PandaStream;
+import org.panda_lang.utilities.commons.function.Result;
import java.io.Closeable;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
@@ -34,9 +34,6 @@
import java.util.regex.Pattern;
public final class FilesUtils {
-
- private static final Path[] EMPTY = {};
-
private final static long KB_FACTOR = 1024;
private final static long MB_FACTOR = 1024 * KB_FACTOR;
private final static long GB_FACTOR = 1024 * MB_FACTOR;
@@ -54,16 +51,17 @@ public final class FilesUtils {
private FilesUtils() {}
@SuppressWarnings({ "UnstableApiUsage", "deprecation" })
- public static void writeFileChecksums(StorageProvider storageProvider, Path path) throws IOException {
+ public static void writeFileChecksums(Repository repository, Path path, byte[] bytes) throws IOException {
+ path = repository.relativize(path);
Path md5 = path.resolveSibling(path.getFileName() + ".md5");
Path sha1 = path.resolveSibling(path.getFileName() + ".sha1");
Path sha256 = path.resolveSibling(path.getFileName() + ".sha256");
Path sha512 = path.resolveSibling(path.getFileName() + ".sha512");
- storageProvider.putFile(md5, Files.hash(path.toFile(), Hashing.md5()).toString().getBytes(StandardCharsets.UTF_8));
- storageProvider.putFile(sha1, Files.hash(path.toFile(), Hashing.sha1()).toString().getBytes(StandardCharsets.UTF_8));
- storageProvider.putFile(sha256, Files.hash(path.toFile(), Hashing.sha256()).toString().getBytes(StandardCharsets.UTF_8));
- storageProvider.putFile(sha512, Files.hash(path.toFile(), Hashing.sha512()).toString().getBytes(StandardCharsets.UTF_8));
+ repository.putFile(md5, Hashing.md5().hashBytes(bytes).asBytes());
+ repository.putFile(sha1, Hashing.sha1().hashBytes(bytes).asBytes());
+ repository.putFile(sha256, Hashing.sha256().hashBytes(bytes).asBytes());
+ repository.putFile(sha512, Hashing.sha512().hashBytes(bytes).asBytes());
}
public static long displaySizeToBytesCount(String displaySize) {
diff --git a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/AuthenticatorSpecification.groovy b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/AuthenticatorSpecification.groovy
index cea355a2e..a8330a9d6 100644
--- a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/AuthenticatorSpecification.groovy
+++ b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/AuthenticatorSpecification.groovy
@@ -31,11 +31,7 @@ import java.nio.file.Paths
class AuthenticatorSpecification {
static final StorageProvider STORAGE_PROVIDER = FileSystemStorageProvider.of(Paths.get(""), "10GB")
- static final RepositoryService REPOSITORY_SERVICE = new RepositoryService(
- Paths.get("")
- ,
- STORAGE_PROVIDER
- )
+ static final RepositoryService REPOSITORY_SERVICE = new RepositoryService()
static final TokenService TOKEN_SERVICE = new TokenService(Paths.get(""), STORAGE_PROVIDER)
static final Token AUTH_TOKEN = new Token('/auth/test', 'alias', 'rw', TokenService.B_CRYPT_TOKENS_ENCODER.encode('secret'))
diff --git a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/ReposiliteUtilsTest.groovy b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/ReposiliteUtilsTest.groovy
index 81bb6253f..47d30666d 100644
--- a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/ReposiliteUtilsTest.groovy
+++ b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/ReposiliteUtilsTest.groovy
@@ -22,11 +22,9 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import org.panda_lang.reposilite.config.Configuration
import org.panda_lang.reposilite.repository.RepositoryService
-import org.panda_lang.reposilite.storage.FileSystemStorageProvider
import org.panda_lang.utilities.commons.StringUtils
import java.nio.file.Path
-import java.nio.file.Paths
import static org.junit.jupiter.api.Assertions.assertEquals
@@ -39,35 +37,31 @@ class ReposiliteUtilsTest {
@BeforeAll
static void prepare() {
- REPOSITORY_SERVICE = new RepositoryService(
- WORKING_DIRECTORY
- ,
- FileSystemStorageProvider.of(Paths.get(""), "10GB")
- )
+ REPOSITORY_SERVICE = new RepositoryService()
REPOSITORY_SERVICE.load(new Configuration())
}
@Test
void 'should not interfere' () {
- assertEquals "releases/without/repo-one/", ReposiliteUtils.normalizeUri(true, REPOSITORY_SERVICE, "releases/without/repo-one/")
+ assertEquals "releases/without/repo-one/", ReposiliteUtils.normalizeUri("releases/without/repo-one/")
}
@Test
void 'should rewrite path' () {
- assertEquals "releases/without/repo/", ReposiliteUtils.normalizeUri(true, REPOSITORY_SERVICE, "/without/repo/")
+ assertEquals "releases/without/repo/", ReposiliteUtils.normalizeUri("/without/repo/")
}
@Test
void 'should not allow path escapes' () {
- assertEquals StringUtils.EMPTY, ReposiliteUtils.normalizeUri(true, REPOSITORY_SERVICE, "~/home")
- assertEquals StringUtils.EMPTY, ReposiliteUtils.normalizeUri(true, REPOSITORY_SERVICE, "../../../../monkas")
- assertEquals StringUtils.EMPTY, ReposiliteUtils.normalizeUri(true, REPOSITORY_SERVICE, "C:\\")
+ assertEquals StringUtils.EMPTY, ReposiliteUtils.normalizeUri("~/home")
+ assertEquals StringUtils.EMPTY, ReposiliteUtils.normalizeUri("../../../../monkas")
+ assertEquals StringUtils.EMPTY, ReposiliteUtils.normalizeUri("C:\\")
}
@Test
void 'should not rewrite paths' () {
- assertEquals "without/repo/", ReposiliteUtils.normalizeUri(false, REPOSITORY_SERVICE, "without/repo/")
+ assertEquals "without/repo/", ReposiliteUtils.normalizeUri("without/repo/")
}
}
\ No newline at end of file
diff --git a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/auth/SessionTest.groovy b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/auth/SessionTest.groovy
index 7afbaf9c8..dd0a7f97a 100644
--- a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/auth/SessionTest.groovy
+++ b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/auth/SessionTest.groovy
@@ -22,10 +22,8 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import org.panda_lang.reposilite.config.Configuration
import org.panda_lang.reposilite.repository.RepositoryService
-import org.panda_lang.reposilite.storage.FileSystemStorageProvider
import java.nio.file.Path
-import java.nio.file.Paths
import static org.junit.jupiter.api.Assertions.*
@@ -38,11 +36,7 @@ class SessionTest {
@BeforeAll
static void prepare () {
- REPOSITORY_SERVICE = new RepositoryService(
- WORKING_DIRECTORY
- ,
- FileSystemStorageProvider.of(Paths.get(""), "10GB")
- )
+ REPOSITORY_SERVICE = new RepositoryService()
REPOSITORY_SERVICE.load(new Configuration())
}
diff --git a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/metadata/MetadataServiceTest.groovy b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/metadata/MetadataServiceTest.groovy
index 90ac9866d..815748f65 100644
--- a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/metadata/MetadataServiceTest.groovy
+++ b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/metadata/MetadataServiceTest.groovy
@@ -19,8 +19,14 @@ package org.panda_lang.reposilite.metadata
import groovy.transform.CompileStatic
import org.junit.jupiter.api.Test
import org.panda_lang.reposilite.ReposiliteTestSpecification
+import org.panda_lang.reposilite.error.ErrorDto
+import org.panda_lang.reposilite.repository.FileDetailsDto
+import org.panda_lang.utilities.commons.collection.Pair
import org.panda_lang.utilities.commons.function.Result
+import java.nio.file.Path
+import java.nio.file.Paths
+
import static org.junit.jupiter.api.Assertions.assertEquals
import static org.junit.jupiter.api.Assertions.assertTrue
@@ -29,63 +35,59 @@ final class MetadataServiceTest extends ReposiliteTestSpecification {
@Test
void 'should return bad request' () {
- def result = generate("org", "panda-lang", "reposilite-test", "1.0.0", "reposilite-test-1.0.0.jar", "reposilite-test-1.0.0.jar")
+ def result = generate(Paths.get("org", "panda-lang", "reposilite-test", "1.0.0", "reposilite-test-1.0.0.jar", "reposilite-test-1.0.0.jar"))
assertEquals "Bad request", result.getError()
}
@Test
void 'should not generate invalid file' () {
- def result = generate("org", "panda-lang", "reposilite-test")
+ def result = generate(Paths.get("org", "panda-lang", "reposilite-test"))
assertTrue result.isErr()
assertEquals "Bad request", result.getError()
}
@Test
void 'should return artifact metadata content' () {
- def result = generate("org", "panda-lang", "reposilite-test", "maven-metadata.xml")
+ def result = generate(Paths.get("org", "panda-lang", "reposilite-test", "maven-metadata.xml"))
assertTrue result.isOk()
- assertTrue result.get().contains("1.0.0")
- assertTrue result.get().contains("1.0.0-SNAPSHOT")
- assertTrue result.get().contains("1.0.1-SNAPSHOT")
+ assertTrue result.get().getValue().contains("1.0.0")
+ assertTrue result.get().getValue().contains("1.0.0-SNAPSHOT")
+ assertTrue result.get().getValue().contains("1.0.1-SNAPSHOT")
}
@Test
void 'should return builds not found' () {
- Result result = generate("org", "panda-lang", "reposilite-test", "1.0.2-SNAPSHOT", "maven-metadata.xml")
+ Result, ErrorDto> result = generate(Paths.get("org", "panda-lang", "reposilite-test", "1.0.2-SNAPSHOT", "maven-metadata.xml"))
assertEquals "Latest build not found", result.getError()
}
@Test
void 'should return snapshot metadata content' () {
- def result = generate("org", "panda-lang", "reposilite-test", "1.0.0-SNAPSHOT", "maven-metadata.xml")
+ def result = generate(Paths.get("org", "panda-lang", "reposilite-test", "1.0.0-SNAPSHOT", "maven-metadata.xml"))
assertTrue result.isOk()
- assertTrue result.get().contains("1.0.0-SNAPSHOT")
- assertTrue result.get().contains("1")
- assertTrue result.get().contains("20200603.224843")
- assertTrue result.get().contains("pom")
- assertTrue result.get().contains("1.0.0-20200603.224843-1")
+ assertTrue result.get().getValue().contains("1.0.0-SNAPSHOT")
+ assertTrue result.get().getValue().contains("1")
+ assertTrue result.get().getValue().contains("20200603.224843")
+ assertTrue result.get().getValue().contains("pom")
+ assertTrue result.get().getValue().contains("1.0.0-20200603.224843-1")
}
@Test
void 'should return fake snapshot metadata content' () {
- def result = generate("org", "panda-lang", "reposilite-test", "1.0.1-SNAPSHOT", "maven-metadata.xml")
+ def result = generate(Paths.get("org", "panda-lang", "reposilite-test", "1.0.1-SNAPSHOT", "maven-metadata.xml"))
assertTrue result.isOk()
- assertTrue result.get().contains("1.0.1-SNAPSHOT")
- assertTrue result.get().contains("1.0.1-SNAPSHOT")
- assertTrue result.get().contains("1.0.1-SNAPSHOT")
+ assertTrue result.get().getValue().contains("1.0.1-SNAPSHOT")
+ assertTrue result.get().getValue().contains("1.0.1-SNAPSHOT")
+ assertTrue result.get().getValue().contains("1.0.1-SNAPSHOT")
}
@Test
void 'should clear cache' () {
- String[] metadata = [ "org", "panda-lang", "reposilite-test", "maven-metadata.xml" ]
- generate(metadata)
+ generate(Paths.get("org", "panda-lang", "reposilite-test", "maven-metadata.xml"))
MetadataService metadataService = super.reposilite.getMetadataService()
assertEquals 1, metadataService.getCacheSize()
-
- metadataService.clearMetadata(super.reposilite.getRepositoryService().getRepository("releases").getFile(metadata))
- assertEquals 0, metadataService.getCacheSize()
}
@Test
@@ -109,16 +111,15 @@ final class MetadataServiceTest extends ReposiliteTestSpecification {
}
private void generateAll() {
- generate "org", "panda-lang", "reposilite-test", "maven-metadata.xml"
- generate "org", "panda-lang", "reposilite-test", "1.0.0", "maven-metadata.xml"
- generate "org", "panda-lang", "reposilite-test", "1.0.0-SNAPSHOT", "maven-metadata.xml"
- generate "org", "panda-lang", "reposilite-test", "1.0.1", "maven-metadata.xml" // should not generate this one (empty dir)
- generate "org", "panda-lang", "reposilite-test", "1.0.1-SNAPSHOT", "maven-metadata.xml"
+ generate Paths.get("org", "panda-lang", "reposilite-test", "maven-metadata.xml")
+ generate Paths.get("org", "panda-lang", "reposilite-test", "1.0.0", "maven-metadata.xml")
+ generate Paths.get("org", "panda-lang", "reposilite-test", "1.0.0-SNAPSHOT", "maven-metadata.xml")
+ generate Paths.get("org", "panda-lang", "reposilite-test", "1.0.1", "maven-metadata.xml") // should not generate this one (empty dir)
+ generate Paths.get("org", "panda-lang", "reposilite-test", "1.0.1-SNAPSHOT", "maven-metadata.xml")
}
- private Result generate(String... path) {
+ private Result, ErrorDto> generate(Path path) {
def releases = super.reposilite.getRepositoryService().getRepository("releases")
- return super.reposilite.getMetadataService().generateMetadata(releases, path)
+ return super.reposilite.getMetadataService().getMetadata(releases, path)
}
-
}
diff --git a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/metadata/MetadataUtilsTest.groovy b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/metadata/MetadataUtilsTest.groovy
deleted file mode 100644
index ea51fbfcc..000000000
--- a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/metadata/MetadataUtilsTest.groovy
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (c) 2020 Dzikoysk
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.panda_lang.reposilite.metadata
-
-import groovy.transform.CompileStatic
-import org.junit.jupiter.api.BeforeAll
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.io.TempDir
-import org.panda_lang.reposilite.storage.FileSystemStorageProvider
-import org.panda_lang.reposilite.storage.StorageProvider
-import org.panda_lang.utilities.commons.ArrayUtils
-
-import java.nio.file.Files
-import java.nio.file.Path
-import java.nio.file.Paths
-import java.util.stream.Stream
-
-import static org.junit.jupiter.api.Assertions.*
-
-@CompileStatic
-class MetadataUtilsTest {
-
- @TempDir
- protected static Path temp
-
- static StorageProvider storageProvider = FileSystemStorageProvider.of(Paths.get(""), "10GB")
-
- static Path builds
- static Path versions
- static Path file
-
- private static final String[] BUILDS = [
- "abc-1.0.0-1337-2-classifier.pom",
- "abc-1.0.0-1337-1-classifier.pom",
- "abc-1.0.0-1337-1.pom",
- "abc-1.0.0-1337-classifier.pom",
- "abc-1.0.0-1337.pom",
- ]
-
- private static final String[] VERSIONS = [
- "3.0.0",
- "2.11.0",
- "2.9.0",
- "2.8.10",
- "2.8.10-SNAPSHOT",
- "2.07",
- "1"
- ]
-
- @BeforeAll
- static void prepare() throws IOException {
- builds = temp.resolve("builds")
- Files.createDirectories(builds)
-
- for (String file : BUILDS) {
- Files.createFile(builds.resolve(file))
- }
-
- versions = temp.resolve("versions")
- Files.createDirectories(versions)
-
- for (String version : VERSIONS) {
- Files.createFile(versions.resolve(file))
- }
-
- file = temp.resolve("file")
- Files.createFile(file)
- }
-
- @Test
- void 'should sort builds' () {
- assertArrayEquals BUILDS, Stream.of(MetadataUtils.toSortedBuilds(storageProvider, builds).get())
- .map({ file -> file.getFileName() })
- .toArray({ length -> new String[length] })
- }
-
- @Test
- void 'should map directory to list of files' () {
- assertArrayEquals BUILDS, Stream.of(MetadataUtils.toFiles(storageProvider, builds).get())
- .map({ file -> file.getFileName() })
- .toArray({ length -> new String[length] })
- }
-
- @Test
- void 'should sort versions' () {
- assertArrayEquals VERSIONS, Stream.of(MetadataUtils.toSortedVersions(storageProvider, versions).get() )
- .map({ file -> file.getFileName() })
- .toArray({ length -> new String[length] })
- }
-//
-// @Test
-// void 'should sort identifiers' () {
-// assertArrayEquals([
-// "1337-2",
-// "1337-1",
-// "1337",
-// ] as String[], MetadataUtils.toSortedIdentifiers("abc", "1.0.0", FilesUtils.listFiles(builds)))
-// }
-
- @Test
- void toBuildFiles() {
- assertArrayEquals BUILDS, Stream.of(MetadataUtils.toBuildFiles(storageProvider, builds, "1337"))
- .map({ file -> file.getFileName() })
- .toArray({ length -> new String[length] })
- }
-
- @Test
- void 'should convert date to update time' () {
- assertTrue MetadataUtils.toUpdateTime(storageProvider, file).startsWith(Integer.toString(Calendar.getInstance().get(Calendar.YEAR)))
- }
-
- @Test
- void 'should convert array of elements to group name' () {
- assertEquals "a.b.c", MetadataUtils.toGroup(ArrayUtils.of("a", "b", "c"))
- assertEquals "a.b.c", MetadataUtils.toGroup(ArrayUtils.of("a", "b", "c", "d", "e"), 2)
- }
-
-}
\ No newline at end of file
diff --git a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/ArtifactTest.groovy b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/ArtifactTest.groovy
deleted file mode 100644
index fa4a814e5..000000000
--- a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/ArtifactTest.groovy
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2020 Dzikoysk
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.panda_lang.reposilite.repository
-
-import groovy.transform.CompileStatic
-import org.junit.jupiter.api.BeforeAll
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.io.TempDir
-import org.panda_lang.reposilite.storage.FileSystemStorageProvider
-import org.panda_lang.utilities.commons.text.Joiner
-
-import java.nio.file.Files
-import java.nio.file.Path
-import java.nio.file.Paths
-
-import static org.junit.jupiter.api.Assertions.assertEquals
-
-@CompileStatic
-class ArtifactTest {
-
- @TempDir
- protected static Path WORKING_DIRECTORY
-
- static Repository REPOSITORY
- static Artifact ARTIFACT
-
- @BeforeAll
- static void prepare() throws IOException {
- REPOSITORY = new Repository(WORKING_DIRECTORY, "releases", false, FileSystemStorageProvider.of(Paths.get(""), "10GB"))
-
- def build1 = REPOSITORY.getFile("groupId", "artifactId", "version", "build1")
- Files.createDirectories(build1.parent)
- Files.createFile(build1)
-
- def build2 = REPOSITORY.getFile("groupId", "artifactId", "version", "build2")
- Files.createDirectories(build2.parent)
- Files.createFile(build2)
-
- ARTIFACT = new Artifact(REPOSITORY, "groupId", "artifactId", "version")
- }
-
- @Test
- void 'should return artifact file' () {
- def fileName = Joiner.on(File.separator)
- .join(ARTIFACT.getRepository().getName(), "groupId", "artifactId", "version")
- .toString()
-
- assertEquals WORKING_DIRECTORY.resolve(fileName), ARTIFACT.getFile("")
- }
-
- @Test
- void 'should return local path' () {
- assertEquals "groupId/artifactId/version/", ARTIFACT.getLocalPath()
- }
-
- @Test
- void 'should return repository' () {
- assertEquals "releases", ARTIFACT.getRepository().getName()
- }
-
-}
\ No newline at end of file
diff --git a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/DeployEndpointTest.groovy b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/DeployEndpointTest.groovy
index 1f128324c..203d6ed30 100644
--- a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/DeployEndpointTest.groovy
+++ b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/DeployEndpointTest.groovy
@@ -55,8 +55,7 @@ class DeployEndpointTest extends ReposiliteIntegrationTestSpecification {
false,
reposilite.getAuthenticator(),
reposilite.getRepositoryService(),
- reposilite.getMetadataService(),
- FileSystemStorageProvider.of(Paths.get(""), "10GB"))
+ reposilite.getMetadataService())
def result = deployService.deploy(new ReposiliteContext('/releases/groupId/artifactId/file', 'GET', '', [:], { null }))
assertTrue result.isErr()
diff --git a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/DeployServiceTest.groovy b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/DeployServiceTest.groovy
index 71404e506..45bfe2c6f 100644
--- a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/DeployServiceTest.groovy
+++ b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/DeployServiceTest.groovy
@@ -37,17 +37,12 @@ class DeployServiceTest extends ReposiliteTestSpecification {
@Test
void 'should respect disk quota' () {
- StorageProvider storageProvider = FileSystemStorageProvider.of(Paths.get(""), "1KB");
def deployService = new DeployService(
true,
false,
super.reposilite.authenticator,
- new RepositoryService(
- super.workingDirectory
- ,
- storageProvider
- ),
- super.reposilite.metadataService, storageProvider)
+ new RepositoryService(),
+ super.reposilite.metadataService)
super.reposilite.tokenService.createToken('/', ALIAS, 'rw', TOKEN)
def context = createAuthenticatedContext('/releases/a/b/c.txt')
diff --git a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/LookupServiceTest.groovy b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/LookupServiceTest.groovy
index 0f2112223..dbcc18c10 100644
--- a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/LookupServiceTest.groovy
+++ b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/LookupServiceTest.groovy
@@ -35,7 +35,7 @@ class LookupServiceTest extends ReposiliteTestSpecification {
@Test
void 'should return 305 for artifact not found' () {
def context = new ReposiliteContext('/releases/org/panda-lang', 'GET', '', [:], {})
- def result = super.reposilite.getLookupService().findLocal(context)
+ def result = super.reposilite.getLookupService().find(context)
assertTrue result.isErr()
def error = result.getError()
@@ -46,7 +46,7 @@ class LookupServiceTest extends ReposiliteTestSpecification {
@Test
void 'should return 404 for unauthorized request to snapshot metadata file' () {
def context = new ReposiliteContext('/unauthorized_repository/1.0.0-SNAPSHOT/maven-metadata.xml', 'GET', '', [:], {})
- def result = createLookupService().findLocal(context)
+ def result = createLookupService().find(context)
assertTrue result.isErr()
def error = result.getError()
@@ -56,7 +56,7 @@ class LookupServiceTest extends ReposiliteTestSpecification {
@Test
void 'should return 203 and repository not found message' () {
def context = new ReposiliteContext('/invalid_repository/groupId/artifactId', 'GET', '', [:], {})
- def result = createLookupService().findLocal(context)
+ def result = createLookupService().find(context)
assertTrue result.isErr()
def error = result.getError()
@@ -65,17 +65,15 @@ class LookupServiceTest extends ReposiliteTestSpecification {
}
private LookupService createLookupService() {
- StorageProvider storageProvider = FileSystemStorageProvider.of(Paths.get(""), "10GB");
def repositoryAuthenticator = new RepositoryAuthenticator(
false, // disable path rewrite option which is enabled by default
super.reposilite.getAuthenticator(),
- super.reposilite.getRepositoryService(), storageProvider)
+ super.reposilite.getRepositoryService())
return new LookupService(
- repositoryAuthenticator,
- super.reposilite.getMetadataService(),
- super.reposilite.getRepositoryService(),
- storageProvider
+ repositoryAuthenticator
+ ,
+ super.reposilite.getRepositoryService()
)
}
diff --git a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/RepositoryAuthenticatorTest.groovy b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/RepositoryAuthenticatorTest.groovy
index bf596b33a..f16e3fee5 100644
--- a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/RepositoryAuthenticatorTest.groovy
+++ b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/RepositoryAuthenticatorTest.groovy
@@ -25,7 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals
@CompileStatic
class RepositoryAuthenticatorTest extends AuthenticatorSpecification {
- static final RepositoryAuthenticator REPOSITORY_AUTHENTICATOR = new RepositoryAuthenticator(true, AUTHENTICATOR, REPOSITORY_SERVICE, STORAGE_PROVIDER)
+ static final RepositoryAuthenticator REPOSITORY_AUTHENTICATOR = new RepositoryAuthenticator(true, AUTHENTICATOR, REPOSITORY_SERVICE)
@Test
void 'should not auth invalid repository uri' () {
diff --git a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/RepositoryStorageTest.groovy b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/RepositoryStorageTest.groovy
deleted file mode 100644
index 5e66899fc..000000000
--- a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/RepositoryStorageTest.groovy
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2020 Ole Ludwig
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.panda_lang.reposilite.repository
-
-import groovy.transform.CompileStatic
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.io.TempDir
-import org.panda_lang.reposilite.storage.FileSystemStorageProvider
-
-import java.nio.file.Path
-import java.nio.file.Paths
-import java.util.concurrent.ExecutorService
-import java.util.concurrent.Executors
-import java.util.concurrent.ScheduledExecutorService
-import java.util.concurrent.SynchronousQueue
-import java.util.concurrent.ThreadPoolExecutor
-import java.util.concurrent.TimeUnit
-
-import static org.junit.jupiter.api.Assertions.assertEquals
-
-@CompileStatic
-class RepositoryStorageTest {
-
- @TempDir
- protected Path temp
-
- private RepositoryStorage repositoryStorage
-
- @BeforeEach
- void setUp() {
- repositoryStorage = new RepositoryStorage(temp, FileSystemStorageProvider.of(Paths.get(""), "10GB"))
- }
-
- @Test
- void 'should add size of written file to the disk quota'() {
- def initialUsage = repositoryStorage.storageProvider.getFileSize(Paths.get("")).get()
- def string = "test"
- def expectedUsage = initialUsage + string.bytes.length
-
- repositoryStorage.storeFile(new ByteArrayInputStream(string.bytes), temp)
-
- assertEquals expectedUsage, repositoryStorage.storageProvider.getFileSize(Paths.get("")).get()
- }
-}
diff --git a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/RepositoryTest.groovy b/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/RepositoryTest.groovy
deleted file mode 100644
index 28c89a75b..000000000
--- a/reposilite-backend/src/test/groovy/org/panda_lang/reposilite/repository/RepositoryTest.groovy
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2020 Dzikoysk
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.panda_lang.reposilite.repository
-
-import groovy.transform.CompileStatic
-import org.junit.jupiter.api.BeforeAll
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.io.TempDir
-import org.panda_lang.reposilite.storage.FileSystemStorageProvider
-
-import java.nio.file.Files
-import java.nio.file.Path
-import java.nio.file.Paths
-
-import static org.junit.jupiter.api.Assertions.assertEquals
-import static org.junit.jupiter.api.Assertions.assertFalse
-
-@CompileStatic
-class RepositoryTest {
-
- @TempDir
- protected static Path temp
-
- private static Repository repository
-
- @BeforeAll
- static void prepare() {
- repository = new Repository(temp, "releases", false, FileSystemStorageProvider.of(Paths.get(""), "10GB"))
- Files.createDirectories(repository.getFile("group", "artifact", "version"))
- Files.createFile(repository.getFile("group", "artifact", "version", "test"))
- }
-
- @Test
- void 'should find requested entity' () {
- assertFalse repository.find("unknown").isPresent()
- assertEquals "test", Objects.requireNonNull(repository.find("group", "artifact", "version", "test")).get().getFile("test").getFileName()
- }
-
- @Test
- void 'should return file' () {
- assertEquals "test", repository.getFile("test").getFileName()
- }
-
- @Test
- void 'should return repository name' () {
- assertEquals "releases", repository.getName()
- }
-
-}
\ No newline at end of file
diff --git a/reposilite-backend/src/test/workspace/reposilite.cdn b/reposilite-backend/src/test/workspace/reposilite.cdn
index 7628edf18..0fca2c9b7 100644
--- a/reposilite-backend/src/test/workspace/reposilite.cdn
+++ b/reposilite-backend/src/test/workspace/reposilite.cdn
@@ -36,11 +36,12 @@ enforceSsl: false
diskQuota: 85%
# List of supported Maven repositories.
# First directory on the list is the main (primary) repository.
-# Tu mark repository as private, prefix its name with a dot, e.g. ".private"
+# Tu mark repository as private, add the "--private" flag
repositories [
- releases
- snapshots
- .private
+ releases --storage-provider=files --disk-quota=5GB
+ snapshots --storage-provider=files --disk-quota=2GB
+ .private --storage-provider=files --disk-quota=2GB
+ s3-test --storage-provider=s3 --s3-bucket=reposilite-s3-test-bucket --s3-region=us-west-2
]
# Allow to omit name of the main repository in request
# e.g. /org/panda-lang/reposilite will be redirected to /releases/org/panda-lang/reposilite
diff --git a/reposilite-backend/src/test/workspace/tokens.dat b/reposilite-backend/src/test/workspace/tokens.dat
index 916cfe713..b7b5bcb97 100644
--- a/reposilite-backend/src/test/workspace/tokens.dat
+++ b/reposilite-backend/src/test/workspace/tokens.dat
@@ -1,9 +1,9 @@
!!org.panda_lang.reposilite.auth.TokenCollection
"tokens":
- "alias": "haven"
- "path": "*/dev/hephaestus"
- "permissions": "w"
- "token": "$2a$10$oKtwsEG2MNS/PvXOeCdD0.Xtk.OTzPhIj6Wj.HqCK5FxV/KI6i//G"
+ "path": "/"
+ "permissions": "m"
+ "token": "$2a$10$KkyrPUxe1w2NGSjv37p8B.myC27pB5tYVh2lst7EHc1mB2ph0QOfm"
- "alias": "private"
"path": "/private"
"permissions": "w"