diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index 4a908b6b2..000000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-**我确定我已经查看了** (标注`[ ]`为`[x]`)
-
-- [ ] [Halo 使用文档](https://halo.run/docs)
-- [ ] [Halo 论坛](https://bbs.halo.run)
-- [ ] [Github Wiki 常见问题](https://github.com/halo-dev/halo/wiki/4.-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)
-- [ ] [其他 Issues](https://github.com/halo-dev/halo/issues)
-
-----
-
-**我要申请** (标注`[ ]`为`[x]`)
-
-- [ ] BUG 反馈
-- [ ] 添加新的特性或者功能
-- [ ] 请求技术支持
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..6814c1f6f
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,54 @@
+---
+name: Bug Report
+about: 事情不像预期的那样工作吗?
+title: ''
+labels: 'bug'
+assignees: ''
+
+---
+
+
+
+
+
+## 我的环境
+
+
+
+---
+
+## 错误日志
+
+
+
+---
+
+## 期望行为
+
+
+
+## 当前行为
+
+
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 000000000..4068f236c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,34 @@
+---
+name: Feature Request
+about: 想让我们为 Halo 增加什么功能吗?
+title: 'feat: '
+labels: 'Feature Request'
+assignees: ''
+
+---
+
+
+
+## 概述
+
+
+
+## 动机
+
+
+
+## 详细解释
+
+
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
new file mode 100644
index 000000000..7ba1586d1
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -0,0 +1,14 @@
+---
+name: Question
+about: 对 Halo 有任何问题吗?
+title: ''
+labels: 'question'
+assignees: ''
+
+---
+
+
\ No newline at end of file
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
new file mode 100644
index 000000000..0e1231212
--- /dev/null
+++ b/.github/workflows/gradle.yml
@@ -0,0 +1,17 @@
+name: Java CI
+
+on: [push]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Build with Gradle
+ run: ./gradlew build
diff --git a/Dockerfile b/Dockerfile
index de964aecf..cc1f2fbd5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,4 @@
-FROM openjdk:8-jre-alpine
-
+FROM adoptopenjdk/openjdk8-openj9
VOLUME /tmp
ARG JAR_FILE=build/libs/halo.jar
@@ -13,4 +12,4 @@ COPY ${JAR_FILE} halo.jar
EXPOSE ${PORT}
-ENTRYPOINT java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -server -jar halo.jar
\ No newline at end of file
+ENTRYPOINT java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -server -jar halo.jar
diff --git a/README.md b/README.md
index 25d04f1eb..ceff6a7e4 100755
--- a/README.md
+++ b/README.md
@@ -2,8 +2,6 @@
> Halo 是一款现代化的个人独立博客系统,给习惯写博客的同学多一个选择。
-
-
@@ -21,20 +19,24 @@
轻快,简洁,功能强大,使用 Java 开发的博客系统。
-> [官方社区](https://bbs.halo.run) | [QQ 交流群](https://jq.qq.com/?_wv=1027&k=5tnr930) | [Telegram 交流群](https://t.me/HaloBlog) | [Telegram 频道](https://t.me/halo_dev) | [WeHalo 小程序](https://github.com/aquanlerou/WeHalo)。
+> [官网](https://halo.run) | [社区](https://bbs.halo.run) | [QQ 交流群](https://jq.qq.com/?_wv=1027&k=5tnr930) | [Telegram 交流群](https://t.me/HaloBlog) | [Telegram 频道](https://t.me/halo_dev)
+
+## 声明
+
+> 本项目旨在创造一个好的产品以供人们使用(虽然现在并不是太好),并非一个所谓提供学习的项目。所以,不提供任何学习代码的帮助。所以,请不要在讨论群,issues,论坛发起任何有关代码学习的问题。当然,如果你是要参与代码贡献,我们非常欢迎。
## 快速开始
### 下载最新的 Halo 安装包
```bash
-curl -L https://github.com/halo-dev/halo/releases/download/v1.1.1/halo-1.1.1.jar --output halo-latest.jar
+curl -L https://github.com/halo-dev/halo/releases/download/v1.2.0/halo-1.2.0.jar --output halo-latest.jar
```
或者
```bash
-wget https://github.com/halo-dev/halo/releases/download/v1.1.1/halo-1.1.1.jar -O halo-latest.jar
+wget https://github.com/halo-dev/halo/releases/download/v1.2.0/halo-1.2.0.jar -O halo-latest.jar
```
### 启动 Halo
@@ -55,12 +57,13 @@ java -jar halo-latest.jar
- 独立评论模块(halo-comment):
- 管理 APP(halo-app):
- 主题仓库:
+- WeHalo 小程序:
## 许可证
[](https://github.com/halo-dev/halo/blob/master/LICENSE)
-> Halo 使用 GPL-v3.0 协议开源,请尽量遵守开源协议,即便是在中国。
+> Halo 使用 GPL-v3.0 协议开源,请尽量遵守开源协议。
## 贡献
参考 [CONTRIBUTING](./CONTRIBUTING.md)。
@@ -81,8 +84,6 @@ java -jar halo-latest.jar

-
-


diff --git a/build.gradle b/build.gradle
index 5ac2a52e1..e2af3f20d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,5 @@
plugins {
- id 'org.springframework.boot' version '2.1.7.RELEASE'
+ id 'org.springframework.boot' version '2.2.1.RELEASE'
id "io.freefair.lombok" version "3.6.6"
// id 'war'
id 'java'
@@ -9,7 +9,7 @@ apply plugin: 'io.spring.dependency-management'
group = 'run.halo.app'
archivesBaseName = 'halo'
-version = '1.1.1'
+version = '1.2.0'
sourceCompatibility = '1.8'
description = 'Halo, personal blog system developed in Java.'
@@ -43,7 +43,7 @@ bootJar {
ext {
ohMyEmailVersion = '0.0.4'
- hutoolVersion = '4.6.3'
+ hutoolVersion = '5.0.3'
upyunSdkVersion = '4.0.1'
qiniuSdkVersion = '7.2.18'
aliyunSdkVersion = '3.4.2'
@@ -56,6 +56,9 @@ ext {
jgitVersion = '5.3.0.201903130848-r'
flexmarkVersion = '0.42.12'
thumbnailatorVersion = '0.4.8'
+ image4jVersion = '0.7zensight1'
+ flywayVersion = '6.1.0'
+ h2Version = '1.4.196'
}
dependencies {
@@ -93,17 +96,21 @@ dependencies {
implementation "com.vladsch.flexmark:flexmark-ext-tables:$flexmarkVersion"
implementation "com.vladsch.flexmark:flexmark-ext-toc:$flexmarkVersion"
implementation "com.vladsch.flexmark:flexmark-ext-yaml-front-matter:$flexmarkVersion"
+ implementation "com.vladsch.flexmark:flexmark-ext-gitlab:$flexmarkVersion"
implementation "com.vladsch.flexmark:flexmark-html-parser:$flexmarkVersion"
implementation "net.coobird:thumbnailator:$thumbnailatorVersion"
+ implementation "net.sf.image4j:image4j:$image4jVersion"
+ implementation "org.flywaydb:flyway-core:$flywayVersion"
- runtimeOnly 'com.h2database:h2'
+ runtimeOnly "com.h2database:h2:$h2Version"
runtimeOnly 'mysql:mysql-connector-java'
+
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
-}
\ No newline at end of file
+}
diff --git a/src/main/java/run/halo/app/Application.java b/src/main/java/run/halo/app/Application.java
index 28ec81d22..4735c15b8 100755
--- a/src/main/java/run/halo/app/Application.java
+++ b/src/main/java/run/halo/app/Application.java
@@ -1,9 +1,11 @@
package run.halo.app;
+import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableAsync;
@@ -11,12 +13,10 @@ import org.springframework.scheduling.annotation.EnableScheduling;
import run.halo.app.repository.base.BaseRepositoryImpl;
/**
- *
- * Halo run!
- *
+ * Halo main class.
*
* @author ryanwang
- * @date : 2017/11/14
+ * @date 2017-11-14
*/
@SpringBootApplication
@EnableJpaAuditing
@@ -25,12 +25,29 @@ import run.halo.app.repository.base.BaseRepositoryImpl;
@EnableJpaRepositories(basePackages = "run.halo.app.repository", repositoryBaseClass = BaseRepositoryImpl.class)
public class Application extends SpringBootServletInitializer {
+ private static ConfigurableApplicationContext context;
+
public static void main(String[] args) {
// Customize the spring config location
System.setProperty("spring.config.additional-location", "file:${user.home}/.halo/,file:${user.home}/halo-dev/");
// Run application
- SpringApplication.run(Application.class, args);
+ context = SpringApplication.run(Application.class, args);
+ }
+
+ /**
+ * Restart Application.
+ */
+ public static void restart() {
+ ApplicationArguments args = context.getBean(ApplicationArguments.class);
+
+ Thread thread = new Thread(() -> {
+ context.close();
+ context = SpringApplication.run(Application.class, args.getSourceArgs());
+ });
+
+ thread.setDaemon(false);
+ thread.start();
}
@Override
diff --git a/src/main/java/run/halo/app/cache/InMemoryCacheStore.java b/src/main/java/run/halo/app/cache/InMemoryCacheStore.java
index b492c2215..f358ee461 100644
--- a/src/main/java/run/halo/app/cache/InMemoryCacheStore.java
+++ b/src/main/java/run/halo/app/cache/InMemoryCacheStore.java
@@ -67,8 +67,8 @@ public class InMemoryCacheStore extends StringCacheStore {
log.debug("Preparing to put key: [{}], value: [{}]", key, cacheWrapper);
+ lock.lock();
try {
- lock.lock();
// Get the value before
Optional valueOptional = get(key);
@@ -98,6 +98,11 @@ public class InMemoryCacheStore extends StringCacheStore {
public void preDestroy() {
log.debug("Cancelling all timer tasks");
timer.cancel();
+ clear();
+ }
+
+ private void clear() {
+ CACHE_CONTAINER.clear();
}
/**
diff --git a/src/main/java/run/halo/app/config/HaloConfiguration.java b/src/main/java/run/halo/app/config/HaloConfiguration.java
index 9b5be4d5b..925a816d8 100644
--- a/src/main/java/run/halo/app/config/HaloConfiguration.java
+++ b/src/main/java/run/halo/app/config/HaloConfiguration.java
@@ -2,6 +2,7 @@ package run.halo.app.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
@@ -24,6 +25,7 @@ import run.halo.app.security.handler.ContentAuthenticationFailureHandler;
import run.halo.app.security.handler.DefaultAuthenticationFailureHandler;
import run.halo.app.service.OptionService;
import run.halo.app.service.UserService;
+import run.halo.app.utils.HaloUtils;
import run.halo.app.utils.HttpClientUtils;
import java.security.KeyManagementException;
@@ -40,7 +42,8 @@ import java.security.NoSuchAlgorithmException;
@Slf4j
public class HaloConfiguration {
- private final static int TIMEOUT = 5000;
+ @Autowired
+ HaloProperties haloProperties;
@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
@@ -49,9 +52,11 @@ public class HaloConfiguration {
}
@Bean
- public RestTemplate httpsRestTemplate(RestTemplateBuilder builder) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
+ public RestTemplate httpsRestTemplate(RestTemplateBuilder builder)
+ throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
RestTemplate httpsRestTemplate = builder.build();
- httpsRestTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(HttpClientUtils.createHttpsClient(TIMEOUT)));
+ httpsRestTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(HttpClientUtils.createHttpsClient(
+ (int) haloProperties.getDownloadTimeout().toMillis())));
return httpsRestTemplate;
}
@@ -94,10 +99,20 @@ public class HaloConfiguration {
@Bean
public FilterRegistrationBean contentFilter(HaloProperties haloProperties,
- OptionService optionService) {
- ContentFilter contentFilter = new ContentFilter(haloProperties, optionService);
+ OptionService optionService,
+ StringCacheStore cacheStore) {
+ ContentFilter contentFilter = new ContentFilter(haloProperties, optionService, cacheStore);
contentFilter.setFailureHandler(new ContentAuthenticationFailureHandler());
- contentFilter.addExcludeUrlPatterns("/api/**", "/install", "/version", "/admin/**", "/js/**", "/css/**");
+
+ String adminPattern = HaloUtils.ensureBoth(haloProperties.getAdminPath(), "/") + "**";
+
+ contentFilter.addExcludeUrlPatterns(
+ adminPattern,
+ "/api/**",
+ "/install",
+ "/version",
+ "/js/**",
+ "/css/**");
FilterRegistrationBean contentFrb = new FilterRegistrationBean<>();
contentFrb.addUrlPatterns("/*");
@@ -110,8 +125,9 @@ public class HaloConfiguration {
@Bean
public FilterRegistrationBean apiAuthenticationFilter(HaloProperties haloProperties,
ObjectMapper objectMapper,
- OptionService optionService) {
- ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter(haloProperties, optionService);
+ OptionService optionService,
+ StringCacheStore cacheStore) {
+ ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter(haloProperties, optionService, cacheStore);
apiFilter.addExcludeUrlPatterns(
"/api/content/*/comments",
"/api/content/**/comments/**",
@@ -151,6 +167,7 @@ public class HaloConfiguration {
"/api/admin/refresh/*",
"/api/admin/installations",
"/api/admin/recoveries/migrations/*",
+ "/api/admin/migrations/*",
"/api/admin/is_installed",
"/api/admin/password/code",
"/api/admin/password/reset"
diff --git a/src/main/java/run/halo/app/config/SwaggerConfiguration.java b/src/main/java/run/halo/app/config/SwaggerConfiguration.java
index 0e3091257..e87cf19e3 100644
--- a/src/main/java/run/halo/app/config/SwaggerConfiguration.java
+++ b/src/main/java/run/halo/app/config/SwaggerConfiguration.java
@@ -13,8 +13,6 @@ import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMethod;
import run.halo.app.config.properties.HaloProperties;
import run.halo.app.model.entity.User;
-import run.halo.app.security.filter.AdminAuthenticationFilter;
-import run.halo.app.security.filter.ApiAuthenticationFilter;
import run.halo.app.security.support.UserDetail;
import springfox.documentation.builders.*;
import springfox.documentation.schema.AlternateTypeRule;
@@ -33,7 +31,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import static run.halo.app.model.support.HaloConst.HALO_VERSION;
+import static run.halo.app.model.support.HaloConst.*;
import static springfox.documentation.schema.AlternateTypeRules.newRule;
/**
@@ -80,7 +78,7 @@ public class SwaggerConfiguration {
log.debug("Doc has been disabled");
}
- return buildApiDocket("run.halo.app.admin",
+ return buildApiDocket("run.halo.app.admin.api",
"run.halo.app.controller.admin",
"/api/admin/**")
.securitySchemes(adminApiKeys())
@@ -123,8 +121,8 @@ public class SwaggerConfiguration {
private List adminApiKeys() {
return Arrays.asList(
- new ApiKey("Token from header", AdminAuthenticationFilter.ADMIN_TOKEN_HEADER_NAME, In.HEADER.name()),
- new ApiKey("Token from query", AdminAuthenticationFilter.ADMIN_TOKEN_QUERY_NAME, In.QUERY.name())
+ new ApiKey("Token from header", ADMIN_TOKEN_HEADER_NAME, In.HEADER.name()),
+ new ApiKey("Token from query", ADMIN_TOKEN_QUERY_NAME, In.QUERY.name())
);
}
@@ -139,8 +137,8 @@ public class SwaggerConfiguration {
private List contentApiKeys() {
return Arrays.asList(
- new ApiKey("Access key from header", ApiAuthenticationFilter.API_ACCESS_KEY_HEADER_NAME, In.HEADER.name()),
- new ApiKey("Access key from query", ApiAuthenticationFilter.API_ACCESS_KEY_QUERY_NAME, In.QUERY.name())
+ new ApiKey("Access key from header", API_ACCESS_KEY_HEADER_NAME, In.HEADER.name()),
+ new ApiKey("Access key from query", API_ACCESS_KEY_QUERY_NAME, In.QUERY.name())
);
}
@@ -171,7 +169,9 @@ public class SwaggerConfiguration {
.description("Documentation for Halo API")
.version(HALO_VERSION)
.termsOfServiceUrl("https://github.com/halo-dev")
- .contact(new Contact("RYAN0UP", "https://ryanc.cc/", "i#ryanc.cc"))
+ .contact(new Contact("halo-dev", "https://github.com/halo-dev/halo/issues", "i#ryanc.cc"))
+ .license("GNU General Public License v3.0")
+ .licenseUrl("https://github.com/halo-dev/halo/blob/master/LICENSE")
.build();
}
diff --git a/src/main/java/run/halo/app/config/WebMvcAutoConfiguration.java b/src/main/java/run/halo/app/config/WebMvcAutoConfiguration.java
index e3f48fe4b..f9b59fd0c 100644
--- a/src/main/java/run/halo/app/config/WebMvcAutoConfiguration.java
+++ b/src/main/java/run/halo/app/config/WebMvcAutoConfiguration.java
@@ -1,6 +1,7 @@
package run.halo.app.config;
import com.fasterxml.jackson.databind.ObjectMapper;
+import freemarker.core.TemplateClassResolver;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import lombok.extern.slf4j.Slf4j;
@@ -31,13 +32,15 @@ import java.io.IOException;
import java.util.List;
import java.util.Properties;
+import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR;
import static run.halo.app.model.support.HaloConst.HALO_ADMIN_RELATIVE_PATH;
+import static run.halo.app.utils.HaloUtils.*;
/**
* Mvc configuration.
*
* @author ryanwang
- * @date : 2018/1/2
+ * @date 2018-01-02
*/
@Slf4j
@Configuration
@@ -80,17 +83,20 @@ public class WebMvcAutoConfiguration implements WebMvcConfigurer {
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
- String workDir = FILE_PROTOCOL + haloProperties.getWorkDir();
+ String workDir = FILE_PROTOCOL + ensureSuffix(haloProperties.getWorkDir(), FILE_SEPARATOR);
+ String backupDir = FILE_PROTOCOL + ensureSuffix(haloProperties.getBackupDir(), FILE_SEPARATOR);
registry.addResourceHandler("/**")
.addResourceLocations(workDir + "templates/themes/")
.addResourceLocations(workDir + "templates/admin/")
.addResourceLocations("classpath:/admin/")
.addResourceLocations(workDir + "static/");
- registry.addResourceHandler("/upload/**")
+
+ String uploadUrlPattern = ensureBoth(haloProperties.getUploadUrlPrefix(), URL_SEPARATOR) + "**";
+ String adminPathPattern = ensureSuffix(haloProperties.getAdminPath(), URL_SEPARATOR) + "**";
+
+ registry.addResourceHandler(uploadUrlPattern)
.addResourceLocations(workDir + "upload/");
- registry.addResourceHandler("/backup/**")
- .addResourceLocations(workDir + "backup/");
- registry.addResourceHandler("/admin/**")
+ registry.addResourceHandler(adminPathPattern)
.addResourceLocations(workDir + HALO_ADMIN_RELATIVE_PATH)
.addResourceLocations("classpath:/admin/");
@@ -126,6 +132,9 @@ public class WebMvcAutoConfiguration implements WebMvcConfigurer {
// Predefine configuration
freemarker.template.Configuration configuration = configurer.createConfiguration();
+
+ configuration.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);
+
if (haloProperties.isProductionEnv()) {
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
}
diff --git a/src/main/java/run/halo/app/config/properties/HaloProperties.java b/src/main/java/run/halo/app/config/properties/HaloProperties.java
index 719475a5c..d404101ac 100644
--- a/src/main/java/run/halo/app/config/properties/HaloProperties.java
+++ b/src/main/java/run/halo/app/config/properties/HaloProperties.java
@@ -2,11 +2,15 @@ package run.halo.app.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
-import run.halo.app.model.support.HaloConst;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
+import java.time.Duration;
+
+import static run.halo.app.model.support.HaloConst.*;
+import static run.halo.app.utils.HaloUtils.ensureSuffix;
+
/**
* Halo configuration properties.
@@ -32,13 +36,34 @@ public class HaloProperties {
*/
private boolean authEnabled = true;
+ /**
+ * Admin path.
+ */
+ private String adminPath = "admin";
+
/**
* Work directory.
*/
- private String workDir = HaloConst.USER_HOME + "/.halo/";
+ private String workDir = ensureSuffix(USER_HOME, FILE_SEPARATOR) + ".halo" + FILE_SEPARATOR;
+
+ /**
+ * Halo backup directory.(Not recommended to modify this config);
+ */
+ private String backupDir = ensureSuffix(TEMP_DIR, FILE_SEPARATOR) + "halo-backup" + FILE_SEPARATOR;
+
+ /**
+ * Upload prefix.
+ */
+ private String uploadUrlPrefix = "upload";
+
+ /**
+ * Download Timeout.
+ */
+ private Duration downloadTimeout = Duration.ofSeconds(30);
public HaloProperties() throws IOException {
// Create work directory if not exist
Files.createDirectories(Paths.get(workDir));
+ Files.createDirectories(Paths.get(backupDir));
}
}
diff --git a/src/main/java/run/halo/app/controller/admin/api/AdminController.java b/src/main/java/run/halo/app/controller/admin/api/AdminController.java
index 0c5f0b332..5035bfaa1 100644
--- a/src/main/java/run/halo/app/controller/admin/api/AdminController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/AdminController.java
@@ -4,6 +4,7 @@ import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
+import run.halo.app.Application;
import run.halo.app.cache.lock.CacheLock;
import run.halo.app.model.dto.EnvironmentDTO;
import run.halo.app.model.dto.StatisticDTO;
@@ -15,6 +16,7 @@ import run.halo.app.security.token.AuthToken;
import run.halo.app.service.AdminService;
import run.halo.app.service.OptionService;
+import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
/**
@@ -22,7 +24,7 @@ import javax.validation.Valid;
*
* @author johnniang
* @author ryanwang
- * @date 3/19/19
+ * @date 2019-03-19
*/
@Slf4j
@RestController
@@ -39,7 +41,7 @@ public class AdminController {
}
@GetMapping(value = "/is_installed")
- @ApiOperation("Check install status")
+ @ApiOperation("Checks Installation status")
public boolean isInstall() {
return optionService.getByPropertyOrDefault(PrimaryProperties.IS_INSTALLED, Boolean.class, false);
}
@@ -59,13 +61,13 @@ public class AdminController {
}
@PostMapping("password/code")
- @ApiOperation("Send reset password verify code.")
+ @ApiOperation("Sends reset password verify code")
public void sendResetCode(@RequestBody @Valid ResetPasswordParam param) {
adminService.sendResetPasswordCode(param);
}
@PutMapping("password/reset")
- @ApiOperation("Reset password by verify code.")
+ @ApiOperation("Resets password by verify code")
public void resetPassword(@RequestBody @Valid ResetPasswordParam param) {
adminService.resetPasswordByCode(param);
}
@@ -77,13 +79,9 @@ public class AdminController {
return adminService.refreshToken(refreshToken);
}
- /**
- * Get some statistics about the count of posts, the count of comments, etc.
- *
- * @return counts
- */
@GetMapping("counts")
@ApiOperation("Gets count info")
+ @Deprecated
public StatisticDTO getCount() {
return adminService.getCount();
}
@@ -100,9 +98,27 @@ public class AdminController {
adminService.updateAdminAssets();
}
- @GetMapping("spring/logs")
- @ApiOperation("Get application logs")
- public BaseResponse getSpringLogs() {
- return BaseResponse.ok(HttpStatus.OK.getReasonPhrase(), adminService.getSpringLogs());
+ @GetMapping("spring/application.yaml")
+ @ApiOperation("Gets application config content")
+ public BaseResponse getSpringApplicationConfig() {
+ return BaseResponse.ok(HttpStatus.OK.getReasonPhrase(), adminService.getApplicationConfig());
+ }
+
+ @PutMapping("spring/application.yaml")
+ @ApiOperation("Updates application config content")
+ public void updateSpringApplicationConfig(@RequestParam(name = "content") String content) {
+ adminService.updateApplicationConfig(content);
+ }
+
+ @PostMapping(value = {"halo/restart", "spring/restart"})
+ @ApiOperation("Restarts halo server")
+ public void restartApplication() {
+ Application.restart();
+ }
+
+ @GetMapping(value = "halo/logfile")
+ @ApiOperation("Gets halo log file content")
+ public BaseResponse getLogFiles(@RequestParam("lines") Long lines) {
+ return BaseResponse.ok(HttpStatus.OK.getReasonPhrase(), adminService.getLogFiles(lines));
}
}
diff --git a/src/main/java/run/halo/app/controller/admin/api/AttachmentController.java b/src/main/java/run/halo/app/controller/admin/api/AttachmentController.java
index cbf4e5199..d1065e68c 100644
--- a/src/main/java/run/halo/app/controller/admin/api/AttachmentController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/AttachmentController.java
@@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import run.halo.app.model.dto.AttachmentDTO;
import run.halo.app.model.entity.Attachment;
+import run.halo.app.model.enums.AttachmentType;
import run.halo.app.model.params.AttachmentParam;
import run.halo.app.model.params.AttachmentQuery;
import run.halo.app.service.AttachmentService;
@@ -35,26 +36,14 @@ public class AttachmentController {
this.attachmentService = attachmentService;
}
- /**
- * List of attachment.
- *
- * @param pageable pageable
- * @return Page
- */
@GetMapping
public Page pageBy(@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable,
AttachmentQuery attachmentQuery) {
return attachmentService.pageDtosBy(pageable, attachmentQuery);
}
- /**
- * Get attachment by id.
- *
- * @param id attachment id
- * @return AttachmentDTO
- */
@GetMapping("{id:\\d+}")
- @ApiOperation("Get attachment detail by id")
+ @ApiOperation("Gets attachment detail by id")
public AttachmentDTO getBy(@PathVariable("id") Integer id) {
Attachment attachment = attachmentService.getById(id);
return attachmentService.convertToDto(attachment);
@@ -69,17 +58,18 @@ public class AttachmentController {
return new AttachmentDTO().convertFrom(attachmentService.update(attachment));
}
- /**
- * Delete attachment by id
- *
- * @param id id
- */
@DeleteMapping("{id:\\d+}")
- @ApiOperation("Delete attachment by id")
+ @ApiOperation("Deletes attachment permanently by id")
public AttachmentDTO deletePermanently(@PathVariable("id") Integer id) {
return attachmentService.convertToDto(attachmentService.removePermanently(id));
}
+ @DeleteMapping
+ @ApiOperation("Deletes attachments permanently in batch by id array")
+ public List deletePermanentlyInBatch(@RequestBody List ids) {
+ return attachmentService.removePermanently(ids);
+ }
+
@PostMapping("upload")
@ApiOperation("Uploads single file")
public AttachmentDTO uploadAttachment(@RequestPart("file") MultipartFile file) {
@@ -106,4 +96,10 @@ public class AttachmentController {
public List listMediaTypes() {
return attachmentService.listAllMediaType();
}
+
+ @GetMapping("types")
+ @ApiOperation("Lists all of types.")
+ public List listTypes() {
+ return attachmentService.listAllType();
+ }
}
diff --git a/src/main/java/run/halo/app/controller/admin/api/BackupController.java b/src/main/java/run/halo/app/controller/admin/api/BackupController.java
index 6f860121e..e75d7f4b0 100644
--- a/src/main/java/run/halo/app/controller/admin/api/BackupController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/BackupController.java
@@ -1,26 +1,39 @@
package run.halo.app.controller.admin.api;
+import cn.hutool.core.util.ZipUtil;
import io.swagger.annotations.ApiOperation;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestPart;
-import org.springframework.web.bind.annotation.RestController;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.time.DateFormatUtils;
+import org.json.JSONObject;
+import org.springframework.core.io.Resource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
+import run.halo.app.exception.FileOperationException;
+import run.halo.app.model.dto.BackupDTO;
import run.halo.app.model.dto.post.BasePostDetailDTO;
import run.halo.app.service.BackupService;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
-import java.util.LinkedList;
+import java.util.Date;
import java.util.List;
/**
* Backup controller
*
* @author johnniang
- * @date 19-4-26
+ * @date 2019-04-26
*/
@RestController
@RequestMapping("/api/admin/backups")
+@Slf4j
public class BackupController {
private final BackupService backupService;
@@ -29,9 +42,94 @@ public class BackupController {
this.backupService = backupService;
}
+ @PostMapping("halo")
+ @ApiOperation("Backups halo")
+ public BackupDTO backupHalo() {
+ return backupService.zipWorkDirectory();
+ }
+
+ @GetMapping("halo")
+ @ApiOperation("Gets all backups")
+ public List listBackups() {
+ return backupService.listHaloBackups();
+ }
+
+ @GetMapping("halo/{fileName:.+}")
+ @ApiOperation("Downloads backup file")
+ public ResponseEntity downloadBackup(@PathVariable("fileName") String fileName, HttpServletRequest request) {
+ log.info("Try to download backup file: [{}]", fileName);
+
+ // Load file as resource
+ Resource backupResource = backupService.loadFileAsResource(fileName);
+
+ String contentType = "application/octet-stream";
+ // Try to determine file's content type
+ try {
+ contentType = request.getServletContext().getMimeType(backupResource.getFile().getAbsolutePath());
+ } catch (IOException e) {
+ log.warn("Could not determine file type", e);
+ }
+
+ return ResponseEntity.ok()
+ .contentType(MediaType.parseMediaType(contentType))
+ .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + backupResource.getFilename() + "\"")
+ .body(backupResource);
+ }
+
+ @DeleteMapping("halo")
+ @ApiOperation("Deletes a backup")
+ public void deleteBackup(@RequestParam("filename") String filename) {
+ backupService.deleteHaloBackup(filename);
+ }
+
@PostMapping("import/markdown")
@ApiOperation("Import markdown")
public BasePostDetailDTO backupMarkdowns(@RequestPart("file") MultipartFile file) throws IOException {
return backupService.importMarkdown(file);
}
+
+ @GetMapping("export/hexo")
+ @ApiOperation("export hexo markdown")
+ public void exportMarkdowns(HttpServletResponse response) {
+ final String tmpDir = System.getProperty("java.io.tmpdir");
+ final String date = DateFormatUtils.format(new Date(), "yyyyMMddHHmmss");
+ String localFilePath = tmpDir + File.separator + "halo-markdown-" + date;
+ log.trace(localFilePath);
+ final File localFile = new File(localFilePath);
+ final File postDir = new File(localFilePath + File.separator + "posts");
+ final File passwordDir = new File(localFilePath + File.separator + "passwords");
+ final File draftDir = new File(localFilePath + File.separator + "drafts");
+ try {
+ if (!postDir.mkdirs()) {
+ throw new Exception("Create dir [" + postDir.getPath() + "] failed");
+ }
+ if (!passwordDir.mkdirs()) {
+ throw new Exception("Create dir [" + passwordDir.getPath() + "] failed");
+ }
+ if (!draftDir.mkdirs()) {
+ throw new Exception("Create dir [" + draftDir.getPath() + "] failed");
+ }
+ final JSONObject result = backupService.exportHexoMDs();
+ final List posts = (List) result.opt("posts");
+ backupService.exportHexoMd(posts, postDir.getPath());
+ final List passwords = (List) result.opt("passwords");
+ backupService.exportHexoMd(passwords, passwordDir.getPath());
+ final List drafts = (List) result.opt("drafts");
+ backupService.exportHexoMd(drafts, draftDir.getPath());
+
+ final File zipFile = ZipUtil.zip(localFile);
+ byte[] zipData;
+ try (final FileInputStream inputStream = new FileInputStream(zipFile)) {
+ zipData = IOUtils.toByteArray(inputStream);
+ response.setContentType("application/zip");
+ final String fileName = "halo-markdown-" + date + ".zip";
+ response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
+ }
+
+ response.getOutputStream().write(zipData);
+ } catch (final Exception e) {
+ log.error("Export failed", e);
+ throw new FileOperationException("文章导出失败", e);
+ }
+ }
}
diff --git a/src/main/java/run/halo/app/controller/admin/api/CategoryController.java b/src/main/java/run/halo/app/controller/admin/api/CategoryController.java
index d8ffdb92e..219835ac7 100644
--- a/src/main/java/run/halo/app/controller/admin/api/CategoryController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/CategoryController.java
@@ -21,7 +21,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Category controller.
*
* @author johnniang
- * @date 3/21/19
+ * @date 2019-03-21
*/
@RestController
@RequestMapping("/api/admin/categories")
diff --git a/src/main/java/run/halo/app/controller/admin/api/InstallController.java b/src/main/java/run/halo/app/controller/admin/api/InstallController.java
index 2ad1661ba..bfb0fb32d 100644
--- a/src/main/java/run/halo/app/controller/admin/api/InstallController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/InstallController.java
@@ -2,6 +2,7 @@ package run.halo.app.controller.admin.api;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.crypto.SecureUtil;
+import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher;
@@ -38,7 +39,7 @@ import java.util.Set;
* Installation controller.
*
* @author ryanwang
- * @date : 2019-03-17
+ * @date 2019-03-17
*/
@Slf4j
@Controller
@@ -78,6 +79,7 @@ public class InstallController {
@PostMapping
@ResponseBody
@CacheLock
+ @ApiOperation("Initializes the blog")
public BaseResponse installBlog(@RequestBody InstallParam installParam) {
// Validate manually
ValidationUtils.validate(installParam, CreateCheck.class);
@@ -179,7 +181,7 @@ public class InstallController {
@Nullable
private Category createDefaultCategoryIfAbsent() {
long categoryCount = categoryService.count();
- if (categoryCount == 0) {
+ if (categoryCount > 0) {
return null;
}
diff --git a/src/main/java/run/halo/app/controller/admin/api/JournalCommentController.java b/src/main/java/run/halo/app/controller/admin/api/JournalCommentController.java
index 195688f4b..630995ecf 100644
--- a/src/main/java/run/halo/app/controller/admin/api/JournalCommentController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/JournalCommentController.java
@@ -2,16 +2,22 @@ package run.halo.app.controller.admin.api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
+import org.springframework.data.web.SortDefault;
import org.springframework.web.bind.annotation.*;
import run.halo.app.model.dto.BaseCommentDTO;
import run.halo.app.model.entity.JournalComment;
import run.halo.app.model.enums.CommentStatus;
import run.halo.app.model.params.CommentQuery;
import run.halo.app.model.params.JournalCommentParam;
+import run.halo.app.model.vo.BaseCommentVO;
+import run.halo.app.model.vo.BaseCommentWithParentVO;
import run.halo.app.model.vo.JournalCommentWithJournalVO;
import run.halo.app.service.JournalCommentService;
+import run.halo.app.service.OptionService;
import java.util.List;
@@ -21,7 +27,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Journal comment controller.
*
* @author johnniang
- * @date 19-4-25
+ * @date 2019-04-25
*/
@RestController
@RequestMapping("/api/admin/journals/comments")
@@ -29,8 +35,12 @@ public class JournalCommentController {
private final JournalCommentService journalCommentService;
- public JournalCommentController(JournalCommentService journalCommentService) {
+ private final OptionService optionService;
+
+ public JournalCommentController(JournalCommentService journalCommentService,
+ OptionService optionService) {
this.journalCommentService = journalCommentService;
+ this.optionService = optionService;
}
@GetMapping
@@ -43,12 +53,29 @@ public class JournalCommentController {
}
@GetMapping("latest")
+ @ApiOperation("Lists latest journal comments")
public List listLatest(@RequestParam(name = "top", defaultValue = "10") int top,
@RequestParam(name = "status", required = false) CommentStatus status) {
List latestComments = journalCommentService.pageLatest(top, status).getContent();
return journalCommentService.convertToWithJournalVo(latestComments);
}
+ @GetMapping("{journalId:\\d+}/tree_view")
+ @ApiOperation("Lists comments with tree view")
+ public Page listCommentTree(@PathVariable("journalId") Integer journalId,
+ @RequestParam(name = "page", required = false, defaultValue = "0") int page,
+ @SortDefault(sort = "createTime", direction = DESC) Sort sort) {
+ return journalCommentService.pageVosAllBy(journalId, PageRequest.of(page, optionService.getCommentPageSize(), sort));
+ }
+
+ @GetMapping("{journalId:\\d+}/list_view")
+ @ApiOperation("Lists comment with list view")
+ public Page listComments(@PathVariable("journalId") Integer journalId,
+ @RequestParam(name = "page", required = false, defaultValue = "0") int page,
+ @SortDefault(sort = "createTime", direction = DESC) Sort sort) {
+ return journalCommentService.pageWithParentVoBy(journalId, PageRequest.of(page, optionService.getCommentPageSize(), sort));
+ }
+
@PostMapping
@ApiOperation("Creates a journal comment")
public BaseCommentDTO createCommentBy(@RequestBody JournalCommentParam journalCommentParam) {
diff --git a/src/main/java/run/halo/app/controller/admin/api/JournalController.java b/src/main/java/run/halo/app/controller/admin/api/JournalController.java
index d4fe65f50..adb78d2a6 100644
--- a/src/main/java/run/halo/app/controller/admin/api/JournalController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/JournalController.java
@@ -2,22 +2,15 @@ package run.halo.app.controller.admin.api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
-import org.springframework.data.web.SortDefault;
import org.springframework.web.bind.annotation.*;
import run.halo.app.model.dto.JournalDTO;
import run.halo.app.model.dto.JournalWithCmtCountDTO;
import run.halo.app.model.entity.Journal;
import run.halo.app.model.params.JournalParam;
import run.halo.app.model.params.JournalQuery;
-import run.halo.app.model.vo.BaseCommentVO;
-import run.halo.app.model.vo.BaseCommentWithParentVO;
-import run.halo.app.service.JournalCommentService;
import run.halo.app.service.JournalService;
-import run.halo.app.service.OptionService;
import javax.validation.Valid;
import java.util.List;
@@ -29,7 +22,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
*
* @author johnniang
* @author ryanwang
- * @date 19-4-25
+ * @date 2019-04-25
*/
@RestController
@RequestMapping("/api/admin/journals")
@@ -37,16 +30,8 @@ public class JournalController {
private final JournalService journalService;
- private final JournalCommentService journalCommentService;
-
- private final OptionService optionService;
-
- public JournalController(JournalService journalService,
- JournalCommentService journalCommentService,
- OptionService optionService) {
+ public JournalController(JournalService journalService) {
this.journalService = journalService;
- this.journalCommentService = journalCommentService;
- this.optionService = optionService;
}
@GetMapping
@@ -77,7 +62,8 @@ public class JournalController {
@RequestBody @Valid JournalParam journalParam) {
Journal journal = journalService.getById(id);
journalParam.update(journal);
- return new JournalDTO().convertFrom(journalService.update(journal));
+ Journal updatedJournal = journalService.updateBy(journal);
+ return journalService.convertTo(updatedJournal);
}
@DeleteMapping("{journalId:\\d+}")
@@ -86,20 +72,4 @@ public class JournalController {
Journal deletedJournal = journalService.removeById(journalId);
return journalService.convertTo(deletedJournal);
}
-
- @GetMapping("{journalId:\\d+}/comments/tree_view")
- @ApiOperation("Lists comments with tree view")
- public Page listCommentTree(@PathVariable("journalId") Integer journalId,
- @RequestParam(name = "page", required = false, defaultValue = "0") int page,
- @SortDefault(sort = "createTime", direction = DESC) Sort sort) {
- return journalCommentService.pageVosBy(journalId, PageRequest.of(page, optionService.getCommentPageSize(), sort));
- }
-
- @GetMapping("{journalId:\\d+}/comments/list_view")
- @ApiOperation("Lists comment with list view")
- public Page listComments(@PathVariable("journalId") Integer journalId,
- @RequestParam(name = "page", required = false, defaultValue = "0") int page,
- @SortDefault(sort = "createTime", direction = DESC) Sort sort) {
- return journalCommentService.pageWithParentVoBy(journalId, PageRequest.of(page, optionService.getCommentPageSize(), sort));
- }
}
diff --git a/src/main/java/run/halo/app/controller/admin/api/LinkController.java b/src/main/java/run/halo/app/controller/admin/api/LinkController.java
index ca59717c2..9934dddb4 100644
--- a/src/main/java/run/halo/app/controller/admin/api/LinkController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/LinkController.java
@@ -12,11 +12,14 @@ import run.halo.app.service.LinkService;
import javax.validation.Valid;
import java.util.List;
+import static org.springframework.data.domain.Sort.Direction.ASC;
+import static org.springframework.data.domain.Sort.Direction.DESC;
+
/**
* Link Controller
*
* @author ryanwang
- * @date : 2019/3/21
+ * @date 2019-03-21
*/
@RestController
@RequestMapping("/api/admin/links")
@@ -28,30 +31,26 @@ public class LinkController {
this.linkService = linkService;
}
- /**
- * List all links
- *
- * @param sort sort
- * @return List
- */
@GetMapping
- public List listLinks(@SortDefault(sort = "priority", direction = Sort.Direction.ASC) Sort sort) {
- return linkService.listDtos(sort);
+ @ApiOperation("Lists links")
+ public List listLinks(@SortDefault(sort = "team", direction = DESC) Sort sort) {
+ return linkService.listDtos(sort.and(Sort.by(ASC, "priority")));
}
- /**
- * Get link by id.
- *
- * @param id id
- * @return LinkDTO
- */
@GetMapping("{id:\\d+}")
- @ApiOperation("Get link detail by id")
+ @ApiOperation("Gets link detail by id")
public LinkDTO getBy(@PathVariable("id") Integer id) {
return new LinkDTO().convertFrom(linkService.getById(id));
}
+ @GetMapping("parse")
+ @ApiOperation("Gets link by parse url")
+ public LinkDTO getByParse(@RequestParam("url") String url) {
+ return linkService.getByParse(url);
+ }
+
@PostMapping
+ @ApiOperation("Creates a link")
public LinkDTO createBy(@RequestBody @Valid LinkParam linkParam) {
Link link = linkService.createBy(linkParam);
return new LinkDTO().convertFrom(link);
@@ -66,14 +65,15 @@ public class LinkController {
return new LinkDTO().convertFrom(linkService.update(link));
}
- /**
- * Delete link by id.
- *
- * @param id id
- */
@DeleteMapping("{id:\\d+}")
@ApiOperation("Delete link by id")
public void deletePermanently(@PathVariable("id") Integer id) {
linkService.removeById(id);
}
+
+ @GetMapping("teams")
+ @ApiOperation(("Lists all link teams"))
+ public List teams() {
+ return linkService.listAllTeams();
+ }
}
diff --git a/src/main/java/run/halo/app/controller/admin/api/LogController.java b/src/main/java/run/halo/app/controller/admin/api/LogController.java
index 34f02d9ce..d186a99c9 100644
--- a/src/main/java/run/halo/app/controller/admin/api/LogController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/LogController.java
@@ -20,7 +20,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Log controller.
*
* @author johnniang
- * @date 3/19/19
+ * @date 2019-03-19
*/
@RestController
@RequestMapping("/api/admin/logs")
@@ -32,12 +32,6 @@ public class LogController {
this.logService = logService;
}
- /**
- * List latest logs.
- *
- * @param top top
- * @return List of logs
- */
@GetMapping("latest")
@ApiOperation("Pages latest logs")
public List pageLatest(@RequestParam(name = "top", defaultValue = "10") int top) {
@@ -45,16 +39,14 @@ public class LogController {
}
@GetMapping
+ @ApiOperation("Lists logs")
public Page pageBy(@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable) {
Page logPage = logService.listAll(pageable);
return logPage.map(log -> new LogDTO().convertFrom(log));
}
- /**
- * Clear all logs.
- */
@GetMapping("clear")
- @ApiOperation("Clear all logs")
+ @ApiOperation("Clears all logs")
public void clear() {
logService.removeAll();
}
diff --git a/src/main/java/run/halo/app/controller/admin/api/MailController.java b/src/main/java/run/halo/app/controller/admin/api/MailController.java
index 4fd753b30..292f52952 100644
--- a/src/main/java/run/halo/app/controller/admin/api/MailController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/MailController.java
@@ -1,5 +1,6 @@
package run.halo.app.controller.admin.api;
+import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -14,7 +15,7 @@ import javax.validation.Valid;
* Mail controller.
*
* @author johnniang
- * @date 19-5-7
+ * @date 2019-05-07
*/
@RestController
@RequestMapping("/api/admin/mails")
@@ -27,6 +28,7 @@ public class MailController {
}
@PostMapping("test")
+ @ApiOperation("Tests the SMTP service")
public BaseResponse testMail(@Valid @RequestBody MailParam mailParam) {
mailService.sendMail(mailParam.getTo(), mailParam.getSubject(), mailParam.getContent());
return BaseResponse.ok("发送成功");
diff --git a/src/main/java/run/halo/app/controller/admin/api/MenuController.java b/src/main/java/run/halo/app/controller/admin/api/MenuController.java
index d85e9d7c8..8aeb534df 100644
--- a/src/main/java/run/halo/app/controller/admin/api/MenuController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/MenuController.java
@@ -20,7 +20,8 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Menu controller.
*
* @author johnniang
- * @date 4/3/19
+ * @author ryanwang
+ * @date 2019-04-03
*/
@RestController
@RequestMapping("/api/admin/menus")
@@ -34,24 +35,18 @@ public class MenuController {
@GetMapping
@ApiOperation("Lists all menus")
- public List listAll(@SortDefault(sort = "priority", direction = DESC) Sort sort) {
- return menuService.listDtos(sort);
+ public List listAll(@SortDefault(sort = "team", direction = DESC) Sort sort) {
+ return menuService.listDtos(sort.and(Sort.by(ASC, "priority")));
}
@GetMapping("tree_view")
- @ApiOperation("List as category tree")
- public List listAsTree(@SortDefault(sort = "priority", direction = ASC) Sort sort) {
- return menuService.listAsTree(sort);
+ @ApiOperation("Lists categories as tree")
+ public List listAsTree(@SortDefault(sort = "team", direction = DESC) Sort sort) {
+ return menuService.listAsTree(sort.and(Sort.by(ASC, "priority")));
}
- /**
- * Get menu by menuId.
- *
- * @param menuId menuId
- * @return MenuDTO
- */
@GetMapping("{menuId:\\d+}")
- @ApiOperation("Get menu detail by id")
+ @ApiOperation("Gets menu detail by id")
public MenuDTO getBy(@PathVariable("menuId") Integer menuId) {
return new MenuDTO().convertFrom(menuService.getById(menuId));
}
@@ -88,4 +83,10 @@ public class MenuController {
}
return new MenuDTO().convertFrom(menuService.removeById(menuId));
}
+
+ @GetMapping("teams")
+ @ApiOperation(("Lists all menu teams"))
+ public List teams() {
+ return menuService.listAllTeams();
+ }
}
diff --git a/src/main/java/run/halo/app/controller/admin/api/MigrateController.java b/src/main/java/run/halo/app/controller/admin/api/MigrateController.java
new file mode 100644
index 000000000..e8df6d20a
--- /dev/null
+++ b/src/main/java/run/halo/app/controller/admin/api/MigrateController.java
@@ -0,0 +1,45 @@
+package run.halo.app.controller.admin.api;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import run.halo.app.model.enums.MigrateType;
+import run.halo.app.service.MigrateService;
+
+/**
+ * Migrate controller
+ *
+ * @author ryanwang
+ * @date 2019-10-29
+ */
+@RestController
+@RequestMapping("/api/admin/migrations")
+public class MigrateController {
+
+ private final MigrateService migrateService;
+
+ public MigrateController(MigrateService migrateService) {
+ this.migrateService = migrateService;
+ }
+
+ @PostMapping("halo_v0_4_4")
+ @ApiOperation("Migrate from Halo 0.4.4")
+ public void migrateHaloOldVersion(@RequestPart("file") MultipartFile file) {
+ migrateService.migrate(file, MigrateType.OLD_VERSION);
+ }
+
+ @PostMapping("wordpress")
+ @ApiOperation("Migrate from WordPress")
+ public void migrateWordPress(@RequestPart("file") MultipartFile file) {
+ migrateService.migrate(file, MigrateType.WORDPRESS);
+ }
+
+ @PostMapping("cnblogs")
+ @ApiOperation("Migrate from cnblogs")
+ public void migrateCnBlogs(@RequestPart("file") MultipartFile file) {
+ migrateService.migrate(file, MigrateType.CNBLOGS);
+ }
+}
diff --git a/src/main/java/run/halo/app/controller/admin/api/OptionController.java b/src/main/java/run/halo/app/controller/admin/api/OptionController.java
index ee572e29e..da09bb9cd 100644
--- a/src/main/java/run/halo/app/controller/admin/api/OptionController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/OptionController.java
@@ -1,21 +1,30 @@
package run.halo.app.controller.admin.api;
import io.swagger.annotations.ApiOperation;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import run.halo.app.model.dto.OptionDTO;
+import run.halo.app.model.dto.OptionSimpleDTO;
+import run.halo.app.model.entity.Option;
import run.halo.app.model.params.OptionParam;
+import run.halo.app.model.params.OptionQuery;
import run.halo.app.service.OptionService;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
+import static org.springframework.data.domain.Sort.Direction.DESC;
+
/**
* Option Controller.
*
* @author johnniang
- * @date 3/20/19
+ * @author ryanwang
+ * @date 2019-03-20
*/
@RestController
@RequestMapping("/api/admin/options")
@@ -28,11 +37,13 @@ public class OptionController {
}
@GetMapping
+ @ApiOperation("Lists options")
public List listAll() {
return optionService.listDtos();
}
@PostMapping("saving")
+ @ApiOperation("Saves options")
public void saveOptions(@Valid @RequestBody List optionParams) {
optionService.save(optionParams);
}
@@ -47,6 +58,39 @@ public class OptionController {
return optionService.listOptions(keys);
}
+ @GetMapping("list_view")
+ @ApiOperation("Lists all options with list view")
+ public Page listAllWithListView(@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable,
+ OptionQuery optionQuery) {
+ return optionService.pageDtosBy(pageable, optionQuery);
+ }
+
+ @GetMapping("{id:\\d+}")
+ @ApiOperation("Gets option detail by id")
+ public OptionSimpleDTO getBy(@PathVariable("id") Integer id) {
+ Option option = optionService.getById(id);
+ return optionService.convertToDto(option);
+ }
+
+ @PostMapping
+ @ApiOperation("Creates option")
+ public void createBy(@RequestBody @Valid OptionParam optionParam) {
+ optionService.save(optionParam);
+ }
+
+ @PutMapping("{optionId:\\d+}")
+ @ApiOperation("Updates option")
+ public void updateBy(@PathVariable("optionId") Integer optionId,
+ @RequestBody @Valid OptionParam optionParam) {
+ optionService.update(optionId, optionParam);
+ }
+
+ @DeleteMapping("{optionId:\\d+}")
+ @ApiOperation("Deletes option")
+ public void deletePermanently(@PathVariable("optionId") Integer optionId) {
+ optionService.removePermanently(optionId);
+ }
+
@PostMapping("map_view/saving")
@ApiOperation("Saves options by option map")
public void saveOptionsWithMapView(@RequestBody Map optionMap) {
diff --git a/src/main/java/run/halo/app/controller/admin/api/PhotoController.java b/src/main/java/run/halo/app/controller/admin/api/PhotoController.java
index 98f5dc2ed..e87a3cc5b 100644
--- a/src/main/java/run/halo/app/controller/admin/api/PhotoController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/PhotoController.java
@@ -22,7 +22,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Photo controller
*
* @author ryanwang
- * @date : 2019-3-21
+ * @date 2019-03-21
*/
@RestController
@RequestMapping("/api/admin/photos")
@@ -34,47 +34,33 @@ public class PhotoController {
this.photoService = photoService;
}
- /**
- * List all photos
- *
- * @param sort sort
- * @return all of photos
- */
@GetMapping(value = "latest")
+ @ApiOperation("Lists latest photos")
public List listPhotos(@SortDefault(sort = "updateTime", direction = Sort.Direction.DESC) Sort sort) {
return photoService.listDtos(sort);
}
@GetMapping
+ @ApiOperation("Lists photos")
public Page pageBy(@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable,
PhotoQuery photoQuery) {
return photoService.pageDtosBy(pageable, photoQuery);
}
- /**
- * Get photo by id.
- *
- * @param photoId photo id
- * @return PhotoDTO
- */
@GetMapping("{photoId:\\d+}")
- @ApiOperation("Get photo detail by id")
+ @ApiOperation("Gets photo detail by id")
public PhotoDTO getBy(@PathVariable("photoId") Integer photoId) {
return new PhotoDTO().convertFrom(photoService.getById(photoId));
}
- /**
- * Delete photo by id.
- *
- * @param photoId photo id
- */
@DeleteMapping("{photoId:\\d+}")
- @ApiOperation("Delete photo by id")
+ @ApiOperation("Deletes photo by id")
public void deletePermanently(@PathVariable("photoId") Integer photoId) {
photoService.removeById(photoId);
}
@PostMapping
+ @ApiOperation("Creates a photo")
public PhotoDTO createBy(@Valid @RequestBody PhotoParam photoParam) {
return new PhotoDTO().convertFrom(photoService.createBy(photoParam));
}
diff --git a/src/main/java/run/halo/app/controller/admin/api/PostCommentController.java b/src/main/java/run/halo/app/controller/admin/api/PostCommentController.java
index 6c7a16e3d..3424a59c7 100644
--- a/src/main/java/run/halo/app/controller/admin/api/PostCommentController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/PostCommentController.java
@@ -2,15 +2,21 @@ package run.halo.app.controller.admin.api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
+import org.springframework.data.web.SortDefault;
import org.springframework.web.bind.annotation.*;
import run.halo.app.model.dto.BaseCommentDTO;
import run.halo.app.model.entity.PostComment;
import run.halo.app.model.enums.CommentStatus;
import run.halo.app.model.params.CommentQuery;
import run.halo.app.model.params.PostCommentParam;
+import run.halo.app.model.vo.BaseCommentVO;
+import run.halo.app.model.vo.BaseCommentWithParentVO;
import run.halo.app.model.vo.PostCommentWithPostVO;
+import run.halo.app.service.OptionService;
import run.halo.app.service.PostCommentService;
import javax.validation.Valid;
@@ -23,7 +29,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
*
* @author johnniang
* @author ryanwang
- * @date 3/19/19
+ * @date 2019-03-29
*/
@RestController
@RequestMapping("/api/admin/posts/comments")
@@ -31,8 +37,12 @@ public class PostCommentController {
private final PostCommentService postCommentService;
- public PostCommentController(PostCommentService postCommentService) {
+ private final OptionService optionService;
+
+ public PostCommentController(PostCommentService postCommentService,
+ OptionService optionService) {
this.postCommentService = postCommentService;
+ this.optionService = optionService;
}
@GetMapping
@@ -44,7 +54,7 @@ public class PostCommentController {
}
@GetMapping("latest")
- @ApiOperation("Pages latest comments")
+ @ApiOperation("Pages post latest comments")
public List listLatest(@RequestParam(name = "top", defaultValue = "10") int top,
@RequestParam(name = "status", required = false) CommentStatus status) {
// Get latest comment
@@ -54,15 +64,31 @@ public class PostCommentController {
return postCommentService.convertToWithPostVo(content);
}
+ @GetMapping("{postId:\\d+}/tree_view")
+ @ApiOperation("Lists post comments with tree view")
+ public Page listCommentTree(@PathVariable("postId") Integer postId,
+ @RequestParam(name = "page", required = false, defaultValue = "0") int page,
+ @SortDefault(sort = "createTime", direction = DESC) Sort sort) {
+ return postCommentService.pageVosAllBy(postId, PageRequest.of(page, optionService.getCommentPageSize(), sort));
+ }
+
+ @GetMapping("{postId:\\d+}/list_view")
+ @ApiOperation("Lists post comment with list view")
+ public Page listComments(@PathVariable("postId") Integer postId,
+ @RequestParam(name = "page", required = false, defaultValue = "0") int page,
+ @SortDefault(sort = "createTime", direction = DESC) Sort sort) {
+ return postCommentService.pageWithParentVoBy(postId, PageRequest.of(page, optionService.getCommentPageSize(), sort));
+ }
+
@PostMapping
- @ApiOperation("Creates a comment (new or reply)")
+ @ApiOperation("Creates a post comment (new or reply)")
public BaseCommentDTO createBy(@RequestBody PostCommentParam postCommentParam) {
PostComment createdPostComment = postCommentService.createBy(postCommentParam);
return postCommentService.convertTo(createdPostComment);
}
@PutMapping("{commentId:\\d+}/status/{status}")
- @ApiOperation("Updates comment status")
+ @ApiOperation("Updates post comment status")
public BaseCommentDTO updateStatusBy(@PathVariable("commentId") Long commentId,
@PathVariable("status") CommentStatus status) {
// Update comment status
@@ -70,13 +96,27 @@ public class PostCommentController {
return postCommentService.convertTo(updatedPostComment);
}
+ @PutMapping("status/{status}")
+ @ApiOperation("Updates post comment status in batch")
+ public List updateStatusInBatch(@PathVariable(name = "status") CommentStatus status,
+ @RequestBody List ids) {
+ List comments = postCommentService.updateStatusByIds(ids, status);
+ return postCommentService.convertTo(comments);
+ }
+
@DeleteMapping("{commentId:\\d+}")
- @ApiOperation("Deletes comment permanently and recursively")
- public BaseCommentDTO deleteBy(@PathVariable("commentId") Long commentId) {
+ @ApiOperation("Deletes post comment permanently and recursively")
+ public BaseCommentDTO deletePermanently(@PathVariable("commentId") Long commentId) {
PostComment deletedPostComment = postCommentService.removeById(commentId);
return postCommentService.convertTo(deletedPostComment);
}
+ @DeleteMapping
+ @ApiOperation("Delete post comments permanently in batch by id array")
+ public List deletePermanentlyInBatch(@RequestBody List ids) {
+ return postCommentService.removeByIds(ids);
+ }
+
@GetMapping("{commentId:\\d+}")
@ApiOperation("Gets a post comment by comment id")
public PostCommentWithPostVO getBy(@PathVariable("commentId") Long commentId) {
@@ -85,6 +125,7 @@ public class PostCommentController {
}
@PutMapping("{commentId:\\d+}")
+ @ApiOperation("Updates a post comment")
public BaseCommentDTO updateBy(@Valid @RequestBody PostCommentParam commentParam,
@PathVariable("commentId") Long commentId) {
PostComment commentToUpdate = postCommentService.getById(commentId);
diff --git a/src/main/java/run/halo/app/controller/admin/api/PostController.java b/src/main/java/run/halo/app/controller/admin/api/PostController.java
index 539a2a54a..b8027d9a7 100644
--- a/src/main/java/run/halo/app/controller/admin/api/PostController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/PostController.java
@@ -3,25 +3,26 @@ package run.halo.app.controller.admin.api;
import cn.hutool.core.util.IdUtil;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
-import org.springframework.data.web.SortDefault;
import org.springframework.web.bind.annotation.*;
import run.halo.app.cache.StringCacheStore;
+import run.halo.app.model.dto.post.BasePostDetailDTO;
import run.halo.app.model.dto.post.BasePostMinimalDTO;
import run.halo.app.model.dto.post.BasePostSimpleDTO;
import run.halo.app.model.entity.Post;
import run.halo.app.model.enums.PostStatus;
+import run.halo.app.model.params.PostContentParam;
import run.halo.app.model.params.PostParam;
import run.halo.app.model.params.PostQuery;
import run.halo.app.model.vo.PostDetailVO;
-import run.halo.app.model.vo.PostListVO;
import run.halo.app.service.OptionService;
import run.halo.app.service.PostService;
import javax.validation.Valid;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -32,7 +33,8 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
*
* @author johnniang
* @author ryanwang
- * @date 3/19/19
+ * @author guqing
+ * @date 2019-03-19
*/
@RestController
@RequestMapping("/api/admin/posts")
@@ -54,15 +56,15 @@ public class PostController {
@GetMapping
@ApiOperation("Lists posts")
- public Page pageBy(Integer page, Integer size,
- @SortDefault.SortDefaults({
- @SortDefault(sort = "topPriority", direction = DESC),
- @SortDefault(sort = "createTime", direction = DESC)
- }) Sort sort,
- PostQuery postQuery) {
- Pageable pageable = PageRequest.of(page, size, sort);
+ public Page extends BasePostSimpleDTO> pageBy(@PageableDefault(sort = {"topPriority", "createTime"}, direction = DESC) Pageable pageable,
+ PostQuery postQuery,
+ @RequestParam(value = "more", defaultValue = "true") Boolean more) {
Page postPage = postService.pageBy(postQuery, pageable);
- return postService.convertToListVo(postPage);
+ if (more) {
+ return postService.convertToListVo(postPage);
+ }
+
+ return postService.convertToSimple(postPage);
}
@GetMapping("latest")
@@ -86,6 +88,7 @@ public class PostController {
}
@GetMapping("{postId:\\d+}")
+ @ApiOperation("Gets a post")
public PostDetailVO getBy(@PathVariable("postId") Integer postId) {
Post post = postService.getById(postId);
return postService.convertToDetailVo(post);
@@ -98,15 +101,16 @@ public class PostController {
}
@PostMapping
+ @ApiOperation("Creates a post")
public PostDetailVO createBy(@Valid @RequestBody PostParam postParam,
@RequestParam(value = "autoSave", required = false, defaultValue = "false") Boolean autoSave) {
// Convert to
Post post = postParam.convertTo();
-
- return postService.createBy(post, postParam.getTagIds(), postParam.getCategoryIds(), autoSave);
+ return postService.createBy(post, postParam.getTagIds(), postParam.getCategoryIds(), postParam.getPostMetas(), autoSave);
}
@PutMapping("{postId:\\d+}")
+ @ApiOperation("Updates a post")
public PostDetailVO updateBy(@Valid @RequestBody PostParam postParam,
@PathVariable("postId") Integer postId,
@RequestParam(value = "autoSave", required = false, defaultValue = "false") Boolean autoSave) {
@@ -114,39 +118,60 @@ public class PostController {
Post postToUpdate = postService.getById(postId);
postParam.update(postToUpdate);
-
- return postService.updateBy(postToUpdate, postParam.getTagIds(), postParam.getCategoryIds(), autoSave);
+ return postService.updateBy(postToUpdate, postParam.getTagIds(), postParam.getCategoryIds(), postParam.getPostMetas(), autoSave);
}
@PutMapping("{postId:\\d+}/status/{status}")
- public void updateStatusBy(
+ @ApiOperation("Updates post status")
+ public BasePostMinimalDTO updateStatusBy(
@PathVariable("postId") Integer postId,
@PathVariable("status") PostStatus status) {
- Post post = postService.getById(postId);
+ Post post = postService.updateStatus(status, postId);
- // Set status
- post.setStatus(status);
+ return new BasePostMinimalDTO().convertFrom(post);
+ }
- // Update
- postService.update(post);
+ @PutMapping("status/{status}")
+ @ApiOperation("Updates post status in batch")
+ public List updateStatusInBatch(@PathVariable(name = "status") PostStatus status,
+ @RequestBody List ids) {
+ return postService.updateStatusByIds(ids, status);
+ }
+
+ @PutMapping("{postId:\\d+}/status/draft/content")
+ @ApiOperation("Updates draft")
+ public BasePostDetailDTO updateDraftBy(
+ @PathVariable("postId") Integer postId,
+ @RequestBody PostContentParam contentParam) {
+ // Update draft content
+ Post post = postService.updateDraftContent(contentParam.getContent(), postId);
+
+ return new BasePostDetailDTO().convertFrom(post);
}
@DeleteMapping("{postId:\\d+}")
+ @ApiOperation("Deletes a photo permanently")
public void deletePermanently(@PathVariable("postId") Integer postId) {
- // Remove it
postService.removeById(postId);
}
- @GetMapping("preview/{postId:\\d+}")
- public String preview(@PathVariable("postId") Integer postId) {
+ @DeleteMapping
+ @ApiOperation("Deletes posts permanently in batch by id array")
+ public List deletePermanentlyInBatch(@RequestBody List ids) {
+ return postService.removeByIds(ids);
+ }
+
+ @GetMapping(value = {"preview/{postId:\\d+}", "{postId:\\d+}/preview"})
+ @ApiOperation("Gets a post preview link")
+ public String preview(@PathVariable("postId") Integer postId) throws UnsupportedEncodingException {
Post post = postService.getById(postId);
String token = IdUtil.simpleUUID();
// cache preview token
- cacheStore.putAny("preview-post-token-" + postId, token, 10, TimeUnit.MINUTES);
+ cacheStore.putAny(token, token, 10, TimeUnit.MINUTES);
// build preview post url and return
- return String.format("%s/archives/%s?preview=true&token=%s", optionService.getBlogBaseUrl(), post.getUrl(), token);
+ return String.format("%s/archives/%s?token=%s", optionService.getBlogBaseUrl(), URLEncoder.encode(post.getUrl(), StandardCharsets.UTF_8.name()), token);
}
}
diff --git a/src/main/java/run/halo/app/controller/admin/api/RecoveryController.java b/src/main/java/run/halo/app/controller/admin/api/RecoveryController.java
index ebb831477..76407ee15 100644
--- a/src/main/java/run/halo/app/controller/admin/api/RecoveryController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/RecoveryController.java
@@ -19,6 +19,7 @@ import run.halo.app.service.RecoveryService;
* @author johnniang
* @date 19-4-26
*/
+@Deprecated
@RestController
@RequestMapping("/api/admin/recoveries")
public class RecoveryController {
diff --git a/src/main/java/run/halo/app/controller/admin/api/SheetCommentController.java b/src/main/java/run/halo/app/controller/admin/api/SheetCommentController.java
index ce8445249..533f21f67 100644
--- a/src/main/java/run/halo/app/controller/admin/api/SheetCommentController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/SheetCommentController.java
@@ -2,15 +2,21 @@ package run.halo.app.controller.admin.api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
+import org.springframework.data.web.SortDefault;
import org.springframework.web.bind.annotation.*;
import run.halo.app.model.dto.BaseCommentDTO;
import run.halo.app.model.entity.SheetComment;
import run.halo.app.model.enums.CommentStatus;
import run.halo.app.model.params.CommentQuery;
import run.halo.app.model.params.SheetCommentParam;
+import run.halo.app.model.vo.BaseCommentVO;
+import run.halo.app.model.vo.BaseCommentWithParentVO;
import run.halo.app.model.vo.SheetCommentWithSheetVO;
+import run.halo.app.service.OptionService;
import run.halo.app.service.SheetCommentService;
import javax.validation.Valid;
@@ -23,7 +29,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
*
* @author johnniang
* @author ryanwang
- * @date 19-4-25
+ * @date 2019-04-25
*/
@RestController
@RequestMapping("/api/admin/sheets/comments")
@@ -31,11 +37,16 @@ public class SheetCommentController {
private final SheetCommentService sheetCommentService;
- public SheetCommentController(SheetCommentService sheetCommentService) {
+ private final OptionService optionService;
+
+ public SheetCommentController(SheetCommentService sheetCommentService,
+ OptionService optionService) {
this.sheetCommentService = sheetCommentService;
+ this.optionService = optionService;
}
@GetMapping
+ @ApiOperation("Lists sheet comments")
public Page pageBy(@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable,
CommentQuery commentQuery) {
Page sheetCommentPage = sheetCommentService.pageBy(commentQuery, pageable);
@@ -43,21 +54,38 @@ public class SheetCommentController {
}
@GetMapping("latest")
+ @ApiOperation("Lists latest sheet comments")
public List listLatest(@RequestParam(name = "top", defaultValue = "10") int top,
@RequestParam(name = "status", required = false) CommentStatus status) {
Page sheetCommentPage = sheetCommentService.pageLatest(top, status);
return sheetCommentService.convertToWithSheetVo(sheetCommentPage.getContent());
}
+ @GetMapping("{sheetId:\\d+}/tree_view")
+ @ApiOperation("Lists sheet comments with tree view")
+ public Page listCommentTree(@PathVariable("sheetId") Integer sheetId,
+ @RequestParam(name = "page", required = false, defaultValue = "0") int page,
+ @SortDefault(sort = "createTime", direction = DESC) Sort sort) {
+ return sheetCommentService.pageVosAllBy(sheetId, PageRequest.of(page, optionService.getCommentPageSize(), sort));
+ }
+
+ @GetMapping("{sheetId:\\d+}/list_view")
+ @ApiOperation("Lists sheet comment with list view")
+ public Page listComments(@PathVariable("sheetId") Integer sheetId,
+ @RequestParam(name = "page", required = false, defaultValue = "0") int page,
+ @SortDefault(sort = "createTime", direction = DESC) Sort sort) {
+ return sheetCommentService.pageWithParentVoBy(sheetId, PageRequest.of(page, optionService.getCommentPageSize(), sort));
+ }
+
@PostMapping
- @ApiOperation("Creates a comment (new or reply)")
+ @ApiOperation("Creates a sheet comment (new or reply)")
public BaseCommentDTO createBy(@RequestBody SheetCommentParam commentParam) {
SheetComment createdComment = sheetCommentService.createBy(commentParam);
return sheetCommentService.convertTo(createdComment);
}
@PutMapping("{commentId:\\d+}/status/{status}")
- @ApiOperation("Updates comment status")
+ @ApiOperation("Updates sheet comment status")
public BaseCommentDTO updateStatusBy(@PathVariable("commentId") Long commentId,
@PathVariable("status") CommentStatus status) {
// Update comment status
@@ -65,21 +93,37 @@ public class SheetCommentController {
return sheetCommentService.convertTo(updatedSheetComment);
}
+ @PutMapping("status/{status}")
+ @ApiOperation("Updates sheet comment status in batch")
+ public List updateStatusInBatch(@PathVariable(name = "status") CommentStatus status,
+ @RequestBody List ids) {
+ List comments = sheetCommentService.updateStatusByIds(ids, status);
+ return sheetCommentService.convertTo(comments);
+ }
+
+
@DeleteMapping("{commentId:\\d+}")
- @ApiOperation("Deletes comment permanently and recursively")
- public BaseCommentDTO deleteBy(@PathVariable("commentId") Long commentId) {
+ @ApiOperation("Deletes sheet comment permanently and recursively")
+ public BaseCommentDTO deletePermanently(@PathVariable("commentId") Long commentId) {
SheetComment deletedSheetComment = sheetCommentService.removeById(commentId);
return sheetCommentService.convertTo(deletedSheetComment);
}
+ @DeleteMapping
+ @ApiOperation("Deletes sheet comments permanently in batch by id array")
+ public List deletePermanentlyInBatch(@RequestBody List ids) {
+ return sheetCommentService.removeByIds(ids);
+ }
+
@GetMapping("{commentId:\\d+}")
- @ApiOperation("Gets a post comment by comment id")
+ @ApiOperation("Gets a sheet comment by comment id")
public SheetCommentWithSheetVO getBy(@PathVariable("commentId") Long commentId) {
SheetComment comment = sheetCommentService.getById(commentId);
return sheetCommentService.convertToWithSheetVo(comment);
}
@PutMapping("{commentId:\\d+}")
+ @ApiOperation("Updates a sheet comment")
public BaseCommentDTO updateBy(@Valid @RequestBody SheetCommentParam commentParam,
@PathVariable("commentId") Long commentId) {
SheetComment commentToUpdate = sheetCommentService.getById(commentId);
diff --git a/src/main/java/run/halo/app/controller/admin/api/SheetController.java b/src/main/java/run/halo/app/controller/admin/api/SheetController.java
index 653774037..1a8733a84 100644
--- a/src/main/java/run/halo/app/controller/admin/api/SheetController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/SheetController.java
@@ -8,15 +8,18 @@ import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.*;
import run.halo.app.cache.StringCacheStore;
import run.halo.app.model.dto.InternalSheetDTO;
-import run.halo.app.model.dto.post.BasePostDetailDTO;
import run.halo.app.model.entity.Sheet;
import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.params.SheetParam;
+import run.halo.app.model.vo.SheetDetailVO;
import run.halo.app.model.vo.SheetListVO;
import run.halo.app.service.OptionService;
import run.halo.app.service.SheetService;
import javax.validation.Valid;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -49,9 +52,9 @@ public class SheetController {
@GetMapping("{sheetId:\\d+}")
@ApiOperation("Gets a sheet")
- public BasePostDetailDTO getBy(@PathVariable("sheetId") Integer sheetId) {
+ public SheetDetailVO getBy(@PathVariable("sheetId") Integer sheetId) {
Sheet sheet = sheetService.getById(sheetId);
- return sheetService.convertToDetail(sheet);
+ return sheetService.convertToDetailVo(sheet);
}
@GetMapping
@@ -69,15 +72,15 @@ public class SheetController {
@PostMapping
@ApiOperation("Creates a sheet")
- public BasePostDetailDTO createBy(@RequestBody @Valid SheetParam sheetParam,
- @RequestParam(value = "autoSave", required = false, defaultValue = "false") Boolean autoSave) {
- Sheet sheet = sheetService.createBy(sheetParam.convertTo(), autoSave);
- return sheetService.convertToDetail(sheet);
+ public SheetDetailVO createBy(@RequestBody @Valid SheetParam sheetParam,
+ @RequestParam(value = "autoSave", required = false, defaultValue = "false") Boolean autoSave) {
+ Sheet sheet = sheetService.createBy(sheetParam.convertTo(), sheetParam.getSheetMetas(), autoSave);
+ return sheetService.convertToDetailVo(sheet);
}
@PutMapping("{sheetId:\\d+}")
@ApiOperation("Updates a sheet")
- public BasePostDetailDTO updateBy(
+ public SheetDetailVO updateBy(
@PathVariable("sheetId") Integer sheetId,
@RequestBody @Valid SheetParam sheetParam,
@RequestParam(value = "autoSave", required = false, defaultValue = "false") Boolean autoSave) {
@@ -85,12 +88,13 @@ public class SheetController {
sheetParam.update(sheetToUpdate);
- Sheet sheet = sheetService.updateBy(sheetToUpdate, autoSave);
+ Sheet sheet = sheetService.updateBy(sheetToUpdate, sheetParam.getSheetMetas(), autoSave);
- return sheetService.convertToDetail(sheet);
+ return sheetService.convertToDetailVo(sheet);
}
@PutMapping("{sheetId:\\d+}/{status}")
+ @ApiOperation("Updates a sheet")
public void updateStatusBy(
@PathVariable("sheetId") Integer sheetId,
@PathVariable("status") PostStatus status) {
@@ -105,21 +109,22 @@ public class SheetController {
@DeleteMapping("{sheetId:\\d+}")
@ApiOperation("Deletes a sheet")
- public BasePostDetailDTO deleteBy(@PathVariable("sheetId") Integer sheetId) {
+ public SheetDetailVO deleteBy(@PathVariable("sheetId") Integer sheetId) {
Sheet sheet = sheetService.removeById(sheetId);
- return sheetService.convertToDetail(sheet);
+ return sheetService.convertToDetailVo(sheet);
}
@GetMapping("preview/{sheetId:\\d+}")
- public String preview(@PathVariable("sheetId") Integer sheetId) {
+ @ApiOperation("Gets a sheet preview link")
+ public String preview(@PathVariable("sheetId") Integer sheetId) throws UnsupportedEncodingException {
Sheet sheet = sheetService.getById(sheetId);
String token = IdUtil.simpleUUID();
// cache preview token
- cacheStore.putAny("preview-sheet-token-" + sheetId, token, 10, TimeUnit.MINUTES);
+ cacheStore.putAny(token, token, 10, TimeUnit.MINUTES);
// build preview post url and return
- return String.format("%s/s/%s?preview=true&token=%s", optionService.getBlogBaseUrl(), sheet.getUrl(), token);
+ return String.format("%s/s/%s?token=%s", optionService.getBlogBaseUrl(), URLEncoder.encode(sheet.getUrl(), StandardCharsets.UTF_8.name()), token);
}
}
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
new file mode 100644
index 000000000..f932e0b12
--- /dev/null
+++ b/src/main/java/run/halo/app/controller/admin/api/StaticStorageController.java
@@ -0,0 +1,52 @@
+package run.halo.app.controller.admin.api;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import run.halo.app.model.support.StaticFile;
+import run.halo.app.service.StaticStorageService;
+
+import java.util.List;
+
+/**
+ * Static storage controller.
+ *
+ * @author ryanwang
+ * @date 2019-12-06
+ */
+@RestController
+@RequestMapping("/api/admin/statics")
+public class StaticStorageController {
+
+ private final StaticStorageService staticStorageService;
+
+ public StaticStorageController(StaticStorageService staticStorageService) {
+ this.staticStorageService = staticStorageService;
+ }
+
+ @GetMapping
+ @ApiOperation("Lists static files")
+ public List list() {
+ return staticStorageService.listStaticFolder();
+ }
+
+ @DeleteMapping
+ @ApiOperation("Deletes file by relative path")
+ public void deletePermanently(@RequestParam("path") String path) {
+ staticStorageService.delete(path);
+ }
+
+ @PostMapping
+ @ApiOperation("Creates a folder")
+ public void createFolder(String basePath,
+ @RequestParam("folderName") String folderName) {
+ staticStorageService.createFolder(basePath, folderName);
+ }
+
+ @PostMapping("upload")
+ @ApiOperation("Uploads static file")
+ public void upload(String basePath,
+ @RequestPart("file") MultipartFile file) {
+ staticStorageService.update(basePath, file);
+ }
+}
diff --git a/src/main/java/run/halo/app/controller/admin/api/StatisticController.java b/src/main/java/run/halo/app/controller/admin/api/StatisticController.java
new file mode 100644
index 000000000..5bb2e272d
--- /dev/null
+++ b/src/main/java/run/halo/app/controller/admin/api/StatisticController.java
@@ -0,0 +1,38 @@
+package run.halo.app.controller.admin.api;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import run.halo.app.model.dto.StatisticDTO;
+import run.halo.app.model.dto.StatisticWithUserDTO;
+import run.halo.app.service.StatisticService;
+
+/**
+ * Statistic controller.
+ *
+ * @author ryanwang
+ * @date 2019-12-16
+ */
+@RestController
+@RequestMapping("/api/admin/statistics")
+public class StatisticController {
+
+ private final StatisticService statisticService;
+
+ public StatisticController(StatisticService statisticService) {
+ this.statisticService = statisticService;
+ }
+
+ @GetMapping
+ @ApiOperation("Gets blog statistics.")
+ public StatisticDTO statistics() {
+ return statisticService.getStatistic();
+ }
+
+ @GetMapping("user")
+ @ApiOperation("Gets blog statistics with user")
+ public StatisticWithUserDTO statisticsWithUser() {
+ return statisticService.getStatisticWithUser();
+ }
+}
diff --git a/src/main/java/run/halo/app/controller/admin/api/TagController.java b/src/main/java/run/halo/app/controller/admin/api/TagController.java
index 18571483d..81885dd86 100644
--- a/src/main/java/run/halo/app/controller/admin/api/TagController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/TagController.java
@@ -37,7 +37,7 @@ public class TagController {
}
@GetMapping
- @ApiOperation("Lists tag")
+ @ApiOperation("Lists tags")
public List extends TagDTO> listTags(@SortDefault(sort = "updateTime", direction = Sort.Direction.DESC) Sort sort,
@ApiParam("Return more information(post count) if it is set")
@RequestParam(name = "more", required = false, defaultValue = "false") Boolean more) {
@@ -48,7 +48,7 @@ public class TagController {
}
@PostMapping
- @ApiOperation("Creates tag")
+ @ApiOperation("Creates a tag")
public TagDTO createTag(@Valid @RequestBody TagParam tagParam) {
// Convert to tag
Tag tag = tagParam.convertTo();
@@ -59,20 +59,14 @@ public class TagController {
return tagService.convertTo(tagService.create(tag));
}
- /**
- * Get tag by id
- *
- * @param tagId tag id
- * @return TagDTO
- */
@GetMapping("{tagId:\\d+}")
- @ApiOperation("Get tag detail by id")
+ @ApiOperation("Gets tag detail by id")
public TagDTO getBy(@PathVariable("tagId") Integer tagId) {
return tagService.convertTo(tagService.getById(tagId));
}
@PutMapping("{tagId:\\d+}")
- @ApiOperation("Updates tag")
+ @ApiOperation("Updates a tag")
public TagDTO updateBy(@PathVariable("tagId") Integer tagId,
@Valid @RequestBody TagParam tagParam) {
// Get old tag
@@ -86,7 +80,7 @@ public class TagController {
}
@DeleteMapping("{tagId:\\d+}")
- @ApiOperation("Deletes tag")
+ @ApiOperation("Deletes a tag")
public TagDTO deletePermanently(@PathVariable("tagId") Integer tagId) {
// Remove the tag
Tag deletedTag = tagService.removeById(tagId);
diff --git a/src/main/java/run/halo/app/controller/admin/api/ThemeController.java b/src/main/java/run/halo/app/controller/admin/api/ThemeController.java
index 361cb7883..b3d79927f 100644
--- a/src/main/java/run/halo/app/controller/admin/api/ThemeController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/ThemeController.java
@@ -20,7 +20,7 @@ import java.util.Set;
* Theme controller.
*
* @author ryanwang
- * @date : 2019/3/20
+ * @date 2019-03-20
*/
@RestController
@RequestMapping("/api/admin/themes")
@@ -43,46 +43,59 @@ public class ThemeController {
}
@GetMapping
- @ApiOperation("List all themes")
+ @ApiOperation("Lists all themes")
public Set listAll() {
return themeService.getThemes();
}
@GetMapping("activation/files")
+ @ApiOperation("Lists all activate theme files")
public List listFiles() {
return themeService.listThemeFolderBy(themeService.getActivatedThemeId());
}
@GetMapping("{themeId}/files")
+ @ApiOperation("Lists theme files by theme id")
public List listFiles(@PathVariable("themeId") String themeId) {
return themeService.listThemeFolderBy(themeId);
}
@GetMapping("files/content")
+ @ApiOperation("Gets template content")
public BaseResponse getContentBy(@RequestParam(name = "path") String path) {
return BaseResponse.ok(HttpStatus.OK.getReasonPhrase(), themeService.getTemplateContent(path));
}
@GetMapping("{themeId}/files/content")
+ @ApiOperation("Gets template content by theme id")
public BaseResponse getContentBy(@PathVariable("themeId") String themeId,
@RequestParam(name = "path") String path) {
return BaseResponse.ok(HttpStatus.OK.getReasonPhrase(), themeService.getTemplateContent(themeId, path));
}
@PutMapping("files/content")
+ @ApiOperation("Updates template content")
public void updateContentBy(@RequestBody ThemeContentParam param) {
themeService.saveTemplateContent(param.getPath(), param.getContent());
}
@PutMapping("{themeId}/files/content")
+ @ApiOperation("Updates template content by theme id")
public void updateContentBy(@PathVariable("themeId") String themeId,
@RequestBody ThemeContentParam param) {
themeService.saveTemplateContent(themeId, param.getPath(), param.getContent());
}
- @GetMapping("files/custom")
- public Set customTemplate() {
- return themeService.listCustomTemplates(themeService.getActivatedThemeId());
+ @GetMapping("activation/template/custom/sheet")
+ @ApiOperation("Gets custom sheet templates")
+ public Set customSheetTemplate() {
+ return themeService.listCustomTemplates(themeService.getActivatedThemeId(), ThemeService.CUSTOM_SHEET_PREFIX);
+ }
+
+ @GetMapping("activation/template/custom/post")
+ @ApiOperation("Gets custom post templates")
+ public Set customPostTemplate() {
+ return themeService.listCustomTemplates(themeService.getActivatedThemeId(), ThemeService.CUSTOM_POST_PREFIX);
}
@PostMapping("{themeId}/activation")
@@ -141,12 +154,13 @@ public class ThemeController {
}
@PostMapping("upload")
- @ApiOperation("Upload theme")
+ @ApiOperation("Uploads a theme")
public ThemeProperty uploadTheme(@RequestPart("file") MultipartFile file) {
return themeService.upload(file);
}
@PutMapping("upload/{themeId}")
+ @ApiOperation("Upgrades theme by file")
public ThemeProperty updateThemeByUpload(@PathVariable("themeId") String themeId,
@RequestPart("file") MultipartFile file) {
return themeService.update(themeId, file);
@@ -159,8 +173,8 @@ public class ThemeController {
}
@PutMapping("fetching/{themeId}")
- public ThemeProperty updateThemeByFetching(@PathVariable("themeId") String themeId,
- @RequestPart(name = "file", required = false) MultipartFile file) {
+ @ApiOperation("Upgrades theme by remote")
+ public ThemeProperty updateThemeByFetching(@PathVariable("themeId") String themeId) {
return themeService.update(themeId);
}
@@ -172,8 +186,8 @@ public class ThemeController {
}
@GetMapping(value = "activation/template/exists")
+ @ApiOperation("Determines if template exists")
public BaseResponse exists(@RequestParam(value = "template") String template) {
return BaseResponse.ok(themeService.templateExists(template));
}
-
}
diff --git a/src/main/java/run/halo/app/controller/admin/api/TraceController.java b/src/main/java/run/halo/app/controller/admin/api/TraceController.java
index 147e2a111..f2fed933e 100644
--- a/src/main/java/run/halo/app/controller/admin/api/TraceController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/TraceController.java
@@ -1,34 +1,34 @@
-package run.halo.app.controller.admin.api;
-
-import io.swagger.annotations.ApiOperation;
-import org.springframework.boot.actuate.trace.http.HttpTrace;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-import run.halo.app.service.TraceService;
-
-import java.util.List;
-
-/**
- * Trace controller.
- *
- * @author johnniang
- * @date 19-6-18
- */
-@RestController
-@RequestMapping("/api/admin/traces")
-public class TraceController {
-
- private final TraceService traceService;
-
- public TraceController(TraceService traceService) {
- this.traceService = traceService;
- }
-
- @GetMapping
- @ApiOperation("Lists http traces")
- public List listHttpTraces() {
- return traceService.listHttpTraces();
- }
-
-}
+//package run.halo.app.controller.admin.api;
+//
+//import io.swagger.annotations.ApiOperation;
+//import org.springframework.boot.actuate.trace.http.HttpTrace;
+//import org.springframework.web.bind.annotation.GetMapping;
+//import org.springframework.web.bind.annotation.RequestMapping;
+//import org.springframework.web.bind.annotation.RestController;
+//import run.halo.app.service.TraceService;
+//
+//import java.util.List;
+//
+///**
+// * Trace controller.
+// *
+// * @author johnniang
+// * @date 19-6-18
+// */
+//@RestController
+//@RequestMapping("/api/admin/traces")
+//public class TraceController {
+//
+// private final TraceService traceService;
+//
+// public TraceController(TraceService traceService) {
+// this.traceService = traceService;
+// }
+//
+// @GetMapping
+// @ApiOperation("Lists http traces")
+// public List listHttpTraces() {
+// return traceService.listHttpTraces();
+// }
+//
+//}
diff --git a/src/main/java/run/halo/app/controller/admin/api/UserController.java b/src/main/java/run/halo/app/controller/admin/api/UserController.java
index df98f8490..5ff4031ef 100644
--- a/src/main/java/run/halo/app/controller/admin/api/UserController.java
+++ b/src/main/java/run/halo/app/controller/admin/api/UserController.java
@@ -1,5 +1,6 @@
package run.halo.app.controller.admin.api;
+import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import run.halo.app.model.dto.UserDTO;
import run.halo.app.model.entity.User;
@@ -16,7 +17,7 @@ import javax.validation.Valid;
* User controller.
*
* @author johnniang
- * @date 3/19/19
+ * @date 2019-03-19
*/
@RestController
@RequestMapping("/api/admin/users")
@@ -29,11 +30,13 @@ public class UserController {
}
@GetMapping("profiles")
+ @ApiOperation("Gets user profile")
public UserDTO getProfile(User user) {
return new UserDTO().convertFrom(user);
}
@PutMapping("profiles")
+ @ApiOperation("Updates user profile")
public UserDTO updateProfile(@RequestBody UserParam userParam, User user) {
// Validate the user param
ValidationUtils.validate(userParam, UpdateCheck.class);
@@ -46,6 +49,7 @@ public class UserController {
}
@PutMapping("profiles/password")
+ @ApiOperation("Updates user's password")
public BaseResponse updatePassword(@RequestBody @Valid PasswordParam passwordParam, User user) {
userService.updatePassword(passwordParam.getOldPassword(), passwordParam.getNewPassword(), user.getId());
return BaseResponse.ok("密码修改成功");
diff --git a/src/main/java/run/halo/app/controller/content/ContentArchiveController.java b/src/main/java/run/halo/app/controller/content/ContentArchiveController.java
index 6c84eab8e..7d0a2ae71 100644
--- a/src/main/java/run/halo/app/controller/content/ContentArchiveController.java
+++ b/src/main/java/run/halo/app/controller/content/ContentArchiveController.java
@@ -3,6 +3,7 @@ package run.halo.app.controller.content;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.PageUtil;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@@ -16,14 +17,14 @@ import run.halo.app.cache.lock.CacheLock;
import run.halo.app.exception.ForbiddenException;
import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.Post;
+import run.halo.app.model.entity.PostMeta;
import run.halo.app.model.entity.Tag;
import run.halo.app.model.enums.PostStatus;
-import run.halo.app.model.vo.BaseCommentVO;
+import run.halo.app.model.support.HaloConst;
import run.halo.app.model.vo.PostListVO;
import run.halo.app.service.*;
import run.halo.app.utils.MarkdownUtils;
-import java.io.File;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -33,7 +34,9 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Blog archive page controller
*
* @author ryanwang
- * @date : 2019-03-17
+ * @author guqing
+ * @author evanwang
+ * @date 2019-03-17
*/
@Slf4j
@Controller
@@ -46,9 +49,9 @@ public class ContentArchiveController {
private final PostCategoryService postCategoryService;
- private final PostTagService postTagService;
+ private final PostMetaService postMetaService;
- private final PostCommentService postCommentService;
+ private final PostTagService postTagService;
private final OptionService optionService;
@@ -57,15 +60,15 @@ public class ContentArchiveController {
public ContentArchiveController(PostService postService,
ThemeService themeService,
PostCategoryService postCategoryService,
+ PostMetaService postMetaService,
PostTagService postTagService,
- PostCommentService postCommentService,
OptionService optionService,
StringCacheStore cacheStore) {
this.postService = postService;
this.themeService = themeService;
this.postCategoryService = postCategoryService;
+ this.postMetaService = postMetaService;
this.postTagService = postTagService;
- this.postCommentService = postCommentService;
this.optionService = optionService;
this.cacheStore = cacheStore;
}
@@ -107,68 +110,51 @@ public class ContentArchiveController {
/**
* Render post page.
*
- * @param url post slug url.
- * @param preview preview
- * @param token preview token
- * @param model model
+ * @param url post slug url.
+ * @param token view token.
+ * @param model model
* @return template path: themes/{theme}/post.ftl
*/
@GetMapping("{url}")
public String post(@PathVariable("url") String url,
- @RequestParam(value = "preview", required = false, defaultValue = "false") boolean preview,
- @RequestParam(value = "intimate", required = false, defaultValue = "false") boolean intimate,
@RequestParam(value = "token", required = false) String token,
- @RequestParam(value = "cp", defaultValue = "1") Integer cp,
- @SortDefault(sort = "createTime", direction = DESC) Sort sort,
Model model) {
- Post post;
- if (preview) {
- post = postService.getBy(PostStatus.DRAFT, url);
- } else if (intimate) {
- post = postService.getBy(PostStatus.INTIMATE, url);
- } else {
+ Post post = postService.getByUrl(url);
+
+ if (post.getStatus().equals(PostStatus.INTIMATE) && StringUtils.isEmpty(token)) {
+ String redirect = String.format("%s/archives/%s/password", optionService.getBlogBaseUrl(), post.getUrl());
+ return "redirect:" + redirect;
+ }
+
+ if (StringUtils.isEmpty(token)) {
post = postService.getBy(PostStatus.PUBLISHED, url);
- }
-
- // if this is a preview url.
- if (preview) {
- // render markdown to html when preview post
- post.setFormatContent(MarkdownUtils.renderHtml(post.getOriginalContent()));
-
- // verify token
- String cachedToken = cacheStore.getAny("preview-post-token-" + post.getId(), String.class).orElseThrow(() -> new ForbiddenException("该文章的预览链接不存在或已过期"));
-
- if (!cachedToken.equals(token)) {
- throw new ForbiddenException("该文章的预览链接不存在或已过期");
- }
- }
-
- // if this is a intimate url.
- if (intimate) {
+ } else {
// verify token
String cachedToken = cacheStore.getAny(token, String.class).orElseThrow(() -> new ForbiddenException("您没有该文章的访问权限"));
if (!cachedToken.equals(token)) {
throw new ForbiddenException("您没有该文章的访问权限");
}
+ post.setFormatContent(MarkdownUtils.renderHtml(post.getOriginalContent()));
}
-
+ postService.publishVisitEvent(post.getId());
postService.getNextPost(post.getCreateTime()).ifPresent(nextPost -> model.addAttribute("nextPost", nextPost));
postService.getPrePost(post.getCreateTime()).ifPresent(prePost -> model.addAttribute("prePost", prePost));
List categories = postCategoryService.listCategoriesBy(post.getId());
List tags = postTagService.listTagsBy(post.getId());
-
- Page comments = postCommentService.pageVosBy(post.getId(), PageRequest.of(cp, optionService.getCommentPageSize(), sort));
+ List metas = postMetaService.listBy(post.getId());
model.addAttribute("is_post", true);
model.addAttribute("post", postService.convertToDetailVo(post));
model.addAttribute("categories", categories);
model.addAttribute("tags", tags);
- model.addAttribute("comments", comments);
+ model.addAttribute("metas", postMetaService.convertToMap(metas));
- if (preview) {
- // refresh timeUnit
- cacheStore.putAny("preview-post-token-" + post.getId(), token, 10, TimeUnit.MINUTES);
+ // TODO,Will be deprecated
+ model.addAttribute("comments", Page.empty());
+
+ if (themeService.templateExists(ThemeService.CUSTOM_POST_PREFIX + post.getTemplate() + HaloConst.SUFFIX_FTL)) {
+ return themeService.render(ThemeService.CUSTOM_POST_PREFIX + post.getTemplate());
}
return themeService.render("post");
@@ -177,29 +163,21 @@ public class ContentArchiveController {
@GetMapping(value = "{url}/password")
public String password(@PathVariable("url") String url,
Model model) {
- Post post = postService.getBy(PostStatus.INTIMATE, url);
- if (null == post) {
- throw new ForbiddenException("没有查询到该文章信息");
- }
-
model.addAttribute("url", url);
return "common/template/post_password";
}
@PostMapping(value = "{url}/password")
- @CacheLock
+ @CacheLock(traceRequest = true, expired = 2)
public String password(@PathVariable("url") String url,
@RequestParam(value = "password") String password) {
Post post = postService.getBy(PostStatus.INTIMATE, url);
- if (null == post) {
- throw new ForbiddenException("没有查询到该文章信息");
- }
if (password.equals(post.getPassword())) {
String token = IdUtil.simpleUUID();
cacheStore.putAny(token, token, 10, TimeUnit.SECONDS);
- String redirect = String.format("%s/archives/%s?intimate=true&token=%s", optionService.getBlogBaseUrl(), post.getUrl(), token);
+ String redirect = String.format("%s/archives/%s?token=%s", optionService.getBlogBaseUrl(), post.getUrl(), token);
return "redirect:" + redirect;
} else {
String redirect = String.format("%s/archives/%s/password", optionService.getBlogBaseUrl(), post.getUrl());
diff --git a/src/main/java/run/halo/app/controller/content/ContentCategoryController.java b/src/main/java/run/halo/app/controller/content/ContentCategoryController.java
index 97956eebc..dc1fd70d8 100644
--- a/src/main/java/run/halo/app/controller/content/ContentCategoryController.java
+++ b/src/main/java/run/halo/app/controller/content/ContentCategoryController.java
@@ -23,7 +23,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Category controller.
*
* @author ryanwang
- * @date : 2019/3/20
+ * @date 2019-03-20
*/
@Controller
@RequestMapping(value = "/categories")
diff --git a/src/main/java/run/halo/app/controller/content/ContentFeedController.java b/src/main/java/run/halo/app/controller/content/ContentFeedController.java
index 13b790130..6e1058772 100644
--- a/src/main/java/run/halo/app/controller/content/ContentFeedController.java
+++ b/src/main/java/run/halo/app/controller/content/ContentFeedController.java
@@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import run.halo.app.model.entity.Post;
import run.halo.app.model.enums.PostStatus;
-import run.halo.app.model.vo.PostListVO;
+import run.halo.app.model.vo.PostDetailVO;
import run.halo.app.service.OptionService;
import run.halo.app.service.PostService;
@@ -32,7 +32,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
/**
* @author ryanwang
- * @date : 2019-03-21
+ * @date 2019-03-21
*/
@Slf4j
@Controller
@@ -79,7 +79,7 @@ public class ContentFeedController {
@GetMapping(value = {"atom", "atom.xml"}, produces = XML_MEDIA_TYPE)
@ResponseBody
public String atom(Model model) throws IOException, TemplateException {
- model.addAttribute("posts", buildPosts(buildPostPageable(optionService.getPostPageSize())));
+ model.addAttribute("posts", buildPosts(buildPostPageable(optionService.getRssPageSize())));
Template template = freeMarker.getConfiguration().getTemplate("common/web/atom.ftl");
return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
}
@@ -146,9 +146,9 @@ public class ContentFeedController {
* @param pageable pageable
* @return List
*/
- private List buildPosts(@NonNull Pageable pageable) {
+ private List buildPosts(@NonNull Pageable pageable) {
Page postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
- Page posts = postService.convertToListVo(postPage);
+ Page posts = postService.convertToDetailVo(postPage);
posts.getContent().forEach(postListVO -> {
try {
// Encode post url
diff --git a/src/main/java/run/halo/app/controller/content/ContentIndexController.java b/src/main/java/run/halo/app/controller/content/ContentIndexController.java
index 51e886373..476184d98 100644
--- a/src/main/java/run/halo/app/controller/content/ContentIndexController.java
+++ b/src/main/java/run/halo/app/controller/content/ContentIndexController.java
@@ -25,7 +25,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Blog index page controller
*
* @author ryanwang
- * @date : 2019-03-17
+ * @date 2019-03-17
*/
@Slf4j
@Controller
diff --git a/src/main/java/run/halo/app/controller/content/ContentJournalController.java b/src/main/java/run/halo/app/controller/content/ContentJournalController.java
index 8308da3f6..da8d428f3 100644
--- a/src/main/java/run/halo/app/controller/content/ContentJournalController.java
+++ b/src/main/java/run/halo/app/controller/content/ContentJournalController.java
@@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import run.halo.app.model.entity.Journal;
import run.halo.app.model.enums.JournalType;
-import run.halo.app.service.JournalCommentService;
import run.halo.app.service.JournalService;
import run.halo.app.service.OptionService;
import run.halo.app.service.ThemeService;
@@ -25,7 +24,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Blog journal page controller
*
* @author ryanwang
- * @date : 2019-05-04
+ * @date 2019-05-04
*/
@Slf4j
@Controller
@@ -34,18 +33,14 @@ public class ContentJournalController {
private final JournalService journalService;
- private final JournalCommentService journalCommentService;
-
private final OptionService optionService;
private final ThemeService themeService;
public ContentJournalController(JournalService journalService,
- JournalCommentService journalCommentService,
OptionService optionService,
ThemeService themeService) {
this.journalService = journalService;
- this.journalCommentService = journalCommentService;
this.optionService = optionService;
this.themeService = themeService;
}
@@ -83,8 +78,8 @@ public class ContentJournalController {
int[] rainbow = PageUtil.rainbow(page, journals.getTotalPages(), 3);
- model.addAttribute("is_journal", true);
- model.addAttribute("journals", journals);
+ model.addAttribute("is_journals", true);
+ model.addAttribute("journals", journalService.convertToCmtCountDto(journals));
model.addAttribute("rainbow", rainbow);
return themeService.render("journals");
}
diff --git a/src/main/java/run/halo/app/controller/content/ContentSearchController.java b/src/main/java/run/halo/app/controller/content/ContentSearchController.java
index 4da44d503..f8d4506d2 100644
--- a/src/main/java/run/halo/app/controller/content/ContentSearchController.java
+++ b/src/main/java/run/halo/app/controller/content/ContentSearchController.java
@@ -25,7 +25,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Search controller.
*
* @author ryanwang
- * @date : 2019-04-21
+ * @date 2019-04-21
*/
@Controller
@RequestMapping(value = "/search")
diff --git a/src/main/java/run/halo/app/controller/content/ContentSheetController.java b/src/main/java/run/halo/app/controller/content/ContentSheetController.java
index e60e53f2a..89c7e2e56 100644
--- a/src/main/java/run/halo/app/controller/content/ContentSheetController.java
+++ b/src/main/java/run/halo/app/controller/content/ContentSheetController.java
@@ -1,9 +1,10 @@
package run.halo.app.controller.content;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
-import org.springframework.data.web.SortDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@@ -11,25 +12,24 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import run.halo.app.cache.StringCacheStore;
import run.halo.app.exception.ForbiddenException;
+import run.halo.app.model.dto.PhotoDTO;
import run.halo.app.model.entity.Sheet;
import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.support.HaloConst;
-import run.halo.app.model.vo.BaseCommentVO;
-import run.halo.app.service.OptionService;
-import run.halo.app.service.SheetCommentService;
+import run.halo.app.model.vo.SheetDetailVO;
+import run.halo.app.service.PhotoService;
import run.halo.app.service.SheetService;
import run.halo.app.service.ThemeService;
import run.halo.app.utils.MarkdownUtils;
-import java.util.concurrent.TimeUnit;
-
import static org.springframework.data.domain.Sort.Direction.DESC;
/**
* Content sheet controller.
*
* @author ryanwang
- * @date : 2019-03-21
+ * @author evanwang
+ * @date 2019-03-21
*/
@Controller
public class ContentSheetController {
@@ -39,21 +39,17 @@ public class ContentSheetController {
private final ThemeService themeService;
- private final SheetCommentService sheetCommentService;
-
- private final OptionService optionService;
+ private final PhotoService photoService;
private final StringCacheStore cacheStore;
public ContentSheetController(SheetService sheetService,
ThemeService themeService,
- SheetCommentService sheetCommentService,
- OptionService optionService,
+ PhotoService photoService,
StringCacheStore cacheStore) {
this.sheetService = sheetService;
this.themeService = themeService;
- this.sheetCommentService = sheetCommentService;
- this.optionService = optionService;
+ this.photoService = photoService;
this.cacheStore = cacheStore;
}
@@ -63,7 +59,26 @@ public class ContentSheetController {
* @return template path: themes/{theme}/photos.ftl
*/
@GetMapping(value = "/photos")
- public String photos() {
+ public String photos(Model model,
+ @RequestParam(value = "size", required = false, defaultValue = "10") Integer size) {
+ return photos(model, 1, size);
+ }
+
+ /**
+ * Render photo page
+ *
+ * @param model model
+ * @param page current page
+ * @param size current page size
+ * @return template path: themes/{theme}/photos.ftl
+ */
+ @GetMapping(value = "/photos/page/{page}")
+ public String photos(Model model,
+ @PathVariable(value = "page") Integer page,
+ @RequestParam(value = "size", required = false, defaultValue = "10") Integer size) {
+ Pageable pageable = PageRequest.of(page >= 1 ? page - 1 : page, size, Sort.by(DESC, "createTime"));
+ Page photos = photoService.pageBy(pageable);
+ model.addAttribute("photos", photos);
return themeService.render("photos");
}
@@ -80,46 +95,42 @@ public class ContentSheetController {
/**
* Render custom sheet
*
- * @param url sheet url
- * @param preview preview
- * @param token token
- * @param model model
+ * @param url sheet url
+ * @param token view token
+ * @param model model
* @return template path: themes/{theme}/sheet.ftl
*/
@GetMapping(value = "/s/{url}")
public String sheet(@PathVariable(value = "url") String url,
- @RequestParam(value = "preview", required = false, defaultValue = "false") boolean preview,
@RequestParam(value = "token", required = false) String token,
- @RequestParam(value = "cp", defaultValue = "1") Integer cp,
- @SortDefault(sort = "createTime", direction = DESC) Sort sort,
Model model) {
- Sheet sheet = sheetService.getBy(preview ? PostStatus.DRAFT : PostStatus.PUBLISHED, url);
- if (preview) {
- // render markdown to html when preview post
+ Sheet sheet = sheetService.getByUrl(url);
+
+ if (StringUtils.isEmpty(token)) {
+ sheet = sheetService.getBy(PostStatus.PUBLISHED, url);
+ } else {
+ // render markdown to html when preview sheet
sheet.setFormatContent(MarkdownUtils.renderHtml(sheet.getOriginalContent()));
// verify token
- String cachedToken = cacheStore.getAny("preview-sheet-token-" + sheet.getId(), String.class).orElseThrow(() -> new ForbiddenException("该页面的预览链接不存在或已过期"));
+ String cachedToken = cacheStore.getAny(token, String.class).orElseThrow(() -> new ForbiddenException("您没有该页面的访问权限"));
if (!cachedToken.equals(token)) {
- throw new ForbiddenException("该页面的预览链接不存在或已过期");
+ throw new ForbiddenException("您没有该页面的访问权限");
}
}
+ sheetService.publishVisitEvent(sheet.getId());
- Page comments = sheetCommentService.pageVosBy(sheet.getId(), PageRequest.of(cp, optionService.getCommentPageSize(), sort));
-
+ SheetDetailVO sheetDetailVO = sheetService.convertToDetailVo(sheet);
// sheet and post all can use
- model.addAttribute("sheet", sheetService.convertToDetail(sheet));
- model.addAttribute("post", sheetService.convertToDetail(sheet));
+ model.addAttribute("sheet", sheetDetailVO);
+ model.addAttribute("post", sheetDetailVO);
model.addAttribute("is_sheet", true);
- model.addAttribute("comments", comments);
- if (preview) {
- // refresh timeUnit
- cacheStore.putAny("preview-sheet-token-" + sheet.getId(), token, 10, TimeUnit.MINUTES);
- }
+ // TODO,Will be deprecated
+ model.addAttribute("comments", Page.empty());
if (themeService.templateExists(ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate() + HaloConst.SUFFIX_FTL)) {
return themeService.render(ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate());
diff --git a/src/main/java/run/halo/app/controller/content/ContentTagController.java b/src/main/java/run/halo/app/controller/content/ContentTagController.java
index ee9a09066..c5b62c03a 100644
--- a/src/main/java/run/halo/app/controller/content/ContentTagController.java
+++ b/src/main/java/run/halo/app/controller/content/ContentTagController.java
@@ -23,7 +23,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Tag controller.
*
* @author ryanwang
- * @date : 2019-03-21
+ * @date 2019-03-21
*/
@Controller
@RequestMapping(value = "/tags")
diff --git a/src/main/java/run/halo/app/controller/content/MainController.java b/src/main/java/run/halo/app/controller/content/MainController.java
index c912ae058..e237b4ab2 100644
--- a/src/main/java/run/halo/app/controller/content/MainController.java
+++ b/src/main/java/run/halo/app/controller/content/MainController.java
@@ -4,6 +4,7 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
+import run.halo.app.config.properties.HaloProperties;
import run.halo.app.exception.ServiceException;
import run.halo.app.model.entity.User;
import run.halo.app.model.properties.BlogProperties;
@@ -11,6 +12,7 @@ import run.halo.app.model.support.HaloConst;
import run.halo.app.service.OptionService;
import run.halo.app.service.UserService;
+import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -18,37 +20,52 @@ import java.io.IOException;
* Main controller.
*
* @author ryanwang
- * @date : 2019-04-23
+ * @date 2019-04-23
*/
@Controller
public class MainController {
+ /**
+ * Index redirect uri.
+ */
+ private final static String INDEX_REDIRECT_URI = "index.html";
+
+ /**
+ * Install redirect uri.
+ */
+ private final static String INSTALL_REDIRECT_URI = INDEX_REDIRECT_URI + "#install";
+
private final UserService userService;
private final OptionService optionService;
- public MainController(UserService userService, OptionService optionService) {
+ private final HaloProperties haloProperties;
+
+ public MainController(UserService userService, OptionService optionService, HaloProperties haloProperties) {
this.userService = userService;
this.optionService = optionService;
+ this.haloProperties = haloProperties;
}
- @GetMapping("/admin")
- public String admin() {
- return "redirect:/admin/index.html";
+ @GetMapping("${halo.admin-path:admin}")
+ public void admin(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ String adminIndexRedirectUri = StringUtils.appendIfMissing(this.haloProperties.getAdminPath(), "/") + INDEX_REDIRECT_URI;
+ response.sendRedirect(adminIndexRedirectUri);
}
- @GetMapping("/install")
- public String installation() {
- return "redirect:/admin/index.html#install";
- }
-
- @GetMapping("/version")
+ @GetMapping("version")
@ResponseBody
public String version() {
return HaloConst.HALO_VERSION;
}
- @GetMapping("/avatar")
+ @GetMapping("install")
+ public void installation(HttpServletResponse response) throws IOException {
+ String installRedirectUri = StringUtils.appendIfMissing(this.haloProperties.getAdminPath(), "/") + INSTALL_REDIRECT_URI;
+ response.sendRedirect(installRedirectUri);
+ }
+
+ @GetMapping("avatar")
public void avatar(HttpServletResponse response) throws IOException {
User user = userService.getCurrentUser().orElseThrow(() -> new ServiceException("未查询到博主信息"));
if (StringUtils.isNotEmpty(user.getAvatar())) {
@@ -56,7 +73,7 @@ public class MainController {
}
}
- @GetMapping("/logo")
+ @GetMapping("logo")
public void logo(HttpServletResponse response) throws IOException {
String blogLogo = optionService.getByProperty(BlogProperties.BLOG_LOGO).orElse("").toString();
if (StringUtils.isNotEmpty(blogLogo)) {
@@ -64,7 +81,7 @@ public class MainController {
}
}
- @GetMapping("/favicon.ico")
+ @GetMapping("favicon.ico")
public void favicon(HttpServletResponse response) throws IOException {
String favicon = optionService.getByProperty(BlogProperties.BLOG_FAVICON).orElse("").toString();
if (StringUtils.isNotEmpty(favicon)) {
diff --git a/src/main/java/run/halo/app/controller/content/api/JournalController.java b/src/main/java/run/halo/app/controller/content/api/JournalController.java
index a463ce481..d655825fb 100644
--- a/src/main/java/run/halo/app/controller/content/api/JournalController.java
+++ b/src/main/java/run/halo/app/controller/content/api/JournalController.java
@@ -35,7 +35,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* @author ryanwang
* @date 2019-04-26
*/
-@RestController("PortalJournalController")
+@RestController("ApiContentJournalController")
@RequestMapping("/api/content/journals")
public class JournalController {
diff --git a/src/main/java/run/halo/app/controller/content/api/PostController.java b/src/main/java/run/halo/app/controller/content/api/PostController.java
index 7e47e0fc1..de6622369 100644
--- a/src/main/java/run/halo/app/controller/content/api/PostController.java
+++ b/src/main/java/run/halo/app/controller/content/api/PostController.java
@@ -16,10 +16,7 @@ import run.halo.app.model.entity.PostComment;
import run.halo.app.model.enums.CommentStatus;
import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.params.PostCommentParam;
-import run.halo.app.model.vo.BaseCommentVO;
-import run.halo.app.model.vo.BaseCommentWithParentVO;
-import run.halo.app.model.vo.CommentWithHasChildrenVO;
-import run.halo.app.model.vo.PostDetailVO;
+import run.halo.app.model.vo.*;
import run.halo.app.service.OptionService;
import run.halo.app.service.PostCommentService;
import run.halo.app.service.PostService;
@@ -54,9 +51,9 @@ public class PostController {
@GetMapping
@ApiOperation("Lists posts")
- public Page pageBy(@PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) {
+ public Page pageBy(@PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) {
Page postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
- return postService.convertToSimple(postPage);
+ return postService.convertToListVo(postPage);
}
@PostMapping(value = "search")
diff --git a/src/main/java/run/halo/app/controller/content/api/SheetController.java b/src/main/java/run/halo/app/controller/content/api/SheetController.java
index 7302bc347..29dce97f3 100644
--- a/src/main/java/run/halo/app/controller/content/api/SheetController.java
+++ b/src/main/java/run/halo/app/controller/content/api/SheetController.java
@@ -10,16 +10,12 @@ import org.springframework.data.web.SortDefault;
import org.springframework.web.bind.annotation.*;
import run.halo.app.cache.lock.CacheLock;
import run.halo.app.model.dto.BaseCommentDTO;
-import run.halo.app.model.dto.post.BasePostDetailDTO;
-import run.halo.app.model.dto.post.BasePostSimpleDTO;
import run.halo.app.model.entity.Sheet;
import run.halo.app.model.entity.SheetComment;
import run.halo.app.model.enums.CommentStatus;
import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.params.SheetCommentParam;
-import run.halo.app.model.vo.BaseCommentVO;
-import run.halo.app.model.vo.BaseCommentWithParentVO;
-import run.halo.app.model.vo.CommentWithHasChildrenVO;
+import run.halo.app.model.vo.*;
import run.halo.app.service.OptionService;
import run.halo.app.service.SheetCommentService;
import run.halo.app.service.SheetService;
@@ -35,7 +31,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* @author ryanwang
* @date 19-4-26
*/
-@RestController("PortalSheetController")
+@RestController("ApiContentSheetController")
@RequestMapping("/api/content/sheets")
public class SheetController {
@@ -53,17 +49,17 @@ public class SheetController {
@GetMapping
@ApiOperation("Lists sheets")
- public Page pageBy(@PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) {
+ public Page pageBy(@PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) {
Page sheetPage = sheetService.pageBy(PostStatus.PUBLISHED, pageable);
- return sheetService.convertToSimple(sheetPage);
+ return sheetService.convertToListVo(sheetPage);
}
@GetMapping("{sheetId:\\d+}")
@ApiOperation("Gets a sheet")
- public BasePostDetailDTO getBy(@PathVariable("sheetId") Integer sheetId,
- @RequestParam(value = "formatDisabled", required = false, defaultValue = "true") Boolean formatDisabled,
- @RequestParam(value = "sourceDisabled", required = false, defaultValue = "false") Boolean sourceDisabled) {
- BasePostDetailDTO sheetDetailVO = sheetService.convertToDetail(sheetService.getById(sheetId));
+ public SheetDetailVO getBy(@PathVariable("sheetId") Integer sheetId,
+ @RequestParam(value = "formatDisabled", required = false, defaultValue = "true") Boolean formatDisabled,
+ @RequestParam(value = "sourceDisabled", required = false, defaultValue = "false") Boolean sourceDisabled) {
+ SheetDetailVO sheetDetailVO = sheetService.convertToDetailVo(sheetService.getById(sheetId));
if (formatDisabled) {
// Clear the format content
diff --git a/src/main/java/run/halo/app/controller/content/api/StatisticController.java b/src/main/java/run/halo/app/controller/content/api/StatisticController.java
new file mode 100644
index 000000000..9faca5bf8
--- /dev/null
+++ b/src/main/java/run/halo/app/controller/content/api/StatisticController.java
@@ -0,0 +1,38 @@
+package run.halo.app.controller.content.api;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import run.halo.app.model.dto.StatisticDTO;
+import run.halo.app.model.dto.StatisticWithUserDTO;
+import run.halo.app.service.StatisticService;
+
+/**
+ * Statistic controller.
+ *
+ * @author ryan0up
+ * @date 2019-12-16
+ */
+@RestController("ApiContentStatisticController")
+@RequestMapping("/api/content/statistics")
+public class StatisticController {
+
+ private final StatisticService statisticService;
+
+ public StatisticController(StatisticService statisticService) {
+ this.statisticService = statisticService;
+ }
+
+ @GetMapping
+ @ApiOperation("Gets blog statistics.")
+ public StatisticDTO statistics() {
+ return statisticService.getStatistic();
+ }
+
+ @GetMapping("user")
+ @ApiOperation("Gets blog statistics with user")
+ public StatisticWithUserDTO statisticsWithUser() {
+ return statisticService.getStatisticWithUser();
+ }
+}
diff --git a/src/main/java/run/halo/app/controller/core/CommonController.java b/src/main/java/run/halo/app/controller/core/CommonController.java
index bf17795e8..4d332a211 100644
--- a/src/main/java/run/halo/app/controller/core/CommonController.java
+++ b/src/main/java/run/halo/app/controller/core/CommonController.java
@@ -1,5 +1,6 @@
package run.halo.app.controller.core;
+import cn.hutool.extra.servlet.ServletUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
@@ -28,7 +29,7 @@ import java.util.Map;
* Error page Controller
*
* @author ryanwang
- * @date : 2017/12/26
+ * @date 2017-12-26
*/
@Slf4j
@Controller
@@ -43,6 +44,8 @@ public class CommonController extends AbstractErrorController {
private static final String DEFAULT_ERROR_PATH = "common/error/error";
+ private static final String COULD_NOT_RESOLVE_VIEW_WITH_NAME_PREFIX = "Could not resolve view with name '";
+
private final ThemeService themeService;
private final ErrorProperties errorProperties;
@@ -66,9 +69,11 @@ public class CommonController extends AbstractErrorController {
*/
@GetMapping
public String handleError(HttpServletRequest request, HttpServletResponse response, Model model) {
- HttpStatus status = getStatus(request);
-
- log.error("Error path: [{}], status: [{}]", getErrorPath(), status);
+ log.error("Request URL: [{}], URI: [{}], Request Method: [{}], IP: [{}]",
+ request.getRequestURL(),
+ request.getRequestURI(),
+ request.getMethod(),
+ ServletUtil.getClientIP(request));
handleCustomException(request);
@@ -77,6 +82,8 @@ public class CommonController extends AbstractErrorController {
log.debug("Error detail: [{}]", errorDetail);
+ HttpStatus status = getStatus(request);
+
if (status.equals(HttpStatus.INTERNAL_SERVER_ERROR)) {
return contentInternalError();
} else if (status.equals(HttpStatus.NOT_FOUND)) {
@@ -162,7 +169,7 @@ public class CommonController extends AbstractErrorController {
request.setAttribute("javax.servlet.error.exception", rootCause);
request.setAttribute("javax.servlet.error.message", haloException.getMessage());
}
- } else if (StringUtils.startsWithIgnoreCase(throwable.getMessage(), "Could not resolve view with name '")) {
+ } else if (StringUtils.startsWithIgnoreCase(throwable.getMessage(), COULD_NOT_RESOLVE_VIEW_WITH_NAME_PREFIX)) {
request.setAttribute("javax.servlet.error.status_code", HttpStatus.NOT_FOUND.value());
NotFoundException viewNotFound = new NotFoundException("该路径没有对应的模板");
@@ -188,7 +195,7 @@ public class CommonController extends AbstractErrorController {
* @param request the source request
* @return if the stacktrace attribute should be included
*/
- protected boolean isIncludeStackTrace(HttpServletRequest request) {
+ private boolean isIncludeStackTrace(HttpServletRequest request) {
ErrorProperties.IncludeStacktrace include = errorProperties.getIncludeStacktrace();
if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {
return true;
diff --git a/src/main/java/run/halo/app/core/ControllerLogAop.java b/src/main/java/run/halo/app/core/ControllerLogAop.java
index a2a7c1ad1..c3df45de3 100644
--- a/src/main/java/run/halo/app/core/ControllerLogAop.java
+++ b/src/main/java/run/halo/app/core/ControllerLogAop.java
@@ -7,7 +7,11 @@ import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.core.io.Resource;
+import org.springframework.http.ResponseEntity;
+import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
@@ -17,6 +21,9 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
+/**
+ * @author johnniang
+ */
@Aspect
@Component
@Slf4j
@@ -75,16 +82,36 @@ public class ControllerLogAop {
private void printResponseLog(HttpServletRequest request, String className, String methodName, Object returnObj, long usage) throws JsonProcessingException {
if (log.isDebugEnabled()) {
- String returningData = null;
+ String returnData = "";
+
if (returnObj != null) {
- if (returnObj.getClass().isAssignableFrom(byte[].class)) {
- returningData = "Binary data";
+ if (returnObj instanceof ResponseEntity) {
+ ResponseEntity responseEntity = (ResponseEntity) returnObj;
+ if (responseEntity.getBody() instanceof Resource) {
+ returnData = "[ BINARY DATA ]";
+ } else {
+ returnData = toString(responseEntity.getBody());
+ }
} else {
- returningData = JsonUtils.objectToJson(returnObj);
+ returnData = toString(returnObj);
}
+
}
- log.debug("{}.{} Response: [{}], usage: [{}]ms", className, methodName, returningData, usage);
+ log.debug("{}.{} Response: [{}], usage: [{}]ms", className, methodName, returnData, usage);
}
}
+
+ @NonNull
+ private String toString(@NonNull Object obj) throws JsonProcessingException {
+ Assert.notNull(obj, "Return object must not be null");
+
+ String toString = "";
+ if (obj.getClass().isAssignableFrom(byte[].class) && obj instanceof Resource) {
+ toString = "[ BINARY DATA ]";
+ } else {
+ toString = JsonUtils.objectToJson(obj);
+ }
+ return toString;
+ }
}
diff --git a/src/main/java/run/halo/app/model/freemarker/method/RandomMethod.java b/src/main/java/run/halo/app/core/freemarker/method/RandomMethod.java
similarity index 76%
rename from src/main/java/run/halo/app/model/freemarker/method/RandomMethod.java
rename to src/main/java/run/halo/app/core/freemarker/method/RandomMethod.java
index 073047020..b11e4f7ce 100644
--- a/src/main/java/run/halo/app/model/freemarker/method/RandomMethod.java
+++ b/src/main/java/run/halo/app/core/freemarker/method/RandomMethod.java
@@ -1,4 +1,4 @@
-package run.halo.app.model.freemarker.method;
+package run.halo.app.core.freemarker.method;
import cn.hutool.core.util.RandomUtil;
import freemarker.template.Configuration;
@@ -10,12 +10,19 @@ import org.springframework.stereotype.Component;
import java.util.List;
/**
+ * Freemarker template random method.
+ *
* @author ryanwang
- * @date : 2018/12/21
+ * @date 2018-12-21
*/
@Component
public class RandomMethod implements TemplateMethodModelEx {
+ /**
+ * Constructor.
+ *
+ * @param configuration injected by spring.
+ */
public RandomMethod(Configuration configuration) {
configuration.setSharedVariable("randomMethod", this);
}
@@ -29,6 +36,9 @@ public class RandomMethod implements TemplateMethodModelEx {
*/
@Override
public Object exec(List arguments) throws TemplateModelException {
+ if (arguments.size() != 2) {
+ throw new TemplateModelException("Wrong arguments! 2 arguments are needed");
+ }
SimpleNumber argOne = (SimpleNumber) arguments.get(0);
SimpleNumber argTwo = (SimpleNumber) arguments.get(1);
int start = argOne.getAsNumber().intValue();
diff --git a/src/main/java/run/halo/app/model/freemarker/tag/CategoryTagDirective.java b/src/main/java/run/halo/app/core/freemarker/tag/CategoryTagDirective.java
similarity index 97%
rename from src/main/java/run/halo/app/model/freemarker/tag/CategoryTagDirective.java
rename to src/main/java/run/halo/app/core/freemarker/tag/CategoryTagDirective.java
index 54056d30d..e23bf7236 100644
--- a/src/main/java/run/halo/app/model/freemarker/tag/CategoryTagDirective.java
+++ b/src/main/java/run/halo/app/core/freemarker/tag/CategoryTagDirective.java
@@ -1,4 +1,4 @@
-package run.halo.app.model.freemarker.tag;
+package run.halo.app.core.freemarker.tag;
import freemarker.core.Environment;
import freemarker.template.*;
@@ -17,7 +17,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Freemarker custom tag of category.
*
* @author ryanwang
- * @date : 2019/3/22
+ * @date 2019-03-22
*/
@Component
public class CategoryTagDirective implements TemplateDirectiveModel {
diff --git a/src/main/java/run/halo/app/model/freemarker/tag/CommentTagDirective.java b/src/main/java/run/halo/app/core/freemarker/tag/CommentTagDirective.java
similarity index 96%
rename from src/main/java/run/halo/app/model/freemarker/tag/CommentTagDirective.java
rename to src/main/java/run/halo/app/core/freemarker/tag/CommentTagDirective.java
index 62e8df8ac..709cb7350 100644
--- a/src/main/java/run/halo/app/model/freemarker/tag/CommentTagDirective.java
+++ b/src/main/java/run/halo/app/core/freemarker/tag/CommentTagDirective.java
@@ -1,4 +1,4 @@
-package run.halo.app.model.freemarker.tag;
+package run.halo.app.core.freemarker.tag;
import freemarker.core.Environment;
import freemarker.template.*;
@@ -16,7 +16,7 @@ import java.util.Map;
* Freemarker custom tag of comment.
*
* @author ryanwang
- * @date : 2019/3/22
+ * @date 2019-03-22
*/
@Component
public class CommentTagDirective implements TemplateDirectiveModel {
diff --git a/src/main/java/run/halo/app/model/freemarker/tag/LinkTagDirective.java b/src/main/java/run/halo/app/core/freemarker/tag/LinkTagDirective.java
similarity index 96%
rename from src/main/java/run/halo/app/model/freemarker/tag/LinkTagDirective.java
rename to src/main/java/run/halo/app/core/freemarker/tag/LinkTagDirective.java
index 6cf6897a5..ed3aa935f 100644
--- a/src/main/java/run/halo/app/model/freemarker/tag/LinkTagDirective.java
+++ b/src/main/java/run/halo/app/core/freemarker/tag/LinkTagDirective.java
@@ -1,4 +1,4 @@
-package run.halo.app.model.freemarker.tag;
+package run.halo.app.core.freemarker.tag;
import freemarker.core.Environment;
import freemarker.template.*;
@@ -16,7 +16,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Freemarker custom tag of link.
*
* @author ryanwang
- * @date : 2019/3/22
+ * @date 2019-03-22
*/
@Component
public class LinkTagDirective implements TemplateDirectiveModel {
diff --git a/src/main/java/run/halo/app/model/freemarker/tag/MenuTagDirective.java b/src/main/java/run/halo/app/core/freemarker/tag/MenuTagDirective.java
similarity index 87%
rename from src/main/java/run/halo/app/model/freemarker/tag/MenuTagDirective.java
rename to src/main/java/run/halo/app/core/freemarker/tag/MenuTagDirective.java
index cb39621bd..ac7355955 100644
--- a/src/main/java/run/halo/app/model/freemarker/tag/MenuTagDirective.java
+++ b/src/main/java/run/halo/app/core/freemarker/tag/MenuTagDirective.java
@@ -1,4 +1,4 @@
-package run.halo.app.model.freemarker.tag;
+package run.halo.app.core.freemarker.tag;
import freemarker.core.Environment;
import freemarker.template.*;
@@ -16,7 +16,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Freemarker custom tag of menu.
*
* @author ryanwang
- * @date : 2019/3/22
+ * @date 2019-03-22
*/
@Component
public class MenuTagDirective implements TemplateDirectiveModel {
@@ -50,6 +50,10 @@ public class MenuTagDirective implements TemplateDirectiveModel {
String team = params.get("team").toString();
env.setVariable("menus", builder.build().wrap(menuService.listByTeam(team, Sort.by(DESC, "priority"))));
break;
+ case "treeByTeam":
+ String treeTeam = params.get("team").toString();
+ env.setVariable("menus", builder.build().wrap(menuService.listByTeamAsTree(treeTeam, Sort.by(DESC, "priority"))));
+ break;
case "count":
env.setVariable("count", builder.build().wrap(menuService.count()));
break;
diff --git a/src/main/java/run/halo/app/model/freemarker/tag/PhotoTagDirective.java b/src/main/java/run/halo/app/core/freemarker/tag/PhotoTagDirective.java
similarity index 96%
rename from src/main/java/run/halo/app/model/freemarker/tag/PhotoTagDirective.java
rename to src/main/java/run/halo/app/core/freemarker/tag/PhotoTagDirective.java
index 16e55b374..0280e90e2 100644
--- a/src/main/java/run/halo/app/model/freemarker/tag/PhotoTagDirective.java
+++ b/src/main/java/run/halo/app/core/freemarker/tag/PhotoTagDirective.java
@@ -1,4 +1,4 @@
-package run.halo.app.model.freemarker.tag;
+package run.halo.app.core.freemarker.tag;
import freemarker.core.Environment;
import freemarker.template.*;
@@ -16,7 +16,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Freemarker custom tag of photo.
*
* @author ryanwang
- * @date : 2019/4/21
+ * @date 2019-04-21
*/
@Component
public class PhotoTagDirective implements TemplateDirectiveModel {
diff --git a/src/main/java/run/halo/app/model/freemarker/tag/PostTagDirective.java b/src/main/java/run/halo/app/core/freemarker/tag/PostTagDirective.java
similarity index 75%
rename from src/main/java/run/halo/app/model/freemarker/tag/PostTagDirective.java
rename to src/main/java/run/halo/app/core/freemarker/tag/PostTagDirective.java
index df8a636d2..69b2c448e 100644
--- a/src/main/java/run/halo/app/model/freemarker/tag/PostTagDirective.java
+++ b/src/main/java/run/halo/app/core/freemarker/tag/PostTagDirective.java
@@ -1,4 +1,4 @@
-package run.halo.app.model.freemarker.tag;
+package run.halo.app.core.freemarker.tag;
import freemarker.core.Environment;
import freemarker.template.*;
@@ -16,7 +16,7 @@ import java.util.Map;
* Freemarker custom tag of post.
*
* @author ryanwang
- * @date : 2018/4/26
+ * @date 2018-04-26
*/
@Component
public class PostTagDirective implements TemplateDirectiveModel {
@@ -48,7 +48,7 @@ public class PostTagDirective implements TemplateDirectiveModel {
env.setVariable("posts", builder.build().wrap(postService.listLatest(top)));
break;
case "count":
- env.setVariable("count", builder.build().wrap(postService.count()));
+ env.setVariable("count", builder.build().wrap(postService.countByStatus(PostStatus.PUBLISHED)));
break;
case "archiveYear":
env.setVariable("archives", builder.build().wrap(postService.listYearArchives()));
@@ -56,14 +56,26 @@ public class PostTagDirective implements TemplateDirectiveModel {
case "archiveMonth":
env.setVariable("archives", builder.build().wrap(postService.listMonthArchives()));
break;
+ case "archive":
+ String type = params.get("type").toString();
+ env.setVariable("archives", builder.build().wrap("year".equals(type) ? postService.listYearArchives() : postService.listMonthArchives()));
+ break;
case "listByCategoryId":
Integer categoryId = Integer.parseInt(params.get("categoryId").toString());
env.setVariable("posts", builder.build().wrap(postCategoryService.listPostBy(categoryId, PostStatus.PUBLISHED)));
break;
+ case "listByCategorySlug":
+ String categorySlug = params.get("categorySlug").toString();
+ env.setVariable("posts", builder.build().wrap(postCategoryService.listPostBy(categorySlug, PostStatus.PUBLISHED)));
+ break;
case "listByTagId":
Integer tagId = Integer.parseInt(params.get("tagId").toString());
env.setVariable("posts", builder.build().wrap(postTagService.listPostsBy(tagId, PostStatus.PUBLISHED)));
break;
+ case "listByTagSlug":
+ String tagSlug = params.get("tagSlug").toString();
+ env.setVariable("posts", builder.build().wrap(postTagService.listPostsBy(tagSlug, PostStatus.PUBLISHED)));
+ break;
default:
break;
}
diff --git a/src/main/java/run/halo/app/model/freemarker/tag/TagTagDirective.java b/src/main/java/run/halo/app/core/freemarker/tag/TagTagDirective.java
similarity index 96%
rename from src/main/java/run/halo/app/model/freemarker/tag/TagTagDirective.java
rename to src/main/java/run/halo/app/core/freemarker/tag/TagTagDirective.java
index f11126cfd..132e92022 100644
--- a/src/main/java/run/halo/app/model/freemarker/tag/TagTagDirective.java
+++ b/src/main/java/run/halo/app/core/freemarker/tag/TagTagDirective.java
@@ -1,4 +1,4 @@
-package run.halo.app.model.freemarker.tag;
+package run.halo.app.core.freemarker.tag;
import freemarker.core.Environment;
import freemarker.template.*;
@@ -17,7 +17,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Freemarker custom tag of tag.
*
* @author ryanwang
- * @date : 2019/3/22
+ * @date 2019-03-22
*/
@Component
public class TagTagDirective implements TemplateDirectiveModel {
diff --git a/src/main/java/run/halo/app/event/comment/CommentPassEvent.java b/src/main/java/run/halo/app/event/comment/CommentPassEvent.java
deleted file mode 100644
index c29865273..000000000
--- a/src/main/java/run/halo/app/event/comment/CommentPassEvent.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package run.halo.app.event.comment;
-
-import org.springframework.lang.NonNull;
-
-/**
- * PostComment pass event.
- *
- * @author johnniang
- * @date 19-4-23
- */
-public class CommentPassEvent extends CommentBaseEvent {
-
- /**
- * Create a new ApplicationEvent.
- *
- * @param source the object on which the event initially occurred (never {@code null})
- * @param commentId comment id
- */
- public CommentPassEvent(Object source, @NonNull Long commentId) {
- super(source, commentId);
- }
-}
diff --git a/src/main/java/run/halo/app/event/logger/LogEvent.java b/src/main/java/run/halo/app/event/logger/LogEvent.java
index d863536a9..2f26c258d 100644
--- a/src/main/java/run/halo/app/event/logger/LogEvent.java
+++ b/src/main/java/run/halo/app/event/logger/LogEvent.java
@@ -3,6 +3,7 @@ package run.halo.app.event.logger;
import org.springframework.context.ApplicationEvent;
import run.halo.app.model.enums.LogType;
import run.halo.app.model.params.LogParam;
+import run.halo.app.utils.ServletUtils;
import run.halo.app.utils.ValidationUtils;
/**
@@ -25,6 +26,9 @@ public class LogEvent extends ApplicationEvent {
// Validate the log param
ValidationUtils.validate(logParam);
+ // Set ip address
+ logParam.setIpAddress(ServletUtils.getRequestIp());
+
this.logParam = logParam;
}
diff --git a/src/main/java/run/halo/app/event/post/PostVisitEvent.java b/src/main/java/run/halo/app/event/post/PostVisitEvent.java
index f7d0d8b1c..ceb41d614 100644
--- a/src/main/java/run/halo/app/event/post/PostVisitEvent.java
+++ b/src/main/java/run/halo/app/event/post/PostVisitEvent.java
@@ -1,5 +1,9 @@
package run.halo.app.event.post;
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
+import run.halo.app.utils.ServiceUtils;
+
/**
* Post visit event.
*
@@ -14,7 +18,8 @@ public class PostVisitEvent extends AbstractVisitEvent {
* @param source the object on which the event initially occurred (never {@code null})
* @param postId post id must not be null
*/
- public PostVisitEvent(Object source, Integer postId) {
+ public PostVisitEvent(Object source, @NonNull Integer postId) {
super(source, postId);
+ Assert.isTrue(!ServiceUtils.isEmptyId(postId), "Post id must not be empty");
}
}
diff --git a/src/main/java/run/halo/app/factory/StringToEnumConverterFactory.java b/src/main/java/run/halo/app/factory/StringToEnumConverterFactory.java
index 6b68e7987..1e1155636 100644
--- a/src/main/java/run/halo/app/factory/StringToEnumConverterFactory.java
+++ b/src/main/java/run/halo/app/factory/StringToEnumConverterFactory.java
@@ -6,7 +6,7 @@ import org.springframework.stereotype.Component;
/**
* @author ryanwang
- * @date : 2019/3/14
+ * @date 2019-3-14
*/
@Component
public class StringToEnumConverterFactory implements ConverterFactory {
diff --git a/src/main/java/run/halo/app/filter/CorsFilter.java b/src/main/java/run/halo/app/filter/CorsFilter.java
index 191022901..51d653acc 100644
--- a/src/main/java/run/halo/app/filter/CorsFilter.java
+++ b/src/main/java/run/halo/app/filter/CorsFilter.java
@@ -4,7 +4,6 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.filter.GenericFilterBean;
-import run.halo.app.security.filter.AdminAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
@@ -14,6 +13,9 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
+import static run.halo.app.model.support.HaloConst.ADMIN_TOKEN_HEADER_NAME;
+import static run.halo.app.model.support.HaloConst.API_ACCESS_KEY_HEADER_NAME;
+
/**
* Filter for CORS.
*
@@ -21,7 +23,7 @@ import java.io.IOException;
*/
public class CorsFilter extends GenericFilterBean {
- private final static String ALLOW_HEADERS = StringUtils.joinWith(",", HttpHeaders.CONTENT_TYPE, AdminAuthenticationFilter.ADMIN_TOKEN_HEADER_NAME);
+ private final static String ALLOW_HEADERS = StringUtils.joinWith(",", HttpHeaders.CONTENT_TYPE, ADMIN_TOKEN_HEADER_NAME, API_ACCESS_KEY_HEADER_NAME);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
diff --git a/src/main/java/run/halo/app/handler/file/AliOssFileHandler.java b/src/main/java/run/halo/app/handler/file/AliOssFileHandler.java
new file mode 100644
index 000000000..854e7aba1
--- /dev/null
+++ b/src/main/java/run/halo/app/handler/file/AliOssFileHandler.java
@@ -0,0 +1,160 @@
+package run.halo.app.handler.file;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.model.DeleteObjectsRequest;
+import com.aliyun.oss.model.PutObjectResult;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+import org.springframework.web.multipart.MultipartFile;
+import run.halo.app.exception.FileOperationException;
+import run.halo.app.model.enums.AttachmentType;
+import run.halo.app.model.properties.AliOssProperties;
+import run.halo.app.model.support.UploadResult;
+import run.halo.app.service.OptionService;
+import run.halo.app.utils.FilenameUtils;
+import run.halo.app.utils.ImageUtils;
+
+import java.awt.image.BufferedImage;
+import java.util.Objects;
+
+/**
+ * Ali oss file handler.
+ *
+ * @author MyFaith
+ * @author ryanwang
+ * @date 2019-04-04
+ */
+@Slf4j
+@Component
+public class AliOssFileHandler implements FileHandler {
+
+ private final OptionService optionService;
+
+ public AliOssFileHandler(OptionService optionService) {
+ this.optionService = optionService;
+ }
+
+ @Override
+ public UploadResult upload(MultipartFile file) {
+ Assert.notNull(file, "Multipart file must not be null");
+
+ // Get config
+ String protocol = optionService.getByPropertyOfNonNull(AliOssProperties.OSS_PROTOCOL).toString();
+ String domain = optionService.getByPropertyOrDefault(AliOssProperties.OSS_DOMAIN, String.class, "");
+ String source = optionService.getByPropertyOrDefault(AliOssProperties.OSS_SOURCE, String.class, "");
+ String endPoint = optionService.getByPropertyOfNonNull(AliOssProperties.OSS_ENDPOINT).toString();
+ String accessKey = optionService.getByPropertyOfNonNull(AliOssProperties.OSS_ACCESS_KEY).toString();
+ String accessSecret = optionService.getByPropertyOfNonNull(AliOssProperties.OSS_ACCESS_SECRET).toString();
+ String bucketName = optionService.getByPropertyOfNonNull(AliOssProperties.OSS_BUCKET_NAME).toString();
+ String styleRule = optionService.getByPropertyOrDefault(AliOssProperties.OSS_STYLE_RULE, String.class, "");
+ String thumbnailStyleRule = optionService.getByPropertyOrDefault(AliOssProperties.OSS_THUMBNAIL_STYLE_RULE, String.class, "");
+
+ // Init OSS client
+ OSS ossClient = new OSSClientBuilder().build(endPoint, accessKey, accessSecret);
+
+ StringBuilder basePath = new StringBuilder(protocol);
+
+ if (StringUtils.isNotEmpty(domain)) {
+ basePath.append(domain)
+ .append("/");
+ } else {
+ basePath.append(bucketName)
+ .append(".")
+ .append(endPoint)
+ .append("/");
+ }
+
+ try {
+ String basename = FilenameUtils.getBasename(file.getOriginalFilename());
+ String extension = FilenameUtils.getExtension(file.getOriginalFilename());
+ String timestamp = String.valueOf(System.currentTimeMillis());
+ StringBuilder upFilePath = new StringBuilder();
+
+ if (StringUtils.isNotEmpty(source)) {
+ upFilePath.append(source)
+ .append("/");
+ }
+
+ upFilePath.append(basename)
+ .append("_")
+ .append(timestamp)
+ .append(".")
+ .append(extension);
+
+ String filePath = StringUtils.join(basePath.toString(), upFilePath.toString());
+
+ log.info(basePath.toString());
+
+ // Upload
+ PutObjectResult putObjectResult = ossClient.putObject(bucketName, upFilePath.toString(), file.getInputStream());
+ if (putObjectResult == null) {
+ throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到阿里云失败 ");
+ }
+
+ // Response result
+ UploadResult uploadResult = new UploadResult();
+ uploadResult.setFilename(basename);
+ uploadResult.setFilePath(StringUtils.isBlank(styleRule) ? filePath : filePath + styleRule);
+ uploadResult.setKey(upFilePath.toString());
+ uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
+ uploadResult.setSuffix(extension);
+ uploadResult.setSize(file.getSize());
+
+ // Handle thumbnail
+ if (FileHandler.isImageType(uploadResult.getMediaType())) {
+ BufferedImage image = ImageUtils.getImageFromFile(file.getInputStream(), extension);
+ uploadResult.setWidth(image.getWidth());
+ uploadResult.setHeight(image.getHeight());
+ if (ImageUtils.EXTENSION_ICO.equals(extension)) {
+ uploadResult.setThumbPath(filePath);
+ } else {
+ uploadResult.setThumbPath(StringUtils.isBlank(thumbnailStyleRule) ? filePath : filePath + thumbnailStyleRule);
+ }
+ }
+
+ return uploadResult;
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ ossClient.shutdown();
+ }
+
+ // Build result
+ UploadResult result = new UploadResult();
+
+ log.info("File: [{}] uploaded successfully", file.getOriginalFilename());
+
+ return result;
+ }
+
+ @Override
+ public void delete(String key) {
+ Assert.notNull(key, "File key must not be blank");
+
+ // Get config
+ String endPoint = optionService.getByPropertyOfNonNull(AliOssProperties.OSS_ENDPOINT).toString();
+ String accessKey = optionService.getByPropertyOfNonNull(AliOssProperties.OSS_ACCESS_KEY).toString();
+ String accessSecret = optionService.getByPropertyOfNonNull(AliOssProperties.OSS_ACCESS_SECRET).toString();
+ String bucketName = optionService.getByPropertyOfNonNull(AliOssProperties.OSS_BUCKET_NAME).toString();
+
+ // Init OSS client
+ OSS ossClient = new OSSClientBuilder().build(endPoint, accessKey, accessSecret);
+
+ try {
+ ossClient.deleteObject(new DeleteObjectsRequest(bucketName).withKey(key));
+ } catch (Exception e) {
+ throw new FileOperationException("附件 " + key + " 从阿里云删除失败", e);
+ } finally {
+ ossClient.shutdown();
+ }
+ }
+
+ @Override
+ public boolean supportType(AttachmentType type) {
+ return AttachmentType.ALIOSS.equals(type);
+ }
+}
diff --git a/src/main/java/run/halo/app/handler/file/AliYunFileHandler.java b/src/main/java/run/halo/app/handler/file/AliYunFileHandler.java
deleted file mode 100644
index d13a1289c..000000000
--- a/src/main/java/run/halo/app/handler/file/AliYunFileHandler.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package run.halo.app.handler.file;
-
-import com.aliyun.oss.OSS;
-import com.aliyun.oss.OSSClientBuilder;
-import com.aliyun.oss.model.DeleteObjectsRequest;
-import com.aliyun.oss.model.PutObjectResult;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.http.MediaType;
-import org.springframework.stereotype.Component;
-import org.springframework.util.Assert;
-import org.springframework.web.multipart.MultipartFile;
-import run.halo.app.exception.FileOperationException;
-import run.halo.app.model.enums.AttachmentType;
-import run.halo.app.model.properties.AliYunProperties;
-import run.halo.app.model.support.UploadResult;
-import run.halo.app.service.OptionService;
-import run.halo.app.utils.FilenameUtils;
-
-import javax.imageio.ImageIO;
-import java.awt.image.BufferedImage;
-import java.util.Objects;
-
-/**
- * AliYun file handler.
- *
- * @author MyFaith
- * @author ryanwang
- * @date 2019-04-04 00:06:13
- */
-@Slf4j
-@Component
-public class AliYunFileHandler implements FileHandler {
-
- private final OptionService optionService;
-
- public AliYunFileHandler(OptionService optionService) {
- this.optionService = optionService;
- }
-
- @Override
- public UploadResult upload(MultipartFile file) {
- Assert.notNull(file, "Multipart file must not be null");
-
- // Get config
- String ossDomain = optionService.getByPropertyOrDefault(AliYunProperties.OSS_DOMAIN, String.class, "");
- String ossEndPoint = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_ENDPOINT).toString();
- String ossAccessKey = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_ACCESS_KEY).toString();
- String ossAccessSecret = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_ACCESS_SECRET).toString();
- String ossBucketName = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_BUCKET_NAME).toString();
- String ossSource = StringUtils.join("https://", ossBucketName, "." + ossEndPoint);
- String ossStyleRule = optionService.getByPropertyOrDefault(AliYunProperties.OSS_STYLE_RULE, String.class, "");
- String ossThumbnailStyleRule = optionService.getByPropertyOrDefault(AliYunProperties.OSS_THUMBNAIL_STYLE_RULE, String.class, "");
-
- // Init OSS client
- OSS ossClient = new OSSClientBuilder().build(ossEndPoint, ossAccessKey, ossAccessSecret);
-
- try {
- String basename = FilenameUtils.getBasename(file.getOriginalFilename());
- String extension = FilenameUtils.getExtension(file.getOriginalFilename());
- String timestamp = String.valueOf(System.currentTimeMillis());
- String upFilePath = StringUtils.join(basename, "_", timestamp, ".", extension);
- String filePath = StringUtils.join(StringUtils.appendIfMissing(StringUtils.isNotBlank(ossDomain) ? ossDomain : ossSource, "/"), upFilePath);
-
- // Upload
- PutObjectResult putObjectResult = ossClient.putObject(ossBucketName, upFilePath, file.getInputStream());
- if (putObjectResult == null) {
- throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到阿里云失败 ");
- }
-
- // Response result
- UploadResult uploadResult = new UploadResult();
- uploadResult.setFilename(basename);
- uploadResult.setFilePath(StringUtils.isBlank(ossStyleRule) ? filePath : filePath + ossStyleRule);
- uploadResult.setKey(upFilePath);
- uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
- uploadResult.setSuffix(extension);
- uploadResult.setSize(file.getSize());
-
- // Handle thumbnail
- if (FileHandler.isImageType(uploadResult.getMediaType())) {
- BufferedImage image = ImageIO.read(file.getInputStream());
- uploadResult.setWidth(image.getWidth());
- uploadResult.setHeight(image.getHeight());
- uploadResult.setThumbPath(StringUtils.isBlank(ossThumbnailStyleRule) ? filePath : filePath + ossThumbnailStyleRule);
- }
-
- return uploadResult;
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- ossClient.shutdown();
- }
-
- // Build result
- UploadResult result = new UploadResult();
-
- log.info("File: [{}] uploaded successfully", file.getOriginalFilename());
-
- return result;
- }
-
- @Override
- public void delete(String key) {
- Assert.notNull(key, "File key must not be blank");
-
- // Get config
- String ossEndPoint = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_ENDPOINT).toString();
- String ossAccessKey = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_ACCESS_KEY).toString();
- String ossAccessSecret = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_ACCESS_SECRET).toString();
- String ossBucketName = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_BUCKET_NAME).toString();
-
- // Init OSS client
- OSS ossClient = new OSSClientBuilder().build(ossEndPoint, ossAccessKey, ossAccessSecret);
-
- try {
- ossClient.deleteObject(new DeleteObjectsRequest(ossBucketName).withKey(key));
- } catch (Exception e) {
- throw new FileOperationException("附件 " + key + " 从阿里云删除失败", e);
- } finally {
- ossClient.shutdown();
- }
- }
-
- @Override
- public boolean supportType(AttachmentType type) {
- return AttachmentType.ALIYUN.equals(type);
- }
-}
diff --git a/src/main/java/run/halo/app/handler/file/BaiDuYunFileHandler.java b/src/main/java/run/halo/app/handler/file/BaiduBosFileHandler.java
similarity index 55%
rename from src/main/java/run/halo/app/handler/file/BaiDuYunFileHandler.java
rename to src/main/java/run/halo/app/handler/file/BaiduBosFileHandler.java
index e01873641..e57641231 100644
--- a/src/main/java/run/halo/app/handler/file/BaiDuYunFileHandler.java
+++ b/src/main/java/run/halo/app/handler/file/BaiduBosFileHandler.java
@@ -13,28 +13,29 @@ import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;
import run.halo.app.exception.FileOperationException;
import run.halo.app.model.enums.AttachmentType;
-import run.halo.app.model.properties.BaiDuYunProperties;
+import run.halo.app.model.properties.BaiduBosProperties;
import run.halo.app.model.support.UploadResult;
import run.halo.app.service.OptionService;
import run.halo.app.utils.FilenameUtils;
+import run.halo.app.utils.ImageUtils;
-import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.util.Objects;
/**
- * BaiDuYun file handler.
+ * Baidu bos file handler.
*
* @author wangya
+ * @author ryanwang
* @date 2019-07-20
*/
@Slf4j
@Component
-public class BaiDuYunFileHandler implements FileHandler {
+public class BaiduBosFileHandler implements FileHandler {
private final OptionService optionService;
- public BaiDuYunFileHandler(OptionService optionService) {
+ public BaiduBosFileHandler(OptionService optionService) {
this.optionService = optionService;
}
@@ -43,31 +44,34 @@ public class BaiDuYunFileHandler implements FileHandler {
Assert.notNull(file, "Multipart file must not be null");
// Get config
- String bosDomain = optionService.getByPropertyOrDefault(BaiDuYunProperties.BOS_DOMAIN, String.class, "");
- String bosEndPoint = optionService.getByPropertyOfNonNull(BaiDuYunProperties.BOS_ENDPOINT).toString();
- String bosAccessKey = optionService.getByPropertyOfNonNull(BaiDuYunProperties.BOS_ACCESS_KEY).toString();
- String bosSecretKey = optionService.getByPropertyOfNonNull(BaiDuYunProperties.BOS_SECRET_KEY).toString();
- String bosBucketName = optionService.getByPropertyOfNonNull(BaiDuYunProperties.BOS_BUCKET_NAME).toString();
- String bosStyleRule = optionService.getByPropertyOrDefault(BaiDuYunProperties.BOS_STYLE_RULE, String.class, "");
- String bosThumbnailStyleRule = optionService.getByPropertyOrDefault(BaiDuYunProperties.BOS_THUMBNAIL_STYLE_RULE, String.class, "");
- String bosSource = StringUtils.join("https://", bosBucketName, "." + bosEndPoint);
+ Object protocol = optionService.getByPropertyOfNonNull(BaiduBosProperties.BOS_PROTOCOL);
+ String domain = optionService.getByPropertyOrDefault(BaiduBosProperties.BOS_DOMAIN, String.class, "");
+ String endPoint = optionService.getByPropertyOfNonNull(BaiduBosProperties.BOS_ENDPOINT).toString();
+ String accessKey = optionService.getByPropertyOfNonNull(BaiduBosProperties.BOS_ACCESS_KEY).toString();
+ String secretKey = optionService.getByPropertyOfNonNull(BaiduBosProperties.BOS_SECRET_KEY).toString();
+ String bucketName = optionService.getByPropertyOfNonNull(BaiduBosProperties.BOS_BUCKET_NAME).toString();
+ String styleRule = optionService.getByPropertyOrDefault(BaiduBosProperties.BOS_STYLE_RULE, String.class, "");
+ String thumbnailStyleRule = optionService.getByPropertyOrDefault(BaiduBosProperties.BOS_THUMBNAIL_STYLE_RULE, String.class, "");
+ String source = StringUtils.join(protocol, bucketName, "." + endPoint);
BosClientConfiguration config = new BosClientConfiguration();
- config.setCredentials(new DefaultBceCredentials(bosAccessKey, bosSecretKey));
- config.setEndpoint(bosEndPoint);
+ config.setCredentials(new DefaultBceCredentials(accessKey, secretKey));
+ config.setEndpoint(endPoint);
// Init OSS client
BosClient client = new BosClient(config);
+ domain = protocol + domain;
+
try {
String basename = FilenameUtils.getBasename(file.getOriginalFilename());
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
String timestamp = String.valueOf(System.currentTimeMillis());
String upFilePath = StringUtils.join(basename, "_", timestamp, ".", extension);
- String filePath = StringUtils.join(StringUtils.appendIfMissing(StringUtils.isNotBlank(bosDomain) ? bosDomain : bosSource, "/"), upFilePath);
+ String filePath = StringUtils.join(StringUtils.appendIfMissing(StringUtils.isNotBlank(domain) ? domain : source, "/"), upFilePath);
// Upload
- PutObjectResponse putObjectResponseFromInputStream = client.putObject(bosBucketName, upFilePath, file.getInputStream());
+ PutObjectResponse putObjectResponseFromInputStream = client.putObject(bucketName, upFilePath, file.getInputStream());
if (putObjectResponseFromInputStream == null) {
throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到百度云失败 ");
}
@@ -75,7 +79,7 @@ public class BaiDuYunFileHandler implements FileHandler {
// Response result
UploadResult uploadResult = new UploadResult();
uploadResult.setFilename(basename);
- uploadResult.setFilePath(StringUtils.isBlank(bosStyleRule) ? filePath : filePath + bosStyleRule);
+ uploadResult.setFilePath(StringUtils.isBlank(styleRule) ? filePath : filePath + styleRule);
uploadResult.setKey(upFilePath);
uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
uploadResult.setSuffix(extension);
@@ -83,10 +87,14 @@ public class BaiDuYunFileHandler implements FileHandler {
// Handle thumbnail
if (FileHandler.isImageType(uploadResult.getMediaType())) {
- BufferedImage image = ImageIO.read(file.getInputStream());
+ BufferedImage image = ImageUtils.getImageFromFile(file.getInputStream(), extension);
uploadResult.setWidth(image.getWidth());
uploadResult.setHeight(image.getHeight());
- uploadResult.setThumbPath(StringUtils.isBlank(bosThumbnailStyleRule) ? filePath : filePath + bosThumbnailStyleRule);
+ if (ImageUtils.EXTENSION_ICO.equals(extension)) {
+ uploadResult.setThumbPath(filePath);
+ } else {
+ uploadResult.setThumbPath(StringUtils.isBlank(thumbnailStyleRule) ? filePath : filePath + thumbnailStyleRule);
+ }
}
return uploadResult;
@@ -102,20 +110,20 @@ public class BaiDuYunFileHandler implements FileHandler {
Assert.notNull(key, "File key must not be blank");
// Get config
- String bosEndPoint = optionService.getByPropertyOfNonNull(BaiDuYunProperties.BOS_ENDPOINT).toString();
- String bosAccessKey = optionService.getByPropertyOfNonNull(BaiDuYunProperties.BOS_ACCESS_KEY).toString();
- String bosSecretKey = optionService.getByPropertyOfNonNull(BaiDuYunProperties.BOS_SECRET_KEY).toString();
- String bosBucketName = optionService.getByPropertyOfNonNull(BaiDuYunProperties.BOS_BUCKET_NAME).toString();
+ String endPoint = optionService.getByPropertyOfNonNull(BaiduBosProperties.BOS_ENDPOINT).toString();
+ String accessKey = optionService.getByPropertyOfNonNull(BaiduBosProperties.BOS_ACCESS_KEY).toString();
+ String secretKey = optionService.getByPropertyOfNonNull(BaiduBosProperties.BOS_SECRET_KEY).toString();
+ String bucketName = optionService.getByPropertyOfNonNull(BaiduBosProperties.BOS_BUCKET_NAME).toString();
BosClientConfiguration config = new BosClientConfiguration();
- config.setCredentials(new DefaultBceCredentials(bosAccessKey, bosSecretKey));
- config.setEndpoint(bosEndPoint);
+ config.setCredentials(new DefaultBceCredentials(accessKey, secretKey));
+ config.setEndpoint(endPoint);
// Init OSS client
BosClient client = new BosClient(config);
try {
- client.deleteObject(bosBucketName, key);
+ client.deleteObject(bucketName, key);
} catch (Exception e) {
throw new FileOperationException("附件 " + key + " 从百度云删除失败", e);
} finally {
@@ -125,6 +133,6 @@ public class BaiDuYunFileHandler implements FileHandler {
@Override
public boolean supportType(AttachmentType type) {
- return AttachmentType.BAIDUYUN.equals(type);
+ return AttachmentType.BAIDUBOS.equals(type);
}
}
diff --git a/src/main/java/run/halo/app/handler/file/FileHandler.java b/src/main/java/run/halo/app/handler/file/FileHandler.java
index 8d7b5da31..5986fbdba 100644
--- a/src/main/java/run/halo/app/handler/file/FileHandler.java
+++ b/src/main/java/run/halo/app/handler/file/FileHandler.java
@@ -16,7 +16,7 @@ import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR;
* File handler interface.
*
* @author johnniang
- * @date 3/27/19
+ * @date 2019-03-27
*/
public interface FileHandler {
diff --git a/src/main/java/run/halo/app/handler/file/FileHandlers.java b/src/main/java/run/halo/app/handler/file/FileHandlers.java
index 4df893abe..8a79d215a 100644
--- a/src/main/java/run/halo/app/handler/file/FileHandlers.java
+++ b/src/main/java/run/halo/app/handler/file/FileHandlers.java
@@ -20,7 +20,7 @@ import java.util.LinkedList;
* File handler manager.
*
* @author johnniang
- * @date 3/27/19
+ * @date 2019-03-27
*/
@Slf4j
@Component
diff --git a/src/main/java/run/halo/app/handler/file/LocalFileHandler.java b/src/main/java/run/halo/app/handler/file/LocalFileHandler.java
index 531543d17..e87797583 100644
--- a/src/main/java/run/halo/app/handler/file/LocalFileHandler.java
+++ b/src/main/java/run/halo/app/handler/file/LocalFileHandler.java
@@ -14,9 +14,10 @@ import run.halo.app.model.support.UploadResult;
import run.halo.app.service.OptionService;
import run.halo.app.utils.FilenameUtils;
import run.halo.app.utils.HaloUtils;
+import run.halo.app.utils.ImageUtils;
-import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
+import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -54,12 +55,9 @@ public class LocalFileHandler implements FileHandler {
* Thumbnail height.
*/
private final static int THUMB_HEIGHT = 256;
-
- ReentrantLock lock = new ReentrantLock();
-
private final OptionService optionService;
-
private final String workDir;
+ ReentrantLock lock = new ReentrantLock();
public LocalFileHandler(OptionService optionService,
HaloProperties haloProperties) {
@@ -151,7 +149,7 @@ public class LocalFileHandler implements FileHandler {
Path thumbnailPath = Paths.get(workDir + thumbnailSubFilePath);
// Read as image
- BufferedImage originalImage = ImageIO.read(uploadPath.toFile());
+ BufferedImage originalImage = ImageUtils.getImageFromFile(new FileInputStream(uploadPath.toFile()), extension);
// Set width and height
uploadResult.setWidth(originalImage.getWidth());
uploadResult.setHeight(originalImage.getHeight());
@@ -238,5 +236,4 @@ public class LocalFileHandler implements FileHandler {
}
return result;
}
-
}
diff --git a/src/main/java/run/halo/app/handler/file/QnYunFileHandler.java b/src/main/java/run/halo/app/handler/file/QiniuOssFileHandler.java
similarity index 70%
rename from src/main/java/run/halo/app/handler/file/QnYunFileHandler.java
rename to src/main/java/run/halo/app/handler/file/QiniuOssFileHandler.java
index c9907e32a..35db08d24 100644
--- a/src/main/java/run/halo/app/handler/file/QnYunFileHandler.java
+++ b/src/main/java/run/halo/app/handler/file/QiniuOssFileHandler.java
@@ -17,11 +17,12 @@ import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;
import run.halo.app.exception.FileOperationException;
import run.halo.app.model.enums.AttachmentType;
-import run.halo.app.model.properties.QnYunProperties;
+import run.halo.app.model.properties.QiniuOssProperties;
import run.halo.app.model.support.QiNiuPutSet;
import run.halo.app.model.support.UploadResult;
import run.halo.app.service.OptionService;
import run.halo.app.utils.FilenameUtils;
+import run.halo.app.utils.ImageUtils;
import run.halo.app.utils.JsonUtils;
import java.io.IOException;
@@ -32,19 +33,19 @@ import java.util.Objects;
import static run.halo.app.handler.file.FileHandler.isImageType;
/**
- * Qi niu yun file handler.
+ * Qiniu oss file handler.
*
* @author johnniang
* @author ryanwang
- * @date 3/27/19
+ * @date 2019-03-27
*/
@Slf4j
@Component
-public class QnYunFileHandler implements FileHandler {
+public class QiniuOssFileHandler implements FileHandler {
private final OptionService optionService;
- public QnYunFileHandler(OptionService optionService) {
+ public QiniuOssFileHandler(OptionService optionService) {
this.optionService = optionService;
}
@@ -54,12 +55,14 @@ public class QnYunFileHandler implements FileHandler {
// Get all config
Zone zone = optionService.getQnYunZone();
- String accessKey = optionService.getByPropertyOfNonNull(QnYunProperties.OSS_ACCESS_KEY).toString();
- String secretKey = optionService.getByPropertyOfNonNull(QnYunProperties.OSS_SECRET_KEY).toString();
- String bucket = optionService.getByPropertyOfNonNull(QnYunProperties.OSS_BUCKET).toString();
- String domain = optionService.getByPropertyOfNonNull(QnYunProperties.OSS_DOMAIN).toString();
- String styleRule = optionService.getByPropertyOrDefault(QnYunProperties.OSS_STYLE_RULE, String.class, "");
- String thumbnailStyleRule = optionService.getByPropertyOrDefault(QnYunProperties.OSS_THUMBNAIL_STYLE_RULE, String.class, "");
+ String accessKey = optionService.getByPropertyOfNonNull(QiniuOssProperties.OSS_ACCESS_KEY).toString();
+ String secretKey = optionService.getByPropertyOfNonNull(QiniuOssProperties.OSS_SECRET_KEY).toString();
+ String bucket = optionService.getByPropertyOfNonNull(QiniuOssProperties.OSS_BUCKET).toString();
+ String protocol = optionService.getByPropertyOfNonNull(QiniuOssProperties.OSS_PROTOCOL).toString();
+ String domain = optionService.getByPropertyOfNonNull(QiniuOssProperties.OSS_DOMAIN).toString();
+ String source = optionService.getByPropertyOrDefault(QiniuOssProperties.OSS_SOURCE, String.class, "");
+ String styleRule = optionService.getByPropertyOrDefault(QiniuOssProperties.OSS_STYLE_RULE, String.class, "");
+ String thumbnailStyleRule = optionService.getByPropertyOrDefault(QiniuOssProperties.OSS_THUMBNAIL_STYLE_RULE, String.class, "");
// Create configuration
Configuration configuration = new Configuration(zone);
@@ -79,16 +82,31 @@ public class QnYunFileHandler implements FileHandler {
// Create temp path
Path tmpPath = Paths.get(System.getProperty("java.io.tmpdir"), bucket);
+ StringBuilder basePath = new StringBuilder(protocol)
+ .append(domain)
+ .append("/");
+
try {
String basename = FilenameUtils.getBasename(file.getOriginalFilename());
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
+ String timestamp = String.valueOf(System.currentTimeMillis());
+ StringBuilder upFilePath = new StringBuilder();
+ if (StringUtils.isNotEmpty(source)) {
+ upFilePath.append(source)
+ .append("/");
+ }
+ upFilePath.append(basename)
+ .append("_")
+ .append(timestamp)
+ .append(".")
+ .append(extension);
// Get file recorder for temp directory
FileRecorder fileRecorder = new FileRecorder(tmpPath.toFile());
// Get upload manager
UploadManager uploadManager = new UploadManager(configuration, fileRecorder);
// Put the file
- Response response = uploadManager.put(file.getInputStream(), null, uploadToken, null, null);
+ Response response = uploadManager.put(file.getInputStream(), upFilePath.toString(), uploadToken, null, null);
log.debug("QnYun response: [{}]", response.toString());
log.debug("QnYun response body: [{}]", response.bodyString());
@@ -99,13 +117,13 @@ public class QnYunFileHandler implements FileHandler {
QiNiuPutSet putSet = JsonUtils.jsonToObject(response.bodyString(), QiNiuPutSet.class);
// Get file full path
- String filePath = StringUtils.appendIfMissing(domain, "/") + putSet.getHash();
+ String filePath = StringUtils.join(basePath.toString(), upFilePath.toString());
// Build upload result
UploadResult result = new UploadResult();
result.setFilename(basename);
result.setFilePath(StringUtils.isBlank(styleRule) ? filePath : filePath + styleRule);
- result.setKey(putSet.getKey());
+ result.setKey(upFilePath.toString());
result.setSuffix(extension);
result.setWidth(putSet.getWidth());
result.setHeight(putSet.getHeight());
@@ -113,7 +131,11 @@ public class QnYunFileHandler implements FileHandler {
result.setSize(file.getSize());
if (isImageType(result.getMediaType())) {
- result.setThumbPath(StringUtils.isBlank(thumbnailStyleRule) ? filePath : filePath + thumbnailStyleRule);
+ if (ImageUtils.EXTENSION_ICO.equals(extension)) {
+ result.setThumbPath(filePath);
+ } else {
+ result.setThumbPath(StringUtils.isBlank(thumbnailStyleRule) ? filePath : filePath + thumbnailStyleRule);
+ }
}
return result;
@@ -132,9 +154,9 @@ public class QnYunFileHandler implements FileHandler {
// Get all config
Zone zone = optionService.getQnYunZone();
- String accessKey = optionService.getByPropertyOfNonNull(QnYunProperties.OSS_ACCESS_KEY).toString();
- String secretKey = optionService.getByPropertyOfNonNull(QnYunProperties.OSS_SECRET_KEY).toString();
- String bucket = optionService.getByPropertyOfNonNull(QnYunProperties.OSS_BUCKET).toString();
+ String accessKey = optionService.getByPropertyOfNonNull(QiniuOssProperties.OSS_ACCESS_KEY).toString();
+ String secretKey = optionService.getByPropertyOfNonNull(QiniuOssProperties.OSS_SECRET_KEY).toString();
+ String bucket = optionService.getByPropertyOfNonNull(QiniuOssProperties.OSS_BUCKET).toString();
// Create configuration
Configuration configuration = new Configuration(zone);
@@ -154,6 +176,6 @@ public class QnYunFileHandler implements FileHandler {
@Override
public boolean supportType(AttachmentType type) {
- return AttachmentType.QNYUN.equals(type);
+ return AttachmentType.QINIUOSS.equals(type);
}
}
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 3acc7c0ee..6465fe425 100644
--- a/src/main/java/run/halo/app/handler/file/SmmsFileHandler.java
+++ b/src/main/java/run/halo/app/handler/file/SmmsFileHandler.java
@@ -5,6 +5,7 @@ import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
@@ -22,13 +23,15 @@ import run.halo.app.utils.FilenameUtils;
import run.halo.app.utils.HttpClientUtils;
import java.io.IOException;
+import java.util.Collections;
import java.util.Objects;
/**
* Sm.ms file handler.
*
* @author johnniang
- * @date 3/29/19
+ * @author ryanwang
+ * @date 2019-03-29
*/
@Slf4j
@Component
@@ -46,8 +49,6 @@ public class SmmsFileHandler implements FileHandler {
private final static String SUCCESS_CODE = "success";
- private final static String DEFAULT_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36";
-
private final RestTemplate httpsRestTemplate;
private final OptionService optionService;
@@ -56,6 +57,10 @@ public class SmmsFileHandler implements FileHandler {
OptionService optionService) {
this.httpsRestTemplate = httpsRestTemplate;
this.optionService = optionService;
+
+ MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
+ mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
+ this.httpsRestTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
}
@Override
@@ -76,8 +81,6 @@ public class SmmsFileHandler implements FileHandler {
HttpHeaders headers = new HttpHeaders();
// Set content type
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
- // Set user agent manually
- headers.set(HttpHeaders.USER_AGENT, DEFAULT_USER_AGENT);
headers.set(HttpHeaders.AUTHORIZATION, apiSecretToken);
LinkedMultiValueMap body = new LinkedMultiValueMap<>();
@@ -89,7 +92,6 @@ public class SmmsFileHandler implements FileHandler {
throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到 SM.MS 失败", e);
}
- body.add("ssl", false);
body.add("format", "json");
HttpEntity> httpEntity = new HttpEntity<>(body, headers);
@@ -109,7 +111,11 @@ public class SmmsFileHandler implements FileHandler {
// Check error
if (!isResponseSuccessfully(smmsResponse)) {
log.error("Smms response detail: [{}]", smmsResponse);
- throw new FileOperationException(smmsResponse == null ? "SM.MS 服务返回内容为空" : smmsResponse.getMsg()).setErrorData(smmsResponse);
+ throw new FileOperationException(smmsResponse == null ? "SM.MS 服务返回内容为空" : smmsResponse.getMessage()).setErrorData(smmsResponse);
+ }
+
+ if (smmsResponse.getSuccess()) {
+ throw new FileOperationException("上传请求失败:" + smmsResponse.getMessage()).setErrorData(smmsResponse);
}
// Get response data
@@ -142,7 +148,6 @@ public class SmmsFileHandler implements FileHandler {
// Set user agent manually
HttpHeaders headers = new HttpHeaders();
- headers.set(HttpHeaders.USER_AGENT, DEFAULT_USER_AGENT);
// Delete the file
ResponseEntity responseEntity = httpsRestTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(null, headers), String.class);
@@ -178,12 +183,15 @@ public class SmmsFileHandler implements FileHandler {
@NoArgsConstructor
private static class SmmsResponse {
+ private Boolean success;
+
private String code;
- private String msg;
+ private String message;
private SmmsResponseData data;
+ private String RequestId;
}
@Data
@@ -191,23 +199,24 @@ public class SmmsFileHandler implements FileHandler {
@NoArgsConstructor
private static class SmmsResponseData {
+ private Integer width;
+
+ private Integer height;
+
private String filename;
private String storename;
private Integer size;
- private Integer width;
-
- private Integer height;
+ private String path;
private String hash;
- private String delete;
-
private String url;
- private String path;
+ private String delete;
+ private String page;
}
}
diff --git a/src/main/java/run/halo/app/handler/file/TencentCosFileHandler.java b/src/main/java/run/halo/app/handler/file/TencentCosFileHandler.java
new file mode 100644
index 000000000..5f0692b56
--- /dev/null
+++ b/src/main/java/run/halo/app/handler/file/TencentCosFileHandler.java
@@ -0,0 +1,169 @@
+package run.halo.app.handler.file;
+
+
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.ClientConfig;
+import com.qcloud.cos.auth.BasicCOSCredentials;
+import com.qcloud.cos.auth.COSCredentials;
+import com.qcloud.cos.model.ObjectMetadata;
+import com.qcloud.cos.model.PutObjectResult;
+import com.qcloud.cos.region.Region;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+import org.springframework.web.multipart.MultipartFile;
+import run.halo.app.exception.FileOperationException;
+import run.halo.app.model.enums.AttachmentType;
+import run.halo.app.model.properties.TencentCosProperties;
+import run.halo.app.model.support.UploadResult;
+import run.halo.app.service.OptionService;
+import run.halo.app.utils.FilenameUtils;
+import run.halo.app.utils.ImageUtils;
+
+import java.awt.image.BufferedImage;
+import java.util.Objects;
+
+/**
+ * Tencent cos file handler.
+ *
+ * @author wangya
+ * @author ryanwang
+ * @date 2019-07-25
+ */
+@Slf4j
+@Component
+public class TencentCosFileHandler implements FileHandler {
+
+ private final OptionService optionService;
+
+ public TencentCosFileHandler(OptionService optionService) {
+ this.optionService = optionService;
+ }
+
+ @Override
+ public UploadResult upload(MultipartFile file) {
+ Assert.notNull(file, "Multipart file must not be null");
+
+ // Get config
+ String protocol = optionService.getByPropertyOfNonNull(TencentCosProperties.COS_PROTOCOL).toString();
+ String domain = optionService.getByPropertyOrDefault(TencentCosProperties.COS_DOMAIN, String.class, "");
+ String region = optionService.getByPropertyOfNonNull(TencentCosProperties.COS_REGION).toString();
+ String secretId = optionService.getByPropertyOfNonNull(TencentCosProperties.COS_SECRET_ID).toString();
+ String secretKey = optionService.getByPropertyOfNonNull(TencentCosProperties.COS_SECRET_KEY).toString();
+ String bucketName = optionService.getByPropertyOfNonNull(TencentCosProperties.COS_BUCKET_NAME).toString();
+ String source = optionService.getByPropertyOrDefault(TencentCosProperties.COS_SOURCE, String.class, "");
+ String styleRule = optionService.getByPropertyOrDefault(TencentCosProperties.COS_STYLE_RULE, String.class, "");
+ String thumbnailStyleRule = optionService.getByPropertyOrDefault(TencentCosProperties.COS_THUMBNAIL_STYLE_RULE, String.class, "");
+
+ COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
+ Region regionConfig = new Region(region);
+ ClientConfig clientConfig = new ClientConfig(regionConfig);
+
+ // Init OSS client
+ COSClient cosClient = new COSClient(cred, clientConfig);
+
+ StringBuilder basePath = new StringBuilder(protocol);
+
+ if (StringUtils.isNotEmpty(domain)) {
+ basePath.append(domain)
+ .append("/");
+ } else {
+ basePath.append(bucketName)
+ .append(".cos.")
+ .append(region)
+ .append(".myqcloud.com")
+ .append("/");
+ }
+
+ try {
+ String basename = FilenameUtils.getBasename(file.getOriginalFilename());
+ String extension = FilenameUtils.getExtension(file.getOriginalFilename());
+ String timestamp = String.valueOf(System.currentTimeMillis());
+ StringBuilder upFilePath = new StringBuilder();
+
+ if (StringUtils.isNotEmpty(source)) {
+ upFilePath.append(source)
+ .append("/");
+ }
+
+ upFilePath.append(basename)
+ .append("_")
+ .append(timestamp)
+ .append(".")
+ .append(extension);
+
+ String filePath = StringUtils.join(basePath.toString(), upFilePath.toString());
+
+ // Upload
+ ObjectMetadata objectMetadata = new ObjectMetadata();
+ //提前告知输入流的长度, 否则可能导致 oom
+ objectMetadata.setContentLength(file.getSize());
+ // 设置 Content type, 默认是 application/octet-stream
+ objectMetadata.setContentType(file.getContentType());
+ PutObjectResult putObjectResponseFromInputStream = cosClient.putObject(bucketName, upFilePath.toString(), file.getInputStream(), objectMetadata);
+ if (putObjectResponseFromInputStream == null) {
+ throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到腾讯云失败 ");
+ }
+
+ // Response result
+ UploadResult uploadResult = new UploadResult();
+ uploadResult.setFilename(basename);
+ uploadResult.setFilePath(StringUtils.isBlank(styleRule) ? filePath : filePath + styleRule);
+ uploadResult.setKey(upFilePath.toString());
+ uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
+ uploadResult.setSuffix(extension);
+ uploadResult.setSize(file.getSize());
+
+ // Handle thumbnail
+ if (FileHandler.isImageType(uploadResult.getMediaType())) {
+ BufferedImage image = ImageUtils.getImageFromFile(file.getInputStream(), extension);
+ uploadResult.setWidth(image.getWidth());
+ uploadResult.setHeight(image.getHeight());
+ if (ImageUtils.EXTENSION_ICO.equals(extension)) {
+ uploadResult.setThumbPath(filePath);
+ } else {
+ uploadResult.setThumbPath(StringUtils.isBlank(thumbnailStyleRule) ? filePath : filePath + thumbnailStyleRule);
+ }
+ }
+
+ return uploadResult;
+ } catch (Exception e) {
+ throw new FileOperationException("附件 " + file.getOriginalFilename() + " 上传失败(腾讯云)", e);
+ } finally {
+ cosClient.shutdown();
+ }
+ }
+
+ @Override
+ public void delete(String key) {
+ Assert.notNull(key, "File key must not be blank");
+
+ // Get config
+ String region = optionService.getByPropertyOfNonNull(TencentCosProperties.COS_REGION).toString();
+ String secretId = optionService.getByPropertyOfNonNull(TencentCosProperties.COS_SECRET_ID).toString();
+ String secretKey = optionService.getByPropertyOfNonNull(TencentCosProperties.COS_SECRET_KEY).toString();
+ String bucketName = optionService.getByPropertyOfNonNull(TencentCosProperties.COS_BUCKET_NAME).toString();
+
+ COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
+ Region regionConfig = new Region(region);
+ ClientConfig clientConfig = new ClientConfig(regionConfig);
+
+ // Init OSS client
+ COSClient cosClient = new COSClient(cred, clientConfig);
+
+ try {
+ cosClient.deleteObject(bucketName, key);
+ } catch (Exception e) {
+ throw new FileOperationException("附件 " + key + " 从腾讯云删除失败", e);
+ } finally {
+ cosClient.shutdown();
+ }
+ }
+
+ @Override
+ public boolean supportType(AttachmentType type) {
+ return AttachmentType.TENCENTCOS.equals(type);
+ }
+}
diff --git a/src/main/java/run/halo/app/handler/file/TencentYunFileHandler.java b/src/main/java/run/halo/app/handler/file/TencentYunFileHandler.java
deleted file mode 100644
index 1abdb97c5..000000000
--- a/src/main/java/run/halo/app/handler/file/TencentYunFileHandler.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package run.halo.app.handler.file;
-
-
-import com.qcloud.cos.COSClient;
-import com.qcloud.cos.ClientConfig;
-import com.qcloud.cos.auth.BasicCOSCredentials;
-import com.qcloud.cos.auth.COSCredentials;
-import com.qcloud.cos.model.ObjectMetadata;
-import com.qcloud.cos.model.PutObjectResult;
-import com.qcloud.cos.region.Region;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.http.MediaType;
-import org.springframework.stereotype.Component;
-import org.springframework.util.Assert;
-import org.springframework.web.multipart.MultipartFile;
-import run.halo.app.exception.FileOperationException;
-import run.halo.app.model.enums.AttachmentType;
-import run.halo.app.model.properties.TencentYunProperties;
-import run.halo.app.model.support.UploadResult;
-import run.halo.app.service.OptionService;
-import run.halo.app.utils.FilenameUtils;
-
-import javax.imageio.ImageIO;
-import java.awt.image.BufferedImage;
-import java.util.Objects;
-
-/**
- * TencentYun file handler.
- *
- * @author wangya
- * @author ryanwang
- * @date 2019-07-25
- */
-@Slf4j
-@Component
-public class TencentYunFileHandler implements FileHandler {
-
- private final OptionService optionService;
-
- public TencentYunFileHandler(OptionService optionService) {
- this.optionService = optionService;
- }
-
- @Override
- public UploadResult upload(MultipartFile file) {
- Assert.notNull(file, "Multipart file must not be null");
-
- // Get config
- String cosDomain = optionService.getByPropertyOrDefault(TencentYunProperties.COS_DOMAIN, String.class, "");
- String cosRegion = optionService.getByPropertyOfNonNull(TencentYunProperties.COS_REGION).toString();
- String cosSecretId = optionService.getByPropertyOfNonNull(TencentYunProperties.COS_SECRET_ID).toString();
- String cosSecretKey = optionService.getByPropertyOfNonNull(TencentYunProperties.COS_SECRET_KEY).toString();
- String cosBucketName = optionService.getByPropertyOfNonNull(TencentYunProperties.COS_BUCKET_NAME).toString();
- String cosSource = StringUtils.join("https://", cosBucketName, ".cos." + cosRegion + ".myqcloud.com");
-
- //get file attribute
- long size = file.getSize();
- String contentType = file.getContentType();
-
- COSCredentials cred = new BasicCOSCredentials(cosSecretId, cosSecretKey);
- Region region = new Region(cosRegion);
- ClientConfig clientConfig = new ClientConfig(region);
-
-
- // Init OSS client
- COSClient cosClient = new COSClient(cred, clientConfig);
-
-
- try {
- String basename = FilenameUtils.getBasename(file.getOriginalFilename());
- String extension = FilenameUtils.getExtension(file.getOriginalFilename());
- String timestamp = String.valueOf(System.currentTimeMillis());
- String upFilePath = StringUtils.join(basename, "_", timestamp, ".", extension);
- String filePath = StringUtils.join(StringUtils.appendIfMissing(StringUtils.isNotBlank(cosDomain) ? cosDomain : cosSource, "/"), upFilePath);
-
- // Upload
- ObjectMetadata objectMetadata = new ObjectMetadata();
- //提前告知输入流的长度, 否则可能导致 oom
- objectMetadata.setContentLength(size);
- // 设置 Content type, 默认是 application/octet-stream
- objectMetadata.setContentType(contentType);
- PutObjectResult putObjectResponseFromInputStream = cosClient.putObject(cosBucketName, upFilePath, file.getInputStream(), objectMetadata);
- if (putObjectResponseFromInputStream == null) {
- throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到腾讯云失败 ");
- }
-
- // Response result
- UploadResult uploadResult = new UploadResult();
- uploadResult.setFilename(basename);
- uploadResult.setFilePath(filePath);
- uploadResult.setKey(upFilePath);
- uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
- uploadResult.setSuffix(extension);
- uploadResult.setSize(file.getSize());
-
- // Handle thumbnail
- if (FileHandler.isImageType(uploadResult.getMediaType())) {
- BufferedImage image = ImageIO.read(file.getInputStream());
- uploadResult.setWidth(image.getWidth());
- uploadResult.setHeight(image.getHeight());
- uploadResult.setThumbPath(filePath);
- }
-
- return uploadResult;
- } catch (Exception e) {
- throw new FileOperationException("附件 " + file.getOriginalFilename() + " 上传失败(腾讯云)", e);
- } finally {
- cosClient.shutdown();
- }
- }
-
- @Override
- public void delete(String key) {
- Assert.notNull(key, "File key must not be blank");
-
- // Get config
- String cosRegion = optionService.getByPropertyOfNonNull(TencentYunProperties.COS_REGION).toString();
- String cosSecretId = optionService.getByPropertyOfNonNull(TencentYunProperties.COS_SECRET_ID).toString();
- String cosSecretKey = optionService.getByPropertyOfNonNull(TencentYunProperties.COS_SECRET_KEY).toString();
- String cosBucketName = optionService.getByPropertyOfNonNull(TencentYunProperties.COS_BUCKET_NAME).toString();
-
- COSCredentials cred = new BasicCOSCredentials(cosSecretId, cosSecretKey);
- Region region = new Region(cosRegion);
- ClientConfig clientConfig = new ClientConfig(region);
-
- // Init OSS client
- COSClient cosClient = new COSClient(cred, clientConfig);
-
- try {
- cosClient.deleteObject(cosBucketName, key);
- } catch (Exception e) {
- throw new FileOperationException("附件 " + key + " 从腾讯云删除失败", e);
- } finally {
- cosClient.shutdown();
- }
- }
-
- @Override
- public boolean supportType(AttachmentType type) {
- return AttachmentType.TENCENTYUN.equals(type);
- }
-}
diff --git a/src/main/java/run/halo/app/handler/file/UpYunFileHandler.java b/src/main/java/run/halo/app/handler/file/UpOssFileHandler.java
similarity index 60%
rename from src/main/java/run/halo/app/handler/file/UpYunFileHandler.java
rename to src/main/java/run/halo/app/handler/file/UpOssFileHandler.java
index 0942c84f4..a335c7f3b 100644
--- a/src/main/java/run/halo/app/handler/file/UpYunFileHandler.java
+++ b/src/main/java/run/halo/app/handler/file/UpOssFileHandler.java
@@ -10,29 +10,29 @@ import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;
import run.halo.app.exception.FileOperationException;
import run.halo.app.model.enums.AttachmentType;
-import run.halo.app.model.properties.UpYunProperties;
+import run.halo.app.model.properties.UpOssProperties;
import run.halo.app.model.support.UploadResult;
import run.halo.app.service.OptionService;
import run.halo.app.utils.FilenameUtils;
+import run.halo.app.utils.ImageUtils;
-import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.util.Objects;
/**
- * Up Yun file handler.
+ * Up oss file handler.
*
* @author johnniang
* @author ryanwang
- * @date 3/27/19
+ * @date 2019-03-27
*/
@Slf4j
@Component
-public class UpYunFileHandler implements FileHandler {
+public class UpOssFileHandler implements FileHandler {
private final OptionService optionService;
- public UpYunFileHandler(OptionService optionService) {
+ public UpOssFileHandler(OptionService optionService) {
this.optionService = optionService;
}
@@ -40,17 +40,18 @@ public class UpYunFileHandler implements FileHandler {
public UploadResult upload(MultipartFile file) {
Assert.notNull(file, "Multipart file must not be null");
- String ossSource = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_SOURCE).toString();
- String ossPassword = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_PASSWORD).toString();
- String ossBucket = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_BUCKET).toString();
- String ossDomain = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_DOMAIN).toString();
- String ossOperator = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_OPERATOR).toString();
+ String source = optionService.getByPropertyOfNonNull(UpOssProperties.OSS_SOURCE).toString();
+ String password = optionService.getByPropertyOfNonNull(UpOssProperties.OSS_PASSWORD).toString();
+ String bucket = optionService.getByPropertyOfNonNull(UpOssProperties.OSS_BUCKET).toString();
+ String protocol = optionService.getByPropertyOfNonNull(UpOssProperties.OSS_PROTOCOL).toString();
+ String domain = optionService.getByPropertyOfNonNull(UpOssProperties.OSS_DOMAIN).toString();
+ String operator = optionService.getByPropertyOfNonNull(UpOssProperties.OSS_OPERATOR).toString();
// style rule can be null
- String ossStyleRule = optionService.getByPropertyOrDefault(UpYunProperties.OSS_STYLE_RULE, String.class, "");
- String ossThumbnailStyleRule = optionService.getByPropertyOrDefault(UpYunProperties.OSS_THUMBNAIL_STYLE_RULE, String.class, "");
+ String styleRule = optionService.getByPropertyOrDefault(UpOssProperties.OSS_STYLE_RULE, String.class, "");
+ String thumbnailStyleRule = optionService.getByPropertyOrDefault(UpOssProperties.OSS_THUMBNAIL_STYLE_RULE, String.class, "");
// Create up yun
- UpYun upYun = new UpYun(ossBucket, ossOperator, ossPassword);
+ UpYun upYun = new UpYun(bucket, operator, password);
upYun.setDebug(log.isDebugEnabled());
upYun.setTimeout(60);
upYun.setApiDomain(UpYun.ED_AUTO);
@@ -63,7 +64,7 @@ public class UpYunFileHandler implements FileHandler {
// Get md5 value of the file
String md5OfFile = DigestUtils.md5DigestAsHex(file.getInputStream());
// Build file path
- String upFilePath = StringUtils.appendIfMissing(ossSource, "/") + md5OfFile + '.' + extension;
+ String upFilePath = StringUtils.appendIfMissing(source, "/") + md5OfFile + '.' + extension;
// Set md5Content
upYun.setContentMD5(md5OfFile);
// Write file
@@ -72,12 +73,12 @@ public class UpYunFileHandler implements FileHandler {
throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到又拍云失败" + upFilePath);
}
- String filePath = StringUtils.removeEnd(ossDomain, "/") + upFilePath;
+ String filePath = protocol + StringUtils.removeEnd(domain, "/") + upFilePath;
// Build upload result
UploadResult uploadResult = new UploadResult();
uploadResult.setFilename(basename);
- uploadResult.setFilePath(StringUtils.isBlank(ossStyleRule) ? filePath : filePath + ossStyleRule);
+ uploadResult.setFilePath(StringUtils.isBlank(styleRule) ? filePath : filePath + styleRule);
uploadResult.setKey(upFilePath);
uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
uploadResult.setSuffix(extension);
@@ -85,10 +86,14 @@ public class UpYunFileHandler implements FileHandler {
// Handle thumbnail
if (FileHandler.isImageType(uploadResult.getMediaType())) {
- BufferedImage image = ImageIO.read(file.getInputStream());
+ BufferedImage image = ImageUtils.getImageFromFile(file.getInputStream(), extension);
uploadResult.setWidth(image.getWidth());
uploadResult.setHeight(image.getHeight());
- uploadResult.setThumbPath(StringUtils.isBlank(ossThumbnailStyleRule) ? filePath : filePath + ossThumbnailStyleRule);
+ if (ImageUtils.EXTENSION_ICO.equals(extension)) {
+ uploadResult.setThumbPath(filePath);
+ } else {
+ uploadResult.setThumbPath(StringUtils.isBlank(thumbnailStyleRule) ? filePath : filePath + thumbnailStyleRule);
+ }
}
return uploadResult;
@@ -102,12 +107,12 @@ public class UpYunFileHandler implements FileHandler {
Assert.notNull(key, "File key must not be blank");
// Get config
- String ossPassword = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_PASSWORD).toString();
- String ossBucket = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_BUCKET).toString();
- String ossOperator = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_OPERATOR).toString();
+ String password = optionService.getByPropertyOfNonNull(UpOssProperties.OSS_PASSWORD).toString();
+ String bucket = optionService.getByPropertyOfNonNull(UpOssProperties.OSS_BUCKET).toString();
+ String operator = optionService.getByPropertyOfNonNull(UpOssProperties.OSS_OPERATOR).toString();
// Create up yun
- UpYun upYun = new UpYun(ossBucket, ossOperator, ossPassword);
+ UpYun upYun = new UpYun(bucket, operator, password);
// Set api domain with ED_AUTO
upYun.setApiDomain(UpYun.ED_AUTO);
@@ -124,6 +129,6 @@ public class UpYunFileHandler implements FileHandler {
@Override
public boolean supportType(AttachmentType type) {
- return AttachmentType.UPYUN.equals(type);
+ return AttachmentType.UPOSS.equals(type);
}
}
diff --git a/src/main/java/run/halo/app/handler/migrate/CnBlogsMigrateHandler.java b/src/main/java/run/halo/app/handler/migrate/CnBlogsMigrateHandler.java
new file mode 100644
index 000000000..bdd512d6d
--- /dev/null
+++ b/src/main/java/run/halo/app/handler/migrate/CnBlogsMigrateHandler.java
@@ -0,0 +1,23 @@
+package run.halo.app.handler.migrate;
+
+import org.springframework.web.multipart.MultipartFile;
+import run.halo.app.model.enums.MigrateType;
+
+/**
+ * Cnblogs(https://cnblogs.com) migrate handler.
+ *
+ * @author ryanwang
+ * @date 2019-10-30
+ */
+public class CnBlogsMigrateHandler implements MigrateHandler {
+
+ @Override
+ public void migrate(MultipartFile file) {
+ // TODO
+ }
+
+ @Override
+ public boolean supportType(MigrateType type) {
+ return MigrateType.CNBLOGS.equals(type);
+ }
+}
diff --git a/src/main/java/run/halo/app/handler/migrate/MigrateHandler.java b/src/main/java/run/halo/app/handler/migrate/MigrateHandler.java
new file mode 100644
index 000000000..3d8f59a9a
--- /dev/null
+++ b/src/main/java/run/halo/app/handler/migrate/MigrateHandler.java
@@ -0,0 +1,30 @@
+package run.halo.app.handler.migrate;
+
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
+import org.springframework.web.multipart.MultipartFile;
+import run.halo.app.model.enums.MigrateType;
+
+/**
+ * Migrate handler interface.
+ *
+ * @author ryanwang
+ * @date 2019-10-28
+ */
+public interface MigrateHandler {
+
+ /**
+ * Migrate
+ *
+ * @param file multipart file must not be null
+ */
+ void migrate(@NonNull MultipartFile file);
+
+ /**
+ * Checks if the given type is supported.
+ *
+ * @param type migrate type
+ * @return true if supported; false or else
+ */
+ boolean supportType(@Nullable MigrateType type);
+}
diff --git a/src/main/java/run/halo/app/handler/migrate/MigrateHandlers.java b/src/main/java/run/halo/app/handler/migrate/MigrateHandlers.java
new file mode 100644
index 000000000..e7ac60f11
--- /dev/null
+++ b/src/main/java/run/halo/app/handler/migrate/MigrateHandlers.java
@@ -0,0 +1,65 @@
+package run.halo.app.handler.migrate;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationContext;
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.multipart.MultipartFile;
+import run.halo.app.exception.FileOperationException;
+import run.halo.app.model.enums.MigrateType;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+/**
+ * Migrate handler manager.
+ *
+ * @author ryanwang
+ * @date 2019-10-28
+ */
+@Slf4j
+@Component
+public class MigrateHandlers {
+
+ /**
+ * Migrate handler container.
+ */
+ private final Collection migrateHandlers = new LinkedList<>();
+
+ public MigrateHandlers(ApplicationContext applicationContext) {
+ // Add all migrate handler
+ addFileHandlers(applicationContext.getBeansOfType(MigrateHandler.class).values());
+ }
+
+ @NonNull
+ public void upload(@NonNull MultipartFile file, @NonNull MigrateType migrateType) {
+ Assert.notNull(file, "Multipart file must not be null");
+ Assert.notNull(migrateType, "Migrate type must not be null");
+
+ for (MigrateHandler migrateHandler : migrateHandlers) {
+ if (migrateHandler.supportType(migrateType)) {
+ migrateHandler.migrate(file);
+ return;
+ }
+ }
+
+ throw new FileOperationException("No available migrate handler to migrate the file").setErrorData(migrateType);
+ }
+
+ /**
+ * Adds migrate handlers.
+ *
+ * @param migrateHandlers migrate handler collection
+ * @return current migrate handlers
+ */
+ @NonNull
+ private MigrateHandlers addFileHandlers(@Nullable Collection migrateHandlers) {
+ if (!CollectionUtils.isEmpty(migrateHandlers)) {
+ this.migrateHandlers.addAll(migrateHandlers);
+ }
+ return this;
+ }
+}
diff --git a/src/main/java/run/halo/app/handler/migrate/OldVersionMigrateHandler.java b/src/main/java/run/halo/app/handler/migrate/OldVersionMigrateHandler.java
new file mode 100644
index 000000000..8ebc22c96
--- /dev/null
+++ b/src/main/java/run/halo/app/handler/migrate/OldVersionMigrateHandler.java
@@ -0,0 +1,693 @@
+package run.halo.app.handler.migrate;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.web.multipart.MultipartFile;
+import run.halo.app.exception.ServiceException;
+import run.halo.app.model.entity.*;
+import run.halo.app.model.enums.AttachmentType;
+import run.halo.app.model.enums.CommentStatus;
+import run.halo.app.model.enums.MigrateType;
+import run.halo.app.model.enums.PostStatus;
+import run.halo.app.service.*;
+import run.halo.app.utils.BeanUtils;
+import run.halo.app.utils.JsonUtils;
+import run.halo.app.utils.ServiceUtils;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Old version(0.4.4) migrate handler
+ *
+ * @author ryanwang
+ * @author johnniang
+ * @date 2019-10-28
+ */
+@Slf4j
+@Component
+@SuppressWarnings("unchecked")
+public class OldVersionMigrateHandler implements MigrateHandler {
+
+ private final AttachmentService attachmentService;
+
+ private final PostService postService;
+
+ private final LinkService linkService;
+
+ private final MenuService menuService;
+
+ private final CategoryService categoryService;
+
+ private final TagService tagService;
+
+ private final PostCommentService postCommentService;
+
+ private final SheetCommentService sheetCommentService;
+
+ private final SheetService sheetService;
+
+ private final PhotoService photoService;
+
+ private final PostCategoryService postCategoryService;
+
+ private final PostTagService postTagService;
+
+ public OldVersionMigrateHandler(AttachmentService attachmentService,
+ PostService postService,
+ LinkService linkService,
+ MenuService menuService,
+ CategoryService categoryService,
+ TagService tagService,
+ PostCommentService postCommentService,
+ SheetCommentService sheetCommentService,
+ SheetService sheetService,
+ PhotoService photoService,
+ PostCategoryService postCategoryService,
+ PostTagService postTagService) {
+ this.attachmentService = attachmentService;
+ this.postService = postService;
+ this.linkService = linkService;
+ this.menuService = menuService;
+ this.categoryService = categoryService;
+ this.tagService = tagService;
+ this.postCommentService = postCommentService;
+ this.sheetCommentService = sheetCommentService;
+ this.sheetService = sheetService;
+ this.photoService = photoService;
+ this.postCategoryService = postCategoryService;
+ this.postTagService = postTagService;
+ }
+
+ @Override
+ public void migrate(MultipartFile file) {
+ // Get migration content
+ try {
+ String migrationContent = FileCopyUtils.copyToString(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8));
+
+ Object migrationObject = JsonUtils.jsonToObject(migrationContent, Object.class);
+
+ if (migrationObject instanceof Map) {
+ Map migrationMap = (Map) migrationObject;
+
+ // Handle attachments
+ List attachments = handleAttachments(migrationMap.get("attachments"));
+
+ log.debug("Migrated attachments: [{}]", attachments);
+
+ // Handle links
+ List links = handleLinks(migrationMap.get("links"));
+
+ log.debug("Migrated links: [{}]", links);
+
+ // Handle galleries
+ List photos = handleGalleries(migrationMap.get("galleries"));
+
+ log.debug("Migrated photos: [{}]", photos);
+
+ // Handle menus
+ List