diff --git a/.github/actions/docker-buildx-push/action.yaml b/.github/actions/docker-buildx-push/action.yaml new file mode 100644 index 00000000000..7b63f45e383 --- /dev/null +++ b/.github/actions/docker-buildx-push/action.yaml @@ -0,0 +1,74 @@ +name: "Docker buildx and push" +description: "Buildx and push the Docker image." + +inputs: + ghcr-token: + description: Token of current GitHub account in GitHub container registry. + required: false + default: "" + dockerhub-user: + description: "User name for the DockerHub account" + required: false + default: "" + dockerhub-token: + description: Token for the DockerHub account + required: false + default: "" + push: + description: Should push the docker image or not. + required: false + default: "false" + platforms: + description: Target platforms for building image + required: false + default: "linux/amd64,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x" + image-name: + description: The basic name of docker. + required: false + default: "halo" + +runs: + using: "composite" + steps: + - name: Docker meta for Halo + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ghcr.io/${{ github.repository_owner }}/${{ inputs.image-name }} + halohub/${{ inputs.image-name }} + tags: | + type=schedule,pattern=nightly-{{date 'YYYYMMDD'}},enabled=${{ github.event_name == 'schedule' }} + type=ref,event=branch,enabled=${{ github.event_name == 'push' }} + type=ref,event=pr,enabled=${{ github.event_name == 'pull_request' }} + type=semver,pattern={{ version }} + type=semver,pattern={{major}}.{{minor}} + type=sha,enabled=${{ github.event_name == 'push' }} + flavor: | + latest=false + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to GHCR + uses: docker/login-action@v3 + if: inputs.ghcr-token != '' && github.event_name != 'pull_request' + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ inputs.ghcr-token }} + - name: Login to DockerHub + if: inputs.dockerhub-token != '' && github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + username: ${{ inputs.dockerhub-user }} + password: ${{ inputs.dockerhub-token }} + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + platforms: ${{ inputs.platforms }} + labels: ${{ steps.meta.outputs.labels }} + tags: ${{ steps.meta.outputs.tags }} + push: ${{ (inputs.ghcr-token != '' || inputs.dockerhub-token != '') && inputs.push == 'true' }} diff --git a/.github/actions/setup-env/action.yaml b/.github/actions/setup-env/action.yaml new file mode 100644 index 00000000000..432490ce27e --- /dev/null +++ b/.github/actions/setup-env/action.yaml @@ -0,0 +1,61 @@ +name: Setup Environment +description: Setup environment to check and build Halo, including console and core projects. + +inputs: + node-version: + description: Node.js version. + required: false + default: "18" + + pnpm-version: + description: pnpm version. + required: false + default: "8" + + java-version: + description: Java version. + required: false + default: "17" + +runs: + using: "composite" + steps: + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ inputs.node-version }} + + - uses: pnpm/action-setup@v2 + name: Setup pnpm + id: pnpm-install + with: + version: ${{ inputs.pnpm-version }} + run_install: false + + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ steps.pnpm-cache.outputs.STORE_PATH}} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Setup JDK + uses: actions/setup-java@v4 + with: + distribution: "temurin" + cache: "gradle" + java-version: ${{ inputs.java-version }} + + - name: Cache SonarCloud packages + uses: actions/cache@v3 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar diff --git a/.github/workflows/halo.yaml b/.github/workflows/halo.yaml index fa52b175663..9ef2b734ebb 100644 --- a/.github/workflows/halo.yaml +++ b/.github/workflows/halo.yaml @@ -1,4 +1,4 @@ -name: Halo CI +name: Halo Workflow on: pull_request: @@ -16,80 +16,110 @@ on: - "**" - "!**.md" release: - types: # This configuration does not affect the page_build event above - - created + types: + - published jobs: - check: + test: + if: github.event_name == 'pull_request' || github.event_name == 'push' runs-on: ubuntu-latest - # Default steps steps: - uses: actions/checkout@v3 - with: - submodules: true - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - distribution: 'temurin' - cache: 'gradle' - java-version: 17 - - name: Cache SonarCloud packages - uses: actions/cache@v3 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar - - uses: dorny/paths-filter@v2 - id: changes - with: - filters: | - console: - - 'console/**' - - name: Check halo + - name: Setup Environment + uses: ./.github/actions/setup-env + - name: Check Halo console + run: make -C console check + - name: Check Halo core run: ./gradlew check - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 - name: Analyze code - if: ${{ github.event_name == 'push' }} # Due to inability to access secrets during PR, only the code pushed into the branch can be analyzed. + if: ${{ github.event_name == 'push' && env.SONAR_TOKEN != '' }} # Due to inability to access secrets during PR, only the code pushed into the branch can be analyzed. env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: ./gradlew sonar --info - - name: Setup console environment - if: steps.changes.outputs.console == 'true' - uses: halo-sigs/actions/admin-env-setup@main - - name: Check console - if: steps.changes.outputs.console == 'true' - run: make -C console check + build: + runs-on: ubuntu-latest + if: always() && (needs.test.result == 'skipped' || needs.test.result == 'success') + needs: test + steps: + - uses: actions/checkout@v3 + - name: Setup Environment + uses: ./.github/actions/setup-env + - name: Build Halo console + run: make -C console build + - name: Reset version of Halo + if: github.event_name == 'release' + shell: bash + run: | + # Set the version with tag name when releasing + version=${{ github.event.release.tag_name }} + version=${version#v} + sed -i "s/version=.*-SNAPSHOT$/version=$version/1" gradle.properties + - name: Build Halo core + run: ./gradlew clean && ./gradlew downloadPluginPresets && ./gradlew build -x check + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: halo-artifacts + path: application/build/libs + retention-days: 1 + + github-release: + runs-on: ubuntu-latest + if: always() && needs.build.result == 'success' && github.event_name == 'release' + needs: build + steps: + - uses: actions/checkout@v3 + - name: Download Artifacts + uses: actions/download-artifact@v4 + with: + name: halo-artifacts + path: application/build/libs + - name: Upload Artifacts + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release upload ${{ github.event.release.tag_name }} application/build/libs/* + docker-build-and-push: + if: always() && needs.build.result == 'success' && (github.event_name == 'push' || github.event_name == 'release') runs-on: ubuntu-latest - needs: check + needs: build steps: - uses: actions/checkout@v3 - - uses: halo-sigs/actions/halo-next-docker-build@main # change the version to specific ref or release tag while the action is stable. - if: github.event_name != 'pull_request' + - name: Download Artifacts + uses: actions/download-artifact@v4 + with: + name: halo-artifacts + path: application/build/libs + - name: Docker Buildx and Push + uses: ./.github/actions/docker-buildx-push with: image-name: ${{ github.event_name == 'release' && 'halo' || 'halo-dev' }} - ghcr-token: ${{ secrets.GHCR_TOKEN }} + ghcr-token: ${{ secrets.GITHUB_TOKEN }} dockerhub-user: ${{ secrets.DOCKER_USERNAME }} dockerhub-token: ${{ secrets.DOCKER_TOKEN }} - push: ${{ github.event_name == 'push' || github.event_name == 'release' }} # we only push to GHCR if the push is to the next branch - console-ref: ${{ github.event_name == 'release' && github.ref || 'main' }} + push: true platforms: linux/amd64,linux/arm64/v8,linux/ppc64le,linux/s390x - - uses: halo-sigs/actions/halo-next-docker-build@main - if: github.event_name == 'pull_request' + + e2e-test: + if: always() && needs.build.result == 'success' && (github.event_name == 'pull_request' || github.event_name == 'push') + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v3 + - name: Download Artifacts + uses: actions/download-artifact@v4 + with: + name: halo-artifacts + path: application/build/libs + - name: Docker Build + uses: docker/build-push-action@v5 with: - image-name: halo-dev + tags: ghcr.io/halo-dev/halo-dev:main push: false - console-ref: false - load: true - platforms: "" + context: . - name: E2E Testing - if: github.event_name == 'pull_request' run: | sudo curl -L https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose sudo chmod u+x /usr/local/bin/docker-compose - - repo=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') - docker tag ghcr.io/${repo}/halo-dev:pr-${{ github.event.number }} ghcr.io/halo-dev/halo-dev:main cd e2e && make all diff --git a/api/src/main/java/run/halo/app/extension/router/selector/Operator.java b/api/src/main/java/run/halo/app/extension/router/selector/Operator.java index 04f7480949b..cab3dc1275a 100644 --- a/api/src/main/java/run/halo/app/extension/router/selector/Operator.java +++ b/api/src/main/java/run/halo/app/extension/router/selector/Operator.java @@ -12,7 +12,7 @@ public enum Operator implements Converter { public SelectorCriteria convert(@Nullable String selector) { if (preFlightCheck(selector, 3)) { var i = selector.indexOf(getOperator()); - if (i > 0 && (i + getOperator().length()) < selector.length() - 1) { + if (i > 0 && (i + getOperator().length()) <= selector.length() - 1) { String key = selector.substring(0, i); String value = selector.substring(i + getOperator().length()); return new SelectorCriteria(key, this, Set.of(value)); diff --git a/api/src/test/java/run/halo/app/extension/router/selector/OperatorTest.java b/api/src/test/java/run/halo/app/extension/router/selector/OperatorTest.java index ee349b4d19c..ae426277c79 100644 --- a/api/src/test/java/run/halo/app/extension/router/selector/OperatorTest.java +++ b/api/src/test/java/run/halo/app/extension/router/selector/OperatorTest.java @@ -27,6 +27,8 @@ record TestCase(String source, Operator converter, SelectorCriteria expected) { new TestCase("name=", Equals, null), new TestCase("name=value", Equals, new SelectorCriteria("name", Equals, Set.of("value"))), + new TestCase("name=v", Equals, + new SelectorCriteria("name", Equals, Set.of("v"))), new TestCase("", NotEquals, null), new TestCase("=", NotEquals, null), @@ -59,4 +61,4 @@ record TestCase(String source, Operator converter, SelectorCriteria expected) { assertEquals(testCase.expected(), testCase.converter().convert(testCase.source())); }); } -} \ No newline at end of file +} diff --git a/application/build.gradle b/application/build.gradle index 5208f100bbb..2fefdcf3886 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -1,3 +1,6 @@ +import de.undercouch.gradle.tasks.download.Download +import org.gradle.crypto.checksum.Checksum + plugins { id 'org.springframework.boot' version '3.2.0' id 'io.spring.dependency-management' version '1.1.0' @@ -7,12 +10,18 @@ plugins { id 'jacoco' id "de.undercouch.download" version "5.3.1" id "io.freefair.lombok" version "8.4" + id 'org.gradle.crypto.checksum' version '1.4.0' } -group = "run.halo.app" -sourceCompatibility = JavaVersion.VERSION_17 -compileJava.options.encoding = "UTF-8" -compileTestJava.options.encoding = "UTF-8" +group = 'run.halo.app' +compileJava.options.encoding = 'UTF-8' +compileTestJava.options.encoding = 'UTF-8' + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} checkstyle { toolVersion = "9.3" @@ -35,13 +44,19 @@ configurations { } springBoot { - buildInfo() + buildInfo { + properties { + artifact = 'halo' + name = 'halo' + } + } } bootJar { + archiveBaseName = 'halo' manifest { - attributes "Implementation-Title": "Halo Application", - "Implementation-Version": archiveVersion + attributes 'Implementation-Title': 'Halo Application', + 'Implementation-Vendor': 'Halo OSS Team' } } @@ -60,12 +75,23 @@ dependencies { testImplementation 'io.projectreactor:reactor-test' } -tasks.named('test') { +tasks.register('createChecksums', Checksum) { + dependsOn tasks.named('bootJar') + inputFiles.setFrom(layout.buildDirectory.files('libs')) + outputDirectory = layout.buildDirectory.dir("libs") + checksumAlgorithm = Checksum.Algorithm.SHA256 +} + +tasks.named('build') { + dependsOn tasks.named('createChecksums') +} + +tasks.named('test', Test) { useJUnitPlatform() finalizedBy jacocoTestReport } -tasks.named('jacocoTestReport') { +tasks.named('jacocoTestReport', JacocoReport) { reports { xml.required = true html.required = false diff --git a/application/src/main/java/run/halo/app/config/ExtensionConfiguration.java b/application/src/main/java/run/halo/app/config/ExtensionConfiguration.java index 5eb0c2e9e14..b9133cd9c08 100644 --- a/application/src/main/java/run/halo/app/config/ExtensionConfiguration.java +++ b/application/src/main/java/run/halo/app/config/ExtensionConfiguration.java @@ -5,8 +5,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.ServerResponse; -import run.halo.app.extension.DefaultSchemeManager; -import run.halo.app.extension.DefaultSchemeWatcherManager; import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.SchemeManager; @@ -19,18 +17,8 @@ public class ExtensionConfiguration { @Bean RouterFunction extensionsRouterFunction(ReactiveExtensionClient client, - SchemeWatcherManager watcherManager) { - return new ExtensionCompositeRouterFunction(client, watcherManager); - } - - @Bean - SchemeManager schemeManager(SchemeWatcherManager watcherManager) { - return new DefaultSchemeManager(watcherManager); - } - - @Bean - SchemeWatcherManager schemeWatcherManager() { - return new DefaultSchemeWatcherManager(); + SchemeWatcherManager watcherManager, SchemeManager schemeManager) { + return new ExtensionCompositeRouterFunction(client, watcherManager, schemeManager); } @Configuration(proxyBeanMethods = false) diff --git a/application/src/main/java/run/halo/app/content/PostIndexInformer.java b/application/src/main/java/run/halo/app/content/PostIndexInformer.java index 7d4f1f87f33..0ede05bdfb7 100644 --- a/application/src/main/java/run/halo/app/content/PostIndexInformer.java +++ b/application/src/main/java/run/halo/app/content/PostIndexInformer.java @@ -7,6 +7,7 @@ import java.util.concurrent.locks.StampedLock; import java.util.function.BiConsumer; import org.springframework.beans.factory.DisposableBean; +import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.ApplicationListener; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; @@ -19,7 +20,6 @@ import run.halo.app.extension.Unstructured; import run.halo.app.extension.Watcher; import run.halo.app.extension.controller.RequestSynchronizer; -import run.halo.app.infra.SchemeInitializedEvent; /** *

Monitor changes to {@link Post} resources and establish a local, in-memory cache in an @@ -33,7 +33,7 @@ * @since 2.0.0 */ @Component -public class PostIndexInformer implements ApplicationListener, +public class PostIndexInformer implements ApplicationListener, DisposableBean { public static final String TAG_POST_INDEXER = "tag-post-indexer"; public static final String LABEL_INDEXER_NAME = "post-label-indexer"; @@ -71,10 +71,6 @@ private DefaultIndexer.IndexFunc labelIndexFunc() { }; } - public Set getByIndex(String indexName, String indexKey) { - return postIndexer.getByIndex(indexName, indexKey); - } - public Set getByTagName(String tagName) { return postIndexer.getByIndex(TAG_POST_INDEXER, tagName); } @@ -104,10 +100,6 @@ String labelKey(String labelName, String labelValue) { return labelName + "=" + labelValue; } - public Set getByLabel(String labelName, String labelValue) { - return postIndexer.getByIndex(LABEL_INDEXER_NAME, labelKey(labelName, labelValue)); - } - @Override public void destroy() throws Exception { if (postWatcher != null) { @@ -119,7 +111,7 @@ public void destroy() throws Exception { } @Override - public void onApplicationEvent(@NonNull SchemeInitializedEvent event) { + public void onApplicationEvent(@NonNull ApplicationStartedEvent event) { if (!synchronizer.isStarted()) { synchronizer.start(); } diff --git a/application/src/main/java/run/halo/app/extension/router/ExtensionCompositeRouterFunction.java b/application/src/main/java/run/halo/app/extension/router/ExtensionCompositeRouterFunction.java index 769e3cd0347..bf5e9814cbd 100644 --- a/application/src/main/java/run/halo/app/extension/router/ExtensionCompositeRouterFunction.java +++ b/application/src/main/java/run/halo/app/extension/router/ExtensionCompositeRouterFunction.java @@ -3,6 +3,9 @@ import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.context.ApplicationListener; import org.springframework.lang.NonNull; import org.springframework.web.reactive.function.server.HandlerFunction; import org.springframework.web.reactive.function.server.RouterFunction; @@ -13,23 +16,31 @@ import reactor.core.publisher.Mono; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.Scheme; +import run.halo.app.extension.SchemeManager; import run.halo.app.extension.SchemeWatcherManager; import run.halo.app.extension.SchemeWatcherManager.SchemeWatcher; public class ExtensionCompositeRouterFunction implements - RouterFunction, SchemeWatcher { + RouterFunction, + SchemeWatcher, + InitializingBean, + ApplicationListener { private final Map> schemeRouterFuncMapper; private final ReactiveExtensionClient client; + private final SchemeManager schemeManager; + + private final SchemeWatcherManager watcherManager; + public ExtensionCompositeRouterFunction(ReactiveExtensionClient client, - SchemeWatcherManager watcherManager) { + SchemeWatcherManager watcherManager, + SchemeManager schemeManager) { this.client = client; + this.schemeManager = schemeManager; + this.watcherManager = watcherManager; schemeRouterFuncMapper = new ConcurrentHashMap<>(); - if (watcherManager != null) { - watcherManager.register(this); - } } @Override @@ -60,4 +71,17 @@ public void onChange(SchemeWatcherManager.ChangeEvent event) { this.schemeRouterFuncMapper.remove(unregisteredEvent.getDeletedScheme()); } } + + @Override + public void onApplicationEvent(ApplicationStartedEvent event) { + schemeManager.schemes().forEach(scheme -> { + var factory = new ExtensionRouterFunctionFactory(scheme, client); + this.schemeRouterFuncMapper.put(scheme, factory.create()); + }); + } + + @Override + public void afterPropertiesSet() { + watcherManager.register(this); + } } diff --git a/application/src/main/java/run/halo/app/infra/DefaultThemeInitializer.java b/application/src/main/java/run/halo/app/infra/DefaultThemeInitializer.java index d8d74d1cbdb..b1869236659 100644 --- a/application/src/main/java/run/halo/app/infra/DefaultThemeInitializer.java +++ b/application/src/main/java/run/halo/app/infra/DefaultThemeInitializer.java @@ -2,6 +2,7 @@ import java.io.IOException; import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.io.UrlResource; import org.springframework.core.io.buffer.DataBufferUtils; @@ -16,7 +17,7 @@ @Slf4j @Component -public class DefaultThemeInitializer implements ApplicationListener { +public class DefaultThemeInitializer implements ApplicationListener { private final ThemeService themeService; @@ -32,7 +33,7 @@ public DefaultThemeInitializer(ThemeService themeService, ThemeRootGetter themeR } @Override - public void onApplicationEvent(SchemeInitializedEvent event) { + public void onApplicationEvent(ApplicationStartedEvent event) { if (themeProps.getInitializer().isDisabled()) { log.debug("Skipped initializing default theme due to disabled"); return; diff --git a/application/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java b/application/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java index d26120d1dee..716ef8157c3 100644 --- a/application/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java +++ b/application/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java @@ -1,12 +1,14 @@ package run.halo.app.infra; import java.io.IOException; +import java.time.Duration; import java.util.HashSet; import java.util.List; import java.util.Set; import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.event.EventListener; +import org.springframework.context.ApplicationListener; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.stereotype.Component; @@ -28,7 +30,7 @@ */ @Slf4j @Component -public class ExtensionResourceInitializer { +public class ExtensionResourceInitializer implements ApplicationListener { public static final Set REQUIRED_EXTENSION_LOCATIONS = Set.of("classpath:/extensions/*.yaml", "classpath:/extensions/*.yml"); @@ -45,8 +47,7 @@ public ExtensionResourceInitializer(HaloProperties haloProperties, this.eventPublisher = eventPublisher; } - @EventListener(SchemeInitializedEvent.class) - public Mono initialize(SchemeInitializedEvent initializedEvent) { + public void onApplicationEvent(ApplicationStartedEvent initializedEvent) { var locations = new HashSet(); if (!haloProperties.isRequiredExtensionDisabled()) { locations.addAll(REQUIRED_EXTENSION_LOCATIONS); @@ -55,10 +56,10 @@ public Mono initialize(SchemeInitializedEvent initializedEvent) { locations.addAll(haloProperties.getInitialExtensionLocations()); } if (CollectionUtils.isEmpty(locations)) { - return Mono.empty(); + return; } - return Flux.fromIterable(locations) + Flux.fromIterable(locations) .doOnNext(location -> log.debug("Trying to initialize extension resources from location: {}", location)) .map(this::listResources) @@ -82,7 +83,8 @@ public Mono initialize(SchemeInitializedEvent initializedEvent) { } }) .then(Mono.fromRunnable( - () -> eventPublisher.publishEvent(new ExtensionInitializedEvent(this)))); + () -> eventPublisher.publishEvent(new ExtensionInitializedEvent(this)))) + .block(Duration.ofMinutes(1)); } private Mono createOrUpdate(Unstructured extension) { diff --git a/application/src/main/java/run/halo/app/infra/SchemeInitializedEvent.java b/application/src/main/java/run/halo/app/infra/SchemeInitializedEvent.java deleted file mode 100644 index 647aa518ce3..00000000000 --- a/application/src/main/java/run/halo/app/infra/SchemeInitializedEvent.java +++ /dev/null @@ -1,11 +0,0 @@ -package run.halo.app.infra; - -import org.springframework.context.ApplicationEvent; - -public class SchemeInitializedEvent extends ApplicationEvent { - - public SchemeInitializedEvent(Object source) { - super(source); - } - -} diff --git a/application/src/main/java/run/halo/app/infra/SchemeInitializer.java b/application/src/main/java/run/halo/app/infra/SchemeInitializer.java index ff2bb8b82fa..e7e9bb7d303 100644 --- a/application/src/main/java/run/halo/app/infra/SchemeInitializer.java +++ b/application/src/main/java/run/halo/app/infra/SchemeInitializer.java @@ -1,7 +1,6 @@ package run.halo.app.infra; -import org.springframework.boot.context.event.ApplicationStartedEvent; -import org.springframework.context.ApplicationEventPublisher; +import org.springframework.boot.context.event.ApplicationContextInitializedEvent; import org.springframework.context.ApplicationListener; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; @@ -36,7 +35,8 @@ import run.halo.app.core.extension.notification.ReasonType; import run.halo.app.core.extension.notification.Subscription; import run.halo.app.extension.ConfigMap; -import run.halo.app.extension.SchemeManager; +import run.halo.app.extension.DefaultSchemeManager; +import run.halo.app.extension.DefaultSchemeWatcherManager; import run.halo.app.extension.Secret; import run.halo.app.migration.Backup; import run.halo.app.plugin.extensionpoint.ExtensionDefinition; @@ -45,20 +45,17 @@ import run.halo.app.security.PersonalAccessToken; @Component -public class SchemeInitializer implements ApplicationListener { +public class SchemeInitializer implements ApplicationListener { - private final SchemeManager schemeManager; - - private final ApplicationEventPublisher eventPublisher; + @Override + public void onApplicationEvent(@NonNull ApplicationContextInitializedEvent event) { + var watcherManager = new DefaultSchemeWatcherManager(); + var schemeManager = new DefaultSchemeManager(watcherManager); - public SchemeInitializer(SchemeManager schemeManager, - ApplicationEventPublisher eventPublisher) { - this.schemeManager = schemeManager; - this.eventPublisher = eventPublisher; - } + var beanFactory = event.getApplicationContext().getBeanFactory(); + beanFactory.registerSingleton("schemeWatcherManager", watcherManager); + beanFactory.registerSingleton("schemeManager", schemeManager); - @Override - public void onApplicationEvent(@NonNull ApplicationStartedEvent event) { schemeManager.register(Role.class); // plugin.halo.run @@ -108,7 +105,5 @@ public void onApplicationEvent(@NonNull ApplicationStartedEvent event) { schemeManager.register(Subscription.class); schemeManager.register(NotifierDescriptor.class); schemeManager.register(Notification.class); - - eventPublisher.publishEvent(new SchemeInitializedEvent(this)); } } diff --git a/application/src/main/java/run/halo/app/search/IndicesInitializer.java b/application/src/main/java/run/halo/app/search/IndicesInitializer.java index 89e6dd6a49d..cbc54a498e6 100644 --- a/application/src/main/java/run/halo/app/search/IndicesInitializer.java +++ b/application/src/main/java/run/halo/app/search/IndicesInitializer.java @@ -1,12 +1,12 @@ package run.halo.app.search; -import java.util.concurrent.CountDownLatch; +import java.time.Duration; import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import org.springframework.util.StopWatch; -import run.halo.app.infra.SchemeInitializedEvent; @Slf4j @Component @@ -19,17 +19,12 @@ public IndicesInitializer(IndicesService indicesService) { } @Async - @EventListener(SchemeInitializedEvent.class) - public void whenSchemeInitialized(SchemeInitializedEvent event) throws InterruptedException { - var latch = new CountDownLatch(1); + @EventListener + public void whenSchemeInitialized(ApplicationStartedEvent event) { log.info("Initialize post indices..."); var watch = new StopWatch("PostIndicesWatch"); watch.start("rebuild"); - indicesService.rebuildPostIndices() - .doFinally(signalType -> latch.countDown()) - .subscribe(); - latch.await(); - watch.stop(); + indicesService.rebuildPostIndices().block(Duration.ofMinutes(5)); log.info("Initialized post indices. Usage: {}", watch); } diff --git a/application/src/main/java/run/halo/app/theme/finders/CategoryFinder.java b/application/src/main/java/run/halo/app/theme/finders/CategoryFinder.java index 9f95646a5ba..f8023fb6c02 100644 --- a/application/src/main/java/run/halo/app/theme/finders/CategoryFinder.java +++ b/application/src/main/java/run/halo/app/theme/finders/CategoryFinder.java @@ -28,4 +28,6 @@ public interface CategoryFinder { Flux listAsTree(); Flux listAsTree(String name); + + Mono getParentByName(String name); } diff --git a/application/src/main/java/run/halo/app/theme/finders/impl/CategoryFinderImpl.java b/application/src/main/java/run/halo/app/theme/finders/impl/CategoryFinderImpl.java index 07a89f9c250..43502d798f5 100644 --- a/application/src/main/java/run/halo/app/theme/finders/impl/CategoryFinderImpl.java +++ b/application/src/main/java/run/halo/app/theme/finders/impl/CategoryFinderImpl.java @@ -81,6 +81,23 @@ public Flux listAsTree(String name) { return this.toCategoryTreeVoFlux(name); } + @Override + public Mono getParentByName(String name) { + if (StringUtils.isBlank(name)) { + return Mono.empty(); + } + return client.list(Category.class, + category -> { + List children = category.getSpec().getChildren(); + if (children == null) { + return false; + } + return children.contains(name); + }, + defaultComparator()) + .next().map(CategoryVo::from); + } + Flux toCategoryTreeVoFlux(String name) { return listAll() .collectList() diff --git a/application/src/main/java/run/halo/app/theme/router/ThemeCompositeRouterFunction.java b/application/src/main/java/run/halo/app/theme/router/ThemeCompositeRouterFunction.java index 996ee0c4186..f1b39af5055 100644 --- a/application/src/main/java/run/halo/app/theme/router/ThemeCompositeRouterFunction.java +++ b/application/src/main/java/run/halo/app/theme/router/ThemeCompositeRouterFunction.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; -import org.springframework.context.ApplicationEvent; +import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.event.EventListener; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; @@ -15,7 +15,6 @@ import org.springframework.web.reactive.function.server.ServerResponse; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import run.halo.app.infra.SchemeInitializedEvent; import run.halo.app.infra.SystemConfigurableEnvironmentFetcher; import run.halo.app.infra.SystemSetting; import run.halo.app.theme.DefaultTemplateEnum; @@ -90,10 +89,15 @@ private RouterFunction createRouterFunction(RoutePattern routePa /** * Refresh the {@link #cachedRouters} when the permalink rule is changed. * - * @param event {@link SchemeInitializedEvent} or {@link PermalinkRuleChangedEvent} + * @param event {@link PermalinkRuleChangedEvent} */ - @EventListener({SchemeInitializedEvent.class, PermalinkRuleChangedEvent.class}) - public void onSchemeInitializedEvent(@NonNull ApplicationEvent event) { + @EventListener + public void onPermalinkRuleChanged(PermalinkRuleChangedEvent event) { + this.cachedRouters = routerFunctions(); + } + + @EventListener + public void onApplicationStarted(ApplicationStartedEvent event) { this.cachedRouters = routerFunctions(); } diff --git a/application/src/main/resources/META-INF/spring.factories b/application/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000000..e63b88094d6 --- /dev/null +++ b/application/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.context.ApplicationListener=run.halo.app.infra.SchemeInitializer diff --git a/application/src/test/java/run/halo/app/extension/router/ExtensionCompositeRouterFunctionTest.java b/application/src/test/java/run/halo/app/extension/router/ExtensionCompositeRouterFunctionTest.java index cd4599478c0..d5f9884bdd6 100644 --- a/application/src/test/java/run/halo/app/extension/router/ExtensionCompositeRouterFunctionTest.java +++ b/application/src/test/java/run/halo/app/extension/router/ExtensionCompositeRouterFunctionTest.java @@ -4,13 +4,14 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.web.reactive.function.server.HandlerStrategies; @@ -18,6 +19,7 @@ import run.halo.app.extension.FakeExtension; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.Scheme; +import run.halo.app.extension.SchemeManager; import run.halo.app.extension.SchemeWatcherManager; import run.halo.app.extension.SchemeWatcherManager.SchemeRegistered; import run.halo.app.extension.SchemeWatcherManager.SchemeUnregistered; @@ -28,10 +30,17 @@ class ExtensionCompositeRouterFunctionTest { @Mock ReactiveExtensionClient client; + @Mock + SchemeManager schemeManager; + + @Mock + SchemeWatcherManager watcherManager; + + @InjectMocks + ExtensionCompositeRouterFunction extensionRouterFunc; + @Test void shouldRouteWhenSchemeRegistered() { - var extensionRouterFunc = new ExtensionCompositeRouterFunction(client, null); - var exchange = MockServerWebExchange.from( MockServerHttpRequest.get("/apis/fake.halo.run/v1alpha1/fakes").build()); @@ -51,8 +60,6 @@ void shouldRouteWhenSchemeRegistered() { @Test void shouldNotRouteWhenSchemeUnregistered() { - var extensionRouterFunc = new ExtensionCompositeRouterFunction(client, null); - var exchange = MockServerWebExchange.from( MockServerHttpRequest.get("/apis/fake.halo.run/v1alpha1/fakes").build()); @@ -74,10 +81,16 @@ void shouldNotRouteWhenSchemeUnregistered() { } @Test - void shouldRegisterWatcherIfWatcherManagerIsNotNull() { - var watcherManager = mock(SchemeWatcherManager.class); - var routerFunction = new ExtensionCompositeRouterFunction(client, watcherManager); - verify(watcherManager, times(1)).register(eq(routerFunction)); + void shouldRegisterWatcherAfterPropertiesSet() { + extensionRouterFunc.afterPropertiesSet(); + verify(watcherManager).register(eq(extensionRouterFunc)); + } + + @Test + void shouldBuildRouterFunctionsOnApplicationStarted() { + var applicationStartedEvent = mock(ApplicationStartedEvent.class); + extensionRouterFunc.onApplicationEvent(applicationStartedEvent); + verify(schemeManager).schemes(); } } diff --git a/application/src/test/java/run/halo/app/infra/ExtensionResourceInitializerTest.java b/application/src/test/java/run/halo/app/infra/ExtensionResourceInitializerTest.java index 4d80e8813c3..b082a188164 100644 --- a/application/src/test/java/run/halo/app/infra/ExtensionResourceInitializerTest.java +++ b/application/src/test/java/run/halo/app/infra/ExtensionResourceInitializerTest.java @@ -23,10 +23,10 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.util.FileSystemUtils; import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; import run.halo.app.extension.GroupVersionKind; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.Unstructured; @@ -47,7 +47,7 @@ class ExtensionResourceInitializerTest { @Mock HaloProperties haloProperties; @Mock - SchemeInitializedEvent applicationReadyEvent; + ApplicationStartedEvent applicationStartedEvent; @Mock ApplicationEventPublisher eventPublisher; @@ -128,10 +128,7 @@ void onApplicationEvent() throws JSONException { .thenReturn(Mono.empty()); when(extensionClient.create(any())).thenReturn(Mono.empty()); - var initializeMono = extensionResourceInitializer.initialize(applicationReadyEvent); - StepVerifier.create(initializeMono) - .verifyComplete(); - + extensionResourceInitializer.onApplicationEvent(applicationStartedEvent); verify(extensionClient, times(3)).create(argumentCaptor.capture()); diff --git a/console/.gitignore b/console/.gitignore index 36bbbe46640..b6c6ca7cd46 100644 --- a/console/.gitignore +++ b/console/.gitignore @@ -29,3 +29,4 @@ coverage *.sw? !src/build +storybook-static diff --git a/console/console-src/composables/use-content-cache.ts b/console/console-src/composables/use-content-cache.ts index 0f4d17d619b..158afea8ad3 100644 --- a/console/console-src/composables/use-content-cache.ts +++ b/console/console-src/composables/use-content-cache.ts @@ -6,7 +6,7 @@ export interface ContentCache { content?: string; version: number; } -import debounce from "lodash.debounce"; +import { debounce } from "lodash-es"; import { useI18n } from "vue-i18n"; interface useContentCacheReturn { diff --git a/console/console-src/composables/use-setting-form.ts b/console/console-src/composables/use-setting-form.ts index 94c3c6e92a0..9e5e4c9e2ca 100644 --- a/console/console-src/composables/use-setting-form.ts +++ b/console/console-src/composables/use-setting-form.ts @@ -5,8 +5,7 @@ import { ref } from "vue"; import { apiClient } from "@/utils/api-client"; // libs -import cloneDeep from "lodash.clonedeep"; -import merge from "lodash.merge"; +import { cloneDeep, merge } from "lodash-es"; import type { ConfigMap, Setting, SettingForm } from "@halo-dev/api-client"; import type { FormKitSchemaCondition, FormKitSchemaNode } from "@formkit/core"; import { Toast } from "@halo-dev/components"; diff --git a/console/console-src/layouts/BasicLayout.vue b/console/console-src/layouts/BasicLayout.vue index 31090de7024..862179444f1 100644 --- a/console/console-src/layouts/BasicLayout.vue +++ b/console/console-src/layouts/BasicLayout.vue @@ -204,6 +204,15 @@ onMounted(() => {

+
+ Powered by + + Halo + +
diff --git a/console/console-src/modules/contents/attachments/AttachmentList.vue b/console/console-src/modules/contents/attachments/AttachmentList.vue index 226e1e45ef0..54153f31ff3 100644 --- a/console/console-src/modules/contents/attachments/AttachmentList.vue +++ b/console/console-src/modules/contents/attachments/AttachmentList.vue @@ -30,7 +30,7 @@ import type { Attachment, Group } from "@halo-dev/api-client"; import { useFetchAttachmentPolicy } from "./composables/use-attachment-policy"; import { useAttachmentControl } from "./composables/use-attachment"; import { apiClient } from "@/utils/api-client"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { isImage } from "@/utils/image"; import { useRouteQuery } from "@vueuse/router"; import { useFetchAttachmentGroup } from "./composables/use-attachment-group"; diff --git a/console/console-src/modules/contents/attachments/components/AttachmentGroupEditingModal.vue b/console/console-src/modules/contents/attachments/components/AttachmentGroupEditingModal.vue index 883dbce0b8b..e41b10095cd 100644 --- a/console/console-src/modules/contents/attachments/components/AttachmentGroupEditingModal.vue +++ b/console/console-src/modules/contents/attachments/components/AttachmentGroupEditingModal.vue @@ -3,7 +3,7 @@ import { Toast, VButton, VModal, VSpace } from "@halo-dev/components"; import SubmitButton from "@/components/button/SubmitButton.vue"; import type { Group } from "@halo-dev/api-client"; import { computed, ref, watch } from "vue"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { apiClient } from "@/utils/api-client"; import { reset } from "@formkit/core"; import { setFocus } from "@/formkit/utils/focus"; diff --git a/console/console-src/modules/contents/attachments/components/AttachmentPolicyEditingModal.vue b/console/console-src/modules/contents/attachments/components/AttachmentPolicyEditingModal.vue index 9cf73da936a..30ef48324e0 100644 --- a/console/console-src/modules/contents/attachments/components/AttachmentPolicyEditingModal.vue +++ b/console/console-src/modules/contents/attachments/components/AttachmentPolicyEditingModal.vue @@ -2,7 +2,7 @@ import { Toast, VButton, VModal, VSpace } from "@halo-dev/components"; import SubmitButton from "@/components/button/SubmitButton.vue"; import type { Policy, PolicyTemplate } from "@halo-dev/api-client"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { computed, ref, toRaw, watch, watchEffect } from "vue"; import { useSettingForm } from "@console/composables/use-setting-form"; import { apiClient } from "@/utils/api-client"; diff --git a/console/console-src/modules/contents/comments/components/CommentListItem.vue b/console/console-src/modules/contents/comments/components/CommentListItem.vue index 7dfddd3a512..073b03a6f38 100644 --- a/console/console-src/modules/contents/comments/components/CommentListItem.vue +++ b/console/console-src/modules/contents/comments/components/CommentListItem.vue @@ -27,7 +27,7 @@ import { formatDatetime } from "@/utils/date"; import { computed, provide, ref, onMounted, type Ref } from "vue"; import ReplyListItem from "./ReplyListItem.vue"; import { apiClient } from "@/utils/api-client"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { usePermission } from "@/utils/permission"; import { useQuery, useQueryClient } from "@tanstack/vue-query"; import { useI18n } from "vue-i18n"; diff --git a/console/console-src/modules/contents/comments/components/ReplyCreationModal.vue b/console/console-src/modules/contents/comments/components/ReplyCreationModal.vue index a5ab859448b..d20b84befa2 100644 --- a/console/console-src/modules/contents/comments/components/ReplyCreationModal.vue +++ b/console/console-src/modules/contents/comments/components/ReplyCreationModal.vue @@ -18,7 +18,7 @@ import { Picker } from "emoji-mart"; import i18n from "@emoji-mart/data/i18n/zh.json"; import { computed, nextTick, ref, watch, watchEffect } from "vue"; import { reset } from "@formkit/core"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { setFocus } from "@/formkit/utils/focus"; import { apiClient } from "@/utils/api-client"; import { useI18n } from "vue-i18n"; diff --git a/console/console-src/modules/contents/comments/components/ReplyListItem.vue b/console/console-src/modules/contents/comments/components/ReplyListItem.vue index 03d08ff0864..d7ace175557 100644 --- a/console/console-src/modules/contents/comments/components/ReplyListItem.vue +++ b/console/console-src/modules/contents/comments/components/ReplyListItem.vue @@ -14,7 +14,7 @@ import type { ListedReply } from "@halo-dev/api-client"; import { formatDatetime } from "@/utils/date"; import { apiClient } from "@/utils/api-client"; import { computed, inject, type Ref } from "vue"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { useI18n } from "vue-i18n"; import { useQueryClient } from "@tanstack/vue-query"; diff --git a/console/console-src/modules/contents/pages/DeletedSinglePageList.vue b/console/console-src/modules/contents/pages/DeletedSinglePageList.vue index ec37d3d16b2..b7490a66177 100644 --- a/console/console-src/modules/contents/pages/DeletedSinglePageList.vue +++ b/console/console-src/modules/contents/pages/DeletedSinglePageList.vue @@ -21,7 +21,7 @@ import { ref, watch } from "vue"; import type { ListedSinglePage, SinglePage } from "@halo-dev/api-client"; import { apiClient } from "@/utils/api-client"; import { formatDatetime } from "@/utils/date"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { usePermission } from "@/utils/permission"; import { useQuery } from "@tanstack/vue-query"; import { useI18n } from "vue-i18n"; diff --git a/console/console-src/modules/contents/pages/SinglePageEditor.vue b/console/console-src/modules/contents/pages/SinglePageEditor.vue index ddfec1d7aee..1d2d7cccbfa 100644 --- a/console/console-src/modules/contents/pages/SinglePageEditor.vue +++ b/console/console-src/modules/contents/pages/SinglePageEditor.vue @@ -24,7 +24,7 @@ import { } from "vue"; import { apiClient } from "@/utils/api-client"; import { useRouteQuery } from "@vueuse/router"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { useRouter } from "vue-router"; import { randomUUID } from "@/utils/id"; import { useContentCache } from "@console/composables/use-content-cache"; diff --git a/console/console-src/modules/contents/pages/components/SinglePageListItem.vue b/console/console-src/modules/contents/pages/components/SinglePageListItem.vue index 60be79293db..a15f82879b6 100644 --- a/console/console-src/modules/contents/pages/components/SinglePageListItem.vue +++ b/console/console-src/modules/contents/pages/components/SinglePageListItem.vue @@ -17,7 +17,7 @@ import type { ListedSinglePage, SinglePage } from "@halo-dev/api-client"; import { apiClient } from "@/utils/api-client"; import { formatDatetime } from "@/utils/date"; import { RouterLink } from "vue-router"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { usePermission } from "@/utils/permission"; import { singlePageLabels } from "@/constants/labels"; import { useMutation, useQueryClient } from "@tanstack/vue-query"; diff --git a/console/console-src/modules/contents/pages/components/SinglePageSettingModal.vue b/console/console-src/modules/contents/pages/components/SinglePageSettingModal.vue index a1f2bd0404b..972162b6fd7 100644 --- a/console/console-src/modules/contents/pages/components/SinglePageSettingModal.vue +++ b/console/console-src/modules/contents/pages/components/SinglePageSettingModal.vue @@ -8,7 +8,7 @@ import { } from "@halo-dev/components"; import { computed, nextTick, ref, watchEffect } from "vue"; import type { SinglePage } from "@halo-dev/api-client"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { apiClient } from "@/utils/api-client"; import { useThemeCustomTemplates } from "@console/modules/interface/themes/composables/use-theme"; import { singlePageLabels } from "@/constants/labels"; diff --git a/console/console-src/modules/contents/pages/module.ts b/console/console-src/modules/contents/pages/module.ts index 7df6f530ea1..9331b0ab04e 100644 --- a/console/console-src/modules/contents/pages/module.ts +++ b/console/console-src/modules/contents/pages/module.ts @@ -49,6 +49,7 @@ export default definePlugin({ meta: { title: "core.page_editor.title", searchable: true, + hideFooter: true, permissions: ["system:singlepages:manage"], }, }, diff --git a/console/console-src/modules/contents/posts/DeletedPostList.vue b/console/console-src/modules/contents/posts/DeletedPostList.vue index 6837127c668..3df872d7711 100644 --- a/console/console-src/modules/contents/posts/DeletedPostList.vue +++ b/console/console-src/modules/contents/posts/DeletedPostList.vue @@ -23,7 +23,7 @@ import type { ListedPost, Post } from "@halo-dev/api-client"; import { apiClient } from "@/utils/api-client"; import { formatDatetime } from "@/utils/date"; import { usePermission } from "@/utils/permission"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { useQuery } from "@tanstack/vue-query"; import { useI18n } from "vue-i18n"; import ContributorList from "../_components/ContributorList.vue"; diff --git a/console/console-src/modules/contents/posts/PostEditor.vue b/console/console-src/modules/contents/posts/PostEditor.vue index 33cdc71cd8b..8c970d53c45 100644 --- a/console/console-src/modules/contents/posts/PostEditor.vue +++ b/console/console-src/modules/contents/posts/PostEditor.vue @@ -22,7 +22,7 @@ import { toRef, type ComputedRef, } from "vue"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { apiClient } from "@/utils/api-client"; import { useRouteQuery } from "@vueuse/router"; import { useRouter } from "vue-router"; diff --git a/console/console-src/modules/contents/posts/categories/components/CategoryEditingModal.vue b/console/console-src/modules/contents/posts/categories/components/CategoryEditingModal.vue index cec6a5167f2..206422443d3 100644 --- a/console/console-src/modules/contents/posts/categories/components/CategoryEditingModal.vue +++ b/console/console-src/modules/contents/posts/categories/components/CategoryEditingModal.vue @@ -17,7 +17,7 @@ import SubmitButton from "@/components/button/SubmitButton.vue"; import type { Category } from "@halo-dev/api-client"; // libs -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { reset } from "@formkit/core"; import { setFocus } from "@/formkit/utils/focus"; import { useThemeCustomTemplates } from "@console/modules/interface/themes/composables/use-theme"; diff --git a/console/console-src/modules/contents/posts/categories/utils/index.ts b/console/console-src/modules/contents/posts/categories/utils/index.ts index 0169e97e4cc..dc5863d1f21 100644 --- a/console/console-src/modules/contents/posts/categories/utils/index.ts +++ b/console/console-src/modules/contents/posts/categories/utils/index.ts @@ -1,5 +1,5 @@ import type { Category, CategorySpec } from "@halo-dev/api-client"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; export interface CategoryTreeSpec extends Omit { children: CategoryTree[]; diff --git a/console/console-src/modules/contents/posts/components/PostSettingModal.vue b/console/console-src/modules/contents/posts/components/PostSettingModal.vue index 1af6215cb66..e64985e1bec 100644 --- a/console/console-src/modules/contents/posts/components/PostSettingModal.vue +++ b/console/console-src/modules/contents/posts/components/PostSettingModal.vue @@ -8,7 +8,7 @@ import { } from "@halo-dev/components"; import { computed, nextTick, ref, watchEffect } from "vue"; import type { Post } from "@halo-dev/api-client"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { apiClient } from "@/utils/api-client"; import { useThemeCustomTemplates } from "@console/modules/interface/themes/composables/use-theme"; import { postLabels } from "@/constants/labels"; diff --git a/console/console-src/modules/contents/posts/module.ts b/console/console-src/modules/contents/posts/module.ts index dc0955e01c7..06a74a3fc8f 100644 --- a/console/console-src/modules/contents/posts/module.ts +++ b/console/console-src/modules/contents/posts/module.ts @@ -55,6 +55,7 @@ export default definePlugin({ meta: { title: "core.post_editor.title", searchable: true, + hideFooter: true, permissions: ["system:posts:manage"], }, }, diff --git a/console/console-src/modules/contents/posts/tags/components/TagEditingModal.vue b/console/console-src/modules/contents/posts/tags/components/TagEditingModal.vue index d406a5ee554..34dac324177 100644 --- a/console/console-src/modules/contents/posts/tags/components/TagEditingModal.vue +++ b/console/console-src/modules/contents/posts/tags/components/TagEditingModal.vue @@ -19,7 +19,7 @@ import SubmitButton from "@/components/button/SubmitButton.vue"; import type { Tag } from "@halo-dev/api-client"; // libs -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { reset } from "@formkit/core"; import { setFocus } from "@/formkit/utils/focus"; import AnnotationsForm from "@/components/form/AnnotationsForm.vue"; diff --git a/console/console-src/modules/dashboard/Dashboard.vue b/console/console-src/modules/dashboard/Dashboard.vue index 727b483691d..dd4b615bd25 100644 --- a/console/console-src/modules/dashboard/Dashboard.vue +++ b/console/console-src/modules/dashboard/Dashboard.vue @@ -123,7 +123,7 @@ import { } from "@halo-dev/components"; import { onMounted, provide, ref, type Ref } from "vue"; import { useStorage } from "@vueuse/core"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { apiClient } from "@/utils/api-client"; import type { DashboardStats } from "@halo-dev/api-client"; import { useI18n } from "vue-i18n"; diff --git a/console/console-src/modules/index.ts b/console/console-src/modules/index.ts index c3f5a66a36f..9e8177aaf19 100644 --- a/console/console-src/modules/index.ts +++ b/console/console-src/modules/index.ts @@ -1,48 +1,10 @@ -// fixme: add supports for auto import -import dashboardModule from "./dashboard/module"; -import postModule from "./contents/posts/module"; -import pageModule from "./contents/pages/module"; -import commentModule from "./contents/comments/module"; -import attachmentModule from "./contents/attachments/module"; -import themeModule from "./interface/themes/module"; -import menuModule from "./interface/menus/module"; -import pluginModule from "./system/plugins/module"; -import userModule from "./system/users/module"; -import roleModule from "./system/roles/module"; -import settingModule from "./system/settings/module"; -import actuatorModule from "./system/actuator/module"; -import authProviderModule from "./system/auth-providers/module"; -import backupModule from "./system/backup/module"; +import type { PluginModule } from "@halo-dev/console-shared"; -// const coreModules = [ -// dashboardModule, -// postModule, -// pageModule, -// commentModule, -// attachmentModule, -// themeModule, -// menuModule, -// pluginModule, -// userModule, -// roleModule, -// settingModule, -// ]; +const modules = Object.values( + import.meta.glob("./**/module.ts", { + eager: true, + import: "default", + }) +) as PluginModule[]; -const coreModules = [ - postModule, - pluginModule, - settingModule, - actuatorModule, - dashboardModule, - menuModule, - commentModule, - attachmentModule, - pageModule, - themeModule, - userModule, - roleModule, - authProviderModule, - backupModule, -]; - -export { coreModules }; +export default modules; diff --git a/console/console-src/modules/interface/menus/Menus.vue b/console/console-src/modules/interface/menus/Menus.vue index f44df6d4885..002e173ca57 100644 --- a/console/console-src/modules/interface/menus/Menus.vue +++ b/console/console-src/modules/interface/menus/Menus.vue @@ -17,7 +17,7 @@ import MenuList from "./components/MenuList.vue"; import { computed, ref } from "vue"; import { apiClient } from "@/utils/api-client"; import type { Menu, MenuItem } from "@halo-dev/api-client"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import type { MenuTreeItem } from "./utils"; import { buildMenuItemsTree, diff --git a/console/console-src/modules/interface/menus/components/MenuEditingModal.vue b/console/console-src/modules/interface/menus/components/MenuEditingModal.vue index 75e7710810e..d9ef0a45f7a 100644 --- a/console/console-src/modules/interface/menus/components/MenuEditingModal.vue +++ b/console/console-src/modules/interface/menus/components/MenuEditingModal.vue @@ -5,7 +5,7 @@ import type { Menu } from "@halo-dev/api-client"; import { computed, ref, watch } from "vue"; import { apiClient } from "@/utils/api-client"; import { reset } from "@formkit/core"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { setFocus } from "@/formkit/utils/focus"; import { useI18n } from "vue-i18n"; diff --git a/console/console-src/modules/interface/menus/components/MenuItemEditingModal.vue b/console/console-src/modules/interface/menus/components/MenuItemEditingModal.vue index 64729efe0be..7d6bf2486cf 100644 --- a/console/console-src/modules/interface/menus/components/MenuItemEditingModal.vue +++ b/console/console-src/modules/interface/menus/components/MenuItemEditingModal.vue @@ -5,7 +5,7 @@ import { computed, nextTick, ref, watch } from "vue"; import type { Menu, MenuItem, Ref } from "@halo-dev/api-client"; import { apiClient } from "@/utils/api-client"; import { reset } from "@formkit/core"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { setFocus } from "@/formkit/utils/focus"; import AnnotationsForm from "@/components/form/AnnotationsForm.vue"; import { useI18n } from "vue-i18n"; diff --git a/console/console-src/modules/interface/menus/utils/index.ts b/console/console-src/modules/interface/menus/utils/index.ts index 1d59e9681fd..01b963dca3b 100644 --- a/console/console-src/modules/interface/menus/utils/index.ts +++ b/console/console-src/modules/interface/menus/utils/index.ts @@ -1,5 +1,5 @@ import type { MenuItem, MenuItemSpec } from "@halo-dev/api-client"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; export interface MenuTreeItemSpec extends Omit { children: MenuTreeItem[]; diff --git a/console/console-src/modules/interface/themes/components/list-tabs/LocalUpload.vue b/console/console-src/modules/interface/themes/components/list-tabs/LocalUpload.vue index 308d449e3ef..11b21c30814 100644 --- a/console/console-src/modules/interface/themes/components/list-tabs/LocalUpload.vue +++ b/console/console-src/modules/interface/themes/components/list-tabs/LocalUpload.vue @@ -30,7 +30,7 @@ const onUploaded = () => { activeTabId.value = "installed"; }; -const onError = (file: UppyFile, response: ErrorResponse) => { +const onError = (file: UppyFile, response: ErrorResponse) => { const body = response.body as ThemeInstallationErrorResponse; if (body.type === THEME_ALREADY_EXISTS_TYPE) { diff --git a/console/console-src/modules/interface/themes/layouts/ThemeLayout.vue b/console/console-src/modules/interface/themes/layouts/ThemeLayout.vue index fd5c1bc2903..d4417bfdb0a 100644 --- a/console/console-src/modules/interface/themes/layouts/ThemeLayout.vue +++ b/console/console-src/modules/interface/themes/layouts/ThemeLayout.vue @@ -5,7 +5,7 @@ import { provide, ref } from "vue"; import { useRoute, useRouter } from "vue-router"; // libs -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; // hooks import { useThemeLifeCycle } from "../composables/use-theme"; diff --git a/console/console-src/modules/system/actuator/Actuator.vue b/console/console-src/modules/system/actuator/Actuator.vue index 28415cedc85..4f0b898720e 100644 --- a/console/console-src/modules/system/actuator/Actuator.vue +++ b/console/console-src/modules/system/actuator/Actuator.vue @@ -369,10 +369,22 @@ const handleDownloadLogfile = () => { [info.java.runtime.name, info.java.runtime.version].join(' / ') " /> - + + + {{ [info.database.name, info.database.version].join(" / ") }} + + + + + {{ info.os.name }} {{ info.os.version }} / {{ info.os.arch }} diff --git a/console/console-src/modules/system/auth-providers/module.ts b/console/console-src/modules/system/auth-providers/module.ts index e7c2e55a93e..086919dd9a2 100644 --- a/console/console-src/modules/system/auth-providers/module.ts +++ b/console/console-src/modules/system/auth-providers/module.ts @@ -6,11 +6,11 @@ import AuthProviderDetail from "./AuthProviderDetail.vue"; export default definePlugin({ routes: [ { - path: "/users", + path: "/users/auth-providers", component: BasicLayout, children: [ { - path: "auth-providers", + path: "", name: "AuthProviders", component: AuthProviders, meta: { @@ -19,7 +19,7 @@ export default definePlugin({ }, }, { - path: "auth-providers/:name", + path: ":name", name: "AuthProviderDetail", component: AuthProviderDetail, meta: { diff --git a/console/console-src/modules/system/plugins/PluginDetail.vue b/console/console-src/modules/system/plugins/PluginDetail.vue index 5d18cb0e571..459ac02c248 100644 --- a/console/console-src/modules/system/plugins/PluginDetail.vue +++ b/console/console-src/modules/system/plugins/PluginDetail.vue @@ -5,7 +5,7 @@ import { useRoute } from "vue-router"; import { apiClient } from "@/utils/api-client"; // libs -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; // components import { VCard, VPageHeader, VTabbar, VAvatar } from "@halo-dev/components"; diff --git a/console/console-src/modules/system/plugins/components/installation-tabs/LocalUpload.vue b/console/console-src/modules/system/plugins/components/installation-tabs/LocalUpload.vue index 9963bb9b840..d1ab245e1ee 100644 --- a/console/console-src/modules/system/plugins/components/installation-tabs/LocalUpload.vue +++ b/console/console-src/modules/system/plugins/components/installation-tabs/LocalUpload.vue @@ -46,7 +46,7 @@ const onUploaded = async (response: SuccessResponse) => { handleShowActiveModalAfterInstall(response.body as Plugin); }; -const onError = (file: UppyFile, response: ErrorResponse) => { +const onError = (file: UppyFile, response: ErrorResponse) => { const body = response.body as PluginInstallationErrorResponse; if (body.type === PLUGIN_ALREADY_EXISTS_TYPE) { diff --git a/console/console-src/modules/system/plugins/composables/use-plugin.ts b/console/console-src/modules/system/plugins/composables/use-plugin.ts index 6bb856d096b..5ab42f72cb3 100644 --- a/console/console-src/modules/system/plugins/composables/use-plugin.ts +++ b/console/console-src/modules/system/plugins/composables/use-plugin.ts @@ -1,7 +1,7 @@ import type { ComputedRef, Ref } from "vue"; import { computed } from "vue"; import { type Plugin } from "@halo-dev/api-client"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { apiClient } from "@/utils/api-client"; import { Dialog, Toast } from "@halo-dev/components"; import { useI18n } from "vue-i18n"; diff --git a/console/console-src/modules/system/roles/components/RoleEditingModal.vue b/console/console-src/modules/system/roles/components/RoleEditingModal.vue index dc8ec6d1277..421ffb02590 100644 --- a/console/console-src/modules/system/roles/components/RoleEditingModal.vue +++ b/console/console-src/modules/system/roles/components/RoleEditingModal.vue @@ -5,7 +5,7 @@ import { computed, watch } from "vue"; import { rbacAnnotations } from "@/constants/annotations"; import type { Role } from "@halo-dev/api-client"; import { useRoleForm, useRoleTemplateSelection } from "@/composables/use-role"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { reset } from "@formkit/core"; import { setFocus } from "@/formkit/utils/focus"; import { pluginLabels, roleLabels } from "@/constants/labels"; diff --git a/console/console-src/modules/system/roles/module.ts b/console/console-src/modules/system/roles/module.ts index 1f27a10aca1..38ff665a82c 100644 --- a/console/console-src/modules/system/roles/module.ts +++ b/console/console-src/modules/system/roles/module.ts @@ -7,11 +7,11 @@ export default definePlugin({ components: {}, routes: [ { - path: "/users", + path: "/users/roles", component: BasicLayout, children: [ { - path: "roles", + path: "", name: "Roles", component: RoleList, meta: { @@ -21,7 +21,7 @@ export default definePlugin({ }, }, { - path: "roles/:name", + path: ":name", name: "RoleDetail", component: RoleDetail, meta: { diff --git a/console/console-src/modules/system/users/UserList.vue b/console/console-src/modules/system/users/UserList.vue index ef98a1bdffa..8bcda7a79bc 100644 --- a/console/console-src/modules/system/users/UserList.vue +++ b/console/console-src/modules/system/users/UserList.vue @@ -48,7 +48,7 @@ const grantPermissionModal = ref(false); const selectedUserNames = ref([]); const selectedUser = ref(); -const keyword = ref(""); +const keyword = useRouteQuery("keyword", ""); const userStore = useUserStore(); @@ -57,8 +57,14 @@ const DELETEDUSER_NAME = "ghost"; // Filters const { roles } = useFetchRole(); -const selectedRoleValue = ref(); -const selectedSortValue = ref(); +const page = useRouteQuery("page", 1, { + transform: Number, +}); +const size = useRouteQuery("size", 20, { + transform: Number, +}); +const selectedRoleValue = useRouteQuery("role"); +const selectedSortValue = useRouteQuery("sort"); function handleClearFilters() { selectedRoleValue.value = undefined; @@ -76,8 +82,6 @@ watch( } ); -const page = ref(1); -const size = ref(20); const total = ref(0); const { diff --git a/console/console-src/modules/system/users/components/UserCreationModal.vue b/console/console-src/modules/system/users/components/UserCreationModal.vue index 1884c457863..c0207465aa8 100644 --- a/console/console-src/modules/system/users/components/UserCreationModal.vue +++ b/console/console-src/modules/system/users/components/UserCreationModal.vue @@ -9,7 +9,7 @@ import { Toast, VButton, VModal, VSpace } from "@halo-dev/components"; import SubmitButton from "@/components/button/SubmitButton.vue"; // libs -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { reset } from "@formkit/core"; // hooks diff --git a/console/console-src/modules/system/users/components/UserEditingModal.vue b/console/console-src/modules/system/users/components/UserEditingModal.vue index 79ba52cbf55..0727a71ae64 100644 --- a/console/console-src/modules/system/users/components/UserEditingModal.vue +++ b/console/console-src/modules/system/users/components/UserEditingModal.vue @@ -9,7 +9,7 @@ import { Toast, VButton, VModal, VSpace } from "@halo-dev/components"; import SubmitButton from "@/components/button/SubmitButton.vue"; // libs -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { reset } from "@formkit/core"; // hooks diff --git a/console/console-src/modules/system/users/components/UserPasswordChangeModal.vue b/console/console-src/modules/system/users/components/UserPasswordChangeModal.vue index 344581ce562..daeb188f054 100644 --- a/console/console-src/modules/system/users/components/UserPasswordChangeModal.vue +++ b/console/console-src/modules/system/users/components/UserPasswordChangeModal.vue @@ -4,7 +4,7 @@ import SubmitButton from "@/components/button/SubmitButton.vue"; import { ref, watch } from "vue"; import type { User } from "@halo-dev/api-client"; import { apiClient } from "@/utils/api-client"; -import cloneDeep from "lodash.clonedeep"; +import { cloneDeep } from "lodash-es"; import { reset } from "@formkit/core"; import { setFocus } from "@/formkit/utils/focus"; diff --git a/console/console-src/modules/system/users/module.ts b/console/console-src/modules/system/users/module.ts index 062b2e1ae57..cba5b88f140 100644 --- a/console/console-src/modules/system/users/module.ts +++ b/console/console-src/modules/system/users/module.ts @@ -1,6 +1,5 @@ import { definePlugin } from "@halo-dev/console-shared"; import BasicLayout from "@console/layouts/BasicLayout.vue"; -import BlankLayout from "@console/layouts/BlankLayout.vue"; import UserStatsWidget from "./widgets/UserStatsWidget.vue"; import UserList from "./UserList.vue"; import UserDetail from "./UserDetail.vue"; @@ -43,45 +42,32 @@ export default definePlugin({ }, { path: "/users", - component: BlankLayout, + component: BasicLayout, children: [ { path: "", - component: BasicLayout, - children: [ - { - path: "", - name: "Users", - component: UserList, - meta: { - title: "core.user.title", - searchable: true, - permissions: ["system:users:view"], - menu: { - name: "core.sidebar.menu.items.users", - group: "system", - icon: markRaw(IconUserSettings), - priority: 1, - mobile: true, - }, - }, + name: "Users", + component: UserList, + meta: { + title: "core.user.title", + searchable: true, + permissions: ["system:users:view"], + menu: { + name: "core.sidebar.menu.items.users", + group: "system", + icon: markRaw(IconUserSettings), + priority: 1, + mobile: true, }, - ], + }, }, { path: ":name", - component: BasicLayout, - name: "User", - children: [ - { - path: "", - name: "UserDetail", - component: UserDetail, - meta: { - title: "core.user.detail.title", - }, - }, - ], + name: "UserDetail", + component: UserDetail, + meta: { + title: "core.user.detail.title", + }, }, ], }, diff --git a/console/console-src/setup/setupModules.ts b/console/console-src/setup/setupModules.ts index bfab108a7ce..f850062611b 100644 --- a/console/console-src/setup/setupModules.ts +++ b/console/console-src/setup/setupModules.ts @@ -1,5 +1,5 @@ import { i18n } from "@/locales"; -import { coreModules } from "@console/modules"; +import modules from "@console/modules"; import router from "@console/router"; import { usePluginModuleStore } from "@/stores/plugin"; import type { PluginModule, RouteRecordAppend } from "@halo-dev/console-shared"; @@ -10,7 +10,7 @@ import type { RouteRecordRaw } from "vue-router"; import { loadStyle } from "@/utils/load-style"; export function setupCoreModules(app: App) { - coreModules.forEach((module) => { + modules.forEach((module) => { registerModule(app, module, true); }); } diff --git a/console/env.d.ts b/console/env.d.ts index 10c13c2ab61..a2f91d29ebd 100644 --- a/console/env.d.ts +++ b/console/env.d.ts @@ -23,6 +23,7 @@ declare module "vue-router" { searchable?: boolean; permissions?: string[]; core?: boolean; + hideFooter?: boolean; menu?: { name: string; group?: CoreMenuGroupId; diff --git a/console/package.json b/console/package.json index 622c08a12dd..c96d3cfafaa 100644 --- a/console/package.json +++ b/console/package.json @@ -47,28 +47,28 @@ "@codemirror/state": "^6.1.4", "@codemirror/view": "^6.5.1", "@emoji-mart/data": "^1.0.8", - "@formkit/core": "^1.3.0", - "@formkit/i18n": "^1.3.0", - "@formkit/inputs": "^1.3.0", - "@formkit/themes": "^1.3.0", - "@formkit/utils": "^1.3.0", - "@formkit/validation": "1.3.0", - "@formkit/vue": "^1.3.0", + "@formkit/core": "^1.4.0", + "@formkit/i18n": "^1.4.0", + "@formkit/inputs": "^1.4.0", + "@formkit/themes": "^1.4.0", + "@formkit/utils": "^1.4.0", + "@formkit/validation": "^1.4.0", + "@formkit/vue": "^1.4.0", "@halo-dev/api-client": "workspace:*", "@halo-dev/components": "workspace:*", "@halo-dev/console-shared": "workspace:*", "@halo-dev/richtext-editor": "workspace:*", "@tanstack/vue-query": "^4.29.1", - "@tiptap/extension-character-count": "^2.0.4", - "@uppy/core": "^3.4.0", - "@uppy/dashboard": "^3.5.1", + "@tiptap/extension-character-count": "^2.1.15", + "@uppy/core": "^3.8.0", + "@uppy/dashboard": "^3.7.1", "@uppy/drag-drop": "^3.0.3", - "@uppy/file-input": "^3.0.3", - "@uppy/locales": "^3.3.0", - "@uppy/progress-bar": "^3.0.3", - "@uppy/status-bar": "^3.2.4", - "@uppy/vue": "^1.0.2", - "@uppy/xhr-upload": "^3.3.2", + "@uppy/file-input": "^3.0.4", + "@uppy/locales": "^3.5.0", + "@uppy/progress-bar": "^3.0.4", + "@uppy/status-bar": "^3.2.5", + "@uppy/vue": "^1.1.0", + "@uppy/xhr-upload": "^3.6.0", "@vueuse/components": "^10.3.0", "@vueuse/core": "^10.3.0", "@vueuse/integrations": "^10.5.0", @@ -84,11 +84,7 @@ "floating-vue": "2.0.0-beta.24", "fuse.js": "^6.6.2", "jsencrypt": "^3.3.2", - "lodash.clonedeep": "^4.5.0", - "lodash.debounce": "^4.0.8", - "lodash.isequal": "^4.5.0", - "lodash.merge": "^4.6.2", - "lodash.sortby": "^4.7.0", + "lodash-es": "^4.17.21", "overlayscrollbars": "^2.1.1", "overlayscrollbars-vue": "^0.5.1", "path-browserify": "^1.0.1", @@ -116,11 +112,7 @@ "@tailwindcss/forms": "^0.5.7", "@tsconfig/node18": "^2.0.1", "@types/jsdom": "^20.0.1", - "@types/lodash.clonedeep": "4.5.7", - "@types/lodash.debounce": "^4.0.7", - "@types/lodash.isequal": "^4.5.6", - "@types/lodash.merge": "^4.6.7", - "@types/lodash.sortby": "^4.7.9", + "@types/lodash-es": "^4.17.12", "@types/node": "^18.11.19", "@types/qs": "^6.9.7", "@types/randomstring": "^1.1.8", diff --git a/console/packages/components/src/components/modal/Modal.stories.ts b/console/packages/components/src/components/modal/Modal.stories.ts index a18f76a6bf2..87eb41c7875 100644 --- a/console/packages/components/src/components/modal/Modal.stories.ts +++ b/console/packages/components/src/components/modal/Modal.stories.ts @@ -4,6 +4,7 @@ import { VModal } from "."; import { VButton } from "../button"; import { IconArrowLeft, IconArrowRight } from "@/icons/icons"; import { VSpace } from "../space"; +import { ref } from "vue"; const meta: Meta = { title: "Modal", @@ -12,16 +13,20 @@ const meta: Meta = { render: (args) => ({ components: { VModal, VButton, VSpace, IconArrowLeft, IconArrowRight }, setup() { - return { args }; + const modal = ref(); + return { args, modal }; }, template: ` 打开 diff --git a/console/packages/components/src/components/modal/Modal.vue b/console/packages/components/src/components/modal/Modal.vue index 967cca40d81..cbccf7aa999 100644 --- a/console/packages/components/src/components/modal/Modal.vue +++ b/console/packages/components/src/components/modal/Modal.vue @@ -1,5 +1,5 @@