Enhance migration

pull/146/head
johnniang 2019-04-28 20:07:18 +08:00
parent 0ded87246a
commit c0b3f89727
13 changed files with 118 additions and 63 deletions

View File

@ -121,7 +121,11 @@ public class HaloConfiguration {
failureHandler.setObjectMapper(objectMapper);
// Config the admin filter
adminAuthenticationFilter.addExcludeUrlPatterns("/api/admin/login");
adminAuthenticationFilter.addExcludeUrlPatterns(
"/api/admin/login",
"/api/admin/installations",
"/api/admin/recoveries/migration/*"
);
adminAuthenticationFilter.addTryAuthUrlMethodPattern("/api/admin/comments", HttpMethod.POST.name());
adminAuthenticationFilter.addTryAuthUrlMethodPattern("/api/comments", HttpMethod.POST.name());
adminAuthenticationFilter.setFailureHandler(

View File

@ -1,21 +1,23 @@
package run.halo.app.controller.content.api;
package run.halo.app.controller.admin.api;
import freemarker.template.Configuration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import run.halo.app.event.logger.LogEvent;
import run.halo.app.exception.BadRequestException;
import run.halo.app.model.entity.*;
import run.halo.app.model.enums.AttachmentType;
import run.halo.app.model.enums.LogType;
import run.halo.app.model.params.CategoryParam;
import run.halo.app.model.params.InstallParam;
import run.halo.app.model.properties.*;
import run.halo.app.model.support.BaseResponse;
import run.halo.app.service.*;
import run.halo.app.utils.ValidationUtils;
import javax.validation.Valid;
import java.util.HashMap;
@ -31,7 +33,7 @@ import static run.halo.app.model.support.HaloConst.DEFAULT_THEME_ID;
*/
@Slf4j
@Controller
@RequestMapping("/installations")
@RequestMapping("/api/admin/installations")
public class InstallController {
private final UserService userService;
@ -70,7 +72,7 @@ public class InstallController {
@PostMapping
@ResponseBody
public BaseResponse<?> installBlog(@Valid InstallParam installParam) {
public BaseResponse<String> installBlog(@RequestBody @Valid InstallParam installParam) {
// TODO Install blog.
// Check is installed
boolean isInstalled = Boolean.parseBoolean(optionService.getByProperty(PrimaryProperties.IS_INSTALLED).orElse(Boolean.FALSE.toString()));
@ -136,14 +138,17 @@ public class InstallController {
return null;
}
@NonNull
private Category createDefaultCategory() {
Category category = new Category();
CategoryParam category = new CategoryParam();
// TODO Multi level category
category.setName("未分类");
category.setSlugName("default");
category.setDescription("未分类");
return categoryService.create(category);
ValidationUtils.validate(category);
return categoryService.create(category.convertTo());
}
private User createDefaultUser(InstallParam installParam) {

View File

@ -7,6 +7,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import run.halo.app.exception.BadRequestException;
import run.halo.app.model.properties.PrimaryProperties;
import run.halo.app.service.OptionService;
import run.halo.app.service.RecoveryService;
/**
@ -21,15 +24,23 @@ public class RecoveryController {
private final RecoveryService recoveryService;
public RecoveryController(RecoveryService recoveryService) {
private final OptionService optionService;
public RecoveryController(RecoveryService recoveryService,
OptionService optionService) {
this.recoveryService = recoveryService;
this.optionService = optionService;
}
@PostMapping
@ApiOperation("Migrate from halo v0.4.3")
@PostMapping("migrations/v0_4_3")
@ApiOperation("Migrates from halo v0.4.3")
public void migrateFromVersion_0_4_3(
@ApiParam("This file content type should be json")
@RequestPart("file") MultipartFile file) {
if (optionService.getByPropertyOrDefault(PrimaryProperties.IS_INSTALLED, Boolean.class, false)) {
throw new BadRequestException("You cannot migrate after blog installing");
}
recoveryService.migrateFromV0_4_3(file);
}
}

View File

@ -5,6 +5,7 @@ import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import org.springframework.core.io.support.ResourcePatternResolver;
import javax.persistence.*;
@ -54,6 +55,14 @@ public class Category extends BaseEntity {
public void prePersist() {
super.prePersist();
id = null;
if (description == null) {
description = "";
}
if (parentId == null || parentId < 0) {
parentId = 0;
}
}
}

View File

@ -2,6 +2,7 @@ package run.halo.app.model.params;
import run.halo.app.model.dto.base.InputConverter;
import run.halo.app.model.entity.Category;
import run.halo.app.utils.HaloUtils;
import run.halo.app.utils.SlugUtils;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
@ -40,17 +41,17 @@ public class CategoryParam implements InputConverter<Category> {
/**
* Parent category.
*/
private Integer parentId;
private Integer parentId = 0;
@Override
public Category convertTo() {
// Handle default value
if (StringUtils.isBlank(slugName)) {
slugName = SlugUtils.slugify(name);
}
if (parentId == null || parentId < 0) {
parentId = 0;
if (StringUtils.isBlank(slugName)) {
slugName = HaloUtils.initializeUrlIfBlank(slugName);
}
}
return InputConverter.super.convertTo();

View File

@ -19,8 +19,7 @@ public class InstallParam extends UserParam {
/**
* Blog locale.
*/
@NotBlank(message = "Blog locale must not be blank")
private String locale;
private String locale = "zh";
/**
* Blog title.

View File

@ -5,6 +5,8 @@ import run.halo.app.repository.base.BaseRepository;
import org.springframework.lang.NonNull;
import run.halo.app.repository.base.BaseRepository;
import java.util.Optional;
/**
* Menu repository.
*
@ -12,11 +14,7 @@ import run.halo.app.repository.base.BaseRepository;
*/
public interface MenuRepository extends BaseRepository<Menu, Integer> {
/**
* Exists by menu name.
*
* @param name must not be blank
* @return true if exists; false otherwise
*/
boolean existsByName(@NonNull String name);
boolean existsByIdNotAndName(@NonNull Integer id, @NonNull String name);
}

View File

@ -108,22 +108,9 @@ public interface BasePostRepository<POST extends BasePost> extends BaseRepositor
*/
long countByStatus(@NonNull PostStatus status);
/**
* Count by post url.
*
* @param url post url must not be blank
* @return the count
*/
long countByUrl(@NonNull String url);
boolean countByUrl(@NonNull String title);
/**
* Count by not url and post id not in.
*
* @param id post id must not be null
* @param url post url must not be null
* @return the count
*/
long countByIdNotAndUrl(@NonNull Integer id, @NonNull String url);
boolean countByIdNotAndUrl(@NonNull Integer id, @NonNull String title);
/**
* Get post by url

View File

@ -55,7 +55,7 @@ public interface CrudService<DOMAIN, ID> {
* @return List
*/
@NonNull
List<DOMAIN> listAllByIds(@NonNull Collection<ID> ids);
List<DOMAIN> listAllByIds(@Nullable Collection<ID> ids);
/**
* List all by ids and sort
@ -65,7 +65,7 @@ public interface CrudService<DOMAIN, ID> {
* @return List
*/
@NonNull
List<DOMAIN> listAllByIds(@NonNull Collection<ID> ids, @NonNull Sort sort);
List<DOMAIN> listAllByIds(@Nullable Collection<ID> ids, @NonNull Sort sort);
/**
* Fetch by id

View File

@ -190,9 +190,6 @@ public abstract class BasePostServiceImpl<POST extends BasePost> extends Abstrac
public POST createOrUpdateBy(POST post) {
Assert.notNull(post, "Post must not be null");
// Check url
urlMustNotExist(post);
// Render content
post.setFormatContent(MarkdownUtils.renderMarkdown(post.getOriginalContent()));
@ -281,6 +278,22 @@ public abstract class BasePostServiceImpl<POST extends BasePost> extends Abstrac
return new BasePostDetailDTO().convertFrom(post);
}
@Override
public POST create(POST post) {
// Check title
urlMustNotExist(post);
return super.create(post);
}
@Override
public POST update(POST post) {
// Check title
urlMustNotExist(post);
return super.update(post);
}
/**
* Check if the url is exist.
*
@ -288,21 +301,20 @@ public abstract class BasePostServiceImpl<POST extends BasePost> extends Abstrac
*/
protected void urlMustNotExist(@NonNull POST post) {
Assert.notNull(post, "Sheet must not be null");
// TODO Refactor this method with BasePostService
// TODO May refactor these queries
// Get url count
long count;
boolean exist;
if (ServiceUtils.isEmptyId(post.getId())) {
// The sheet will be created
count = basePostRepository.countByUrl(post.getUrl());
exist = basePostRepository.countByUrl(post.getUrl());
} else {
// The sheet will be updated
count = basePostRepository.countByIdNotAndUrl(post.getId(), post.getUrl());
exist = basePostRepository.countByIdNotAndUrl(post.getId(), post.getUrl());
}
if (count > 0) {
throw new AlreadyExistsException("The sheet url has been exist");
if (exist) {
throw new AlreadyExistsException("The post url " + post.getUrl() + " has been exist");
}
}
}

View File

@ -13,6 +13,7 @@ import run.halo.app.model.vo.MenuVO;
import run.halo.app.repository.MenuRepository;
import run.halo.app.service.MenuService;
import run.halo.app.service.base.AbstractCrudService;
import run.halo.app.utils.ServiceUtils;
import java.util.Collections;
import java.util.LinkedList;
@ -46,13 +47,6 @@ public class MenuServiceImpl extends AbstractCrudService<Menu, Integer> implemen
public Menu createBy(MenuParam menuParam) {
Assert.notNull(menuParam, "Menu param must not be null");
// Check the name
boolean exists = menuRepository.existsByName(menuParam.getName());
if (exists) {
throw new AlreadyExistsException("The menu name " + menuParam.getName() + " has already existed").setErrorData(menuParam.getName());
}
// Create an return
return create(menuParam.convertTo());
}
@ -83,11 +77,25 @@ public class MenuServiceImpl extends AbstractCrudService<Menu, Integer> implemen
return topLevelMenu.getChildren();
}
@Override
public Menu create(Menu menu) {
nameMustNotExist(menu);
return super.create(menu);
}
@Override
public Menu update(Menu menu) {
nameMustNotExist(menu);
return super.update(menu);
}
/**
* Concrete menu tree.
*
* @param parentMenu parent menu vo must not be null
* @param menus a list of menu
* @param menus a list of menu
*/
private void concreteTree(MenuVO parentMenu, List<Menu> menus) {
Assert.notNull(parentMenu, "Parent menu must not be null");
@ -148,4 +156,22 @@ public class MenuServiceImpl extends AbstractCrudService<Menu, Integer> implemen
.map(menu -> new MenuDTO().<MenuDTO>convertFrom(menu))
.collect(Collectors.toList());
}
private void nameMustNotExist(@NonNull Menu menu) {
Assert.notNull(menu, "Menu must not be null");
boolean exist = false;
if (ServiceUtils.isEmptyId(menu.getId())) {
// Create action
exist = menuRepository.existsByName(menu.getName());
} else {
// Update action
exist = menuRepository.existsByIdNotAndName(menu.getId(), menu.getName());
}
if (exist) {
throw new AlreadyExistsException("The menu name " + menu.getName() + " already exists");
}
}
}

View File

@ -181,7 +181,7 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
Assert.notNull(post, "Post param must not be null");
// Create or update post
post = createOrUpdateBy(post);
post = super.createOrUpdateBy(post);
// List all tags
List<Tag> tags = tagService.listAllByIds(tagIds);

View File

@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
@ -77,6 +78,7 @@ public class RecoveryServiceImpl implements RecoveryService {
}
@Override
@Async
public void migrateFromV0_4_3(MultipartFile file) {
// TODO Async execution
// Get migration content
@ -198,7 +200,7 @@ public class RecoveryServiceImpl implements RecoveryService {
Post post = BeanUtils.transformFrom(basePost, Post.class);
// Create it
Post createdPost = postService.create(post);
Post createdPost = postService.createOrUpdateBy(post);
Object commentsObject = postMap.get("comments");
// TODO Handle comments
@ -210,6 +212,7 @@ public class RecoveryServiceImpl implements RecoveryService {
try {
// Create comments
// TODO Don't use createInBatch method
List<PostComment> createdPostComments = postCommentService.createInBatch(postComments);
} catch (Exception e) {
log.warn("Failed to create post comments for post with id " + createdPost.getId(), e);
@ -224,7 +227,7 @@ public class RecoveryServiceImpl implements RecoveryService {
Sheet sheet = BeanUtils.transformFrom(basePost, Sheet.class);
// Create it
Sheet createdSheet = sheetService.create(sheet);
Sheet createdSheet = sheetService.createOrUpdateBy(sheet);
Object commentsObject = postMap.get("comments");
// TODO Handle comments