Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add API to obtain the bundled js file for all enabled plugins #3444

Merged
merged 17 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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 @@ -19,8 +19,8 @@
*/
public abstract class BundleResourceUtils {
private static final String CONSOLE_BUNDLE_LOCATION = "console";
private static final String JS_BUNDLE = "main.js";
private static final String CSS_BUNDLE = "style.css";
public static final String JS_BUNDLE = "main.js";
public static final String CSS_BUNDLE = "style.css";

/**
* Gets plugin css bundle resource path relative to the plugin classpath if exists.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ rules:
- apiGroups: [ "api.console.halo.run" ]
resources: [ "auth-providers" ]
verbs: [ "list" ]
- apiGroups: [ "api.console.halo.run" ]
resources: [ "plugins/bundle.js", "plugins/bundle.css" ]
resourceNames: [ "-" ]
verbs: [ "get" ]
---
apiVersion: v1alpha1
kind: "Role"
Expand Down
1 change: 1 addition & 0 deletions console/docs/extension-points/editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default definePlugin({
{
name: "markdown-editor",
displayName: "Markdown",
logo: "logo.png"
component: markRaw(MarkdownEditor),
rawType: "markdown",
},
Expand Down
1 change: 1 addition & 0 deletions console/packages/shared/src/states/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Component } from "vue";
export interface EditorProvider {
name: string;
displayName: string;
logo?: string;
component: Component;
rawType: string;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
<script lang="ts" setup>
import {
useEditorExtensionPoints,
type EditorProvider,
} from "@/composables/use-editor-extension-points";
import { useEditorExtensionPoints } from "@/composables/use-editor-extension-points";
import type { EditorProvider } from "@halo-dev/console-shared";
import {
VAvatar,
IconExchange,
Expand Down Expand Up @@ -51,7 +49,7 @@ const { editorProviders } = useEditorExtensionPoints();
"
@click="emit('select', editorProvider)"
>
<template #prefix-icon>
<template v-if="editorProvider.logo" #prefix-icon>
<VAvatar :src="editorProvider.logo" size="xs"></VAvatar>
</template>
{{ editorProvider.displayName }}
Expand Down
3 changes: 2 additions & 1 deletion console/src/components/editor/DefaultEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ import { useFetchAttachmentPolicy } from "@/modules/contents/attachments/composa
import { useI18n } from "vue-i18n";
import { i18n } from "@/locales";
import { OverlayScrollbarsComponent } from "overlayscrollbars-vue";
import { usePluginModuleStore, type PluginModule } from "@/stores/plugin";
import { usePluginModuleStore } from "@/stores/plugin";
import type { PluginModule } from "@halo-dev/console-shared";
import { useDebounceFn } from "@vueuse/core";

const { t } = useI18n();
Expand Down
16 changes: 2 additions & 14 deletions console/src/composables/use-editor-extension-points.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { usePluginModuleStore } from "@/stores/plugin";
import type { EditorProvider as EditorProviderRaw } from "@halo-dev/console-shared";
import type { PluginModule } from "@/stores/plugin";
import type { EditorProvider, PluginModule } from "@halo-dev/console-shared";
import { onMounted, ref, type Ref, defineAsyncComponent } from "vue";
import { VLoading } from "@halo-dev/components";
import Logo from "@/assets/logo.png";
import { useI18n } from "vue-i18n";

export interface EditorProvider extends EditorProviderRaw {
logo?: string;
}

interface useEditorExtensionPointsReturn {
editorProviders: Ref<EditorProvider[]>;
}
Expand Down Expand Up @@ -42,14 +37,7 @@ export function useEditorExtensionPoints(): useEditorExtensionPointsReturn {

const providers = extensionPoints["editor:create"]() as EditorProvider[];

if (providers) {
providers.forEach((provider) => {
editorProviders.value.push({
...provider,
logo: pluginModule.extension.status?.logo,
});
});
}
editorProviders.value.push(...providers);
});
});

Expand Down
4 changes: 2 additions & 2 deletions console/src/locales/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -786,8 +786,8 @@ core:
last_starttime: Last Start Time
loader:
toast:
entry_load_failed: "{name}: Failed to load plugin entry file"
style_load_failed: "{name}: Failed to load plugin stylesheet file"
entry_load_failed: "Failed to load plugins entry file"
style_load_failed: "Failed to load plugins stylesheet file"
extension_points:
editor:
providers:
Expand Down
4 changes: 2 additions & 2 deletions console/src/locales/zh-CN.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -786,8 +786,8 @@ core:
last_starttime: 最近一次启动
loader:
toast:
entry_load_failed: "{name}:加载插件入口文件失败"
style_load_failed: "{name}:加载插件样式文件失败"
entry_load_failed: "加载插件入口文件失败"
style_load_failed: "加载插件样式文件失败"
extension_points:
editor:
providers:
Expand Down
4 changes: 2 additions & 2 deletions console/src/locales/zh-TW.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -786,8 +786,8 @@ core:
last_starttime: 最近一次啟動
loader:
toast:
entry_load_failed: "{name}:讀取插件入口文件失敗"
style_load_failed: "{name}:讀取插件樣式文件失敗"
entry_load_failed: "讀取插件入口文件失敗"
style_load_failed: "讀取插件樣式文件失敗"
extension_points:
editor:
providers:
Expand Down
Loading