From aee67b05bcf713145b76640e9903546bd19ab406 Mon Sep 17 00:00:00 2001 From: Andrey Popov Date: Fri, 21 Oct 2022 13:52:06 +0300 Subject: [PATCH 1/2] feat: Provide web gui for {list, add, add, update, remove} repository settings --- src/main/java/com/artipie/front/Service.java | 82 +++-- .../com/artipie/front/rest/AuthService.java | 31 +- .../com/artipie/front/rest/BaseService.java | 194 ++++++++---- .../artipie/front/rest/RepositoryName.java | 141 +++++++++ .../artipie/front/rest/RepositoryService.java | 96 +++++- .../java/com/artipie/front/ui/PostSignIn.java | 24 +- .../java/com/artipie/front/ui/RepoPage.java | 166 ---------- .../java/com/artipie/front/ui/UserPage.java | 38 --- .../front/ui/repository/RepoAddConfig.java | 66 ++++ .../front/ui/repository/RepoAddInfo.java | 32 ++ .../artipie/front/ui/repository/RepoEdit.java | 96 ++++++ .../artipie/front/ui/repository/RepoList.java | 23 +- .../front/ui/repository/RepoRemove.java | 46 +++ .../artipie/front/ui/repository/RepoSave.java | 50 +++ .../front/ui/repository/RepositoryInfo.java | 68 ++++ .../ui/repository/RepositoryTemplate.java | 74 +++++ .../ui/repository/flat/package-info.java | 10 - .../ui/repository/org/RepoListByUser.java | 45 --- .../front/ui/repository/org/package-info.java | 10 - src/main/resources/html/base | 33 +- src/main/resources/html/repo | 295 ------------------ src/main/resources/html/repository/add | 27 ++ src/main/resources/html/repository/add_config | 23 ++ .../html/{user => repository/add_info} | 24 +- src/main/resources/html/repository/edit | 57 ++++ src/main/resources/html/repository/list | 30 +- src/main/resources/html/repository/org/list | 29 -- src/main/resources/html/repository/result | 18 ++ src/main/resources/info/conda.info.html | 18 ++ src/main/resources/info/deb.info.html | 6 + src/main/resources/info/docker.info.html | 8 + src/main/resources/info/file-proxy.info.html | 0 src/main/resources/info/file.info.html | 9 + src/main/resources/info/gem.info.html | 13 + src/main/resources/info/go.info.html | 9 + src/main/resources/info/helm.info.html | 9 + src/main/resources/info/maven-group.info.html | 8 + src/main/resources/info/maven-proxy.info.html | 18 ++ src/main/resources/info/maven.info.html | 31 ++ src/main/resources/info/npm-proxy.info.html | 0 src/main/resources/info/npm.info.html | 6 + src/main/resources/info/nuget.info.html | 21 ++ src/main/resources/info/php-proxy.info.html | 0 src/main/resources/info/php.info.html | 26 ++ src/main/resources/info/pypi-proxy.info.html | 0 src/main/resources/info/pypi.info.html | 9 + src/main/resources/info/rpm.info.html | 14 + .../resources/template/default.template.yaml | 9 + .../template/maven-group.template.yaml | 14 + .../template/maven-proxy.template.yaml | 14 + 50 files changed, 1310 insertions(+), 760 deletions(-) create mode 100644 src/main/java/com/artipie/front/rest/RepositoryName.java delete mode 100644 src/main/java/com/artipie/front/ui/RepoPage.java delete mode 100644 src/main/java/com/artipie/front/ui/UserPage.java create mode 100644 src/main/java/com/artipie/front/ui/repository/RepoAddConfig.java create mode 100644 src/main/java/com/artipie/front/ui/repository/RepoAddInfo.java create mode 100644 src/main/java/com/artipie/front/ui/repository/RepoEdit.java create mode 100644 src/main/java/com/artipie/front/ui/repository/RepoRemove.java create mode 100644 src/main/java/com/artipie/front/ui/repository/RepoSave.java create mode 100644 src/main/java/com/artipie/front/ui/repository/RepositoryInfo.java create mode 100644 src/main/java/com/artipie/front/ui/repository/RepositoryTemplate.java delete mode 100644 src/main/java/com/artipie/front/ui/repository/flat/package-info.java delete mode 100644 src/main/java/com/artipie/front/ui/repository/org/RepoListByUser.java delete mode 100644 src/main/java/com/artipie/front/ui/repository/org/package-info.java delete mode 100644 src/main/resources/html/repo create mode 100644 src/main/resources/html/repository/add create mode 100644 src/main/resources/html/repository/add_config rename src/main/resources/html/{user => repository/add_info} (71%) create mode 100644 src/main/resources/html/repository/edit delete mode 100644 src/main/resources/html/repository/org/list create mode 100644 src/main/resources/html/repository/result create mode 100644 src/main/resources/info/conda.info.html create mode 100644 src/main/resources/info/deb.info.html create mode 100644 src/main/resources/info/docker.info.html create mode 100644 src/main/resources/info/file-proxy.info.html create mode 100644 src/main/resources/info/file.info.html create mode 100644 src/main/resources/info/gem.info.html create mode 100644 src/main/resources/info/go.info.html create mode 100644 src/main/resources/info/helm.info.html create mode 100644 src/main/resources/info/maven-group.info.html create mode 100644 src/main/resources/info/maven-proxy.info.html create mode 100644 src/main/resources/info/maven.info.html create mode 100644 src/main/resources/info/npm-proxy.info.html create mode 100644 src/main/resources/info/npm.info.html create mode 100644 src/main/resources/info/nuget.info.html create mode 100644 src/main/resources/info/php-proxy.info.html create mode 100644 src/main/resources/info/php.info.html create mode 100644 src/main/resources/info/pypi-proxy.info.html create mode 100644 src/main/resources/info/pypi.info.html create mode 100644 src/main/resources/info/rpm.info.html create mode 100644 src/main/resources/template/default.template.yaml create mode 100644 src/main/resources/template/maven-group.template.yaml create mode 100644 src/main/resources/template/maven-proxy.template.yaml diff --git a/src/main/java/com/artipie/front/Service.java b/src/main/java/com/artipie/front/Service.java index 276c62c..bf495c8 100644 --- a/src/main/java/com/artipie/front/Service.java +++ b/src/main/java/com/artipie/front/Service.java @@ -27,11 +27,15 @@ import com.artipie.front.settings.YamlRepoPermissions; import com.artipie.front.ui.HbTemplateEngine; import com.artipie.front.ui.PostSignIn; -import com.artipie.front.ui.RepoPage; import com.artipie.front.ui.SignInPage; -import com.artipie.front.ui.UserPage; +import com.artipie.front.ui.repository.RepoAddConfig; +import com.artipie.front.ui.repository.RepoAddInfo; +import com.artipie.front.ui.repository.RepoEdit; import com.artipie.front.ui.repository.RepoList; -import com.artipie.front.ui.repository.org.RepoListByUser; +import com.artipie.front.ui.repository.RepoRemove; +import com.artipie.front.ui.repository.RepoSave; +import com.artipie.front.ui.repository.RepositoryInfo; +import com.artipie.front.ui.repository.RepositoryTemplate; import com.fasterxml.jackson.core.JsonParseException; import com.jcabi.log.Logger; import java.io.File; @@ -290,36 +294,62 @@ void start(final int port) { this.ignite.path( "/dashboard", () -> { + this.ignite.get( + "", + (req, res) -> { + res.redirect("/dashboard/repository/list"); + return "Ok"; + } + ); final ArtipieEndpoint endpoint = this.settings.artipieEnpoint(); final RepositoryService repository = new RepositoryService(endpoint); + final RepositoryInfo info = new RepositoryInfo(); + final RepositoryTemplate template = new RepositoryTemplate(); this.ignite.path( "/repository", () -> { - this.ignite.get("/list", new RepoList(repository), engine); - if ("org".equals(this.settings.layout())) { - this.ignite.get("/list/:uname", new RepoListByUser(repository), engine); + final String layout = this.settings.layout(); + this.ignite.get("/list", new RepoList(repository, layout), engine); + if ("flat".equals(layout)) { + this.ignite.get( + "/edit/:repo", + new RepoEdit(repository, layout, info), + engine + ); + this.ignite.post( + "/update/:repo", + new RepoSave(repository, layout), + engine + ); + this.ignite.post( + "/remove/:repo", + new RepoRemove(repository, layout), + engine + ); + } else { + this.ignite.get( + "/edit/:user/:repo", + new RepoEdit(repository, layout, info), + engine + ); + this.ignite.post( + "/update/:user/:repo", + new RepoSave(repository, layout), + engine + ); + this.ignite.post( + "/remove/:user/:repo", + new RepoRemove(repository, layout), + engine + ); } + this.ignite.get("/add/info", new RepoAddInfo(), engine); + this.ignite.get( + "/add/config", + new RepoAddConfig(layout, info, template), + engine + ); } ); - final RepoSettings stn = new RepoSettings( - this.settings.layout(), this.settings.repoConfigsStorage() - ); - this.ignite.get("", new UserPage(stn), engine); - this.ignite.get( - new RequestPath().with(Users.USER_PARAM).toString(), new UserPage(stn), engine - ); - this.ignite.get( - new RequestPath().with(Users.USER_PARAM) - .with(Repositories.REPO_PARAM).toString(), - new RepoPage.TemplateView(stn), engine - ); - this.ignite.post( - new RequestPath().with("api").with("repos").with(Users.USER_PARAM).toString(), - new RepoPage.Post(stn) - ); - this.ignite.get( - new RequestPath().with("api").with("repos").with(Users.USER_PARAM).toString(), - new RepoPage.Get() - ); } ); this.ignite.before(AuthFilters.AUTHENTICATE); diff --git a/src/main/java/com/artipie/front/rest/AuthService.java b/src/main/java/com/artipie/front/rest/AuthService.java index 24fc675..6b5eb8f 100644 --- a/src/main/java/com/artipie/front/rest/AuthService.java +++ b/src/main/java/com/artipie/front/rest/AuthService.java @@ -5,9 +5,9 @@ package com.artipie.front.rest; import com.artipie.front.settings.ArtipieEndpoint; -import java.net.http.HttpResponse; +import io.vavr.Tuple3; +import java.util.Optional; import javax.json.Json; -import javax.servlet.http.HttpServletResponse; /** * Auth-service. @@ -29,22 +29,23 @@ public AuthService(final ArtipieEndpoint endpoint) { } /** - * Obtains JWT-token from auth rest-service. - * + * Obtain JWT-token from auth rest-service. * @param name User name. * @param password User password. - * @return JWT-token. + * @return Tuple3 of 'Status code, JWT-token, Error message'. */ - public String getJwtToken(final String name, final String password) { - final HttpResponse response = this.httpPost( - AuthService.TOKEN_PATH, - () -> - Json.createObjectBuilder() - .add("name", name) - .add("pass", password) - .build().toString() + public Tuple3 getJwtToken(final String name, final String password) { + return BaseService.handle( + this.httpPost( + Optional.empty(), + AuthService.TOKEN_PATH, + () -> + Json.createObjectBuilder() + .add("name", name) + .add("pass", password) + .build().toString() + ), + res -> BaseService.jsonObject(res).getString("token") ); - checkStatus(HttpServletResponse.SC_OK, response); - return BaseService.jsonObject(response).getString("token"); } } diff --git a/src/main/java/com/artipie/front/rest/BaseService.java b/src/main/java/com/artipie/front/rest/BaseService.java index a7ac46e..0fa1d9f 100644 --- a/src/main/java/com/artipie/front/rest/BaseService.java +++ b/src/main/java/com/artipie/front/rest/BaseService.java @@ -5,7 +5,10 @@ package com.artipie.front.rest; import com.artipie.ArtipieException; +import com.artipie.front.misc.Json2Yaml; import com.artipie.front.settings.ArtipieEndpoint; +import com.google.common.net.HttpHeaders; +import io.vavr.Tuple3; import java.io.IOException; import java.io.StringReader; import java.net.URI; @@ -15,10 +18,13 @@ import java.net.http.HttpResponse; import java.time.Duration; import java.time.temporal.ChronoUnit; +import java.util.Optional; +import java.util.function.Function; import java.util.function.Supplier; import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonObject; +import javax.servlet.http.HttpServletResponse; /** * Base rest-service. @@ -27,9 +33,9 @@ @SuppressWarnings({"PMD.DataClass", "PMD.AvoidFieldNameMatchingMethodName", "PMD.TooManyMethods"}) public class BaseService { /** - * Authorization header. + * Application json content-type value. */ - public static final String AUTHORIZATION = "Authorization"; + public static final String APPLICATION_JSON = "application/json"; /** * Http request timeout. @@ -76,34 +82,18 @@ protected static String bearer(final String token) { return String.format("Bearer %s", token); } - /** - * Checks response status. - * @param expected Expected status code. - * @param response Response. - * @throws ArtipieException In case mismatch expected status code in response. - */ - protected static void checkStatus(final int expected, final HttpResponse response) - throws ArtipieException { - if (response.statusCode() != expected) { - throw new ArtipieException( - String.format( - "Expected %s result code, but received %s", expected, response.statusCode() - ) - ); - } - } - /** * Invokes GET http request. + * @param token JWT token. * @param path Path in URL. * @return Http response. */ - protected HttpResponse httpGet(final String path) { + protected HttpResponse httpGet(final Optional token, final String path) { try { return HttpClient.newBuilder() .build() .send( - this.createGetRequest(path), + this.createGetRequest(token, path), HttpResponse.BodyHandlers.ofString() ); } catch (final IOException | InterruptedException exc) { @@ -112,17 +102,19 @@ protected HttpResponse httpGet(final String path) { } /** - * Invokes GET http request. + * Invokes POST http request. * @param token JWT token. * @param path Path in URL. + * @param payload Payload supplier. * @return Http response. */ - protected HttpResponse httpGet(final String token, final String path) { + protected HttpResponse httpPost(final Optional token, final String path, + final Supplier payload) { try { return HttpClient.newBuilder() .build() .send( - this.createGetRequest(token, path), + this.createPostRequest(token, path, payload), HttpResponse.BodyHandlers.ofString() ); } catch (final IOException | InterruptedException exc) { @@ -132,16 +124,18 @@ protected HttpResponse httpGet(final String token, final String path) { /** * Invokes POST http request. + * @param token JWT token. * @param path Path in URL. * @param payload Payload supplier. * @return Http response. */ - protected HttpResponse httpPost(final String path, final Supplier payload) { + protected HttpResponse httpPut(final Optional token, final String path, + final Supplier payload) { try { return HttpClient.newBuilder() .build() .send( - this.createPostRequest(path, payload), + this.createPutRequest(token, path, payload), HttpResponse.BodyHandlers.ofString() ); } catch (final IOException | InterruptedException exc) { @@ -150,19 +144,17 @@ protected HttpResponse httpPost(final String path, final Supplier httpPost(final String token, final String path, - final Supplier payload) { + protected HttpResponse httpDelete(final Optional token, final String path) { try { return HttpClient.newBuilder() .build() .send( - this.createPostRequest(token, path, payload), + this.createDeleteRequest(token, path), HttpResponse.BodyHandlers.ofString() ); } catch (final IOException | InterruptedException exc) { @@ -176,70 +168,81 @@ protected HttpResponse httpPost(final String token, final String path, * @param path Path in URL. * @return Http request. */ - protected HttpRequest createGetRequest(final String token, final String path) { + protected HttpRequest createGetRequest(final Optional token, final String path) { try { - return HttpRequest.newBuilder() + final HttpRequest.Builder builder = HttpRequest.newBuilder() .uri(this.uri(path)) .GET() - .timeout(BaseService.TIMEOUT) - .header(BaseService.AUTHORIZATION, bearer(token)) - .build(); + .header(HttpHeaders.ACCEPT, BaseService.APPLICATION_JSON) + .timeout(BaseService.TIMEOUT); + token.ifPresent(value -> builder.header(HttpHeaders.AUTHORIZATION, bearer(value))); + return builder.build(); } catch (final URISyntaxException exc) { throw new ArtipieException(exc); } } /** - * Creates GET http request. + * Creates POST http request. + * @param token JWT token. * @param path Path in URL. + * @param payload Payload supplier. * @return Http request. */ - protected HttpRequest createGetRequest(final String path) { + protected HttpRequest createPostRequest(final Optional token, final String path, + final Supplier payload) { try { - return HttpRequest.newBuilder() + final HttpRequest.Builder builder = HttpRequest.newBuilder() .uri(this.uri(path)) - .GET() - .timeout(BaseService.TIMEOUT) - .build(); + .POST(HttpRequest.BodyPublishers.ofString(payload.get())) + .header(HttpHeaders.ACCEPT, BaseService.APPLICATION_JSON) + .header(HttpHeaders.CONTENT_TYPE, BaseService.APPLICATION_JSON) + .timeout(BaseService.TIMEOUT); + token.ifPresent(value -> builder.header(HttpHeaders.AUTHORIZATION, bearer(value))); + return builder.build(); } catch (final URISyntaxException exc) { throw new ArtipieException(exc); } } /** - * Creates POST http request. + * Creates PUT http request. + * @param token JWT token. * @param path Path in URL. * @param payload Payload supplier. * @return Http request. */ - protected HttpRequest createPostRequest(final String path, final Supplier payload) { + protected HttpRequest createPutRequest(final Optional token, final String path, + final Supplier payload) { try { - return HttpRequest.newBuilder() + final HttpRequest.Builder builder = HttpRequest.newBuilder() .uri(this.uri(path)) - .POST(HttpRequest.BodyPublishers.ofString(payload.get())) - .timeout(BaseService.TIMEOUT) - .build(); + .PUT(HttpRequest.BodyPublishers.ofString(payload.get())) + .header(HttpHeaders.ACCEPT, BaseService.APPLICATION_JSON) + .header(HttpHeaders.CONTENT_TYPE, BaseService.APPLICATION_JSON) + .timeout(BaseService.TIMEOUT); + token.ifPresent(value -> builder.header(HttpHeaders.AUTHORIZATION, bearer(value))); + return builder.build(); } catch (final URISyntaxException exc) { throw new ArtipieException(exc); } } /** - * Creates POST http request. + * Creates DELETE http request. * @param token JWT token. * @param path Path in URL. - * @param payload Payload supplier. * @return Http request. */ - protected HttpRequest createPostRequest(final String token, final String path, - final Supplier payload) { + protected HttpRequest createDeleteRequest(final Optional token, final String path) { try { - return HttpRequest.newBuilder() + final HttpRequest.Builder builder = HttpRequest.newBuilder() .uri(this.uri(path)) - .POST(HttpRequest.BodyPublishers.ofString(payload.get())) - .header(BaseService.AUTHORIZATION, bearer(token)) - .timeout(BaseService.TIMEOUT) - .build(); + .DELETE() + .header(HttpHeaders.ACCEPT, BaseService.APPLICATION_JSON) + .timeout(BaseService.TIMEOUT); + token.ifPresent(value -> builder.header(HttpHeaders.AUTHORIZATION, bearer(value))); + return builder.build(); } catch (final URISyntaxException exc) { throw new ArtipieException(exc); } @@ -262,4 +265,81 @@ protected static JsonObject jsonObject(final HttpResponse response) { protected static JsonArray jsonArray(final HttpResponse response) { return Json.createReader(new StringReader(response.body())).readArray(); } + + /** + * Convert response json-body to yaml. + * @param response Response. + * @return Yaml content. + */ + protected static String toYaml(final HttpResponse response) { + return new Json2Yaml().apply(BaseService.jsonObject(response).toString()) + .toString(); + } + + /** + * Strip leading and ending quotes. + * @param str String with leading and ending quotes or without them. + * @return Stripped string. + */ + protected static String stripQuotes(final String str) { + String result = str; + if (str.length() > 1 && str.charAt(0) == '"' && str.charAt(str.length() - 1) == '"') { + result = str.substring(1, str.length() - 1); + } + return result; + } + + /** + * Check response result code and forms triple-tuple: + * 1. response status code. + * 2. resulting content in case success result code + * 3. error message based on response content in case receiveing of error in status code. + * @param response Response. + * @param map Map-function to form resulting content in case successful status code. + * @param Type of resulting of map-function. + * @return Triple-tuple. + */ + protected static Tuple3 handle(final HttpResponse response, + final Function, V> map) { + return handle(HttpServletResponse.SC_OK, response, map); + } + + /** + * Check response result code and forms triple-tuple: + * 1. response status code. + * 2. resulting content in case success result code + * 3. error message based on response content in case receiveing of error in status code. + * @param success Expected success result code. + * @param response Response. + * @param map Map-function to form resulting content in case successful status code. + * @param Type of resulting of map-function. + * @return Triple-tuple. + */ + protected static Tuple3 handle(final int success, + final HttpResponse response, final Function, V> map) { + V content = null; + String error = null; + if (success == response.statusCode()) { + content = map.apply(response); + } else { + error = response.body(); + } + return new Tuple3<>(response.statusCode(), content, error); + } + + /** + * Join path-parts. + * @param parts Parts of path. + * @return Path as joined by '/'-symbol path-parts. + */ + protected static String path(final Object...parts) { + final StringBuilder builder = new StringBuilder(); + for (final Object path : parts) { + builder.append(path).append('/'); + } + if (builder.length() > 0) { + builder.setLength(builder.length() - 1); + } + return builder.toString(); + } } diff --git a/src/main/java/com/artipie/front/rest/RepositoryName.java b/src/main/java/com/artipie/front/rest/RepositoryName.java new file mode 100644 index 0000000..8c54f73 --- /dev/null +++ b/src/main/java/com/artipie/front/rest/RepositoryName.java @@ -0,0 +1,141 @@ +/* + * The MIT License (MIT) Copyright (c) 2022 artipie.com + * https://github.com/artipie/front/LICENSE.txt + */ +package com.artipie.front.rest; + +import spark.Request; + +/** + * Repository name. + * + * @since 1.0 + */ +public interface RepositoryName { + /** + * Repository name path parameter name. + */ + String REPO = ":repo"; + + /** + * The name of the repository. + * + * @return String name + */ + String toString(); + + /** + * Repository name from request. + * + * @since 1.0 + */ + class FromRequest implements RepositoryName { + /** + * Request. + */ + private final Request request; + + /** + * Layout. + */ + private final String layout; + + /** + * Ctor. + * + * @param request Request + * @param layout Layout + */ + public FromRequest(final Request request, final String layout) { + this.request = request; + this.layout = layout; + } + + /** + * Provides string representation of repository name in toString() method. + *
    + *
  • 'reponame' for flat layout
  • + *
  • 'username/reponame' for org layout
  • + *
+ * + * @checkstyle NoJavadocForOverriddenMethodsCheck (10 lines) + */ + @Override + public String toString() { + final String reponame; + if ("flat".equals(this.layout)) { + reponame = new Flat( + this.request.params(RepositoryName.REPO) + ).toString(); + } else { + reponame = new Org( + this.request.params(RepositoryName.REPO), + this.request.session().attribute("uname") + ).toString(); + } + return reponame; + } + } + + /** + * Repository name for flat layout. + * + * @since 1.0 + */ + class Flat implements RepositoryName { + + /** + * Repository name. + */ + private final String repo; + + /** + * Ctor. + * + * @param repo Name + */ + public Flat(final String repo) { + this.repo = repo; + } + + @Override + public String toString() { + return this.repo; + } + } + + /** + * Repository name for org layout is combined from username and reponame: + * 'username/reponame'. + * + * @since 1.0 + */ + class Org implements RepositoryName { + + /** + * Repository name. + */ + private final String repo; + + /** + * User name. + */ + private final String user; + + /** + * Ctor. + * + * @param repo Repository name + * @param user User name + */ + public Org(final String repo, final String user) { + this.repo = repo; + this.user = user; + } + + @Override + public String toString() { + return String.format("%s/%s", this.user, this.repo); + } + } +} diff --git a/src/main/java/com/artipie/front/rest/RepositoryService.java b/src/main/java/com/artipie/front/rest/RepositoryService.java index f5fbcfa..1939160 100644 --- a/src/main/java/com/artipie/front/rest/RepositoryService.java +++ b/src/main/java/com/artipie/front/rest/RepositoryService.java @@ -4,13 +4,15 @@ */ package com.artipie.front.rest; +import com.artipie.front.misc.Yaml2Json; import com.artipie.front.settings.ArtipieEndpoint; +import io.vavr.Tuple3; import java.net.http.HttpResponse; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import javax.json.JsonArray; import javax.json.JsonValue; -import javax.servlet.http.HttpServletResponse; /** * Repository-service. @@ -18,10 +20,17 @@ * @since 1.0 */ public class RepositoryService extends BaseService { + /** + * Path to 'repository'. + */ + private static final String REPOSITORY_PATH = "/api/v1/repository"; + /** * Path to 'list' rest-api. */ - private static final String LIST_PATH = "/api/v1/repository/list"; + private static final String LIST_PATH = BaseService.path( + RepositoryService.REPOSITORY_PATH, "list" + ); /** * Ctor. @@ -33,27 +42,82 @@ public RepositoryService(final ArtipieEndpoint endpoint) { } /** - * Obtains list of repository names. + * Obtain list of repository names. * @param token Token. - * @return List of repository names. + * @return Tuple3 of 'Status code, List of repository names, Error message' */ - public List list(final String token) { - final HttpResponse response = this.httpGet(token, RepositoryService.LIST_PATH); - checkStatus(HttpServletResponse.SC_OK, response); - return RepositoryService.listOfStrings(response); + public Tuple3, String> list(final String token) { + return BaseService.handle( + this.httpGet(Optional.of(token), RepositoryService.LIST_PATH), + RepositoryService::listOfStrings + ); } /** - * Obtains list of repository names by user's name. + * Obtain list of repository names by user's name. * @param token Token. * @param uname User name. - * @return List of repository names. + * @return Tuple3 of 'Status code, List of repository names, Error message' + */ + public Tuple3, String> list(final String token, final String uname) { + return BaseService.handle( + this.httpGet( + Optional.of(token), + BaseService.path(RepositoryService.LIST_PATH, uname) + ), + RepositoryService::listOfStrings + ); + } + + /** + * Obtain repository content. + * @param token Token. + * @param rname Repository name. + * @return Tuple3 of 'Status code, Repository content, Error message' + */ + public Tuple3 repo(final String token, final RepositoryName rname) { + return BaseService.handle( + this.httpGet( + Optional.of(token), + BaseService.path(RepositoryService.REPOSITORY_PATH, rname) + ), + BaseService::toYaml + ); + } + + /** + * Save repository config. + * @param token Token. + * @param rname Repository name. + * @param config Repository config. + * @return Tuple3 of 'Status code, Resulting message, Error message' + */ + public Tuple3 save(final String token, final RepositoryName rname, + final String config) { + return BaseService.handle( + this.httpPut( + Optional.of(token), + BaseService.path(RepositoryService.REPOSITORY_PATH, rname), + () -> new Yaml2Json().apply(config).toString() + ), + res -> String.format("Repository %s saved successfully", rname) + ); + } + + /** + * Remove repository. + * @param token Token. + * @param rname Repository name. + * @return Tuple3 of 'Status code, Resulting message, Error message' */ - public List list(final String token, final String uname) { - final HttpResponse response = - this.httpGet(token, String.format("%s/%s", RepositoryService.LIST_PATH, uname)); - checkStatus(HttpServletResponse.SC_OK, response); - return RepositoryService.listOfStrings(response); + public Tuple3 remove(final String token, final RepositoryName rname) { + return BaseService.handle( + this.httpDelete( + Optional.of(token), + RepositoryService.path(RepositoryService.REPOSITORY_PATH, rname.toString()) + ), + res -> String.format("Repository %s removed", rname) + ); } /** @@ -66,7 +130,7 @@ private static List listOfStrings(final HttpResponse res) { final JsonArray array = BaseService.jsonArray(res); final List result = new ArrayList<>(array.size()); for (final JsonValue item : array) { - result.add(item.toString()); + result.add(BaseService.stripQuotes(item.toString())); } return result; } diff --git a/src/main/java/com/artipie/front/ui/PostSignIn.java b/src/main/java/com/artipie/front/ui/PostSignIn.java index 409dd9c..f6e586a 100644 --- a/src/main/java/com/artipie/front/ui/PostSignIn.java +++ b/src/main/java/com/artipie/front/ui/PostSignIn.java @@ -4,8 +4,8 @@ */ package com.artipie.front.ui; -import com.artipie.ArtipieException; import com.artipie.front.rest.AuthService; +import io.vavr.Tuple3; import java.util.Objects; import org.eclipse.jetty.http.HttpStatus; import spark.Request; @@ -49,18 +49,22 @@ public Object handle(final Request req, final Response rsp) throws Exception { Spark.halt(HttpStatus.BAD_REQUEST_400, "CRSF validation failed"); } String result = null; - try { - final String token = this.auth.getJwtToken( - req.queryParamOrDefault("username", ""), - req.queryParamOrDefault("password", "") - ); + final Tuple3 res = this.auth.getJwtToken( + req.queryParamOrDefault("username", ""), + req.queryParamOrDefault("password", "") + ); + if (res._3 == null) { req.session().attribute("uid", ""); req.session().attribute("uname", req.queryParamOrDefault("username", "")); - req.session().attribute("token", token); - rsp.redirect("/dashboard/repository/list"); + req.session().attribute("token", res._2); + rsp.redirect("/dashboard"); result = "OK"; - } catch (final ArtipieException exception) { - Spark.halt(HttpStatus.UNAUTHORIZED_401, "bad credentials"); + } else { + if (res._1 == HttpStatus.UNAUTHORIZED_401) { + Spark.halt(HttpStatus.UNAUTHORIZED_401, "bad credentials"); + } else { + Spark.halt(res._1, res._3); + } } return result; } diff --git a/src/main/java/com/artipie/front/ui/RepoPage.java b/src/main/java/com/artipie/front/ui/RepoPage.java deleted file mode 100644 index be27944..0000000 --- a/src/main/java/com/artipie/front/ui/RepoPage.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * The MIT License (MIT) Copyright (c) 2022 artipie.com - * https://github.com/artipie/front/LICENSE.txt - */ -package com.artipie.front.ui; - -import com.amihaiemil.eoyaml.Yaml; -import com.amihaiemil.eoyaml.YamlMapping; -import com.artipie.front.RequestAttr; -import com.artipie.front.api.Repositories; -import com.artipie.front.api.Users; -import com.artipie.front.misc.RouteWrap; -import com.artipie.front.misc.ValueFromBody; -import com.artipie.front.settings.RepoSettings; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import org.eclipse.jetty.http.HttpStatus; -import spark.Request; -import spark.Response; -import spark.Route; - -/** - * Dashboard repo page. - * @since 0.1 - */ -@SuppressWarnings("PMD.AvoidDuplicateLiterals") -public final class RepoPage { - - /** - * Dashboard repo page template view. - * @since 0.1 - */ - public static final class TemplateView extends RouteWrap.TemplateViewRoute { - - /** - * Template view wrap route. - * - * @param repos Repository settings - */ - public TemplateView(final RepoSettings repos) { - super( - new HbPage( - "repo", - req -> { - final String uid = RequestAttr.Standard.USER_ID.readOrThrow(req); - final String name = Repositories.REPO_PARAM.parse(req); - final Map res = new HashMap<>(5); - res.put("user", uid); - res.put("title", uid); - res.put("name", name); - if (repos.exists(name, uid)) { - try { - final YamlMapping yaml = Yaml.createYamlInput( - new ByteArrayInputStream(repos.value(name, uid)) - ).readYamlMapping(); - res.put("type", yaml.yamlMapping("repo").string("type")); - res.put("config", yaml.toString()); - } catch (final IOException err) { - throw new UncheckedIOException(err); - } - } else { - res.put("type", req.queryParamOrDefault("type", "maven")); - } - return res; - } - ) - ); - } - } - - /** - * Handles post request from dashboard repo page to remove/update repository. - * @since 0.1 - * @checkstyle ExecutableStatementCountCheck (100 lines) - * @checkstyle ReturnCountCheck (100 lines) - */ - public static final class Post implements Route { - - /** - * Repository settings. - */ - private final RepoSettings stn; - - /** - * Ctor. - * @param stn Repository settings - */ - public Post(final RepoSettings stn) { - this.stn = stn; - } - - @Override - @SuppressWarnings("PMD.OnlyOneReturn") - public Object handle(final Request request, final Response response) { - final ValueFromBody vals = new ValueFromBody(request.body()); - final String action = vals.byNameOrThrow("action"); - final String name = vals.byNameOrThrow("repo"); - final String uid = Users.USER_PARAM.parse(request); - if ("update".equals(action)) { - final YamlMapping yaml = Post.configsFromBody(vals); - final YamlMapping repo = yaml.yamlMapping("repo"); - if (repo == null) { - response.status(HttpStatus.BAD_REQUEST_400); - return "Repo section is required"; - } - if (repo.value("type") == null) { - response.status(HttpStatus.BAD_REQUEST_400); - return "Repository type is required"; - } - if (repo.value("storage") == null) { - response.status(HttpStatus.BAD_REQUEST_400); - return "Repository storage is required"; - } - if (this.stn.exists(name, uid)) { - this.stn.delete(name, uid); - } - this.stn.save(name, uid, yaml.toString().getBytes(StandardCharsets.UTF_8)); - response.redirect(String.format("/dashboard/%s/%s", uid, name)); - } else if ("delete".equals(action)) { - this.stn.delete(name, uid); - response.redirect("/dashboard"); - } - response.status(HttpStatus.FOUND_302); - return null; - } - - /** - * Obtains config from body. - * @param vals Values in body - * @return Config content from body - */ - private static YamlMapping configsFromBody(final ValueFromBody vals) { - try { - return Yaml.createYamlInput(vals.byNameOrThrow("config")).readYamlMapping(); - } catch (final IOException err) { - throw new UncheckedIOException(err); - } - } - } - - /** - * Handle get request while creating new repository. - * @since 0.1 - */ - public static final class Get implements Route { - - @Override - public Object handle(final Request request, final Response response) { - response.status(HttpStatus.FOUND_302); - response.redirect( - String.format( - "/dashboard/%s/%s?type=%s", - RequestAttr.Standard.USER_ID.readOrThrow(request), - request.queryParams("repo"), - request.queryParams("type") - ) - ); - return null; - } - } - -} diff --git a/src/main/java/com/artipie/front/ui/UserPage.java b/src/main/java/com/artipie/front/ui/UserPage.java deleted file mode 100644 index 2efe251..0000000 --- a/src/main/java/com/artipie/front/ui/UserPage.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * The MIT License (MIT) Copyright (c) 2022 artipie.com - * https://github.com/artipie/front/LICENSE.txt - */ -package com.artipie.front.ui; - -import com.artipie.front.RequestAttr; -import com.artipie.front.misc.RouteWrap; -import com.artipie.front.settings.RepoSettings; -import java.util.Map; -import java.util.Optional; - -/** - * Dashboard user page. - * @since 0.1 - */ -@SuppressWarnings("PMD.AvoidDuplicateLiterals") -public final class UserPage extends RouteWrap.TemplateViewRoute { - - /** - * New user page. - * @param repos Artipie repository settings - */ - public UserPage(final RepoSettings repos) { - super( - new HbPage( - "user", - req -> { - final String uid = RequestAttr.Standard.USER_ID.readOrThrow(req); - return Map.of( - "user", uid, "title", uid, - "repos", repos.list(Optional.of(uid)) - ); - } - ) - ); - } -} diff --git a/src/main/java/com/artipie/front/ui/repository/RepoAddConfig.java b/src/main/java/com/artipie/front/ui/repository/RepoAddConfig.java new file mode 100644 index 0000000..bbb1172 --- /dev/null +++ b/src/main/java/com/artipie/front/ui/repository/RepoAddConfig.java @@ -0,0 +1,66 @@ +/* + * The MIT License (MIT) Copyright (c) 2022 artipie.com + * https://github.com/artipie/front/LICENSE.txt + */ +package com.artipie.front.ui.repository; + +import com.artipie.front.misc.RouteWrap; +import com.artipie.front.rest.RepositoryName; +import com.artipie.front.ui.HbPage; +import java.util.Map; + +/** + * Add repository config view page. + * + * @since 1.0 + */ +@SuppressWarnings("PMD.AvoidDuplicateLiterals") +public final class RepoAddConfig extends RouteWrap.TemplateViewRoute { + /** + * Add repository info page. + * @param layout Layout + * @param info Infor template + * @param template Repository template + */ + public RepoAddConfig(final String layout, final RepositoryInfo info, + final RepositoryTemplate template) { + super( + new HbPage( + "repository/add_config", + req -> { + final String name = req.queryParams("name"); + final String type = req.queryParams("type"); + final String user = req.session().attribute("uname"); + final String rname; + if ("flat".equals(layout)) { + rname = new RepositoryName.Flat(name).toString(); + } else { + rname = new RepositoryName.Org(name, user).toString(); + } + return Map.of( + "title", "Add repository", + "uname", user, + "rname", rname, + "type", type, + "info", info.render( + type, + Map.of( + "user", user, + "repo", name, + "type", type + ) + ), + "template", template.render( + type, + Map.of( + "user", user, + "repo", name, + "type", type + ) + ) + ); + } + ) + ); + } +} diff --git a/src/main/java/com/artipie/front/ui/repository/RepoAddInfo.java b/src/main/java/com/artipie/front/ui/repository/RepoAddInfo.java new file mode 100644 index 0000000..f598741 --- /dev/null +++ b/src/main/java/com/artipie/front/ui/repository/RepoAddInfo.java @@ -0,0 +1,32 @@ +/* + * The MIT License (MIT) Copyright (c) 2022 artipie.com + * https://github.com/artipie/front/LICENSE.txt + */ +package com.artipie.front.ui.repository; + +import com.artipie.front.misc.RouteWrap; +import com.artipie.front.ui.HbPage; +import java.util.Map; + +/** + * Add repository info view page. + * + * @since 1.0 + */ +@SuppressWarnings("PMD.AvoidDuplicateLiterals") +public final class RepoAddInfo extends RouteWrap.TemplateViewRoute { + /** + * Add repository info page. + */ + public RepoAddInfo() { + super( + new HbPage( + "repository/add_info", + req -> Map.of( + "title", "Add repository", + "uname", req.session().attribute("uname") + ) + ) + ); + } +} diff --git a/src/main/java/com/artipie/front/ui/repository/RepoEdit.java b/src/main/java/com/artipie/front/ui/repository/RepoEdit.java new file mode 100644 index 0000000..2751699 --- /dev/null +++ b/src/main/java/com/artipie/front/ui/repository/RepoEdit.java @@ -0,0 +1,96 @@ +/* + * The MIT License (MIT) Copyright (c) 2022 artipie.com + * https://github.com/artipie/front/LICENSE.txt + */ +package com.artipie.front.ui.repository; + +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMapping; +import com.artipie.front.misc.RouteWrap; +import com.artipie.front.rest.RepositoryName; +import com.artipie.front.rest.RepositoryService; +import com.artipie.front.ui.HbPage; +import io.vavr.Tuple3; +import java.io.IOException; +import java.util.Map; +import java.util.Optional; +import org.eclipse.jetty.io.RuntimeIOException; + +/** + * Repository editor page. + * + * @since 1.0 + */ +@SuppressWarnings("PMD.AvoidDuplicateLiterals") +public final class RepoEdit extends RouteWrap.TemplateViewRoute { + /** + * Repository editor page. + * + * @param repository Repository service. + * @param layout Layout. + * @param info Repository info templates. + */ + public RepoEdit(final RepositoryService repository, final String layout, + final RepositoryInfo info) { + super( + new HbPage( + "repository/edit", + req -> { + final RepositoryName rname = new RepositoryName.FromRequest(req, layout); + final String repo = req.params(":repo"); + final String user = req.session().attribute("uname"); + final Tuple3 result = repository.repo( + req.session().attribute("token"), rname + ); + return Map.of( + "title", String.format("Repository %s", rname), + "rname", rname, + "result", result, + "error", "", + "info", RepoEdit.repoType(result._2) + .map( + type -> { + String rendered; + try { + rendered = info.render( + type, + Map.of( + "user", user, + "repo", repo, + "type", type + ) + ); + } catch (final RuntimeIOException exc) { + rendered = ""; + } + return rendered; + } + ).orElse("") + ); + } + ) + ); + } + + /** + * Provides repository type from yaml-configuration. + * @param repoconf Yaml-content of repository configuration. + * @return Repository type. + */ + private static Optional repoType(final String repoconf) { + return Optional.ofNullable(repoconf) + .flatMap( + content -> { + Optional yaml; + try { + yaml = Optional.of(Yaml.createYamlInput(content).readYamlMapping()); + } catch (final IOException exc) { + yaml = Optional.empty(); + } + return yaml; + } + ) + .map(yaml -> yaml.yamlMapping("repo")) + .map(repo -> repo.string("type")); + } +} diff --git a/src/main/java/com/artipie/front/ui/repository/RepoList.java b/src/main/java/com/artipie/front/ui/repository/RepoList.java index 65d34bf..dd22dc2 100644 --- a/src/main/java/com/artipie/front/ui/repository/RepoList.java +++ b/src/main/java/com/artipie/front/ui/repository/RepoList.java @@ -7,6 +7,8 @@ import com.artipie.front.misc.RouteWrap; import com.artipie.front.rest.RepositoryService; import com.artipie.front.ui.HbPage; +import io.vavr.Tuple3; +import java.util.List; import java.util.Map; /** @@ -21,16 +23,27 @@ public final class RepoList extends RouteWrap.TemplateViewRoute { * List of repositories page. * * @param repository Repository service. + * @param layout Layout. */ - public RepoList(final RepositoryService repository) { + public RepoList(final RepositoryService repository, final String layout) { super( new HbPage( "repository/list", - req -> - Map.of( + req -> { + final String uname = req.session().attribute("uname"); + final String token = req.session().attribute("token"); + final Tuple3, String> result; + if ("flat".equals(layout)) { + result = repository.list(token); + } else { + result = repository.list(token, uname); + } + return Map.of( "title", "Repository list", - "repos", repository.list(req.session().attribute("token")) - ) + "result", result, + "error", "" + ); + } ) ); } diff --git a/src/main/java/com/artipie/front/ui/repository/RepoRemove.java b/src/main/java/com/artipie/front/ui/repository/RepoRemove.java new file mode 100644 index 0000000..a05099f --- /dev/null +++ b/src/main/java/com/artipie/front/ui/repository/RepoRemove.java @@ -0,0 +1,46 @@ +/* + * The MIT License (MIT) Copyright (c) 2022 artipie.com + * https://github.com/artipie/front/LICENSE.txt + */ +package com.artipie.front.ui.repository; + +import com.artipie.front.misc.RouteWrap; +import com.artipie.front.rest.RepositoryName; +import com.artipie.front.rest.RepositoryService; +import com.artipie.front.ui.HbPage; +import java.util.Map; + +/** + * Repository remove POST-handler. + * Removes repository and shows result of on page. + * + * @since 1.0 + */ +@SuppressWarnings("PMD.AvoidDuplicateLiterals") +public final class RepoRemove extends RouteWrap.TemplateViewRoute { + /** + * Repository delete. + * + * @param repository Repository service. + * @param layout Layout. + */ + public RepoRemove(final RepositoryService repository, final String layout) { + super( + new HbPage( + "repository/result", + req -> { + final RepositoryName rname = new RepositoryName.FromRequest(req, layout); + return Map.of( + "title", String.format("Repository %s", rname), + "rname", rname, + "result", repository.remove(req.session().attribute("token"), rname), + "error", "", + "redirectUrl", "/dashboard/repository/list", + "redirectMessage", "Continue" + ); + } + ) + ); + } +} + diff --git a/src/main/java/com/artipie/front/ui/repository/RepoSave.java b/src/main/java/com/artipie/front/ui/repository/RepoSave.java new file mode 100644 index 0000000..15400af --- /dev/null +++ b/src/main/java/com/artipie/front/ui/repository/RepoSave.java @@ -0,0 +1,50 @@ +/* + * The MIT License (MIT) Copyright (c) 2022 artipie.com + * https://github.com/artipie/front/LICENSE.txt + */ +package com.artipie.front.ui.repository; + +import com.artipie.front.misc.RouteWrap; +import com.artipie.front.rest.RepositoryName; +import com.artipie.front.rest.RepositoryService; +import com.artipie.front.ui.HbPage; +import java.util.Map; + +/** + * Repository configuration saver. + * Saves repository configuration and shows result of saving on page. + * + * @since 1.0 + */ +@SuppressWarnings("PMD.AvoidDuplicateLiterals") +public final class RepoSave extends RouteWrap.TemplateViewRoute { + /** + * List of repositories page. + * + * @param repository Repository service. + * @param layout Layout. + */ + public RepoSave(final RepositoryService repository, final String layout) { + super( + new HbPage( + "repository/result", + req -> { + final RepositoryName rname = new RepositoryName.FromRequest(req, layout); + return Map.of( + "title", String.format("Repository %s", rname), + "rname", rname, + "result", repository.save( + req.session().attribute("token"), + rname, + req.queryParams("config") + ), + "error", "", + "redirectUrl", String.format("/dashboard/repository/edit/%s", rname), + "redirectMessage", "Continue" + ); + } + ) + ); + } +} + diff --git a/src/main/java/com/artipie/front/ui/repository/RepositoryInfo.java b/src/main/java/com/artipie/front/ui/repository/RepositoryInfo.java new file mode 100644 index 0000000..62a6252 --- /dev/null +++ b/src/main/java/com/artipie/front/ui/repository/RepositoryInfo.java @@ -0,0 +1,68 @@ +/* + * The MIT License (MIT) Copyright (c) 2022 artipie.com + * https://github.com/artipie/front/LICENSE.txt + */ +package com.artipie.front.ui.repository; + +import com.artipie.front.ui.HbTemplateEngine; +import java.util.Map; +import spark.ModelAndView; + +/** + * Info-template renderer. + * Provides 'html'-template with information about repository usage of specified type. + * All info-templates are situated in '/info'-resource folder. + * + * @since 1.0 + */ +public final class RepositoryInfo { + /** + * Format for info-template. + */ + private static final String FORMAT = "%s.info.html"; + + /** + * Type-aliases. + */ + private static final Map ALIAS = Map.of("binary", "file"); + + /** + * Template engine. + */ + private final HbTemplateEngine template; + + /** + * Ctor. + */ + public RepositoryInfo() { + this.template = new HbTemplateEngine("/info"); + } + + /** + * Renders template. + * @param type Repository type. + * @param model Model. + * @return Rendered template + */ + public String render(final String type, final Map model) { + return this.template.render( + new ModelAndView( + model, + String.format(RepositoryInfo.FORMAT, RepositoryInfo.resolveAlias(type)) + ) + ); + } + + /** + * Resolve alias-type to type. + * @param alias Alias-type. + * @return Resolved type. + */ + private static String resolveAlias(final String alias) { + String type = alias; + if (RepositoryInfo.ALIAS.containsKey(alias)) { + type = RepositoryInfo.ALIAS.get(alias); + } + return type; + } +} diff --git a/src/main/java/com/artipie/front/ui/repository/RepositoryTemplate.java b/src/main/java/com/artipie/front/ui/repository/RepositoryTemplate.java new file mode 100644 index 0000000..f613ffd --- /dev/null +++ b/src/main/java/com/artipie/front/ui/repository/RepositoryTemplate.java @@ -0,0 +1,74 @@ +/* + * The MIT License (MIT) Copyright (c) 2022 artipie.com + * https://github.com/artipie/front/LICENSE.txt + */ +package com.artipie.front.ui.repository; + +import com.artipie.front.ui.HbTemplateEngine; +import java.util.List; +import java.util.Map; +import spark.ModelAndView; + +/** + * Repository template renderer. + * Provides 'yaml'-template of repository configuration. + * Provides 'yaml'-template with repository configuration of specified type. + * All templates are situated in '/template'-resource folder. + * + * @since 1.0 + */ +public final class RepositoryTemplate { + /** + * Default template. + */ + private static final String DEFAULT_TEMPLATE = "default"; + + /** + * Format for template. + */ + private static final String FORMAT = "%s.template.yaml"; + + /** + * Pre-defined template names. + */ + private static final List PREDEFINED = List.of("maven-group", "maven-proxy"); + + /** + * Template engine. + */ + private final HbTemplateEngine template; + + /** + * Ctor. + */ + public RepositoryTemplate() { + this.template = new HbTemplateEngine("/template"); + } + + /** + * Render template. + * + * @param type Repository type. + * @param model Model. + * @return Rendered yaml-template for repository type. + */ + public String render(final String type, final Map model) { + final String content; + if (RepositoryTemplate.PREDEFINED.contains(type)) { + content = this.template.render( + new ModelAndView( + model, + String.format(RepositoryTemplate.FORMAT, type) + ) + ); + } else { + content = this.template.render( + new ModelAndView( + model, + String.format(RepositoryTemplate.FORMAT, RepositoryTemplate.DEFAULT_TEMPLATE) + ) + ); + } + return content; + } +} diff --git a/src/main/java/com/artipie/front/ui/repository/flat/package-info.java b/src/main/java/com/artipie/front/ui/repository/flat/package-info.java deleted file mode 100644 index 8f6d1dc..0000000 --- a/src/main/java/com/artipie/front/ui/repository/flat/package-info.java +++ /dev/null @@ -1,10 +0,0 @@ -/* - * The MIT License (MIT) Copyright (c) 2022 artipie.com - * https://github.com/artipie/front/LICENSE.txt - */ - -/** - * Artipie front web service. - * @since 1.0 - */ -package com.artipie.front.ui.repository.flat; diff --git a/src/main/java/com/artipie/front/ui/repository/org/RepoListByUser.java b/src/main/java/com/artipie/front/ui/repository/org/RepoListByUser.java deleted file mode 100644 index 98eb831..0000000 --- a/src/main/java/com/artipie/front/ui/repository/org/RepoListByUser.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * The MIT License (MIT) Copyright (c) 2022 artipie.com - * https://github.com/artipie/front/LICENSE.txt - */ -package com.artipie.front.ui.repository.org; - -import com.artipie.front.misc.RouteWrap; -import com.artipie.front.rest.RepositoryService; -import com.artipie.front.ui.HbPage; -import java.util.Map; - -/** - * List of user repositories page. - * - * @checkstyle AvoidDuplicateLiterals (500 lines) - * @since 1.0 - */ -@SuppressWarnings("PMD.AvoidDuplicateLiterals") -public final class RepoListByUser extends RouteWrap.TemplateViewRoute { - /** - * List of user repositories page. - * - * @param repository Repository service. - */ - public RepoListByUser(final RepositoryService repository) { - super( - new HbPage( - "repository/org/list", - req -> { - String error = ""; - final String uname = req.session().attribute("uname"); - if (!uname.equals(req.params(":uname"))) { - error = "Access denied"; - } - return Map.of( - "title", String.format("Repository list by %s", uname), - "uname", uname, - "repos", repository.list(req.session().attribute("token"), uname), - "error", error - ); - } - ) - ); - } -} diff --git a/src/main/java/com/artipie/front/ui/repository/org/package-info.java b/src/main/java/com/artipie/front/ui/repository/org/package-info.java deleted file mode 100644 index d3bf97f..0000000 --- a/src/main/java/com/artipie/front/ui/repository/org/package-info.java +++ /dev/null @@ -1,10 +0,0 @@ -/* - * The MIT License (MIT) Copyright (c) 2022 artipie.com - * https://github.com/artipie/front/LICENSE.txt - */ - -/** - * Artipie front web service. - * @since 1.0 - */ -package com.artipie.front.ui.repository.org; diff --git a/src/main/resources/html/base b/src/main/resources/html/base index 81442d9..1c5e165 100644 --- a/src/main/resources/html/base +++ b/src/main/resources/html/base @@ -18,12 +18,33 @@ -
-
- {{#block "content"}} - {{/block}} -
-
+ + + + + +
+ + +
+
+ {{#block "content"}} + {{/block}} +
+
+