mirror of https://github.com/halo-dev/halo
release: 1.2.0. (#467)
release: 1.2.0. Co-authored-by: Jiahuan Shen <shenjiahuan@sjtu.edu.cn> Co-authored-by: John Niang <johnniang@foxmail.com> Co-authored-by: Ryan Wang <i@ryanc.cc> Co-authored-by: null <weiwensangsang@users.noreply.github.com> Co-authored-by: bobo <yabo_han@163.com> Co-authored-by: Lei XinXin <nglsl666@aliyun.com> Co-authored-by: 寒山 <ms915818993@163.com> Co-authored-by: IJKZEN <ijkzen@outlook.com>pull/645/head v1.2.0
commit
2203889fe7
|
@ -1,19 +0,0 @@
|
|||
<!--
|
||||
如果你不认真勾选下面的内容,我可能会直接关闭你的 Issue。
|
||||
提问之前,建议先阅读 https://github.com/ruby-china/How-To-Ask-Questions-The-Smart-Way
|
||||
-->
|
||||
|
||||
**我确定我已经查看了** (标注`[ ]`为`[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 反馈
|
||||
- [ ] 添加新的特性或者功能
|
||||
- [ ] 请求技术支持
|
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
name: Bug Report
|
||||
about: 事情不像预期的那样工作吗?
|
||||
title: ''
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
你好!感谢你正在考虑为 Halo 提交一个 bug。请花一点点时间尽量详细地回答以下基础问题。
|
||||
|
||||
谢谢!
|
||||
-->
|
||||
|
||||
<!--
|
||||
请确认你已经做了下面这些事情,若 bug 还是未解决,那么请尽可详细地描述你的问题。
|
||||
|
||||
- 我已经安装了最新版的 Halo
|
||||
- 我已经搜索了已有的 Issues 列表中有关的信息
|
||||
- 我已经搜索了论坛中有关的信息:https://bbs.halo.run
|
||||
- 我已经阅读了 Halo 的 FAQ:https://halo.run/guide/faq.html
|
||||
-->
|
||||
|
||||
## 我的环境
|
||||
|
||||
<!--
|
||||
请登录到博客后台,进入菜单 `系统->关于` 点击 `环境信息` 旁边的按钮即可复制环境信息。
|
||||
-->
|
||||
|
||||
---
|
||||
|
||||
## 错误日志
|
||||
|
||||
<!--
|
||||
请登录到博客后台,点击菜单栏中的 `Halo Dashboard` 10 次即可开启 `开发者选项`。进入开发者选项中的 `实时日志`。
|
||||
你可以选择查找具体的错误日志,或者直接下载日志文件复制到下面即可。(注意:日志堆栈信息请用 Markdown 的代码块语法,以方便查看。)
|
||||
-->
|
||||
|
||||
---
|
||||
|
||||
## 期望行为
|
||||
|
||||
<!--
|
||||
你期望会发生什么?
|
||||
-->
|
||||
|
||||
## 当前行为
|
||||
|
||||
<!--
|
||||
描述 bug 细节,确认出现此问题的复现步骤,例如点击了哪里,发生了什么情况?
|
||||
|
||||
你可以粘贴截图或附件。
|
||||
-->
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
name: Feature Request
|
||||
about: 想让我们为 Halo 增加什么功能吗?
|
||||
title: 'feat: '
|
||||
labels: 'Feature Request'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
你好!感谢你愿意考虑希望 Halo 增加某个新功能。请花一点点时间尽量详细地回答以下基础问题。
|
||||
|
||||
谢谢!
|
||||
-->
|
||||
|
||||
## 概述
|
||||
|
||||
<!--
|
||||
对这个新功能的一段描述
|
||||
-->
|
||||
|
||||
## 动机
|
||||
|
||||
<!--
|
||||
为什么你希望在 Halo 中使用这个功能?
|
||||
-->
|
||||
|
||||
## 详细解释
|
||||
|
||||
<!--
|
||||
详细描述这个新功能。
|
||||
|
||||
如果这是一个小功能,你可以忽略这部分。
|
||||
-->
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
name: Question
|
||||
about: 对 Halo 有任何问题吗?
|
||||
title: ''
|
||||
labels: 'question'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
如果你有任何问题也可以通过此渠道来向我们反馈。
|
||||
|
||||
谢谢!
|
||||
-->
|
|
@ -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
|
|
@ -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
|
||||
ENTRYPOINT java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -server -jar halo.jar
|
||||
|
|
17
README.md
17
README.md
|
@ -2,8 +2,6 @@
|
|||
|
||||
> Halo 是一款现代化的个人独立博客系统,给习惯写博客的同学多一个选择。
|
||||
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="#"><img alt="JDK" src="https://img.shields.io/badge/JDK-1.8-yellow.svg?style=flat-square"/></a>
|
||||
<a href="https://github.com/halo-dev/halo/releases"><img alt="GitHub release" src="https://img.shields.io/github/release/halo-dev/halo.svg?style=flat-square"/></a>
|
||||
|
@ -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):<https://github.com/halo-dev/halo-comment>
|
||||
- 管理 APP(halo-app):<https://github.com/halo-dev/halo-app>
|
||||
- 主题仓库:<https://halo.run/theme>
|
||||
- WeHalo 小程序:<https://github.com/aquanlerou/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
|
|||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
|
17
build.gradle
17
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'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Halo run!
|
||||
* </pre>
|
||||
* 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
|
||||
|
|
|
@ -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<String> 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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> 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<ContentFilter> contentFrb = new FilterRegistrationBean<>();
|
||||
contentFrb.addUrlPatterns("/*");
|
||||
|
@ -110,8 +125,9 @@ public class HaloConfiguration {
|
|||
@Bean
|
||||
public FilterRegistrationBean<ApiAuthenticationFilter> 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"
|
||||
|
|
|
@ -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<ApiKey> 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<ApiKey> 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> getSpringLogs() {
|
||||
return BaseResponse.ok(HttpStatus.OK.getReasonPhrase(), adminService.getSpringLogs());
|
||||
@GetMapping("spring/application.yaml")
|
||||
@ApiOperation("Gets application config content")
|
||||
public BaseResponse<String> 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<String> getLogFiles(@RequestParam("lines") Long lines) {
|
||||
return BaseResponse.ok(HttpStatus.OK.getReasonPhrase(), adminService.getLogFiles(lines));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<AttachmentDTO>
|
||||
*/
|
||||
@GetMapping
|
||||
public Page<AttachmentDTO> 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<Attachment> deletePermanentlyInBatch(@RequestBody List<Integer> 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<String> listMediaTypes() {
|
||||
return attachmentService.listAllMediaType();
|
||||
}
|
||||
|
||||
@GetMapping("types")
|
||||
@ApiOperation("Lists all of types.")
|
||||
public List<AttachmentType> listTypes() {
|
||||
return attachmentService.listAllType();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<BackupDTO> listBackups() {
|
||||
return backupService.listHaloBackups();
|
||||
}
|
||||
|
||||
@GetMapping("halo/{fileName:.+}")
|
||||
@ApiOperation("Downloads backup file")
|
||||
public ResponseEntity<Resource> 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<JSONObject> posts = (List<JSONObject>) result.opt("posts");
|
||||
backupService.exportHexoMd(posts, postDir.getPath());
|
||||
final List<JSONObject> passwords = (List<JSONObject>) result.opt("passwords");
|
||||
backupService.exportHexoMd(passwords, passwordDir.getPath());
|
||||
final List<JSONObject> drafts = (List<JSONObject>) 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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<String> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<JournalCommentWithJournalVO> listLatest(@RequestParam(name = "top", defaultValue = "10") int top,
|
||||
@RequestParam(name = "status", required = false) CommentStatus status) {
|
||||
List<JournalComment> latestComments = journalCommentService.pageLatest(top, status).getContent();
|
||||
return journalCommentService.convertToWithJournalVo(latestComments);
|
||||
}
|
||||
|
||||
@GetMapping("{journalId:\\d+}/tree_view")
|
||||
@ApiOperation("Lists comments with tree view")
|
||||
public Page<BaseCommentVO> 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<BaseCommentWithParentVO> 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) {
|
||||
|
|
|
@ -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<BaseCommentVO> 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<BaseCommentWithParentVO> 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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<LinkDTO> listLinks(@SortDefault(sort = "priority", direction = Sort.Direction.ASC) Sort sort) {
|
||||
return linkService.listDtos(sort);
|
||||
@ApiOperation("Lists links")
|
||||
public List<LinkDTO> 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<String> teams() {
|
||||
return linkService.listAllTeams();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<LogDTO> pageLatest(@RequestParam(name = "top", defaultValue = "10") int top) {
|
||||
|
@ -45,16 +39,14 @@ public class LogController {
|
|||
}
|
||||
|
||||
@GetMapping
|
||||
@ApiOperation("Lists logs")
|
||||
public Page<LogDTO> pageBy(@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable) {
|
||||
Page<Log> 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();
|
||||
}
|
||||
|
|
|
@ -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("发送成功");
|
||||
|
|
|
@ -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<MenuDTO> listAll(@SortDefault(sort = "priority", direction = DESC) Sort sort) {
|
||||
return menuService.listDtos(sort);
|
||||
public List<MenuDTO> 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<MenuVO> listAsTree(@SortDefault(sort = "priority", direction = ASC) Sort sort) {
|
||||
return menuService.listAsTree(sort);
|
||||
@ApiOperation("Lists categories as tree")
|
||||
public List<MenuVO> 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<String> teams() {
|
||||
return menuService.listAllTeams();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<OptionDTO> listAll() {
|
||||
return optionService.listDtos();
|
||||
}
|
||||
|
||||
@PostMapping("saving")
|
||||
@ApiOperation("Saves options")
|
||||
public void saveOptions(@Valid @RequestBody List<OptionParam> 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<OptionSimpleDTO> 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<String, Object> optionMap) {
|
||||
|
|
|
@ -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<PhotoDTO> listPhotos(@SortDefault(sort = "updateTime", direction = Sort.Direction.DESC) Sort sort) {
|
||||
return photoService.listDtos(sort);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@ApiOperation("Lists photos")
|
||||
public Page<PhotoDTO> 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));
|
||||
}
|
||||
|
|
|
@ -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<PostCommentWithPostVO> 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<BaseCommentVO> 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<BaseCommentWithParentVO> 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<BaseCommentDTO> updateStatusInBatch(@PathVariable(name = "status") CommentStatus status,
|
||||
@RequestBody List<Long> ids) {
|
||||
List<PostComment> 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<PostComment> deletePermanentlyInBatch(@RequestBody List<Long> 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);
|
||||
|
|
|
@ -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<PostListVO> 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<Post> 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<Post> updateStatusInBatch(@PathVariable(name = "status") PostStatus status,
|
||||
@RequestBody List<Integer> 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<Post> deletePermanentlyInBatch(@RequestBody List<Integer> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<SheetCommentWithSheetVO> pageBy(@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable,
|
||||
CommentQuery commentQuery) {
|
||||
Page<SheetComment> sheetCommentPage = sheetCommentService.pageBy(commentQuery, pageable);
|
||||
|
@ -43,21 +54,38 @@ public class SheetCommentController {
|
|||
}
|
||||
|
||||
@GetMapping("latest")
|
||||
@ApiOperation("Lists latest sheet comments")
|
||||
public List<SheetCommentWithSheetVO> listLatest(@RequestParam(name = "top", defaultValue = "10") int top,
|
||||
@RequestParam(name = "status", required = false) CommentStatus status) {
|
||||
Page<SheetComment> sheetCommentPage = sheetCommentService.pageLatest(top, status);
|
||||
return sheetCommentService.convertToWithSheetVo(sheetCommentPage.getContent());
|
||||
}
|
||||
|
||||
@GetMapping("{sheetId:\\d+}/tree_view")
|
||||
@ApiOperation("Lists sheet comments with tree view")
|
||||
public Page<BaseCommentVO> 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<BaseCommentWithParentVO> 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<BaseCommentDTO> updateStatusInBatch(@PathVariable(name = "status") CommentStatus status,
|
||||
@RequestBody List<Long> ids) {
|
||||
List<SheetComment> 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<SheetComment> deletePermanentlyInBatch(@RequestBody List<Long> 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<StaticFile> 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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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<ThemeProperty> listAll() {
|
||||
return themeService.getThemes();
|
||||
}
|
||||
|
||||
@GetMapping("activation/files")
|
||||
@ApiOperation("Lists all activate theme files")
|
||||
public List<ThemeFile> listFiles() {
|
||||
return themeService.listThemeFolderBy(themeService.getActivatedThemeId());
|
||||
}
|
||||
|
||||
@GetMapping("{themeId}/files")
|
||||
@ApiOperation("Lists theme files by theme id")
|
||||
public List<ThemeFile> listFiles(@PathVariable("themeId") String themeId) {
|
||||
return themeService.listThemeFolderBy(themeId);
|
||||
}
|
||||
|
||||
@GetMapping("files/content")
|
||||
@ApiOperation("Gets template content")
|
||||
public BaseResponse<String> 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<String> 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<String> customTemplate() {
|
||||
return themeService.listCustomTemplates(themeService.getActivatedThemeId());
|
||||
@GetMapping("activation/template/custom/sheet")
|
||||
@ApiOperation("Gets custom sheet templates")
|
||||
public Set<String> customSheetTemplate() {
|
||||
return themeService.listCustomTemplates(themeService.getActivatedThemeId(), ThemeService.CUSTOM_SHEET_PREFIX);
|
||||
}
|
||||
|
||||
@GetMapping("activation/template/custom/post")
|
||||
@ApiOperation("Gets custom post templates")
|
||||
public Set<String> 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<HttpTrace> 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<HttpTrace> listHttpTraces() {
|
||||
// return traceService.listHttpTraces();
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
|
|
@ -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("密码修改成功");
|
||||
|
|
|
@ -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<Category> categories = postCategoryService.listCategoriesBy(post.getId());
|
||||
List<Tag> tags = postTagService.listTagsBy(post.getId());
|
||||
|
||||
Page<BaseCommentVO> comments = postCommentService.pageVosBy(post.getId(), PageRequest.of(cp, optionService.getCommentPageSize(), sort));
|
||||
List<PostMeta> 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());
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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<Post>
|
||||
*/
|
||||
private List<PostListVO> buildPosts(@NonNull Pageable pageable) {
|
||||
private List<PostDetailVO> buildPosts(@NonNull Pageable pageable) {
|
||||
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
|
||||
Page<PostListVO> posts = postService.convertToListVo(postPage);
|
||||
Page<PostDetailVO> posts = postService.convertToDetailVo(postPage);
|
||||
posts.getContent().forEach(postListVO -> {
|
||||
try {
|
||||
// Encode post url
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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<PhotoDTO> 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<BaseCommentVO> 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());
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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<BasePostSimpleDTO> pageBy(@PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) {
|
||||
public Page<PostListVO> pageBy(@PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) {
|
||||
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
|
||||
return postService.convertToSimple(postPage);
|
||||
return postService.convertToListVo(postPage);
|
||||
}
|
||||
|
||||
@PostMapping(value = "search")
|
||||
|
|
|
@ -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<BasePostSimpleDTO> pageBy(@PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) {
|
||||
public Page<SheetListVO> pageBy(@PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) {
|
||||
Page<Sheet> 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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
|
@ -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 {
|
|
@ -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 {
|
|
@ -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 {
|
|
@ -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;
|
|
@ -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 {
|
|
@ -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;
|
||||
}
|
|
@ -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 {
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, Enum> {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.util.LinkedList;
|
|||
* File handler manager.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 3/27/19
|
||||
* @date 2019-03-27
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<String, Object> 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<LinkedMultiValueMap<String, Object>> 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<String> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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<MigrateHandler> 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<MigrateHandler> migrateHandlers) {
|
||||
if (!CollectionUtils.isEmpty(migrateHandlers)) {
|
||||
this.migrateHandlers.addAll(migrateHandlers);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -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<String, Object> migrationMap = (Map<String, Object>) migrationObject;
|
||||
|
||||
// Handle attachments
|
||||
List<Attachment> attachments = handleAttachments(migrationMap.get("attachments"));
|
||||
|
||||
log.debug("Migrated attachments: [{}]", attachments);
|
||||
|
||||
// Handle links
|
||||
List<Link> links = handleLinks(migrationMap.get("links"));
|
||||
|
||||
log.debug("Migrated links: [{}]", links);
|
||||
|
||||
// Handle galleries
|
||||
List<Photo> photos = handleGalleries(migrationMap.get("galleries"));
|
||||
|
||||
log.debug("Migrated photos: [{}]", photos);
|
||||
|
||||
// Handle menus
|
||||
List<Menu> menus = handleMenus(migrationMap.get("menus"));
|
||||
|
||||
log.debug("Migrated menus: [{}]", menus);
|
||||
|
||||
// Handle posts
|
||||
List<BasePost> posts = handleBasePosts(migrationMap.get("posts"));
|
||||
|
||||
log.debug("Migrated posts: [{}]", posts);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("备份文件 " + file.getOriginalFilename() + " 读取失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private List<BasePost> handleBasePosts(@Nullable Object postsObject) {
|
||||
if (!(postsObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> postObjectList = (List<Object>) postsObject;
|
||||
|
||||
List<BasePost> result = new LinkedList<>();
|
||||
|
||||
postObjectList.forEach(postObject -> {
|
||||
if (!(postObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> postMap = (Map<String, Object>) postObject;
|
||||
|
||||
BasePost post = new BasePost();
|
||||
post.setTitle(postMap.getOrDefault("postTitle", "").toString());
|
||||
post.setUrl(postMap.getOrDefault("postUrl", "").toString());
|
||||
post.setOriginalContent(postMap.getOrDefault("postContentMd", "").toString());
|
||||
post.setFormatContent(postMap.getOrDefault("postContent", "").toString());
|
||||
post.setSummary(postMap.getOrDefault("postSummary", "").toString());
|
||||
post.setThumbnail(postMap.getOrDefault("postThumbnail", "").toString());
|
||||
post.setVisits(getLongOrDefault(postMap.getOrDefault("postViews", "").toString(), 0L));
|
||||
post.setDisallowComment(false);
|
||||
post.setTemplate(postMap.getOrDefault("customTpl", "").toString());
|
||||
|
||||
// Set disallow comment
|
||||
Integer allowComment = getIntegerOrDefault(postMap.getOrDefault("allowComment", "1").toString(), 1);
|
||||
if (allowComment != 1) {
|
||||
post.setDisallowComment(true);
|
||||
}
|
||||
|
||||
// Set create time
|
||||
Long createTime = getLongOrDefault(postMap.getOrDefault("postDate", "").toString(), 0L);
|
||||
if (createTime != 0L) {
|
||||
post.setCreateTime(new Date(createTime));
|
||||
}
|
||||
|
||||
// Set update time
|
||||
Long updateTime = getLongOrDefault(postMap.getOrDefault("postUpdate", "").toString(), 0L);
|
||||
if (updateTime != 0L) {
|
||||
post.setUpdateTime(new Date(updateTime));
|
||||
}
|
||||
|
||||
// Set status (default draft)
|
||||
Integer postStatus = getIntegerOrDefault(postMap.getOrDefault("postStatus", "").toString(), 1);
|
||||
if (postStatus == 0) {
|
||||
post.setStatus(PostStatus.PUBLISHED);
|
||||
} else if (postStatus == 1) {
|
||||
post.setStatus(PostStatus.DRAFT);
|
||||
} else {
|
||||
post.setStatus(PostStatus.RECYCLE);
|
||||
}
|
||||
|
||||
String postType = postMap.getOrDefault("postType", "post").toString();
|
||||
|
||||
try {
|
||||
if ("post".equalsIgnoreCase(postType)) {
|
||||
// Handle post
|
||||
result.add(handlePost(post, postMap));
|
||||
} else {
|
||||
// Handle page
|
||||
result.add(handleSheet(post, postMap));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a post or sheet", e);
|
||||
// Ignore this exception
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Post handlePost(@NonNull BasePost basePost, @NonNull Map<String, Object> postMap) {
|
||||
Post post = BeanUtils.transformFrom(basePost, Post.class);
|
||||
|
||||
// Create it
|
||||
Post createdPost = postService.createOrUpdateBy(post);
|
||||
|
||||
Object commentsObject = postMap.get("comments");
|
||||
Object categoriesObject = postMap.get("categories");
|
||||
Object tagsObject = postMap.get("tags");
|
||||
// Handle comments
|
||||
List<BaseComment> baseComments = handleComment(commentsObject, createdPost.getId());
|
||||
|
||||
// Handle categories
|
||||
List<Category> categories = handleCategories(categoriesObject, createdPost.getId());
|
||||
|
||||
log.debug("Migrated categories of post [{}]: [{}]", categories, createdPost.getId());
|
||||
|
||||
// Handle tags
|
||||
List<Tag> tags = handleTags(tagsObject, createdPost.getId());
|
||||
|
||||
log.debug("Migrated tags of post [{}]: [{}]", tags, createdPost.getId());
|
||||
|
||||
List<PostComment> postComments = baseComments.stream()
|
||||
.map(baseComment -> BeanUtils.transformFrom(baseComment, PostComment.class))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
try {
|
||||
// Build virtual comment
|
||||
PostComment virtualPostComment = new PostComment();
|
||||
virtualPostComment.setId(0L);
|
||||
// Create comments
|
||||
createPostCommentRecursively(virtualPostComment, postComments);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to create post comments for post with id " + createdPost.getId(), e);
|
||||
// Ignore this exception
|
||||
}
|
||||
|
||||
return createdPost;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Sheet handleSheet(@NonNull BasePost basePost, @NonNull Map<String, Object> postMap) {
|
||||
Sheet sheet = BeanUtils.transformFrom(basePost, Sheet.class);
|
||||
|
||||
// Create it
|
||||
Sheet createdSheet = sheetService.createOrUpdateBy(sheet);
|
||||
|
||||
Object commentsObject = postMap.get("comments");
|
||||
// Handle comments
|
||||
List<BaseComment> baseComments = handleComment(commentsObject, createdSheet.getId());
|
||||
|
||||
List<SheetComment> sheetComments = baseComments.stream()
|
||||
.map(baseComment -> BeanUtils.transformFrom(baseComment, SheetComment.class))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Create comments
|
||||
try {
|
||||
// Build virtual comment
|
||||
SheetComment virtualSheetComment = new SheetComment();
|
||||
virtualSheetComment.setId(0L);
|
||||
// Create comments
|
||||
createSheetCommentRecursively(virtualSheetComment, sheetComments);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to create sheet comments for sheet with id " + createdSheet.getId(), e);
|
||||
// Ignore this exception
|
||||
}
|
||||
|
||||
return createdSheet;
|
||||
}
|
||||
|
||||
|
||||
private void createPostCommentRecursively(@NonNull final PostComment parentComment, List<PostComment> postComments) {
|
||||
Long oldParentId = parentComment.getId();
|
||||
|
||||
// Create parent
|
||||
if (!ServiceUtils.isEmptyId(parentComment.getId())) {
|
||||
PostComment createdComment = postCommentService.create(parentComment);
|
||||
log.debug("Created post comment: [{}]", createdComment);
|
||||
parentComment.setId(createdComment.getId());
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(postComments)) {
|
||||
return;
|
||||
}
|
||||
// Get all children
|
||||
List<PostComment> children = postComments.stream()
|
||||
.filter(postComment -> Objects.equals(oldParentId, postComment.getParentId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
||||
// Set parent id again
|
||||
children.forEach(postComment -> postComment.setParentId(parentComment.getId()));
|
||||
|
||||
// Remove children
|
||||
postComments.removeAll(children);
|
||||
|
||||
// Create children recursively
|
||||
children.forEach(childComment -> createPostCommentRecursively(childComment, postComments));
|
||||
}
|
||||
|
||||
private void createSheetCommentRecursively(@NonNull final SheetComment parentComment, List<SheetComment> sheetComments) {
|
||||
Long oldParentId = parentComment.getId();
|
||||
// Create parent
|
||||
if (!ServiceUtils.isEmptyId(parentComment.getId())) {
|
||||
SheetComment createComment = sheetCommentService.create(parentComment);
|
||||
parentComment.setId(createComment.getId());
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(sheetComments)) {
|
||||
return;
|
||||
}
|
||||
// Get all children
|
||||
List<SheetComment> children = sheetComments.stream()
|
||||
.filter(sheetComment -> Objects.equals(oldParentId, sheetComment.getParentId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Set parent id again
|
||||
children.forEach(postComment -> postComment.setParentId(parentComment.getId()));
|
||||
|
||||
// Remove children
|
||||
sheetComments.removeAll(children);
|
||||
|
||||
// Create children recursively
|
||||
children.forEach(childComment -> createSheetCommentRecursively(childComment, sheetComments));
|
||||
}
|
||||
|
||||
private List<BaseComment> handleComment(@Nullable Object commentsObject, @NonNull Integer postId) {
|
||||
Assert.notNull(postId, "Post id must not be null");
|
||||
|
||||
if (!(commentsObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> commentObjectList = (List<Object>) commentsObject;
|
||||
|
||||
List<BaseComment> result = new LinkedList<>();
|
||||
|
||||
commentObjectList.forEach(commentObject -> {
|
||||
if (!(commentObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> commentMap = (Map<String, Object>) commentObject;
|
||||
|
||||
BaseComment baseComment = new BaseComment();
|
||||
baseComment.setId(getLongOrDefault(commentMap.getOrDefault("commentId", "").toString(), null));
|
||||
baseComment.setAuthor(commentMap.getOrDefault("commentAuthor", "").toString());
|
||||
baseComment.setEmail(commentMap.getOrDefault("commentAuthorEmail", "").toString());
|
||||
baseComment.setIpAddress(commentMap.getOrDefault("commentAuthorIp", "").toString());
|
||||
baseComment.setAuthorUrl(commentMap.getOrDefault("commentAuthorUrl", "").toString());
|
||||
baseComment.setGravatarMd5(commentMap.getOrDefault("commentAuthorAvatarMd5", "").toString());
|
||||
baseComment.setContent(commentMap.getOrDefault("commentContent", "").toString());
|
||||
baseComment.setUserAgent(commentMap.getOrDefault("commentAgent", "").toString());
|
||||
baseComment.setIsAdmin(getBooleanOrDefault(commentMap.getOrDefault("isAdmin", "").toString(), false));
|
||||
baseComment.setPostId(postId);
|
||||
baseComment.setParentId(getLongOrDefault(commentMap.getOrDefault("commentParent", "").toString(), 0L));
|
||||
|
||||
// Set create date
|
||||
Long createTimestamp = getLongOrDefault(commentMap.getOrDefault("createDate", "").toString(), System.currentTimeMillis());
|
||||
baseComment.setCreateTime(new Date(createTimestamp));
|
||||
|
||||
Integer commentStatus = getIntegerOrDefault(commentMap.getOrDefault("commentStatus", "").toString(), 1);
|
||||
if (commentStatus == 0) {
|
||||
baseComment.setStatus(CommentStatus.PUBLISHED);
|
||||
} else if (commentStatus == 1) {
|
||||
baseComment.setStatus(CommentStatus.AUDITING);
|
||||
} else {
|
||||
baseComment.setStatus(CommentStatus.RECYCLE);
|
||||
}
|
||||
|
||||
result.add(baseComment);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Category> handleCategories(@Nullable Object categoriesObject, @NonNull Integer postId) {
|
||||
Assert.notNull(postId, "Post id must not be null");
|
||||
|
||||
if (!(categoriesObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> categoryObjectList = (List<Object>) categoriesObject;
|
||||
|
||||
List<Category> result = new LinkedList<>();
|
||||
|
||||
categoryObjectList.forEach(categoryObject -> {
|
||||
if (!(categoryObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> categoryMap = (Map<String, Object>) categoryObject;
|
||||
|
||||
String slugName = categoryMap.getOrDefault("cateUrl", "").toString();
|
||||
|
||||
Category category = categoryService.getBySlugName(slugName);
|
||||
|
||||
if (null == category) {
|
||||
category = new Category();
|
||||
category.setName(categoryMap.getOrDefault("cateName", "").toString());
|
||||
category.setSlugName(slugName);
|
||||
category.setDescription(categoryMap.getOrDefault("cateDesc", "").toString());
|
||||
category = categoryService.create(category);
|
||||
}
|
||||
|
||||
PostCategory postCategory = new PostCategory();
|
||||
postCategory.setCategoryId(category.getId());
|
||||
postCategory.setPostId(postId);
|
||||
postCategoryService.create(postCategory);
|
||||
|
||||
try {
|
||||
result.add(category);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a category", e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Tag> handleTags(@Nullable Object tagsObject, @NonNull Integer postId) {
|
||||
Assert.notNull(postId, "Post id must not be null");
|
||||
|
||||
if (!(tagsObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> tagObjectList = (List<Object>) tagsObject;
|
||||
|
||||
List<Tag> result = new LinkedList<>();
|
||||
|
||||
tagObjectList.forEach(tagObject -> {
|
||||
if (!(tagObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> tagMap = (Map<String, Object>) tagObject;
|
||||
|
||||
String slugName = tagMap.getOrDefault("tagUrl", "").toString();
|
||||
|
||||
Tag tag = tagService.getBySlugName(slugName);
|
||||
|
||||
if (null == tag) {
|
||||
tag = new Tag();
|
||||
tag.setName(tagMap.getOrDefault("tagName", "").toString());
|
||||
tag.setSlugName(slugName);
|
||||
tag = tagService.create(tag);
|
||||
}
|
||||
|
||||
PostTag postTag = new PostTag();
|
||||
postTag.setTagId(tag.getId());
|
||||
postTag.setPostId(postId);
|
||||
postTagService.create(postTag);
|
||||
|
||||
try {
|
||||
result.add(tag);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a tag", e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Menu> handleMenus(@Nullable Object menusObject) {
|
||||
if (!(menusObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> menuObjectList = (List<Object>) menusObject;
|
||||
|
||||
List<Menu> result = new LinkedList<>();
|
||||
|
||||
menuObjectList.forEach(menuObject -> {
|
||||
if (!(menuObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> menuMap = (Map<String, Object>) menuObject;
|
||||
|
||||
Menu menu = new Menu();
|
||||
|
||||
menu.setName(menuMap.getOrDefault("menuName", "").toString());
|
||||
menu.setUrl(menuMap.getOrDefault("menuUrl", "").toString());
|
||||
// Set priority
|
||||
String sortString = menuMap.getOrDefault("menuSort", "0").toString();
|
||||
menu.setPriority(getIntegerOrDefault(sortString, 0));
|
||||
menu.setTarget(menuMap.getOrDefault("menuTarget", "_self").toString());
|
||||
menu.setIcon(menuMap.getOrDefault("menuIcon", "").toString());
|
||||
|
||||
try {
|
||||
// Create menu
|
||||
result.add(menuService.create(menu));
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a menu", e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Photo> handleGalleries(@Nullable Object galleriesObject) {
|
||||
if (!(galleriesObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> galleryObjectList = (List<Object>) galleriesObject;
|
||||
|
||||
List<Photo> result = new LinkedList<>();
|
||||
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
galleryObjectList.forEach(galleryObject -> {
|
||||
if (!(galleriesObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> galleryMap = (Map<String, Object>) galleryObject;
|
||||
|
||||
Photo photo = new Photo();
|
||||
photo.setName(galleryMap.getOrDefault("galleryName", "").toString());
|
||||
photo.setDescription(galleryMap.getOrDefault("galleryDesc", "").toString());
|
||||
photo.setLocation(galleryMap.getOrDefault("galleryLocation", "").toString());
|
||||
photo.setThumbnail(galleryMap.getOrDefault("galleryThumbnailUrl", "").toString());
|
||||
photo.setUrl(galleryMap.getOrDefault("galleryUrl", "").toString());
|
||||
|
||||
Object galleryDate = galleryMap.get("galleryDate");
|
||||
|
||||
try {
|
||||
if (galleryDate != null) {
|
||||
photo.setTakeTime(dateFormat.parse(galleryDate.toString()));
|
||||
}
|
||||
|
||||
// Create it
|
||||
result.add(photoService.create(photo));
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to create a photo", e);
|
||||
// Ignore this exception
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Link> handleLinks(@Nullable Object linksObject) {
|
||||
if (!(linksObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> linkObjectList = (List<Object>) linksObject;
|
||||
|
||||
List<Link> result = new LinkedList<>();
|
||||
|
||||
linkObjectList.forEach(linkObject -> {
|
||||
if (!(linkObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> linkMap = (Map<String, Object>) linkObject;
|
||||
|
||||
Link link = new Link();
|
||||
|
||||
link.setName(linkMap.getOrDefault("linkName", "").toString());
|
||||
link.setUrl(linkMap.getOrDefault("linkUrl", "").toString());
|
||||
link.setLogo(linkMap.getOrDefault("linkPic", "").toString());
|
||||
link.setDescription(linkMap.getOrDefault("linkDesc", "").toString());
|
||||
try {
|
||||
result.add(linkService.create(link));
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a link", e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Attachment> handleAttachments(@Nullable Object attachmentsObject) {
|
||||
if (!(attachmentsObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> attachmentObjectList = (List<Object>) attachmentsObject;
|
||||
|
||||
List<Attachment> result = new LinkedList<>();
|
||||
|
||||
attachmentObjectList.forEach(attachmentObject -> {
|
||||
if (!(attachmentObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> attachmentMap = (Map<String, Object>) attachmentObject;
|
||||
// Convert to attachment param
|
||||
Attachment attachment = new Attachment();
|
||||
|
||||
attachment.setName(attachmentMap.getOrDefault("attachName", "").toString());
|
||||
attachment.setPath(StringUtils.removeStart(attachmentMap.getOrDefault("attachPath", "").toString(), "/"));
|
||||
attachment.setThumbPath(attachmentMap.getOrDefault("attachSmallPath", "").toString());
|
||||
attachment.setMediaType(attachmentMap.getOrDefault("attachType", "").toString());
|
||||
attachment.setSuffix(StringUtils.removeStart(attachmentMap.getOrDefault("attachSuffix", "").toString(), "."));
|
||||
attachment.setSize(0L);
|
||||
|
||||
if (StringUtils.startsWith(attachment.getPath(), "/upload")) {
|
||||
// Set this key
|
||||
attachment.setFileKey(attachment.getPath());
|
||||
}
|
||||
|
||||
// Set location
|
||||
String attachLocation = attachmentMap.getOrDefault("attachLocation", "").toString();
|
||||
if (StringUtils.equalsIgnoreCase(attachLocation, "qiniu")) {
|
||||
attachment.setType(AttachmentType.QINIUOSS);
|
||||
} else if (StringUtils.equalsIgnoreCase(attachLocation, "upyun")) {
|
||||
attachment.setType(AttachmentType.UPOSS);
|
||||
} else {
|
||||
attachment.setType(AttachmentType.LOCAL);
|
||||
}
|
||||
|
||||
try {
|
||||
// Save to db
|
||||
Attachment createdAttachment = attachmentService.create(attachment);
|
||||
|
||||
result.add(createdAttachment);
|
||||
|
||||
} catch (Exception e) {
|
||||
// Ignore this exception
|
||||
log.warn("Failed to migrate an attachment " + attachment.getPath(), e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Integer getIntegerOrDefault(@Nullable String numberString, @Nullable Integer defaultNumber) {
|
||||
try {
|
||||
return Integer.valueOf(numberString);
|
||||
} catch (Exception e) {
|
||||
// Ignore this exception
|
||||
return defaultNumber;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Long getLongOrDefault(@Nullable String numberString, @Nullable Long defaultNumber) {
|
||||
try {
|
||||
return Long.valueOf(numberString);
|
||||
} catch (Exception e) {
|
||||
// Ignore this exception
|
||||
return defaultNumber;
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean getBooleanOrDefault(@Nullable String boolString, @Nullable Boolean defaultValue) {
|
||||
if (StringUtils.equalsIgnoreCase(boolString, "0")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StringUtils.equalsIgnoreCase(boolString, "1")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StringUtils.equalsIgnoreCase(boolString, "true")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StringUtils.equalsIgnoreCase(boolString, "false")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportType(MigrateType type) {
|
||||
return MigrateType.OLD_VERSION.equals(type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
package run.halo.app.handler.migrate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dom4j.Element;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.exception.ServiceException;
|
||||
import run.halo.app.model.entity.BasePost;
|
||||
import run.halo.app.model.entity.Category;
|
||||
import run.halo.app.model.entity.Post;
|
||||
import run.halo.app.model.entity.Tag;
|
||||
import run.halo.app.model.enums.MigrateType;
|
||||
import run.halo.app.service.*;
|
||||
import run.halo.app.utils.MarkdownUtils;
|
||||
import run.halo.app.utils.WordPressMigrateUtils;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* WordPress migrate handler
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-10-28
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SuppressWarnings("unchecked")
|
||||
public class WordPressMigrateHandler 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 WordPressMigrateHandler(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) {
|
||||
try {
|
||||
String migrationContent = FileCopyUtils.copyToString(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8));
|
||||
Element rootElement = WordPressMigrateUtils.getRootElement(new FileInputStream(migrationContent));
|
||||
Map<String, Object> resultSetMapping = WordPressMigrateUtils.getResultSetMapping(rootElement);
|
||||
|
||||
// Handle categories
|
||||
List<Category> categories = handleCategories(resultSetMapping.get("wp:category"));
|
||||
|
||||
// Handle tags
|
||||
List<Tag> tags = handleTags(resultSetMapping.get("wp:tag"));
|
||||
|
||||
// Handle posts
|
||||
List<BasePost> posts = handlePosts(resultSetMapping.get("item"));
|
||||
|
||||
log.debug("Migrated posts: [{}]", posts);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("WordPress 导出文件 " + file.getOriginalFilename() + " 读取失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Category> handleCategories(@Nullable Object categoriesObject) {
|
||||
|
||||
if (!(categoriesObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> categoryObjectList = (List<Object>) categoriesObject;
|
||||
|
||||
List<Category> result = new LinkedList<>();
|
||||
|
||||
categoryObjectList.forEach(categoryObject -> {
|
||||
|
||||
if (!(categoryObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> categoryMap = (Map<String, Object>) categoryObject;
|
||||
|
||||
String slugName = categoryMap.getOrDefault("wp:category_nicename", "").toString();
|
||||
|
||||
Category category = categoryService.getBySlugName(slugName);
|
||||
|
||||
if (null == category) {
|
||||
category = new Category();
|
||||
category.setName(categoryMap.getOrDefault("wp:cat_name", "").toString());
|
||||
category.setSlugName(slugName);
|
||||
category = categoryService.create(category);
|
||||
}
|
||||
|
||||
try {
|
||||
result.add(category);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a category", e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Tag> handleTags(@Nullable Object tagsObject) {
|
||||
|
||||
if (!(tagsObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> tagObjectList = (List<Object>) tagsObject;
|
||||
|
||||
List<Tag> result = new LinkedList<>();
|
||||
|
||||
tagObjectList.forEach(tagObject -> {
|
||||
if (!(tagObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> tagMap = (Map<String, Object>) tagObject;
|
||||
|
||||
String slugName = tagMap.getOrDefault("wp:tag_slug", "").toString();
|
||||
|
||||
Tag tag = tagService.getBySlugName(slugName);
|
||||
|
||||
if (null == tag) {
|
||||
tag = new Tag();
|
||||
tag.setName(tagMap.getOrDefault("wp:tag_name", "").toString());
|
||||
tag.setSlugName(slugName);
|
||||
tag = tagService.create(tag);
|
||||
}
|
||||
|
||||
try {
|
||||
result.add(tag);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a tag", e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<BasePost> handlePosts(@Nullable Object postsObject) {
|
||||
if (!(postsObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> postObjectList = (List<Object>) postsObject;
|
||||
|
||||
List<BasePost> result = new LinkedList<>();
|
||||
|
||||
postObjectList.forEach(postObject -> {
|
||||
if (!(postObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> postMap = (Map<String, Object>) postObject;
|
||||
|
||||
BasePost post = new BasePost();
|
||||
post.setTitle(postMap.getOrDefault("title", "").toString());
|
||||
post.setUrl(postMap.getOrDefault("wp:post_name", "").toString());
|
||||
post.setOriginalContent(MarkdownUtils.renderMarkdown(postMap.getOrDefault("content:encoded", "").toString()));
|
||||
post.setFormatContent(postMap.getOrDefault("content:encoded", "").toString());
|
||||
post.setSummary(postMap.getOrDefault("excerpt:encoded", "").toString());
|
||||
|
||||
String url = postMap.getOrDefault("wp:post_name", "").toString();
|
||||
|
||||
Post temp = postService.getByUrl(url);
|
||||
|
||||
if (temp != null) {
|
||||
post.setUrl(post.getUrl() + "_1");
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportType(MigrateType type) {
|
||||
return MigrateType.WORDPRESS.equals(type);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ import java.util.List;
|
|||
* Theme config resolver interface.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 4/10/19
|
||||
* @date 2019-04-10
|
||||
*/
|
||||
public interface ThemeConfigResolver {
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import java.io.IOException;
|
|||
* Theme file resolver.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 4/11/19
|
||||
* @date 2019-04-11
|
||||
*/
|
||||
public interface ThemePropertyResolver {
|
||||
|
||||
|
|
|
@ -22,7 +22,8 @@ import java.util.Map;
|
|||
* Theme configuration resolver.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 4/10/19
|
||||
* @author ryanwang
|
||||
* @date 2019-04-10
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
|
@ -116,6 +117,7 @@ public class YamlThemeConfigResolverImpl implements ThemeConfigResolver {
|
|||
item.setType(InputType.typeOf(itemMap.get("type")));
|
||||
item.setDefaultValue(itemMap.get("default"));
|
||||
item.setPlaceholder(itemMap.getOrDefault("placeholder", "").toString());
|
||||
item.setDescription(itemMap.getOrDefault("description", "").toString());
|
||||
|
||||
// Handle options
|
||||
item.setOptions(handleOptions(itemMap.get("options")));
|
||||
|
@ -142,6 +144,7 @@ public class YamlThemeConfigResolverImpl implements ThemeConfigResolver {
|
|||
item.setType(InputType.typeOf(itemMap.get("type")));
|
||||
item.setDefaultValue(itemMap.get("default"));
|
||||
item.setPlaceholder(itemMap.getOrDefault("placeholder", "").toString());
|
||||
item.setDescription(itemMap.getOrDefault("description", "").toString());
|
||||
|
||||
// Handle options
|
||||
item.setOptions(handleOptions(itemMap.get("options")));
|
||||
|
|
|
@ -2,7 +2,6 @@ package run.halo.app.handler.theme.config.impl;
|
|||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -15,7 +14,7 @@ import java.io.IOException;
|
|||
* Yaml theme file resolver.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 4/11/19
|
||||
* @date 2019-04-11
|
||||
*/
|
||||
@Service
|
||||
public class YamlThemePropertyResolver implements ThemePropertyResolver {
|
||||
|
|
|
@ -11,7 +11,8 @@ import java.util.Objects;
|
|||
* Theme configuration: item entity
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 4/10/19
|
||||
* @author ryanwang
|
||||
* @date 2019-04-10
|
||||
*/
|
||||
@Data
|
||||
public class Item {
|
||||
|
@ -46,6 +47,11 @@ public class Item {
|
|||
*/
|
||||
private String placeholder;
|
||||
|
||||
/**
|
||||
* Text item description.
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* Item's options, default is empty list
|
||||
*/
|
||||
|
|
|
@ -3,12 +3,13 @@ package run.halo.app.handler.theme.config.support;
|
|||
import lombok.Data;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Theme property.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date : 2019-03-22
|
||||
* @date 2019-03-22
|
||||
*/
|
||||
@Data
|
||||
public class ThemeProperty {
|
||||
|
@ -83,6 +84,16 @@ public class ThemeProperty {
|
|||
*/
|
||||
private String screenshots;
|
||||
|
||||
/**
|
||||
* Post preset metas.
|
||||
*/
|
||||
private Set<String> postMetaField;
|
||||
|
||||
/**
|
||||
* Sheet preset metas.
|
||||
*/
|
||||
private Set<String> sheetMetaField;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
@ -101,7 +112,7 @@ public class ThemeProperty {
|
|||
}
|
||||
|
||||
@Data
|
||||
public static class Author {
|
||||
private static class Author {
|
||||
|
||||
/**
|
||||
* Author name.
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
package run.halo.app.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flywaydb.core.Flyway;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import run.halo.app.config.properties.HaloProperties;
|
||||
import run.halo.app.model.properties.PrimaryProperties;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.ThemeService;
|
||||
import run.halo.app.service.UserService;
|
||||
import run.halo.app.utils.FileUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.*;
|
||||
import java.util.Collections;
|
||||
|
@ -23,7 +27,8 @@ import java.util.Collections;
|
|||
* The method executed after the application is started.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date : 2018/12/5
|
||||
* @author guqing
|
||||
* @date 2018-12-05
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
|
@ -39,24 +44,47 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
|
|||
@Autowired
|
||||
private ThemeService themeService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
@Value("${spring.datasource.url}")
|
||||
private String url;
|
||||
|
||||
@Value("${spring.datasource.username}")
|
||||
private String username;
|
||||
|
||||
@Value("${spring.datasource.password}")
|
||||
private String password;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationStartedEvent event) {
|
||||
// save halo version to database
|
||||
this.printStartInfo();
|
||||
this.migrate();
|
||||
this.initThemes();
|
||||
this.printStartInfo();
|
||||
}
|
||||
|
||||
private void printStartInfo() {
|
||||
String blogUrl = optionService.getBlogBaseUrl();
|
||||
|
||||
log.info("Halo started at {}", blogUrl);
|
||||
log.info("Halo admin started at {}/admin", blogUrl);
|
||||
log.info("Halo admin started at {}/{}", blogUrl, haloProperties.getAdminPath());
|
||||
if (!haloProperties.isDocDisabled()) {
|
||||
log.debug("Halo doc was enable at {}/swagger-ui.html", blogUrl);
|
||||
log.debug("Halo api doc was enabled at {}/swagger-ui.html", blogUrl);
|
||||
}
|
||||
log.info("Halo has started successfully!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate database.
|
||||
*/
|
||||
private void migrate() {
|
||||
log.info("Starting migrate database...");
|
||||
Flyway flyway = Flyway
|
||||
.configure()
|
||||
.locations("classpath:/migration")
|
||||
.baselineVersion("1")
|
||||
.baselineOnMigrate(true)
|
||||
.dataSource(url, username, password)
|
||||
.load();
|
||||
flyway.migrate();
|
||||
log.info("Migrate database succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,12 +93,6 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
|
|||
private void initThemes() {
|
||||
// Whether the blog has initialized
|
||||
Boolean isInstalled = optionService.getByPropertyOrDefault(PrimaryProperties.IS_INSTALLED, Boolean.class, false);
|
||||
|
||||
/*if (haloProperties.isProductionEnv() && isInstalled) {
|
||||
// Skip
|
||||
return;
|
||||
}*/
|
||||
|
||||
try {
|
||||
String themeClassPath = ResourceUtils.CLASSPATH_URL_PREFIX + ThemeService.THEME_FOLDER;
|
||||
|
||||
|
@ -81,8 +103,9 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
|
|||
Path source;
|
||||
|
||||
if ("jar".equalsIgnoreCase(themeUri.getScheme())) {
|
||||
|
||||
// Create new file system for jar
|
||||
FileSystem fileSystem = FileSystems.newFileSystem(themeUri, Collections.emptyMap());
|
||||
FileSystem fileSystem = getFileSystem(themeUri);
|
||||
source = fileSystem.getPath("/BOOT-INF/classes/" + ThemeService.THEME_FOLDER);
|
||||
} else {
|
||||
source = Paths.get(themeUri);
|
||||
|
@ -94,13 +117,27 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
|
|||
// Fix the problem that the project cannot start after moving to a new server
|
||||
if (!haloProperties.isProductionEnv() || Files.notExists(themePath) || !isInstalled) {
|
||||
FileUtils.copyFolder(source, themePath);
|
||||
log.info("Copied theme folder from [{}] to [{}]", source, themePath);
|
||||
log.debug("Copied theme folder from [{}] to [{}]", source, themePath);
|
||||
} else {
|
||||
log.info("Skipped copying theme folder due to existence of theme folder");
|
||||
log.debug("Skipped copying theme folder due to existence of theme folder");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Initialize internal theme to user path error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private FileSystem getFileSystem(@NonNull URI uri) throws IOException {
|
||||
Assert.notNull(uri, "Uri must not be null");
|
||||
|
||||
FileSystem fileSystem;
|
||||
|
||||
try {
|
||||
fileSystem = FileSystems.getFileSystem(uri);
|
||||
} catch (FileSystemNotFoundException e) {
|
||||
fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap());
|
||||
}
|
||||
|
||||
return fileSystem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package run.halo.app.event.comment;
|
||||
package run.halo.app.listener.comment;
|
||||
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.text.StrBuilder;
|
||||
|
@ -7,6 +7,8 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.event.comment.CommentNewEvent;
|
||||
import run.halo.app.event.comment.CommentReplyEvent;
|
||||
import run.halo.app.exception.ServiceException;
|
||||
import run.halo.app.model.entity.*;
|
||||
import run.halo.app.model.properties.CommentProperties;
|
||||
|
@ -19,7 +21,8 @@ import java.util.Map;
|
|||
* PostComment event listener.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 19-4-23
|
||||
* @author ryanwang
|
||||
* @date 2019-04-23
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
|
@ -55,6 +58,11 @@ public class CommentEventListener {
|
|||
this.userService = userService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Received a new new comment event.
|
||||
*
|
||||
* @param newEvent new comment event.
|
||||
*/
|
||||
@Async
|
||||
@EventListener
|
||||
public void handleCommentNewEvent(CommentNewEvent newEvent) {
|
||||
|
@ -117,12 +125,11 @@ public class CommentEventListener {
|
|||
mailService.sendTemplateMail(user.getEmail(), "您的博客有新的评论", data, "common/mail_template/mail_notice.ftl");
|
||||
}
|
||||
|
||||
@Async
|
||||
@EventListener
|
||||
public void handleCommentPassEvent(CommentPassEvent passEvent) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Received a new reply comment event.
|
||||
*
|
||||
* @param newEvent reply comment event.
|
||||
*/
|
||||
@Async
|
||||
@EventListener
|
||||
public void handleCommentReplyEvent(CommentReplyEvent replyEvent) {
|
||||
|
@ -151,6 +158,10 @@ public class CommentEventListener {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!baseComment.getAllowNotification()) {
|
||||
return;
|
||||
}
|
||||
|
||||
baseAuthorEmail = baseComment.getEmail();
|
||||
|
||||
Post post = postService.getById(postComment.getPostId());
|
||||
|
@ -175,6 +186,10 @@ public class CommentEventListener {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!baseComment.getAllowNotification()) {
|
||||
return;
|
||||
}
|
||||
|
||||
baseAuthorEmail = baseComment.getEmail();
|
||||
|
||||
Sheet sheet = sheetService.getById(sheetComment.getPostId());
|
||||
|
@ -198,6 +213,10 @@ public class CommentEventListener {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!baseComment.getAllowNotification()) {
|
||||
return;
|
||||
}
|
||||
|
||||
baseAuthorEmail = baseComment.getEmail();
|
||||
|
||||
Journal journal = journalService.getById(journalComment.getPostId());
|
|
@ -1,4 +1,4 @@
|
|||
package run.halo.app.event.freemarker;
|
||||
package run.halo.app.listener.freemarker;
|
||||
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.TemplateModelException;
|
||||
|
@ -12,7 +12,6 @@ import run.halo.app.event.options.OptionUpdatedEvent;
|
|||
import run.halo.app.event.theme.ThemeActivatedEvent;
|
||||
import run.halo.app.event.user.UserUpdatedEvent;
|
||||
import run.halo.app.handler.theme.config.support.ThemeProperty;
|
||||
import run.halo.app.model.properties.OtherProperties;
|
||||
import run.halo.app.model.support.HaloConst;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.ThemeService;
|
||||
|
@ -99,8 +98,7 @@ public class FreemarkerConfigAwareListener {
|
|||
private void loadThemeConfig() throws TemplateModelException {
|
||||
ThemeProperty activatedTheme = themeService.getActivatedTheme();
|
||||
configuration.setSharedVariable("theme", activatedTheme);
|
||||
String baseUrl = optionService.getByPropertyOrDefault(OtherProperties.CDN_DOMAIN, String.class, optionService.getBlogBaseUrl());
|
||||
configuration.setSharedVariable("static", baseUrl + "/" + activatedTheme.getFolderName());
|
||||
configuration.setSharedVariable("static", optionService.getBlogBaseUrl() + "/" + activatedTheme.getFolderName());
|
||||
configuration.setSharedVariable("settings", themeSettingService.listAsMapBy(themeService.getActivatedThemeId()));
|
||||
log.debug("Loaded theme and settings");
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
package run.halo.app.event.logger;
|
||||
package run.halo.app.listener.logger;
|
||||
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.event.logger.LogEvent;
|
||||
import run.halo.app.model.entity.Log;
|
||||
import run.halo.app.service.LogService;
|
||||
|
||||
|
@ -26,6 +27,7 @@ public class LogEventListener {
|
|||
public void onApplicationEvent(LogEvent event) {
|
||||
// Convert to log
|
||||
Log logToCreate = event.getLogParam().convertTo();
|
||||
|
||||
// Create log
|
||||
logService.create(logToCreate);
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
package run.halo.app.event.post;
|
||||
package run.halo.app.listener.post;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.Assert;
|
||||
import run.halo.app.event.post.AbstractVisitEvent;
|
||||
import run.halo.app.service.base.BasePostService;
|
||||
|
||||
import java.util.Map;
|
|
@ -1,8 +1,9 @@
|
|||
package run.halo.app.event.post;
|
||||
package run.halo.app.listener.post;
|
||||
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.event.post.PostVisitEvent;
|
||||
import run.halo.app.service.PostService;
|
||||
|
||||
/**
|
|
@ -1,8 +1,9 @@
|
|||
package run.halo.app.event.post;
|
||||
package run.halo.app.listener.post;
|
||||
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.event.post.SheetVisitEvent;
|
||||
import run.halo.app.service.SheetService;
|
||||
|
||||
/**
|
|
@ -1,9 +1,10 @@
|
|||
package run.halo.app.event.theme;
|
||||
package run.halo.app.listener.theme;
|
||||
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.cache.StringCacheStore;
|
||||
import run.halo.app.event.options.OptionUpdatedEvent;
|
||||
import run.halo.app.event.theme.ThemeUpdatedEvent;
|
||||
import run.halo.app.service.ThemeService;
|
||||
|
||||
/**
|
|
@ -2,8 +2,6 @@ package run.halo.app.model.dto;
|
|||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author ryanwang
|
||||
* @date 2019-05-25
|
||||
|
@ -11,13 +9,14 @@ import java.util.Date;
|
|||
@Data
|
||||
public class BackupDTO {
|
||||
|
||||
private String fileName;
|
||||
@Deprecated
|
||||
private String downloadUrl;
|
||||
|
||||
private Date createTime;
|
||||
private String downloadLink;
|
||||
|
||||
private String fileSize;
|
||||
private String filename;
|
||||
|
||||
private String fileType;
|
||||
private Long updateTime;
|
||||
|
||||
private String type;
|
||||
private Long fileSize;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import java.util.Date;
|
|||
* Base comment output dto.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 2019-03-20
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
|
@ -41,6 +43,8 @@ public class BaseCommentDTO implements OutputConverter<BaseCommentDTO, BaseComme
|
|||
|
||||
private Boolean isAdmin;
|
||||
|
||||
private Boolean allowNotification;
|
||||
|
||||
private Date createTime;
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package run.halo.app.model.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import run.halo.app.model.dto.base.OutputConverter;
|
||||
import run.halo.app.model.entity.BaseMeta;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Base meta Dto.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-10
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
public class BaseMetaDTO implements OutputConverter<BaseMetaDTO, BaseMeta> {
|
||||
private Long id;
|
||||
|
||||
private Integer postId;
|
||||
|
||||
private String key;
|
||||
|
||||
private String value;
|
||||
|
||||
private Date createTime;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue