mirror of
https://github.com/HangarMC/Hangar.git
synced 2025-03-13 15:39:18 +08:00
chore: update to spring boot 3 and switch to springdoc (#1060)
This commit is contained in:
parent
5754c54139
commit
ba27710bd5
129
.github/renovate.json5
vendored
129
.github/renovate.json5
vendored
@ -1,62 +1,71 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base"
|
||||
],
|
||||
"schedule": [
|
||||
"before 3am on Monday"
|
||||
],
|
||||
"ignoreDeps": [
|
||||
"io.awspring.cloud:spring-cloud-aws-starter-s3", // m3 is spring boot 3 only
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchManagers": ["docker-compose", "dockerfile", "helmv3", "helm-values"],
|
||||
"matchPackagePatterns": [
|
||||
"*"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"groupName": "infra non-major dependencies",
|
||||
"groupSlug": "infra-minor-patch"
|
||||
},
|
||||
{
|
||||
"matchManagers": ["github-actions"],
|
||||
"matchPackagePatterns": [
|
||||
"*"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"groupName": "gh-actions non-major dependencies",
|
||||
"groupSlug": "actions-minor-patch"
|
||||
},
|
||||
{
|
||||
"matchManagers": ["maven", "gradle"],
|
||||
"matchPackagePatterns": [
|
||||
"*"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"groupName": "backend non-major dependencies",
|
||||
"groupSlug": "backend-minor-patch"
|
||||
},
|
||||
{
|
||||
"matchManagers": ["npm"],
|
||||
"matchPackagePatterns": [
|
||||
"*"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"groupName": "frontend non-major dependencies",
|
||||
"groupSlug": "frontend-minor-patch"
|
||||
}
|
||||
]
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base"
|
||||
],
|
||||
"schedule": [
|
||||
"before 3am on Monday"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchManagers": [
|
||||
"docker-compose",
|
||||
"dockerfile",
|
||||
"helmv3",
|
||||
"helm-values"
|
||||
],
|
||||
"matchPackagePatterns": [
|
||||
"*"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"groupName": "infra non-major dependencies",
|
||||
"groupSlug": "infra-minor-patch"
|
||||
},
|
||||
{
|
||||
"matchManagers": [
|
||||
"github-actions"
|
||||
],
|
||||
"matchPackagePatterns": [
|
||||
"*"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"groupName": "gh-actions non-major dependencies",
|
||||
"groupSlug": "actions-minor-patch"
|
||||
},
|
||||
{
|
||||
"matchManagers": [
|
||||
"maven",
|
||||
"gradle"
|
||||
],
|
||||
"matchPackagePatterns": [
|
||||
"*"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"groupName": "backend non-major dependencies",
|
||||
"groupSlug": "backend-minor-patch"
|
||||
},
|
||||
{
|
||||
"matchManagers": [
|
||||
"npm"
|
||||
],
|
||||
"matchPackagePatterns": [
|
||||
"*"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"groupName": "frontend non-major dependencies",
|
||||
"groupSlug": "frontend-minor-patch"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.7.7</version>
|
||||
<version>3.0.1</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
@ -28,11 +28,11 @@
|
||||
<!-- dependency management -->
|
||||
<jdbi3-bom.version>3.35.0</jdbi3-bom.version>
|
||||
<configurate.version>4.1.2</configurate.version>
|
||||
<spring-cloud-aws.version>3.0.0-M2</spring-cloud-aws.version>
|
||||
<spring-cloud-aws.version>3.0.0-M3</spring-cloud-aws.version>
|
||||
|
||||
<!-- dependencies -->
|
||||
<owasp-html-sanitizer>20220608.1</owasp-html-sanitizer>
|
||||
<springfox-boot-starter.version>3.0.0</springfox-boot-starter.version>
|
||||
<springdoc.version>2.0.2</springdoc.version>
|
||||
<flexmark-all.version>0.64.0</flexmark-all.version>
|
||||
<jsitemapgenerator.version>4.5</jsitemapgenerator.version>
|
||||
<org-json.version>20220924</org-json.version>
|
||||
@ -48,6 +48,18 @@
|
||||
<maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
|
||||
</properties>
|
||||
|
||||
<!-- spring-cloud-aws requires this rn -->
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@ -68,6 +80,11 @@
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-properties-migrator</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
@ -135,24 +152,17 @@
|
||||
<artifactId>configurate-yaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--SpringFox dependencies -->
|
||||
<!-- SpringDoc dependencies -->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-boot-starter</artifactId>
|
||||
<version>${springfox-boot-starter.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<!-- old version -->
|
||||
<groupId>io.github.classgraph</groupId>
|
||||
<artifactId>classgraph</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Bean Validation API support -->
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- config stuff -->
|
||||
@ -313,8 +323,8 @@
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
<groupId>io.github.git-commit-id</groupId>
|
||||
<artifactId>git-commit-id-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-resources</phase>
|
||||
|
@ -6,9 +6,9 @@ import io.papermc.hangar.security.authentication.HangarAuthenticationToken;
|
||||
import io.papermc.hangar.security.authentication.HangarPrincipal;
|
||||
import io.papermc.hangar.service.PermissionService;
|
||||
import io.papermc.hangar.service.internal.UserActionLogService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.jdbi.v3.core.internal.MemoizingSupplier;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -1,88 +1,64 @@
|
||||
package io.papermc.hangar.config;
|
||||
|
||||
import io.papermc.hangar.controller.extras.pagination.FilterRegistry;
|
||||
import io.papermc.hangar.controller.extras.pagination.SorterRegistry;
|
||||
import io.papermc.hangar.controller.extras.pagination.annotations.ApplicableFilters;
|
||||
import io.papermc.hangar.controller.extras.pagination.annotations.ApplicableSorters;
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.ServletContext;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.Operation;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.parameters.Parameter;
|
||||
import org.springdoc.core.customizers.OperationCustomizer;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.builders.RequestParameterBuilder;
|
||||
import springfox.documentation.schema.ScalarType;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.service.ParameterStyle;
|
||||
import springfox.documentation.service.ParameterType;
|
||||
import springfox.documentation.service.RequestParameter;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.OperationBuilderPlugin;
|
||||
import springfox.documentation.spi.service.RequestHandlerProvider;
|
||||
import springfox.documentation.spi.service.contexts.OperationContext;
|
||||
import springfox.documentation.spring.web.WebMvcRequestHandler;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper;
|
||||
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
|
||||
import springfox.documentation.spring.web.readers.operation.HandlerMethodResolver;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static springfox.documentation.RequestHandler.byPatternsCondition;
|
||||
import static springfox.documentation.spring.web.paths.Paths.ROOT;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
public class SwaggerConfig {
|
||||
|
||||
ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
.title("Hangar API")
|
||||
.description("This page describes the format for the current Hangar REST API, in addition to common questions when using it.<br>" +
|
||||
"Note that all routes **not** listed here should be considered **internal**, and can change at a moment's notice. **Do not use them**." +
|
||||
"<h2>Authentication and Authorization</h2>" +
|
||||
"There are two ways to consume the API: Authenticated or Anonymous." +
|
||||
"<h3>Anonymous</h3>" +
|
||||
"When using anonymous authentication you only get access to public information, but don't need to worry about creating and storing an API key or handing JWTs." +
|
||||
"<h3>Authenticated</h3>" +
|
||||
"If you need access to non-public actions, or want to do something programmatically, you likely want an API key.<br>" +
|
||||
"These can be created by going to your user page and clicking on the key icon.<br>" +
|
||||
"API keys allow you to impersonate yourself, so they should be handled like passwords, **never** share them!" +
|
||||
"<h4>Authentication</h4>" +
|
||||
"Once you have an api key, you need to authenticate yourself. For that you `POST` your API Key to `/api/v1/authenticate?apiKey=yourKey`. The response will contain your JWT.<br>" +
|
||||
"You want to store that JWT and send it with every request. It's valid for a certain amount of time, the token itself contains a field with a timestamp when it will expire. If it expired, you want to reauthenticate yourself." +
|
||||
"<h4>Authorization</h4>" +
|
||||
"Now that you have your JWT, you want to set it in the Authorization header for every request like so `Authorization: HangarAuth your.jwt`. The request will be then executed with the permission scope of your api key." +
|
||||
"<br>" +
|
||||
"While talking about headers. Please also set a useful User-Agent header. This allows us to better identify loads and need for potentially new endpoints." +
|
||||
"<h2>FAQ</h2>" +
|
||||
"<h3>What format do dates have?</h3>" +
|
||||
"Standard ISO types. Where possible, we use the OpenAPI format modifier. You can view its meanings [here](https://swagger.io/docs/specification/data-models/data-types/#format)." +
|
||||
"<h3>Are there rate-limits? What about caching?</h3>" +
|
||||
"There are currently no rate limits. Please don't abuse that fact. If applicable, always cache the responses. The Hangar API itself is cached by CloudFlare and internally.")
|
||||
.version("1.0")
|
||||
.build();
|
||||
static {
|
||||
io.swagger.v3.core.jackson.ModelResolver.enumsAsRef = true;
|
||||
}
|
||||
|
||||
OpenAPI apiInfo(final OpenAPI openAPI) {
|
||||
return openAPI
|
||||
.info(new Info().title("Hangar API")
|
||||
.description("This page describes the format for the current Hangar REST API, in addition to common questions when using it.<br>" +
|
||||
"Note that all routes **not** listed here should be considered **internal**, and can change at a moment's notice. **Do not use them**." +
|
||||
"<h2>Authentication and Authorization</h2>" +
|
||||
"There are two ways to consume the API: Authenticated or Anonymous." +
|
||||
"<h3>Anonymous</h3>" +
|
||||
"When using anonymous authentication you only get access to public information, but don't need to worry about creating and storing an API key or handing JWTs." +
|
||||
"<h3>Authenticated</h3>" +
|
||||
"If you need access to non-public actions, or want to do something programmatically, you likely want an API key.<br>" +
|
||||
"These can be created by going to your user page and clicking on the key icon.<br>" +
|
||||
"API keys allow you to impersonate yourself, so they should be handled like passwords, **never** share them!" +
|
||||
"<h4>Authentication</h4>" +
|
||||
"Once you have an api key, you need to authenticate yourself. For that you `POST` your API Key to `/api/v1/authenticate?apiKey=yourKey`. The response will contain your JWT.<br>" +
|
||||
"You want to store that JWT and send it with every request. It's valid for a certain amount of time, the token itself contains a field with a timestamp when it will expire. If it expired, you want to reauthenticate yourself." +
|
||||
"<h4>Authorization</h4>" +
|
||||
"Now that you have your JWT, you want to set it in the Authorization header for every request like so `Authorization: HangarAuth your.jwt`. The request will be then executed with the permission scope of your api key." +
|
||||
"<br>" +
|
||||
"While talking about headers. Please also set a useful User-Agent header. This allows us to better identify loads and need for potentially new endpoints." +
|
||||
"<h2>FAQ</h2>" +
|
||||
"<h3>What format do dates have?</h3>" +
|
||||
"Standard ISO types. Where possible, we use the OpenAPI format modifier. You can view its meanings [here](https://swagger.io/docs/specification/data-models/data-types/#format)." +
|
||||
"<h3>Are there rate-limits? What about caching?</h3>" +
|
||||
"There are currently no rate limits. Please don't abuse that fact. If applicable, always cache the responses. The Hangar API itself is cached by CloudFlare and internally.")
|
||||
.version("1.0"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Docket customImplementation() {
|
||||
return new Docket(DocumentationType.OAS_30)
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage("io.papermc.hangar.controller.api.v1"))
|
||||
.build()
|
||||
.directModelSubstitute(LocalDate.class, java.sql.Date.class)
|
||||
.directModelSubstitute(OffsetDateTime.class, Date.class)
|
||||
.apiInfo(this.apiInfo());
|
||||
public GroupedOpenApi customImplementation(final CustomScanner customScanner) {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("Hangar API")
|
||||
.packagesToScan("io.papermc.hangar.controller.api.v1")
|
||||
.addOperationCustomizer(customScanner)
|
||||
.addOpenApiCustomizer(this::apiInfo)
|
||||
.build();
|
||||
// TODO fix
|
||||
//.directModelSubstitute(LocalDate.class, java.sql.Date.class)
|
||||
//.directModelSubstitute(OffsetDateTime.class, Date.class)
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ -90,7 +66,7 @@ public class SwaggerConfig {
|
||||
return new CustomScanner(filterRegistry);
|
||||
}
|
||||
|
||||
static class CustomScanner implements OperationBuilderPlugin {
|
||||
static class CustomScanner implements OperationCustomizer {
|
||||
|
||||
private final FilterRegistry filterRegistry;
|
||||
|
||||
@ -98,62 +74,33 @@ public class SwaggerConfig {
|
||||
this.filterRegistry = filterRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(final OperationContext context) {
|
||||
final Optional<ApplicableSorters> sorters = context.findAnnotation(ApplicableSorters.class);
|
||||
if (sorters.isPresent()) {
|
||||
final Set<RequestParameter> requestParameters = context.operationBuilder().build().getRequestParameters();
|
||||
requestParameters.add(new RequestParameterBuilder()
|
||||
.name("sort")
|
||||
.in(ParameterType.QUERY)
|
||||
.description("Used to sort the result")
|
||||
.query(q -> q.style(ParameterStyle.SIMPLE).model(m -> m.scalarModel(ScalarType.STRING)).enumerationFacet(e -> e.allowedValues(Arrays.asList(sorters.get().value()).stream().map(SorterRegistry::getName).collect(Collectors.toSet())))).build());
|
||||
context.operationBuilder().requestParameters(requestParameters);
|
||||
}
|
||||
final Optional<ApplicableFilters> filters = context.findAnnotation(ApplicableFilters.class);
|
||||
if (filters.isPresent()) {
|
||||
final Set<RequestParameter> requestParameters = context.operationBuilder().build().getRequestParameters();
|
||||
|
||||
for (final var clazz : filters.get().value()) {
|
||||
@Override
|
||||
public Operation customize(final Operation operation, final HandlerMethod handlerMethod) {
|
||||
final ApplicableSorters sorters = handlerMethod.getMethodAnnotation(ApplicableSorters.class);
|
||||
if (sorters != null) {
|
||||
operation.addParametersItem(new Parameter()
|
||||
.name("sort")
|
||||
.in("query")
|
||||
.description("Used to sort the result"));
|
||||
// TODO fix
|
||||
//.query(q -> q.style(ParameterStyle.SIMPLE).model(m -> m.scalarModel(ScalarType.STRING)).enumerationFacet(e -> e.allowedValues(Arrays.asList(sorters.value()).stream().map(SorterRegistry::getName).collect(Collectors.toSet())))).build());
|
||||
}
|
||||
final ApplicableFilters filters = handlerMethod.getMethodAnnotation(ApplicableFilters.class);
|
||||
if (filters != null) {
|
||||
for (final var clazz : filters.value()) {
|
||||
final var filter = this.filterRegistry.get(clazz);
|
||||
|
||||
requestParameters.add(new RequestParameterBuilder()
|
||||
operation.addParametersItem(new Parameter()
|
||||
.name(filter.getSingleQueryParam()) // TODO multi-param filters
|
||||
.in(ParameterType.QUERY)
|
||||
.description(filter.getDescription())
|
||||
.query(q -> q.style(ParameterStyle.SIMPLE).model(m -> m.scalarModel(ScalarType.STRING))).build());
|
||||
.in("query")
|
||||
.description(filter.getDescription()));
|
||||
// TODO fix
|
||||
// .query(q -> q.style(ParameterStyle.SIMPLE).model(m -> m.scalarModel(ScalarType.STRING))).build());
|
||||
}
|
||||
context.operationBuilder().requestParameters(requestParameters);
|
||||
}
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final DocumentationType documentationType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Hack to make this shit work on spring boot 2.6 // TODO migrate to springdoc
|
||||
@Bean
|
||||
public InitializingBean removeSpringfoxHandlerProvider(final DocumentationPluginsBootstrapper bootstrapper) {
|
||||
return () -> bootstrapper.getHandlerProviders().removeIf(WebMvcRequestHandlerProvider.class::isInstance);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RequestHandlerProvider customRequestHandlerProvider(final Optional<ServletContext> servletContext, final HandlerMethodResolver methodResolver, final List<RequestMappingInfoHandlerMapping> handlerMappings) {
|
||||
final String contextPath = servletContext.map(ServletContext::getContextPath).orElse(ROOT);
|
||||
return () -> handlerMappings.stream()
|
||||
.filter(mapping -> !mapping.getClass().getSimpleName().equals("IntegrationRequestMappingHandlerMapping"))
|
||||
.map(mapping -> mapping.getHandlerMethods().entrySet())
|
||||
.flatMap(Set::stream)
|
||||
.map(entry -> new WebMvcRequestHandler(contextPath, methodResolver, this.tweakInfo(entry.getKey()), entry.getValue()))
|
||||
.sorted(byPatternsCondition())
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
RequestMappingInfo tweakInfo(final RequestMappingInfo info) {
|
||||
if (info.getPathPatternsCondition() == null) return info;
|
||||
final String[] patterns = info.getPathPatternsCondition().getPatternValues().toArray(String[]::new);
|
||||
return info.mutate().options(new RequestMappingInfo.BuilderConfiguration()).paths(patterns).build();
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,11 @@ import com.fasterxml.jackson.module.paramnames.ParameterNamesAnnotationIntrospec
|
||||
import io.papermc.hangar.config.hangar.HangarConfig;
|
||||
import io.papermc.hangar.config.jackson.HangarAnnotationIntrospector;
|
||||
import io.papermc.hangar.security.annotations.ratelimit.RateLimitInterceptor;
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
@ -14,11 +19,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -169,7 +169,7 @@ public class WebConfig extends WebMvcConfigurationSupport {
|
||||
}
|
||||
final ClientHttpResponse response = ex.execute(req, reqBody);
|
||||
if (interceptorLogger.isDebugEnabled()) {
|
||||
final int code = response.getRawStatusCode();
|
||||
final int code = response.getStatusCode().value();
|
||||
final HttpStatus status = HttpStatus.resolve(code);
|
||||
|
||||
final InputStreamReader isr = new InputStreamReader(response.getBody(), StandardCharsets.UTF_8);
|
||||
|
@ -3,8 +3,8 @@ package io.papermc.hangar.config.hangar;
|
||||
import io.papermc.hangar.model.common.Color;
|
||||
import io.papermc.hangar.model.internal.api.responses.Validation;
|
||||
import io.papermc.hangar.util.PatternWrapper;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.Size;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
|
||||
|
@ -15,24 +15,17 @@ import io.papermc.hangar.service.AuthenticationService;
|
||||
import io.papermc.hangar.service.TokenService;
|
||||
import io.papermc.hangar.service.ValidationService;
|
||||
import io.papermc.hangar.service.internal.auth.SSOService;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.CookieValue;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
import java.util.Optional;
|
||||
|
||||
@Controller
|
||||
@RateLimit(path = "login")
|
||||
|
@ -2,14 +2,16 @@ package io.papermc.hangar.controller.api.v1.interfaces;
|
||||
|
||||
import io.papermc.hangar.model.api.ApiKey;
|
||||
import io.papermc.hangar.model.internal.api.requests.CreateAPIKeyForm;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.annotations.ApiResponse;
|
||||
import io.swagger.annotations.ApiResponses;
|
||||
import io.swagger.annotations.Authorization;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@ -18,52 +20,50 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Api(tags = "API Keys")
|
||||
@Tag(name = "API Keys")
|
||||
@RequestMapping("/api/v1")
|
||||
public interface IApiKeysController {
|
||||
|
||||
@ApiOperation(
|
||||
value = "Creates an API key",
|
||||
nickname = "createKey",
|
||||
notes = "Creates an API key. Requires the `edit_api_keys` permission.",
|
||||
response = String.class,
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Creates an API key",
|
||||
operationId = "createKey",
|
||||
description = "Creates an API key. Requires the `edit_api_keys` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "API Keys"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 201, message = "Key created", response = String.class),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")})
|
||||
@ApiResponse(responseCode = "201", description = "Key created", content = @Content(schema = @Schema(implementation = String.class))),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")})
|
||||
@PostMapping(path = "/keys", produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
String createKey(@ApiParam(value = "Data about the key to create", required = true) @Valid @RequestBody CreateAPIKeyForm apiKeyForm);
|
||||
String createKey(@Parameter(description = "Data about the key to create", required = true) @Valid @RequestBody CreateAPIKeyForm apiKeyForm);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Fetches a list of API Keys",
|
||||
nickname = "getKeys",
|
||||
notes = "Fetches a list of API Keys. Requires the `edit_api_keys` permission.",
|
||||
response = String.class,
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Fetches a list of API Keys",
|
||||
operationId = "getKeys",
|
||||
description = "Fetches a list of API Keys. Requires the `edit_api_keys` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "API Keys"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Key created", response = ApiKey.class, responseContainer = "List"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")})
|
||||
@ApiResponse(responseCode = "200", description = "Key created", content = @Content(schema = @Schema(implementation = ApiKey.class))),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")})
|
||||
@GetMapping(path = "/keys", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
List<ApiKey> getKeys();
|
||||
|
||||
@ApiOperation(
|
||||
value = "Deletes an API key",
|
||||
nickname = "deleteKey",
|
||||
notes = "Deletes an API key. Requires the `edit_api_keys` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Deletes an API key",
|
||||
operationId = "deleteKey",
|
||||
description = "Deletes an API key. Requires the `edit_api_keys` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "API Keys"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 204, message = "Key deleted"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "204", description = "Key deleted"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@DeleteMapping("/keys")
|
||||
void deleteKey(@ApiParam(value = "The name of the key to delete", required = true) @RequestParam String name);
|
||||
void deleteKey(@Parameter(description = "The name of the key to delete", required = true) @RequestParam String name);
|
||||
}
|
||||
|
@ -1,34 +1,34 @@
|
||||
package io.papermc.hangar.controller.api.v1.interfaces;
|
||||
|
||||
import io.papermc.hangar.model.api.auth.ApiSession;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.annotations.ApiResponse;
|
||||
import io.swagger.annotations.ApiResponses;
|
||||
import io.swagger.annotations.Authorization;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Api(tags = "Authentication", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Tag(name = "Authentication")
|
||||
@RequestMapping(path = "/api/v1", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public interface IAuthenticationController {
|
||||
|
||||
@ApiOperation(
|
||||
value = "Creates an API JWT",
|
||||
nickname = "authenticate",
|
||||
notes = "`Log-in` with your API key in order to be able to call other endpoints authenticated. The returned JWT should be specified as a header in all following requests: `Authorization: HangarAuth your.jwt`",
|
||||
authorizations = @Authorization("Key"),
|
||||
@Operation(
|
||||
summary = "Creates an API JWT",
|
||||
operationId = "authenticate",
|
||||
description = "`Log-in` with your API key in order to be able to call other endpoints authenticated. The returned JWT should be specified as a header in all following requests: `Authorization: HangarAuth your.jwt`",
|
||||
security = @SecurityRequirement(name = "Key"),
|
||||
tags = "Authentication"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 400, message = "Bad Request"),
|
||||
@ApiResponse(code = 401, message = "Api key missing or invalid")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "400", description = "Bad Request"),
|
||||
@ApiResponse(responseCode = "401", description = "Api key missing or invalid")
|
||||
})
|
||||
@PostMapping("/authenticate")
|
||||
ResponseEntity<ApiSession> authenticate(@ApiParam("JWT") @RequestParam String apiKey);
|
||||
ResponseEntity<ApiSession> authenticate(@Parameter(description = "JWT") @RequestParam String apiKey);
|
||||
}
|
||||
|
@ -3,12 +3,12 @@ package io.papermc.hangar.controller.api.v1.interfaces;
|
||||
import io.papermc.hangar.model.api.permissions.PermissionCheck;
|
||||
import io.papermc.hangar.model.api.permissions.UserPermissions;
|
||||
import io.papermc.hangar.model.common.NamedPermission;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.annotations.ApiResponse;
|
||||
import io.swagger.annotations.ApiResponses;
|
||||
import io.swagger.annotations.Authorization;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.List;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@ -17,67 +17,65 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
|
||||
@Api(tags = "Permissions", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Tag(name = "Permissions")
|
||||
@RequestMapping(path = "/api/v1", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
|
||||
public interface IPermissionsController {
|
||||
|
||||
@ApiOperation(
|
||||
value = "Checks whether you have all the provided permissions",
|
||||
nickname = "hasAll",
|
||||
notes = "Checks whether you have all the provided permissions in the given context",
|
||||
response = PermissionCheck.class,
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Checks whether you have all the provided permissions",
|
||||
operationId = "hasAll",
|
||||
description = "Checks whether you have all the provided permissions in the given context",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Permissions"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok", response = PermissionCheck.class),
|
||||
@ApiResponse(code = 400, message = "Bad Request"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "400", description = "Bad Request"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired")
|
||||
})
|
||||
@GetMapping("/permissions/hasAll")
|
||||
ResponseEntity<PermissionCheck> hasAllPermissions(@ApiParam(value = "The permissions to check", required = true) @RequestParam List<NamedPermission> permissions,
|
||||
@ApiParam("The owner of the project to check permissions in. Must not be used together with `organizationName`") @RequestParam(required = false) String author,
|
||||
@ApiParam("The project slug of the project to check permissions in. Must not be used together with `organizationName`") @RequestParam(required = false) String slug,
|
||||
@ApiParam("The organization to check permissions in. Must not be used together with `projectOwner` and `projectSlug`") @RequestParam(required = false) String organization
|
||||
ResponseEntity<PermissionCheck> hasAllPermissions(@Parameter(description = "The permissions to check", required = true) @RequestParam List<NamedPermission> permissions,
|
||||
@Parameter(description = "The owner of the project to check permissions in. Must not be used together with `organizationName`") @RequestParam(required = false) String author,
|
||||
@Parameter(description = "The project slug of the project to check permissions in. Must not be used together with `organizationName`") @RequestParam(required = false) String slug,
|
||||
@Parameter(description = "The organization to check permissions in. Must not be used together with `projectOwner` and `projectSlug`") @RequestParam(required = false) String organization
|
||||
);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Checks whether you have at least one of the provided permissions",
|
||||
nickname = "hasAny",
|
||||
notes = "Checks whether you have at least one of the provided permissions in the given context",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Checks whether you have at least one of the provided permissions",
|
||||
operationId = "hasAny",
|
||||
description = "Checks whether you have at least one of the provided permissions in the given context",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Permissions"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 400, message = "Bad Request"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "400", description = "Bad Request"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired")
|
||||
})
|
||||
@GetMapping("/permissions/hasAny")
|
||||
ResponseEntity<PermissionCheck> hasAny(@ApiParam(value = "The permissions to check", required = true) @RequestParam List<NamedPermission> permissions,
|
||||
@ApiParam("The owner of the project to check permissions in. Must not be used together with `organizationName") @RequestParam(required = false) String author,
|
||||
@ApiParam("The slug of the project to check permissions in. Must not be used together with `organizationName`") @RequestParam(required = false) String slug,
|
||||
@ApiParam("The organization to check permissions in. Must not be used together with `projectOwner` and `projectSlug`") @RequestParam(required = false) String organization
|
||||
ResponseEntity<PermissionCheck> hasAny(@Parameter(description = "The permissions to check", required = true) @RequestParam List<NamedPermission> permissions,
|
||||
@Parameter(description = "The owner of the project to check permissions in. Must not be used together with `organizationName") @RequestParam(required = false) String author,
|
||||
@Parameter(description = "The slug of the project to check permissions in. Must not be used together with `organizationName`") @RequestParam(required = false) String slug,
|
||||
@Parameter(description = "The organization to check permissions in. Must not be used together with `projectOwner` and `projectSlug`") @RequestParam(required = false) String organization
|
||||
);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns your permissions",
|
||||
nickname = "showPermissions",
|
||||
notes = "Returns a list of permissions you have in the given context",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns your permissions",
|
||||
operationId = "showPermissions",
|
||||
description = "Returns a list of permissions you have in the given context",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Permissions"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 400, message = "Bad Request"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "400", description = "Bad Request"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired")
|
||||
})
|
||||
@GetMapping(value = "/permissions",
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
ResponseEntity<UserPermissions> showPermissions(
|
||||
@ApiParam("The owner of the project to get the permissions for. Must not be used together with `organizationName`") @RequestParam(required = false) String author,
|
||||
@ApiParam("The slug of the project get the permissions for. Must not be used together with `organizationName`") @RequestParam(required = false) String slug,
|
||||
@ApiParam("The organization to check permissions in. Must not be used together with `projectOwner` and `projectSlug`") @RequestParam(required = false) String organization
|
||||
@Parameter(description = "The owner of the project to get the permissions for. Must not be used together with `organizationName`") @RequestParam(required = false) String author,
|
||||
@Parameter(description = "The slug of the project get the permissions for. Must not be used together with `organizationName`") @RequestParam(required = false) String slug,
|
||||
@Parameter(description = "The organization to check permissions in. Must not be used together with `projectOwner` and `projectSlug`") @RequestParam(required = false) String organization
|
||||
);
|
||||
}
|
||||
|
@ -6,12 +6,12 @@ import io.papermc.hangar.model.api.project.DayProjectStats;
|
||||
import io.papermc.hangar.model.api.project.Project;
|
||||
import io.papermc.hangar.model.api.project.ProjectMember;
|
||||
import io.papermc.hangar.model.api.requests.RequestPagination;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.annotations.ApiResponse;
|
||||
import io.swagger.annotations.ApiResponses;
|
||||
import io.swagger.annotations.Authorization;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Map;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -23,118 +23,118 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Api(tags = "Projects", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Tag(name = "Projects")
|
||||
@RequestMapping(path = "/api/v1", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
|
||||
public interface IProjectsController {
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns info on a specific project",
|
||||
nickname = "getProject",
|
||||
notes = "Returns info on a specific project. Requires the `view_public_info` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns info on a specific project",
|
||||
operationId = "getProject",
|
||||
description = "Returns info on a specific project. Requires the `view_public_info` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Projects"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/projects/{author}/{slug}")
|
||||
ResponseEntity<Project> getProject(@ApiParam("The author of the project to return") @PathVariable String author,
|
||||
@ApiParam("The slug of the project to return") @PathVariable String slug);
|
||||
ResponseEntity<Project> getProject(@Parameter(description = "The author of the project to return") @PathVariable String author,
|
||||
@Parameter(description = "The slug of the project to return") @PathVariable String slug);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns the members of a project",
|
||||
nickname = "getProjectMembers",
|
||||
notes = "Returns the members of a project. Requires the `view_public_info` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns the members of a project",
|
||||
operationId = "getProjectMembers",
|
||||
description = "Returns the members of a project. Requires the `view_public_info` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Projects"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/projects/{author}/{slug}/members")
|
||||
ResponseEntity<PaginatedResult<ProjectMember>> getProjectMembers(
|
||||
@ApiParam("The author of the project to return members for") @PathVariable("author") String author,
|
||||
@ApiParam("The slug of the project to return members for") @PathVariable("slug") String slug,
|
||||
@ApiParam("Pagination information") @NotNull RequestPagination pagination
|
||||
@Parameter(description = "The author of the project to return members for") @PathVariable("author") String author,
|
||||
@Parameter(description = "The slug of the project to return members for") @PathVariable("slug") String slug,
|
||||
@Parameter(description = "Pagination information") @NotNull RequestPagination pagination
|
||||
);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Searches the projects on Hangar",
|
||||
nickname = "getProjects",
|
||||
notes = "Searches all the projects on Hangar, or for a single user. Requires the `view_public_info` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Searches the projects on Hangar",
|
||||
operationId = "getProjects",
|
||||
description = "Searches all the projects on Hangar, or for a single user. Requires the `view_public_info` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Projects"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/projects")
|
||||
ResponseEntity<PaginatedResult<Project>> getProjects(
|
||||
@ApiParam("The query to use when searching") @RequestParam(required = false) String q,
|
||||
@ApiParam("Whether projects should be sorted by the relevance to the given query") @RequestParam(defaultValue = "true") boolean relevance,
|
||||
@ApiParam("Pagination information") @NotNull RequestPagination pagination
|
||||
@Parameter(description = "The query to use when searching") @RequestParam(required = false) String q,
|
||||
@Parameter(description = "Whether projects should be sorted by the relevance to the given query") @RequestParam(defaultValue = "true") boolean relevance,
|
||||
@Parameter(description = "Pagination information") @NotNull RequestPagination pagination
|
||||
);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns the stats for a project",
|
||||
nickname = "showProjectStats",
|
||||
notes = "Returns the stats (downloads and views) for a project per day for a certain date range. Requires the `is_subject_member` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns the stats for a project",
|
||||
operationId = "showProjectStats",
|
||||
description = "Returns the stats (downloads and views) for a project per day for a certain date range. Requires the `is_subject_member` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Projects"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/projects/{author}/{slug}/stats")
|
||||
ResponseEntity<Map<String, DayProjectStats>> getProjectStats(@ApiParam("The author of the project to return stats for") @PathVariable String author,
|
||||
@ApiParam("The slug of the project to return stats for") @PathVariable String slug,
|
||||
@NotNull @ApiParam(value = "The first date to include in the result", required = true) @RequestParam OffsetDateTime fromDate,
|
||||
@NotNull @ApiParam(value = "The last date to include in the result", required = true) @RequestParam OffsetDateTime toDate
|
||||
ResponseEntity<Map<String, DayProjectStats>> getProjectStats(@Parameter(description = "The author of the project to return stats for") @PathVariable String author,
|
||||
@Parameter(description = "The slug of the project to return stats for") @PathVariable String slug,
|
||||
@NotNull @Parameter(description = "The first date to include in the result", required = true) @RequestParam OffsetDateTime fromDate,
|
||||
@NotNull @Parameter(description = "The last date to include in the result", required = true) @RequestParam OffsetDateTime toDate
|
||||
);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns the stargazers of a project",
|
||||
nickname = "getProjectStargazers",
|
||||
notes = "Returns the stargazers of a project. Requires the `view_public_info` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns the stargazers of a project",
|
||||
operationId = "getProjectStargazers",
|
||||
description = "Returns the stargazers of a project. Requires the `view_public_info` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Projects"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/projects/{author}/{slug}/stargazers")
|
||||
ResponseEntity<PaginatedResult<User>> getProjectStargazers(
|
||||
@ApiParam("The author of the project to return stargazers for") @PathVariable("author") String author,
|
||||
@ApiParam("The slug of the project to return stargazers for") @PathVariable("slug") String slug,
|
||||
@ApiParam("Pagination information") @NotNull RequestPagination pagination
|
||||
@Parameter(description = "The author of the project to return stargazers for") @PathVariable("author") String author,
|
||||
@Parameter(description = "The slug of the project to return stargazers for") @PathVariable("slug") String slug,
|
||||
@Parameter(description = "Pagination information") @NotNull RequestPagination pagination
|
||||
);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns the watchers of a project",
|
||||
nickname = "getProjectWatchers",
|
||||
notes = "Returns the watchers of a project. Requires the `view_public_info` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns the watchers of a project",
|
||||
operationId = "getProjectWatchers",
|
||||
description = "Returns the watchers of a project. Requires the `view_public_info` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Projects"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/projects/{author}/{slug}/watchers")
|
||||
ResponseEntity<PaginatedResult<User>> getProjectWatchers(
|
||||
@ApiParam("The author of the project to return watchers for") @PathVariable("author") String author,
|
||||
@ApiParam("The slug of the project to return watchers for") @PathVariable("slug") String slug,
|
||||
@ApiParam("Pagination information") @NotNull RequestPagination pagination
|
||||
@Parameter(description = "The author of the project to return watchers for") @PathVariable("author") String author,
|
||||
@Parameter(description = "The slug of the project to return watchers for") @PathVariable("slug") String slug,
|
||||
@Parameter(description = "Pagination information") @NotNull RequestPagination pagination
|
||||
);
|
||||
}
|
||||
|
@ -5,12 +5,12 @@ import io.papermc.hangar.model.api.User;
|
||||
import io.papermc.hangar.model.api.project.ProjectCompact;
|
||||
import io.papermc.hangar.model.api.project.ProjectSortingStrategy;
|
||||
import io.papermc.hangar.model.api.requests.RequestPagination;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.annotations.ApiResponse;
|
||||
import io.swagger.annotations.ApiResponses;
|
||||
import io.swagger.annotations.Authorization;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.http.MediaType;
|
||||
@ -21,117 +21,117 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Api(tags = "Users", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Tag(name = "Users")
|
||||
@RequestMapping(path = "/api/v1", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
|
||||
public interface IUsersController {
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns a specific user",
|
||||
nickname = "getUser",
|
||||
notes = "Returns a specific user. Requires the `view_public_info` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns a specific user",
|
||||
operationId = "getUser",
|
||||
description = "Returns a specific user. Requires the `view_public_info` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Users"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/users/{user}")
|
||||
ResponseEntity<User> getUser(@ApiParam("The name of the user to return") @PathVariable("user") String userName);
|
||||
ResponseEntity<User> getUser(@Parameter(description = "The name of the user to return") @PathVariable("user") String userName);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Searches for users",
|
||||
nickname = "showUsers",
|
||||
notes = "Returns a list of users based on a search query",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Searches for users",
|
||||
operationId = "showUsers",
|
||||
description = "Returns a list of users based on a search query",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Users"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/users")
|
||||
ResponseEntity<PaginatedResult<User>> getUsers(@ApiParam(value = "The search query", required = true) @RequestParam(value = "query", required = false) String query,
|
||||
@ApiParam("Pagination information") @NotNull RequestPagination pagination);
|
||||
ResponseEntity<PaginatedResult<User>> getUsers(@Parameter(description = "The search query", required = true) @RequestParam(defaultValue = "query", required = false) String query,
|
||||
@Parameter(description = "Pagination information") @NotNull RequestPagination pagination);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns the starred projects for a specific user",
|
||||
nickname = "showStarred",
|
||||
notes = "Returns the starred projects for a specific user. Requires the `view_public_info` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns the starred projects for a specific user",
|
||||
operationId = "showStarred",
|
||||
description = "Returns the starred projects for a specific user. Requires the `view_public_info` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Users"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/users/{user}/starred")
|
||||
ResponseEntity<PaginatedResult<ProjectCompact>> getUserStarred(@ApiParam("The user to return starred projects for") @PathVariable("user") String userName,
|
||||
@ApiParam("How to sort the projects") @RequestParam(defaultValue = "updated") ProjectSortingStrategy sort,
|
||||
@ApiParam("Pagination information") @NotNull RequestPagination pagination);
|
||||
ResponseEntity<PaginatedResult<ProjectCompact>> getUserStarred(@Parameter(description = "The user to return starred projects for") @PathVariable("user") String userName,
|
||||
@Parameter(description = "How to sort the projects") @RequestParam(defaultValue = "updated") ProjectSortingStrategy sort,
|
||||
@Parameter(description = "Pagination information") @NotNull RequestPagination pagination);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns the watched projects for a specific user",
|
||||
nickname = "getUserWatching",
|
||||
notes = "Returns the watched projects for a specific user. Requires the `view_public_info` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns the watched projects for a specific user",
|
||||
operationId = "getUserWatching",
|
||||
description = "Returns the watched projects for a specific user. Requires the `view_public_info` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Users"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/users/{user}/watching")
|
||||
ResponseEntity<PaginatedResult<ProjectCompact>> getUserWatching(@ApiParam("The user to return watched projects for") @PathVariable("user") String userName,
|
||||
@ApiParam("How to sort the projects") @RequestParam(defaultValue = "updated") ProjectSortingStrategy sort,
|
||||
@ApiParam("Pagination information") @NotNull RequestPagination pagination);
|
||||
ResponseEntity<PaginatedResult<ProjectCompact>> getUserWatching(@Parameter(description = "The user to return watched projects for") @PathVariable("user") String userName,
|
||||
@Parameter(description = "How to sort the projects") @RequestParam(defaultValue = "updated") ProjectSortingStrategy sort,
|
||||
@Parameter(description = "Pagination information") @NotNull RequestPagination pagination);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns the pinned projects for a specific user",
|
||||
nickname = "getUserPinnedProjects",
|
||||
notes = "Returns the pinned projects for a specific user. Requires the `view_public_info` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns the pinned projects for a specific user",
|
||||
operationId = "getUserPinnedProjects",
|
||||
description = "Returns the pinned projects for a specific user. Requires the `view_public_info` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Users"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/users/{user}/pinned")
|
||||
ResponseEntity<List<ProjectCompact>> getUserPinnedProjects(@ApiParam("The user to return pinned projects for") @PathVariable("user") String userName);
|
||||
ResponseEntity<List<ProjectCompact>> getUserPinnedProjects(@Parameter(description = "The user to return pinned projects for") @PathVariable("user") String userName);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns all users with at least one public project",
|
||||
nickname = "getAuthors",
|
||||
notes = "Returns all users that have at least one public project. Requires the `view_public_info` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns all users with at least one public project",
|
||||
operationId = "getAuthors",
|
||||
description = "Returns all users that have at least one public project. Requires the `view_public_info` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Users"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/authors")
|
||||
ResponseEntity<PaginatedResult<User>> getAuthors(@ApiParam("Pagination information") @NotNull RequestPagination pagination);
|
||||
ResponseEntity<PaginatedResult<User>> getAuthors(@Parameter(description = "Pagination information") @NotNull RequestPagination pagination);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns Hangar staff",
|
||||
nickname = "getStaff",
|
||||
notes = "Returns Hanagr staff. Requires the `view_public_info` permission.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns Hangar staff",
|
||||
operationId = "getStaff",
|
||||
description = "Returns Hanagr staff. Requires the `view_public_info` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Users"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/staff")
|
||||
ResponseEntity<PaginatedResult<User>> getStaff(@ApiParam("Pagination information") @NotNull RequestPagination pagination);
|
||||
ResponseEntity<PaginatedResult<User>> getStaff(@Parameter(description = "Pagination information") @NotNull RequestPagination pagination);
|
||||
}
|
||||
|
@ -5,12 +5,12 @@ import io.papermc.hangar.model.api.project.version.Version;
|
||||
import io.papermc.hangar.model.api.project.version.VersionStats;
|
||||
import io.papermc.hangar.model.api.requests.RequestPagination;
|
||||
import io.papermc.hangar.model.common.Platform;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.annotations.ApiResponse;
|
||||
import io.swagger.annotations.ApiResponses;
|
||||
import io.swagger.annotations.Authorization;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Map;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -21,98 +21,97 @@ import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Api(tags = "Versions", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Tag(name = "Versions")
|
||||
@RequestMapping(path = "/api/v1", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public interface IVersionsController {
|
||||
|
||||
// TODO implement version creation via API
|
||||
/*@ApiOperation(
|
||||
value = "Creates a new version",
|
||||
nickname = "deployVersion",
|
||||
/*@Operation(
|
||||
summary ="Creates a new version",
|
||||
operationId = "deployVersion",
|
||||
notes = "Creates a new version for a project. Requires the `create_version` permission in the project or owning organization.",
|
||||
authorizations = @Authorization("Session"),
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Versions"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 201, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = 201, description = "Ok"),
|
||||
@ApiResponse(responseCode = 401, description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = 403, description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@PostMapping(path = "/projects/{author}/{slug}/versions", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
ResponseEntity<Version> deployVersion()*/
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns a specific version of a project",
|
||||
nickname = "showVersion",
|
||||
notes = "Returns a specific version of a project. Requires the `view_public_info` permission in the project or owning organization.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns a specific version of a project",
|
||||
operationId = "showVersion",
|
||||
description = "Returns a specific version of a project. Requires the `view_public_info` permission in the project or owning organization.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Version"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/projects/{author}/{slug}/versions/{name}")
|
||||
Version getVersion(@ApiParam("The author of the project to return the version for") @PathVariable String author,
|
||||
@ApiParam("The slug of the project to return the version for") @PathVariable String slug,
|
||||
@ApiParam("The name of the version to return") @PathVariable("name") String versionString);
|
||||
Version getVersion(@Parameter(description = "The author of the project to return the version for") @PathVariable String author,
|
||||
@Parameter(description = "The slug of the project to return the version for") @PathVariable String slug,
|
||||
@Parameter(description = "The name of the version to return") @PathVariable("name") String versionString);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns all versions of a project",
|
||||
nickname = "listVersions",
|
||||
notes = "Returns all versions of a project. Requires the `view_public_info` permission in the project or owning organization.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns all versions of a project",
|
||||
operationId = "listVersions",
|
||||
description = "Returns all versions of a project. Requires the `view_public_info` permission in the project or owning organization.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Versions"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/projects/{author}/{slug}/versions")
|
||||
PaginatedResult<Version> getVersions(@ApiParam("The author of the project to return versions for") @PathVariable String author,
|
||||
@ApiParam("The slug of the project to return versions for") @PathVariable String slug,
|
||||
@ApiParam("Pagination information") @NotNull RequestPagination pagination);
|
||||
PaginatedResult<Version> getVersions(@Parameter(description = "The author of the project to return versions for") @PathVariable String author,
|
||||
@Parameter(description = "The slug of the project to return versions for") @PathVariable String slug,
|
||||
@Parameter(description = "Pagination information") @NotNull RequestPagination pagination);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Returns the stats for a version",
|
||||
nickname = "showVersionStats",
|
||||
notes = "Returns the stats (downloads) for a version per day for a certain date range. Requires the `is_subject_member` permission.",
|
||||
responseContainer = "Map",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Returns the stats for a version",
|
||||
operationId = "showVersionStats",
|
||||
description = "Returns the stats (downloads) for a version per day for a certain date range. Requires the `is_subject_member` permission.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Versions"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping("/projects/{author}/{slug}/versions/{name}/{platform}/stats")
|
||||
Map<String, VersionStats> getVersionStats(@ApiParam("The author of the version to return the stats for") @PathVariable String author,
|
||||
@ApiParam("The slug of the project to return stats for") @PathVariable String slug,
|
||||
@ApiParam("The version to return the stats for") @PathVariable("name") String versionString,
|
||||
@ApiParam("The platform of the version to return stats for") @PathVariable Platform platform,
|
||||
@ApiParam(value = "The first date to include in the result", required = true) @RequestParam @NotNull OffsetDateTime fromDate,
|
||||
@ApiParam(value = "The last date to include in the result", required = true) @RequestParam @NotNull OffsetDateTime toDate);
|
||||
Map<String, VersionStats> getVersionStats(@Parameter(description = "The author of the version to return the stats for") @PathVariable String author,
|
||||
@Parameter(description = "The slug of the project to return stats for") @PathVariable String slug,
|
||||
@Parameter(description = "The version to return the stats for") @PathVariable("name") String versionString,
|
||||
@Parameter(description = "The platform of the version to return stats for") @PathVariable Platform platform,
|
||||
@Parameter(description = "The first date to include in the result", required = true) @RequestParam @NotNull OffsetDateTime fromDate,
|
||||
@Parameter(description = "The last date to include in the result", required = true) @RequestParam @NotNull OffsetDateTime toDate);
|
||||
|
||||
@ApiOperation(
|
||||
value = "Downloads a version",
|
||||
nickname = "downloadVersion",
|
||||
notes = "Downloads the file for a specific platform of a version. Requires visibility of the project and version.",
|
||||
authorizations = @Authorization("Session"),
|
||||
@Operation(
|
||||
summary = "Downloads a version",
|
||||
operationId = "downloadVersion",
|
||||
description = "Downloads the file for a specific platform of a version. Requires visibility of the project and version.",
|
||||
security = @SecurityRequirement(name = "Session"),
|
||||
tags = "Versions"
|
||||
)
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "Ok"),
|
||||
@ApiResponse(code = 303, message = "Version has an external download url"),
|
||||
@ApiResponse(code = 400, message = "Version doesn't have a file attached to it"),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")
|
||||
@ApiResponse(responseCode = "200", description = "Ok"),
|
||||
@ApiResponse(responseCode = "303", description = "Version has an external download url"),
|
||||
@ApiResponse(responseCode = "400", description = "Version doesn't have a file attached to it"),
|
||||
@ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint")
|
||||
})
|
||||
@GetMapping(value = "/projects/{author}/{slug}/versions/{name}/{platform}/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
Resource downloadVersion(@ApiParam("The author of the project to download the version from") @PathVariable String author,
|
||||
@ApiParam("The slug of the project to download the version from") @PathVariable String slug,
|
||||
@ApiParam("The name of the version to download") @PathVariable("name") String versionString,
|
||||
@ApiParam("The platform of the version to download") @PathVariable Platform platform);
|
||||
Resource downloadVersion(@Parameter(description = "The author of the project to download the version from") @PathVariable String author,
|
||||
@Parameter(description = "The slug of the project to download the version from") @PathVariable String slug,
|
||||
@Parameter(description = "The name of the version to download") @PathVariable("name") String versionString,
|
||||
@Parameter(description = "The platform of the version to download") @PathVariable Platform platform);
|
||||
}
|
||||
|
@ -12,20 +12,13 @@ import io.papermc.hangar.security.annotations.ratelimit.RateLimit;
|
||||
import io.papermc.hangar.security.annotations.unlocked.Unlocked;
|
||||
import io.papermc.hangar.service.APIKeyService;
|
||||
import io.papermc.hangar.service.PermissionService;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
|
||||
// @el(user: io.papermc.hangar.model.db.UserTable)
|
||||
@LoggedIn
|
||||
@ -70,14 +63,14 @@ public class ApiKeyController {
|
||||
@CurrentUser("#user")
|
||||
@RateLimit(overdraft = 5, refillTokens = 1, refillSeconds = 20)
|
||||
@PostMapping(path = "/create-key/{user}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
|
||||
public String createApiKey(@PathVariable final UserTable user, @RequestBody final @Valid CreateAPIKeyForm apiKeyForm) {
|
||||
public String createApiKey(@PathVariable final UserTable user, @RequestBody @Valid final CreateAPIKeyForm apiKeyForm) {
|
||||
return this.apiKeyService.createApiKey(user, apiKeyForm, this.permissionService.getAllPossiblePermissions(user.getId()));
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@CurrentUser("#user")
|
||||
@PostMapping(path = "/delete-key/{user}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void deleteApiKey(@PathVariable final UserTable user, @RequestBody final @Valid StringContent nameContent) {
|
||||
public void deleteApiKey(@PathVariable final UserTable user, @RequestBody @Valid final StringContent nameContent) {
|
||||
this.apiKeyService.deleteApiKey(user, nameContent.getContent());
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,10 @@ import io.papermc.hangar.security.annotations.unlocked.Unlocked;
|
||||
import io.papermc.hangar.security.annotations.visibility.VisibilityRequired;
|
||||
import io.papermc.hangar.service.internal.projects.ChannelService;
|
||||
import io.papermc.hangar.service.internal.projects.ProjectService;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
@ -75,7 +75,7 @@ public class ChannelController extends HangarComponent {
|
||||
@RateLimit(overdraft = 5, refillTokens = 1, refillSeconds = 15)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_CHANNEL, args = "{#projectId}")
|
||||
@PostMapping(path = "/{projectId}/create", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void createChannel(@PathVariable final long projectId, @RequestBody final @Valid ChannelForm channelForm) {
|
||||
public void createChannel(@PathVariable final long projectId, @RequestBody @Valid final ChannelForm channelForm) {
|
||||
final Set<ChannelFlag> flags = channelForm.getFlags();
|
||||
flags.retainAll(flags.stream().filter(ChannelFlag::isEditable).collect(Collectors.toSet()));
|
||||
this.channelService.createProjectChannel(channelForm.getName(), channelForm.getColor(), projectId, flags);
|
||||
@ -86,7 +86,7 @@ public class ChannelController extends HangarComponent {
|
||||
@RateLimit(overdraft = 5, refillTokens = 1, refillSeconds = 15)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_CHANNEL, args = "{#projectId}")
|
||||
@PostMapping(path = "/{projectId}/edit", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void editChannel(@PathVariable final long projectId, @RequestBody final @Valid EditChannelForm channelForm) {
|
||||
public void editChannel(@PathVariable final long projectId, @RequestBody @Valid final EditChannelForm channelForm) {
|
||||
final Set<ChannelFlag> flags = channelForm.getFlags();
|
||||
flags.retainAll(flags.stream().filter(ChannelFlag::isEditable).collect(Collectors.toSet()));
|
||||
this.channelService.editProjectChannel(channelForm.getId(), channelForm.getName(), channelForm.getColor(), projectId, flags);
|
||||
|
@ -37,25 +37,17 @@ import io.papermc.hangar.service.internal.users.UserService;
|
||||
import io.papermc.hangar.service.internal.users.invites.InviteService;
|
||||
import io.papermc.hangar.service.internal.users.invites.OrganizationInviteService;
|
||||
import io.papermc.hangar.service.internal.users.invites.ProjectInviteService;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.CookieValue;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@LoggedIn
|
||||
@ -124,7 +116,7 @@ public class HangarUserController extends HangarComponent {
|
||||
@RateLimit(overdraft = 7, refillTokens = 1, refillSeconds = 20)
|
||||
@PermissionRequired(NamedPermission.EDIT_OWN_USER_SETTINGS)
|
||||
@PostMapping(path = "/users/{userName}/settings/tagline", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void saveTagline(@PathVariable final String userName, @RequestBody final @Valid StringContent content) {
|
||||
public void saveTagline(@PathVariable final String userName, @RequestBody @Valid final StringContent content) {
|
||||
final UserTable userTable = this.userService.getUserTable(userName);
|
||||
if (userTable == null) {
|
||||
throw new HangarApiException(HttpStatus.NOT_FOUND);
|
||||
|
@ -28,24 +28,17 @@ import io.papermc.hangar.service.internal.organizations.OrganizationService;
|
||||
import io.papermc.hangar.service.internal.perms.members.OrganizationMemberService;
|
||||
import io.papermc.hangar.service.internal.users.UserService;
|
||||
import io.papermc.hangar.service.internal.users.invites.OrganizationInviteService;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
// @el(orgName: String)
|
||||
@Controller
|
||||
@ -94,7 +87,7 @@ public class OrganizationController extends HangarComponent {
|
||||
@RateLimit(overdraft = 7, refillTokens = 2, refillSeconds = 10)
|
||||
@PermissionRequired(type = PermissionType.ORGANIZATION, perms = NamedPermission.MANAGE_SUBJECT_MEMBERS, args = "{#orgName}")
|
||||
@PostMapping(path = "/org/{orgName}/members/add", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void addOrganizationMember(@PathVariable final String orgName, @RequestBody final @Valid EditMembersForm.Member<OrganizationRole> member) {
|
||||
public void addOrganizationMember(@PathVariable final String orgName, @RequestBody @Valid final EditMembersForm.Member<OrganizationRole> member) {
|
||||
final OrganizationTable organizationTable = this.organizationService.getOrganizationTable(orgName);
|
||||
if (organizationTable == null) {
|
||||
throw new HangarApiException("Org " + orgName + " doesn't exist");
|
||||
@ -107,7 +100,7 @@ public class OrganizationController extends HangarComponent {
|
||||
@RateLimit(overdraft = 5, refillTokens = 2, refillSeconds = 10)
|
||||
@PermissionRequired(type = PermissionType.ORGANIZATION, perms = NamedPermission.MANAGE_SUBJECT_MEMBERS, args = "{#orgName}")
|
||||
@PostMapping(path = "/org/{orgName}/members/edit", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void editOrganizationMember(@PathVariable final String orgName, @RequestBody final @Valid EditMembersForm.Member<OrganizationRole> member) {
|
||||
public void editOrganizationMember(@PathVariable final String orgName, @RequestBody @Valid final EditMembersForm.Member<OrganizationRole> member) {
|
||||
final OrganizationTable organizationTable = this.organizationService.getOrganizationTable(orgName);
|
||||
this.memberService.editMember(member, organizationTable);
|
||||
}
|
||||
@ -117,7 +110,7 @@ public class OrganizationController extends HangarComponent {
|
||||
@RateLimit(overdraft = 7, refillTokens = 2, refillSeconds = 10)
|
||||
@PermissionRequired(type = PermissionType.ORGANIZATION, perms = NamedPermission.MANAGE_SUBJECT_MEMBERS, args = "{#orgName}")
|
||||
@PostMapping(path = "/org/{orgName}/members/remove", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void removeOrganizationMember(@PathVariable final String orgName, @RequestBody final @Valid EditMembersForm.Member<OrganizationRole> member) {
|
||||
public void removeOrganizationMember(@PathVariable final String orgName, @RequestBody @Valid final EditMembersForm.Member<OrganizationRole> member) {
|
||||
final OrganizationTable organizationTable = this.organizationService.getOrganizationTable(orgName);
|
||||
this.memberService.removeMember(member, organizationTable);
|
||||
}
|
||||
@ -136,7 +129,7 @@ public class OrganizationController extends HangarComponent {
|
||||
@PermissionRequired(type = PermissionType.ORGANIZATION, perms = NamedPermission.IS_SUBJECT_OWNER, args = "{#orgName}")
|
||||
@RateLimit(overdraft = 5, refillTokens = 1, refillSeconds = 60)
|
||||
@PostMapping(path = "/org/{orgName}/transfer", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void transferOrganization(@PathVariable final String orgName, @RequestBody final @Valid StringContent nameContent) {
|
||||
public void transferOrganization(@PathVariable final String orgName, @RequestBody @Valid final StringContent nameContent) {
|
||||
final OrganizationTable organizationTable = this.organizationService.getOrganizationTable(orgName);
|
||||
this.inviteService.sendTransferRequest(nameContent.getContent(), organizationTable);
|
||||
}
|
||||
@ -154,7 +147,7 @@ public class OrganizationController extends HangarComponent {
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@RateLimit(overdraft = 3, refillTokens = 1, refillSeconds = 60)
|
||||
@PostMapping(path = "/create", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void create(@RequestBody final @Valid CreateOrganizationForm createOrganizationForm) {
|
||||
public void create(@RequestBody @Valid final CreateOrganizationForm createOrganizationForm) {
|
||||
this.organizationFactory.createOrganization(createOrganizationForm.getName());
|
||||
}
|
||||
|
||||
@ -163,7 +156,7 @@ public class OrganizationController extends HangarComponent {
|
||||
@RateLimit(overdraft = 3, refillTokens = 1, refillSeconds = 60)
|
||||
@PermissionRequired(type = PermissionType.ORGANIZATION, perms = NamedPermission.DELETE_ORGANIZATION, args = "{#orgName}")
|
||||
@PostMapping(path = "/org/{orgName}/delete", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void delete(@PathVariable final String orgName, @RequestBody final @Valid StringContent content) {
|
||||
public void delete(@PathVariable final String orgName, @RequestBody @Valid final StringContent content) {
|
||||
final OrganizationTable organizationTable = this.organizationService.getOrganizationTable(orgName);
|
||||
this.organizationFactory.deleteOrganization(organizationTable, content.getContent());
|
||||
}
|
||||
@ -173,7 +166,7 @@ public class OrganizationController extends HangarComponent {
|
||||
@RateLimit(overdraft = 7, refillTokens = 1, refillSeconds = 20)
|
||||
@PermissionRequired(type = PermissionType.ORGANIZATION, perms = NamedPermission.EDIT_SUBJECT_SETTINGS, args = "{#orgName}")
|
||||
@PostMapping(path = "/org/{orgName}/settings/tagline", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void saveTagline(@PathVariable final String orgName, @RequestBody final @Valid StringContent content) {
|
||||
public void saveTagline(@PathVariable final String orgName, @RequestBody @Valid final StringContent content) {
|
||||
final UserTable userTable = this.userService.getUserTable(orgName);
|
||||
final OrganizationTable organizationTable = this.organizationService.getOrganizationTable(orgName);
|
||||
if (userTable == null || organizationTable == null) {
|
||||
|
@ -9,19 +9,14 @@ import io.papermc.hangar.security.annotations.permission.PermissionRequired;
|
||||
import io.papermc.hangar.security.annotations.ratelimit.RateLimit;
|
||||
import io.papermc.hangar.security.annotations.unlocked.Unlocked;
|
||||
import io.papermc.hangar.service.internal.versions.ReviewService;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
|
||||
@Unlocked
|
||||
@Controller
|
||||
@ -45,49 +40,49 @@ public class ReviewController {
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PostMapping(path = "/{versionId}/reviews/start", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void startVersionReview(@PathVariable final long versionId, @RequestBody final @Valid ReviewMessage message) {
|
||||
public void startVersionReview(@PathVariable final long versionId, @RequestBody @Valid final ReviewMessage message) {
|
||||
this.reviewService.startReview(versionId, message);
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PostMapping(path = "/{versionId}/reviews/message", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void addVersionReviewMessage(@PathVariable final long versionId, @RequestBody final @Valid ReviewMessage message) {
|
||||
public void addVersionReviewMessage(@PathVariable final long versionId, @RequestBody @Valid final ReviewMessage message) {
|
||||
this.reviewService.addReviewMessage(versionId, message);
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PostMapping(path = "/{versionId}/reviews/stop", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void stopVersionReview(@PathVariable final long versionId, @RequestBody final @Valid ReviewMessage message) {
|
||||
public void stopVersionReview(@PathVariable final long versionId, @RequestBody @Valid final ReviewMessage message) {
|
||||
this.reviewService.stopReview(versionId, message);
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PostMapping(path = "/{versionId}/reviews/reopen", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void reopenVersionReview(@PathVariable final long versionId, @RequestBody final @Valid ReviewMessage message) {
|
||||
public void reopenVersionReview(@PathVariable final long versionId, @RequestBody @Valid final ReviewMessage message) {
|
||||
this.reviewService.reopenReview(versionId, message, ReviewAction.REOPEN);
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PostMapping(path = "/{versionId}/reviews/approve", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void approveVersionReview(@PathVariable final long versionId, @RequestBody final @Valid ReviewMessage message) {
|
||||
public void approveVersionReview(@PathVariable final long versionId, @RequestBody @Valid final ReviewMessage message) {
|
||||
this.reviewService.approveReview(versionId, message, ReviewState.REVIEWED, ReviewAction.APPROVE);
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PostMapping(path = "/{versionId}/reviews/approvePartial", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void approvePartialVersionReview(@PathVariable final long versionId, @RequestBody final @Valid ReviewMessage message) {
|
||||
public void approvePartialVersionReview(@PathVariable final long versionId, @RequestBody @Valid final ReviewMessage message) {
|
||||
this.reviewService.approveReview(versionId, message, ReviewState.PARTIALLY_REVIEWED, ReviewAction.PARTIALLY_APPROVE);
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PostMapping(path = "/{versionId}/reviews/undoApproval", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void undoApproval(@PathVariable final long versionId, @RequestBody final @Valid ReviewMessage message) {
|
||||
public void undoApproval(@PathVariable final long versionId, @RequestBody @Valid final ReviewMessage message) {
|
||||
this.reviewService.undoApproval(versionId, message);
|
||||
}
|
||||
}
|
||||
|
@ -24,10 +24,10 @@ import io.papermc.hangar.service.internal.versions.PinnedVersionService;
|
||||
import io.papermc.hangar.service.internal.versions.VersionDependencyService;
|
||||
import io.papermc.hangar.service.internal.versions.VersionFactory;
|
||||
import io.papermc.hangar.service.internal.versions.VersionService;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@ -79,9 +79,9 @@ public class VersionController extends HangarComponent {
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.CREATE_VERSION, args = "{#projectId}")
|
||||
@PostMapping(path = "/version/{id}/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<PendingVersion> create(@PathVariable("id") final long projectId,
|
||||
@RequestPart(required = false) final @Size(max = 3, message = "version.new.error.invalidNumOfPlatforms") List<@Valid MultipartFile> files,
|
||||
@RequestPart final @Size(min = 1, max = 3, message = "version.new.error.invalidNumOfPlatforms") List<@Valid MultipartFileOrUrl> data,
|
||||
@RequestPart final @NotBlank String channel) {
|
||||
@RequestPart(required = false) @Size(max = 3, message = "version.new.error.invalidNumOfPlatforms") final List<@Valid MultipartFile> files,
|
||||
@RequestPart @Size(min = 1, max = 3, message = "version.new.error.invalidNumOfPlatforms") final List<@Valid MultipartFileOrUrl> data,
|
||||
@RequestPart @NotBlank final String channel) {
|
||||
// Use separate lists to hack around multipart form data limitations
|
||||
return ResponseEntity.ok(this.versionFactory.createPendingVersion(projectId, data, files, channel));
|
||||
}
|
||||
@ -91,7 +91,7 @@ public class VersionController extends HangarComponent {
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.CREATE_VERSION, args = "{#projectId}")
|
||||
@PostMapping(path = "/version/{id}/create", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void createVersion(@PathVariable("id") final long projectId, @RequestBody final @Valid PendingVersion pendingVersion) {
|
||||
public void createVersion(@PathVariable("id") final long projectId, @RequestBody @Valid final PendingVersion pendingVersion) {
|
||||
this.versionFactory.publishPendingVersion(projectId, pendingVersion);
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ public class VersionController extends HangarComponent {
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_VERSION, args = "{#projectId}")
|
||||
@PostMapping(path = "/version/{projectId}/{versionId}/saveDescription", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void saveDescription(@PathVariable final long projectId, @PathVariable final long versionId, @RequestBody final @Valid StringContent stringContent) {
|
||||
public void saveDescription(@PathVariable final long projectId, @PathVariable final long versionId, @RequestBody @Valid final StringContent stringContent) {
|
||||
if (stringContent.getContent().length() > this.config.pages.maxLen()) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "page.new.error.maxLength");
|
||||
}
|
||||
@ -121,7 +121,7 @@ public class VersionController extends HangarComponent {
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_VERSION, args = "{#projectId}")
|
||||
@RateLimit(overdraft = 5, refillTokens = 1, refillSeconds = 10)
|
||||
@PostMapping(path = "/version/{projectId}/{versionId}/savePlatformVersions", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void savePlatformVersions(@PathVariable final long projectId, @PathVariable final long versionId, @RequestBody final @Valid UpdatePlatformVersions updatePlatformVersions) {
|
||||
public void savePlatformVersions(@PathVariable final long projectId, @PathVariable final long versionId, @RequestBody @Valid final UpdatePlatformVersions updatePlatformVersions) {
|
||||
this.versionDependencyService.updateVersionPlatformVersions(projectId, versionId, updatePlatformVersions);
|
||||
}
|
||||
|
||||
@ -130,7 +130,7 @@ public class VersionController extends HangarComponent {
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_VERSION, args = "{#projectId}")
|
||||
@RateLimit(overdraft = 5, refillTokens = 1, refillSeconds = 10)
|
||||
@PostMapping(path = "/version/{projectId}/{versionId}/savePluginDependencies", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void savePluginDependencies(@PathVariable final long projectId, @PathVariable final long versionId, @RequestBody final @Valid UpdatePluginDependencies updatePluginDependencies) {
|
||||
public void savePluginDependencies(@PathVariable final long projectId, @PathVariable final long versionId, @RequestBody @Valid final UpdatePluginDependencies updatePluginDependencies) {
|
||||
this.versionDependencyService.updateVersionPluginDependencies(projectId, versionId, updatePluginDependencies);
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ public class VersionController extends HangarComponent {
|
||||
@RateLimit(overdraft = 3, refillTokens = 1, refillSeconds = 30)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.DELETE_VERSION, args = "{#projectId}")
|
||||
@PostMapping(path = "/version/{projectId}/{versionId}/delete", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void softDeleteVersion(@PathVariable final long projectId, @PathVariable("versionId") final ProjectVersionTable version, @RequestBody final @Valid StringContent commentContent) {
|
||||
public void softDeleteVersion(@PathVariable final long projectId, @PathVariable("versionId") final ProjectVersionTable version, @RequestBody @Valid final StringContent commentContent) {
|
||||
this.versionService.softDeleteVersion(projectId, version, commentContent.getContent());
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@ public class VersionController extends HangarComponent {
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
@PermissionRequired(NamedPermission.HARD_DELETE_VERSION)
|
||||
@PostMapping(path = "/version/{projectId}/{versionId}/hardDelete", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void hardDeleteVersion(@PathVariable("projectId") final ProjectTable projectTable, @PathVariable("versionId") final ProjectVersionTable projectVersionTable, @RequestBody final @Valid StringContent commentContent) {
|
||||
public void hardDeleteVersion(@PathVariable("projectId") final ProjectTable projectTable, @PathVariable("versionId") final ProjectVersionTable projectVersionTable, @RequestBody @Valid final StringContent commentContent) {
|
||||
this.versionService.hardDeleteVersion(projectTable, projectVersionTable, commentContent.getContent());
|
||||
}
|
||||
|
||||
|
@ -33,9 +33,9 @@ import io.papermc.hangar.service.internal.admin.HealthService;
|
||||
import io.papermc.hangar.service.internal.admin.StatService;
|
||||
import io.papermc.hangar.service.internal.perms.roles.GlobalRoleService;
|
||||
import io.papermc.hangar.service.internal.users.UserService;
|
||||
import jakarta.validation.Valid;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
@ -80,7 +80,7 @@ public class AdminController extends HangarComponent {
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PostMapping(path = "/platformVersions", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
@PermissionRequired(NamedPermission.MANUAL_VALUE_CHANGES)
|
||||
public void changePlatformVersions(@RequestBody final @Valid ChangePlatformVersionsForm form) {
|
||||
public void changePlatformVersions(@RequestBody @Valid final ChangePlatformVersionsForm form) {
|
||||
this.platformService.updatePlatformVersions(form);
|
||||
}
|
||||
|
||||
@ -115,7 +115,7 @@ public class AdminController extends HangarComponent {
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(NamedPermission.IS_STAFF)
|
||||
@PostMapping(value = "/lock-user/{user}/{locked}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void setUserLock(@PathVariable final UserTable user, @PathVariable final boolean locked, @RequestBody final @Valid StringContent comment) {
|
||||
public void setUserLock(@PathVariable final UserTable user, @PathVariable final boolean locked, @RequestBody @Valid final StringContent comment) {
|
||||
this.userService.setLocked(user, locked, comment.getContent());
|
||||
}
|
||||
|
||||
|
@ -13,20 +13,14 @@ import io.papermc.hangar.security.annotations.permission.PermissionRequired;
|
||||
import io.papermc.hangar.security.annotations.ratelimit.RateLimit;
|
||||
import io.papermc.hangar.security.annotations.unlocked.Unlocked;
|
||||
import io.papermc.hangar.service.internal.admin.FlagService;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.validation.Valid;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
|
||||
@LoggedIn
|
||||
@Controller
|
||||
@ -44,7 +38,7 @@ public class FlagController extends HangarComponent {
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
@RateLimit(overdraft = 5, refillTokens = 1, refillSeconds = 10)
|
||||
@PostMapping(path = "/", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void flag(@RequestBody final @Valid FlagForm form) {
|
||||
public void flag(@RequestBody @Valid final FlagForm form) {
|
||||
this.flagService.createFlag(form.getProjectId(), form.getReason(), form.getComment());
|
||||
}
|
||||
|
||||
|
@ -11,19 +11,14 @@ import io.papermc.hangar.security.annotations.ratelimit.RateLimit;
|
||||
import io.papermc.hangar.security.annotations.unlocked.Unlocked;
|
||||
import io.papermc.hangar.service.internal.projects.ProjectAdminService;
|
||||
import io.papermc.hangar.service.internal.projects.ProjectNoteService;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
|
||||
// @el(projectId: long)
|
||||
@Unlocked
|
||||
@ -50,14 +45,14 @@ public class ProjectAdminController extends HangarComponent {
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
@PermissionRequired(NamedPermission.MOD_NOTES_AND_FLAGS)
|
||||
@PostMapping(path = "/notes/{projectId}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void addProjectNote(@PathVariable final long projectId, @RequestBody final @Valid StringContent content) {
|
||||
public void addProjectNote(@PathVariable final long projectId, @RequestBody @Valid final StringContent content) {
|
||||
this.projectNoteService.addNote(projectId, content.getContent());
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(NamedPermission.REVIEWER)
|
||||
@PostMapping(path = "/visibility/{projectId}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void changeProjectVisibility(@PathVariable final long projectId, @RequestBody final @Valid VisibilityChangeForm visibilityChangeForm) {
|
||||
public void changeProjectVisibility(@PathVariable final long projectId, @RequestBody @Valid final VisibilityChangeForm visibilityChangeForm) {
|
||||
this.projectAdminService.changeVisibility(projectId, visibilityChangeForm.getVisibility(), visibilityChangeForm.getComment());
|
||||
}
|
||||
|
||||
|
@ -29,9 +29,9 @@ import io.papermc.hangar.service.internal.projects.ProjectService;
|
||||
import io.papermc.hangar.service.internal.uploads.ImageService;
|
||||
import io.papermc.hangar.service.internal.users.UserService;
|
||||
import io.papermc.hangar.service.internal.users.invites.ProjectInviteService;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
@ -95,7 +95,7 @@ public class ProjectController extends HangarComponent {
|
||||
@Unlocked
|
||||
@RateLimit(overdraft = 5, refillTokens = 1, refillSeconds = 60)
|
||||
@PostMapping(value = "/create", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<String> createProject(@RequestBody final @Valid NewProjectForm newProject) {
|
||||
public ResponseEntity<String> createProject(@RequestBody @Valid final NewProjectForm newProject) {
|
||||
final ProjectTable projectTable = this.projectFactory.createProject(newProject);
|
||||
// need to do this here, outside the transactional
|
||||
this.projectService.refreshHomeProjects();
|
||||
@ -116,7 +116,7 @@ public class ProjectController extends HangarComponent {
|
||||
@RateLimit(overdraft = 10, refillTokens = 1, refillSeconds = 10)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_SUBJECT_SETTINGS, args = "{#author, #slug}")
|
||||
@PostMapping(path = "/project/{author}/{slug}/settings", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void saveProjectSettings(@PathVariable final String author, @PathVariable final String slug, @RequestBody final @Valid ProjectSettingsForm settingsForm) {
|
||||
public void saveProjectSettings(@PathVariable final String author, @PathVariable final String slug, @RequestBody @Valid final ProjectSettingsForm settingsForm) {
|
||||
this.projectService.saveSettings(author, slug, settingsForm);
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ public class ProjectController extends HangarComponent {
|
||||
@RateLimit(overdraft = 10, refillTokens = 1, refillSeconds = 5)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_SUBJECT_SETTINGS, args = "{#author, #slug}")
|
||||
@PostMapping(path = "/project/{author}/{slug}/sponsors", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void saveProjectSettings(@PathVariable final String author, @PathVariable final String slug, @RequestBody final @Valid StringContent content) {
|
||||
public void saveProjectSettings(@PathVariable final String author, @PathVariable final String slug, @RequestBody @Valid final StringContent content) {
|
||||
if (content.getContent().length() > this.config.projects.maxSponsorsLen()) {
|
||||
throw new HangarApiException("page.new.error.name.maxLength");
|
||||
}
|
||||
@ -155,7 +155,7 @@ public class ProjectController extends HangarComponent {
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.IS_SUBJECT_OWNER, args = "{#author, #slug}")
|
||||
@RateLimit(overdraft = 5, refillTokens = 1, refillSeconds = 60)
|
||||
@PostMapping(path = "/project/{author}/{slug}/rename", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<String> renameProject(@PathVariable final String author, @PathVariable final String slug, @RequestBody final @Valid StringContent nameContent) {
|
||||
public ResponseEntity<String> renameProject(@PathVariable final String author, @PathVariable final String slug, @RequestBody @Valid final StringContent nameContent) {
|
||||
return ResponseEntity.ok(this.projectFactory.renameProject(author, slug, nameContent.getContent()));
|
||||
}
|
||||
|
||||
@ -164,7 +164,7 @@ public class ProjectController extends HangarComponent {
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.IS_SUBJECT_OWNER, args = "{#author, #slug}")
|
||||
@RateLimit(overdraft = 5, refillTokens = 1, refillSeconds = 60)
|
||||
@PostMapping(path = "/project/{author}/{slug}/transfer", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void transferProject(@PathVariable final String author, @PathVariable final String slug, @RequestBody final @Valid StringContent nameContent) {
|
||||
public void transferProject(@PathVariable final String author, @PathVariable final String slug, @RequestBody @Valid final StringContent nameContent) {
|
||||
final ProjectTable projectTable = this.projectService.getProjectTable(author, slug);
|
||||
this.projectInviteService.sendTransferRequest(nameContent.getContent(), projectTable);
|
||||
}
|
||||
@ -183,7 +183,7 @@ public class ProjectController extends HangarComponent {
|
||||
@RateLimit(overdraft = 7, refillTokens = 2, refillSeconds = 10)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.MANAGE_SUBJECT_MEMBERS, args = "{#author, #slug}")
|
||||
@PostMapping(path = "/project/{author}/{slug}/members/add", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void addProjectMember(@PathVariable final String author, @PathVariable final String slug, @RequestBody final @Valid EditMembersForm.Member<ProjectRole> member) {
|
||||
public void addProjectMember(@PathVariable final String author, @PathVariable final String slug, @RequestBody @Valid final EditMembersForm.Member<ProjectRole> member) {
|
||||
final ProjectTable projectTable = this.projectService.getProjectTable(author, slug);
|
||||
this.projectInviteService.sendInvite(member, projectTable);
|
||||
}
|
||||
@ -193,7 +193,7 @@ public class ProjectController extends HangarComponent {
|
||||
@RateLimit(overdraft = 7, refillTokens = 1, refillSeconds = 10)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.MANAGE_SUBJECT_MEMBERS, args = "{#author, #slug}")
|
||||
@PostMapping(path = "/project/{author}/{slug}/members/edit", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void editProjectMember(@PathVariable final String author, @PathVariable final String slug, @RequestBody final @Valid EditMembersForm.Member<ProjectRole> member) {
|
||||
public void editProjectMember(@PathVariable final String author, @PathVariable final String slug, @RequestBody @Valid final EditMembersForm.Member<ProjectRole> member) {
|
||||
final ProjectTable projectTable = this.projectService.getProjectTable(author, slug);
|
||||
this.projectMemberService.editMember(member, projectTable);
|
||||
}
|
||||
@ -202,7 +202,7 @@ public class ProjectController extends HangarComponent {
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.MANAGE_SUBJECT_MEMBERS, args = "{#author, #slug}")
|
||||
@PostMapping(path = "/project/{author}/{slug}/members/remove", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void removeProjectMember(@PathVariable final String author, @PathVariable final String slug, @RequestBody final @Valid EditMembersForm.Member<ProjectRole> member) {
|
||||
public void removeProjectMember(@PathVariable final String author, @PathVariable final String slug, @RequestBody @Valid final EditMembersForm.Member<ProjectRole> member) {
|
||||
final ProjectTable projectTable = this.projectService.getProjectTable(author, slug);
|
||||
this.projectMemberService.removeMember(member, projectTable);
|
||||
}
|
||||
@ -252,7 +252,7 @@ public class ProjectController extends HangarComponent {
|
||||
@RateLimit(overdraft = 3, refillTokens = 1, refillSeconds = 45)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.DELETE_PROJECT, args = "{#project}")
|
||||
@PostMapping(path = "/project/{projectId}/manage/delete", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void softDeleteProject(@PathVariable("projectId") final ProjectTable project, @RequestBody final @Valid StringContent commentContent) {
|
||||
public void softDeleteProject(@PathVariable("projectId") final ProjectTable project, @RequestBody @Valid final StringContent commentContent) {
|
||||
this.projectFactory.softDelete(project, commentContent.getContent());
|
||||
}
|
||||
|
||||
@ -260,7 +260,7 @@ public class ProjectController extends HangarComponent {
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
@PermissionRequired(NamedPermission.HARD_DELETE_PROJECT)
|
||||
@PostMapping(path = "/project/{projectId}/manage/hardDelete", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void hardDeleteProject(@PathVariable("projectId") final ProjectTable project, @RequestBody final @Valid StringContent commentContent) {
|
||||
public void hardDeleteProject(@PathVariable("projectId") final ProjectTable project, @RequestBody @Valid final StringContent commentContent) {
|
||||
this.projectFactory.hardDelete(project, commentContent.getContent());
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,8 @@ import io.papermc.hangar.service.ValidationService;
|
||||
import io.papermc.hangar.service.internal.MarkdownService;
|
||||
import io.papermc.hangar.service.internal.projects.ProjectPageService;
|
||||
import io.papermc.hangar.util.BBCodeConverter;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.Collection;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
@ -54,7 +54,7 @@ public class ProjectPageController extends HangarComponent {
|
||||
@Anyone
|
||||
@RateLimit(overdraft = 20, refillTokens = 3, refillSeconds = 5, greedy = true)
|
||||
@PostMapping(path = "/render", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<String> renderMarkdown(@RequestBody final @Valid StringContent content) {
|
||||
public ResponseEntity<String> renderMarkdown(@RequestBody @Valid final StringContent content) {
|
||||
if (content.getContent().length() > this.config.projects.contentMaxLen()) {
|
||||
throw new HangarApiException("page.new.error.name.maxLength");
|
||||
}
|
||||
@ -65,7 +65,7 @@ public class ProjectPageController extends HangarComponent {
|
||||
@RateLimit(overdraft = 10, refillTokens = 3, refillSeconds = 5)
|
||||
@ResponseBody
|
||||
@PostMapping(path = "/convert-bbcode", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
|
||||
public String convertBBCode(@RequestBody final @Valid StringContent bbCodeContent) {
|
||||
public String convertBBCode(@RequestBody @Valid final StringContent bbCodeContent) {
|
||||
if (bbCodeContent.getContent().length() > this.config.projects.maxBBCodeLen()) {
|
||||
throw new HangarApiException("page.new.error.name.maxLength");
|
||||
}
|
||||
@ -100,7 +100,7 @@ public class ProjectPageController extends HangarComponent {
|
||||
@PermissionRequired(perms = NamedPermission.EDIT_PAGE, type = PermissionType.PROJECT, args = "{#projectId}")
|
||||
@PostMapping(value = "/create/{projectId}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public ResponseEntity<String> createProjectPage(@PathVariable final long projectId, @RequestBody final @Valid NewProjectPage newProjectPage) {
|
||||
public ResponseEntity<String> createProjectPage(@PathVariable final long projectId, @RequestBody @Valid final NewProjectPage newProjectPage) {
|
||||
return ResponseEntity.ok(this.projectPageService.createProjectPage(projectId, newProjectPage));
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@ public class ProjectPageController extends HangarComponent {
|
||||
@PermissionRequired(perms = NamedPermission.EDIT_PAGE, type = PermissionType.PROJECT, args = "{#projectId}")
|
||||
@PostMapping(value = "/save/{projectId}/{pageId}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public void saveProjectPage(@PathVariable final long projectId, @PathVariable final long pageId, @RequestBody final @Valid StringContent content) {
|
||||
public void saveProjectPage(@PathVariable final long projectId, @PathVariable final long pageId, @RequestBody @Valid final StringContent content) {
|
||||
this.projectPageService.saveProjectPage(projectId, pageId, content.getContent());
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
package io.papermc.hangar.controller.validations;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import jakarta.validation.Payload;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import javax.validation.Payload;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
|
@ -1,21 +1,16 @@
|
||||
package io.papermc.hangar.controller.validations;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import javax.validation.Payload;
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import jakarta.validation.Payload;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Documented
|
||||
@Constraint(validatedBy = Validate.ValidateConstraintValidator.class)
|
||||
|
@ -8,6 +8,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.boot.jackson.JsonComponent;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
public class HangarApiException extends ResponseStatusException {
|
||||
@ -84,7 +85,7 @@ public class HangarApiException extends ResponseStatusException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HttpHeaders getResponseHeaders() {
|
||||
public @NotNull HttpHeaders getHeaders() {
|
||||
return this.httpHeaders;
|
||||
}
|
||||
|
||||
@ -93,19 +94,12 @@ public class HangarApiException extends ResponseStatusException {
|
||||
|
||||
@Override
|
||||
public void serialize(final HangarApiException exception, final JsonGenerator gen, final SerializerProvider provider) throws IOException {
|
||||
String message = exception.getReason();
|
||||
HttpStatus status = null;
|
||||
final String message = exception.getReason();
|
||||
HttpStatusCode status = null;
|
||||
try {
|
||||
status = exception.getStatus();
|
||||
status = exception.getStatusCode();
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
if (message == null || message.isBlank()) {
|
||||
if (status != null) {
|
||||
message = status.getReasonPhrase();
|
||||
} else {
|
||||
message = "UNKNOWN";
|
||||
}
|
||||
}
|
||||
gen.writeStartObject();
|
||||
gen.writeStringField("message", message);
|
||||
gen.writeArrayFieldStart("messageArgs");
|
||||
@ -117,10 +111,8 @@ public class HangarApiException extends ResponseStatusException {
|
||||
gen.writeObjectFieldStart("httpError");
|
||||
if (status != null) {
|
||||
gen.writeNumberField("statusCode", status.value());
|
||||
gen.writeStringField("statusPhrase", status.getReasonPhrase());
|
||||
} else {
|
||||
gen.writeNumberField("statusCode", exception.getRawStatusCode());
|
||||
gen.writeStringField("statusPhrase", "UNKNOWN");
|
||||
gen.writeNumberField("statusCode", exception.getStatusCode().value());
|
||||
}
|
||||
gen.writeEndObject();
|
||||
}
|
||||
|
@ -32,10 +32,7 @@ public class MultiHangarApiException extends ResponseStatusException {
|
||||
gen.writeBooleanField("isHangarApiException", true);
|
||||
gen.writeArrayFieldStart("exceptions");
|
||||
for (final HangarApiException exception : value.exceptions) {
|
||||
String message = exception.getReason();
|
||||
if (message == null || message.isBlank()) {
|
||||
message = exception.getStatus().getReasonPhrase();
|
||||
}
|
||||
final String message = exception.getReason();
|
||||
gen.writeStartObject();
|
||||
gen.writeStringField("message", message);
|
||||
gen.writeArrayFieldStart("messageArgs");
|
||||
@ -45,8 +42,7 @@ public class MultiHangarApiException extends ResponseStatusException {
|
||||
gen.writeEndArray();
|
||||
gen.writeBooleanField("isHangarApiException", true);
|
||||
gen.writeObjectFieldStart("httpError");
|
||||
gen.writeNumberField("statusCode", exception.getStatus().value());
|
||||
gen.writeStringField("statusPhrase", exception.getStatus().getReasonPhrase());
|
||||
gen.writeNumberField("statusCode", exception.getStatusCode().value());
|
||||
gen.writeEndObject();
|
||||
gen.writeEndObject();
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import io.papermc.hangar.exceptions.MultiHangarApiException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
@ -26,16 +26,16 @@ public class HangarEntityExceptionHandler extends ResponseEntityExceptionHandler
|
||||
|
||||
@ExceptionHandler(HangarApiException.class)
|
||||
public ResponseEntity<HangarApiException> handleException(final HangarApiException exception) {
|
||||
return new ResponseEntity<>(exception, exception.getResponseHeaders(), exception.getRawStatusCode());
|
||||
return new ResponseEntity<>(exception, exception.getHeaders(), exception.getStatusCode().value());
|
||||
}
|
||||
|
||||
@ExceptionHandler(MultiHangarApiException.class)
|
||||
public ResponseEntity<MultiHangarApiException> handleException(final MultiHangarApiException exception) {
|
||||
return new ResponseEntity<>(exception, exception.getResponseHeaders(), exception.getRawStatusCode());
|
||||
return new ResponseEntity<>(exception, exception.getHeaders(), exception.getStatusCode().value());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResponseEntity<Object> handleExceptionInternal(final Exception ex, final Object body, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||
protected ResponseEntity<Object> handleExceptionInternal(final Exception ex, final Object body, final HttpHeaders headers, final HttpStatusCode status, final WebRequest request) {
|
||||
if (this.config.isDev()) {
|
||||
return new ResponseEntity<>(new HangarApiException(ex.getMessage()), status);
|
||||
} else {
|
||||
@ -44,7 +44,7 @@ public class HangarEntityExceptionHandler extends ResponseEntityExceptionHandler
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull ResponseEntity<Object> handleMethodArgumentNotValid(final @NotNull MethodArgumentNotValidException ex, final @NotNull HttpHeaders headers, final @NotNull HttpStatus status, final @NotNull WebRequest request) {
|
||||
protected @NotNull ResponseEntity<Object> handleMethodArgumentNotValid(final @NotNull MethodArgumentNotValidException ex, final @NotNull HttpHeaders headers, final @NotNull HttpStatusCode status, final @NotNull WebRequest request) {
|
||||
return new ResponseEntity<>(ex, headers, status);
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,11 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.papermc.hangar.model.Model;
|
||||
import io.papermc.hangar.model.Named;
|
||||
import io.papermc.hangar.model.common.roles.GlobalRole;
|
||||
import jakarta.annotation.Nullable;
|
||||
import org.jdbi.v3.core.mapper.reflect.JdbiConstructor;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nullable;
|
||||
import org.jdbi.v3.core.mapper.reflect.JdbiConstructor;
|
||||
|
||||
public class User extends Model implements Named {
|
||||
|
||||
|
@ -2,9 +2,9 @@ package io.papermc.hangar.model.api.project;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import io.papermc.hangar.controller.validations.Validate;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Collection;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.jdbi.v3.core.mapper.Nested;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -24,11 +24,14 @@ public class ProjectSettings {
|
||||
|
||||
// @el(root: String)
|
||||
private final @Validate(SpEL = "@validate.regex(#root, @hangarConfig.urlRegex)", message = "validation.invalidUrl") String wiki;
|
||||
private final @Valid ProjectLicense license;
|
||||
private final @Valid ProjectDonationSettings donation;
|
||||
@Valid
|
||||
private final ProjectLicense license;
|
||||
@Valid
|
||||
private final ProjectDonationSettings donation;
|
||||
|
||||
// @el(root: Collection<String>)
|
||||
private final @NotNull @Validate(SpEL = "@validate.max(#root, @hangarConfig.projects.maxKeywords)", message = "project.new.error.tooManyKeywords") Collection<String> keywords;
|
||||
@NotNull
|
||||
private final @Validate(SpEL = "@validate.max(#root, @hangarConfig.projects.maxKeywords)", message = "project.new.error.tooManyKeywords") Collection<String> keywords;
|
||||
private final boolean forumSync;
|
||||
|
||||
// @el(root: String)
|
||||
|
@ -4,8 +4,8 @@ import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import io.papermc.hangar.controller.validations.AtLeastOneNotNull;
|
||||
import io.papermc.hangar.model.Named;
|
||||
import io.papermc.hangar.model.api.project.ProjectNamespace;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.util.Objects;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import org.jdbi.v3.core.mapper.Nested;
|
||||
import org.jdbi.v3.core.mapper.reflect.JdbiConstructor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -13,7 +13,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
@AtLeastOneNotNull(fieldNames = {"namespace", "externalUrl"}, includeBlankStrings = true, message = "Must specify a projectId or external URL for a dependency")
|
||||
public class PluginDependency implements Named {
|
||||
|
||||
private final @NotBlank(message = "Must have a dependency name") String name;
|
||||
@NotBlank(message = "Must have a dependency name")
|
||||
private final String name;
|
||||
private final boolean required;
|
||||
private final ProjectNamespace namespace;
|
||||
private final String externalUrl;
|
||||
|
@ -2,13 +2,15 @@ package io.papermc.hangar.model.api.requests;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import io.papermc.hangar.model.common.projects.FlagReason;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class FlagForm {
|
||||
private final @NotBlank String comment;
|
||||
@NotBlank
|
||||
private final String comment;
|
||||
private final long projectId;
|
||||
private final @NotNull FlagReason reason;
|
||||
@NotNull
|
||||
private final FlagReason reason;
|
||||
|
||||
@JsonCreator
|
||||
public FlagForm(final String comment, final long projectId, final FlagReason reason) {
|
||||
|
@ -2,27 +2,30 @@ package io.papermc.hangar.model.api.requests;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.papermc.hangar.controller.extras.pagination.Filter;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
|
||||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED;
|
||||
|
||||
public class RequestPagination {
|
||||
|
||||
@ApiModelProperty(value = "The maximum amount of items to return", example = "1", allowEmptyValue = true, allowableValues = "range[1, 25]")
|
||||
@Schema(description = "The maximum amount of items to return", example = "1", requiredMode = NOT_REQUIRED, allowableValues = "range[1, 25]")
|
||||
private final long limit;
|
||||
|
||||
@ApiModelProperty(value = "Where to start searching", example = "0", allowEmptyValue = true, allowableValues = "range[0, infinity]")
|
||||
@Schema(description = "Where to start searching", example = "0", requiredMode = NOT_REQUIRED, allowableValues = "range[0, infinity]")
|
||||
private final long offset;
|
||||
|
||||
@JsonIgnore
|
||||
@ApiModelProperty(hidden = true)
|
||||
@Schema(accessMode = READ_ONLY)
|
||||
private final List<Filter.FilterInstance> filters;
|
||||
|
||||
@JsonIgnore
|
||||
@ApiModelProperty(hidden = true)
|
||||
@Schema(accessMode = READ_ONLY)
|
||||
private final Map<String, Consumer<StringBuilder>> sorters;
|
||||
|
||||
/**
|
||||
|
@ -1,17 +1,17 @@
|
||||
package io.papermc.hangar.model.internal.api.requests;
|
||||
|
||||
import io.papermc.hangar.model.common.NamedPermission;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class CreateAPIKeyForm {
|
||||
|
||||
@ApiModelProperty(allowableValues = "range[5,256)", required = true)
|
||||
@Schema(allowableValues = "range[5,256)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private final @NotBlank @Size(min = 5, max = 255) String name;
|
||||
|
||||
@ApiModelProperty(required = true)
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private final @Size(min = 1) List<NamedPermission> permissions;
|
||||
|
||||
public CreateAPIKeyForm(final String name, final List<NamedPermission> permissions) {
|
||||
|
@ -5,9 +5,9 @@ import io.papermc.hangar.controller.validations.Validate;
|
||||
import io.papermc.hangar.exceptions.HangarApiException;
|
||||
import io.papermc.hangar.model.common.roles.Role;
|
||||
import io.papermc.hangar.model.db.roles.ExtendedRoleTable;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class EditMembersForm<R extends Role<? extends ExtendedRoleTable<R, ?>>> {
|
||||
@ -32,7 +32,8 @@ public class EditMembersForm<R extends Role<? extends ExtendedRoleTable<R, ?>>>
|
||||
|
||||
public static class Member<R extends Role<? extends ExtendedRoleTable<R, ?>>> {
|
||||
|
||||
private final @NotBlank String name;
|
||||
@NotBlank
|
||||
private final String name;
|
||||
|
||||
// @el(root: Role)
|
||||
private final @Validate(SpEL = "#root.assignable") R role;
|
||||
|
@ -1,13 +1,14 @@
|
||||
package io.papermc.hangar.model.internal.api.requests;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* Request body for simple strings
|
||||
*/
|
||||
public class StringContent {
|
||||
|
||||
private @NotBlank String content;
|
||||
@NotBlank
|
||||
private String content;
|
||||
|
||||
public String getContent() {
|
||||
return this.content;
|
||||
|
@ -1,10 +1,10 @@
|
||||
package io.papermc.hangar.model.internal.api.requests.admin;
|
||||
|
||||
import io.papermc.hangar.model.common.Platform;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class ChangePlatformVersionsForm extends EnumMap<Platform, @Size(min = 1) List<@NotBlank String>> {
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package io.papermc.hangar.model.internal.api.requests.admin;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import jakarta.validation.Valid;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public record ReportNotificationForm(boolean warning, boolean toReporter, @Valid @NotNull String content) {
|
||||
|
@ -4,15 +4,17 @@ import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import io.papermc.hangar.controller.validations.Validate;
|
||||
import io.papermc.hangar.model.common.ChannelFlag;
|
||||
import io.papermc.hangar.model.common.Color;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Set;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class ChannelForm {
|
||||
|
||||
// @el(root: String)
|
||||
private final @NotBlank @Validate(SpEL = "@validations.regex(#root, @hangarConfig.channels.nameRegex)", message = "channel.modal.error.invalidName") @Validate(SpEL = "@validations.max(#root, @hangarConfig.channels.maxNameLen)", message = "channel.modal.error.tooLongName") String name;
|
||||
private final @NotNull Color color;
|
||||
@NotBlank
|
||||
private final @Validate(SpEL = "@validations.regex(#root, @hangarConfig.channels.nameRegex)", message = "channel.modal.error.invalidName") @Validate(SpEL = "@validations.max(#root, @hangarConfig.channels.maxNameLen)", message = "channel.modal.error.tooLongName") String name;
|
||||
@NotNull
|
||||
private final Color color;
|
||||
private final Set<ChannelFlag> flags;
|
||||
|
||||
@JsonCreator
|
||||
|
@ -3,14 +3,15 @@ package io.papermc.hangar.model.internal.api.requests.projects;
|
||||
import io.papermc.hangar.controller.validations.Validate;
|
||||
import io.papermc.hangar.model.api.project.ProjectSettings;
|
||||
import io.papermc.hangar.model.common.projects.Category;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class NewProjectForm extends ProjectSettingsForm {
|
||||
|
||||
private final long ownerId;
|
||||
|
||||
// @el(root: String)
|
||||
private final @NotNull(message = "project.new.error.invalidName") @Validate(SpEL = "@validate.max(#root, @hangarConfig.projects.maxNameLen)", message = "project.new.error.tooLongName") @Validate(SpEL = "@validate.regex(#root, @hangarConfig.projects.nameRegex)", message = "project.new.error.invalidName") String name;
|
||||
@NotNull(message = "project.new.error.invalidName")
|
||||
private final @Validate(SpEL = "@validate.max(#root, @hangarConfig.projects.maxNameLen)", message = "project.new.error.tooLongName") @Validate(SpEL = "@validate.regex(#root, @hangarConfig.projects.nameRegex)", message = "project.new.error.invalidName") String name;
|
||||
|
||||
// @el(root: String)
|
||||
private final @Validate(SpEL = "@validate.max(#root, @hangarConfig.pages.maxLen)", message = "page.new.error.maxLength") String pageContent;
|
||||
|
@ -2,16 +2,17 @@ package io.papermc.hangar.model.internal.api.requests.projects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import io.papermc.hangar.controller.validations.Validate;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class NewProjectPage {
|
||||
|
||||
// @el(root: String)
|
||||
private final @NotBlank @Validate(SpEL = "@validate.min(#root, @hangarConfig.pages.minNameLen)", message = "page.new.error.name.minLength") @Validate(SpEL = "@validate.max(#root, @hangarConfig.pages.maxNameLen)", message = "page.new.error.name.maxLength") @Validate(SpEL = "@validate.regex(#root, @hangarConfig.pages.nameRegex)", message = "page.new.error.name.invalidChars") String name;
|
||||
@NotBlank
|
||||
private final @Validate(SpEL = "@validate.min(#root, @hangarConfig.pages.minNameLen)", message = "page.new.error.name.minLength") @Validate(SpEL = "@validate.max(#root, @hangarConfig.pages.maxNameLen)", message = "page.new.error.name.maxLength") @Validate(SpEL = "@validate.regex(#root, @hangarConfig.pages.nameRegex)", message = "page.new.error.name.invalidChars") String name;
|
||||
private final Long parentId;
|
||||
|
||||
@JsonCreator
|
||||
public NewProjectPage(final @NotBlank String name, final Long parentId) {
|
||||
public NewProjectPage(@NotBlank final String name, final Long parentId) {
|
||||
this.name = name;
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
@ -4,16 +4,19 @@ import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import io.papermc.hangar.controller.validations.Validate;
|
||||
import io.papermc.hangar.model.api.project.ProjectSettings;
|
||||
import io.papermc.hangar.model.common.projects.Category;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class ProjectSettingsForm {
|
||||
|
||||
private final @Valid ProjectSettings settings;
|
||||
private final @NotNull(message = "project.new.error.noCategory") Category category;
|
||||
@Valid
|
||||
private final ProjectSettings settings;
|
||||
@NotNull(message = "project.new.error.noCategory")
|
||||
private final Category category;
|
||||
|
||||
// @el(root: String)
|
||||
private final @NotNull(message = "project.new.error.noDescription") @Validate(SpEL = "@validate.max(#root, @hangarConfig.projects.maxDescLen)", message = "project.new.error.tooLongDesc") String description;
|
||||
@NotNull(message = "project.new.error.noDescription")
|
||||
private final @Validate(SpEL = "@validate.max(#root, @hangarConfig.projects.maxDescLen)", message = "project.new.error.tooLongDesc") String description;
|
||||
|
||||
@JsonCreator
|
||||
public ProjectSettingsForm(final ProjectSettings settings, final Category category, final String description) {
|
||||
|
@ -2,11 +2,12 @@ package io.papermc.hangar.model.internal.api.requests.projects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import io.papermc.hangar.model.common.projects.Visibility;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class VisibilityChangeForm {
|
||||
|
||||
private final @NotNull Visibility visibility;
|
||||
@NotNull
|
||||
private final Visibility visibility;
|
||||
private final String comment;
|
||||
|
||||
@JsonCreator
|
||||
|
@ -2,12 +2,14 @@ package io.papermc.hangar.model.internal.api.requests.versions;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class ReviewMessage {
|
||||
|
||||
private final @NotNull String message;
|
||||
private final @NotNull ObjectNode args;
|
||||
@NotNull
|
||||
private final String message;
|
||||
@NotNull
|
||||
private final ObjectNode args;
|
||||
|
||||
@JsonCreator
|
||||
public ReviewMessage(final String message, final ObjectNode args) {
|
||||
|
@ -2,15 +2,17 @@ package io.papermc.hangar.model.internal.api.requests.versions;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import io.papermc.hangar.model.common.Platform;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.Set;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class UpdatePlatformVersions {
|
||||
|
||||
private final @NotNull Platform platform;
|
||||
private final @Size(min = 1, message = "version.edit.error.noPlatformVersions") Set<@NotBlank(message = "version.new.error.invalidPlatformVersion") String> versions;
|
||||
@NotNull
|
||||
private final Platform platform;
|
||||
@Size(min = 1, message = "version.edit.error.noPlatformVersions")
|
||||
private final Set<@NotBlank(message = "version.new.error.invalidPlatformVersion") String> versions;
|
||||
|
||||
@JsonCreator
|
||||
public UpdatePlatformVersions(final Platform platform, final Set<String> versions) {
|
||||
|
@ -3,20 +3,21 @@ package io.papermc.hangar.model.internal.api.requests.versions;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import io.papermc.hangar.model.api.project.version.PluginDependency;
|
||||
import io.papermc.hangar.model.common.Platform;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class UpdatePluginDependencies {
|
||||
|
||||
private final @NotNull Platform platform;
|
||||
@NotNull
|
||||
private final Platform platform;
|
||||
private final Map<String, @Valid PluginDependency> pluginDependencies;
|
||||
|
||||
@JsonCreator
|
||||
public UpdatePluginDependencies(final @NotNull Platform platform, final Set<@Valid PluginDependency> pluginDependencies) {
|
||||
public UpdatePluginDependencies(@NotNull final Platform platform, final Set<@Valid PluginDependency> pluginDependencies) {
|
||||
this.platform = platform;
|
||||
this.pluginDependencies = pluginDependencies.stream().collect(Collectors.toMap(PluginDependency::getName, Function.identity()));
|
||||
}
|
||||
|
@ -2,20 +2,20 @@ package io.papermc.hangar.model.internal.discourse;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
|
||||
public class DiscourseError extends RuntimeException {
|
||||
|
||||
public static class StatusError extends DiscourseError {
|
||||
private final HttpStatus status;
|
||||
private final HttpStatusCode status;
|
||||
private final String message;
|
||||
|
||||
public StatusError(final HttpStatus status, final String message) {
|
||||
public StatusError(final HttpStatusCode status, final String message) {
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public HttpStatus getStatus() {
|
||||
public HttpStatusCode getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,9 @@ import io.papermc.hangar.model.api.User;
|
||||
import io.papermc.hangar.model.api.UserNameChange;
|
||||
import io.papermc.hangar.model.common.Permission;
|
||||
import io.papermc.hangar.model.common.roles.GlobalRole;
|
||||
import jakarta.annotation.Nullable;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class HangarUser extends User implements Identified {
|
||||
|
||||
|
@ -2,8 +2,8 @@ package io.papermc.hangar.model.internal.versions;
|
||||
|
||||
import io.papermc.hangar.controller.validations.Validate;
|
||||
import io.papermc.hangar.model.common.Platform;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.Size;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
// @el(root: String) // can't figure out how to apply this to just the one record component
|
||||
|
@ -6,31 +6,37 @@ import io.papermc.hangar.model.api.project.version.PluginDependency;
|
||||
import io.papermc.hangar.model.common.ChannelFlag;
|
||||
import io.papermc.hangar.model.common.Color;
|
||||
import io.papermc.hangar.model.common.Platform;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PendingVersion {
|
||||
|
||||
// @el(root: String)
|
||||
private final @NotBlank(message = "version.new.error.invalidVersionString") @Validate(SpEL = "@validate.regex(#root, @hangarConfig.projects.versionNameRegex)", message = "version.new.error.invalidVersionString") String versionString;
|
||||
@NotBlank(message = "version.new.error.invalidVersionString")
|
||||
private final @Validate(SpEL = "@validate.regex(#root, @hangarConfig.projects.versionNameRegex)", message = "version.new.error.invalidVersionString") String versionString;
|
||||
private final Map<Platform, Set<@Valid PluginDependency>> pluginDependencies;
|
||||
private final @Size(min = 1, max = 3, message = "version.new.error.invalidNumOfPlatforms") Map<Platform, @Size(min = 1, message = "version.edit.error.noPlatformVersions") SortedSet<@NotBlank(message = "version.new.error.invalidPlatformVersion") String>> platformDependencies;
|
||||
@Size(min = 1, max = 3, message = "version.new.error.invalidNumOfPlatforms")
|
||||
private final Map<Platform, @Size(min = 1, message = "version.edit.error.noPlatformVersions") SortedSet<@NotBlank(message = "version.new.error.invalidPlatformVersion") String>> platformDependencies;
|
||||
|
||||
// @el(root: String)
|
||||
private final @NotBlank(message = "version.new.error.noDescription") @Validate(SpEL = "@validate.max(#root, @hangarConfig.pages.maxLen)", message = "page.new.error.maxLength") String description;
|
||||
private final @Size(min = 1, max = 3, message = "version.new.error.invalidNumOfPlatforms") List<@Valid PendingVersionFile> files;
|
||||
@NotBlank(message = "version.new.error.noDescription")
|
||||
private final @Validate(SpEL = "@validate.max(#root, @hangarConfig.pages.maxLen)", message = "page.new.error.maxLength") String description;
|
||||
@Size(min = 1, max = 3, message = "version.new.error.invalidNumOfPlatforms")
|
||||
private final List<@Valid PendingVersionFile> files;
|
||||
|
||||
// @el(root: String)
|
||||
private final @NotBlank(message = "version.new.error.channel.noName") @Validate(SpEL = "@validate.regex(#root, @hangarConfig.channels.nameRegex)", message = "channel.modal.error.invalidName") String channelName;
|
||||
private final @NotNull(message = "version.new.error.channel.noColor") Color channelColor;
|
||||
@NotBlank(message = "version.new.error.channel.noName")
|
||||
private final @Validate(SpEL = "@validate.regex(#root, @hangarConfig.channels.nameRegex)", message = "channel.modal.error.invalidName") String channelName;
|
||||
@NotNull(message = "version.new.error.channel.noColor")
|
||||
private final Color channelColor;
|
||||
private final Set<ChannelFlag> channelFlags;
|
||||
private final boolean forumSync;
|
||||
|
||||
|
@ -3,8 +3,8 @@ package io.papermc.hangar.model.internal.versions;
|
||||
import io.papermc.hangar.controller.validations.Validate;
|
||||
import io.papermc.hangar.model.api.project.version.FileInfo;
|
||||
import io.papermc.hangar.model.common.Platform;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.Size;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
// @el(root: String) // can't figure out how to apply this to just the field
|
||||
|
@ -3,9 +3,9 @@ package io.papermc.hangar.security.annotations.ratelimit;
|
||||
import io.github.bucket4j.Bucket;
|
||||
import io.papermc.hangar.exceptions.HangarApiException;
|
||||
import io.papermc.hangar.service.internal.BucketService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.lang.reflect.Method;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -2,9 +2,9 @@ package io.papermc.hangar.security.authentication;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.papermc.hangar.exceptions.HangarApiException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.pdfbox.util.Charsets;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
@ -4,15 +4,11 @@ import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||
import com.auth0.jwt.exceptions.TokenExpiredException;
|
||||
import io.papermc.hangar.security.configs.SecurityConfig;
|
||||
import io.papermc.hangar.service.TokenService;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@ -27,6 +23,10 @@ import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class HangarAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
|
||||
|
||||
|
@ -15,6 +15,7 @@ import io.papermc.hangar.model.internal.job.UpdateDiscourseVersionPostJob;
|
||||
import io.papermc.hangar.service.internal.discourse.DiscourseService;
|
||||
import io.papermc.hangar.service.internal.projects.ProjectService;
|
||||
import io.papermc.hangar.service.internal.versions.VersionService;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.time.Duration;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
@ -22,7 +23,6 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.annotation.PostConstruct;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
@ -10,12 +10,12 @@ import io.papermc.hangar.model.identified.ProjectIdentified;
|
||||
import io.papermc.hangar.model.identified.VersionIdentified;
|
||||
import io.papermc.hangar.model.internal.admin.DayStats;
|
||||
import io.papermc.hangar.util.RequestUtil;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import java.net.InetAddress;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import javax.servlet.http.Cookie;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
|
@ -16,6 +16,7 @@ import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
@ -96,8 +97,8 @@ public class DiscourseApi {
|
||||
}
|
||||
}
|
||||
|
||||
private DiscourseError createFromStatus(final HttpStatus status, final String message) {
|
||||
if (status.equals(HttpStatus.TOO_MANY_REQUESTS)) {
|
||||
private DiscourseError createFromStatus(final HttpStatusCode status, final String message) {
|
||||
if (status.value() == HttpStatus.TOO_MANY_REQUESTS.value()) {
|
||||
if (message != null) {
|
||||
try {
|
||||
final JSONObject json = new JSONObject(message);
|
||||
|
@ -22,6 +22,7 @@ import io.papermc.hangar.service.internal.admin.StatService;
|
||||
import io.papermc.hangar.service.internal.file.FileService;
|
||||
import io.papermc.hangar.service.internal.uploads.ProjectFiles;
|
||||
import io.papermc.hangar.util.RequestUtil;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import java.net.InetAddress;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
@ -30,7 +31,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import javax.servlet.http.Cookie;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
@ -19,10 +19,10 @@ import io.papermc.hangar.model.internal.versions.HangarReview;
|
||||
import io.papermc.hangar.model.internal.versions.HangarReviewQueueEntry;
|
||||
import io.papermc.hangar.service.internal.users.NotificationService;
|
||||
import io.papermc.hangar.service.internal.visibility.ProjectVersionVisibilityService;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -136,7 +136,8 @@ public class ReviewService extends HangarComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private @NotNull ProjectVersionReviewTable getLatestUnfinishedReviewAndValidate(final long versionId) {
|
||||
@NotNull
|
||||
private ProjectVersionReviewTable getLatestUnfinishedReviewAndValidate(final long versionId) {
|
||||
final ProjectVersionReviewTable latestUnfinishedReview = this.projectVersionReviewsDAO.getLatestUnfinishedReview(versionId, this.getHangarPrincipal().getUserId());
|
||||
if (latestUnfinishedReview == null) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "reviews.error.noReviewStarted");
|
||||
|
@ -1,11 +1,11 @@
|
||||
package io.papermc.hangar.util;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
public class RequestUtil {
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
package io.papermc.hangar.util;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.annotation.PostConstruct;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cglib.proxy.InvocationHandler;
|
||||
import org.springframework.cglib.proxy.Proxy;
|
||||
|
@ -1,11 +1,11 @@
|
||||
package io.papermc.hangar.util;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public final class StringUtils {
|
||||
|
||||
@ -126,7 +126,7 @@ public final class StringUtils {
|
||||
return input.equals("None") ? null : input;
|
||||
}
|
||||
|
||||
public static boolean isAnyEqualIgnoreCase(final @NotNull String lhs, final String @NotNull ... rhs) {
|
||||
public static boolean isAnyEqualIgnoreCase(@NotNull final String lhs, final String @NotNull ... rhs) {
|
||||
|
||||
for (final String string : rhs) {
|
||||
if (lhs.equalsIgnoreCase(string)) {
|
||||
|
@ -95,7 +95,7 @@ export default defineNuxtConfig({
|
||||
"/handle-logout": defineProxyBackend(),
|
||||
"/refresh": defineProxyBackend(),
|
||||
"/invalidate": defineProxyBackend(),
|
||||
"/v2/api-docs/": defineProxyBackend(),
|
||||
"/v3/api-docs": defineProxyBackend(),
|
||||
"/robots.txt": defineProxyBackend(),
|
||||
"/sitemap.xml": defineProxyBackend(),
|
||||
"/global-sitemap.xml": defineProxyBackend(),
|
||||
|
@ -11,7 +11,7 @@ const route = useRoute();
|
||||
const authStore = useAuthStore();
|
||||
|
||||
onMounted(() => {
|
||||
const swaggerUiVersion = "4.13.0";
|
||||
const swaggerUiVersion = "4.15.5";
|
||||
|
||||
const bundle = document.createElement("script");
|
||||
bundle.src = `https://unpkg.com/swagger-ui-dist@${swaggerUiVersion}/swagger-ui-bundle.js`;
|
||||
@ -26,22 +26,22 @@ onMounted(() => {
|
||||
const script = document.createElement("script");
|
||||
// language=JavaScript
|
||||
script.innerHTML = `
|
||||
window.ui = SwaggerUIBundle({
|
||||
url: "/v2/api-docs/",
|
||||
dom_id: "#swagger-ui",
|
||||
deepLinking: true,
|
||||
presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset],
|
||||
plugins: [SwaggerUIBundle.plugins.DownloadUrl],
|
||||
layout: "BaseLayout",
|
||||
requestInterceptor: (req) => {
|
||||
if (!req.loadSpec) {
|
||||
if (req.url.startsWith("http://localhost:8080")) {
|
||||
req.url = req.url.replace("http://localhost:8080", "http://localhost:3333");
|
||||
window.ui = SwaggerUIBundle({
|
||||
url: "/v3/api-docs.yaml",
|
||||
dom_id: "#swagger-ui",
|
||||
deepLinking: true,
|
||||
presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset],
|
||||
plugins: [SwaggerUIBundle.plugins.DownloadUrl],
|
||||
layout: "BaseLayout",
|
||||
requestInterceptor: (req) => {
|
||||
if (!req.loadSpec) {
|
||||
if (req.url.startsWith("http://localhost:8080")) {
|
||||
req.url = req.url.replace("http://localhost:8080", "http://localhost:3333");
|
||||
}
|
||||
}
|
||||
return req;
|
||||
}
|
||||
return req;
|
||||
}
|
||||
});
|
||||
});
|
||||
`;
|
||||
|
||||
bundle.onload = () => document.body.append(script);
|
||||
@ -51,7 +51,7 @@ useHead(useSeo(i18n.t("apiDocs.title"), "API Docs for the Hangar REST API", rout
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-gray-100 dark:(bg-gray-200) rounded-md my-auto mx-2 py-1" lg="w-2/3 min-w-2/3 max-w-2/3">
|
||||
<div class="bg-gray-100 dark:(bg-gray-200) rounded-md my-auto mx-2 py-1">
|
||||
<div id="swagger-ui">
|
||||
<h1 class="text-3xl text-bold">Hangar API</h1>
|
||||
<h2 class="text-2xl">API Docs for the Hangar REST API</h2>
|
||||
@ -68,27 +68,33 @@ useHead(useSeo(i18n.t("apiDocs.title"), "API Docs for the Hangar REST API", rout
|
||||
.info hgroup.main a {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wrapper .info {
|
||||
background-color: unset !important;
|
||||
border-color: unset !important;
|
||||
margin: 2rem 0;
|
||||
|
||||
.title small pre {
|
||||
background-color: unset;
|
||||
border: unset;
|
||||
}
|
||||
|
||||
.description h2 {
|
||||
padding-top: 1.5rem;
|
||||
margin: 1.5rem 0 0;
|
||||
border-top: 3px solid #333333;
|
||||
}
|
||||
|
||||
.scheme-container {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.markdown {
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.model-container,
|
||||
.responses-inner {
|
||||
overflow-x: auto;
|
||||
|
Loading…
x
Reference in New Issue
Block a user