diff --git a/src/main/java/run/halo/app/cache/InMemoryCacheStore.java b/src/main/java/run/halo/app/cache/InMemoryCacheStore.java index bc647c778..b6914bd70 100644 --- a/src/main/java/run/halo/app/cache/InMemoryCacheStore.java +++ b/src/main/java/run/halo/app/cache/InMemoryCacheStore.java @@ -34,7 +34,7 @@ public class InMemoryCacheStore extends AbstractStringCacheStore { /** * Lock. */ - private Lock lock = new ReentrantLock(); + private final Lock lock = new ReentrantLock(); public InMemoryCacheStore() { // Run a cache store cleaner diff --git a/src/main/java/run/halo/app/config/HaloRequestMappingHandlerMapping.java b/src/main/java/run/halo/app/config/HaloRequestMappingHandlerMapping.java new file mode 100644 index 000000000..705955f2e --- /dev/null +++ b/src/main/java/run/halo/app/config/HaloRequestMappingHandlerMapping.java @@ -0,0 +1,96 @@ +package run.halo.app.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationListener; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import run.halo.app.config.properties.HaloProperties; +import run.halo.app.event.StaticStorageChangedEvent; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +import static run.halo.app.utils.HaloUtils.URL_SEPARATOR; +import static run.halo.app.utils.HaloUtils.ensureBoth; + +/** + * @author ryanwang + * @date 2020-03-24 + */ +@Slf4j +public class HaloRequestMappingHandlerMapping extends RequestMappingHandlerMapping implements ApplicationListener { + + private final Set blackPatterns = new HashSet<>(16); + + private final PathMatcher pathMatcher; + + private final HaloProperties haloProperties; + + public HaloRequestMappingHandlerMapping(HaloProperties haloProperties) { + this.haloProperties = haloProperties; + this.initBlackPatterns(); + pathMatcher = new AntPathMatcher(); + } + + @Override + protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { + log.debug("Looking path: [{}]", lookupPath); + for (String blackPattern : blackPatterns) { + if (this.pathMatcher.match(blackPattern, lookupPath)) { + log.debug("Skipped path [{}] with pattern: [{}]", lookupPath, blackPattern); + return null; + } + } + return super.lookupHandlerMethod(lookupPath, request); + } + + private void initBlackPatterns() { + String uploadUrlPattern = ensureBoth(haloProperties.getUploadUrlPrefix(), URL_SEPARATOR) + "**"; + String adminPathPattern = ensureBoth(haloProperties.getAdminPath(), URL_SEPARATOR) + "?*/**"; + + blackPatterns.add("/themes/**"); + blackPatterns.add("/js/**"); + blackPatterns.add("/images/**"); + blackPatterns.add("/fonts/**"); + blackPatterns.add("/css/**"); + blackPatterns.add("/assets/**"); + blackPatterns.add("/color.less"); + blackPatterns.add("/swagger-ui.html"); + blackPatterns.add("/csrf"); + blackPatterns.add("/webjars/**"); + blackPatterns.add(uploadUrlPattern); + blackPatterns.add(adminPathPattern); + } + + @Override + public void onApplicationEvent(StaticStorageChangedEvent event) { + Path staticPath = event.getStaticPath(); + try (Stream rootPathStream = Files.list(staticPath)) { + synchronized (this) { + blackPatterns.clear(); + initBlackPatterns(); + rootPathStream.forEach(rootPath -> { + if (Files.isDirectory(rootPath)) { + String directoryPattern = "/" + rootPath.getFileName().toString() + "/**"; + blackPatterns.add(directoryPattern); + log.debug("Exclude for folder path pattern: [{}]", directoryPattern); + } else { + String pathPattern = "/" + rootPath.getFileName().toString(); + blackPatterns.add(pathPattern); + log.debug("Exclude for file path pattern: [{}]", pathPattern); + } + } + ); + } + } catch (IOException e) { + log.error("Failed to refresh static directory mapping", e); + } + } +} diff --git a/src/main/java/run/halo/app/config/WebMvcAutoConfiguration.java b/src/main/java/run/halo/app/config/WebMvcAutoConfiguration.java index d00898502..eb1246d3c 100644 --- a/src/main/java/run/halo/app/config/WebMvcAutoConfiguration.java +++ b/src/main/java/run/halo/app/config/WebMvcAutoConfiguration.java @@ -17,9 +17,6 @@ import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.util.AntPathMatcher; -import org.springframework.util.PathMatcher; -import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewResolverRegistry; @@ -33,12 +30,9 @@ import run.halo.app.factory.StringToEnumConverterFactory; import run.halo.app.model.support.HaloConst; import run.halo.app.security.resolver.AuthenticationArgumentResolver; -import javax.servlet.http.HttpServletRequest; import java.io.IOException; -import java.util.HashSet; import java.util.List; import java.util.Properties; -import java.util.Set; import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR; import static run.halo.app.utils.HaloUtils.*; @@ -188,48 +182,4 @@ public class WebMvcAutoConfiguration extends WebMvcConfigurationSupport { return new HaloRequestMappingHandlerMapping(haloProperties); } - private static class HaloRequestMappingHandlerMapping extends RequestMappingHandlerMapping { - - private final Set blackPatterns = new HashSet<>(16); - - private final PathMatcher pathMatcher; - - private final HaloProperties haloProperties; - - public HaloRequestMappingHandlerMapping(HaloProperties haloProperties) { - this.haloProperties = haloProperties; - this.initBlackPatterns(); - pathMatcher = new AntPathMatcher(); - } - - @Override - protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { - log.debug("Looking path: [{}]", lookupPath); - for (String blackPattern : blackPatterns) { - if (this.pathMatcher.match(blackPattern, lookupPath)) { - log.debug("Skipped path [{}] with pattern: [{}]", lookupPath, blackPattern); - return null; - } - } - return super.lookupHandlerMethod(lookupPath, request); - } - - private void initBlackPatterns() { - String uploadUrlPattern = ensureBoth(haloProperties.getUploadUrlPrefix(), URL_SEPARATOR) + "**"; - String adminPathPattern = ensureBoth(haloProperties.getAdminPath(), URL_SEPARATOR) + "?*/**"; - - blackPatterns.add("/themes/**"); - blackPatterns.add("/js/**"); - blackPatterns.add("/images/**"); - blackPatterns.add("/fonts/**"); - blackPatterns.add("/css/**"); - blackPatterns.add("/assets/**"); - blackPatterns.add("/color.less"); - blackPatterns.add("/swagger-ui.html"); - blackPatterns.add("/csrf"); - blackPatterns.add("/webjars/**"); - blackPatterns.add(uploadUrlPattern); - blackPatterns.add(adminPathPattern); - } - } } diff --git a/src/main/java/run/halo/app/controller/admin/api/StaticStorageController.java b/src/main/java/run/halo/app/controller/admin/api/StaticStorageController.java index f932e0b12..ce3d8fc2c 100644 --- a/src/main/java/run/halo/app/controller/admin/api/StaticStorageController.java +++ b/src/main/java/run/halo/app/controller/admin/api/StaticStorageController.java @@ -47,6 +47,6 @@ public class StaticStorageController { @ApiOperation("Uploads static file") public void upload(String basePath, @RequestPart("file") MultipartFile file) { - staticStorageService.update(basePath, file); + staticStorageService.upload(basePath, file); } } diff --git a/src/main/java/run/halo/app/event/StaticStorageChangedEvent.java b/src/main/java/run/halo/app/event/StaticStorageChangedEvent.java new file mode 100644 index 000000000..538f02b3a --- /dev/null +++ b/src/main/java/run/halo/app/event/StaticStorageChangedEvent.java @@ -0,0 +1,27 @@ +package run.halo.app.event; + +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +import java.nio.file.Path; + +/** + * @author ryanwang + * @date 2020-03-24 + */ +public class StaticStorageChangedEvent extends ApplicationEvent { + + @Getter + private final Path staticPath; + + /** + * Create a new {@code ApplicationEvent}. + * + * @param source the object on which the event initially occurred or with + * which the event is associated (never {@code null}) + */ + public StaticStorageChangedEvent(Object source, Path staticPath) { + super(source); + this.staticPath = staticPath; + } +} diff --git a/src/main/java/run/halo/app/factory/StringToEnumConverterFactory.java b/src/main/java/run/halo/app/factory/StringToEnumConverterFactory.java index 3214e05ec..58a31ef4d 100644 --- a/src/main/java/run/halo/app/factory/StringToEnumConverterFactory.java +++ b/src/main/java/run/halo/app/factory/StringToEnumConverterFactory.java @@ -20,7 +20,7 @@ public class StringToEnumConverterFactory implements ConverterFactory implements Converter { - private Class enumType; + private final Class enumType; private StringToEnumConverter(Class enumType) { this.enumType = enumType; diff --git a/src/main/java/run/halo/app/handler/file/SmmsFileHandler.java b/src/main/java/run/halo/app/handler/file/SmmsFileHandler.java index 5474fbf9d..6ab165749 100644 --- a/src/main/java/run/halo/app/handler/file/SmmsFileHandler.java +++ b/src/main/java/run/halo/app/handler/file/SmmsFileHandler.java @@ -54,7 +54,7 @@ public class SmmsFileHandler implements FileHandler { private final OptionService optionService; - private HttpHeaders headers = new HttpHeaders(); + private final HttpHeaders headers = new HttpHeaders(); public SmmsFileHandler(RestTemplate httpsRestTemplate, OptionService optionService) { diff --git a/src/main/java/run/halo/app/model/enums/AttachmentType.java b/src/main/java/run/halo/app/model/enums/AttachmentType.java index d1ac24060..33371fea9 100644 --- a/src/main/java/run/halo/app/model/enums/AttachmentType.java +++ b/src/main/java/run/halo/app/model/enums/AttachmentType.java @@ -43,7 +43,7 @@ public enum AttachmentType implements ValueEnum { */ TENCENTCOS(6); - private Integer value; + private final Integer value; AttachmentType(Integer value) { this.value = value; diff --git a/src/main/java/run/halo/app/model/enums/BanStatusEnum.java b/src/main/java/run/halo/app/model/enums/BanStatusEnum.java index 477e0ba24..bbc333bf4 100644 --- a/src/main/java/run/halo/app/model/enums/BanStatusEnum.java +++ b/src/main/java/run/halo/app/model/enums/BanStatusEnum.java @@ -15,7 +15,7 @@ public enum BanStatusEnum { */ NORMAL(0); - private int status; + private final int status; BanStatusEnum(int status) { this.status = status; diff --git a/src/main/java/run/halo/app/model/enums/CommentViolationTypeEnum.java b/src/main/java/run/halo/app/model/enums/CommentViolationTypeEnum.java index 5136b3ba4..896d9c884 100644 --- a/src/main/java/run/halo/app/model/enums/CommentViolationTypeEnum.java +++ b/src/main/java/run/halo/app/model/enums/CommentViolationTypeEnum.java @@ -19,7 +19,7 @@ public enum CommentViolationTypeEnum { */ FREQUENTLY(1); - private int type; + private final int type; CommentViolationTypeEnum(int type) { this.type = type; diff --git a/src/main/java/run/halo/app/model/enums/DataType.java b/src/main/java/run/halo/app/model/enums/DataType.java index 8af96b00a..a8510cc0e 100644 --- a/src/main/java/run/halo/app/model/enums/DataType.java +++ b/src/main/java/run/halo/app/model/enums/DataType.java @@ -23,7 +23,7 @@ public enum DataType implements ValueEnum { BOOL(3); - private Integer value; + private final Integer value; DataType(Integer value) { this.value = value; diff --git a/src/main/java/run/halo/app/model/enums/GlobalPathType.java b/src/main/java/run/halo/app/model/enums/GlobalPathType.java index 1fbd940e7..e5d2447e3 100644 --- a/src/main/java/run/halo/app/model/enums/GlobalPathType.java +++ b/src/main/java/run/halo/app/model/enums/GlobalPathType.java @@ -18,7 +18,7 @@ public enum GlobalPathType implements ValueEnum { */ ABSOLUTE(1); - private Integer value; + private final Integer value; GlobalPathType(Integer value) { this.value = value; diff --git a/src/main/java/run/halo/app/model/enums/MigrateType.java b/src/main/java/run/halo/app/model/enums/MigrateType.java index c38e3b5c2..08ce4c55b 100644 --- a/src/main/java/run/halo/app/model/enums/MigrateType.java +++ b/src/main/java/run/halo/app/model/enums/MigrateType.java @@ -23,7 +23,7 @@ public enum MigrateType implements ValueEnum { */ CNBLOGS(2); - private Integer value; + private final Integer value; MigrateType(Integer value) { this.value = value; diff --git a/src/main/java/run/halo/app/model/enums/OptionType.java b/src/main/java/run/halo/app/model/enums/OptionType.java index 5c9872c58..dc0efa597 100644 --- a/src/main/java/run/halo/app/model/enums/OptionType.java +++ b/src/main/java/run/halo/app/model/enums/OptionType.java @@ -18,7 +18,7 @@ public enum OptionType implements ValueEnum { */ CUSTOM(1); - private Integer value; + private final Integer value; OptionType(Integer value) { this.value = value; diff --git a/src/main/java/run/halo/app/model/enums/PostEditorType.java b/src/main/java/run/halo/app/model/enums/PostEditorType.java index 7cba53b42..e219a6a84 100644 --- a/src/main/java/run/halo/app/model/enums/PostEditorType.java +++ b/src/main/java/run/halo/app/model/enums/PostEditorType.java @@ -16,7 +16,7 @@ public enum PostEditorType implements ValueEnum { */ RICHTEXT(1); - private Integer value; + private final Integer value; PostEditorType(Integer value) { this.value = value; diff --git a/src/main/java/run/halo/app/model/enums/PostPermalinkType.java b/src/main/java/run/halo/app/model/enums/PostPermalinkType.java index 566f9197b..c9ce61ee3 100644 --- a/src/main/java/run/halo/app/model/enums/PostPermalinkType.java +++ b/src/main/java/run/halo/app/model/enums/PostPermalinkType.java @@ -28,7 +28,7 @@ public enum PostPermalinkType implements ValueEnum { */ ID(3); - private Integer value; + private final Integer value; PostPermalinkType(Integer value) { this.value = value; diff --git a/src/main/java/run/halo/app/model/enums/StaticDeployType.java b/src/main/java/run/halo/app/model/enums/StaticDeployType.java index 4dcd9a50d..a57df9b75 100644 --- a/src/main/java/run/halo/app/model/enums/StaticDeployType.java +++ b/src/main/java/run/halo/app/model/enums/StaticDeployType.java @@ -18,7 +18,7 @@ public enum StaticDeployType implements ValueEnum { */ NETLIFY(1); - private Integer value; + private final Integer value; StaticDeployType(Integer value) { this.value = value; diff --git a/src/main/java/run/halo/app/model/properties/UpOssProperties.java b/src/main/java/run/halo/app/model/properties/UpOssProperties.java index 736d9cfbf..f470b2263 100644 --- a/src/main/java/run/halo/app/model/properties/UpOssProperties.java +++ b/src/main/java/run/halo/app/model/properties/UpOssProperties.java @@ -52,8 +52,8 @@ public enum UpOssProperties implements PropertyEnum { OSS_THUMBNAIL_STYLE_RULE("oss_upyun_thumbnail_style_rule", String.class, ""); private final String defaultValue; - private String value; - private Class type; + private final String value; + private final Class type; UpOssProperties(String value, Class type, String defaultValue) { this.defaultValue = defaultValue; diff --git a/src/main/java/run/halo/app/security/filter/AbstractAuthenticationFilter.java b/src/main/java/run/halo/app/security/filter/AbstractAuthenticationFilter.java index 26f5b7df6..913c459e5 100644 --- a/src/main/java/run/halo/app/security/filter/AbstractAuthenticationFilter.java +++ b/src/main/java/run/halo/app/security/filter/AbstractAuthenticationFilter.java @@ -46,7 +46,7 @@ public abstract class AbstractAuthenticationFilter extends OncePerRequestFilter protected final OptionService optionService; protected final AbstractStringCacheStore cacheStore; private final UrlPathHelper urlPathHelper = new UrlPathHelper(); - private OneTimeTokenService oneTimeTokenService; + private final OneTimeTokenService oneTimeTokenService; private volatile AuthenticationFailureHandler failureHandler; /** diff --git a/src/main/java/run/halo/app/service/StaticStorageService.java b/src/main/java/run/halo/app/service/StaticStorageService.java index f73f24e0f..305abfe10 100644 --- a/src/main/java/run/halo/app/service/StaticStorageService.java +++ b/src/main/java/run/halo/app/service/StaticStorageService.java @@ -14,6 +14,8 @@ import java.util.List; */ public interface StaticStorageService { + String API_FOLDER_NAME = "api"; + /** * Static folder location. */ @@ -47,5 +49,5 @@ public interface StaticStorageService { * @param basePath base path * @param file file must not be null. */ - void update(String basePath, @NonNull MultipartFile file); + void upload(String basePath, @NonNull MultipartFile file); } diff --git a/src/main/java/run/halo/app/service/impl/OptionServiceImpl.java b/src/main/java/run/halo/app/service/impl/OptionServiceImpl.java index 8055f2cd1..620fc4919 100644 --- a/src/main/java/run/halo/app/service/impl/OptionServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/OptionServiceImpl.java @@ -54,7 +54,7 @@ public class OptionServiceImpl extends AbstractCrudService impl private final AbstractStringCacheStore cacheStore; private final Map propertyEnumMap; private final ApplicationEventPublisher eventPublisher; - private HaloProperties haloProperties; + private final HaloProperties haloProperties; public OptionServiceImpl(HaloProperties haloProperties, OptionRepository optionRepository, diff --git a/src/main/java/run/halo/app/service/impl/StaticStorageServiceImpl.java b/src/main/java/run/halo/app/service/impl/StaticStorageServiceImpl.java index d4cd75e83..2c3b45cb2 100644 --- a/src/main/java/run/halo/app/service/impl/StaticStorageServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/StaticStorageServiceImpl.java @@ -3,12 +3,16 @@ package run.halo.app.service.impl; import cn.hutool.core.util.IdUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationListener; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.springframework.web.multipart.MultipartFile; import run.halo.app.config.properties.HaloProperties; +import run.halo.app.event.StaticStorageChangedEvent; import run.halo.app.exception.FileOperationException; import run.halo.app.exception.ServiceException; import run.halo.app.model.support.StaticFile; @@ -32,16 +36,17 @@ import java.util.stream.Stream; */ @Service @Slf4j -public class StaticStorageServiceImpl implements StaticStorageService { +public class StaticStorageServiceImpl implements StaticStorageService, ApplicationListener { private final Path staticDir; - private final HaloProperties haloProperties; + private final ApplicationEventPublisher eventPublisher; - public StaticStorageServiceImpl(HaloProperties haloProperties) throws IOException { + public StaticStorageServiceImpl(HaloProperties haloProperties, + ApplicationEventPublisher eventPublisher) throws IOException { staticDir = Paths.get(haloProperties.getWorkDir(), STATIC_FOLDER); + this.eventPublisher = eventPublisher; FileUtils.createIfAbsent(staticDir); - this.haloProperties = haloProperties; } @Override @@ -100,6 +105,7 @@ public class StaticStorageServiceImpl implements StaticStorageService { } else { Files.deleteIfExists(path); } + onChange(); } catch (IOException e) { throw new FileOperationException("文件 " + relativePath + " 删除失败", e); } @@ -111,6 +117,10 @@ public class StaticStorageServiceImpl implements StaticStorageService { Path path; + if (StringUtils.startsWith(folderName, API_FOLDER_NAME)) { + throw new FileOperationException("目录名称 " + folderName + " 不合法"); + } + if (StringUtils.isEmpty(basePath)) { path = Paths.get(staticDir.toString(), folderName); } else { @@ -129,11 +139,15 @@ public class StaticStorageServiceImpl implements StaticStorageService { } @Override - public void update(String basePath, MultipartFile file) { + public void upload(String basePath, MultipartFile file) { Assert.notNull(file, "Multipart file must not be null"); Path uploadPath; + if (StringUtils.startsWith(file.getOriginalFilename(), API_FOLDER_NAME)) { + throw new FileOperationException("文件名称 " + file.getOriginalFilename() + " 不合法"); + } + if (StringUtils.isEmpty(basePath)) { uploadPath = Paths.get(staticDir.toString(), file.getOriginalFilename()); } else { @@ -147,8 +161,18 @@ public class StaticStorageServiceImpl implements StaticStorageService { try { Files.createFile(uploadPath); file.transferTo(uploadPath); + onChange(); } catch (IOException e) { throw new ServiceException("上传文件失败").setErrorData(uploadPath); } } + + private void onChange() { + eventPublisher.publishEvent(new StaticStorageChangedEvent(this, staticDir)); + } + + @Override + public void onApplicationEvent(ApplicationStartedEvent event) { + onChange(); + } }