mirror of https://github.com/halo-dev/halo
Enhance migration
parent
0ded87246a
commit
c0b3f89727
|
@ -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(
|
||||
|
|
|
@ -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) {
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue