diff --git a/src/main/java/run/halo/app/content/permalinks/CategoryPermalinkPolicy.java b/src/main/java/run/halo/app/content/permalinks/CategoryPermalinkPolicy.java index d4e363a6c..619d6c89e 100644 --- a/src/main/java/run/halo/app/content/permalinks/CategoryPermalinkPolicy.java +++ b/src/main/java/run/halo/app/content/permalinks/CategoryPermalinkPolicy.java @@ -58,8 +58,8 @@ public class CategoryPermalinkPolicy @Override public void onPermalinkDelete(Category category) { - applicationContext.publishEvent(new PermalinkIndexDeleteCommand(this, getLocator(category), - category.getStatusOrDefault().getPermalink())); + applicationContext.publishEvent( + new PermalinkIndexDeleteCommand(this, getLocator(category))); } private ExtensionLocator getLocator(Category category) { diff --git a/src/main/java/run/halo/app/content/permalinks/ExtensionLocator.java b/src/main/java/run/halo/app/content/permalinks/ExtensionLocator.java index 062363a8d..a9c256756 100644 --- a/src/main/java/run/halo/app/content/permalinks/ExtensionLocator.java +++ b/src/main/java/run/halo/app/content/permalinks/ExtensionLocator.java @@ -11,6 +11,20 @@ import run.halo.app.extension.GroupVersionKind; * @param slug extension slug */ public record ExtensionLocator(GroupVersionKind gvk, String name, String slug) { + + /** + * Create a new {@link ExtensionLocator} instance. + * + * @param gvk group version kind + * @param name extension name + * @param slug extension slug + */ + public ExtensionLocator { + Objects.requireNonNull(gvk, "Group version kind must not be null"); + Objects.requireNonNull(name, "Extension name must not be null"); + Objects.requireNonNull(slug, "Extension slug must not be null"); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/run/halo/app/content/permalinks/PostPermalinkPolicy.java b/src/main/java/run/halo/app/content/permalinks/PostPermalinkPolicy.java index 556cfed9f..15730bd02 100644 --- a/src/main/java/run/halo/app/content/permalinks/PostPermalinkPolicy.java +++ b/src/main/java/run/halo/app/content/permalinks/PostPermalinkPolicy.java @@ -10,6 +10,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import run.halo.app.core.extension.Post; import run.halo.app.extension.GroupVersionKind; +import run.halo.app.infra.utils.PathUtils; import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.router.PermalinkIndexAddCommand; import run.halo.app.theme.router.PermalinkIndexDeleteCommand; @@ -23,7 +24,6 @@ import run.halo.app.theme.router.PermalinkWatch; */ @Component public class PostPermalinkPolicy implements PermalinkPolicy<Post>, PermalinkWatch<Post> { - private final GroupVersionKind gvk = GroupVersionKind.fromExtension(Post.class); private static final NumberFormat NUMBER_FORMAT = new DecimalFormat("00"); @@ -65,8 +65,7 @@ public class PostPermalinkPolicy implements PermalinkPolicy<Post>, PermalinkWatc @Override public void onPermalinkDelete(Post post) { - applicationContext.publishEvent(new PermalinkIndexDeleteCommand(this, getLocator(post), - post.getStatusOrDefault().getPermalink())); + applicationContext.publishEvent(new PermalinkIndexDeleteCommand(this, getLocator(post))); } private ExtensionLocator getLocator(Post post) { @@ -85,6 +84,8 @@ public class PostPermalinkPolicy implements PermalinkPolicy<Post>, PermalinkWatc properties.put("year", String.valueOf(zonedDateTime.getYear())); properties.put("month", NUMBER_FORMAT.format(zonedDateTime.getMonthValue())); properties.put("day", NUMBER_FORMAT.format(zonedDateTime.getDayOfMonth())); - return PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(pattern, properties); + + String simplifiedPattern = PathUtils.simplifyPathPattern(pattern); + return PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(simplifiedPattern, properties); } } diff --git a/src/main/java/run/halo/app/content/permalinks/TagPermalinkPolicy.java b/src/main/java/run/halo/app/content/permalinks/TagPermalinkPolicy.java index 979c28e52..9cf816550 100644 --- a/src/main/java/run/halo/app/content/permalinks/TagPermalinkPolicy.java +++ b/src/main/java/run/halo/app/content/permalinks/TagPermalinkPolicy.java @@ -57,8 +57,7 @@ public class TagPermalinkPolicy implements PermalinkPolicy<Tag>, PermalinkWatch< @Override public void onPermalinkDelete(Tag tag) { - applicationContext.publishEvent(new PermalinkIndexDeleteCommand(this, getLocator(tag), - tag.getStatusOrDefault().getPermalink())); + applicationContext.publishEvent(new PermalinkIndexDeleteCommand(this, getLocator(tag))); } private ExtensionLocator getLocator(Tag tag) { diff --git a/src/main/java/run/halo/app/core/extension/reconciler/SinglePageReconciler.java b/src/main/java/run/halo/app/core/extension/reconciler/SinglePageReconciler.java index e1a23d376..9e8efc6de 100644 --- a/src/main/java/run/halo/app/core/extension/reconciler/SinglePageReconciler.java +++ b/src/main/java/run/halo/app/core/extension/reconciler/SinglePageReconciler.java @@ -133,8 +133,7 @@ public class SinglePageReconciler implements Reconciler<Reconciler.Request> { .setPermalink(PathUtils.combinePath(singlePage.getSpec().getSlug())); ExtensionLocator locator = new ExtensionLocator(GVK, singlePage.getMetadata().getName(), singlePage.getSpec().getSlug()); - applicationContext.publishEvent(new PermalinkIndexDeleteCommand(this, locator, - singlePage.getStatusOrDefault().getPermalink())); + applicationContext.publishEvent(new PermalinkIndexDeleteCommand(this, locator)); templateRouteManager.changeTemplatePattern(DefaultTemplateEnum.SINGLE_PAGE.getValue()); } diff --git a/src/main/java/run/halo/app/infra/utils/PathUtils.java b/src/main/java/run/halo/app/infra/utils/PathUtils.java index 0ee96f186..bd7b13819 100644 --- a/src/main/java/run/halo/app/infra/utils/PathUtils.java +++ b/src/main/java/run/halo/app/infra/utils/PathUtils.java @@ -47,4 +47,35 @@ public class PathUtils { public static String appendPathSeparatorIfMissing(String path) { return StringUtils.appendIfMissing(path, "/", "/"); } + + /** + * <p>Remove the regex in the path pattern placeholder.</p> + * <p>For example: </p> + * <ul> + * <li>'{@code /{year:\d{4}}/{month:\d{2}}}' → '{@code /{year}/{month}}'</li> + * <li>'{@code /archives/{year:\d{4}}/{month:\d{2}}}' → '{@code /archives/{year}/{month} + * }'</li> + * <li>'{@code /archives/{year:\d{4}}/{slug}}' → '{@code /archives/{year}/{slug}}'</li> + * </ul> + * + * @param pattern path pattern + * @return Simplified path pattern + */ + public static String simplifyPathPattern(String pattern) { + if (StringUtils.isBlank(pattern)) { + return StringUtils.EMPTY; + } + String[] parts = StringUtils.split(pattern, '/'); + for (int i = 0; i < parts.length; i++) { + String part = parts[i]; + if (part.startsWith("{") && part.endsWith("}")) { + int colonIdx = part.indexOf(':'); + if (colonIdx != -1) { + parts[i] = part.substring(0, colonIdx) + part.charAt(part.length() - 1); + } + + } + } + return combinePath(parts); + } } diff --git a/src/main/java/run/halo/app/theme/router/PermalinkIndexDeleteCommand.java b/src/main/java/run/halo/app/theme/router/PermalinkIndexDeleteCommand.java index 7c5990c53..d5bd880f3 100644 --- a/src/main/java/run/halo/app/theme/router/PermalinkIndexDeleteCommand.java +++ b/src/main/java/run/halo/app/theme/router/PermalinkIndexDeleteCommand.java @@ -12,19 +12,13 @@ import run.halo.app.content.permalinks.ExtensionLocator; */ public class PermalinkIndexDeleteCommand extends ApplicationEvent { private final ExtensionLocator locator; - private final String permalink; - public PermalinkIndexDeleteCommand(Object source, ExtensionLocator locator, String permalink) { + public PermalinkIndexDeleteCommand(Object source, ExtensionLocator locator) { super(source); this.locator = locator; - this.permalink = permalink; } public ExtensionLocator getLocator() { return locator; } - - public String getPermalink() { - return permalink; - } } diff --git a/src/main/java/run/halo/app/theme/router/PermalinkIndexer.java b/src/main/java/run/halo/app/theme/router/PermalinkIndexer.java index 1275f326b..075d8977b 100644 --- a/src/main/java/run/halo/app/theme/router/PermalinkIndexer.java +++ b/src/main/java/run/halo/app/theme/router/PermalinkIndexer.java @@ -3,12 +3,11 @@ package run.halo.app.theme.router; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.springframework.context.event.EventListener; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; import run.halo.app.content.permalinks.ExtensionLocator; import run.halo.app.extension.GroupVersionKind; @@ -21,9 +20,12 @@ import run.halo.app.extension.GroupVersionKind; @Component public class PermalinkIndexer { private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - private final MultiValueMap<GroupVersionKind, String> permalinkLookup = - new LinkedMultiValueMap<>(); - private final Map<String, ExtensionLocator> permalinkLocatorMap = new HashMap<>(); + + private final Map<GvkName, String> gvkNamePermalinkLookup = new HashMap<>(); + private final Map<String, ExtensionLocator> permalinkLocatorLookup = new HashMap<>(); + + record GvkName(GroupVersionKind gvk, String name) { + } /** * Register extension and permalink mapping. @@ -34,8 +36,9 @@ public class PermalinkIndexer { public void register(ExtensionLocator locator, String permalink) { readWriteLock.writeLock().lock(); try { - permalinkLookup.add(locator.gvk(), permalink); - permalinkLocatorMap.put(permalink, locator); + GvkName gvkName = new GvkName(locator.gvk(), locator.name()); + gvkNamePermalinkLookup.put(gvkName, permalink); + permalinkLocatorLookup.put(permalink, locator); } finally { readWriteLock.writeLock().unlock(); } @@ -44,110 +47,87 @@ public class PermalinkIndexer { /** * Remove extension and permalink mapping. * - * @param locator extension locator - * @param permalink extension permalink for theme template route + * @param locator extension info */ - public void remove(ExtensionLocator locator, String permalink) { + public void remove(ExtensionLocator locator) { readWriteLock.writeLock().lock(); try { - List<String> permalinks = permalinkLookup.get(locator.gvk()); - if (permalinks != null) { - permalinks.remove(permalink); - if (permalinks.isEmpty()) { - permalinkLookup.remove(locator.gvk()); - } + String permalink = + gvkNamePermalinkLookup.remove(new GvkName(locator.gvk(), locator.name())); + if (permalink != null) { + permalinkLocatorLookup.remove(permalink); } - permalinkLocatorMap.remove(permalink); } finally { readWriteLock.writeLock().unlock(); } } /** - * Lookup extension locator by permalink. + * Gets permalink by {@link GroupVersionKind}. + * + * @param gvk group version kind + * @return permalinks + */ + @NonNull + public List<String> getPermalinks(GroupVersionKind gvk) { + readWriteLock.readLock().lock(); + try { + return gvkNamePermalinkLookup.entrySet() + .stream() + .filter(entry -> entry.getKey().gvk.equals(gvk)) + .map(Map.Entry::getValue) + .toList(); + } finally { + readWriteLock.readLock().unlock(); + } + } + + /** + * Lookup extension info by permalink. * * @param permalink extension permalink for theme template route * @return extension locator */ + @Nullable public ExtensionLocator lookup(String permalink) { readWriteLock.readLock().lock(); try { - return permalinkLocatorMap.get(permalink); + return permalinkLocatorLookup.get(permalink); } finally { readWriteLock.readLock().unlock(); } } /** - * Gets permalinks by extension's {@link GroupVersionKind}. + * Lookup extension permalink by {@link GroupVersionKind} and {@code name}. * - * @param gvk extension's {@link GroupVersionKind} - * @return permalinks for extension's {@link GroupVersionKind} + * @param gvk group version kind + * @param name extension name + * @return {@code true} if contains, otherwise {@code false} */ - public List<String> getPermalinks(GroupVersionKind gvk) { + public boolean containsName(GroupVersionKind gvk, String name) { readWriteLock.readLock().lock(); try { - return Objects.requireNonNullElse(permalinkLookup.get(gvk), List.of()); + return gvkNamePermalinkLookup.containsKey(new GvkName(gvk, name)); } finally { readWriteLock.readLock().unlock(); } } /** - * Lookup extension resource names by {@link GroupVersionKind}. + * Lookup extension permalink by {@link GroupVersionKind} and {@code slug}. * - * @param gvk extension's {@link GroupVersionKind} - * @return extension resource names + * @param gvk group version kind + * @param slug extension slug + * @return {@code true} if contains, otherwise {@code false} */ - public List<String> getNames(GroupVersionKind gvk) { + public boolean containsSlug(GroupVersionKind gvk, String slug) { readWriteLock.readLock().lock(); try { - return permalinkLocatorMap.values() + return permalinkLocatorLookup.values() .stream() - .filter(locator -> locator.gvk().equals(gvk)) - .map(ExtensionLocator::name) - .toList(); - } finally { - readWriteLock.readLock().unlock(); - } - } - - /** - * Lookup extension resource slugs by {@link GroupVersionKind}. - * - * @param gvk extension's {@link GroupVersionKind} - * @return extension resource slugs - */ - public List<String> getSlugs(GroupVersionKind gvk) { - readWriteLock.readLock().lock(); - try { - return permalinkLocatorMap.values() - .stream() - .filter(locator -> locator.gvk().equals(gvk)) - .map(ExtensionLocator::slug) - .toList(); - } finally { - readWriteLock.readLock().unlock(); - } - } - - /** - * Lookup extension slug by resource name. - * - * @param gvk extension's {@link GroupVersionKind} - * @param name extension resource name - * @return extension slug specified by resource name - */ - public String getSlugByName(GroupVersionKind gvk, String name) { - readWriteLock.readLock().lock(); - try { - return permalinkLocatorMap.values() - .stream() - .filter(locator -> locator.gvk().equals(gvk)) - .filter(locator -> locator.name().equals(name)) - .findFirst() - .map(ExtensionLocator::slug) - .orElseThrow(); + .anyMatch(locator -> locator.gvk().equals(gvk) + && locator.slug().equals(slug)); } finally { readWriteLock.readLock().unlock(); } @@ -163,10 +143,10 @@ public class PermalinkIndexer { public String getNameBySlug(GroupVersionKind gvk, String slug) { readWriteLock.readLock().lock(); try { - return permalinkLocatorMap.values() + return permalinkLocatorLookup.values() .stream() - .filter(locator -> locator.gvk().equals(gvk)) - .filter(locator -> locator.slug().equals(slug)) + .filter(locator -> locator.gvk().equals(gvk) + && locator.slug().equals(slug)) .findFirst() .map(ExtensionLocator::name) .orElseThrow(); @@ -180,8 +160,8 @@ public class PermalinkIndexer { * * @return permalinkLookup map size */ - protected long permalinkLookupSize() { - return permalinkLookup.size(); + protected long gvkNamePermalinkMapSize() { + return gvkNamePermalinkLookup.size(); } /** @@ -190,7 +170,7 @@ public class PermalinkIndexer { * @return permalinkLocatorMap map size */ protected long permalinkLocatorMapSize() { - return permalinkLocatorMap.size(); + return permalinkLocatorLookup.size(); } @EventListener(PermalinkIndexAddCommand.class) @@ -200,12 +180,12 @@ public class PermalinkIndexer { @EventListener(PermalinkIndexDeleteCommand.class) public void onPermalinkDelete(PermalinkIndexDeleteCommand deleteCommand) { - remove(deleteCommand.getLocator(), deleteCommand.getPermalink()); + remove(deleteCommand.getLocator()); } @EventListener(PermalinkIndexUpdateCommand.class) public void onPermalinkUpdate(PermalinkIndexUpdateCommand updateCommand) { - remove(updateCommand.getLocator(), updateCommand.getPermalink()); + remove(updateCommand.getLocator()); register(updateCommand.getLocator(), updateCommand.getPermalink()); } } diff --git a/src/main/java/run/halo/app/theme/router/strategy/ArchivesRouteStrategy.java b/src/main/java/run/halo/app/theme/router/strategy/ArchivesRouteStrategy.java index e28f80372..bcb832cdb 100644 --- a/src/main/java/run/halo/app/theme/router/strategy/ArchivesRouteStrategy.java +++ b/src/main/java/run/halo/app/theme/router/strategy/ArchivesRouteStrategy.java @@ -40,9 +40,10 @@ public class ArchivesRouteStrategy implements TemplateRouterStrategy { public RouterFunction<ServerResponse> getRouteFunction(String template, String prefix) { return RouterFunctions .route(GET(prefix) - .or(GET(PathUtils.combinePath(prefix, "/page/{page}"))) - .or(GET(PathUtils.combinePath(prefix, "/{year}/{month}"))) - .or(GET(PathUtils.combinePath(prefix, "/{year}/{month}/page/{page}"))) + .or(GET(PathUtils.combinePath(prefix, "/page/{page:\\d+}"))) + .or(GET(PathUtils.combinePath(prefix, "/{year:\\d{4}}/{month:\\d{2}}"))) + .or(GET(PathUtils.combinePath(prefix, + "/{year:\\d{4}}/{month:\\d{2}}/page/{page:\\d+}"))) .and(accept(MediaType.TEXT_HTML)), request -> ServerResponse.ok() .render(DefaultTemplateEnum.ARCHIVES.getValue(), diff --git a/src/main/java/run/halo/app/theme/router/strategy/CategoryRouteStrategy.java b/src/main/java/run/halo/app/theme/router/strategy/CategoryRouteStrategy.java index 4b3848127..bae7d97ac 100644 --- a/src/main/java/run/halo/app/theme/router/strategy/CategoryRouteStrategy.java +++ b/src/main/java/run/halo/app/theme/router/strategy/CategoryRouteStrategy.java @@ -5,7 +5,6 @@ import static org.springframework.web.reactive.function.server.RequestPredicates import static run.halo.app.theme.router.TemplateRouterStrategy.PageUrlUtils.pageNum; import static run.halo.app.theme.router.TemplateRouterStrategy.PageUrlUtils.totalPage; -import java.util.List; import java.util.Map; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; @@ -53,13 +52,12 @@ public class CategoryRouteStrategy implements TemplateRouterStrategy { public RouterFunction<ServerResponse> getRouteFunction(String template, String prefix) { return RouterFunctions .route(GET(PathUtils.combinePath(prefix, "/{slug}")) - .or(GET(PathUtils.combinePath(prefix, "/{slug}/page/{page}"))) + .or(GET(PathUtils.combinePath(prefix, "/{slug}/page/{page:\\d+}"))) .and(accept(MediaType.TEXT_HTML)), request -> { String slug = request.pathVariable("slug"); GroupVersionKind gvk = GroupVersionKind.fromExtension(Category.class); - List<String> slugs = permalinkIndexer.getSlugs(gvk); - if (!slugs.contains(slug)) { + if (!permalinkIndexer.containsSlug(gvk, slug)) { return ServerResponse.notFound().build(); } String categoryName = permalinkIndexer.getNameBySlug(gvk, slug); diff --git a/src/main/java/run/halo/app/theme/router/strategy/IndexRouteStrategy.java b/src/main/java/run/halo/app/theme/router/strategy/IndexRouteStrategy.java index dbf5e7ebf..91a3ef2b5 100644 --- a/src/main/java/run/halo/app/theme/router/strategy/IndexRouteStrategy.java +++ b/src/main/java/run/halo/app/theme/router/strategy/IndexRouteStrategy.java @@ -39,7 +39,7 @@ public class IndexRouteStrategy implements TemplateRouterStrategy { @Override public RouterFunction<ServerResponse> getRouteFunction(String template, String pattern) { return RouterFunctions - .route(GET("/").or(GET("/page/{page}")) + .route(GET("/").or(GET("/page/{page:\\d+}")) .and(accept(MediaType.TEXT_HTML)), request -> ServerResponse.ok() .render(DefaultTemplateEnum.INDEX.getValue(), diff --git a/src/main/java/run/halo/app/theme/router/strategy/PostRouteStrategy.java b/src/main/java/run/halo/app/theme/router/strategy/PostRouteStrategy.java index 45740b500..7d11ec7c4 100644 --- a/src/main/java/run/halo/app/theme/router/strategy/PostRouteStrategy.java +++ b/src/main/java/run/halo/app/theme/router/strategy/PostRouteStrategy.java @@ -4,7 +4,6 @@ import static org.springframework.web.reactive.function.server.RequestPredicates import static org.springframework.web.reactive.function.server.RequestPredicates.accept; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -75,32 +74,27 @@ public class PostRouteStrategy implements TemplateRouterStrategy { } return ServerResponse.ok() .render(DefaultTemplateEnum.POST.getValue(), - Map.of(PostRequestParamPredicate.NAME_PARAM, name) + Map.of(PostRequestParamPredicate.NAME_PARAM, name, + "post", postByName(name)) ); }); } return RouterFunctions .route(GET(pattern).and(accept(MediaType.TEXT_HTML)), request -> { - List<String> permalinks = - permalinkIndexer.getPermalinks( - PostRequestParamPredicate.GVK); - boolean contains = permalinks.contains(request.path()); - if (!contains) { + ExtensionLocator locator = permalinkIndexer.lookup(request.path()); + if (locator == null) { return ServerResponse.notFound().build(); } - ExtensionLocator extensionLocator = - permalinkIndexer.lookup(request.path()); PathPattern parse = PathPatternParser.defaultInstance.parse(pattern); PathPattern.PathMatchInfo pathMatchInfo = parse.matchAndExtract(PathContainer.parsePath(request.path())); Map<String, Object> model = new HashMap<>(); - model.put(PostRequestParamPredicate.NAME_PARAM, - extensionLocator.name()); + model.put(PostRequestParamPredicate.NAME_PARAM, locator.name()); if (pathMatchInfo != null) { model.putAll(pathMatchInfo.getUriVariables()); } - model.put("post", postByName(extensionLocator.name())); + model.put("post", postByName(locator.name())); return ServerResponse.ok() .render(DefaultTemplateEnum.POST.getValue(), model); }); @@ -139,17 +133,13 @@ public class PostRouteStrategy implements TemplateRouterStrategy { } if (NAME_PARAM.equals(placeholderName)) { - return RequestPredicates.queryParam(paramName, name -> { - List<String> names = permalinkIndexer.getNames(GVK); - return names.contains(name); - }); + return RequestPredicates.queryParam(paramName, + name -> permalinkIndexer.containsName(GVK, name)); } if (SLUG_PARAM.equals(placeholderName)) { - return RequestPredicates.queryParam(paramName, slug -> { - List<String> slugs = permalinkIndexer.getSlugs(GVK); - return slugs.contains(slug); - }); + return RequestPredicates.queryParam(paramName, + slug -> permalinkIndexer.containsSlug(GVK, slug)); } throw new IllegalArgumentException( String.format("Unknown param value placeholder [%s] in pattern [%s]", diff --git a/src/main/java/run/halo/app/theme/router/strategy/SinglePageRouteStrategy.java b/src/main/java/run/halo/app/theme/router/strategy/SinglePageRouteStrategy.java index 2f402e2f5..1b03b3f0c 100644 --- a/src/main/java/run/halo/app/theme/router/strategy/SinglePageRouteStrategy.java +++ b/src/main/java/run/halo/app/theme/router/strategy/SinglePageRouteStrategy.java @@ -4,7 +4,6 @@ import static org.springframework.web.reactive.function.server.RequestPredicates import java.util.List; import java.util.Map; -import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; @@ -48,8 +47,7 @@ public class SinglePageRouteStrategy implements TemplateRouterStrategy { RequestPredicate requestPredicate = request -> false; - List<String> permalinks = - Objects.requireNonNullElse(permalinkIndexer.getPermalinks(gvk), List.of()); + List<String> permalinks = permalinkIndexer.getPermalinks(gvk); for (String permalink : permalinks) { requestPredicate = requestPredicate.or(RequestPredicates.GET(permalink)); } diff --git a/src/main/java/run/halo/app/theme/router/strategy/TagRouteStrategy.java b/src/main/java/run/halo/app/theme/router/strategy/TagRouteStrategy.java index 837210ac2..e2965b569 100644 --- a/src/main/java/run/halo/app/theme/router/strategy/TagRouteStrategy.java +++ b/src/main/java/run/halo/app/theme/router/strategy/TagRouteStrategy.java @@ -5,7 +5,6 @@ import static org.springframework.web.reactive.function.server.RequestPredicates import static run.halo.app.theme.router.TemplateRouterStrategy.PageUrlUtils.pageNum; import static run.halo.app.theme.router.TemplateRouterStrategy.PageUrlUtils.totalPage; -import java.util.List; import java.util.Map; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; @@ -53,15 +52,15 @@ public class TagRouteStrategy implements TemplateRouterStrategy { public RouterFunction<ServerResponse> getRouteFunction(String template, String prefix) { return RouterFunctions .route(GET(PathUtils.combinePath(prefix, "/{slug}")) - .or(GET(PathUtils.combinePath(prefix, "/{slug}/page/{page}"))) + .or(GET(PathUtils.combinePath(prefix, "/{slug}/page/{page:\\d+}"))) .and(accept(MediaType.TEXT_HTML)), request -> { GroupVersionKind gvk = GroupVersionKind.fromExtension(Tag.class); - List<String> slugs = permalinkIndexer.getSlugs(gvk); String slug = request.pathVariable("slug"); - if (!slugs.contains(slug)) { + if (!permalinkIndexer.containsSlug(gvk, slug)) { return ServerResponse.notFound().build(); } + String name = permalinkIndexer.getNameBySlug(gvk, slug); return ServerResponse.ok() .render(DefaultTemplateEnum.TAG.getValue(), diff --git a/src/main/resources/extensions/system-setting.yaml b/src/main/resources/extensions/system-setting.yaml index c4295b6c8..e8e073b73 100644 --- a/src/main/resources/extensions/system-setting.yaml +++ b/src/main/resources/extensions/system-setting.yaml @@ -32,7 +32,7 @@ spec: label: "默认角色" name: defaultRole validation: required - - group: themeRules + - group: routeRules label: 主题模板路由设置 formSchema: - $formkit: text @@ -58,9 +58,9 @@ spec: - /archives/{name} - /?p={name} - /?p={slug} - - /{year}/{slug} - - /{year}/{month}/{slug} - - /{year}/{month}/{day}/{slug} + - /{year:\d{4}}/{slug} + - /{year:\d{4}}/{month:\d{2}}/{slug} + - /{year:\d{4}}/{month:\d{2}}/{day:\d{2}}/{slug} name: post validation: required - group: post diff --git a/src/test/java/run/halo/app/infra/utils/PathUtilsTest.java b/src/test/java/run/halo/app/infra/utils/PathUtilsTest.java index 104f200df..fc12d43ea 100644 --- a/src/test/java/run/halo/app/infra/utils/PathUtilsTest.java +++ b/src/test/java/run/halo/app/infra/utils/PathUtilsTest.java @@ -43,4 +43,17 @@ class PathUtilsTest { s = PathUtils.appendPathSeparatorIfMissing(null); assertThat(s).isEqualTo(null); } + + @Test + void simplifyPathPattern() { + assertThat(PathUtils.simplifyPathPattern("/a/b/c")).isEqualTo("/a/b/c"); + assertThat(PathUtils.simplifyPathPattern("/a/{b}/c")).isEqualTo("/a/{b}/c"); + assertThat(PathUtils.simplifyPathPattern("/a/{b}/*")).isEqualTo("/a/{b}/*"); + assertThat(PathUtils.simplifyPathPattern("/archives/{year:\\d{4}}/{month:\\d{2}}")) + .isEqualTo("/archives/{year}/{month}"); + assertThat(PathUtils.simplifyPathPattern("/archives/{year:\\d{4}}/{slug}")) + .isEqualTo("/archives/{year}/{slug}"); + assertThat(PathUtils.simplifyPathPattern("/archives/{year:\\d{4}}/page/{page:\\d+}")) + .isEqualTo("/archives/{year}/page/{page}"); + } } \ No newline at end of file diff --git a/src/test/java/run/halo/app/theme/router/PermalinkIndexerTest.java b/src/test/java/run/halo/app/theme/router/PermalinkIndexerTest.java index 2bb396d9c..29a016b1d 100644 --- a/src/test/java/run/halo/app/theme/router/PermalinkIndexerTest.java +++ b/src/test/java/run/halo/app/theme/router/PermalinkIndexerTest.java @@ -49,7 +49,7 @@ class PermalinkIndexerTest { assertThat(permalinkIndexer.permalinkLocatorMapSize()).isEqualTo(1); assertThat(permalinkIndexer.permalinkLocatorMapSize()).isEqualTo(1); - permalinkIndexer.remove(locator, "/fake-permalink"); + permalinkIndexer.remove(locator); assertThat(permalinkIndexer.permalinkLocatorMapSize()).isEqualTo(0); assertThat(permalinkIndexer.permalinkLocatorMapSize()).isEqualTo(0); } @@ -77,8 +77,8 @@ class PermalinkIndexerTest { ExtensionLocator locator = new ExtensionLocator(gvk, "test-name", "test-slug"); permalinkIndexer.register(locator, "/test-permalink"); - List<String> names = permalinkIndexer.getNames(gvk); - assertThat(names).isEqualTo(List.of("fake-name", "test-name")); + assertThat(permalinkIndexer.containsName(gvk, "test-name")).isTrue(); + assertThat(permalinkIndexer.containsName(gvk, "nothing")).isFalse(); } @Test @@ -86,24 +86,9 @@ class PermalinkIndexerTest { ExtensionLocator locator = new ExtensionLocator(gvk, "test-name", "test-slug"); permalinkIndexer.register(locator, "/test-permalink"); - List<String> slugs = permalinkIndexer.getSlugs(gvk); - assertThat(slugs).isEqualTo(List.of("fake-slug", "test-slug")); - } - - @Test - void getSlugByName() { - ExtensionLocator locator = new ExtensionLocator(gvk, "test-name", "test-slug"); - permalinkIndexer.register(locator, "/test-permalink"); - - String slugByName = permalinkIndexer.getSlugByName(gvk, "test-name"); - assertThat(slugByName).isEqualTo("test-slug"); - - slugByName = permalinkIndexer.getSlugByName(gvk, "fake-name"); - assertThat(slugByName).isEqualTo("fake-slug"); - - assertThatThrownBy(() -> { - permalinkIndexer.getSlugByName(gvk, "nothing"); - }).isInstanceOf(NoSuchElementException.class); + assertThat(permalinkIndexer.containsSlug(gvk, "fake-slug")).isTrue(); + assertThat(permalinkIndexer.containsSlug(gvk, "test-slug")).isTrue(); + assertThat(permalinkIndexer.containsSlug(gvk, "nothing")).isFalse(); } @Test diff --git a/src/test/java/run/halo/app/theme/router/strategy/ArchivesRouteStrategyTest.java b/src/test/java/run/halo/app/theme/router/strategy/ArchivesRouteStrategyTest.java index bb20fa1ba..5ef45f045 100644 --- a/src/test/java/run/halo/app/theme/router/strategy/ArchivesRouteStrategyTest.java +++ b/src/test/java/run/halo/app/theme/router/strategy/ArchivesRouteStrategyTest.java @@ -82,14 +82,20 @@ class ArchivesRouteStrategyTest { .expectStatus().isOk(); client.get() - .uri(prefix + "/year/month") + .uri(prefix + "/2022/09") .exchange() .expectStatus().isOk(); client.get() - .uri(prefix + "/year/month/page/1") + .uri(prefix + "/2022/08/page/1") .exchange() .expectStatus().isOk(); + + client.get() + .uri(prefix + "/2022/8/page/1") + .exchange() + .expectStatus() + .isEqualTo(HttpStatus.NOT_FOUND); } @Test diff --git a/src/test/java/run/halo/app/theme/router/strategy/CategoryRouteStrategyTest.java b/src/test/java/run/halo/app/theme/router/strategy/CategoryRouteStrategyTest.java index af30cc862..fa6a68e33 100644 --- a/src/test/java/run/halo/app/theme/router/strategy/CategoryRouteStrategyTest.java +++ b/src/test/java/run/halo/app/theme/router/strategy/CategoryRouteStrategyTest.java @@ -20,6 +20,8 @@ import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.result.view.ViewResolver; import reactor.core.publisher.Mono; +import run.halo.app.core.extension.Category; +import run.halo.app.extension.GroupVersionKind; import run.halo.app.extension.ListResult; import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.finders.PostFinder; @@ -47,8 +49,11 @@ class CategoryRouteStrategyTest { @BeforeEach void setUp() { - when(permalinkIndexer.getSlugs(any())) - .thenReturn(List.of("category-slug-1", "category-slug-2")); + GroupVersionKind gvk = GroupVersionKind.fromExtension(Category.class); + when(permalinkIndexer.containsSlug(eq(gvk), eq("category-slug-1"))) + .thenReturn(true); + when(permalinkIndexer.containsSlug(eq(gvk), eq("category-slug-2"))) + .thenReturn(true); when(permalinkIndexer.getNameBySlug(any(), eq("category-slug-1"))) .thenReturn("category-name-1"); when(permalinkIndexer.getNameBySlug(any(), eq("category-slug-2"))) diff --git a/src/test/java/run/halo/app/theme/router/strategy/PostRouteStrategyTest.java b/src/test/java/run/halo/app/theme/router/strategy/PostRouteStrategyTest.java index 161bb0ca9..8359e3cd9 100644 --- a/src/test/java/run/halo/app/theme/router/strategy/PostRouteStrategyTest.java +++ b/src/test/java/run/halo/app/theme/router/strategy/PostRouteStrategyTest.java @@ -5,7 +5,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; -import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -64,9 +63,6 @@ class PostRouteStrategyTest { piling(); - when(permalinkIndexer.getPermalinks(any())) - .thenReturn(List.of("/posts-test/fake-slug")); - client.get() .uri("/posts-test/fake-slug") .exchange() @@ -84,9 +80,6 @@ class PostRouteStrategyTest { piling(); - when(permalinkIndexer.getPermalinks(any())) - .thenReturn(List.of("/posts-test/fake-name")); - client.get() .uri("/posts-test/fake-name") .exchange() @@ -104,9 +97,6 @@ class PostRouteStrategyTest { piling(); - when(permalinkIndexer.getPermalinks(any())) - .thenReturn(List.of("/2022/08/fake-slug")); - client.get() .uri("/2022/08/fake-slug") .exchange() @@ -144,14 +134,11 @@ class PostRouteStrategyTest { } private void piling() { - lenient().when(permalinkIndexer.getNames(any())) - .thenReturn(List.of("fake-name")); - - lenient().when(permalinkIndexer.getSlugs(any())) - .thenReturn(List.of("fake-slug")); - - lenient().when(permalinkIndexer.getSlugs(any())) - .thenReturn(List.of("fake-slug")); + GroupVersionKind postGvk = GroupVersionKind.fromExtension(Post.class); + lenient().when(permalinkIndexer.containsName(eq(postGvk), eq("fake-name"))) + .thenReturn(true); + lenient().when(permalinkIndexer.containsSlug(eq(postGvk), eq("fake-slug"))) + .thenReturn(true); lenient().when(permalinkIndexer.getNameBySlug(any(), eq("fake-slug"))) .thenReturn("fake-name"); diff --git a/src/test/java/run/halo/app/theme/router/strategy/TagRouteStrategyTest.java b/src/test/java/run/halo/app/theme/router/strategy/TagRouteStrategyTest.java index 6a8bf6b72..3b2c50e78 100644 --- a/src/test/java/run/halo/app/theme/router/strategy/TagRouteStrategyTest.java +++ b/src/test/java/run/halo/app/theme/router/strategy/TagRouteStrategyTest.java @@ -20,6 +20,8 @@ import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.result.view.ViewResolver; import reactor.core.publisher.Mono; +import run.halo.app.core.extension.Tag; +import run.halo.app.extension.GroupVersionKind; import run.halo.app.extension.ListResult; import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.finders.PostFinder; @@ -48,8 +50,9 @@ class TagRouteStrategyTest { void setUp() { lenient().when(postFinder.listByTag(anyInt(), anyInt(), any())) .thenReturn(new ListResult<>(1, 10, 0, List.of())); - when(permalinkIndexer.getSlugs(any())) - .thenReturn(List.of("fake-slug")); + GroupVersionKind gvk = GroupVersionKind.fromExtension(Tag.class); + when(permalinkIndexer.containsSlug(eq(gvk), eq("fake-slug"))) + .thenReturn(true); when(permalinkIndexer.getNameBySlug(any(), eq("fake-slug"))) .thenReturn("fake-name"); }