mirror of https://github.com/halo-dev/halo
release 1.1.0 (#296)
release 1.1.0 Co-authored-by: Darkcloth <darkcloth@126.com> Co-authored-by: null <jinqilin721@163.com> Co-authored-by: John Niang <johnniang@foxmail.com> Co-authored-by: WangYa <757914144@qq.com> Co-authored-by: ikaisec <ikaisec@gmail.com>pull/344/head^2 v1.1.0
commit
b788dbe67e
|
@ -2,14 +2,15 @@ FROM openjdk:8-jre-alpine
|
|||
|
||||
VOLUME /tmp
|
||||
|
||||
ARG JAR_FILE=build/libs/halo.jar
|
||||
ARG JAR_FILE=build/libs/halo-1.1.0-beta.2.jar
|
||||
ARG PORT=8090
|
||||
ARG TIME_ZONE=Asia/Shanghai
|
||||
|
||||
ENV TZ=${TIME_ZONE}
|
||||
ENV JAVA_OPTS="-Xms256m -Xmx256m"
|
||||
|
||||
COPY ${JAR_FILE} halo.jar
|
||||
|
||||
EXPOSE ${PORT}
|
||||
|
||||
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","halo.jar"]
|
||||
ENTRYPOINT java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -server -jar halo.jar
|
44
README.md
44
README.md
|
@ -1,12 +1,14 @@
|
|||
<h1 align="center"><a href="https://github.com/halo-dev" target="_blank">Halo</a></h1>
|
||||
|
||||
> Halo 是一款现代化的个人独立博客系统,给习惯写博客的同学一个更好的选择。
|
||||
> 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>
|
||||
<a href="https://github.com/halo-dev/halo/releases"><img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/halo-dev/halo/total.svg?style=flat-square"></a>
|
||||
<a href="https://github.com/halo-dev/halo/commits"><img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/w/halo-dev/halo.svg?style=flat-square"></a>
|
||||
<a href="https://hub.docker.com/r/ruibaby/halo"><img alt="Docker pulls" src="https://img.shields.io/docker/pulls/ruibaby/halo?style=flat-square"></a>
|
||||
<a href="https://github.com/halo-dev/halo/commits"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/halo-dev/halo.svg?style=flat-square"></a>
|
||||
<a href="https://travis-ci.org/halo-dev/halo"><img alt="Travis CI" src="https://img.shields.io/travis/halo-dev/halo.svg?style=flat-square"/></a>
|
||||
</p>
|
||||
|
@ -19,26 +21,26 @@
|
|||
|
||||
轻快,简洁,功能强大,使用 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://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)。
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 下载最新的 Halo 安装包
|
||||
|
||||
```bash
|
||||
curl -L https://github.com/halo-dev/halo/releases/download/v1.0.3/halo-1.0.3.jar --output halo-latest.jar
|
||||
curl -L https://github.com/halo-dev/halo/releases/download/v1.1.0/halo-1.1.0.jar --output halo-latest.jar
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```bash
|
||||
wget https://github.com/halo-dev/halo/releases/download/v1.0.3/halo-1.0.3.jar -O halo-latest.jar
|
||||
wget https://github.com/halo-dev/halo/releases/download/v1.1.0/halo-1.1.0.jar -O halo-latest.jar
|
||||
```
|
||||
|
||||
### 启动 Halo
|
||||
|
||||
```bash
|
||||
nohup java -jar halo-latest.jar >/dev/null 2>&1&
|
||||
java -jar halo-latest.jar
|
||||
```
|
||||
|
||||
详细文档请移步:<https://halo.run/guide>
|
||||
|
@ -70,32 +72,32 @@ nohup java -jar halo-latest.jar >/dev/null 2>&1&
|
|||
|
||||
## 预览图
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
79
build.gradle
79
build.gradle
|
@ -1,5 +1,5 @@
|
|||
plugins {
|
||||
id 'org.springframework.boot' version '2.1.3.RELEASE'
|
||||
id 'org.springframework.boot' version '2.1.7.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.0.3'
|
||||
version = '1.1.0'
|
||||
sourceCompatibility = '1.8'
|
||||
description = 'Halo, personal blog system developed in Java.'
|
||||
|
||||
|
@ -24,6 +24,7 @@ repositories {
|
|||
configurations {
|
||||
implementation {
|
||||
exclude module: 'spring-boot-starter-tomcat'
|
||||
exclude module: 'slf4j-log4j12'
|
||||
}
|
||||
|
||||
developmentOnly
|
||||
|
@ -40,6 +41,23 @@ bootJar {
|
|||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
ohMyEmailVersion = '0.0.4'
|
||||
hutoolVersion = '4.6.3'
|
||||
upyunSdkVersion = '4.0.1'
|
||||
qiniuSdkVersion = '7.2.18'
|
||||
aliyunSdkVersion = '3.4.2'
|
||||
baiduSdkVersion = '0.10.36'
|
||||
qcloudSdkVersion = '5.5.7'
|
||||
swaggerVersion = '2.9.2'
|
||||
commonsLangVersion = '3.8.1'
|
||||
httpclientVersion = '4.5.7'
|
||||
dataformatYamlVersion = '2.9.2'
|
||||
jgitVersion = '5.3.0.201903130848-r'
|
||||
flexmarkVersion = '0.42.12'
|
||||
thumbnailatorVersion = '0.4.8'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
|
@ -47,34 +65,37 @@ dependencies {
|
|||
implementation 'org.springframework.boot:spring-boot-starter-undertow'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-freemarker'
|
||||
|
||||
implementation 'io.github.biezhi:oh-my-email:0.0.4'
|
||||
implementation 'cn.hutool:hutool-core:4.5.0'
|
||||
implementation 'cn.hutool:hutool-crypto:4.5.0'
|
||||
implementation 'cn.hutool:hutool-extra:4.5.0'
|
||||
implementation 'com.upyun:java-sdk:4.0.1'
|
||||
implementation 'com.qiniu:qiniu-java-sdk:7.2.18'
|
||||
implementation 'com.aliyun.oss:aliyun-sdk-oss:3.4.2'
|
||||
implementation 'net.coobird:thumbnailator:0.4.8'
|
||||
implementation 'io.springfox:springfox-swagger2:2.9.2'
|
||||
implementation 'io.springfox:springfox-swagger-ui:2.9.2'
|
||||
implementation 'org.apache.commons:commons-lang3:3.8.1'
|
||||
implementation 'org.apache.httpcomponents:httpclient:4.5.7'
|
||||
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.9.2'
|
||||
implementation 'org.eclipse.jgit:org.eclipse.jgit:5.3.0.201903130848-r'
|
||||
implementation "io.github.biezhi:oh-my-email:$ohMyEmailVersion"
|
||||
implementation "cn.hutool:hutool-core:$hutoolVersion"
|
||||
implementation "cn.hutool:hutool-crypto:$hutoolVersion"
|
||||
implementation "cn.hutool:hutool-extra:$hutoolVersion"
|
||||
implementation "com.upyun:java-sdk:$upyunSdkVersion"
|
||||
implementation "com.qiniu:qiniu-java-sdk:$qiniuSdkVersion"
|
||||
implementation "com.aliyun.oss:aliyun-sdk-oss:$aliyunSdkVersion"
|
||||
implementation "com.baidubce:bce-java-sdk:$baiduSdkVersion"
|
||||
implementation "com.qcloud:cos_api:$qcloudSdkVersion"
|
||||
implementation "io.springfox:springfox-swagger2:$swaggerVersion"
|
||||
implementation "io.springfox:springfox-swagger-ui:$swaggerVersion"
|
||||
implementation "org.apache.commons:commons-lang3:$commonsLangVersion"
|
||||
implementation "org.apache.httpcomponents:httpclient:$httpclientVersion"
|
||||
implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$dataformatYamlVersion"
|
||||
implementation "org.eclipse.jgit:org.eclipse.jgit:$jgitVersion"
|
||||
|
||||
implementation 'com.vladsch.flexmark:flexmark:0.42.12'
|
||||
implementation 'com.vladsch.flexmark:flexmark-ext-attributes:0.42.12'
|
||||
implementation 'com.vladsch.flexmark:flexmark-ext-autolink:0.42.12'
|
||||
implementation 'com.vladsch.flexmark:flexmark-ext-emoji:0.42.12'
|
||||
implementation 'com.vladsch.flexmark:flexmark-ext-escaped-character:0.42.12'
|
||||
implementation 'com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:0.42.12'
|
||||
implementation 'com.vladsch.flexmark:flexmark-ext-gfm-tasklist:0.42.12'
|
||||
implementation 'com.vladsch.flexmark:flexmark-ext-ins:0.42.12'
|
||||
implementation 'com.vladsch.flexmark:flexmark-ext-media-tags:0.42.12'
|
||||
implementation 'com.vladsch.flexmark:flexmark-ext-tables:0.42.12'
|
||||
implementation 'com.vladsch.flexmark:flexmark-ext-toc:0.42.12'
|
||||
implementation 'com.vladsch.flexmark:flexmark-ext-yaml-front-matter:0.42.12'
|
||||
implementation 'com.vladsch.flexmark:flexmark-html-parser:0.42.12'
|
||||
implementation "com.vladsch.flexmark:flexmark:$flexmarkVersion"
|
||||
implementation "com.vladsch.flexmark:flexmark-ext-attributes:$flexmarkVersion"
|
||||
implementation "com.vladsch.flexmark:flexmark-ext-autolink:$flexmarkVersion"
|
||||
implementation "com.vladsch.flexmark:flexmark-ext-emoji:$flexmarkVersion"
|
||||
implementation "com.vladsch.flexmark:flexmark-ext-escaped-character:$flexmarkVersion"
|
||||
implementation "com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:$flexmarkVersion"
|
||||
implementation "com.vladsch.flexmark:flexmark-ext-gfm-tasklist:$flexmarkVersion"
|
||||
implementation "com.vladsch.flexmark:flexmark-ext-ins:$flexmarkVersion"
|
||||
implementation "com.vladsch.flexmark:flexmark-ext-media-tags:$flexmarkVersion"
|
||||
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-html-parser:$flexmarkVersion"
|
||||
|
||||
implementation "net.coobird:thumbnailator:$thumbnailatorVersion"
|
||||
|
||||
runtimeOnly 'com.h2database:h2'
|
||||
runtimeOnly 'mysql:mysql-connector-java'
|
||||
|
|
Binary file not shown.
|
@ -1,6 +1,6 @@
|
|||
#Tue Apr 23 17:12:17 CST 2019
|
||||
#Mon Sep 09 12:27:59 CST 2019
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
|
||||
|
|
|
@ -28,7 +28,7 @@ APP_NAME="Gradle"
|
|||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
|
|
@ -14,7 +14,7 @@ set APP_BASE_NAME=%~n0
|
|||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
|
|
@ -27,7 +27,7 @@ public class InMemoryCacheStore extends StringCacheStore {
|
|||
/**
|
||||
* Cache container.
|
||||
*/
|
||||
private final static ConcurrentHashMap<String, CacheWrapper<String>> cacheContainer = new ConcurrentHashMap<>();
|
||||
private final static ConcurrentHashMap<String, CacheWrapper<String>> CACHE_CONTAINER = new ConcurrentHashMap<>();
|
||||
|
||||
private final Timer timer;
|
||||
|
||||
|
@ -46,7 +46,7 @@ public class InMemoryCacheStore extends StringCacheStore {
|
|||
Optional<CacheWrapper<String>> getInternal(String key) {
|
||||
Assert.hasText(key, "Cache key must not be blank");
|
||||
|
||||
return Optional.ofNullable(cacheContainer.get(key));
|
||||
return Optional.ofNullable(CACHE_CONTAINER.get(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -55,7 +55,7 @@ public class InMemoryCacheStore extends StringCacheStore {
|
|||
Assert.notNull(cacheWrapper, "Cache wrapper must not be null");
|
||||
|
||||
// Put the cache wrapper
|
||||
CacheWrapper<String> putCacheWrapper = cacheContainer.put(key, cacheWrapper);
|
||||
CacheWrapper<String> putCacheWrapper = CACHE_CONTAINER.put(key, cacheWrapper);
|
||||
|
||||
log.debug("Put [{}] cache result: [{}], original cache wrapper: [{}]", key, putCacheWrapper, cacheWrapper);
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ public class InMemoryCacheStore extends StringCacheStore {
|
|||
public void delete(String key) {
|
||||
Assert.hasText(key, "Cache key must not be blank");
|
||||
|
||||
cacheContainer.remove(key);
|
||||
CACHE_CONTAINER.remove(key);
|
||||
log.debug("Removed key: [{}]", key);
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ public class InMemoryCacheStore extends StringCacheStore {
|
|||
|
||||
@Override
|
||||
public void run() {
|
||||
cacheContainer.keySet().forEach(key -> {
|
||||
CACHE_CONTAINER.keySet().forEach(key -> {
|
||||
if (!InMemoryCacheStore.this.get(key).isPresent()) {
|
||||
log.debug("Deleted the cache: [{}] for expiration", key);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package run.halo.app.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
|
@ -36,6 +37,7 @@ import java.security.NoSuchAlgorithmException;
|
|||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(HaloProperties.class)
|
||||
@Slf4j
|
||||
public class HaloConfiguration {
|
||||
|
||||
private final static int TIMEOUT = 5000;
|
||||
|
@ -80,7 +82,6 @@ public class HaloConfiguration {
|
|||
*
|
||||
* @return Log filter registration bean
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean<LogFilter> logFilter() {
|
||||
FilterRegistrationBean<LogFilter> logFilter = new FilterRegistrationBean<>();
|
||||
|
||||
|
@ -149,7 +150,10 @@ public class HaloConfiguration {
|
|||
"/api/admin/login",
|
||||
"/api/admin/refresh/*",
|
||||
"/api/admin/installations",
|
||||
"/api/admin/recoveries/migrations/*"
|
||||
"/api/admin/recoveries/migrations/*",
|
||||
"/api/admin/is_installed",
|
||||
"/api/admin/password/code",
|
||||
"/api/admin/password/reset"
|
||||
);
|
||||
adminAuthenticationFilter.setFailureHandler(
|
||||
failureHandler);
|
||||
|
|
|
@ -22,7 +22,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
|
||||
import run.halo.app.config.properties.HaloProperties;
|
||||
import run.halo.app.controller.support.PageJacksonSerializer;
|
||||
import run.halo.app.core.PageJacksonSerializer;
|
||||
import run.halo.app.factory.StringToEnumConverterFactory;
|
||||
import run.halo.app.model.support.HaloConst;
|
||||
import run.halo.app.security.resolver.AuthenticationArgumentResolver;
|
||||
|
|
|
@ -8,9 +8,12 @@ import run.halo.app.cache.lock.CacheLock;
|
|||
import run.halo.app.model.dto.EnvironmentDTO;
|
||||
import run.halo.app.model.dto.StatisticDTO;
|
||||
import run.halo.app.model.params.LoginParam;
|
||||
import run.halo.app.model.params.ResetPasswordParam;
|
||||
import run.halo.app.model.properties.PrimaryProperties;
|
||||
import run.halo.app.model.support.BaseResponse;
|
||||
import run.halo.app.security.token.AuthToken;
|
||||
import run.halo.app.service.AdminService;
|
||||
import run.halo.app.service.OptionService;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
|
@ -28,8 +31,50 @@ public class AdminController {
|
|||
|
||||
private final AdminService adminService;
|
||||
|
||||
public AdminController(AdminService adminService) {
|
||||
private final OptionService optionService;
|
||||
|
||||
public AdminController(AdminService adminService, OptionService optionService) {
|
||||
this.adminService = adminService;
|
||||
this.optionService = optionService;
|
||||
}
|
||||
|
||||
@GetMapping(value = "/is_installed")
|
||||
@ApiOperation("Check install status")
|
||||
public boolean isInstall() {
|
||||
return optionService.getByPropertyOrDefault(PrimaryProperties.IS_INSTALLED, Boolean.class, false);
|
||||
}
|
||||
|
||||
@PostMapping("login")
|
||||
@ApiOperation("Login")
|
||||
@CacheLock(autoDelete = false)
|
||||
public AuthToken auth(@RequestBody @Valid LoginParam loginParam) {
|
||||
return adminService.authenticate(loginParam);
|
||||
}
|
||||
|
||||
@PostMapping("logout")
|
||||
@ApiOperation("Logs out (Clear session)")
|
||||
@CacheLock(autoDelete = false)
|
||||
public void logout() {
|
||||
adminService.clearToken();
|
||||
}
|
||||
|
||||
@PostMapping("password/code")
|
||||
@ApiOperation("Send reset password verify code.")
|
||||
public void sendResetCode(@RequestBody @Valid ResetPasswordParam param) {
|
||||
adminService.sendResetPasswordCode(param);
|
||||
}
|
||||
|
||||
@PutMapping("password/reset")
|
||||
@ApiOperation("Reset password by verify code.")
|
||||
public void resetPassword(@RequestBody @Valid ResetPasswordParam param) {
|
||||
adminService.resetPasswordByCode(param);
|
||||
}
|
||||
|
||||
@PostMapping("refresh/{refreshToken}")
|
||||
@ApiOperation("Refreshes token")
|
||||
@CacheLock(autoDelete = false)
|
||||
public AuthToken refresh(@PathVariable("refreshToken") String refreshToken) {
|
||||
return adminService.refreshToken(refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,27 +94,6 @@ public class AdminController {
|
|||
return adminService.getEnvironments();
|
||||
}
|
||||
|
||||
@PostMapping("login")
|
||||
@ApiOperation("Login")
|
||||
@CacheLock(autoDelete = false)
|
||||
public AuthToken auth(@RequestBody @Valid LoginParam loginParam) {
|
||||
return adminService.authenticate(loginParam);
|
||||
}
|
||||
|
||||
@PostMapping("logout")
|
||||
@ApiOperation("Logs out (Clear session)")
|
||||
@CacheLock(autoDelete = false)
|
||||
public void logout() {
|
||||
adminService.clearToken();
|
||||
}
|
||||
|
||||
@PostMapping("refresh/{refreshToken}")
|
||||
@ApiOperation("Refreshes token")
|
||||
@CacheLock(autoDelete = false)
|
||||
public AuthToken refresh(@PathVariable("refreshToken") String refreshToken) {
|
||||
return adminService.refreshToken(refreshToken);
|
||||
}
|
||||
|
||||
@PutMapping("halo-admin")
|
||||
@ApiOperation("Updates halo-admin manually")
|
||||
public void updateAdmin() {
|
||||
|
|
|
@ -23,7 +23,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
|
|||
* Attachment controller.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 3/21/19
|
||||
* @date 2019-03-21
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/attachments")
|
||||
|
|
|
@ -29,14 +29,9 @@ public class BackupController {
|
|||
this.backupService = backupService;
|
||||
}
|
||||
|
||||
@PostMapping("import/markdowns")
|
||||
@ApiOperation("Import markdowns")
|
||||
public List<BasePostDetailDTO> backupMarkdowns(@RequestPart("files") MultipartFile[] files) throws IOException {
|
||||
List<BasePostDetailDTO> result = new LinkedList<>();
|
||||
for (MultipartFile file : files) {
|
||||
BasePostDetailDTO post = backupService.importMarkdowns(file);
|
||||
result.add(post);
|
||||
}
|
||||
return result;
|
||||
@PostMapping("import/markdown")
|
||||
@ApiOperation("Import markdown")
|
||||
public BasePostDetailDTO backupMarkdowns(@RequestPart("file") MultipartFile file) throws IOException {
|
||||
return backupService.importMarkdown(file);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package run.halo.app.controller.admin.api;
|
||||
|
||||
import cn.hutool.core.text.StrBuilder;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
@ -144,9 +146,9 @@ public class InstallController {
|
|||
|
||||
PostCommentParam commentParam = new PostCommentParam();
|
||||
commentParam.setAuthor("Halo Bot");
|
||||
commentParam.setAuthorUrl("https://github.com/halo-dev/halo");
|
||||
commentParam.setAuthorUrl("https://halo.run");
|
||||
commentParam.setContent("欢迎使用 Halo,这是你的第一条评论。");
|
||||
commentParam.setEmail("i@ryanc.cc");
|
||||
commentParam.setEmail("halo@halo.run");
|
||||
commentParam.setPostId(post.getId());
|
||||
return postCommentService.create(commentParam.convertTo());
|
||||
}
|
||||
|
@ -196,11 +198,15 @@ public class InstallController {
|
|||
installParam.update(user);
|
||||
// Set password manually
|
||||
userService.setPassword(user, installParam.getPassword());
|
||||
// Set default avatar
|
||||
userService.setDefaultAvatar(user);
|
||||
// Update user
|
||||
return userService.update(user);
|
||||
}).orElseGet(() -> userService.createBy(installParam));
|
||||
}).orElseGet(() -> {
|
||||
StrBuilder gravatar = new StrBuilder("//cn.gravatar.com/avatar/");
|
||||
gravatar.append(SecureUtil.md5(installParam.getEmail()));
|
||||
gravatar.append("?s=256&d=mm");
|
||||
installParam.setAvatar(gravatar.toString());
|
||||
return userService.createBy(installParam);
|
||||
});
|
||||
}
|
||||
|
||||
private void initSettings(InstallParam installParam) {
|
||||
|
|
|
@ -51,7 +51,7 @@ public class JournalController {
|
|||
|
||||
@GetMapping
|
||||
@ApiOperation("Lists journals")
|
||||
public Page<JournalWithCmtCountDTO> pageBy(@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable,
|
||||
public Page<JournalWithCmtCountDTO> pageBy(@PageableDefault(sort = "createTime", direction = DESC) Pageable pageable,
|
||||
JournalQuery journalQuery) {
|
||||
Page<Journal> journalPage = journalService.pageBy(journalQuery, pageable);
|
||||
return journalService.convertToCmtCountDto(journalPage);
|
||||
|
|
|
@ -40,7 +40,7 @@ public class MenuController {
|
|||
|
||||
@GetMapping("tree_view")
|
||||
@ApiOperation("List as category tree")
|
||||
public List<MenuVO> listAsTree(@SortDefault(sort = "name", direction = ASC) Sort sort) {
|
||||
public List<MenuVO> listAsTree(@SortDefault(sort = "priority", direction = ASC) Sort sort) {
|
||||
return menuService.listAsTree(sort);
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,13 @@ public class MenuController {
|
|||
@DeleteMapping("{menuId:\\d+}")
|
||||
@ApiOperation("Deletes a menu")
|
||||
public MenuDTO deleteBy(@PathVariable("menuId") Integer menuId) {
|
||||
List<Menu> menus = menuService.listByParentId(menuId);
|
||||
if (null != menus && menus.size() > 0) {
|
||||
menus.forEach(menu -> {
|
||||
menu.setParentId(0);
|
||||
menuService.update(menu);
|
||||
});
|
||||
}
|
||||
return new MenuDTO().convertFrom(menuService.removeById(menuId));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ public class OptionController {
|
|||
|
||||
@PostMapping("map_view/saving")
|
||||
@ApiOperation("Saves options by option map")
|
||||
public void saveOptionsWithMapView(@RequestBody Map<String, String> optionMap) {
|
||||
public void saveOptionsWithMapView(@RequestBody Map<String, Object> optionMap) {
|
||||
optionService.save(optionMap);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
|
|||
* Photo controller
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date : 2019/3/21
|
||||
* @date : 2019-3-21
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/photos")
|
||||
|
@ -92,4 +92,10 @@ public class PhotoController {
|
|||
// Update menu in database
|
||||
return new PhotoDTO().convertFrom(photoService.update(photo));
|
||||
}
|
||||
|
||||
@GetMapping("teams")
|
||||
@ApiOperation("Lists all of photo teams")
|
||||
public List<String> listTeams() {
|
||||
return photoService.listAllTeams();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import run.halo.app.model.params.PostCommentParam;
|
|||
import run.halo.app.model.vo.PostCommentWithPostVO;
|
||||
import run.halo.app.service.PostCommentService;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
@ -21,6 +22,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
|
|||
* Post comment controller.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 3/19/19
|
||||
*/
|
||||
@RestController
|
||||
|
@ -74,4 +76,21 @@ public class PostCommentController {
|
|||
PostComment deletedPostComment = postCommentService.removeById(commentId);
|
||||
return postCommentService.convertTo(deletedPostComment);
|
||||
}
|
||||
|
||||
@GetMapping("{commentId:\\d+}")
|
||||
@ApiOperation("Gets a post comment by comment id")
|
||||
public PostCommentWithPostVO getBy(@PathVariable("commentId") Long commentId) {
|
||||
PostComment comment = postCommentService.getById(commentId);
|
||||
return postCommentService.convertToWithPostVo(comment);
|
||||
}
|
||||
|
||||
@PutMapping("{commentId:\\d+}")
|
||||
public BaseCommentDTO updateBy(@Valid @RequestBody PostCommentParam commentParam,
|
||||
@PathVariable("commentId") Long commentId) {
|
||||
PostComment commentToUpdate = postCommentService.getById(commentId);
|
||||
|
||||
commentParam.update(commentToUpdate);
|
||||
|
||||
return postCommentService.convertTo(postCommentService.update(commentToUpdate));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
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.BasePostMinimalDTO;
|
||||
import run.halo.app.model.dto.post.BasePostSimpleDTO;
|
||||
import run.halo.app.model.entity.Post;
|
||||
|
@ -13,10 +18,12 @@ 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.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
||||
|
@ -24,6 +31,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
|
|||
* Post controller.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 3/19/19
|
||||
*/
|
||||
@RestController
|
||||
|
@ -32,14 +40,27 @@ public class PostController {
|
|||
|
||||
private final PostService postService;
|
||||
|
||||
public PostController(PostService postService) {
|
||||
private final StringCacheStore cacheStore;
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
public PostController(PostService postService,
|
||||
StringCacheStore cacheStore,
|
||||
OptionService optionService) {
|
||||
this.postService = postService;
|
||||
this.cacheStore = cacheStore;
|
||||
this.optionService = optionService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@ApiOperation("Lists posts")
|
||||
public Page<PostListVO> pageBy(@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable,
|
||||
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);
|
||||
Page<Post> postPage = postService.pageBy(postQuery, pageable);
|
||||
return postService.convertToListVo(postPage);
|
||||
}
|
||||
|
@ -54,7 +75,7 @@ public class PostController {
|
|||
@ApiOperation("Gets a page of post by post status")
|
||||
public Page<? extends BasePostSimpleDTO> pageByStatus(@PathVariable(name = "status") PostStatus status,
|
||||
@RequestParam(value = "more", required = false, defaultValue = "false") Boolean more,
|
||||
@PageableDefault(sort = "editTime", direction = DESC) Pageable pageable) {
|
||||
@PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) {
|
||||
Page<Post> posts = postService.pageBy(status, pageable);
|
||||
|
||||
if (more) {
|
||||
|
@ -116,4 +137,16 @@ public class PostController {
|
|||
postService.removeById(postId);
|
||||
}
|
||||
|
||||
@GetMapping("preview/{postId:\\d+}")
|
||||
public String preview(@PathVariable("postId") Integer postId) {
|
||||
Post post = postService.getById(postId);
|
||||
|
||||
String token = IdUtil.simpleUUID();
|
||||
|
||||
// cache preview token
|
||||
cacheStore.putAny("preview-post-token-" + postId, 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import run.halo.app.model.params.SheetCommentParam;
|
|||
import run.halo.app.model.vo.SheetCommentWithSheetVO;
|
||||
import run.halo.app.service.SheetCommentService;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
@ -21,6 +22,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
|
|||
* Sheet comment controller.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 19-4-25
|
||||
*/
|
||||
@RestController
|
||||
|
@ -37,14 +39,14 @@ public class SheetCommentController {
|
|||
public Page<SheetCommentWithSheetVO> pageBy(@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable,
|
||||
CommentQuery commentQuery) {
|
||||
Page<SheetComment> sheetCommentPage = sheetCommentService.pageBy(commentQuery, pageable);
|
||||
return sheetCommentService.convertToWithPostVo(sheetCommentPage);
|
||||
return sheetCommentService.convertToWithSheetVo(sheetCommentPage);
|
||||
}
|
||||
|
||||
@GetMapping("latest")
|
||||
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.convertToWithPostVo(sheetCommentPage.getContent());
|
||||
return sheetCommentService.convertToWithSheetVo(sheetCommentPage.getContent());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
|
@ -69,4 +71,21 @@ public class SheetCommentController {
|
|||
SheetComment deletedSheetComment = sheetCommentService.removeById(commentId);
|
||||
return sheetCommentService.convertTo(deletedSheetComment);
|
||||
}
|
||||
|
||||
@GetMapping("{commentId:\\d+}")
|
||||
@ApiOperation("Gets a post comment by comment id")
|
||||
public SheetCommentWithSheetVO getBy(@PathVariable("commentId") Long commentId) {
|
||||
SheetComment comment = sheetCommentService.getById(commentId);
|
||||
return sheetCommentService.convertToWithSheetVo(comment);
|
||||
}
|
||||
|
||||
@PutMapping("{commentId:\\d+}")
|
||||
public BaseCommentDTO updateBy(@Valid @RequestBody SheetCommentParam commentParam,
|
||||
@PathVariable("commentId") Long commentId) {
|
||||
SheetComment commentToUpdate = sheetCommentService.getById(commentId);
|
||||
|
||||
commentParam.update(commentToUpdate);
|
||||
|
||||
return sheetCommentService.convertTo(sheetCommentService.update(commentToUpdate));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
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.Pageable;
|
||||
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.SheetListVO;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.SheetService;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
||||
|
@ -31,8 +35,16 @@ public class SheetController {
|
|||
|
||||
private final SheetService sheetService;
|
||||
|
||||
public SheetController(SheetService sheetService) {
|
||||
private final StringCacheStore cacheStore;
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
public SheetController(SheetService sheetService,
|
||||
StringCacheStore cacheStore,
|
||||
OptionService optionService) {
|
||||
this.sheetService = sheetService;
|
||||
this.cacheStore = cacheStore;
|
||||
this.optionService = optionService;
|
||||
}
|
||||
|
||||
@GetMapping("{sheetId:\\d+}")
|
||||
|
@ -44,7 +56,7 @@ public class SheetController {
|
|||
|
||||
@GetMapping
|
||||
@ApiOperation("Gets a page of sheet")
|
||||
public Page<SheetListVO> pageBy(@PageableDefault(sort = "editTime", direction = DESC) Pageable pageable) {
|
||||
public Page<SheetListVO> pageBy(@PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) {
|
||||
Page<Sheet> sheetPage = sheetService.pageBy(pageable);
|
||||
return sheetService.convertToListVo(sheetPage);
|
||||
}
|
||||
|
@ -97,4 +109,17 @@ public class SheetController {
|
|||
Sheet sheet = sheetService.removeById(sheetId);
|
||||
return sheetService.convertToDetail(sheet);
|
||||
}
|
||||
|
||||
@GetMapping("preview/{sheetId:\\d+}")
|
||||
public String preview(@PathVariable("sheetId") Integer sheetId) {
|
||||
Sheet sheet = sheetService.getById(sheetId);
|
||||
|
||||
String token = IdUtil.simpleUUID();
|
||||
|
||||
// cache preview token
|
||||
cacheStore.putAny("preview-sheet-token-" + sheetId, 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.*;
|
|||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.handler.theme.config.support.Group;
|
||||
import run.halo.app.handler.theme.config.support.ThemeProperty;
|
||||
import run.halo.app.model.params.ThemeContentParam;
|
||||
import run.halo.app.model.support.BaseResponse;
|
||||
import run.halo.app.model.support.ThemeFile;
|
||||
import run.halo.app.service.ThemeService;
|
||||
|
@ -47,20 +48,36 @@ public class ThemeController {
|
|||
return themeService.getThemes();
|
||||
}
|
||||
|
||||
@GetMapping("files")
|
||||
@GetMapping("activation/files")
|
||||
public List<ThemeFile> listFiles() {
|
||||
return themeService.listThemeFolderBy(themeService.getActivatedThemeId());
|
||||
}
|
||||
|
||||
@GetMapping("{themeId}/files")
|
||||
public List<ThemeFile> listFiles(@PathVariable("themeId") String themeId) {
|
||||
return themeService.listThemeFolderBy(themeId);
|
||||
}
|
||||
|
||||
@GetMapping("files/content")
|
||||
public BaseResponse<String> getContentBy(@RequestParam(name = "path") String path) {
|
||||
return BaseResponse.ok(HttpStatus.OK.getReasonPhrase(), themeService.getTemplateContent(path));
|
||||
}
|
||||
|
||||
@GetMapping("{themeId}/files/content")
|
||||
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")
|
||||
public void updateContentBy(@RequestParam(name = "path") String path,
|
||||
@RequestBody String content) {
|
||||
themeService.saveTemplateContent(path, content);
|
||||
public void updateContentBy(@RequestBody ThemeContentParam param) {
|
||||
themeService.saveTemplateContent(param.getPath(), param.getContent());
|
||||
}
|
||||
|
||||
@PutMapping("{themeId}/files/content")
|
||||
public void updateContentBy(@PathVariable("themeId") String themeId,
|
||||
@RequestBody ThemeContentParam param) {
|
||||
themeService.saveTemplateContent(themeId, param.getPath(), param.getContent());
|
||||
}
|
||||
|
||||
@GetMapping("files/custom")
|
||||
|
@ -117,13 +134,6 @@ public class ThemeController {
|
|||
themeSettingService.save(settings, themeId);
|
||||
}
|
||||
|
||||
@PutMapping("{themeId}")
|
||||
public ThemeProperty updateTheme(@PathVariable("themeId") String themeId,
|
||||
@RequestPart(name = "file", required = false) MultipartFile file) {
|
||||
|
||||
return themeService.update(themeId);
|
||||
}
|
||||
|
||||
@DeleteMapping("{themeId}")
|
||||
@ApiOperation("Deletes a theme")
|
||||
public void deleteBy(@PathVariable("themeId") String themeId) {
|
||||
|
@ -136,12 +146,25 @@ public class ThemeController {
|
|||
return themeService.upload(file);
|
||||
}
|
||||
|
||||
@PutMapping("upload/{themeId}")
|
||||
public ThemeProperty updateThemeByUpload(@PathVariable("themeId") String themeId,
|
||||
@RequestPart("file") MultipartFile file) {
|
||||
return themeService.update(themeId, file);
|
||||
}
|
||||
|
||||
@PostMapping("fetching")
|
||||
@ApiOperation("Fetches a new theme")
|
||||
public ThemeProperty fetchTheme(@RequestParam("uri") String uri) {
|
||||
return themeService.fetch(uri);
|
||||
}
|
||||
|
||||
@PutMapping("fetching/{themeId}")
|
||||
public ThemeProperty updateThemeByFetching(@PathVariable("themeId") String themeId,
|
||||
@RequestPart(name = "file", required = false) MultipartFile file) {
|
||||
|
||||
return themeService.update(themeId);
|
||||
}
|
||||
|
||||
@PostMapping("reload")
|
||||
@ApiOperation("Reloads themes")
|
||||
public void reload() {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
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.springframework.data.domain.Page;
|
||||
|
@ -9,10 +10,10 @@ 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;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import run.halo.app.cache.StringCacheStore;
|
||||
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.Tag;
|
||||
|
@ -20,8 +21,10 @@ import run.halo.app.model.enums.PostStatus;
|
|||
import run.halo.app.model.vo.BaseCommentVO;
|
||||
import run.halo.app.model.vo.PostListVO;
|
||||
import run.halo.app.service.*;
|
||||
import run.halo.app.utils.MarkdownUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
||||
|
@ -38,28 +41,32 @@ public class ContentArchiveController {
|
|||
|
||||
private final PostService postService;
|
||||
|
||||
private final PostCommentService postCommentService;
|
||||
|
||||
private final ThemeService themeService;
|
||||
|
||||
private final PostCategoryService postCategoryService;
|
||||
|
||||
private final PostTagService postTagService;
|
||||
|
||||
private final PostCommentService postCommentService;
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
private final StringCacheStore cacheStore;
|
||||
|
||||
public ContentArchiveController(PostService postService,
|
||||
PostCommentService postCommentService,
|
||||
ThemeService themeService,
|
||||
PostCategoryService postCategoryService,
|
||||
PostTagService postTagService,
|
||||
OptionService optionService) {
|
||||
PostCommentService postCommentService,
|
||||
OptionService optionService,
|
||||
StringCacheStore cacheStore) {
|
||||
this.postService = postService;
|
||||
this.postCommentService = postCommentService;
|
||||
this.themeService = themeService;
|
||||
this.postCategoryService = postCategoryService;
|
||||
this.postTagService = postTagService;
|
||||
this.postCommentService = postCommentService;
|
||||
this.optionService = optionService;
|
||||
this.cacheStore = cacheStore;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,34 +107,102 @@ public class ContentArchiveController {
|
|||
* Render post page.
|
||||
*
|
||||
* @param url post slug url.
|
||||
* @param cp comment page number
|
||||
* @param preview preview
|
||||
* @param token preview 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 = postService.getBy(PostStatus.PUBLISHED, url);
|
||||
Post post;
|
||||
if (preview) {
|
||||
post = postService.getBy(PostStatus.DRAFT, url);
|
||||
} else if (intimate) {
|
||||
post = postService.getBy(PostStatus.INTIMATE, url);
|
||||
} else {
|
||||
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) {
|
||||
// verify token
|
||||
String cachedToken = cacheStore.getAny(token, String.class).orElseThrow(() -> new ForbiddenException("您没有该文章的访问权限"));
|
||||
if (!cachedToken.equals(token)) {
|
||||
throw new ForbiddenException("您没有该文章的访问权限");
|
||||
}
|
||||
}
|
||||
|
||||
postService.getNextPost(post.getCreateTime()).ifPresent(nextPost -> model.addAttribute("nextPost", nextPost));
|
||||
postService.getPrePost(post.getCreateTime()).ifPresent(prePost -> model.addAttribute("prePost", prePost));
|
||||
|
||||
|
||||
List<Category> categories = postCategoryService.listCategoryBy(post.getId());
|
||||
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));
|
||||
final int[] pageRainbow = PageUtil.rainbow(cp, comments.getTotalPages(), 3);
|
||||
|
||||
model.addAttribute("is_post", true);
|
||||
model.addAttribute("post", post);
|
||||
model.addAttribute("post", postService.convertToDetailVo(post));
|
||||
model.addAttribute("categories", categories);
|
||||
model.addAttribute("tags", tags);
|
||||
model.addAttribute("comments", comments);
|
||||
model.addAttribute("pageRainbow", pageRainbow);
|
||||
|
||||
if (preview) {
|
||||
// refresh timeUnit
|
||||
cacheStore.putAny("preview-post-token-" + post.getId(), token, 10, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
return themeService.render("post");
|
||||
}
|
||||
|
||||
@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
|
||||
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);
|
||||
return "redirect:" + redirect;
|
||||
} else {
|
||||
String redirect = String.format("%s/archives/%s/password", optionService.getBlogBaseUrl(), post.getUrl());
|
||||
return "redirect:" + redirect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package run.halo.app.controller.content;
|
|||
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
@ -22,6 +23,9 @@ import run.halo.app.service.OptionService;
|
|||
import run.halo.app.service.PostService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
@ -30,6 +34,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
|
|||
* @author ryanwang
|
||||
* @date : 2019-03-21
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class ContentFeedController {
|
||||
|
||||
|
@ -144,6 +149,14 @@ public class ContentFeedController {
|
|||
private List<PostListVO> buildPosts(@NonNull Pageable pageable) {
|
||||
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
|
||||
Page<PostListVO> posts = postService.convertToListVo(postPage);
|
||||
posts.getContent().forEach(postListVO -> {
|
||||
try {
|
||||
// Encode post url
|
||||
postListVO.setUrl(URLEncoder.encode(postListVO.getUrl(), StandardCharsets.UTF_8.name()));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.warn("Failed to encode url: " + postListVO.getUrl(), e);
|
||||
}
|
||||
});
|
||||
return posts.getContent();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ 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;
|
||||
|
@ -14,6 +13,7 @@ import org.springframework.web.bind.annotation.PathVariable;
|
|||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import run.halo.app.model.entity.Post;
|
||||
import run.halo.app.model.enums.PostStatus;
|
||||
import run.halo.app.model.properties.PostProperties;
|
||||
import run.halo.app.model.vo.PostListVO;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.PostService;
|
||||
|
@ -55,7 +55,7 @@ public class ContentIndexController {
|
|||
*/
|
||||
@GetMapping
|
||||
public String index(Model model) {
|
||||
return this.index(model, 1, Sort.by(DESC, "topPriority").and(Sort.by(DESC, "createTime")));
|
||||
return this.index(model, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,14 +67,10 @@ public class ContentIndexController {
|
|||
*/
|
||||
@GetMapping(value = "page/{page}")
|
||||
public String index(Model model,
|
||||
@PathVariable(value = "page") Integer page,
|
||||
@SortDefault.SortDefaults({
|
||||
@SortDefault(sort = "topPriority", direction = DESC),
|
||||
@SortDefault(sort = "createTime", direction = DESC)
|
||||
}) Sort sort) {
|
||||
log.debug("Requested index page, sort info: [{}]", sort);
|
||||
@PathVariable(value = "page") Integer page) {
|
||||
String indexSort = optionService.getByPropertyOfNonNull(PostProperties.INDEX_SORT).toString();
|
||||
int pageSize = optionService.getPostPageSize();
|
||||
Pageable pageable = PageRequest.of(page >= 1 ? page - 1 : page, pageSize, sort);
|
||||
Pageable pageable = PageRequest.of(page >= 1 ? page - 1 : page, pageSize, Sort.by(DESC, "topPriority").and(Sort.by(DESC, indexSort)));
|
||||
|
||||
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
|
||||
Page<PostListVO> posts = postService.convertToListVo(postPage);
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||
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;
|
||||
|
@ -78,7 +79,7 @@ public class ContentJournalController {
|
|||
|
||||
Pageable pageable = PageRequest.of(page >= 1 ? page - 1 : page, pageSize, sort);
|
||||
|
||||
Page<Journal> journals = journalService.listAll(pageable);
|
||||
Page<Journal> journals = journalService.pageBy(JournalType.PUBLIC, pageable);
|
||||
|
||||
int[] rainbow = PageUtil.rainbow(page, journals.getTotalPages(), 3);
|
||||
|
||||
|
|
|
@ -1,14 +1,29 @@
|
|||
package run.halo.app.controller.content;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
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;
|
||||
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.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.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.
|
||||
|
@ -24,10 +39,22 @@ public class ContentSheetController {
|
|||
|
||||
private final ThemeService themeService;
|
||||
|
||||
private final SheetCommentService sheetCommentService;
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
private final StringCacheStore cacheStore;
|
||||
|
||||
public ContentSheetController(SheetService sheetService,
|
||||
ThemeService themeService) {
|
||||
ThemeService themeService,
|
||||
SheetCommentService sheetCommentService,
|
||||
OptionService optionService,
|
||||
StringCacheStore cacheStore) {
|
||||
this.sheetService = sheetService;
|
||||
this.themeService = themeService;
|
||||
this.sheetCommentService = sheetCommentService;
|
||||
this.optionService = optionService;
|
||||
this.cacheStore = cacheStore;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,18 +81,45 @@ public class ContentSheetController {
|
|||
* Render custom sheet
|
||||
*
|
||||
* @param url sheet url
|
||||
* @param preview preview
|
||||
* @param token 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(PostStatus.PUBLISHED, url);
|
||||
Sheet sheet = sheetService.getBy(preview ? PostStatus.DRAFT : PostStatus.PUBLISHED, url);
|
||||
|
||||
if (preview) {
|
||||
// render markdown to html when preview post
|
||||
sheet.setFormatContent(MarkdownUtils.renderHtml(sheet.getOriginalContent()));
|
||||
|
||||
// verify token
|
||||
String cachedToken = cacheStore.getAny("preview-sheet-token-" + sheet.getId(), String.class).orElseThrow(() -> new ForbiddenException("该页面的预览链接不存在或已过期"));
|
||||
|
||||
if (!cachedToken.equals(token)) {
|
||||
throw new ForbiddenException("该页面的预览链接不存在或已过期");
|
||||
}
|
||||
}
|
||||
|
||||
Page<BaseCommentVO> comments = sheetCommentService.pageVosBy(sheet.getId(), PageRequest.of(cp, optionService.getCommentPageSize(), sort));
|
||||
|
||||
|
||||
// sheet and post all can use
|
||||
model.addAttribute("sheet", sheetService.convertToDetail(sheet));
|
||||
model.addAttribute("post", sheetService.convertToDetail(sheet));
|
||||
model.addAttribute("is_sheet", true);
|
||||
model.addAttribute("comments", comments);
|
||||
|
||||
if (preview) {
|
||||
// refresh timeUnit
|
||||
cacheStore.putAny("preview-sheet-token-" + sheet.getId(), token, 10, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
if (themeService.templateExists(ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate() + HaloConst.SUFFIX_FTL)) {
|
||||
return themeService.render(ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate());
|
||||
|
|
|
@ -50,7 +50,7 @@ public class MainController {
|
|||
|
||||
@GetMapping("/avatar")
|
||||
public void avatar(HttpServletResponse response) throws IOException {
|
||||
User user = userService.getCurrentUser().orElseThrow(() -> new ServiceException("找不到博主信息"));
|
||||
User user = userService.getCurrentUser().orElseThrow(() -> new ServiceException("未查询到博主信息"));
|
||||
if (StringUtils.isNotEmpty(user.getAvatar())) {
|
||||
response.sendRedirect(user.getAvatar());
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ 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.service.OptionService;
|
||||
import run.halo.app.service.PostCommentService;
|
||||
import run.halo.app.service.PostService;
|
||||
|
@ -69,22 +70,22 @@ public class PostController {
|
|||
|
||||
@GetMapping("{postId:\\d+}")
|
||||
@ApiOperation("Gets a post")
|
||||
public BasePostDetailDTO getBy(@PathVariable("postId") Integer postId,
|
||||
public PostDetailVO getBy(@PathVariable("postId") Integer postId,
|
||||
@RequestParam(value = "formatDisabled", required = false, defaultValue = "true") Boolean formatDisabled,
|
||||
@RequestParam(value = "sourceDisabled", required = false, defaultValue = "false") Boolean sourceDisabled) {
|
||||
BasePostDetailDTO detailDTO = postService.convertToDetail(postService.getById(postId));
|
||||
PostDetailVO postDetailVO = postService.convertToDetailVo(postService.getById(postId));
|
||||
|
||||
if (formatDisabled) {
|
||||
// Clear the format content
|
||||
detailDTO.setFormatContent(null);
|
||||
postDetailVO.setFormatContent(null);
|
||||
}
|
||||
|
||||
if (sourceDisabled) {
|
||||
// Clear the original content
|
||||
detailDTO.setOriginalContent(null);
|
||||
postDetailVO.setOriginalContent(null);
|
||||
}
|
||||
|
||||
return detailDTO;
|
||||
return postDetailVO;
|
||||
}
|
||||
|
||||
@GetMapping("{postId:\\d+}/comments/top_view")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package run.halo.app.controller.base;
|
||||
package run.halo.app.core;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpStatus;
|
|
@ -1,4 +1,4 @@
|
|||
package run.halo.app.controller.base;
|
||||
package run.halo.app.core;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
|
@ -38,7 +38,7 @@ public class ControllerExceptionHandler {
|
|||
if (e.getCause() instanceof org.hibernate.exception.ConstraintViolationException) {
|
||||
baseResponse = handleBaseException(e.getCause());
|
||||
}
|
||||
baseResponse.setMessage("Failed to validate request parameter");
|
||||
baseResponse.setMessage("字段验证错误,请完善后重试!");
|
||||
return baseResponse;
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ public class ControllerExceptionHandler {
|
|||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public BaseResponse handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
|
||||
BaseResponse<?> baseResponse = handleBaseException(e);
|
||||
baseResponse.setMessage(String.format("Missing request parameter, required %s type %s parameter", e.getParameterType(), e.getParameterName()));
|
||||
baseResponse.setMessage(String.format("请求字段缺失, 类型为 %s,名称为 %s", e.getParameterType(), e.getParameterName()));
|
||||
return baseResponse;
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ public class ControllerExceptionHandler {
|
|||
public BaseResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
|
||||
BaseResponse<?> baseResponse = handleBaseException(e);
|
||||
baseResponse.setStatus(HttpStatus.BAD_REQUEST.value());
|
||||
baseResponse.setMessage("Required request body is missing");
|
||||
baseResponse.setMessage("缺失请求主体");
|
||||
return baseResponse;
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package run.halo.app.core;
|
||||
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.utils.JsonUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Objects;
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ControllerLogAop {
|
||||
|
||||
@Pointcut("execution(* *..*.*.controller..*.*(..))")
|
||||
public void controller() {
|
||||
}
|
||||
|
||||
@Around("controller()")
|
||||
public Object controller(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
String className = joinPoint.getTarget().getClass().getSimpleName();
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
Object[] args = joinPoint.getArgs();
|
||||
|
||||
// Get request attribute
|
||||
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
HttpServletRequest request = Objects.requireNonNull(requestAttributes).getRequest();
|
||||
|
||||
printRequestLog(request, className, methodName, args);
|
||||
long start = System.currentTimeMillis();
|
||||
Object returnObj = joinPoint.proceed();
|
||||
printResponseLog(request, className, methodName, returnObj, System.currentTimeMillis() - start);
|
||||
return returnObj;
|
||||
}
|
||||
|
||||
|
||||
private void printRequestLog(HttpServletRequest request, String clazzName, String methodName, Object[] args) throws JsonProcessingException {
|
||||
log.debug("Request URL: [{}], URI: [{}], Request Method: [{}], IP: [{}]",
|
||||
request.getRequestURL(),
|
||||
request.getRequestURI(),
|
||||
request.getMethod(),
|
||||
ServletUtil.getClientIP(request));
|
||||
|
||||
if (args == null || !log.isDebugEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean shouldNotLog = false;
|
||||
for (Object arg : args) {
|
||||
if (arg == null ||
|
||||
arg instanceof HttpServletRequest ||
|
||||
arg instanceof HttpServletResponse ||
|
||||
arg instanceof MultipartFile ||
|
||||
arg.getClass().isAssignableFrom(MultipartFile[].class)) {
|
||||
shouldNotLog = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldNotLog) {
|
||||
String requestBody = JsonUtils.objectToJson(args);
|
||||
log.debug("{}.{} Parameters: [{}]", clazzName, methodName, requestBody);
|
||||
}
|
||||
}
|
||||
|
||||
private void printResponseLog(HttpServletRequest request, String className, String methodName, Object returnObj, long usage) throws JsonProcessingException {
|
||||
if (log.isDebugEnabled()) {
|
||||
String returningData = null;
|
||||
if (returnObj != null) {
|
||||
if (returnObj.getClass().isAssignableFrom(byte[].class)) {
|
||||
returningData = "Binary data";
|
||||
} else {
|
||||
returningData = JsonUtils.objectToJson(returnObj);
|
||||
}
|
||||
}
|
||||
log.debug("{}.{} Response: [{}], usage: [{}]ms", className, methodName, returningData, usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package run.halo.app.controller.support;
|
||||
package run.halo.app.core;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
|
@ -65,7 +65,7 @@ public class CommentEventListener {
|
|||
return;
|
||||
}
|
||||
|
||||
User user = userService.getCurrentUser().orElseThrow(() -> new ServiceException("找不到博主信息"));
|
||||
User user = userService.getCurrentUser().orElseThrow(() -> new ServiceException("未查询到博主信息"));
|
||||
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
|
|
|
@ -12,6 +12,7 @@ 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;
|
||||
|
@ -98,7 +99,8 @@ public class FreemarkerConfigAwareListener {
|
|||
private void loadThemeConfig() throws TemplateModelException {
|
||||
ThemeProperty activatedTheme = themeService.getActivatedTheme();
|
||||
configuration.setSharedVariable("theme", activatedTheme);
|
||||
configuration.setSharedVariable("static", optionService.getBlogBaseUrl() + "/" + activatedTheme.getFolderName());
|
||||
String baseUrl = optionService.getByPropertyOrDefault(OtherProperties.CDN_DOMAIN, String.class, optionService.getBlogBaseUrl());
|
||||
configuration.setSharedVariable("static", baseUrl + "/" + activatedTheme.getFolderName());
|
||||
configuration.setSharedVariable("settings", themeSettingService.listAsMapBy(themeService.getActivatedThemeId()));
|
||||
log.debug("Loaded theme and settings");
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.util.Objects;
|
|||
* AliYun file handler.
|
||||
*
|
||||
* @author MyFaith
|
||||
* @author ryanwang
|
||||
* @date 2019-04-04 00:06:13
|
||||
*/
|
||||
@Slf4j
|
||||
|
@ -42,12 +43,14 @@ public class AliYunFileHandler implements FileHandler {
|
|||
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 ossStyleRule = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_STYLE_RULE).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);
|
||||
|
@ -57,7 +60,7 @@ public class AliYunFileHandler implements FileHandler {
|
|||
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(ossSource, "/"), upFilePath);
|
||||
String filePath = StringUtils.join(StringUtils.appendIfMissing(StringUtils.isNotBlank(ossDomain) ? ossDomain : ossSource, "/"), upFilePath);
|
||||
|
||||
// Upload
|
||||
PutObjectResult putObjectResult = ossClient.putObject(ossBucketName, upFilePath, file.getInputStream());
|
||||
|
@ -68,7 +71,7 @@ public class AliYunFileHandler implements FileHandler {
|
|||
// Response result
|
||||
UploadResult uploadResult = new UploadResult();
|
||||
uploadResult.setFilename(basename);
|
||||
uploadResult.setFilePath(filePath);
|
||||
uploadResult.setFilePath(StringUtils.isBlank(ossStyleRule) ? filePath : filePath + ossStyleRule);
|
||||
uploadResult.setKey(upFilePath);
|
||||
uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
|
||||
uploadResult.setSuffix(extension);
|
||||
|
@ -79,7 +82,7 @@ public class AliYunFileHandler implements FileHandler {
|
|||
BufferedImage image = ImageIO.read(file.getInputStream());
|
||||
uploadResult.setWidth(image.getWidth());
|
||||
uploadResult.setHeight(image.getHeight());
|
||||
uploadResult.setThumbPath(StringUtils.isBlank(ossStyleRule) ? filePath : filePath + ossStyleRule);
|
||||
uploadResult.setThumbPath(StringUtils.isBlank(ossThumbnailStyleRule) ? filePath : filePath + ossThumbnailStyleRule);
|
||||
}
|
||||
|
||||
return uploadResult;
|
||||
|
@ -106,7 +109,6 @@ public class AliYunFileHandler implements FileHandler {
|
|||
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);
|
||||
|
||||
// Init OSS client
|
||||
OSS ossClient = new OSSClientBuilder().build(ossEndPoint, ossAccessKey, ossAccessSecret);
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
package run.halo.app.handler.file;
|
||||
|
||||
|
||||
import com.baidubce.auth.DefaultBceCredentials;
|
||||
import com.baidubce.services.bos.BosClient;
|
||||
import com.baidubce.services.bos.BosClientConfiguration;
|
||||
import com.baidubce.services.bos.model.PutObjectResponse;
|
||||
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.BaiDuYunProperties;
|
||||
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;
|
||||
|
||||
/**
|
||||
* BaiDuYun file handler.
|
||||
*
|
||||
* @author wangya
|
||||
* @date 2019-07-20
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BaiDuYunFileHandler implements FileHandler {
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
public BaiDuYunFileHandler(OptionService optionService) {
|
||||
this.optionService = optionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UploadResult upload(MultipartFile file) {
|
||||
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);
|
||||
|
||||
BosClientConfiguration config = new BosClientConfiguration();
|
||||
config.setCredentials(new DefaultBceCredentials(bosAccessKey, bosSecretKey));
|
||||
config.setEndpoint(bosEndPoint);
|
||||
|
||||
// Init OSS client
|
||||
BosClient client = new BosClient(config);
|
||||
|
||||
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);
|
||||
|
||||
// Upload
|
||||
PutObjectResponse putObjectResponseFromInputStream = client.putObject(bosBucketName, upFilePath, file.getInputStream());
|
||||
if (putObjectResponseFromInputStream == null) {
|
||||
throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到百度云失败 ");
|
||||
}
|
||||
|
||||
// Response result
|
||||
UploadResult uploadResult = new UploadResult();
|
||||
uploadResult.setFilename(basename);
|
||||
uploadResult.setFilePath(StringUtils.isBlank(bosStyleRule) ? filePath : filePath + bosStyleRule);
|
||||
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(bosThumbnailStyleRule) ? filePath : filePath + bosThumbnailStyleRule);
|
||||
}
|
||||
|
||||
return uploadResult;
|
||||
} catch (Exception e) {
|
||||
throw new FileOperationException("附件 " + file.getOriginalFilename() + " 上传失败(百度云)", e);
|
||||
} finally {
|
||||
client.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
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();
|
||||
|
||||
BosClientConfiguration config = new BosClientConfiguration();
|
||||
config.setCredentials(new DefaultBceCredentials(bosAccessKey, bosSecretKey));
|
||||
config.setEndpoint(bosEndPoint);
|
||||
|
||||
// Init OSS client
|
||||
BosClient client = new BosClient(config);
|
||||
|
||||
try {
|
||||
client.deleteObject(bosBucketName, key);
|
||||
} catch (Exception e) {
|
||||
throw new FileOperationException("附件 " + key + " 从百度云删除失败", e);
|
||||
} finally {
|
||||
client.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportType(AttachmentType type) {
|
||||
return AttachmentType.BAIDUYUN.equals(type);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ package run.halo.app.handler.file;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.coobird.thumbnailator.Thumbnails;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
@ -24,6 +23,7 @@ import java.nio.file.Path;
|
|||
import java.nio.file.Paths;
|
||||
import java.util.Calendar;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR;
|
||||
|
||||
|
@ -31,7 +31,8 @@ import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR;
|
|||
* Local file handler.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 3/27/19
|
||||
* @author ryanwang
|
||||
* @date 2019-03-27
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
|
@ -54,6 +55,8 @@ public class LocalFileHandler implements FileHandler {
|
|||
*/
|
||||
private final static int THUMB_HEIGHT = 256;
|
||||
|
||||
ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
private final String workDir;
|
||||
|
@ -135,28 +138,38 @@ public class LocalFileHandler implements FileHandler {
|
|||
uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
|
||||
uploadResult.setSize(file.getSize());
|
||||
|
||||
// TODO refactor this: if image is svg ext. extension
|
||||
boolean isSvg = "svg".equals(extension);
|
||||
|
||||
// Check file type
|
||||
if (FileHandler.isImageType(uploadResult.getMediaType())) {
|
||||
if (FileHandler.isImageType(uploadResult.getMediaType()) && !isSvg) {
|
||||
lock.lock();
|
||||
try {
|
||||
// Upload a thumbnail
|
||||
String thumbnailBasename = basename + THUMBNAIL_SUFFIX;
|
||||
String thumbnailSubFilePath = subDir + thumbnailBasename + '.' + extension;
|
||||
Path thumbnailPath = Paths.get(workDir + thumbnailSubFilePath);
|
||||
|
||||
// Create the thumbnail
|
||||
Files.createFile(thumbnailPath);
|
||||
// Read as image
|
||||
BufferedImage originalImage = ImageIO.read(uploadPath.toFile());
|
||||
// Set width and height
|
||||
uploadResult.setWidth(originalImage.getWidth());
|
||||
uploadResult.setHeight(originalImage.getHeight());
|
||||
|
||||
// Generate thumbnail
|
||||
generateThumbnail(uploadPath, thumbnailPath);
|
||||
|
||||
// Read as image
|
||||
BufferedImage image = ImageIO.read(Files.newInputStream(uploadPath));
|
||||
|
||||
// Set width and height
|
||||
uploadResult.setWidth(image.getWidth());
|
||||
uploadResult.setHeight(image.getHeight());
|
||||
|
||||
boolean result = generateThumbnail(originalImage, thumbnailPath, extension);
|
||||
if (result) {
|
||||
// Set thumb path
|
||||
uploadResult.setThumbPath(thumbnailSubFilePath);
|
||||
} else {
|
||||
// If generate error
|
||||
uploadResult.setThumbPath(subFilePath);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
uploadResult.setThumbPath(subFilePath);
|
||||
}
|
||||
|
||||
return uploadResult;
|
||||
|
@ -194,7 +207,7 @@ public class LocalFileHandler implements FileHandler {
|
|||
try {
|
||||
boolean deleteResult = Files.deleteIfExists(thumbnailPath);
|
||||
if (!deleteResult) {
|
||||
log.warn("Thumbnail: [{}] way not exist", thumbnailPath.toString());
|
||||
log.warn("Thumbnail: [{}] may not exist", thumbnailPath.toString());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new FileOperationException("附件缩略图 " + thumbnailName + " 删除失败", e);
|
||||
|
@ -206,21 +219,24 @@ public class LocalFileHandler implements FileHandler {
|
|||
return AttachmentType.LOCAL.equals(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates thumbnail image.
|
||||
*
|
||||
* @param imagePath image path must not be null
|
||||
* @param thumbPath thumbnail path must not be null
|
||||
* @throws IOException throws if image provided is not valid
|
||||
*/
|
||||
private void generateThumbnail(@NonNull Path imagePath, @NonNull Path thumbPath) throws IOException {
|
||||
Assert.notNull(imagePath, "Image path must not be null");
|
||||
private boolean generateThumbnail(BufferedImage originalImage, Path thumbPath, String extension) {
|
||||
Assert.notNull(originalImage, "Image must not be null");
|
||||
Assert.notNull(thumbPath, "Thumb path must not be null");
|
||||
|
||||
log.info("Generating thumbnail: [{}] for image: [{}]", thumbPath.getFileName(), imagePath.getFileName());
|
||||
|
||||
boolean result = false;
|
||||
// Create the thumbnail
|
||||
try {
|
||||
Files.createFile(thumbPath);
|
||||
// Convert to thumbnail and copy the thumbnail
|
||||
Thumbnails.of(imagePath.toFile()).size(THUMB_WIDTH, THUMB_HEIGHT).keepAspectRatio(true).toFile(thumbPath.toFile());
|
||||
log.debug("Trying to generate thumbnail: [{}]", thumbPath.toString());
|
||||
Thumbnails.of(originalImage).size(THUMB_WIDTH, THUMB_HEIGHT).keepAspectRatio(true).toFile(thumbPath.toFile());
|
||||
log.debug("Generated thumbnail image, and wrote the thumbnail to [{}]", thumbPath.toString());
|
||||
result = true;
|
||||
} catch (Throwable t) {
|
||||
log.warn("Failed to generate thumbnail: [{}]", thumbPath);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import static run.halo.app.handler.file.FileHandler.isImageType;
|
|||
* Qi niu yun file handler.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 3/27/19
|
||||
*/
|
||||
@Slf4j
|
||||
|
@ -58,6 +59,7 @@ public class QnYunFileHandler implements FileHandler {
|
|||
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, "");
|
||||
|
||||
// Create configuration
|
||||
Configuration configuration = new Configuration(zone);
|
||||
|
@ -77,7 +79,6 @@ public class QnYunFileHandler implements FileHandler {
|
|||
// Create temp path
|
||||
Path tmpPath = Paths.get(System.getProperty("java.io.tmpdir"), bucket);
|
||||
|
||||
|
||||
try {
|
||||
String basename = FilenameUtils.getBasename(file.getOriginalFilename());
|
||||
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
|
||||
|
@ -103,7 +104,7 @@ public class QnYunFileHandler implements FileHandler {
|
|||
// Build upload result
|
||||
UploadResult result = new UploadResult();
|
||||
result.setFilename(basename);
|
||||
result.setFilePath(filePath);
|
||||
result.setFilePath(StringUtils.isBlank(styleRule) ? filePath : filePath + styleRule);
|
||||
result.setKey(putSet.getKey());
|
||||
result.setSuffix(extension);
|
||||
result.setWidth(putSet.getWidth());
|
||||
|
@ -112,7 +113,7 @@ public class QnYunFileHandler implements FileHandler {
|
|||
result.setSize(file.getSize());
|
||||
|
||||
if (isImageType(result.getMediaType())) {
|
||||
result.setThumbPath(StringUtils.isBlank(styleRule) ? filePath : filePath + styleRule);
|
||||
result.setThumbPath(StringUtils.isBlank(thumbnailStyleRule) ? filePath : filePath + thumbnailStyleRule);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -147,7 +148,7 @@ public class QnYunFileHandler implements FileHandler {
|
|||
bucketManager.delete(bucket, key);
|
||||
} catch (QiniuException e) {
|
||||
log.error("QnYun error response: [{}]", e.response);
|
||||
throw new FileOperationException("Failed to delete file with " + key + " key", e);
|
||||
throw new FileOperationException("附件 " + key + " 从七牛云删除失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,11 +9,15 @@ import org.springframework.lang.Nullable;
|
|||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.exception.FileOperationException;
|
||||
import run.halo.app.exception.ServiceException;
|
||||
import run.halo.app.model.enums.AttachmentType;
|
||||
import run.halo.app.model.properties.SmmsProperties;
|
||||
import run.halo.app.model.support.UploadResult;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.utils.FilenameUtils;
|
||||
import run.halo.app.utils.HttpClientUtils;
|
||||
|
||||
|
@ -30,24 +34,40 @@ import java.util.Objects;
|
|||
@Component
|
||||
public class SmmsFileHandler implements FileHandler {
|
||||
|
||||
@Deprecated
|
||||
private final static String UPLOAD_API = "https://sm.ms/api/upload";
|
||||
|
||||
private final static String UPLOAD_API_V2 = "https://sm.ms/api/v2/upload";
|
||||
|
||||
@Deprecated
|
||||
private final static String DELETE_API = "https://sm.ms/api/delete/%s";
|
||||
|
||||
private final static String DELETE_API_V2 = "https://sm.ms/api/v2/delete/%s";
|
||||
|
||||
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;
|
||||
|
||||
public SmmsFileHandler(RestTemplate httpsRestTemplate) {
|
||||
private final OptionService optionService;
|
||||
|
||||
public SmmsFileHandler(RestTemplate httpsRestTemplate,
|
||||
OptionService optionService) {
|
||||
this.httpsRestTemplate = httpsRestTemplate;
|
||||
this.optionService = optionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UploadResult upload(MultipartFile file) {
|
||||
Assert.notNull(file, "Multipart file must not be null");
|
||||
|
||||
String apiSecretToken = optionService.getByPropertyOfNonNull(SmmsProperties.SMMS_API_SECRET_TOKEN).toString();
|
||||
|
||||
if (StringUtils.isEmpty(apiSecretToken)) {
|
||||
throw new ServiceException("请先设置 SM.MS 的 Secret Token");
|
||||
}
|
||||
|
||||
if (!FileHandler.isImageType(file.getContentType())) {
|
||||
log.error("Invalid extension: [{}]", file.getContentType());
|
||||
throw new FileOperationException("不支持的文件类型,仅支持 \"jpeg, jpg, png, gif, bmp\" 格式的图片");
|
||||
|
@ -58,6 +78,7 @@ public class SmmsFileHandler implements FileHandler {
|
|||
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<>();
|
||||
|
||||
|
@ -74,7 +95,7 @@ public class SmmsFileHandler implements FileHandler {
|
|||
HttpEntity<LinkedMultiValueMap<String, Object>> httpEntity = new HttpEntity<>(body, headers);
|
||||
|
||||
// Upload file
|
||||
ResponseEntity<SmmsResponse> mapResponseEntity = httpsRestTemplate.postForEntity(UPLOAD_API, httpEntity, SmmsResponse.class);
|
||||
ResponseEntity<SmmsResponse> mapResponseEntity = httpsRestTemplate.postForEntity(UPLOAD_API_V2, httpEntity, SmmsResponse.class);
|
||||
|
||||
// Check status
|
||||
if (mapResponseEntity.getStatusCode().isError()) {
|
||||
|
@ -117,7 +138,7 @@ public class SmmsFileHandler implements FileHandler {
|
|||
Assert.hasText(key, "Deleting key must not be blank");
|
||||
|
||||
// Build delete url
|
||||
String url = String.format(DELETE_API, key);
|
||||
String url = String.format(DELETE_API_V2, key);
|
||||
|
||||
// Set user agent manually
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import java.util.Objects;
|
|||
* Up Yun file handler.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 3/27/19
|
||||
*/
|
||||
@Slf4j
|
||||
|
@ -46,6 +47,7 @@ public class UpYunFileHandler implements FileHandler {
|
|||
String ossOperator = optionService.getByPropertyOfNonNull(UpYunProperties.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, "");
|
||||
|
||||
// Create up yun
|
||||
UpYun upYun = new UpYun(ossBucket, ossOperator, ossPassword);
|
||||
|
@ -75,7 +77,7 @@ public class UpYunFileHandler implements FileHandler {
|
|||
// Build upload result
|
||||
UploadResult uploadResult = new UploadResult();
|
||||
uploadResult.setFilename(basename);
|
||||
uploadResult.setFilePath(filePath);
|
||||
uploadResult.setFilePath(StringUtils.isBlank(ossStyleRule) ? filePath : filePath + ossStyleRule);
|
||||
uploadResult.setKey(upFilePath);
|
||||
uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
|
||||
uploadResult.setSuffix(extension);
|
||||
|
@ -86,7 +88,7 @@ public class UpYunFileHandler implements FileHandler {
|
|||
BufferedImage image = ImageIO.read(file.getInputStream());
|
||||
uploadResult.setWidth(image.getWidth());
|
||||
uploadResult.setHeight(image.getHeight());
|
||||
uploadResult.setThumbPath(StringUtils.isBlank(ossStyleRule) ? filePath : filePath + ossStyleRule);
|
||||
uploadResult.setThumbPath(StringUtils.isBlank(ossThumbnailStyleRule) ? filePath : filePath + ossThumbnailStyleRule);
|
||||
}
|
||||
|
||||
return uploadResult;
|
||||
|
@ -100,7 +102,6 @@ public class UpYunFileHandler implements FileHandler {
|
|||
Assert.notNull(key, "File key must not be blank");
|
||||
|
||||
// Get config
|
||||
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 ossOperator = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_OPERATOR).toString();
|
||||
|
@ -111,14 +112,13 @@ public class UpYunFileHandler implements FileHandler {
|
|||
upYun.setApiDomain(UpYun.ED_AUTO);
|
||||
|
||||
try {
|
||||
String filePath = ossSource + key;
|
||||
// Delete the file
|
||||
boolean deleteResult = upYun.deleteFile(filePath);
|
||||
boolean deleteResult = upYun.deleteFile(key);
|
||||
if (!deleteResult) {
|
||||
log.warn("Failed to delete file " + filePath + " from UpYun");
|
||||
log.warn("Failed to delete file " + key + " from UpYun");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new FileOperationException("附件从又拍云删除失败", e);
|
||||
throw new FileOperationException("附件 " + key + " 从又拍云删除失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,8 +53,12 @@ public class Item {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Item item = (Item) o;
|
||||
return name.equals(item.name);
|
||||
}
|
||||
|
|
|
@ -85,8 +85,12 @@ public class ThemeProperty {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ThemeProperty that = (ThemeProperty) o;
|
||||
return id.equals(that.id);
|
||||
}
|
||||
|
|
|
@ -66,10 +66,10 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
|
|||
// Whether the blog has initialized
|
||||
Boolean isInstalled = optionService.getByPropertyOrDefault(PrimaryProperties.IS_INSTALLED, Boolean.class, false);
|
||||
|
||||
if (haloProperties.isProductionEnv() && isInstalled) {
|
||||
/*if (haloProperties.isProductionEnv() && isInstalled) {
|
||||
// Skip
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
|
||||
try {
|
||||
String themeClassPath = ResourceUtils.CLASSPATH_URL_PREFIX + ThemeService.THEME_FOLDER;
|
||||
|
@ -80,7 +80,7 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
|
|||
|
||||
Path source;
|
||||
|
||||
if (themeUri.getScheme().equalsIgnoreCase("jar")) {
|
||||
if ("jar".equalsIgnoreCase(themeUri.getScheme())) {
|
||||
// Create new file system for jar
|
||||
FileSystem fileSystem = FileSystems.newFileSystem(themeUri, Collections.emptyMap());
|
||||
source = fileSystem.getPath("/BOOT-INF/classes/" + ThemeService.THEME_FOLDER);
|
||||
|
@ -91,7 +91,8 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
|
|||
// Create theme folder
|
||||
Path themePath = themeService.getBasePath();
|
||||
|
||||
if (!haloProperties.isProductionEnv() || Files.notExists(themePath)) {
|
||||
// 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);
|
||||
} else {
|
||||
|
|
|
@ -3,6 +3,7 @@ package run.halo.app.model.dto;
|
|||
import lombok.Data;
|
||||
import run.halo.app.model.dto.base.OutputConverter;
|
||||
import run.halo.app.model.entity.Journal;
|
||||
import run.halo.app.model.enums.JournalType;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
|
@ -22,4 +23,6 @@ public class JournalDTO implements OutputConverter<JournalDTO, Journal> {
|
|||
private Long likes;
|
||||
|
||||
private Date createTime;
|
||||
|
||||
private JournalType type;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import run.halo.app.model.entity.Menu;
|
|||
* Menu output dto.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 4/3/19
|
||||
*/
|
||||
@Data
|
||||
|
@ -30,4 +31,6 @@ public class MenuDTO implements OutputConverter<MenuDTO, Menu> {
|
|||
private String icon;
|
||||
|
||||
private Integer parentId;
|
||||
|
||||
private String team;
|
||||
}
|
||||
|
|
|
@ -28,8 +28,6 @@ public class BasePostMinimalDTO implements OutputConverter<BasePostMinimalDTO, B
|
|||
|
||||
private String url;
|
||||
|
||||
private PostType type;
|
||||
|
||||
private Date updateTime;
|
||||
|
||||
private Date createTime;
|
||||
|
|
|
@ -4,7 +4,6 @@ import lombok.Data;
|
|||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import run.halo.app.model.enums.PostCreateFrom;
|
||||
import run.halo.app.model.enums.PostType;
|
||||
|
||||
/**
|
||||
* Base page simple output dto.
|
||||
|
@ -16,8 +15,6 @@ import run.halo.app.model.enums.PostType;
|
|||
@EqualsAndHashCode(callSuper = true)
|
||||
public class BasePostSimpleDTO extends BasePostMinimalDTO {
|
||||
|
||||
private PostType type;
|
||||
|
||||
private String summary;
|
||||
|
||||
private String thumbnail;
|
||||
|
@ -26,6 +23,8 @@ public class BasePostSimpleDTO extends BasePostMinimalDTO {
|
|||
|
||||
private Boolean disallowComment;
|
||||
|
||||
private String password;
|
||||
|
||||
private String template;
|
||||
|
||||
private Integer topPriority = 0;
|
||||
|
|
|
@ -14,9 +14,9 @@ import java.util.Date;
|
|||
* @author johnniang
|
||||
* @date 3/20/19
|
||||
*/
|
||||
@MappedSuperclass
|
||||
@Data
|
||||
@ToString
|
||||
@MappedSuperclass
|
||||
@EqualsAndHashCode
|
||||
public class BaseEntity {
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package run.halo.app.model.entity;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
/**
|
||||
* Base meta entity.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @author ikaisec
|
||||
* @date 2019-08-04
|
||||
*/
|
||||
@Data
|
||||
@Entity(name = "BaseMeta")
|
||||
@Table(name = "metas")
|
||||
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.INTEGER, columnDefinition = "int default 0")
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class BaseMeta extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* Post id.
|
||||
*/
|
||||
@Column(name = "post_id", columnDefinition = "int not null")
|
||||
private Integer postId;
|
||||
|
||||
/**
|
||||
* meta key
|
||||
*/
|
||||
@Column(name = "meta_key", columnDefinition = "varchar(100) not null")
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* meta value
|
||||
*/
|
||||
@Column(name = "meta_value", columnDefinition = "varchar(1023) not null")
|
||||
private String value;
|
||||
}
|
|
@ -10,9 +10,10 @@ import javax.persistence.*;
|
|||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Post entity.
|
||||
* Post base entity.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
*/
|
||||
@Data
|
||||
@Entity(name = "BasePost")
|
||||
|
@ -41,7 +42,7 @@ public class BasePost extends BaseEntity {
|
|||
/**
|
||||
* Post url.
|
||||
*/
|
||||
@Column(name = "url", columnDefinition = "varchar(255) not null")
|
||||
@Column(name = "url", columnDefinition = "varchar(255) not null", unique = true)
|
||||
private String url;
|
||||
|
||||
/**
|
||||
|
@ -168,6 +169,10 @@ public class BasePost extends BaseEntity {
|
|||
if (likes == null || likes < 0) {
|
||||
likes = 0L;
|
||||
}
|
||||
|
||||
if (formatContent == null) {
|
||||
formatContent = "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public class Category extends BaseEntity {
|
|||
/**
|
||||
* Category slug name.
|
||||
*/
|
||||
@Column(name = "slug_name", columnDefinition = "varchar(50) not null")
|
||||
@Column(name = "slug_name", columnDefinition = "varchar(50) not null", unique = true)
|
||||
private String slugName;
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,6 +3,7 @@ package run.halo.app.model.entity;
|
|||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import run.halo.app.model.enums.JournalType;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
|
@ -10,6 +11,7 @@ import javax.persistence.*;
|
|||
* Journal entity
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 3/22/19
|
||||
*/
|
||||
@Data
|
||||
|
@ -29,6 +31,9 @@ public class Journal extends BaseEntity {
|
|||
@Column(name = "likes", columnDefinition = "bigint default 0")
|
||||
private Long likes;
|
||||
|
||||
@Column(name = "type", columnDefinition = "int default 1")
|
||||
private JournalType type;
|
||||
|
||||
@Override
|
||||
public void prePersist() {
|
||||
super.prePersist();
|
||||
|
@ -38,5 +43,10 @@ public class Journal extends BaseEntity {
|
|||
if (likes == null || likes < 0) {
|
||||
likes = 0L;
|
||||
}
|
||||
|
||||
|
||||
if (type == null) {
|
||||
type = JournalType.PUBLIC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,12 @@ public class Menu extends BaseEntity {
|
|||
@Column(name = "parent_id", columnDefinition = "int default 0")
|
||||
private Integer parentId;
|
||||
|
||||
/**
|
||||
* Menu team name.
|
||||
*/
|
||||
@Column(name = "team", columnDefinition = "varchar(255) default ''")
|
||||
private String team;
|
||||
|
||||
@Override
|
||||
public void prePersist() {
|
||||
super.prePersist();
|
||||
|
@ -81,5 +87,9 @@ public class Menu extends BaseEntity {
|
|||
if (parentId == null) {
|
||||
parentId = 0;
|
||||
}
|
||||
|
||||
if(team == null){
|
||||
team = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package run.halo.app.model.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
@ -43,8 +42,12 @@ public class PostCategory extends BaseEntity {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PostCategory that = (PostCategory) o;
|
||||
return categoryId.equals(that.categoryId) &&
|
||||
postId.equals(that.postId);
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package run.halo.app.model.entity;
|
||||
|
||||
import javax.persistence.DiscriminatorValue;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* PostMeta entity.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @author ikaisec
|
||||
* @date 2019-08-04
|
||||
*/
|
||||
@Entity(name = "PostMeta")
|
||||
@DiscriminatorValue("0")
|
||||
public class PostMeta extends BaseMeta {
|
||||
}
|
|
@ -44,8 +44,12 @@ public class PostTag extends BaseEntity {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PostTag postTag = (PostTag) o;
|
||||
return Objects.equals(postId, postTag.postId) &&
|
||||
Objects.equals(tagId, postTag.tagId);
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package run.halo.app.model.entity;
|
||||
|
||||
import javax.persistence.DiscriminatorValue;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* SheetMeta entity.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @author ikaisec
|
||||
* @date 2019-08-04
|
||||
*/
|
||||
@Entity(name = "SheetMeta")
|
||||
@DiscriminatorValue("1")
|
||||
public class SheetMeta extends BaseMeta {
|
||||
}
|
|
@ -33,7 +33,7 @@ public class Tag extends BaseEntity {
|
|||
/**
|
||||
* Tag slug name.
|
||||
*/
|
||||
@Column(name = "slug_name", columnDefinition = "varchar(255) not null")
|
||||
@Column(name = "slug_name", columnDefinition = "varchar(255) not null", unique = true)
|
||||
private String slugName;
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,7 +31,17 @@ public enum AttachmentType implements ValueEnum<Integer> {
|
|||
/**
|
||||
* 阿里云
|
||||
*/
|
||||
ALIYUN(4);
|
||||
ALIYUN(4),
|
||||
|
||||
/**
|
||||
* 百度云
|
||||
*/
|
||||
BAIDUYUN(5),
|
||||
|
||||
/**
|
||||
* 腾讯云
|
||||
*/
|
||||
TENCENTYUN(6);
|
||||
|
||||
private Integer value;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import org.springframework.lang.Nullable;
|
|||
* Input type enum.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 4/10/19
|
||||
*/
|
||||
public enum InputType {
|
||||
|
@ -18,7 +19,11 @@ public enum InputType {
|
|||
|
||||
SELECT,
|
||||
|
||||
TEXTAREA;
|
||||
TEXTAREA,
|
||||
|
||||
COLOR,
|
||||
|
||||
ATTACHMENT;
|
||||
|
||||
/**
|
||||
* Convert type to input type.
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package run.halo.app.model.enums;
|
||||
|
||||
/**
|
||||
* Journal type.
|
||||
*
|
||||
* @author ryanwnag
|
||||
*/
|
||||
public enum JournalType implements ValueEnum<Integer> {
|
||||
|
||||
/**
|
||||
* Public type.
|
||||
*/
|
||||
PUBLIC(1),
|
||||
|
||||
/**
|
||||
* Intimate type.
|
||||
*/
|
||||
INTIMATE(0);
|
||||
|
||||
private final int value;
|
||||
|
||||
JournalType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -30,15 +30,15 @@ public enum Mode {
|
|||
@Nullable
|
||||
@JsonCreator
|
||||
public static Mode valueFrom(@Nullable String value) {
|
||||
if (StringUtils.isBlank(value) || value.equalsIgnoreCase("prod")) {
|
||||
if (StringUtils.isBlank(value) || "prod".equalsIgnoreCase(value)) {
|
||||
return Mode.PRODUCTION;
|
||||
}
|
||||
|
||||
if (value.equalsIgnoreCase("dev")) {
|
||||
if ("dev".equalsIgnoreCase(value)) {
|
||||
return Mode.DEVELOPMENT;
|
||||
}
|
||||
|
||||
if (value.equalsIgnoreCase("test")) {
|
||||
if ("test".equalsIgnoreCase(value)) {
|
||||
return Mode.TEST;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,12 @@ public enum PostStatus implements ValueEnum<Integer> {
|
|||
/**
|
||||
* Recycle status.
|
||||
*/
|
||||
RECYCLE(2);
|
||||
RECYCLE(2),
|
||||
|
||||
/**
|
||||
* Intimate status
|
||||
*/
|
||||
INTIMATE(3);
|
||||
|
||||
private final int value;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ package run.halo.app.model.enums;
|
|||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
@Deprecated
|
||||
public enum PostType implements ValueEnum<Integer> {
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,6 +11,7 @@ import javax.persistence.Converter;
|
|||
* @date 3/27/19
|
||||
*/
|
||||
@Converter(autoApply = true)
|
||||
@Deprecated
|
||||
public class PostTypeConverter extends AbstractConverter<PostType, Integer> {
|
||||
|
||||
public PostTypeConverter() {
|
||||
|
|
|
@ -44,7 +44,7 @@ public class CategoryTagDirective implements TemplateDirectiveModel {
|
|||
break;
|
||||
case "listByPostId":
|
||||
Integer postId = Integer.parseInt(params.get("postId").toString());
|
||||
env.setVariable("categories", builder.build().wrap(postCategoryService.listCategoryBy(postId)));
|
||||
env.setVariable("categories", builder.build().wrap(postCategoryService.listCategoriesBy(postId)));
|
||||
break;
|
||||
case "count":
|
||||
env.setVariable("count", builder.build().wrap(categoryService.count()));
|
||||
|
|
|
@ -2,7 +2,10 @@ package run.halo.app.model.freemarker.tag;
|
|||
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.template.*;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.model.entity.PostComment;
|
||||
import run.halo.app.model.enums.CommentStatus;
|
||||
import run.halo.app.model.support.HaloConst;
|
||||
import run.halo.app.service.PostCommentService;
|
||||
|
||||
|
@ -34,7 +37,8 @@ public class CommentTagDirective implements TemplateDirectiveModel {
|
|||
switch (method) {
|
||||
case "latest":
|
||||
int top = Integer.parseInt(params.get("top").toString());
|
||||
env.setVariable("comments", builder.build().wrap(postCommentService.pageLatest(top)));
|
||||
Page<PostComment> postComments = postCommentService.pageLatest(top, CommentStatus.PUBLISHED);
|
||||
env.setVariable("comments", builder.build().wrap(postCommentService.convertToWithPostVo(postComments)));
|
||||
break;
|
||||
case "count":
|
||||
env.setVariable("count", builder.build().wrap(postCommentService.count()));
|
||||
|
|
|
@ -43,6 +43,13 @@ public class MenuTagDirective implements TemplateDirectiveModel {
|
|||
case "tree":
|
||||
env.setVariable("menus", builder.build().wrap(menuService.listAsTree(Sort.by(DESC, "priority"))));
|
||||
break;
|
||||
case "listTeams":
|
||||
env.setVariable("teams", builder.build().wrap(menuService.listTeamVos(Sort.by(DESC, "priority"))));
|
||||
break;
|
||||
case "listByTeam":
|
||||
String team = params.get("team").toString();
|
||||
env.setVariable("menus", builder.build().wrap(menuService.listByTeam(team, Sort.by(DESC, "priority"))));
|
||||
break;
|
||||
case "count":
|
||||
env.setVariable("count", builder.build().wrap(menuService.count()));
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package run.halo.app.model.params;
|
||||
|
||||
import lombok.Data;
|
||||
import run.halo.app.model.dto.base.InputConverter;
|
||||
import run.halo.app.utils.ReflectionUtils;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
|
||||
/**
|
||||
* Base meta param.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @author ikaisec
|
||||
* @date 2019-08-04
|
||||
*/
|
||||
@Data
|
||||
public abstract class BaseMetaParam<META> implements InputConverter<META> {
|
||||
|
||||
@NotBlank(message = "文章id不能为空")
|
||||
private Integer postId;
|
||||
|
||||
@NotBlank(message = "Meta key 不能为空")
|
||||
@Size(max = 1023, message = "Meta key 的字符长度不能超过 {max}")
|
||||
private String key;
|
||||
|
||||
@NotBlank(message = "Meta value 不能为空")
|
||||
@Size(max = 1023, message = "Meta value 的字符长度不能超过 {max}")
|
||||
private String value;
|
||||
|
||||
@Override
|
||||
public ParameterizedType parameterizedType() {
|
||||
return ReflectionUtils.getParameterizedTypeBySuperClass(BaseMetaParam.class, this.getClass());
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package run.halo.app.model.params;
|
|||
import lombok.Data;
|
||||
import run.halo.app.model.dto.base.InputConverter;
|
||||
import run.halo.app.model.entity.Journal;
|
||||
import run.halo.app.model.enums.JournalType;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
@ -11,6 +12,7 @@ import javax.validation.constraints.Size;
|
|||
* Journal param.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 19-4-25
|
||||
*/
|
||||
@Data
|
||||
|
@ -19,4 +21,6 @@ public class JournalParam implements InputConverter<Journal> {
|
|||
@NotBlank(message = "内容不能为空")
|
||||
@Size(max = 511, message = "内容的字符长度不能超过 {max}")
|
||||
private String content;
|
||||
|
||||
private JournalType type = JournalType.PUBLIC;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package run.halo.app.model.params;
|
||||
|
||||
import lombok.Data;
|
||||
import run.halo.app.model.enums.JournalType;
|
||||
|
||||
/**
|
||||
* Journal query params.
|
||||
|
@ -15,4 +16,6 @@ public class JournalQuery {
|
|||
* Keyword.
|
||||
*/
|
||||
private String keyword;
|
||||
|
||||
private JournalType type;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import javax.validation.constraints.Size;
|
|||
* Menu param.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 4/3/19
|
||||
*/
|
||||
@Data
|
||||
|
@ -37,4 +38,7 @@ public class MenuParam implements InputConverter<Menu> {
|
|||
private String icon;
|
||||
|
||||
private Integer parentId;
|
||||
|
||||
@Size(max = 255, message = "菜单分组的字符长度不能超过 {max}")
|
||||
private String team;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import javax.validation.constraints.NotBlank;
|
|||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* Optiona param.
|
||||
* Optional param.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 3/20/19
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package run.halo.app.model.params;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import run.halo.app.model.entity.PostMeta;
|
||||
|
||||
/**
|
||||
* Post meta param.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @author ikaisec
|
||||
* @date 2019-08-04
|
||||
*/
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PostMetaParam extends BaseMetaParam<PostMeta> {
|
||||
}
|
|
@ -7,7 +7,6 @@ import run.halo.app.model.dto.base.InputConverter;
|
|||
import run.halo.app.model.entity.Post;
|
||||
import run.halo.app.model.enums.PostCreateFrom;
|
||||
import run.halo.app.model.enums.PostStatus;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
@ -19,6 +18,7 @@ import java.util.Set;
|
|||
* Post param.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 3/21/19
|
||||
*/
|
||||
@Data
|
||||
|
@ -62,29 +62,24 @@ public class PostParam implements InputConverter<Post> {
|
|||
@Override
|
||||
public Post convertTo() {
|
||||
if (StringUtils.isBlank(url)) {
|
||||
url = title;
|
||||
url = title.replace(".","");
|
||||
}
|
||||
if (null == thumbnail) {
|
||||
thumbnail = "";
|
||||
}
|
||||
|
||||
Post post = InputConverter.super.convertTo();
|
||||
// Crypt password
|
||||
if (StringUtils.isNotBlank(password)) {
|
||||
post.setPassword(BCrypt.hashpw(password, BCrypt.gensalt()));
|
||||
}
|
||||
|
||||
return post;
|
||||
return InputConverter.super.convertTo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Post post) {
|
||||
if (StringUtils.isBlank(url)) {
|
||||
url = title;
|
||||
url = title.replace(".","");
|
||||
}
|
||||
if (null == thumbnail) {
|
||||
thumbnail = "";
|
||||
}
|
||||
|
||||
InputConverter.super.update(post);
|
||||
|
||||
// Crypt password
|
||||
if (StringUtils.isNotBlank(password)) {
|
||||
post.setPassword(BCrypt.hashpw(password, BCrypt.gensalt()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package run.halo.app.model.params;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* Reset password params.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-09-05
|
||||
*/
|
||||
@Data
|
||||
public class ResetPasswordParam {
|
||||
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
private String email;
|
||||
|
||||
private String code;
|
||||
|
||||
private String password;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package run.halo.app.model.params;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import run.halo.app.model.entity.SheetMeta;
|
||||
|
||||
/**
|
||||
* Sheet meta param.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @author ikaisec
|
||||
* @date 2019-08-04
|
||||
*/
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SheetMetaParam extends BaseMetaParam<SheetMeta> {
|
||||
}
|
|
@ -11,9 +11,13 @@ import run.halo.app.utils.HaloUtils;
|
|||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Sheet param.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 19-4-24
|
||||
*/
|
||||
@Data
|
||||
|
@ -35,6 +39,8 @@ public class SheetParam implements InputConverter<Sheet> {
|
|||
|
||||
private Boolean disallowComment = false;
|
||||
|
||||
private Date createTime;
|
||||
|
||||
@Size(max = 255, message = "Length of password must not be more than {max}")
|
||||
private String password;
|
||||
|
||||
|
@ -47,37 +53,26 @@ public class SheetParam implements InputConverter<Sheet> {
|
|||
@Override
|
||||
public Sheet convertTo() {
|
||||
if (StringUtils.isBlank(url)) {
|
||||
url = HaloUtils.normalizeUrl(title);
|
||||
} else {
|
||||
url = HaloUtils.normalizeUrl(url);
|
||||
url = title.replace(".","");
|
||||
}
|
||||
|
||||
url = HaloUtils.initializeUrlIfBlank(url);
|
||||
|
||||
Sheet sheet = InputConverter.super.convertTo();
|
||||
// Crypt password
|
||||
if (StringUtils.isNotBlank(password)) {
|
||||
sheet.setPassword(BCrypt.hashpw(password, BCrypt.gensalt()));
|
||||
if (null == thumbnail) {
|
||||
thumbnail = "";
|
||||
}
|
||||
|
||||
return sheet;
|
||||
return InputConverter.super.convertTo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Sheet sheet) {
|
||||
if (StringUtils.isBlank(url)) {
|
||||
url = HaloUtils.normalizeUrl(title);
|
||||
} else {
|
||||
url = HaloUtils.normalizeUrl(url);
|
||||
url = title.replace(".","");
|
||||
}
|
||||
|
||||
url = HaloUtils.initializeUrlIfBlank(url);
|
||||
if (null == thumbnail) {
|
||||
thumbnail = "";
|
||||
}
|
||||
|
||||
InputConverter.super.update(sheet);
|
||||
|
||||
// Crypt password
|
||||
if (StringUtils.isNotBlank(password)) {
|
||||
sheet.setPassword(BCrypt.hashpw(password, BCrypt.gensalt()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package run.halo.app.model.params;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Theme content param.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-09-01
|
||||
*/
|
||||
@Data
|
||||
public class ThemeContentParam {
|
||||
private String path;
|
||||
private String content;
|
||||
}
|
|
@ -4,10 +4,16 @@ package run.halo.app.model.properties;
|
|||
* AliYun properties.
|
||||
*
|
||||
* @author MyFaith
|
||||
* @author ryanwang
|
||||
* @date 2019-04-04 00:00:56
|
||||
*/
|
||||
public enum AliYunProperties implements PropertyEnum {
|
||||
|
||||
/**
|
||||
* Aliyun oss domain
|
||||
*/
|
||||
OSS_DOMAIN("oss_aliyun_domain",String.class,""),
|
||||
|
||||
/**
|
||||
* Aliyun oss endpoint.
|
||||
*/
|
||||
|
@ -31,7 +37,12 @@ public enum AliYunProperties implements PropertyEnum {
|
|||
/**
|
||||
* Aliyun oss style rule
|
||||
*/
|
||||
OSS_STYLE_RULE("oss_aliyun_style_rule", String.class, "");
|
||||
OSS_STYLE_RULE("oss_aliyun_style_rule", String.class, ""),
|
||||
|
||||
/**
|
||||
* Aliyun oss thumbnail style rule
|
||||
*/
|
||||
OSS_THUMBNAIL_STYLE_RULE("oss_aliyun_thumbnail_style_rule", String.class, "");
|
||||
|
||||
private final String value;
|
||||
|
||||
|
|
|
@ -8,8 +8,14 @@ package run.halo.app.model.properties;
|
|||
*/
|
||||
public enum ApiProperties implements PropertyEnum {
|
||||
|
||||
/**
|
||||
* api_enabled
|
||||
*/
|
||||
API_ENABLED("api_enabled", Boolean.class, "false"),
|
||||
|
||||
/**
|
||||
* api_access_key
|
||||
*/
|
||||
API_ACCESS_KEY("api_access_key", String.class, "");
|
||||
|
||||
private final String value;
|
||||
|
|
|
@ -10,6 +10,24 @@ import run.halo.app.model.enums.AttachmentType;
|
|||
*/
|
||||
public enum AttachmentProperties implements PropertyEnum {
|
||||
|
||||
/**
|
||||
* Upload image preview enable
|
||||
*/
|
||||
UPLOAD_IMAGE_PREVIEW_ENABLE("attachment_upload_image_preview_enable", Boolean.class, "true"),
|
||||
|
||||
/**
|
||||
* Upload max parallel uploads
|
||||
*/
|
||||
UPLOAD_MAX_PARALLEL_UPLOADS("attachment_upload_max_parallel_uploads", Integer.class, "3"),
|
||||
|
||||
/**
|
||||
* Upload max files
|
||||
*/
|
||||
UPLOAD_MAX_FILES("attachment_upload_max_files", Integer.class, "50"),
|
||||
|
||||
/**
|
||||
* attachment_type
|
||||
*/
|
||||
ATTACHMENT_TYPE("attachment_type", AttachmentType.class, AttachmentType.LOCAL.name());
|
||||
|
||||
private final String value;
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
package run.halo.app.model.properties;
|
||||
|
||||
/**
|
||||
* BaiDuYun properties.
|
||||
*
|
||||
* @author wangya
|
||||
* @author ryanwang
|
||||
* @date 2019-07-19
|
||||
*/
|
||||
public enum BaiDuYunProperties implements PropertyEnum {
|
||||
|
||||
/**
|
||||
* Baidu yun bos domain.
|
||||
*/
|
||||
BOS_DOMAIN("bos_baiduyun_domain",String.class,""),
|
||||
|
||||
/**
|
||||
* Baidu yun bos endpoint.
|
||||
*/
|
||||
BOS_ENDPOINT("bos_baiduyun_endpoint", String.class, ""),
|
||||
|
||||
/**
|
||||
* Baidu yun bos bucket name.
|
||||
*/
|
||||
BOS_BUCKET_NAME("bos_baiduyun_bucket_name", String.class, ""),
|
||||
|
||||
/**
|
||||
* Baidu yun bos access key.
|
||||
*/
|
||||
BOS_ACCESS_KEY("bos_baiduyun_access_key", String.class, ""),
|
||||
|
||||
/**
|
||||
* Baidu yun bos secret key.
|
||||
*/
|
||||
BOS_SECRET_KEY("bos_baiduyun_secret_key", String.class, ""),
|
||||
|
||||
/**
|
||||
* Baidu yun bos style rule.
|
||||
*/
|
||||
BOS_STYLE_RULE("bos_baiduyun_style_rule", String.class, ""),
|
||||
|
||||
/**
|
||||
* Baidu yun bos thumbnail style rule.
|
||||
*/
|
||||
BOS_THUMBNAIL_STYLE_RULE("bos_baiduyun_thumbnail_style_rule", String.class, "");
|
||||
|
||||
private final String value;
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
private final String defaultValue;
|
||||
|
||||
BaiDuYunProperties(String value, Class<?> type, String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
if (!PropertyEnum.isSupportedType(type)) {
|
||||
throw new IllegalArgumentException("Unsupported blog property type: " + type);
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -23,7 +23,9 @@ public enum CommentProperties implements PropertyEnum {
|
|||
|
||||
PAGE_SIZE("comment_page_size", Integer.class, "10"),
|
||||
|
||||
CONTENT_PLACEHOLDER("comment_content_placeholder", String.class, "");
|
||||
CONTENT_PLACEHOLDER("comment_content_placeholder", String.class, ""),
|
||||
|
||||
INTERNAL_PLUGIN_JS("comment_internal_plugin_js", String.class, "//cdn.jsdelivr.net/gh/halo-dev/halo-comment@latest/dist/halo-comment.min.js");
|
||||
|
||||
private final String value;
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ package run.halo.app.model.properties;
|
|||
*/
|
||||
public enum OtherProperties implements PropertyEnum {
|
||||
|
||||
CDN_DOMAIN("blog_cdn_domain", String.class, ""),
|
||||
|
||||
CUSTOM_HEAD("blog_custom_head", String.class, ""),
|
||||
|
||||
STATISTICS_CODE("blog_statistics_code", String.class, "");
|
||||
|
|
|
@ -12,7 +12,9 @@ public enum PostProperties implements PropertyEnum {
|
|||
|
||||
RSS_PAGE_SIZE("rss_page_size", Integer.class, "20"),
|
||||
|
||||
INDEX_PAGE_SIZE("post_index_page_size", Integer.class, "10");
|
||||
INDEX_PAGE_SIZE("post_index_page_size", Integer.class, "10"),
|
||||
|
||||
INDEX_SORT("post_index_sort",String.class,"createTime");
|
||||
|
||||
private final String value;
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ public interface PropertyEnum extends ValueEnum<String> {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> T convertTo(@NonNull String value, @NonNull Class<T> type) {
|
||||
Assert.hasText(value, "Value must not be null");
|
||||
Assert.notNull(value, "Value must not be null");
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
|
||||
if (type.isAssignableFrom(String.class)) {
|
||||
|
@ -57,7 +57,7 @@ public interface PropertyEnum extends ValueEnum<String> {
|
|||
}
|
||||
|
||||
if (type.isAssignableFrom(Double.class)) {
|
||||
return (T) Byte.valueOf(value);
|
||||
return (T) Double.valueOf(value);
|
||||
}
|
||||
|
||||
if (type.isAssignableFrom(Float.class)) {
|
||||
|
|
|
@ -4,6 +4,7 @@ package run.halo.app.model.properties;
|
|||
* Qi niu yun properties.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 3/26/19
|
||||
*/
|
||||
public enum QnYunProperties implements PropertyEnum {
|
||||
|
@ -18,7 +19,9 @@ public enum QnYunProperties implements PropertyEnum {
|
|||
|
||||
OSS_BUCKET("oss_qiniu_bucket", String.class, ""),
|
||||
|
||||
OSS_STYLE_RULE("oss_qiniu_style_rule", String.class, "");
|
||||
OSS_STYLE_RULE("oss_qiniu_style_rule", String.class, ""),
|
||||
|
||||
OSS_THUMBNAIL_STYLE_RULE("oss_qiniu_thumbnail_style_rule", String.class, "");
|
||||
|
||||
private final String value;
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package run.halo.app.model.properties;
|
||||
|
||||
/**
|
||||
* Base meta entity.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @author ikaisec
|
||||
* @date 2019-08-04
|
||||
*/
|
||||
public enum SmmsProperties implements PropertyEnum {
|
||||
|
||||
/**
|
||||
* SM.MS personal api secret token
|
||||
*/
|
||||
SMMS_API_SECRET_TOKEN("smms_api_secret_token", String.class, "");
|
||||
|
||||
private final String value;
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
private final String defaultValue;
|
||||
|
||||
SmmsProperties(String value, Class<?> type, String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
if (!PropertyEnum.isSupportedType(type)) {
|
||||
throw new IllegalArgumentException("Unsupported blog property type: " + type);
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package run.halo.app.model.properties;
|
||||
|
||||
/**
|
||||
* TencentYun properties.
|
||||
*
|
||||
* @author wangya
|
||||
* @author ryanwang
|
||||
* @date 2019-07-25
|
||||
*/
|
||||
public enum TencentYunProperties implements PropertyEnum {
|
||||
|
||||
/**
|
||||
* Tencentyun cos domain.
|
||||
*/
|
||||
COS_DOMAIN("cos_tencentyun_domain",String.class,""),
|
||||
|
||||
/**
|
||||
* Tencentyun cos endpoint.
|
||||
*/
|
||||
COS_REGION("cos_tencentyun_region", String.class, ""),
|
||||
|
||||
/**
|
||||
* Tencentyun cos bucket name.
|
||||
*/
|
||||
COS_BUCKET_NAME("cos_tencentyun_bucket_name", String.class, ""),
|
||||
|
||||
/**
|
||||
* Tencentyun cos secret id.
|
||||
*/
|
||||
COS_SECRET_ID("cos_tencentyun_secret_id", String.class, ""),
|
||||
|
||||
/**
|
||||
* Tencentyun cos secret key.
|
||||
*/
|
||||
COS_SECRET_KEY("cos_tencentyun_secret_key", String.class, "");
|
||||
|
||||
private final String value;
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
private final String defaultValue;
|
||||
|
||||
TencentYunProperties(String value, Class<?> type, String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
if (!PropertyEnum.isSupportedType(type)) {
|
||||
throw new IllegalArgumentException("Unsupported blog property type: " + type);
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -4,21 +4,45 @@ package run.halo.app.model.properties;
|
|||
* You pai yun properties.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 3/27/19
|
||||
*/
|
||||
public enum UpYunProperties implements PropertyEnum {
|
||||
|
||||
/**
|
||||
* upyun oss source
|
||||
*/
|
||||
OSS_SOURCE("oss_upyun_source", String.class, ""),
|
||||
|
||||
/**
|
||||
* upyun oss password
|
||||
*/
|
||||
OSS_PASSWORD("oss_upyun_password", String.class, ""),
|
||||
|
||||
/**
|
||||
* upyun oss bucket
|
||||
*/
|
||||
OSS_BUCKET("oss_upyun_bucket", String.class, ""),
|
||||
|
||||
/**
|
||||
* upyun oss domain
|
||||
*/
|
||||
OSS_DOMAIN("oss_upyun_domain", String.class, ""),
|
||||
|
||||
/**
|
||||
* upyun oss operator
|
||||
*/
|
||||
OSS_OPERATOR("oss_upyun_operator", String.class, ""),
|
||||
|
||||
OSS_STYLE_RULE("oss_upyun_style_rule", String.class, "");
|
||||
/**
|
||||
* upyun oss style rule
|
||||
*/
|
||||
OSS_STYLE_RULE("oss_upyun_style_rule", String.class, ""),
|
||||
|
||||
/**
|
||||
* upyun oss thumbnail style rule
|
||||
*/
|
||||
OSS_THUMBNAIL_STYLE_RULE("oss_upyun_thumbnail_style_rule",String.class,"");
|
||||
|
||||
private final String defaultValue;
|
||||
private String value;
|
||||
|
|
|
@ -23,6 +23,7 @@ public class BaseCommentWithParentVO extends BaseCommentDTO implements Cloneable
|
|||
*/
|
||||
private BaseCommentWithParentVO parent;
|
||||
|
||||
@Override
|
||||
public BaseCommentWithParentVO clone() {
|
||||
try {
|
||||
return (BaseCommentWithParentVO) super.clone();
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package run.halo.app.model.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import run.halo.app.model.dto.MenuDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Menu team vo.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date : 2019/8/28
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
public class MenuTeamVO {
|
||||
|
||||
private String team;
|
||||
|
||||
private List<MenuDTO> menus;
|
||||
}
|
|
@ -2,8 +2,14 @@ package run.halo.app.model.vo;
|
|||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import run.halo.app.model.dto.CategoryDTO;
|
||||
import run.halo.app.model.dto.TagDTO;
|
||||
import run.halo.app.model.dto.post.BasePostDetailDTO;
|
||||
import run.halo.app.model.entity.Category;
|
||||
import run.halo.app.model.entity.Tag;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -12,11 +18,17 @@ import java.util.Set;
|
|||
* @author johnniang
|
||||
* @date 3/21/19
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PostDetailVO extends BasePostDetailDTO {
|
||||
|
||||
private Set<Integer> tagIds;
|
||||
|
||||
private List<TagDTO> tags;
|
||||
|
||||
private Set<Integer> categoryIds;
|
||||
|
||||
private List<CategoryDTO> categories;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.List;
|
|||
* Attachment repository
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 2019-04-03
|
||||
*/
|
||||
public interface AttachmentRepository extends BaseRepository<Attachment, Integer>, JpaSpecificationExecutor<Attachment> {
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.springframework.lang.NonNull;
|
|||
import run.halo.app.model.entity.Category;
|
||||
import run.halo.app.repository.base.BaseRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
|
@ -44,4 +45,11 @@ public interface CategoryRepository extends BaseRepository<Category, Integer> {
|
|||
* @return Optional of Category
|
||||
*/
|
||||
Optional<Category> getByName(@NonNull String name);
|
||||
|
||||
/**
|
||||
* List categories by parent id.
|
||||
* @param id parent id.
|
||||
* @return list of category
|
||||
*/
|
||||
List<Category> findByParentId(@NonNull Integer id);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
package run.halo.app.repository;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.lang.NonNull;
|
||||
import run.halo.app.model.entity.Journal;
|
||||
import run.halo.app.model.enums.JournalType;
|
||||
import run.halo.app.repository.base.BaseRepository;
|
||||
|
||||
/**
|
||||
* Journal repository.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 3/22/19
|
||||
* @author ryanwang
|
||||
* @date 2019-03-22
|
||||
*/
|
||||
public interface JournalRepository extends BaseRepository<Journal, Integer>, JpaSpecificationExecutor<Journal> {
|
||||
|
||||
/**
|
||||
* Finds journals by type and pageable.
|
||||
*
|
||||
* @param type journal type must not be null
|
||||
* @param pageable page info must not be null
|
||||
* @return a page of journal
|
||||
*/
|
||||
@NonNull
|
||||
Page<Journal> findAllByType(@NonNull JournalType type, @NonNull Pageable pageable);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue