mirror of https://github.com/halo-dev/halo
refactor: add version flag to plugin static assets (#3381)
#### What type of PR is this? /kind improvement /area core /milestone 2.3.x #### What this PR does / why we need it: 对插件的静态资源(js, css) 增加 version 版本号,用于解决插件静态资源缓存的问题。另外同时也对插件 Logo 增加了版本号标识。 #### Which issue(s) this PR fixes: Fixes #3205 #### Special notes for your reviewer: 验证 Console 端插件静态资源链接是否携带 `version={pluginVersion}` 。 特别的,对于 logo只会增加相对路径下的地址,绝对路径的地址将直接返回原始路径/值。 #### Does this PR introduce a user-facing change? ```release-note 为插件静态资源路径增加版本标识以解决缓存更新不及时的问题 ```pull/3363/head^2
parent
c8f3229cd6
commit
3330ff8c3d
|
@ -29,9 +29,11 @@ import org.springframework.context.ApplicationEventPublisher;
|
|||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import run.halo.app.core.extension.Plugin;
|
||||
import run.halo.app.core.extension.ReverseProxy;
|
||||
import run.halo.app.core.extension.Setting;
|
||||
|
@ -108,20 +110,12 @@ public class PluginReconciler implements Reconciler<Request> {
|
|||
}
|
||||
createInitialReverseProxyIfNotPresent(plugin);
|
||||
|
||||
Plugin.PluginStatus status = plugin.statusNonNull();
|
||||
|
||||
// filled logo path
|
||||
String logo = plugin.getSpec().getLogo();
|
||||
if (PathUtils.isAbsoluteUri(logo)) {
|
||||
status.setLogo(logo);
|
||||
} else {
|
||||
String assetsPrefix =
|
||||
PluginConst.assertsRoutePrefix(plugin.getMetadata().getName());
|
||||
status.setLogo(PathUtils.combinePath(assetsPrefix, logo));
|
||||
}
|
||||
generateAccessibleLogoUrl(plugin);
|
||||
|
||||
// update phase
|
||||
PluginWrapper pluginWrapper = getPluginWrapper(name);
|
||||
Plugin.PluginStatus status = plugin.statusNonNull();
|
||||
status.setPhase(pluginWrapper.getPluginState());
|
||||
updateStatus(plugin.getMetadata().getName(), status);
|
||||
return false;
|
||||
|
@ -129,6 +123,23 @@ public class PluginReconciler implements Reconciler<Request> {
|
|||
.orElse(false);
|
||||
}
|
||||
|
||||
void generateAccessibleLogoUrl(Plugin plugin) {
|
||||
String logo = plugin.getSpec().getLogo();
|
||||
if (StringUtils.isBlank(logo)) {
|
||||
return;
|
||||
}
|
||||
Plugin.PluginStatus status = plugin.statusNonNull();
|
||||
if (PathUtils.isAbsoluteUri(logo)) {
|
||||
status.setLogo(logo);
|
||||
} else {
|
||||
String assetsPrefix =
|
||||
PluginConst.assertsRoutePrefix(plugin.getMetadata().getName());
|
||||
String versionedLogo =
|
||||
applyVersioningToStaticResource(logo, plugin.getSpec().getVersion());
|
||||
status.setLogo(PathUtils.combinePath(assetsPrefix, versionedLogo));
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
@ -374,12 +385,15 @@ public class PluginReconciler implements Reconciler<Request> {
|
|||
|
||||
plugin.statusNonNull().setLastStartTime(Instant.now());
|
||||
|
||||
final String pluginVersion = plugin.getSpec().getVersion();
|
||||
String jsBundlePath =
|
||||
BundleResourceUtils.getJsBundlePath(haloPluginManager, name);
|
||||
jsBundlePath = applyVersioningToStaticResource(jsBundlePath, pluginVersion);
|
||||
status.setEntry(jsBundlePath);
|
||||
|
||||
String cssBundlePath =
|
||||
BundleResourceUtils.getCssBundlePath(haloPluginManager, name);
|
||||
cssBundlePath = applyVersioningToStaticResource(cssBundlePath, pluginVersion);
|
||||
status.setStylesheet(cssBundlePath);
|
||||
|
||||
status.setPhase(currentState);
|
||||
|
@ -398,6 +412,15 @@ public class PluginReconciler implements Reconciler<Request> {
|
|||
});
|
||||
}
|
||||
|
||||
private String applyVersioningToStaticResource(@Nullable String path, String pluginVersion) {
|
||||
if (StringUtils.isNotBlank(path)) {
|
||||
return UriComponentsBuilder.fromUriString(path)
|
||||
.queryParam("version", pluginVersion)
|
||||
.build().toString();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
PluginStartingError getStaringErrorInfo(String name) {
|
||||
PluginStartingError startingError =
|
||||
haloPluginManager.getPluginStartingError(name);
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.util.Optional;
|
|||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
@ -287,6 +288,78 @@ class PluginReconcilerTest {
|
|||
verify(extensionClient, times(1)).update(any());
|
||||
}
|
||||
|
||||
@Nested
|
||||
class PluginLogoTest {
|
||||
|
||||
@Test
|
||||
void absoluteUri() {
|
||||
Plugin plugin = new Plugin();
|
||||
plugin.setSpec(new Plugin.PluginSpec());
|
||||
plugin.getSpec().setLogo("https://example.com/logo.png");
|
||||
plugin.getSpec().setVersion("1.0.0");
|
||||
pluginReconciler.generateAccessibleLogoUrl(plugin);
|
||||
assertThat(plugin.statusNonNull().getLogo())
|
||||
.isEqualTo("https://example.com/logo.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
void absoluteUriWithQueryParam() {
|
||||
Plugin plugin = new Plugin();
|
||||
plugin.setSpec(new Plugin.PluginSpec());
|
||||
plugin.getSpec().setLogo("https://example.com/logo.png?hello=world");
|
||||
plugin.getSpec().setVersion("1.0.0");
|
||||
pluginReconciler.generateAccessibleLogoUrl(plugin);
|
||||
assertThat(plugin.statusNonNull().getLogo())
|
||||
.isEqualTo("https://example.com/logo.png?hello=world");
|
||||
}
|
||||
|
||||
@Test
|
||||
void logoIsNull() {
|
||||
Plugin plugin = new Plugin();
|
||||
plugin.setSpec(new Plugin.PluginSpec());
|
||||
plugin.getSpec().setLogo(null);
|
||||
plugin.getSpec().setVersion("1.0.0");
|
||||
pluginReconciler.generateAccessibleLogoUrl(plugin);
|
||||
assertThat(plugin.statusNonNull().getLogo()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void logoIsEmpty() {
|
||||
Plugin plugin = new Plugin();
|
||||
plugin.setSpec(new Plugin.PluginSpec());
|
||||
plugin.getSpec().setLogo("");
|
||||
plugin.getSpec().setVersion("1.0.0");
|
||||
pluginReconciler.generateAccessibleLogoUrl(plugin);
|
||||
assertThat(plugin.statusNonNull().getLogo()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void relativePath() {
|
||||
Plugin plugin = new Plugin();
|
||||
plugin.setSpec(new Plugin.PluginSpec());
|
||||
plugin.setMetadata(new Metadata());
|
||||
plugin.getMetadata().setName("fake-plugin");
|
||||
plugin.getSpec().setLogo("/static/logo.jpg");
|
||||
plugin.getSpec().setVersion("1.0.0");
|
||||
pluginReconciler.generateAccessibleLogoUrl(plugin);
|
||||
assertThat(plugin.statusNonNull().getLogo())
|
||||
.isEqualTo("/plugins/fake-plugin/assets/static/logo.jpg?version=1.0.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void dataBlob() {
|
||||
Plugin plugin = new Plugin();
|
||||
plugin.setSpec(new Plugin.PluginSpec());
|
||||
plugin.setMetadata(new Metadata());
|
||||
plugin.getMetadata().setName("fake-plugin");
|
||||
plugin.getSpec().setLogo("data:image/gif;base64,R0lGODfake");
|
||||
plugin.getSpec().setVersion("2.0.0");
|
||||
pluginReconciler.generateAccessibleLogoUrl(plugin);
|
||||
assertThat(plugin.statusNonNull().getLogo())
|
||||
.isEqualTo("data:image/gif;base64,R0lGODfake");
|
||||
}
|
||||
}
|
||||
|
||||
private ArgumentCaptor<Plugin> doReconcileNeedRequeue() {
|
||||
ArgumentCaptor<Plugin> pluginCaptor = ArgumentCaptor.forClass(Plugin.class);
|
||||
doNothing().when(extensionClient).update(pluginCaptor.capture());
|
||||
|
|
Loading…
Reference in New Issue