mirror of
https://github.com/HangarMC/Hangar.git
synced 2025-03-13 15:39:18 +08:00
convert almost all custom config objects to records
This commit is contained in:
parent
03352e47cb
commit
1ec34b350b
@ -1,49 +1,19 @@
|
||||
package io.papermc.hangar.config.hangar;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "hangar.api")
|
||||
public class ApiConfig {
|
||||
public record ApiConfig(@NestedConfigurationProperty Session session) { // TODO is this used anywhere now?
|
||||
|
||||
@NestedConfigurationProperty
|
||||
public Session session;
|
||||
|
||||
@Autowired
|
||||
public ApiConfig(Session session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "hangar.api.session")
|
||||
public static class Session {
|
||||
@DurationUnit(ChronoUnit.HOURS)
|
||||
private Duration publicExpiration = Duration.ofHours(3);
|
||||
@DurationUnit(ChronoUnit.DAYS)
|
||||
private Duration expiration = Duration.ofDays(14);
|
||||
|
||||
|
||||
public Duration getPublicExpiration() {
|
||||
return publicExpiration;
|
||||
}
|
||||
|
||||
public void setPublicExpiration(Duration publicExpiration) {
|
||||
this.publicExpiration = publicExpiration;
|
||||
}
|
||||
|
||||
public Duration getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public void setExpiration(Duration expiration) {
|
||||
this.expiration = expiration;
|
||||
}
|
||||
public record Session( // TODO is this used anywhere now?
|
||||
@DurationUnit(ChronoUnit.HOURS) @DefaultValue("3") Duration publicExpiration,
|
||||
@DurationUnit(ChronoUnit.DAYS) @DefaultValue("14") Duration expiration
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +1,18 @@
|
||||
package io.papermc.hangar.config.hangar;
|
||||
|
||||
import io.papermc.hangar.model.common.Color;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.Size;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "hangar.channels")
|
||||
public class ChannelsConfig {
|
||||
@Size(min = 1)
|
||||
private int maxNameLen = 15;
|
||||
private String nameRegex = "^[a-zA-Z0-9]+$";
|
||||
private Color colorDefault = Color.CYAN;
|
||||
@Size(min = 1, max = 15)
|
||||
private String nameDefault = "Release";
|
||||
|
||||
public int getMaxNameLen() {
|
||||
return maxNameLen;
|
||||
}
|
||||
|
||||
public void setMaxNameLen(int maxNameLen) {
|
||||
this.maxNameLen = maxNameLen;
|
||||
}
|
||||
|
||||
public String getNameRegex() {
|
||||
return nameRegex;
|
||||
}
|
||||
|
||||
public void setNameRegex(String nameRegex) {
|
||||
this.nameRegex = nameRegex;
|
||||
}
|
||||
|
||||
public Color getColorDefault() {
|
||||
return colorDefault;
|
||||
}
|
||||
|
||||
public void setColorDefault(Color colorDefault) {
|
||||
this.colorDefault = colorDefault;
|
||||
}
|
||||
|
||||
public String getNameDefault() {
|
||||
return nameDefault;
|
||||
}
|
||||
|
||||
public void setNameDefault(String nameDefault) {
|
||||
this.nameDefault = nameDefault;
|
||||
}
|
||||
public record ChannelsConfig(
|
||||
@Min(1) @DefaultValue("15") int maxNameLen,
|
||||
@DefaultValue("^[a-zA-Z0-9]+$") String nameRegex,
|
||||
@DefaultValue("cyan") Color colorDefault,
|
||||
@Size(min = 1, max = 15) @DefaultValue("Release") String nameDefault
|
||||
) {
|
||||
|
||||
public boolean isValidChannelName(String name) {
|
||||
return name.length() >= 1 && name.length() <= maxNameLen && name.matches(nameRegex);
|
||||
|
@ -1,64 +1,15 @@
|
||||
package io.papermc.hangar.config.hangar;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "hangar.discourse")
|
||||
public class DiscourseConfig {
|
||||
|
||||
private boolean enabled = false;
|
||||
private String url = "https://papermc.io/forums/";
|
||||
private String adminUser;
|
||||
private String apiKey;
|
||||
private int category;
|
||||
private int categoryDeleted;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getAdminUser() {
|
||||
return adminUser;
|
||||
}
|
||||
|
||||
public void setAdminUser(String adminUser) {
|
||||
this.adminUser = adminUser;
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public int getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(int category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public int getCategoryDeleted() {
|
||||
return categoryDeleted;
|
||||
}
|
||||
|
||||
public void setCategoryDeleted(int categoryDeleted) {
|
||||
this.categoryDeleted = categoryDeleted;
|
||||
}
|
||||
public record DiscourseConfig(
|
||||
@DefaultValue("false") boolean enabled,
|
||||
@DefaultValue("https://papermc.io/forums/") String url,
|
||||
String adminUser,
|
||||
String apiKey,
|
||||
int category,
|
||||
int categoryDeleted
|
||||
) {
|
||||
}
|
||||
|
@ -1,37 +1,12 @@
|
||||
package io.papermc.hangar.config.hangar;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "fake-user")
|
||||
public class FakeUserConfig {
|
||||
|
||||
private boolean enabled = true;
|
||||
private String username = "paper";
|
||||
private String email = "paper@papermc.io";
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
public record FakeUserConfig(
|
||||
@DefaultValue("true") boolean enabled,
|
||||
@DefaultValue("paper") String username,
|
||||
@DefaultValue("paper@papermc.io") String email
|
||||
) {
|
||||
}
|
||||
|
@ -8,138 +8,32 @@ import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "hangar.security")
|
||||
public class HangarSecurityConfig {
|
||||
|
||||
private boolean secure = false;
|
||||
private long unsafeDownloadMaxAge = 600000;
|
||||
private List<String> safeDownloadHosts = List.of();
|
||||
private String imageProxyUrl = "http://localhost:3001/image/%s";
|
||||
private String tokenIssuer;
|
||||
private String tokenSecret;
|
||||
@DurationUnit(ChronoUnit.SECONDS)
|
||||
private Duration tokenExpiry;
|
||||
@DurationUnit(ChronoUnit.DAYS)
|
||||
private Duration refreshTokenExpiry;
|
||||
@NestedConfigurationProperty
|
||||
public SecurityApiConfig api;
|
||||
|
||||
@Autowired
|
||||
public HangarSecurityConfig(SecurityApiConfig api) {
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
@Component
|
||||
public record HangarSecurityConfig(
|
||||
@DefaultValue("true") boolean secure,
|
||||
@DefaultValue("600000") long unsafeDownloadMaxAge, // TODO implement or remove
|
||||
@DefaultValue List<String> safeDownloadHosts,
|
||||
@DefaultValue("http://localhost:3001/image/%s") String imageProxyUrl,
|
||||
String tokenIssuer,
|
||||
String tokenSecret,
|
||||
@DurationUnit(ChronoUnit.SECONDS) Duration tokenExpiry,
|
||||
@DurationUnit(ChronoUnit.DAYS) Duration refreshTokenExpiry,
|
||||
@NestedConfigurationProperty SecurityApiConfig api
|
||||
) {
|
||||
@ConfigurationProperties(prefix = "hangar.security.api")
|
||||
public static class SecurityApiConfig {
|
||||
|
||||
private String url = "http://localhost:8081";
|
||||
private String avatarUrl = "http://localhost:8081/avatar/%s";
|
||||
private long timeout = 10000;
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
public void setAvatarUrl(String avatarUrl) {
|
||||
this.avatarUrl = avatarUrl;
|
||||
}
|
||||
|
||||
public long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
public record SecurityApiConfig(
|
||||
@DefaultValue("http://localhost:8081") String url,
|
||||
@DefaultValue("http://localhost:8081/avatar/%s") String avatarUrl,
|
||||
@DefaultValue("10000") long timeout // TODO implement or remove
|
||||
) {
|
||||
}
|
||||
|
||||
public Duration getTokenExpiry() {
|
||||
return tokenExpiry;
|
||||
}
|
||||
|
||||
public void setTokenExpiry(Duration tokenExpiry) {
|
||||
this.tokenExpiry = tokenExpiry;
|
||||
}
|
||||
|
||||
public Duration getRefreshTokenExpiry() {
|
||||
return refreshTokenExpiry;
|
||||
}
|
||||
|
||||
public void setRefreshTokenExpiry(Duration refreshTokenExpiry) {
|
||||
this.refreshTokenExpiry = refreshTokenExpiry;
|
||||
}
|
||||
|
||||
public String getTokenIssuer() {
|
||||
return tokenIssuer;
|
||||
}
|
||||
|
||||
public void setTokenIssuer(String tokenIssuer) {
|
||||
this.tokenIssuer = tokenIssuer;
|
||||
}
|
||||
|
||||
public String getTokenSecret() {
|
||||
return tokenSecret;
|
||||
}
|
||||
|
||||
public void setTokenSecret(String tokenSecret) {
|
||||
this.tokenSecret = tokenSecret;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
return secure;
|
||||
}
|
||||
|
||||
public void setSecure(boolean secure) {
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
public long getUnsafeDownloadMaxAge() {
|
||||
return unsafeDownloadMaxAge;
|
||||
}
|
||||
|
||||
public void setUnsafeDownloadMaxAge(long unsafeDownloadMaxAge) {
|
||||
this.unsafeDownloadMaxAge = unsafeDownloadMaxAge;
|
||||
}
|
||||
|
||||
public List<String> getSafeDownloadHosts() {
|
||||
return safeDownloadHosts;
|
||||
}
|
||||
|
||||
public void setSafeDownloadHosts(List<String> safeDownloadHosts) {
|
||||
this.safeDownloadHosts = safeDownloadHosts;
|
||||
}
|
||||
|
||||
public String getImageProxyUrl() {
|
||||
return imageProxyUrl;
|
||||
}
|
||||
|
||||
public void setImageProxyUrl(String imageProxyUrl) {
|
||||
this.imageProxyUrl = imageProxyUrl;
|
||||
}
|
||||
|
||||
public SecurityApiConfig getApi() {
|
||||
return api;
|
||||
}
|
||||
|
||||
public void setApi(SecurityApiConfig api) {
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
public boolean checkSafe(String url) {
|
||||
return safeDownloadHosts.contains(URI.create(url).getHost());
|
||||
public boolean checkSafe(final String url) {
|
||||
return this.safeDownloadHosts.contains(URI.create(url).getHost());
|
||||
}
|
||||
|
||||
public boolean isSafeHost(final String host) {
|
||||
@ -160,25 +54,25 @@ public class HangarSecurityConfig {
|
||||
if (uri.getScheme().equals("mailto")) {
|
||||
return true;
|
||||
}
|
||||
} else if (host == null || isSafeHost(host)) {
|
||||
} else if (host == null || this.isSafeHost(host)) {
|
||||
return true;
|
||||
}
|
||||
} catch (URISyntaxException ignored) {
|
||||
} catch (final URISyntaxException ignored) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String makeSafe(final String urlString) {
|
||||
if (isSafe(urlString)) {
|
||||
if (this.isSafe(urlString)) {
|
||||
return urlString;
|
||||
}
|
||||
return "/linkout?remoteUrl=" + urlString;
|
||||
}
|
||||
|
||||
public String proxyImage(String urlString) {
|
||||
if (isSafe(urlString)) {
|
||||
public String proxyImage(final String urlString) {
|
||||
if (this.isSafe(urlString)) {
|
||||
return urlString;
|
||||
}
|
||||
return String.format(imageProxyUrl, urlString);
|
||||
return String.format(this.imageProxyUrl(), urlString);
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,11 @@
|
||||
package io.papermc.hangar.config.hangar;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "hangar.homepage")
|
||||
public class HomepageConfig {
|
||||
|
||||
@DurationUnit(ChronoUnit.MINUTES)
|
||||
private Duration updateInterval = Duration.ofMinutes(10);
|
||||
|
||||
public Duration getUpdateInterval() {
|
||||
return updateInterval;
|
||||
}
|
||||
|
||||
public void setUpdateInterval(Duration updateInterval) {
|
||||
this.updateInterval = updateInterval;
|
||||
}
|
||||
public record HomepageConfig(@DurationUnit(ChronoUnit.MINUTES) @DefaultValue("10") Duration updateInterval) {
|
||||
}
|
||||
|
@ -1,67 +1,17 @@
|
||||
package io.papermc.hangar.config.hangar;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "hangar.jobs")
|
||||
public class JobsConfig {
|
||||
|
||||
@DurationUnit(ChronoUnit.MINUTES)
|
||||
private Duration checkInterval = Duration.ofMinutes(1);
|
||||
|
||||
@DurationUnit(ChronoUnit.MINUTES)
|
||||
private Duration unknownErrorTimeout = Duration.ofMinutes(15);
|
||||
|
||||
@DurationUnit(ChronoUnit.MINUTES)
|
||||
private Duration statusErrorTimeout = Duration.ofMinutes(5);
|
||||
|
||||
@DurationUnit(ChronoUnit.MINUTES)
|
||||
private Duration notAvailableTimeout = Duration.ofMinutes(2);
|
||||
|
||||
private int maxConcurrentJobs = 32;
|
||||
|
||||
public Duration getCheckInterval() {
|
||||
return checkInterval;
|
||||
}
|
||||
|
||||
public void setCheckInterval(Duration checkInterval) {
|
||||
this.checkInterval = checkInterval;
|
||||
}
|
||||
|
||||
public Duration getUnknownErrorTimeout() {
|
||||
return unknownErrorTimeout;
|
||||
}
|
||||
|
||||
public void setUnknownErrorTimeout(Duration unknownErrorTimeout) {
|
||||
this.unknownErrorTimeout = unknownErrorTimeout;
|
||||
}
|
||||
|
||||
public Duration getStatusErrorTimeout() {
|
||||
return statusErrorTimeout;
|
||||
}
|
||||
|
||||
public void setStatusErrorTimeout(Duration statusErrorTimeout) {
|
||||
this.statusErrorTimeout = statusErrorTimeout;
|
||||
}
|
||||
|
||||
public Duration getNotAvailableTimeout() {
|
||||
return notAvailableTimeout;
|
||||
}
|
||||
|
||||
public void setNotAvailableTimeout(Duration notAvailableTimeout) {
|
||||
this.notAvailableTimeout = notAvailableTimeout;
|
||||
}
|
||||
|
||||
public int getMaxConcurrentJobs() {
|
||||
return maxConcurrentJobs;
|
||||
}
|
||||
|
||||
public void setMaxConcurrentJobs(int maxConcurrentJobs) {
|
||||
this.maxConcurrentJobs = maxConcurrentJobs;
|
||||
}
|
||||
public record JobsConfig(
|
||||
@DurationUnit(ChronoUnit.MINUTES) @DefaultValue("1") Duration checkInterval,
|
||||
@DurationUnit(ChronoUnit.MINUTES) @DefaultValue("15") Duration unknownErrorTimeout,
|
||||
@DurationUnit(ChronoUnit.MINUTES) @DefaultValue("5") Duration statusErrorTimeout,
|
||||
@DurationUnit(ChronoUnit.MINUTES) @DefaultValue("2") Duration notAvailableTimeout,
|
||||
@DefaultValue("32") int maxConcurrentJobs
|
||||
) {
|
||||
}
|
||||
|
@ -1,71 +1,19 @@
|
||||
package io.papermc.hangar.config.hangar;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "hangar.orgs")
|
||||
public class OrganizationsConfig {
|
||||
private boolean enabled = true;
|
||||
private String dummyEmailDomain = "org.papermc.io";
|
||||
private int createLimit = 5;
|
||||
private int minNameLen = 3;
|
||||
private int maxNameLen = 20;
|
||||
private String nameRegex = "[a-zA-Z0-9-_]*";
|
||||
private final Predicate<String> namePredicate = Pattern.compile(nameRegex).asMatchPredicate();
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getDummyEmailDomain() {
|
||||
return dummyEmailDomain;
|
||||
}
|
||||
|
||||
public void setDummyEmailDomain(String dummyEmailDomain) {
|
||||
this.dummyEmailDomain = dummyEmailDomain;
|
||||
}
|
||||
|
||||
public int getCreateLimit() {
|
||||
return createLimit;
|
||||
}
|
||||
|
||||
public void setCreateLimit(int createLimit) {
|
||||
this.createLimit = createLimit;
|
||||
}
|
||||
|
||||
public int getMinNameLen() {
|
||||
return minNameLen;
|
||||
}
|
||||
|
||||
public void setMinNameLen(int minNameLen) {
|
||||
this.minNameLen = minNameLen;
|
||||
}
|
||||
|
||||
public String getNameRegex() {
|
||||
return nameRegex;
|
||||
}
|
||||
|
||||
public void setNameRegex(String nameRegex) {
|
||||
this.nameRegex = nameRegex;
|
||||
}
|
||||
|
||||
public int getMaxNameLen() {
|
||||
return maxNameLen;
|
||||
}
|
||||
|
||||
public void setMaxNameLen(int maxNameLen) {
|
||||
this.maxNameLen = maxNameLen;
|
||||
}
|
||||
|
||||
public boolean testName(String name) {
|
||||
return namePredicate.test(name);
|
||||
}
|
||||
public record OrganizationsConfig(
|
||||
@DefaultValue("true") boolean enabled,
|
||||
@DefaultValue("org.papermc.io") String dummyEmailDomain,
|
||||
@DefaultValue("5") int createLimit,
|
||||
@DefaultValue("3") int minNameLen,
|
||||
@DefaultValue("20") int maxNameLen,
|
||||
@DefaultValue("[a-zA-Z0-9-_]*") String nameRegex
|
||||
) {
|
||||
}
|
||||
|
@ -1,242 +1,36 @@
|
||||
package io.papermc.hangar.config.hangar;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import io.papermc.hangar.util.PatternWrapper;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "hangar.projects")
|
||||
public class ProjectsConfig {
|
||||
|
||||
private String nameRegex = "^[a-zA-Z0-9-_]{3,}$";
|
||||
private String versionNameRegex = "^[a-zA-Z0-9-_.+]+$";
|
||||
private String pageNameRegex = "^[a-zA-Z0-9-_.]+$";
|
||||
private Pattern namePattern = Pattern.compile(this.nameRegex);
|
||||
private Pattern versionNamePattern = Pattern.compile(this.versionNameRegex);
|
||||
private Pattern pageNamePattern = Pattern.compile(this.versionNameRegex);
|
||||
private int maxNameLen = 25;
|
||||
private int maxVersionNameLen = 30;
|
||||
private int maxDependencies = 100;
|
||||
private int maxPageNameLen = 25;
|
||||
private int maxPages = 50;
|
||||
private int maxChannels = 5;
|
||||
private int maxBBCodeLen = 30_000;
|
||||
private int initLoad = 25;
|
||||
private int initVersionLoad = 10;
|
||||
private int maxDescLen = 120;
|
||||
private int maxSponsorsLen = 500;
|
||||
private int maxKeywords = 5;
|
||||
private int contentMaxLen = 1_000_000;
|
||||
private boolean fileValidate = true;
|
||||
@DurationUnit(ChronoUnit.DAYS)
|
||||
private Duration staleAge = Duration.ofDays(28);
|
||||
private String checkInterval = "1h";
|
||||
private String draftExpire = "1d";
|
||||
private int userGridPageSize = 30;
|
||||
@DurationUnit(ChronoUnit.MINUTES)
|
||||
private Duration unsafeDownloadMaxAge = Duration.ofMinutes(10);
|
||||
private boolean showUnreviewedDownloadWarning;
|
||||
|
||||
public String getNameRegex() {
|
||||
return nameRegex;
|
||||
}
|
||||
|
||||
public Predicate<String> getNameMatcher() {
|
||||
return namePattern.asMatchPredicate();
|
||||
}
|
||||
|
||||
public Predicate<String> getVersionNameMatcher() {
|
||||
return versionNamePattern.asMatchPredicate();
|
||||
}
|
||||
|
||||
public Predicate<String> getPageNameMatcher() {
|
||||
return pageNamePattern.asMatchPredicate();
|
||||
}
|
||||
|
||||
public void setNameRegex(String nameRegex) {
|
||||
this.nameRegex = nameRegex;
|
||||
this.namePattern = Pattern.compile(nameRegex);
|
||||
}
|
||||
|
||||
public String getVersionNameRegex() {
|
||||
return versionNameRegex;
|
||||
}
|
||||
|
||||
public void setVersionNameRegex(String versionNameRegex) {
|
||||
this.versionNameRegex = versionNameRegex;
|
||||
this.versionNamePattern = Pattern.compile(versionNameRegex);
|
||||
}
|
||||
|
||||
public String getPageNameRegex() {
|
||||
return pageNameRegex;
|
||||
}
|
||||
|
||||
public void setPageNameRegex(String pageNameRegex) {
|
||||
this.pageNameRegex = pageNameRegex;
|
||||
}
|
||||
|
||||
public int getMaxNameLen() {
|
||||
return maxNameLen;
|
||||
}
|
||||
|
||||
public void setMaxNameLen(int maxNameLen) {
|
||||
this.maxNameLen = maxNameLen;
|
||||
}
|
||||
|
||||
public int getMaxVersionNameLen() {
|
||||
return maxVersionNameLen;
|
||||
}
|
||||
|
||||
public int getMaxDependencies() {
|
||||
return maxDependencies;
|
||||
}
|
||||
|
||||
public void setMaxDependencies(final int maxDependencies) {
|
||||
this.maxDependencies = maxDependencies;
|
||||
}
|
||||
|
||||
public void setMaxVersionNameLen(int maxVersionNameLen) {
|
||||
this.maxVersionNameLen = maxVersionNameLen;
|
||||
}
|
||||
|
||||
public int getMaxPageNameLen() {
|
||||
return maxPageNameLen;
|
||||
}
|
||||
|
||||
public void setMaxPageNameLen(int maxPageNameLen) {
|
||||
this.maxPageNameLen = maxPageNameLen;
|
||||
}
|
||||
|
||||
public int getMaxPages() {
|
||||
return maxPages;
|
||||
}
|
||||
|
||||
public void setMaxPages(int maxPages) {
|
||||
this.maxPages = maxPages;
|
||||
}
|
||||
|
||||
public int getMaxChannels() {
|
||||
return maxChannels;
|
||||
}
|
||||
|
||||
public void setMaxChannels(int maxChannels) {
|
||||
this.maxChannels = maxChannels;
|
||||
}
|
||||
|
||||
public void setMaxBBCodeLen(int maxBBCodeLen) {
|
||||
this.maxBBCodeLen = maxBBCodeLen;
|
||||
}
|
||||
|
||||
public int getMaxBBCodeLen() {
|
||||
return maxBBCodeLen;
|
||||
}
|
||||
|
||||
public int getMaxSponsorsLen() {
|
||||
return maxSponsorsLen;
|
||||
}
|
||||
|
||||
public void setMaxSponsorsLen(final int maxSponsorsLen) {
|
||||
this.maxSponsorsLen = maxSponsorsLen;
|
||||
}
|
||||
|
||||
public int getInitLoad() {
|
||||
return initLoad;
|
||||
}
|
||||
|
||||
public void setInitLoad(int initLoad) {
|
||||
this.initLoad = initLoad;
|
||||
}
|
||||
|
||||
public int getInitVersionLoad() {
|
||||
return initVersionLoad;
|
||||
}
|
||||
|
||||
public void setInitVersionLoad(int initVersionLoad) {
|
||||
this.initVersionLoad = initVersionLoad;
|
||||
}
|
||||
|
||||
public int getMaxDescLen() {
|
||||
return maxDescLen;
|
||||
}
|
||||
|
||||
public void setMaxDescLen(int maxDescLen) {
|
||||
this.maxDescLen = maxDescLen;
|
||||
}
|
||||
|
||||
public boolean isFileValidate() {
|
||||
return fileValidate;
|
||||
}
|
||||
|
||||
public void setFileValidate(boolean fileValidate) {
|
||||
this.fileValidate = fileValidate;
|
||||
}
|
||||
|
||||
public Duration getStaleAge() {
|
||||
return staleAge;
|
||||
}
|
||||
|
||||
public void setStaleAge(Duration staleAge) {
|
||||
this.staleAge = staleAge;
|
||||
}
|
||||
|
||||
public String getCheckInterval() {
|
||||
return checkInterval;
|
||||
}
|
||||
|
||||
public void setCheckInterval(String checkInterval) {
|
||||
this.checkInterval = checkInterval;
|
||||
}
|
||||
|
||||
public String getDraftExpire() {
|
||||
return draftExpire;
|
||||
}
|
||||
|
||||
public void setDraftExpire(String draftExpire) {
|
||||
this.draftExpire = draftExpire;
|
||||
}
|
||||
|
||||
public int getUserGridPageSize() {
|
||||
return userGridPageSize;
|
||||
}
|
||||
|
||||
public void setUserGridPageSize(int userGridPageSize) {
|
||||
this.userGridPageSize = userGridPageSize;
|
||||
}
|
||||
|
||||
public int getMaxKeywords() {
|
||||
return maxKeywords;
|
||||
}
|
||||
|
||||
public void setMaxKeywords(int maxKeywords) {
|
||||
this.maxKeywords = maxKeywords;
|
||||
}
|
||||
|
||||
public Duration getUnsafeDownloadMaxAge() {
|
||||
return unsafeDownloadMaxAge;
|
||||
}
|
||||
|
||||
public void setUnsafeDownloadMaxAge(Duration unsafeDownloadMaxAage) {
|
||||
this.unsafeDownloadMaxAge = unsafeDownloadMaxAage;
|
||||
}
|
||||
|
||||
public boolean showUnreviewedDownloadWarning() {
|
||||
return showUnreviewedDownloadWarning;
|
||||
}
|
||||
|
||||
public void setShowUnreviewedDownloadWarning(boolean showUnreviewedDownloadWarning) {
|
||||
this.showUnreviewedDownloadWarning = showUnreviewedDownloadWarning;
|
||||
}
|
||||
|
||||
public int getContentMaxLen() {
|
||||
return contentMaxLen;
|
||||
}
|
||||
|
||||
public void setContentMaxLen(int contentMaxLen) {
|
||||
this.contentMaxLen = contentMaxLen;
|
||||
}
|
||||
public record ProjectsConfig( // TODO split into ProjectsConfig and VersionsConfig
|
||||
@DefaultValue("^[a-zA-Z0-9-_]{3,}$") PatternWrapper nameRegex,
|
||||
@DefaultValue("^[a-zA-Z0-9-_.+]+$") PatternWrapper versionNameRegex,
|
||||
@DefaultValue("25") int maxNameLen,
|
||||
@DefaultValue("30") int maxVersionNameLen,
|
||||
@DefaultValue("100") int maxDependencies,
|
||||
@DefaultValue("50") int maxPages,
|
||||
@DefaultValue("5") int maxChannels,
|
||||
@DefaultValue("30000") int maxBBCodeLen,
|
||||
@DefaultValue("25") int initLoad,
|
||||
@DefaultValue("10") int initVersionLoad, // TODO implement (see @ConfigurePagination)
|
||||
@DefaultValue("120") int maxDescLen,
|
||||
@DefaultValue("500") int maxSponsorsLen,
|
||||
@DefaultValue("5") int maxKeywords,
|
||||
@DefaultValue("1000000") int contentMaxLen,
|
||||
@DefaultValue("true") boolean fileValidate, // TODO implement or remove
|
||||
@DefaultValue("28") @DurationUnit(ChronoUnit.DAYS) Duration staleAge,
|
||||
@DefaultValue("1h") String checkInterval, // TODO implement or remove
|
||||
@DefaultValue("1d") String draftExpire, // TODO implement or remove
|
||||
@DefaultValue("30") int userGridPageSize, // TODO implement or remove
|
||||
@DefaultValue("10") @DurationUnit(ChronoUnit.MINUTES) Duration unsafeDownloadMaxAge,
|
||||
@DefaultValue("false") boolean showUnreviewedDownloadWarning
|
||||
) {
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package io.papermc.hangar.config.hangar;
|
||||
|
||||
import io.awspring.cloud.autoconfigure.core.AwsProperties;
|
||||
import io.papermc.hangar.HangarApplication;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
@ -13,100 +15,37 @@ import org.springframework.stereotype.Component;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "hangar.storage")
|
||||
public class StorageConfig {
|
||||
public record StorageConfig(
|
||||
@DefaultValue("local") String type,
|
||||
@DefaultValue("backend/work") String workDir,
|
||||
String accessKey,
|
||||
String secretKey,
|
||||
String bucket,
|
||||
String objectStorageEndpoint,
|
||||
String cdnEndpoint,
|
||||
@DefaultValue("true") boolean cdnIncludeBucket
|
||||
) {
|
||||
|
||||
// type = local or object
|
||||
private String type = "local";
|
||||
// local
|
||||
private String pluginUploadDir = new ApplicationHome(HangarApplication.class).getDir().toPath().resolve("work").toString();
|
||||
// object
|
||||
private String accessKey;
|
||||
private String secretKey;
|
||||
private String bucket;
|
||||
private String objectStorageEndpoint;
|
||||
private String cdnEndpoint;
|
||||
private boolean cdnIncludeBucket = true;
|
||||
@Component
|
||||
public record AWSConfig(StorageConfig storageConfig) {
|
||||
|
||||
@Bean
|
||||
public StaticCredentialsProvider credProvider() {
|
||||
return StaticCredentialsProvider.create(AwsBasicCredentials.create(getAccessKey(), getSecretKey()));
|
||||
}
|
||||
@Bean
|
||||
public StaticCredentialsProvider credProvider() {
|
||||
return StaticCredentialsProvider.create(AwsBasicCredentials.create(this.storageConfig.accessKey(), this.storageConfig.secretKey()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AwsRegionProvider regionProvider() {
|
||||
return () -> Region.of("hangar");
|
||||
}
|
||||
@Bean
|
||||
public AwsRegionProvider regionProvider() {
|
||||
return () -> Region.of("hangar");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AwsProperties awsProperties() throws URISyntaxException {
|
||||
AwsProperties awsProperties = new AwsProperties();
|
||||
awsProperties.setEndpoint(new URI(objectStorageEndpoint));
|
||||
return awsProperties;
|
||||
}
|
||||
@Bean
|
||||
public AwsProperties awsProperties() throws URISyntaxException {
|
||||
final AwsProperties awsProperties = new AwsProperties();
|
||||
awsProperties.setEndpoint(new URI(this.storageConfig.objectStorageEndpoint()));
|
||||
return awsProperties;
|
||||
}
|
||||
|
||||
public String getPluginUploadDir() {
|
||||
return pluginUploadDir;
|
||||
}
|
||||
|
||||
public void setPluginUploadDir(String pluginUploadDir) {
|
||||
this.pluginUploadDir = pluginUploadDir;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getAccessKey() {
|
||||
return accessKey;
|
||||
}
|
||||
|
||||
public void setAccessKey(String accessKey) {
|
||||
this.accessKey = accessKey;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
public String getObjectStorageEndpoint() {
|
||||
return objectStorageEndpoint;
|
||||
}
|
||||
|
||||
public void setObjectStorageEndpoint(String objectStorageEndpoint) {
|
||||
this.objectStorageEndpoint = objectStorageEndpoint;
|
||||
}
|
||||
|
||||
public String getBucket() {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
public void setBucket(String bucket) {
|
||||
this.bucket = bucket;
|
||||
}
|
||||
|
||||
public String getCdnEndpoint() {
|
||||
return cdnEndpoint;
|
||||
}
|
||||
|
||||
public void setCdnEndpoint(String cdnEndpoint) {
|
||||
this.cdnEndpoint = cdnEndpoint;
|
||||
}
|
||||
|
||||
public boolean isCdnIncludeBucket() {
|
||||
return cdnIncludeBucket;
|
||||
}
|
||||
|
||||
public void setCdnIncludeBucket(boolean cdnIncludeBucket) {
|
||||
this.cdnIncludeBucket = cdnIncludeBucket;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
package io.papermc.hangar.config.hangar.converters;
|
||||
|
||||
import io.papermc.hangar.util.PatternWrapper;
|
||||
import java.util.regex.Pattern;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
@Component
|
||||
@ConfigurationPropertiesBinding
|
||||
public class StringToPatternWrapperConverter implements Converter<String, PatternWrapper> {
|
||||
|
||||
@Override
|
||||
public @Nullable PatternWrapper convert(final String source) {
|
||||
return new PatternWrapper(Pattern.compile(source));
|
||||
}
|
||||
}
|
@ -50,7 +50,7 @@ public class LoginController extends HangarComponent {
|
||||
|
||||
@GetMapping(path = "/login", params = "returnUrl")
|
||||
public RedirectView loginFromFrontend(@RequestParam(defaultValue = "/") String returnUrl) {
|
||||
if (config.fakeUser.isEnabled()) {
|
||||
if (config.fakeUser.enabled()) {
|
||||
config.checkDev();
|
||||
|
||||
UserTable fakeUser = authenticationService.loginAsFakeUser();
|
||||
@ -95,7 +95,7 @@ public class LoginController extends HangarComponent {
|
||||
|
||||
@GetMapping(path = "/logout", params = "returnUrl")
|
||||
public RedirectView logout(@RequestParam(defaultValue = "/logged-out") String returnUrl) {
|
||||
if (config.fakeUser.isEnabled()) {
|
||||
if (config.fakeUser.enabled()) {
|
||||
response.addCookie(new Cookie("url", returnUrl));
|
||||
return new RedirectView("/fake-logout");
|
||||
} else {
|
||||
@ -151,7 +151,7 @@ public class LoginController extends HangarComponent {
|
||||
|
||||
@GetMapping("/signup")
|
||||
public RedirectView signUp(@RequestParam(defaultValue = "") String returnUrl) {
|
||||
if (config.fakeUser.isEnabled()) {
|
||||
if (config.fakeUser.enabled()) {
|
||||
throw new HangarApiException("nav.user.error.fakeUserEnabled", "Signup");
|
||||
}
|
||||
return new RedirectView(ssoService.getSignupUrl(returnUrl));
|
||||
|
@ -18,7 +18,7 @@ public class ApiUtils {
|
||||
* @return actual limit
|
||||
*/
|
||||
public static long limitOrDefault(@Nullable Long limit) {
|
||||
return limitOrDefault(limit, hangarConfig.projects.getInitLoad());
|
||||
return limitOrDefault(limit, hangarConfig.projects.initLoad());
|
||||
}
|
||||
|
||||
public static long limitOrDefault(@Nullable Long limit, long maxLimit) {
|
||||
|
@ -18,4 +18,6 @@ public @interface ConfigurePagination {
|
||||
* -1 means fallback to default configured value
|
||||
*/
|
||||
long maxLimit();
|
||||
|
||||
// TODO add String SpEL param to use configurable values for action log amounts and version amounts
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
// @el(user: io.papermc.hangar.model.db.UserTable)
|
||||
@LoggedIn
|
||||
@Controller
|
||||
@RateLimit(path = "apikey")
|
||||
|
@ -210,19 +210,19 @@ public class BackendDataController {
|
||||
public ResponseEntity<ObjectNode> getValidations() {
|
||||
ObjectNode validations = noJsonValueMapper.createObjectNode();
|
||||
ObjectNode projectValidations = noJsonValueMapper.createObjectNode();
|
||||
projectValidations.set("name", noJsonValueMapper.valueToTree(new Validation(config.projects.getNameRegex(), config.projects.getMaxNameLen(), null)));
|
||||
projectValidations.set("desc", noJsonValueMapper.valueToTree(new Validation(null, config.projects.getMaxDescLen(), null)));
|
||||
projectValidations.set("keywords", noJsonValueMapper.valueToTree(new Validation(null, config.projects.getMaxKeywords(), null)));
|
||||
projectValidations.set("channels", noJsonValueMapper.valueToTree(new Validation(config.channels.getNameRegex(), config.channels.getMaxNameLen(), null)));
|
||||
projectValidations.set("name", noJsonValueMapper.valueToTree(new Validation(config.projects.nameRegex().strPattern(), config.projects.maxNameLen(), null)));
|
||||
projectValidations.set("desc", noJsonValueMapper.valueToTree(new Validation(null, config.projects.maxDescLen(), null)));
|
||||
projectValidations.set("keywords", noJsonValueMapper.valueToTree(new Validation(null, config.projects.maxKeywords(), null)));
|
||||
projectValidations.set("channels", noJsonValueMapper.valueToTree(new Validation(config.channels.nameRegex(), config.channels.maxNameLen(), null)));
|
||||
projectValidations.set("pageName", noJsonValueMapper.valueToTree(new Validation(config.pages.nameRegex(), config.pages.maxNameLen(), config.pages.minNameLen())));
|
||||
projectValidations.set("pageContent", noJsonValueMapper.valueToTree(new Validation(null, config.pages.maxLen(), config.pages.minLen())));
|
||||
projectValidations.put("maxPageCount", config.projects.getMaxPages());
|
||||
projectValidations.put("maxChannelCount", config.projects.getMaxChannels());
|
||||
projectValidations.put("maxPageCount", config.projects.maxPages());
|
||||
projectValidations.put("maxChannelCount", config.projects.maxChannels());
|
||||
validations.set("project", projectValidations);
|
||||
validations.set("userTagline", noJsonValueMapper.valueToTree(new Validation(null, config.user.maxTaglineLen(), null)));
|
||||
validations.set("version", noJsonValueMapper.valueToTree(new Validation(config.projects.getVersionNameRegex(), config.projects.getMaxVersionNameLen(), null)));
|
||||
validations.set("org", noJsonValueMapper.valueToTree(new Validation(config.org.getNameRegex(), config.org.getMaxNameLen(), config.org.getMinNameLen())));
|
||||
validations.put("maxOrgCount", config.org.getCreateLimit());
|
||||
validations.set("version", noJsonValueMapper.valueToTree(new Validation(config.projects.versionNameRegex().strPattern(), config.projects.maxVersionNameLen(), null)));
|
||||
validations.set("org", noJsonValueMapper.valueToTree(new Validation(config.org.nameRegex(), config.org.maxNameLen(), config.org.minNameLen())));
|
||||
validations.put("maxOrgCount", config.org.createLimit());
|
||||
validations.put("urlRegex", config.getUrlRegex());
|
||||
return ResponseEntity.ok(validations);
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ public class DiscourseController extends HangarComponent {
|
||||
@RateLimit(overdraft = 5, refillTokens = 1, refillSeconds = 30)
|
||||
@VisibilityRequired(type = Type.PROJECT, args = "{#projectId}")
|
||||
public String createPost(@PathVariable long projectId, @RequestBody Map<String, String> content) {
|
||||
if (!config.discourse.isEnabled()) {
|
||||
if (!config.discourse.enabled()) {
|
||||
throw new HangarApiException("Discourse is NOT enabled!");
|
||||
}
|
||||
jobService.save(new PostDiscourseReplyJob(projectId, getHangarPrincipal().getName(), content.get("content")));
|
||||
|
@ -126,7 +126,7 @@ public class ProjectController extends HangarComponent {
|
||||
@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 String author, @PathVariable String slug, @RequestBody @Valid StringContent content) {
|
||||
if (content.getContent().length() > config.projects.getMaxSponsorsLen()) {
|
||||
if (content.getContent().length() > config.projects.maxSponsorsLen()) {
|
||||
throw new HangarApiException("page.new.error.name.maxLength");
|
||||
}
|
||||
projectService.saveSponsors(author, slug, content);
|
||||
|
@ -56,7 +56,7 @@ public class ProjectPageController extends HangarComponent {
|
||||
@RateLimit(overdraft = 10, refillTokens = 3, refillSeconds = 5, greedy = true)
|
||||
@PostMapping(path = "/render", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<String> renderMarkdown(@RequestBody @Valid StringContent content) {
|
||||
if (content.getContent().length() > config.projects.getContentMaxLen()) {
|
||||
if (content.getContent().length() > config.projects.contentMaxLen()) {
|
||||
throw new HangarApiException("page.new.error.name.maxLength");
|
||||
}
|
||||
return ResponseEntity.ok(markdownService.render(content.getContent()));
|
||||
@ -67,7 +67,7 @@ public class ProjectPageController extends HangarComponent {
|
||||
@ResponseBody
|
||||
@PostMapping(path = "/convert-bbcode", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
|
||||
public String convertBBCode(@RequestBody @Valid StringContent bbCodeContent) {
|
||||
if (bbCodeContent.getContent().length() > config.projects.getMaxBBCodeLen()) {
|
||||
if (bbCodeContent.getContent().length() > config.projects.maxBBCodeLen()) {
|
||||
throw new HangarApiException("page.new.error.name.maxLength");
|
||||
}
|
||||
BBCodeConverter bbCodeConverter = new BBCodeConverter();
|
||||
|
@ -12,19 +12,19 @@ public class CreateOrganizationForm extends EditMembersForm<OrganizationRole> {
|
||||
@Validate(SpEL = "@validate.min(#root, @hangarConfig.org.minNameLen)", message = "organization.new.error.invalidName")
|
||||
private final String name;
|
||||
|
||||
public CreateOrganizationForm(List<Member<OrganizationRole>> members, String name) {
|
||||
public CreateOrganizationForm(final List<Member<OrganizationRole>> members, final String name) {
|
||||
super(members);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CreateOrganizationForm{" +
|
||||
"name='" + name + '\'' +
|
||||
"name='" + this.name + '\'' +
|
||||
"} " + super.toString();
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ public class APIKeyService extends HangarComponent {
|
||||
|
||||
String tokenIdentifier = UUID.randomUUID().toString();
|
||||
String token = UUID.randomUUID().toString();
|
||||
String hashedToken = CryptoUtils.hmacSha256(config.security.getTokenSecret(), token.getBytes(StandardCharsets.UTF_8));
|
||||
String hashedToken = CryptoUtils.hmacSha256(config.security.tokenSecret(), token.getBytes(StandardCharsets.UTF_8));
|
||||
apiKeyDAO.insert(new ApiKeyTable(apiKeyForm.getName(), userIdentified.getUserId(), tokenIdentifier, hashedToken, keyPermission));
|
||||
actionLogger.user(LogAction.USER_APIKEY_CREATED.create(UserContext.of(userIdentified.getUserId()), "Key Name: " + apiKeyForm.getName() + "<br>" + apiKeyForm.getPermissions().stream().map(NamedPermission::getFrontendName).collect(Collectors.joining(",<br>")), ""));
|
||||
return tokenIdentifier + "." + token;
|
||||
|
@ -37,14 +37,14 @@ public class AuthenticationService extends HangarComponent {
|
||||
}
|
||||
|
||||
public UserTable loginAsFakeUser() {
|
||||
String userName = config.fakeUser.getUsername();
|
||||
String userName = config.fakeUser.username();
|
||||
UserTable userTable = userService.getUserTable(userName);
|
||||
if (userTable == null) {
|
||||
userTable = new UserTable(
|
||||
-1, // we can pass -1 here since it's not actually inserted in the DB in the DAO
|
||||
UUID.randomUUID(),
|
||||
userName,
|
||||
config.fakeUser.getEmail(),
|
||||
config.fakeUser.email(),
|
||||
List.of(),
|
||||
false,
|
||||
Locale.ENGLISH.toLanguageTag(),
|
||||
@ -68,7 +68,7 @@ public class AuthenticationService extends HangarComponent {
|
||||
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
|
||||
|
||||
try {
|
||||
ResponseEntity<Void> response = restTemplate.postForEntity(config.security.api.getUrl() + "/avatar/org/" + org + "?apiKey=" + config.sso.apiKey(), requestEntity, Void.class);
|
||||
ResponseEntity<Void> response = restTemplate.postForEntity(config.security.api().url() + "/avatar/org/" + org + "?apiKey=" + config.sso.apiKey(), requestEntity, Void.class);
|
||||
if (!response.getStatusCode().is2xxSuccessful()) {
|
||||
throw new ResponseStatusException(response.getStatusCode(), "Error from auth api");
|
||||
}
|
||||
|
@ -54,14 +54,14 @@ public class TokenService extends HangarComponent {
|
||||
|
||||
public void issueRefreshAndAccessToken(UserTable userTable) {
|
||||
UserRefreshToken userRefreshToken = userRefreshTokenDAO.insert(new UserRefreshToken(userTable.getId(), UUID.randomUUID(), UUID.randomUUID()));
|
||||
addCookie(SecurityConfig.REFRESH_COOKIE_NAME, userRefreshToken.getToken().toString(), config.security.getRefreshTokenExpiry().toSeconds(), true);
|
||||
addCookie(SecurityConfig.REFRESH_COOKIE_NAME, userRefreshToken.getToken().toString(), config.security.refreshTokenExpiry().toSeconds(), true);
|
||||
String accessToken = newToken0(userTable);
|
||||
// let the access token cookie be around for longer, so we can more nicely detect expired tokens via the response code
|
||||
addCookie(SecurityConfig.AUTH_NAME, accessToken, config.security.getTokenExpiry().toSeconds() * 2, false);
|
||||
addCookie(SecurityConfig.AUTH_NAME, accessToken, config.security.tokenExpiry().toSeconds() * 2, false);
|
||||
}
|
||||
|
||||
private void addCookie(String name, String value, long maxAge, boolean httpOnly) {
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, ResponseCookie.from(name, value).path("/").secure(config.security.isSecure()).maxAge(maxAge).sameSite("Lax").httpOnly(httpOnly).build().toString());
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, ResponseCookie.from(name, value).path("/").secure(config.security.secure()).maxAge(maxAge).sameSite("Lax").httpOnly(httpOnly).build().toString());
|
||||
}
|
||||
|
||||
public void refreshAccessToken(String refreshToken) {
|
||||
@ -78,7 +78,7 @@ public class TokenService extends HangarComponent {
|
||||
if (userRefreshToken == null) {
|
||||
throw new HangarApiException(HttpStatus.UNAUTHORIZED, "Unrecognized refresh token " + uuid);
|
||||
}
|
||||
if (userRefreshToken.getLastUpdated().isBefore(OffsetDateTime.now().minus(config.security.getRefreshTokenExpiry()))) {
|
||||
if (userRefreshToken.getLastUpdated().isBefore(OffsetDateTime.now().minus(config.security.refreshTokenExpiry()))) {
|
||||
throw new HangarApiException(HttpStatus.UNAUTHORIZED, "Expired refresh token" + uuid);
|
||||
}
|
||||
UserTable userTable = userService.getUserTable(userRefreshToken.getUserId());
|
||||
@ -88,10 +88,10 @@ public class TokenService extends HangarComponent {
|
||||
// we gotta update the refresh token
|
||||
userRefreshToken.setToken(UUID.randomUUID());
|
||||
userRefreshToken = userRefreshTokenDAO.update(userRefreshToken);
|
||||
addCookie(SecurityConfig.REFRESH_COOKIE_NAME, userRefreshToken.getToken().toString(), config.security.getRefreshTokenExpiry().toSeconds(), true);
|
||||
addCookie(SecurityConfig.REFRESH_COOKIE_NAME, userRefreshToken.getToken().toString(), config.security.refreshTokenExpiry().toSeconds(), true);
|
||||
// then issue a new access token
|
||||
String accessToken = newToken0(userTable);
|
||||
addCookie(SecurityConfig.AUTH_NAME, accessToken, config.security.getTokenExpiry().toSeconds(), false);
|
||||
addCookie(SecurityConfig.AUTH_NAME, accessToken, config.security.tokenExpiry().toSeconds(), false);
|
||||
}
|
||||
|
||||
public void invalidateToken(String refreshToken) {
|
||||
@ -109,8 +109,8 @@ public class TokenService extends HangarComponent {
|
||||
|
||||
public String expiring(UserTable userTable, Permission globalPermission, @Nullable String apiKeyIdentifier) {
|
||||
return JWT.create()
|
||||
.withIssuer(config.security.getTokenIssuer())
|
||||
.withExpiresAt(new Date(Instant.now().plus(config.security.getTokenExpiry()).toEpochMilli()))
|
||||
.withIssuer(config.security.tokenIssuer())
|
||||
.withExpiresAt(new Date(Instant.now().plus(config.security.tokenExpiry()).toEpochMilli()))
|
||||
.withSubject(userTable.getName())
|
||||
.withClaim("id", userTable.getId())
|
||||
.withClaim("permissions", globalPermission.toBinString())
|
||||
@ -121,8 +121,8 @@ public class TokenService extends HangarComponent {
|
||||
|
||||
public String simple(String username) {
|
||||
return JWT.create()
|
||||
.withIssuer(config.security.getTokenIssuer())
|
||||
.withExpiresAt(new Date(Instant.now().plus(config.security.getTokenExpiry()).toEpochMilli()))
|
||||
.withIssuer(config.security.tokenIssuer())
|
||||
.withExpiresAt(new Date(Instant.now().plus(config.security.tokenExpiry()).toEpochMilli()))
|
||||
.withSubject(username)
|
||||
.sign(getAlgo());
|
||||
}
|
||||
@ -151,7 +151,7 @@ public class TokenService extends HangarComponent {
|
||||
if (verifier == null) {
|
||||
verifier = JWT.require(getAlgo())
|
||||
.acceptLeeway(10)
|
||||
.withIssuer(config.security.getTokenIssuer())
|
||||
.withIssuer(config.security.tokenIssuer())
|
||||
.build();
|
||||
}
|
||||
return verifier;
|
||||
@ -159,7 +159,7 @@ public class TokenService extends HangarComponent {
|
||||
|
||||
private Algorithm getAlgo() {
|
||||
if (algo == null) {
|
||||
algo = Algorithm.HMAC256(config.security.getTokenSecret());
|
||||
algo = Algorithm.HMAC256(config.security.tokenSecret());
|
||||
}
|
||||
return algo;
|
||||
}
|
||||
|
@ -39,9 +39,9 @@ public class ValidationService {
|
||||
error = "invalidName";
|
||||
} else if (name.length() < 3) {
|
||||
error = "tooShortName";
|
||||
} else if (name.length() > config.projects.getMaxNameLen()) {
|
||||
} else if (name.length() > config.projects.maxNameLen()) {
|
||||
error = "tooLongName";
|
||||
} else if (name.contains(ProjectFactory.SOFT_DELETION_SUFFIX) || !config.projects.getNameMatcher().test(name)) {
|
||||
} else if (name.contains(ProjectFactory.SOFT_DELETION_SUFFIX) || !config.projects.nameRegex().test(name)) {
|
||||
error = "invalidName";
|
||||
}
|
||||
return error != null ? "project.new.error." + error : null;
|
||||
@ -52,7 +52,7 @@ public class ValidationService {
|
||||
if (bannedRoutes.contains(name) || name.contains(ProjectFactory.SOFT_DELETION_SUFFIX)) {
|
||||
return false;
|
||||
}
|
||||
if (name.length() < 1 || name.length() > config.projects.getMaxVersionNameLen() || !config.projects.getVersionNameMatcher().test(name)) {
|
||||
if (name.length() < 1 || name.length() > config.projects.maxVersionNameLen() || !config.projects.versionNameRegex().test(name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -39,13 +39,13 @@ public class APIAuthenticationService extends HangarComponent {
|
||||
}
|
||||
String identifier = apiKey.split("\\.")[0];
|
||||
String token = apiKey.split("\\.")[1];
|
||||
String hashedToken = CryptoUtils.hmacSha256(config.security.getTokenSecret(), token.getBytes(StandardCharsets.UTF_8));
|
||||
String hashedToken = CryptoUtils.hmacSha256(config.security.tokenSecret(), token.getBytes(StandardCharsets.UTF_8));
|
||||
ApiKeyTable apiKeyTable = apiKeyDAO.findApiKey(identifier, hashedToken);
|
||||
if (apiKeyTable == null) {
|
||||
throw new HangarApiException("No valid API Key found");
|
||||
}
|
||||
UserTable userTable = userDAO.getUserTable(apiKeyTable.getOwnerId());
|
||||
String jwt = tokenService.expiring(userTable, apiKeyTable.getPermissions(), identifier);
|
||||
return new ApiSession(jwt, config.security.getRefreshTokenExpiry().toSeconds());
|
||||
return new ApiSession(jwt, config.security.refreshTokenExpiry().toSeconds());
|
||||
}
|
||||
}
|
||||
|
@ -51,15 +51,15 @@ public class JobService extends HangarComponent {
|
||||
|
||||
@PostConstruct
|
||||
public void initThreadPool() {
|
||||
this.executorService = new ThreadPoolExecutor(1, config.jobs.getMaxConcurrentJobs(), 60, TimeUnit.SECONDS, new SynchronousQueue<>());
|
||||
this.executorService = new ThreadPoolExecutor(1, config.jobs.maxConcurrentJobs(), 60, TimeUnit.SECONDS, new SynchronousQueue<>());
|
||||
}
|
||||
|
||||
public void checkAndProcess() {
|
||||
if (!config.discourse.isEnabled()) { return; }
|
||||
if (!config.discourse.enabled()) { return; }
|
||||
long awaitingJobs = jobsDAO.countAwaitingJobs();
|
||||
logger.debug("Found {} awaiting jobs", awaitingJobs);
|
||||
if (awaitingJobs > 0) {
|
||||
long numberToProcess = Math.max(1, Math.min(awaitingJobs, config.jobs.getMaxConcurrentJobs()));
|
||||
long numberToProcess = Math.max(1, Math.min(awaitingJobs, config.jobs.maxConcurrentJobs()));
|
||||
for (long i = 0; i < numberToProcess; i++) {
|
||||
executorService.submit(this::process);
|
||||
}
|
||||
@ -72,7 +72,7 @@ public class JobService extends HangarComponent {
|
||||
|
||||
@Transactional
|
||||
public void save(Job job) {
|
||||
if (!config.discourse.isEnabled()) { return; }
|
||||
if (!config.discourse.enabled()) { return; }
|
||||
jobsDAO.save(job.toTable());
|
||||
}
|
||||
|
||||
@ -97,15 +97,15 @@ public class JobService extends HangarComponent {
|
||||
toJobString(jobTable) +
|
||||
"Status Code: " + statusError.getStatus() + "\n" +
|
||||
toMessageString(statusError);
|
||||
jobsDAO.retryIn(jobTable.getId(), OffsetDateTime.now().plus(config.jobs.getStatusErrorTimeout()).plusSeconds(5), error, "status_error_" + statusError.getStatus().value());
|
||||
jobsDAO.retryIn(jobTable.getId(), OffsetDateTime.now().plus(config.jobs.statusErrorTimeout()).plusSeconds(5), error, "status_error_" + statusError.getStatus().value());
|
||||
} catch (DiscourseError.UnknownError unknownError) {
|
||||
String error = "Encountered error when executing Discourse request\n" +
|
||||
toJobString(jobTable) +
|
||||
"Type: " + unknownError.getDescriptor() + "\n" +
|
||||
toMessageString(unknownError);
|
||||
jobsDAO.retryIn(jobTable.getId(), OffsetDateTime.now().plus(config.jobs.getUnknownErrorTimeout()).plusSeconds(5), error, "unknown_error" + unknownError.getDescriptor());
|
||||
jobsDAO.retryIn(jobTable.getId(), OffsetDateTime.now().plus(config.jobs.unknownErrorTimeout()).plusSeconds(5), error, "unknown_error" + unknownError.getDescriptor());
|
||||
} catch (DiscourseError.NotAvailableError notAvailableError) {
|
||||
jobsDAO.retryIn(jobTable.getId(), OffsetDateTime.now().plus(config.jobs.getNotAvailableTimeout()).plusSeconds(5), "Not Available", "not_available");
|
||||
jobsDAO.retryIn(jobTable.getId(), OffsetDateTime.now().plus(config.jobs.notAvailableTimeout()).plusSeconds(5), "Not Available", "not_available");
|
||||
} catch (DiscourseError.NotProcessable notProcessable) {
|
||||
logger.debug("job failed to process discourse job: {} {}", notProcessable.getMessage(), jobTable);
|
||||
String error = "Encountered error when processing discourse job\n" +
|
||||
|
@ -30,7 +30,7 @@ public class HealthService extends HangarComponent {
|
||||
}
|
||||
|
||||
public List<UnhealthyProject> getStaleProjects() {
|
||||
return healthDAO.getStaleProjects("'" + config.projects.getStaleAge().toSeconds() + " SECONDS'");
|
||||
return healthDAO.getStaleProjects("'" + config.projects.staleAge().toSeconds() + " SECONDS'");
|
||||
}
|
||||
|
||||
public List<UnhealthyProject> getNonPublicProjects() {
|
||||
|
@ -66,7 +66,7 @@ public class StatService extends HangarComponent {
|
||||
private void setCookie(String cookieValue) {
|
||||
response.addHeader(HttpHeaders.SET_COOKIE,
|
||||
ResponseCookie.from(STAT_TRACKING_COOKIE, cookieValue)
|
||||
.secure(config.security.isSecure())
|
||||
.secure(config.security.secure())
|
||||
.path("/")
|
||||
.maxAge((long) (60 * 60 * 24 * 356.24 * 1000))
|
||||
.sameSite("Strict")
|
||||
|
@ -39,8 +39,8 @@ public class DiscourseApi {
|
||||
|
||||
private HttpHeaders header(String poster) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Api-Key", config.getApiKey());
|
||||
headers.set("Api-Username", poster == null ? config.getAdminUser() : poster);
|
||||
headers.set("Api-Key", config.apiKey());
|
||||
headers.set("Api-Username", poster == null ? config.adminUser() : poster);
|
||||
return headers;
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ public class DiscourseApi {
|
||||
Map<String, Object> args = new HashMap<>();
|
||||
args.put("topic_id", topicId);
|
||||
args.put("raw", content);
|
||||
return execute(args, config.getUrl() + "/posts.json", header(poster), HttpMethod.POST, DiscoursePost.class);
|
||||
return execute(args, config.url() + "/posts.json", header(poster), HttpMethod.POST, DiscoursePost.class);
|
||||
}
|
||||
|
||||
public DiscoursePost createTopic(String poster, String title, String content, @Nullable Integer categoryId) {
|
||||
@ -56,7 +56,7 @@ public class DiscourseApi {
|
||||
args.put("title", title);
|
||||
args.put("raw", content);
|
||||
args.put("category", categoryId);
|
||||
return execute(args, config.getUrl() + "/posts.json", header(poster), HttpMethod.POST, DiscoursePost.class);
|
||||
return execute(args, config.url() + "/posts.json", header(poster), HttpMethod.POST, DiscoursePost.class);
|
||||
}
|
||||
|
||||
public void updateTopic(String poster, long topicId, @Nullable String title, @Nullable Integer categoryId) {
|
||||
@ -64,17 +64,17 @@ public class DiscourseApi {
|
||||
args.put("topic_id", topicId);
|
||||
args.put("title", title);
|
||||
args.put("category", categoryId);
|
||||
execute(args, config.getUrl() + "/t/-/" + topicId + ".json", header(poster), HttpMethod.PUT);
|
||||
execute(args, config.url() + "/t/-/" + topicId + ".json", header(poster), HttpMethod.PUT);
|
||||
}
|
||||
|
||||
public void updatePost(String poster, long postId, String content) {
|
||||
Map<String, String> args = new HashMap<>();
|
||||
args.put("raw", content);
|
||||
execute(args, config.getUrl() + "/posts/" + postId + ".json", header(poster), HttpMethod.PUT);
|
||||
execute(args, config.url() + "/posts/" + postId + ".json", header(poster), HttpMethod.PUT);
|
||||
}
|
||||
|
||||
public void deleteTopic(String poster, long topicId) {
|
||||
execute(null, config.getUrl() + "/t/" + topicId + ".json", header(poster), HttpMethod.DELETE);
|
||||
execute(null, config.url() + "/t/" + topicId + ".json", header(poster), HttpMethod.DELETE);
|
||||
}
|
||||
|
||||
private void execute(Object args, String url, HttpHeaders headers, HttpMethod method) {
|
||||
|
@ -69,7 +69,7 @@ public class DiscourseService {
|
||||
String title = discourseFormatter.formatProjectTitle(project);
|
||||
String content = discourseFormatter.formatProjectTopic(project, getHomepageContent(project));
|
||||
|
||||
DiscoursePost post = api.createTopic(project.getOwnerName(), title, content, config.getCategory());
|
||||
DiscoursePost post = api.createTopic(project.getOwnerName(), title, content, config.category());
|
||||
if (post == null) {
|
||||
throw new JobException("project post wasn't created " + project.getProjectId(), "sanity_check");
|
||||
}
|
||||
@ -87,7 +87,7 @@ public class DiscourseService {
|
||||
String title = discourseFormatter.formatProjectTitle(project);
|
||||
String content = discourseFormatter.formatProjectTopic(project, getHomepageContent(project));
|
||||
|
||||
api.updateTopic(project.getOwnerName(), project.getTopicId(), title, project.getVisibility() == Visibility.PUBLIC ? config.getCategory() : config.getCategoryDeleted());
|
||||
api.updateTopic(project.getOwnerName(), project.getTopicId(), title, project.getVisibility() == Visibility.PUBLIC ? config.category() : config.categoryDeleted());
|
||||
api.updatePost(project.getOwnerName(), project.getPostId(), content);
|
||||
}
|
||||
|
||||
@ -124,6 +124,6 @@ public class DiscourseService {
|
||||
}
|
||||
|
||||
public void deleteTopic(long topicId) {
|
||||
api.deleteTopic(config.getAdminUser(), topicId);
|
||||
api.deleteTopic(config.adminUser(), topicId);
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ public class LocalStorageFileService implements FileService {
|
||||
|
||||
@Override
|
||||
public String getRoot() {
|
||||
return config.getPluginUploadDir();
|
||||
return config.workDir();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -40,7 +40,7 @@ public class S3FileService implements FileService {
|
||||
|
||||
@Override
|
||||
public void deleteDirectory(String dir) {
|
||||
this.s3Template.deleteObject(config.getBucket(), dir);
|
||||
this.s3Template.deleteObject(config.bucket(), dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -86,11 +86,11 @@ public class S3FileService implements FileService {
|
||||
|
||||
@Override
|
||||
public String getRoot() {
|
||||
return "s3://" + config.getBucket() + "/";
|
||||
return "s3://" + config.bucket() + "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String user, String project, String version, Platform platform, String fileName) {
|
||||
return config.getCdnEndpoint() + (config.isCdnIncludeBucket() ? "/" + config.getBucket() : "") + "/plugins/" + user + "/" + project + "/versions/" + version + "/" + platform.name() + "/" + fileName;
|
||||
return config.cdnEndpoint() + (config.cdnIncludeBucket() ? "/" + config.bucket() : "") + "/plugins/" + user + "/" + project + "/versions/" + version + "/" + platform.name() + "/" + fileName;
|
||||
}
|
||||
}
|
||||
|
@ -48,14 +48,14 @@ public class OrganizationFactory extends HangarComponent {
|
||||
|
||||
@Transactional
|
||||
public void createOrganization(String name) {
|
||||
if (!config.org.isEnabled()) {
|
||||
if (!config.org.enabled()) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "organization.new.error.notEnabled");
|
||||
}
|
||||
if (organizationService.getOrganizationsOwnedBy(getHangarPrincipal().getId()).size() >= config.org.getCreateLimit()) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "organization.new.error.tooManyOrgs", config.org.getCreateLimit());
|
||||
if (organizationService.getOrganizationsOwnedBy(getHangarPrincipal().getId()).size() >= config.org.createLimit()) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "organization.new.error.tooManyOrgs", config.org.createLimit());
|
||||
}
|
||||
|
||||
String dummyEmail = name.replaceAll("[^a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]", "") + '@' + config.org.getDummyEmailDomain();
|
||||
String dummyEmail = name.replaceAll("[^a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]", "") + '@' + config.org.dummyEmailDomain();
|
||||
UserTable userTable = userDAO.create(UUID.randomUUID(), name, dummyEmail, "", "", List.of(), false, null);
|
||||
OrganizationTable organizationTable = organizationDAO.insert(new OrganizationTable(userTable.getId(), name, getHangarPrincipal().getId(), userTable.getId()));
|
||||
globalRoleService.addRole(GlobalRole.ORGANIZATION.create(null, userTable.getId(), false));
|
||||
|
@ -56,8 +56,8 @@ public class ChannelService extends HangarComponent {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "channel.modal.error.invalidName");
|
||||
}
|
||||
|
||||
if (existingChannels.size() >= this.config.projects.getMaxChannels()) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "channel.modal.error.maxChannels", this.config.projects.getMaxChannels());
|
||||
if (existingChannels.size() >= this.config.projects.maxChannels()) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "channel.modal.error.maxChannels", this.config.projects.maxChannels());
|
||||
}
|
||||
|
||||
this.checkName(projectId, name, null, ignored -> existingChannels);
|
||||
|
@ -69,7 +69,7 @@ public class ProjectFactory extends HangarComponent {
|
||||
ProjectTable projectTable = null;
|
||||
try {
|
||||
projectTable = this.projectsDAO.insert(new ProjectTable(projectOwner, newProject));
|
||||
this.channelService.createProjectChannel(this.config.channels.getNameDefault(), this.config.channels.getColorDefault(), projectTable.getId(), Set.of(ChannelFlag.FROZEN, ChannelFlag.PINNED));
|
||||
this.channelService.createProjectChannel(this.config.channels.nameDefault(), this.config.channels.colorDefault(), projectTable.getId(), Set.of(ChannelFlag.FROZEN, ChannelFlag.PINNED));
|
||||
this.projectMemberService.addNewAcceptedByDefaultMember(ProjectRole.PROJECT_OWNER.create(projectTable.getId(), projectOwner.getUserId(), true));
|
||||
String newPageContent = newProject.getPageContent();
|
||||
if (newPageContent == null) {
|
||||
|
@ -138,7 +138,7 @@ public class ProjectService extends HangarComponent {
|
||||
|
||||
final Map<Platform, HangarVersion> mainChannelVersions = new EnumMap<>(Platform.class);
|
||||
for (final Platform platform : Platform.getValues()) {
|
||||
final HangarVersion version = getLastVersion(author, slug, platform, config.channels.getNameDefault());
|
||||
final HangarVersion version = getLastVersion(author, slug, platform, config.channels.nameDefault());
|
||||
if (version != null) {
|
||||
if (version.getPlatformDependencies().isEmpty()) {
|
||||
final Map<Platform, SortedSet<String>> platformDependencies = versionsApiDAO.getPlatformDependencies(version.getId());
|
||||
@ -246,7 +246,7 @@ public class ProjectService extends HangarComponent {
|
||||
|
||||
private void evictIconCache(String author, String slug) {
|
||||
String url = config.getBaseUrl() + "/api/internal/projects/project/" + author + "/" + slug + "/icon";
|
||||
restTemplate.delete(config.security.api.getUrl() + "/image/" + url + "?apiKey=" + config.sso.apiKey());
|
||||
restTemplate.delete(config.security.api().url() + "/image/" + url + "?apiKey=" + config.sso.apiKey());
|
||||
}
|
||||
|
||||
private String getBase64(String author, String slug, String old, String path) {
|
||||
|
@ -42,6 +42,6 @@ public class ImageService extends HangarComponent {
|
||||
}
|
||||
|
||||
public String getUserIcon(String author) {
|
||||
return String.format(config.security.api.getAvatarUrl(), author);
|
||||
return String.format(config.security.api().avatarUrl(), author);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public class ProjectFiles {
|
||||
@Autowired
|
||||
public ProjectFiles(StorageConfig storageConfig, FileService fileService) {
|
||||
this.fileService = fileService;
|
||||
Path uploadsDir = Path.of(storageConfig.getPluginUploadDir());
|
||||
Path uploadsDir = Path.of(storageConfig.workDir());
|
||||
pluginsDir = fileService.resolve(fileService.getRoot(), "plugins");
|
||||
tmpDir = uploadsDir.resolve("tmp");
|
||||
if (Files.exists(tmpDir)) {
|
||||
|
@ -113,7 +113,7 @@ public class UserService extends HangarComponent {
|
||||
HttpEntity<Traits> requestEntity = new HttpEntity<>(traits, headers);
|
||||
|
||||
try {
|
||||
ResponseEntity<Void> response = restTemplate.postForEntity(config.security.api.getUrl() + "/sync/user/" + uuid.toString() + "?apiKey=" + config.sso.apiKey(), requestEntity, Void.class);
|
||||
ResponseEntity<Void> response = restTemplate.postForEntity(config.security.api().url() + "/sync/user/" + uuid.toString() + "?apiKey=" + config.sso.apiKey(), requestEntity, Void.class);
|
||||
if (!response.getStatusCode().is2xxSuccessful()) {
|
||||
throw new ResponseStatusException(response.getStatusCode(), "Error from auth api");
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ public class DownloadService extends HangarComponent {
|
||||
|
||||
// create new token
|
||||
UUID token = UUID.randomUUID();
|
||||
OffsetDateTime expiresAt = OffsetDateTime.now().plus(config.projects.getUnsafeDownloadMaxAge().toMillis(), ChronoUnit.MILLIS);
|
||||
OffsetDateTime expiresAt = OffsetDateTime.now().plus(config.projects.unsafeDownloadMaxAge().toMillis(), ChronoUnit.MILLIS);
|
||||
projectVersionDownloadWarningsDAO.insert(new ProjectVersionDownloadWarningTable(
|
||||
expiresAt,
|
||||
token,
|
||||
|
@ -116,7 +116,7 @@ public class VersionDependencyService extends HangarComponent {
|
||||
|
||||
@Transactional
|
||||
public void updateVersionPluginDependencies(long projectId, long versionId, UpdatePluginDependencies form) {
|
||||
if (form.getPluginDependencies().size() > config.projects.getMaxDependencies()) {
|
||||
if (form.getPluginDependencies().size() > config.projects.maxDependencies()) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "version.new.error.tooManyDependencies");
|
||||
}
|
||||
|
||||
|
@ -246,7 +246,7 @@ public class VersionFactory extends HangarComponent {
|
||||
if (!validationService.isValidVersionName(pendingVersion.getVersionString())) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "version.new.error.invalidName");
|
||||
}
|
||||
if (pendingVersion.getPluginDependencies().values().stream().anyMatch(pluginDependencies -> pluginDependencies.size() > config.projects.getMaxDependencies())) {
|
||||
if (pendingVersion.getPluginDependencies().values().stream().anyMatch(pluginDependencies -> pluginDependencies.size() > config.projects.maxDependencies())) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "version.new.error.tooManyDependencies");
|
||||
}
|
||||
if (exists(projectId, pendingVersion.getVersionString())) {
|
||||
|
@ -1,11 +1,10 @@
|
||||
package io.papermc.hangar.tasks;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import io.papermc.hangar.service.internal.admin.StatService;
|
||||
import io.papermc.hangar.service.internal.projects.ProjectService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class DbUpdateTask {
|
||||
@ -14,19 +13,19 @@ public class DbUpdateTask {
|
||||
private final StatService statService;
|
||||
|
||||
@Autowired
|
||||
public DbUpdateTask(ProjectService projectService, StatService statService) {
|
||||
public DbUpdateTask(final ProjectService projectService, final StatService statService) {
|
||||
this.projectService = projectService;
|
||||
this.statService = statService;
|
||||
}
|
||||
|
||||
@Scheduled(fixedRateString = "#{@hangarConfig.homepage.updateInterval.toMillis()}")
|
||||
public void refreshHomePage() {
|
||||
projectService.refreshHomeProjects();
|
||||
this.projectService.refreshHomeProjects();
|
||||
}
|
||||
|
||||
@Scheduled(fixedRateString = "#{@hangarConfig.homepage.updateInterval.toMillis()}", initialDelay = 1000)
|
||||
public void updateStats() {
|
||||
statService.processProjectViews();
|
||||
statService.processVersionDownloads();
|
||||
this.statService.processProjectViews();
|
||||
this.statService.processVersionDownloads();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package io.papermc.hangar.util;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public record PatternWrapper(Pattern pattern, Predicate<String> matcherPredicate) implements Predicate<String> {
|
||||
|
||||
public PatternWrapper(final Pattern pattern) {
|
||||
this(pattern, pattern.asPredicate());
|
||||
}
|
||||
|
||||
public String strPattern() {
|
||||
return this.pattern.pattern();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(final String s) {
|
||||
return this.matcherPredicate.test(s);
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class StringUtils {
|
||||
public final class StringUtils {
|
||||
|
||||
private StringUtils() {
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user