Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into feature/notification
Browse files Browse the repository at this point in the history
  • Loading branch information
guqing committed Aug 31, 2023
2 parents 11fa007 + 58eac2e commit 0c58b31
Show file tree
Hide file tree
Showing 118 changed files with 4,271 additions and 2,142 deletions.
2 changes: 1 addition & 1 deletion api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ dependencies {
api "org.apache.lucene:lucene-queryparser"
api "org.apache.lucene:lucene-highlighter"
api "org.apache.lucene:lucene-backward-codecs"
api 'cn.shenyanchao.ik-analyzer:ik-analyzer'
api 'org.apache.lucene:lucene-analysis-common'

api "org.apache.commons:commons-lang3"
api "io.seruco.encoding:base62"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package run.halo.app.infra.utils;

import java.io.IOException;
import java.util.function.Supplier;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.type.TypeDescription;
import reactor.core.Exceptions;

public enum GenericClassUtils {
;
Expand Down Expand Up @@ -40,9 +38,6 @@ public static <T> Class<?> generateConcreteClass(Class<?> rawClass, Class<T> par
.name(nameGenerator.get())
.make()) {
return unloaded.load(parameterType.getClassLoader()).getLoaded();
} catch (IOException e) {
// Should never happen
throw Exceptions.propagate(e);
}
}
}
2 changes: 1 addition & 1 deletion application/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id 'org.springframework.boot' version '3.1.2'
id 'org.springframework.boot' version '3.1.3'
id 'io.spring.dependency-management' version '1.1.0'
id "com.gorylenko.gradle-git-properties" version "2.3.2"
id "checkstyle"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import lombok.AllArgsConstructor;
Expand Down Expand Up @@ -217,10 +218,61 @@ public RouterFunction<ServerResponse> endpoint() {
builder -> builder.operationId("ListPluginPresets")
.description("List all plugin presets in the system.")
.tag(tag)
.response(responseBuilder().implementationArray(Plugin.class)))
.response(responseBuilder().implementationArray(Plugin.class))
)
.GET("plugins/-/bundle.js", this::fetchJsBundle,
builder -> builder.operationId("fetchJsBundle")
.description("Merge all JS bundles of enabled plugins into one.")
.tag(tag)
.response(responseBuilder().implementation(String.class))
)
.GET("plugins/-/bundle.css", this::fetchCssBundle,
builder -> builder.operationId("fetchCssBundle")
.description("Merge all CSS bundles of enabled plugins into one.")
.tag(tag)
.response(responseBuilder().implementation(String.class))
)
.build();
}

private Mono<ServerResponse> fetchJsBundle(ServerRequest request) {
Optional<String> versionOption = request.queryParam("v");
if (versionOption.isEmpty()) {
return pluginService.generateJsBundleVersion()
.flatMap(v -> ServerResponse
.temporaryRedirect(buildJsBundleUri("js", v))
.build()
);
}
return pluginService.uglifyJsBundle()
.defaultIfEmpty("")
.flatMap(bundle -> ServerResponse.ok()
.contentType(MediaType.valueOf("text/javascript"))
.bodyValue(bundle)
);
}

private Mono<ServerResponse> fetchCssBundle(ServerRequest request) {
Optional<String> versionOption = request.queryParam("v");
if (versionOption.isEmpty()) {
return pluginService.generateJsBundleVersion()
.flatMap(v -> ServerResponse
.temporaryRedirect(buildJsBundleUri("css", v))
.build()
);
}
return pluginService.uglifyCssBundle()
.flatMap(bundle -> ServerResponse.ok()
.contentType(MediaType.valueOf("text/css"))
.bodyValue(bundle)
);
}

URI buildJsBundleUri(String type, String version) {
return URI.create(
"/apis/api.console.halo.run/v1alpha1/plugins/-/bundle." + type + "?v=" + version);
}

