Skip to content

Commit

Permalink
Merge branch 'main' into feature/3016
Browse files Browse the repository at this point in the history
  • Loading branch information
ruibaby committed Aug 18, 2023
2 parents c7acb35 + 43e1e44 commit 6d6d358
Show file tree
Hide file tree
Showing 107 changed files with 3,286 additions and 1,954 deletions.
14 changes: 14 additions & 0 deletions api/src/main/java/run/halo/app/infra/BackupRootGetter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package run.halo.app.infra;

import java.nio.file.Path;
import java.util.function.Supplier;

/**
* Utility of getting backup root path.
*
* @author johnniang
* @since 2.9.0
*/
public interface BackupRootGetter extends Supplier<Path> {

}
4 changes: 4 additions & 0 deletions api/src/main/java/run/halo/app/infra/utils/JsonUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ public class JsonUtils {
private JsonUtils() {
}

public static ObjectMapper mapper() {
return DEFAULT_JSON_MAPPER;
}

/**
* Converts a map to the object specified type.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.stereotype.Component;
import run.halo.app.extension.ConfigMap;
import run.halo.app.infra.InitializationStateGetter;
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
import run.halo.app.infra.SystemSetting;
import run.halo.app.infra.SystemSetting.Basic;
Expand All @@ -33,13 +34,19 @@ public class GlobalInfoEndpoint {

private final AuthProviderService authProviderService;

private final InitializationStateGetter initializationStateGetter;

@ReadOperation
public GlobalInfo globalInfo() {
final var info = new GlobalInfo();
info.setExternalUrl(haloProperties.getExternalUrl());
info.setUseAbsolutePermalink(haloProperties.isUseAbsolutePermalink());
info.setLocale(Locale.getDefault());
info.setTimeZone(TimeZone.getDefault());
info.setUserInitialized(initializationStateGetter.userInitialized()
.blockOptional().orElse(false));
info.setDataInitialized(initializationStateGetter.dataInitialized()
.blockOptional().orElse(false));
handleSocialAuthProvider(info);
systemConfigFetcher.ifAvailable(fetcher -> fetcher.getConfigMapBlocking()
.ifPresent(configMap -> {
Expand Down Expand Up @@ -70,6 +77,10 @@ public static class GlobalInfo {

private String favicon;

private boolean userInitialized;

private boolean dataInitialized;

private List<SocialAuthProvider> socialAuthProviders;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
Expand All @@ -27,13 +26,11 @@
import org.springframework.web.reactive.function.server.ServerResponse;
import run.halo.app.core.extension.service.RoleService;
import run.halo.app.core.extension.service.UserService;
import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.infra.AnonymousUserConst;
import run.halo.app.infra.properties.HaloProperties;
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
import run.halo.app.security.DefaultUserDetailService;
import run.halo.app.security.DynamicMatcherSecurityWebFilterChain;
import run.halo.app.security.SuperAdminInitializer;
import run.halo.app.security.authentication.SecurityConfigurer;
import run.halo.app.security.authentication.login.CryptoService;
import run.halo.app.security.authentication.login.PublicKeyRouteBuilder;
Expand Down Expand Up @@ -127,16 +124,6 @@ PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

@Bean
@ConditionalOnProperty(name = "halo.security.initializer.disabled",
havingValue = "false",
matchIfMissing = true)
SuperAdminInitializer superAdminInitializer(ReactiveExtensionClient client,
HaloProperties halo) {
return new SuperAdminInitializer(client, passwordEncoder(),
halo.getSecurity().getInitializer());
}

@Bean
RouterFunction<ServerResponse> publicKeyRoute(CryptoService cryptoService) {
return new PublicKeyRouteBuilder(cryptoService).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder;
import static org.springdoc.core.fn.builders.schema.Builder.schemaBuilder;
import static org.springframework.boot.convert.ApplicationConversionService.getSharedInstance;
import static org.springframework.core.io.buffer.DataBufferUtils.write;
import static org.springframework.web.reactive.function.server.RequestPredicates.contentType;
import static run.halo.app.extension.ListResult.generateGenericClass;
import static run.halo.app.extension.router.QueryParamBuildUtil.buildParametersFromType;
import static run.halo.app.extension.router.selector.SelectorUtil.labelAndFieldSelectorToPredicate;
import static run.halo.app.infra.utils.FileUtils.deleteFileSilently;

import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -33,15 +33,16 @@
import java.util.function.Predicate;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.domain.Sort;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.FormFieldPart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.server.RouterFunction;
Expand All @@ -50,6 +51,7 @@
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;
import reactor.util.retry.Retry;
import run.halo.app.core.extension.Plugin;
Expand All @@ -61,9 +63,6 @@
import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.router.IListRequest.QueryListRequest;
import run.halo.app.infra.ReactiveUrlDataBufferFetcher;
import run.halo.app.infra.exception.ThemeInstallationException;
import run.halo.app.infra.exception.ThemeUpgradeException;
import run.halo.app.infra.utils.DataBufferUtils;
import run.halo.app.plugin.PluginNotFoundException;

@Slf4j
Expand All @@ -77,6 +76,8 @@ public class PluginEndpoint implements CustomEndpoint {

private final ReactiveUrlDataBufferFetcher reactiveUrlDataBufferFetcher;

private final Scheduler scheduler = Schedulers.boundedElastic();

@Override
public RouterFunction<ServerResponse> endpoint() {
final var tag = "api.console.halo.run/v1alpha1/Plugin";
Expand Down Expand Up @@ -221,48 +222,29 @@ public RouterFunction<ServerResponse> endpoint() {
}

private Mono<ServerResponse> upgradeFromUri(ServerRequest request) {
final var name = request.pathVariable("name");
return request.bodyToMono(UpgradeFromUriRequest.class)
.flatMap(upgradeRequest -> Mono.fromCallable(() -> DataBufferUtils.toInputStream(
reactiveUrlDataBufferFetcher.fetch(upgradeRequest.uri())))
)
.subscribeOn(Schedulers.boundedElastic())
.onErrorMap(throwable -> {
log.error("Failed to fetch plugin file from uri.", throwable);
return new ThemeUpgradeException("Failed to fetch plugin file from uri.", null,
null);
})
.flatMap(inputStream -> Mono.usingWhen(
transferToTemp(inputStream),
(path) -> pluginService.upgrade(name, path),
var name = request.pathVariable("name");
var content = request.bodyToMono(UpgradeFromUriRequest.class)
.map(UpgradeFromUriRequest::uri)
.flatMapMany(reactiveUrlDataBufferFetcher::fetch);

return Mono.usingWhen(
writeToTempFile(content),
path -> pluginService.upgrade(name, path),
this::deleteFileIfExists)
)
.flatMap(theme -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(theme)
);
.flatMap(upgradedPlugin -> ServerResponse.ok().bodyValue(upgradedPlugin));
}

private Mono<ServerResponse> installFromUri(ServerRequest request) {
return request.bodyToMono(InstallFromUriRequest.class)
.flatMap(installRequest -> Mono.fromCallable(() -> DataBufferUtils.toInputStream(
reactiveUrlDataBufferFetcher.fetch(installRequest.uri())))
)
.subscribeOn(Schedulers.boundedElastic())
.doOnError(throwable -> {
log.error("Failed to fetch plugin file from uri.", throwable);
throw new ThemeInstallationException("Failed to fetch plugin file from uri.", null,
null);
})
.flatMap(inputStream -> Mono.usingWhen(
transferToTemp(inputStream),
var content = request.bodyToMono(InstallFromUriRequest.class)
.map(InstallFromUriRequest::uri)
.flatMapMany(reactiveUrlDataBufferFetcher::fetch);

return Mono.usingWhen(
writeToTempFile(content),
pluginService::install,
this::deleteFileIfExists)
this::deleteFileIfExists
)
.flatMap(theme -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(theme)
);
.flatMap(newPlugin -> ServerResponse.ok().bodyValue(newPlugin));
}

public record InstallFromUriRequest(@Schema(requiredMode = REQUIRED) URI uri) {
Expand Down Expand Up @@ -402,10 +384,12 @@ private Mono<ServerResponse> upgrade(ServerRequest request) {
.bodyValue(upgradedPlugin));
}

private Mono<Plugin> installFromFile(Mono<FilePart> filePartMono,
private Mono<Plugin> installFromFile(FilePart filePart,
Function<Path, Mono<Plugin>> resourceClosure) {
var pathMono = filePartMono.flatMap(this::transferToTemp);
return Mono.usingWhen(pathMono, resourceClosure, this::deleteFileIfExists);
return Mono.usingWhen(
writeToTempFile(filePart.content()),
resourceClosure,
this::deleteFileIfExists);
}

private Mono<Plugin> installFromPreset(Mono<String> presetNameMono,
Expand Down Expand Up @@ -529,19 +513,18 @@ public InstallRequest(MultiValueMap<String, Part> multipartData) {
}

@Schema(requiredMode = NOT_REQUIRED, description = "Plugin Jar file.")
public Mono<FilePart> getFile() {
public FilePart getFile() {
var part = multipartData.getFirst("file");
if (part == null) {
return Mono.error(new ServerWebInputException("Form field file is required"));
throw new ServerWebInputException("Form field file is required");
}
if (!(part instanceof FilePart file)) {
return Mono.error(new ServerWebInputException("Invalid parameter of file"));
throw new ServerWebInputException("Invalid parameter of file");
}
if (!Paths.get(file.filename()).toString().endsWith(".jar")) {
return Mono.error(
new ServerWebInputException("Invalid file type, only jar is supported"));
throw new ServerWebInputException("Invalid file type, only jar is supported");
}
return Mono.just(file);
return file;
}

@Schema(requiredMode = NOT_REQUIRED,
Expand Down Expand Up @@ -584,29 +567,13 @@ public enum InstallSource {
}

Mono<Void> deleteFileIfExists(Path path) {
return Mono.fromRunnable(() -> {
try {
Files.deleteIfExists(path);
} catch (IOException e) {
// ignore this error
log.warn("Failed to delete temporary jar file: {}", path, e);
}
}).subscribeOn(Schedulers.boundedElastic()).then();
return deleteFileSilently(path, this.scheduler).then();
}

private Mono<Path> transferToTemp(FilePart filePart) {
return Mono.fromCallable(() -> Files.createTempFile("halo-plugins", ".jar"))
.subscribeOn(Schedulers.boundedElastic())
.flatMap(path -> filePart.transferTo(path)
.thenReturn(path)
);
private Mono<Path> writeToTempFile(Publisher<DataBuffer> content) {
return Mono.fromCallable(() -> Files.createTempFile("halo-plugin-", ".jar"))
.flatMap(path -> write(content, path).thenReturn(path))
.subscribeOn(this.scheduler);
}

private Mono<Path> transferToTemp(InputStream inputStream) {
return Mono.fromCallable(() -> {
Path tempFile = Files.createTempFile("halo-plugins", ".jar");
FileCopyUtils.copy(inputStream, Files.newOutputStream(tempFile));
return tempFile;
}).subscribeOn(Schedulers.boundedElastic());
}
}

0 comments on commit 6d6d358

Please sign in to comment.