private Mono<ServerResponse> upgradeFromUri(ServerRequest request) {
var name = request.pathVariable("name");
var content = request.bodyToMono(UpgradeFromUriRequest.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.fn.builders.requestbody.Builder;
Expand Down Expand Up @@ -556,37 +554,39 @@ public Sort getSort() {
return SortResolver.defaultInstance.resolve(exchange);
}

/**
* Converts query parameters to user predicate.
*
* @return user predicate to filter users
*/
public Predicate<User> toPredicate() {
Predicate<User> displayNamePredicate = user -> {
Predicate<User> keywordPredicate = user -> {
var keyword = getKeyword();
if (!org.springframework.util.StringUtils.hasText(keyword)) {
if (StringUtils.isBlank(keyword)) {
return true;
}
var username = user.getMetadata().getName();
var displayName = user.getSpec().getDisplayName();
if (!org.springframework.util.StringUtils.hasText(displayName)) {
return false;
}
return displayName.toLowerCase().contains(keyword.trim().toLowerCase());
return StringUtils.containsIgnoreCase(displayName, keyword)
|| keyword.equalsIgnoreCase(username);
};

Predicate<User> rolePredicate = user -> {
var role = getRole();
if (role == null) {
var roleName = getRole();
if (StringUtils.isBlank(roleName)) {
return true;
}
var annotations = user.getMetadata().getAnnotations();
if (annotations == null || !annotations.containsKey(User.ROLE_NAMES_ANNO)) {
var roleNamesAnno = MetadataUtil.nullSafeAnnotations(user)
.get(User.ROLE_NAMES_ANNO);
if (StringUtils.isBlank(roleNamesAnno)) {
return false;
} else {
Pattern pattern = Pattern.compile("\\[\"([^\"]*)\"\\]");
Matcher matcher = pattern.matcher(annotations.get(User.ROLE_NAMES_ANNO));
if (matcher.find()) {
return matcher.group(1).equals(role);
} else {
return false;
}
}
Set<String> roleNames = JsonUtils.jsonToObject(roleNamesAnno,
new TypeReference<>() {
});
return roleNames.contains(roleName);
};
return displayNamePredicate
return keywordPredicate
.and(rolePredicate)
.and(labelAndFieldSelectorToPredicate(getLabelSelector(), getFieldSelector()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import run.halo.app.extension.controller.Reconciler.Request;
import run.halo.app.infra.Condition;
import run.halo.app.infra.ConditionStatus;
import run.halo.app.infra.utils.FileUtils;
import run.halo.app.infra.utils.JsonUtils;
import run.halo.app.infra.utils.PathUtils;
import run.halo.app.infra.utils.YamlUnstructuredLoader;
Expand Down Expand Up @@ -183,13 +184,6 @@ Optional<Setting> lookupPluginSetting(String name, String settingName) {
Assert.notNull(name, "Plugin name must not be null");
Assert.notNull(settingName, "Setting name must not be null");
PluginWrapper pluginWrapper = getPluginWrapper(name);
// If it already exists, do not look for setting
if (RuntimeMode.DEPLOYMENT.equals(pluginWrapper.getRuntimeMode())) {
Optional<Setting> existing = client.fetch(Setting.class, settingName);
if (existing.isPresent()) {
return existing;
}
}

var resourceLoader =
new DefaultResourceLoader(pluginWrapper.getPluginClassLoader());
Expand Down Expand Up @@ -391,9 +385,11 @@ void updateStatus(String name, UnaryOperator<Plugin.PluginStatus> operator) {
return null;
});
} catch (Exception e) {
haloPluginManager.stopPlugin(name);
PluginWrapper pluginWrapper = haloPluginManager.getPlugin(name);
pluginWrapper.setPluginState(PluginState.FAILED);
if (pluginWrapper != null) {
haloPluginManager.stopPlugin(name);
pluginWrapper.setPluginState(PluginState.FAILED);
}
throw e;
}
}
Expand Down Expand Up @@ -614,7 +610,11 @@ String buildPluginLocation(String name, String pluginPathString) {
}
return pluginPath.toString();
}
return PathUtils.combinePath(pluginsRoot.toString(), pluginPath.toString());
var result = pluginsRoot.resolve(pluginPath);
if (!isDevelopmentMode(name)) {
FileUtils.checkDirectoryTraversal(pluginsRoot, result);
}
return result.toString();
}

boolean shouldDeleteFile(String newPluginPath, URI oldPluginLocation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,26 @@ public interface PluginService {
* @see run.halo.app.plugin.HaloPluginManager#reloadPlugin(String)
*/
Mono<Plugin> reload(String name);

/**
* Uglify js bundle from all enabled plugins to a single js bundle string.
*
* @return uglified js bundle
*/
Mono<String> uglifyJsBundle();

/**
* Uglify css bundle from all enabled plugins to a single css bundle string.
*
* @return uglified css bundle
*/
Mono<String> uglifyCssBundle();

/**
* <p>Generate js bundle version for cache control.</p>
* This method will list all enabled plugins version and sign it to a string.
*
* @return signed js bundle version by all enabled plugins version.
*/
Mono<String> generateJsBundleVersion();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

import com.github.zafarkhaja.semver.Version;
import com.google.common.hash.Hashing;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.Validate;
Expand Down Expand Up @@ -37,6 +43,7 @@
import run.halo.app.plugin.PluginProperties;
import run.halo.app.plugin.PluginUtils;
import run.halo.app.plugin.YamlPluginFinder;
import run.halo.app.plugin.resources.BundleResourceUtils;

@Slf4j
@Component
Expand Down Expand Up @@ -124,6 +131,70 @@ public Mono<Plugin> reload(String name) {
return updateReloadAnno(name, pluginWrapper.getPluginPath());
}

@Override
public Mono<String> uglifyJsBundle() {
return Mono.fromSupplier(() -> {
StringBuilder jsBundle = new StringBuilder();
List<String> pluginNames = new ArrayList<>();
for (PluginWrapper pluginWrapper : pluginManager.getStartedPlugins()) {
String pluginName = pluginWrapper.getPluginId();
pluginNames.add(pluginName);
Resource jsBundleResource =
BundleResourceUtils.getJsBundleResource(pluginManager, pluginName,
BundleResourceUtils.JS_BUNDLE);
if (jsBundleResource != null) {
try {
jsBundle.append(
jsBundleResource.getContentAsString(StandardCharsets.UTF_8));
jsBundle.append("\n");
} catch (IOException e) {
log.error("Failed to read js bundle of plugin [{}]", pluginName, e);
}
}
}

String plugins = """
this.enabledPluginNames = [%s];
""".formatted(pluginNames.stream()
.collect(Collectors.joining("','", "'", "'")));
return jsBundle + plugins;
});
}

@Override
public Mono<String> uglifyCssBundle() {
return Mono.fromSupplier(() -> {
StringBuilder cssBundle = new StringBuilder();
for (PluginWrapper pluginWrapper : pluginManager.getStartedPlugins()) {
String pluginName = pluginWrapper.getPluginId();
Resource cssBundleResource =
BundleResourceUtils.getJsBundleResource(pluginManager, pluginName,
BundleResourceUtils.CSS_BUNDLE);
if (cssBundleResource != null) {
try {
cssBundle.append(
cssBundleResource.getContentAsString(StandardCharsets.UTF_8));
} catch (IOException e) {
log.error("Failed to read css bundle of plugin [{}]", pluginName, e);
}
}
}
return cssBundle.toString();
});
}

@Override
public Mono<String> generateJsBundleVersion() {
return Mono.fromSupplier(() -> {
var compactVersion = pluginManager.getStartedPlugins()
.stream()
.sorted(Comparator.comparing(PluginWrapper::getPluginId))
.map(pluginWrapper -> pluginWrapper.getDescriptor().getVersion())
.collect(Collectors.joining());
return Hashing.sha256().hashUnencodedChars(compactVersion).toString();
});
}

Mono<Plugin> findPluginManifest(Path path) {
return Mono.fromSupplier(
() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,25 @@ public Mono<Theme> persistent(Unstructured themeManifest) {
})
.doOnNext(unstructured ->
populateThemeNameLabel(unstructured, theme.getMetadata().getName()))
.flatMap(unstructured -> client.create(unstructured)
.retryWhen(Retry.backoff(5, Duration.ofMillis(100))
.filter(OptimisticLockingFailureException.class::isInstance))
)
.flatMap(this::createOrUpdate)
.then(Mono.just(theme));
});
}

Mono<Unstructured> createOrUpdate(Unstructured unstructured) {
return Mono.defer(() -> client.fetch(unstructured.groupVersionKind(),
unstructured.getMetadata().getName())
.flatMap(existUnstructured -> {
existUnstructured.getMetadata()
.setVersion(unstructured.getMetadata().getVersion());
return client.update(existUnstructured);
})
.switchIfEmpty(Mono.defer(() -> client.create(unstructured)))
)
.retryWhen(Retry.backoff(5, Duration.ofMillis(100))
.filter(OptimisticLockingFailureException.class::isInstance));
}

@Override
public Mono<Theme> reloadTheme(String name) {
return client.fetch(Theme.class, name)
Expand Down
Loading

0 comments on commit 0c58b31

Please sign in to comment.