feat: add data export and import api. (#672)

* feat: add data export and import api.

* refactor: data export and import api.
pull/687/head
Ryan Wang 2020-03-17 13:14:15 +08:00 committed by GitHub
parent d6b3d6cb5d
commit 4bc8ad44da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 451 additions and 223 deletions

View File

@ -62,13 +62,16 @@ public class AdminController {
@PostMapping("password/code") @PostMapping("password/code")
@ApiOperation("Sends reset password verify code") @ApiOperation("Sends reset password verify code")
@CacheLock(autoDelete = false)
@DisableOnCondition
public void sendResetCode(@RequestBody @Valid ResetPasswordParam param) { public void sendResetCode(@RequestBody @Valid ResetPasswordParam param) {
adminService.sendResetPasswordCode(param); adminService.sendResetPasswordCode(param);
} }
@PutMapping("password/reset") @PutMapping("password/reset")
@DisableOnCondition
@ApiOperation("Resets password by verify code") @ApiOperation("Resets password by verify code")
@CacheLock(autoDelete = false)
@DisableOnCondition
public void resetPassword(@RequestBody @Valid ResetPasswordParam param) { public void resetPassword(@RequestBody @Valid ResetPasswordParam param) {
adminService.resetPasswordByCode(param); adminService.resetPasswordByCode(param);
} }
@ -95,32 +98,35 @@ public class AdminController {
@PutMapping("halo-admin") @PutMapping("halo-admin")
@ApiOperation("Updates halo-admin manually") @ApiOperation("Updates halo-admin manually")
@Deprecated
public void updateAdmin() { public void updateAdmin() {
adminService.updateAdminAssets(); adminService.updateAdminAssets();
} }
@GetMapping("spring/application.yaml") @GetMapping("spring/application.yaml")
@ApiOperation("Gets application config content") @ApiOperation("Gets application config content")
@DisableOnCondition
public BaseResponse<String> getSpringApplicationConfig() { public BaseResponse<String> getSpringApplicationConfig() {
return BaseResponse.ok(HttpStatus.OK.getReasonPhrase(), adminService.getApplicationConfig()); return BaseResponse.ok(HttpStatus.OK.getReasonPhrase(), adminService.getApplicationConfig());
} }
@PutMapping("spring/application.yaml") @PutMapping("spring/application.yaml")
@DisableOnCondition
@ApiOperation("Updates application config content") @ApiOperation("Updates application config content")
@DisableOnCondition
public void updateSpringApplicationConfig(@RequestParam(name = "content") String content) { public void updateSpringApplicationConfig(@RequestParam(name = "content") String content) {
adminService.updateApplicationConfig(content); adminService.updateApplicationConfig(content);
} }
@PostMapping(value = {"halo/restart", "spring/restart"}) @PostMapping(value = {"halo/restart", "spring/restart"})
@DisableOnCondition
@ApiOperation("Restarts halo server") @ApiOperation("Restarts halo server")
@DisableOnCondition
public void restartApplication() { public void restartApplication() {
Application.restart(); Application.restart();
} }
@GetMapping(value = "halo/logfile") @GetMapping(value = "halo/logfile")
@ApiOperation("Gets halo log file content") @ApiOperation("Gets halo log file content")
@DisableOnCondition
public BaseResponse<String> getLogFiles(@RequestParam("lines") Long lines) { public BaseResponse<String> getLogFiles(@RequestParam("lines") Long lines) {
return BaseResponse.ok(HttpStatus.OK.getReasonPhrase(), adminService.getLogFiles(lines)); return BaseResponse.ok(HttpStatus.OK.getReasonPhrase(), adminService.getLogFiles(lines));
} }

View File

@ -1,26 +1,20 @@
package run.halo.app.controller.admin.api; package run.halo.app.controller.admin.api;
import cn.hutool.core.util.ZipUtil; import cn.hutool.core.date.DateUtil;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.json.JSONObject;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import run.halo.app.exception.FileOperationException; import run.halo.app.model.annotation.DisableOnCondition;
import run.halo.app.model.dto.BackupDTO; import run.halo.app.model.dto.BackupDTO;
import run.halo.app.model.dto.post.BasePostDetailDTO; import run.halo.app.model.dto.post.BasePostDetailDTO;
import run.halo.app.service.BackupService; import run.halo.app.service.BackupService;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -44,6 +38,7 @@ public class BackupController {
@PostMapping("halo") @PostMapping("halo")
@ApiOperation("Backups halo") @ApiOperation("Backups halo")
@DisableOnCondition
public BackupDTO backupHalo() { public BackupDTO backupHalo() {
return backupService.zipWorkDirectory(); return backupService.zipWorkDirectory();
} }
@ -56,6 +51,7 @@ public class BackupController {
@GetMapping("halo/{fileName:.+}") @GetMapping("halo/{fileName:.+}")
@ApiOperation("Downloads backup file") @ApiOperation("Downloads backup file")
@DisableOnCondition
public ResponseEntity<Resource> downloadBackup(@PathVariable("fileName") String fileName, HttpServletRequest request) { public ResponseEntity<Resource> downloadBackup(@PathVariable("fileName") String fileName, HttpServletRequest request) {
log.info("Try to download backup file: [{}]", fileName); log.info("Try to download backup file: [{}]", fileName);
@ -79,6 +75,7 @@ public class BackupController {
@DeleteMapping("halo") @DeleteMapping("halo")
@ApiOperation("Deletes a backup") @ApiOperation("Deletes a backup")
@DisableOnCondition
public void deleteBackup(@RequestParam("filename") String filename) { public void deleteBackup(@RequestParam("filename") String filename) {
backupService.deleteHaloBackup(filename); backupService.deleteHaloBackup(filename);
} }
@ -89,48 +86,17 @@ public class BackupController {
return backupService.importMarkdown(file); return backupService.importMarkdown(file);
} }
@GetMapping("export/hexo") @GetMapping("export/data")
@ApiOperation("export hexo markdown") @DisableOnCondition
public void exportMarkdowns(HttpServletResponse response) { public ResponseEntity<String> exportData() {
final String tmpDir = System.getProperty("java.io.tmpdir");
final String date = DateFormatUtils.format(new Date(), "yyyyMMddHHmmss");
String localFilePath = tmpDir + File.separator + "halo-markdown-" + date;
log.trace(localFilePath);
final File localFile = new File(localFilePath);
final File postDir = new File(localFilePath + File.separator + "posts");
final File passwordDir = new File(localFilePath + File.separator + "passwords");
final File draftDir = new File(localFilePath + File.separator + "drafts");
try {
if (!postDir.mkdirs()) {
throw new Exception("Create dir [" + postDir.getPath() + "] failed");
}
if (!passwordDir.mkdirs()) {
throw new Exception("Create dir [" + passwordDir.getPath() + "] failed");
}
if (!draftDir.mkdirs()) {
throw new Exception("Create dir [" + draftDir.getPath() + "] failed");
}
final JSONObject result = backupService.exportHexoMDs();
final List<JSONObject> posts = (List<JSONObject>) result.opt("posts");
backupService.exportHexoMd(posts, postDir.getPath());
final List<JSONObject> passwords = (List<JSONObject>) result.opt("passwords");
backupService.exportHexoMd(passwords, passwordDir.getPath());
final List<JSONObject> drafts = (List<JSONObject>) result.opt("drafts");
backupService.exportHexoMd(drafts, draftDir.getPath());
final File zipFile = ZipUtil.zip(localFile); String contentType = "application/octet-stream;charset=UTF-8";
byte[] zipData;
try (final FileInputStream inputStream = new FileInputStream(zipFile)) {
zipData = IOUtils.toByteArray(inputStream);
response.setContentType("application/zip");
final String fileName = "halo-markdown-" + date + ".zip";
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
}
response.getOutputStream().write(zipData); String filename = "halo-data-" + DateUtil.format(new Date(), "yyyy-MM-dd-HH-mm-ss.json");
} catch (final Exception e) {
log.error("Export failed", e); return ResponseEntity.ok()
throw new FileOperationException("文章导出失败", e); .contentType(MediaType.parseMediaType(contentType))
} .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
.body(backupService.exportData().toJSONString());
} }
} }

View File

@ -23,18 +23,18 @@ public class DataProcessController {
this.themeSettingService = themeSettingService; this.themeSettingService = themeSettingService;
} }
@PutMapping("url/replace") // @PutMapping("url/replace")
@ApiOperation("Replace url in all table.") // @ApiOperation("Replace url in all table.")
public void replaceUrl(@RequestParam("oldUrl") String oldUrl, // public void replaceUrl(@RequestParam("oldUrl") String oldUrl,
@RequestParam("newUrl") String newUrl) { // @RequestParam("newUrl") String newUrl) {
dataProcessService.replaceAllUrl(oldUrl, newUrl); // dataProcessService.replaceAllUrl(oldUrl, newUrl);
} // }
//
@DeleteMapping("themes/settings/inactivated") // @DeleteMapping("themes/settings/inactivated")
@ApiOperation("Delete inactivated theme settings.") // @ApiOperation("Delete inactivated theme settings.")
public void deleteInactivatedThemeSettings() { // public void deleteInactivatedThemeSettings() {
themeSettingService.deleteInactivated(); // themeSettingService.deleteInactivated();
} // }
@DeleteMapping("tags/unused") @DeleteMapping("tags/unused")
@ApiOperation("Delete unused tags") @ApiOperation("Delete unused tags")

View File

@ -164,6 +164,12 @@ public class InstallController {
return null; return null;
} }
long commentCount = postCommentService.count();
if (commentCount > 0) {
return null;
}
PostComment comment = new PostComment(); PostComment comment = new PostComment();
comment.setAuthor("Halo"); comment.setAuthor("Halo");
comment.setAuthorUrl("https://halo.run"); comment.setAuthorUrl("https://halo.run");
@ -175,7 +181,9 @@ public class InstallController {
@Nullable @Nullable
private PostDetailVO createDefaultPostIfAbsent(@Nullable Category category) { private PostDetailVO createDefaultPostIfAbsent(@Nullable Category category) {
long publishedCount = postService.countByStatus(PostStatus.PUBLISHED); long publishedCount = postService.countByStatus(PostStatus.PUBLISHED);
if (publishedCount > 0) { if (publishedCount > 0) {
return null; return null;
} }
@ -210,6 +218,11 @@ public class InstallController {
@Nullable @Nullable
private void createDefaultSheet() { private void createDefaultSheet() {
long publishedCount = sheetService.countByStatus(PostStatus.PUBLISHED);
if (publishedCount > 0) {
return;
}
SheetParam sheetParam = new SheetParam(); SheetParam sheetParam = new SheetParam();
sheetParam.setSlug("about"); sheetParam.setSlug("about");
sheetParam.setTitle("关于页面"); sheetParam.setTitle("关于页面");
@ -261,10 +274,19 @@ public class InstallController {
properties.put(PrimaryProperties.IS_INSTALLED, Boolean.TRUE.toString()); properties.put(PrimaryProperties.IS_INSTALLED, Boolean.TRUE.toString());
properties.put(BlogProperties.BLOG_LOCALE, installParam.getLocale()); properties.put(BlogProperties.BLOG_LOCALE, installParam.getLocale());
properties.put(BlogProperties.BLOG_TITLE, installParam.getTitle()); properties.put(BlogProperties.BLOG_TITLE, installParam.getTitle());
properties.put(BlogProperties.BLOG_URL, StringUtils.isBlank(installParam.getUrl()) ? properties.put(BlogProperties.BLOG_URL, StringUtils.isBlank(installParam.getUrl()) ? optionService.getBlogBaseUrl() : installParam.getUrl());
optionService.getBlogBaseUrl() : installParam.getUrl());
properties.put(PrimaryProperties.BIRTHDAY, String.valueOf(System.currentTimeMillis())); Long birthday = optionService.getByPropertyOrDefault(PrimaryProperties.BIRTHDAY, Long.class, 0L);
properties.put(OtherProperties.GLOBAL_ABSOLUTE_PATH_ENABLED, Boolean.FALSE.toString());
if (birthday.equals(0L)) {
properties.put(PrimaryProperties.BIRTHDAY, String.valueOf(System.currentTimeMillis()));
}
Boolean globalAbsolutePathEnabled = optionService.getByPropertyOrDefault(OtherProperties.GLOBAL_ABSOLUTE_PATH_ENABLED, Boolean.class, null);
if (globalAbsolutePathEnabled == null) {
properties.put(OtherProperties.GLOBAL_ABSOLUTE_PATH_ENABLED, Boolean.FALSE.toString());
}
// Create properties // Create properties
optionService.saveProperties(properties); optionService.saveProperties(properties);

View File

@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import run.halo.app.mail.MailService; import run.halo.app.mail.MailService;
import run.halo.app.model.annotation.DisableOnCondition;
import run.halo.app.model.params.MailParam; import run.halo.app.model.params.MailParam;
import run.halo.app.model.support.BaseResponse; import run.halo.app.model.support.BaseResponse;
@ -29,6 +30,7 @@ public class MailController {
@PostMapping("test") @PostMapping("test")
@ApiOperation("Tests the SMTP service") @ApiOperation("Tests the SMTP service")
@DisableOnCondition
public BaseResponse<String> testMail(@Valid @RequestBody MailParam mailParam) { public BaseResponse<String> testMail(@Valid @RequestBody MailParam mailParam) {
mailService.sendTextMail(mailParam.getTo(), mailParam.getSubject(), mailParam.getContent()); mailService.sendTextMail(mailParam.getTo(), mailParam.getSubject(), mailParam.getContent());
return BaseResponse.ok("已发送,请查收。若确认没有收到邮件,请检查服务器日志"); return BaseResponse.ok("已发送,请查收。若确认没有收到邮件,请检查服务器日志");
@ -36,6 +38,7 @@ public class MailController {
@PostMapping("test/connection") @PostMapping("test/connection")
@ApiOperation("Test connection with email server") @ApiOperation("Test connection with email server")
@DisableOnCondition
public BaseResponse<String> testConnection() { public BaseResponse<String> testConnection() {
mailService.testConnection(); mailService.testConnection();
return BaseResponse.ok("您和邮箱服务器的连接通畅"); return BaseResponse.ok("您和邮箱服务器的连接通畅");

View File

@ -6,8 +6,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import run.halo.app.exception.BadRequestException;
import run.halo.app.model.enums.MigrateType; import run.halo.app.model.enums.MigrateType;
import run.halo.app.model.properties.PrimaryProperties;
import run.halo.app.service.MigrateService; import run.halo.app.service.MigrateService;
import run.halo.app.service.OptionService;
/** /**
* Migrate controller * Migrate controller
@ -21,25 +24,32 @@ public class MigrateController {
private final MigrateService migrateService; private final MigrateService migrateService;
public MigrateController(MigrateService migrateService) { private final OptionService optionService;
public MigrateController(MigrateService migrateService,
OptionService optionService) {
this.migrateService = migrateService; this.migrateService = migrateService;
this.optionService = optionService;
} }
@PostMapping("halo_v0_4_4") @PostMapping("halo")
@ApiOperation("Migrate from Halo 0.4.4") @ApiOperation("Migrate from Halo")
public void migrateHaloOldVersion(@RequestPart("file") MultipartFile file) { public void migrateHalo(@RequestPart("file") MultipartFile file) {
migrateService.migrate(file, MigrateType.OLD_VERSION); if (optionService.getByPropertyOrDefault(PrimaryProperties.IS_INSTALLED, Boolean.class, false)) {
throw new BadRequestException("无法在博客初始化完成之后迁移数据");
}
migrateService.migrate(file, MigrateType.HALO);
} }
@PostMapping("wordpress") // @PostMapping("wordpress")
@ApiOperation("Migrate from WordPress") // @ApiOperation("Migrate from WordPress")
public void migrateWordPress(@RequestPart("file") MultipartFile file) { // public void migrateWordPress(@RequestPart("file") MultipartFile file) {
migrateService.migrate(file, MigrateType.WORDPRESS); // migrateService.migrate(file, MigrateType.WORDPRESS);
} // }
//
@PostMapping("cnblogs") // @PostMapping("cnblogs")
@ApiOperation("Migrate from cnblogs") // @ApiOperation("Migrate from cnblogs")
public void migrateCnBlogs(@RequestPart("file") MultipartFile file) { // public void migrateCnBlogs(@RequestPart("file") MultipartFile file) {
migrateService.migrate(file, MigrateType.CNBLOGS); // migrateService.migrate(file, MigrateType.CNBLOGS);
} // }
} }

View File

@ -44,8 +44,8 @@ public class OptionController {
} }
@PostMapping("saving") @PostMapping("saving")
@DisableOnCondition
@ApiOperation("Saves options") @ApiOperation("Saves options")
@DisableOnCondition
public void saveOptions(@Valid @RequestBody List<OptionParam> optionParams) { public void saveOptions(@Valid @RequestBody List<OptionParam> optionParams) {
optionService.save(optionParams); optionService.save(optionParams);
} }
@ -78,30 +78,30 @@ public class OptionController {
} }
@PostMapping @PostMapping
@DisableOnCondition
@ApiOperation("Creates option") @ApiOperation("Creates option")
@DisableOnCondition
public void createBy(@RequestBody @Valid OptionParam optionParam) { public void createBy(@RequestBody @Valid OptionParam optionParam) {
optionService.save(optionParam); optionService.save(optionParam);
} }
@PutMapping("{optionId:\\d+}") @PutMapping("{optionId:\\d+}")
@DisableOnCondition
@ApiOperation("Updates option") @ApiOperation("Updates option")
@DisableOnCondition
public void updateBy(@PathVariable("optionId") Integer optionId, public void updateBy(@PathVariable("optionId") Integer optionId,
@RequestBody @Valid OptionParam optionParam) { @RequestBody @Valid OptionParam optionParam) {
optionService.update(optionId, optionParam); optionService.update(optionId, optionParam);
} }
@DeleteMapping("{optionId:\\d+}") @DeleteMapping("{optionId:\\d+}")
@DisableOnCondition
@ApiOperation("Deletes option") @ApiOperation("Deletes option")
@DisableOnCondition
public void deletePermanently(@PathVariable("optionId") Integer optionId) { public void deletePermanently(@PathVariable("optionId") Integer optionId) {
optionService.removePermanently(optionId); optionService.removePermanently(optionId);
} }
@PostMapping("map_view/saving") @PostMapping("map_view/saving")
@DisableOnCondition
@ApiOperation("Saves options by option map") @ApiOperation("Saves options by option map")
@DisableOnCondition
public void saveOptionsWithMapView(@RequestBody Map<String, Object> optionMap) { public void saveOptionsWithMapView(@RequestBody Map<String, Object> optionMap) {
optionService.save(optionMap); optionService.save(optionMap);
} }

View File

@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import run.halo.app.handler.theme.config.support.Group; import run.halo.app.handler.theme.config.support.Group;
import run.halo.app.handler.theme.config.support.ThemeProperty; import run.halo.app.handler.theme.config.support.ThemeProperty;
import run.halo.app.model.annotation.DisableOnCondition;
import run.halo.app.model.params.ThemeContentParam; import run.halo.app.model.params.ThemeContentParam;
import run.halo.app.model.support.BaseResponse; import run.halo.app.model.support.BaseResponse;
import run.halo.app.model.support.ThemeFile; import run.halo.app.model.support.ThemeFile;
@ -75,12 +76,14 @@ public class ThemeController {
@PutMapping("files/content") @PutMapping("files/content")
@ApiOperation("Updates template content") @ApiOperation("Updates template content")
@DisableOnCondition
public void updateContentBy(@RequestBody ThemeContentParam param) { public void updateContentBy(@RequestBody ThemeContentParam param) {
themeService.saveTemplateContent(param.getPath(), param.getContent()); themeService.saveTemplateContent(param.getPath(), param.getContent());
} }
@PutMapping("{themeId}/files/content") @PutMapping("{themeId}/files/content")
@ApiOperation("Updates template content by theme id") @ApiOperation("Updates template content by theme id")
@DisableOnCondition
public void updateContentBy(@PathVariable("themeId") String themeId, public void updateContentBy(@PathVariable("themeId") String themeId,
@RequestBody ThemeContentParam param) { @RequestBody ThemeContentParam param) {
themeService.saveTemplateContent(themeId, param.getPath(), param.getContent()); themeService.saveTemplateContent(themeId, param.getPath(), param.getContent());
@ -149,6 +152,7 @@ public class ThemeController {
@DeleteMapping("{themeId}") @DeleteMapping("{themeId}")
@ApiOperation("Deletes a theme") @ApiOperation("Deletes a theme")
@DisableOnCondition
public void deleteBy(@PathVariable("themeId") String themeId) { public void deleteBy(@PathVariable("themeId") String themeId) {
themeService.deleteTheme(themeId); themeService.deleteTheme(themeId);
} }
@ -187,7 +191,7 @@ public class ThemeController {
@GetMapping(value = "activation/template/exists") @GetMapping(value = "activation/template/exists")
@ApiOperation("Determines if template exists") @ApiOperation("Determines if template exists")
public BaseResponse exists(@RequestParam(value = "template") String template) { public BaseResponse<Boolean> exists(@RequestParam(value = "template") String template) {
return BaseResponse.ok(themeService.templateExists(template)); return BaseResponse.ok(themeService.templateExists(template));
} }
} }

View File

@ -38,6 +38,7 @@ public class UserController {
@PutMapping("profiles") @PutMapping("profiles")
@ApiOperation("Updates user profile") @ApiOperation("Updates user profile")
@DisableOnCondition
public UserDTO updateProfile(@RequestBody UserParam userParam, User user) { public UserDTO updateProfile(@RequestBody UserParam userParam, User user) {
// Validate the user param // Validate the user param
ValidationUtils.validate(userParam, UpdateCheck.class); ValidationUtils.validate(userParam, UpdateCheck.class);
@ -50,9 +51,9 @@ public class UserController {
} }
@PutMapping("profiles/password") @PutMapping("profiles/password")
@DisableOnCondition
@ApiOperation("Updates user's password") @ApiOperation("Updates user's password")
public BaseResponse updatePassword(@RequestBody @Valid PasswordParam passwordParam, User user) { @DisableOnCondition
public BaseResponse<String> updatePassword(@RequestBody @Valid PasswordParam passwordParam, User user) {
userService.updatePassword(passwordParam.getOldPassword(), passwordParam.getNewPassword(), user.getId()); userService.updatePassword(passwordParam.getOldPassword(), passwordParam.getNewPassword(), user.getId());
return BaseResponse.ok("密码修改成功"); return BaseResponse.ok("密码修改成功");
} }

View File

@ -2,6 +2,7 @@ package run.halo.app.exception;
/** /**
* repeat type exception * repeat type exception
*
* @author bestsort * @author bestsort
* @date 3/13/20 5:03 PM * @date 3/13/20 5:03 PM
*/ */
@ -9,6 +10,7 @@ public class RepeatTypeException extends ServiceException {
public RepeatTypeException(String message) { public RepeatTypeException(String message) {
super(message); super(message);
} }
public RepeatTypeException(String message, Throwable cause) { public RepeatTypeException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }

View File

@ -75,6 +75,7 @@ public interface FileHandler {
/** /**
* Get attachment type is supported. * Get attachment type is supported.
*
* @return attachment type * @return attachment type
*/ */
AttachmentType getAttachmentType(); AttachmentType getAttachmentType();

View File

@ -61,7 +61,7 @@ public class FileHandlers {
public void delete(@NonNull Attachment attachment) { public void delete(@NonNull Attachment attachment) {
Assert.notNull(attachment, "Attachment must not be null"); Assert.notNull(attachment, "Attachment must not be null");
getSupportedType(attachment.getType()) getSupportedType(attachment.getType())
.delete(attachment.getFileKey()); .delete(attachment.getFileKey());
} }
/** /**

View File

@ -0,0 +1,36 @@
package run.halo.app.handler.migrate;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import run.halo.app.model.enums.MigrateType;
import run.halo.app.service.BackupService;
import java.io.IOException;
/**
* @author ryanwang
* @date 2020-03-14
*/
@Component
public class HaloMigrateHandler implements MigrateHandler {
private final BackupService backupService;
public HaloMigrateHandler(BackupService backupService) {
this.backupService = backupService;
}
@Override
public void migrate(MultipartFile file) {
try {
backupService.importData(file);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean supportType(MigrateType type) {
return MigrateType.HALO.equals(type);
}
}

View File

@ -688,6 +688,6 @@ public class OldVersionMigrateHandler implements MigrateHandler {
@Override @Override
public boolean supportType(MigrateType type) { public boolean supportType(MigrateType type) {
return MigrateType.OLD_VERSION.equals(type); return false;
} }
} }

View File

@ -55,6 +55,7 @@ public abstract class AbstractMailService implements MailService {
/** /**
* Test connection with email server. * Test connection with email server.
*/ */
@Override
public void testConnection() { public void testConnection() {
JavaMailSender javaMailSender = getMailSender(); JavaMailSender javaMailSender = getMailSender();
if (javaMailSender instanceof JavaMailSenderImpl) { if (javaMailSender instanceof JavaMailSenderImpl) {

View File

@ -4,6 +4,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.GenericGenerator;
import run.halo.app.model.enums.AttachmentType; import run.halo.app.model.enums.AttachmentType;
import javax.persistence.*; import javax.persistence.*;
@ -22,8 +23,8 @@ import javax.persistence.*;
public class Attachment extends BaseEntity { public class Attachment extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@Column(name = "id") @GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Integer id; private Integer id;
/** /**

View File

@ -4,6 +4,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.GenericGenerator;
import run.halo.app.model.enums.CommentStatus; import run.halo.app.model.enums.CommentStatus;
import run.halo.app.utils.ServiceUtils; import run.halo.app.utils.ServiceUtils;
@ -25,7 +26,8 @@ import javax.persistence.*;
public class BaseComment extends BaseEntity { public class BaseComment extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Long id; private Long id;
/** /**

View File

@ -4,6 +4,7 @@ package run.halo.app.model.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*; import javax.persistence.*;
@ -23,7 +24,8 @@ import javax.persistence.*;
public class BaseMeta extends BaseEntity { public class BaseMeta extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Long id; private Long id;
/** /**

View File

@ -4,6 +4,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.GenericGenerator;
import run.halo.app.model.enums.PostEditorType; import run.halo.app.model.enums.PostEditorType;
import run.halo.app.model.enums.PostStatus; import run.halo.app.model.enums.PostStatus;
@ -25,7 +26,8 @@ import java.util.Date;
public class BasePost extends BaseEntity { public class BasePost extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Integer id; private Integer id;
/** /**

View File

@ -4,6 +4,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*; import javax.persistence.*;
@ -24,7 +25,8 @@ import javax.persistence.*;
public class Category extends BaseEntity { public class Category extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Integer id; private Integer id;
/** /**

View File

@ -1,6 +1,7 @@
package run.halo.app.model.entity; package run.halo.app.model.entity;
import lombok.*; import lombok.*;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*; import javax.persistence.*;
import java.util.Date; import java.util.Date;
@ -20,7 +21,8 @@ import java.util.Date;
@NoArgsConstructor @NoArgsConstructor
public class CommentBlackList extends BaseEntity { public class CommentBlackList extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Long id; private Long id;
@Column(name = "ip_address", length = 127, nullable = false) @Column(name = "ip_address", length = 127, nullable = false)

View File

@ -4,6 +4,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.GenericGenerator;
import run.halo.app.model.enums.JournalType; import run.halo.app.model.enums.JournalType;
import javax.persistence.*; import javax.persistence.*;
@ -23,7 +24,8 @@ import javax.persistence.*;
public class Journal extends BaseEntity { public class Journal extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Integer id; private Integer id;
@Column(name = "source_content", nullable = false) @Column(name = "source_content", nullable = false)

View File

@ -4,6 +4,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*; import javax.persistence.*;
@ -21,8 +22,8 @@ import javax.persistence.*;
public class Link extends BaseEntity { public class Link extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@Column(name = "id") @GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Integer id; private Integer id;
/** /**

View File

@ -4,6 +4,7 @@ package run.halo.app.model.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import run.halo.app.model.enums.LogType; import run.halo.app.model.enums.LogType;
import javax.persistence.*; import javax.persistence.*;
@ -21,7 +22,8 @@ import javax.persistence.*;
public class Log extends BaseEntity { public class Log extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Long id; private Long id;
/** /**

View File

@ -4,6 +4,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*; import javax.persistence.*;
@ -21,8 +22,8 @@ import javax.persistence.*;
public class Menu extends BaseEntity { public class Menu extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@Column(name = "id") @GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Integer id; private Integer id;
/** /**

View File

@ -5,6 +5,7 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.GenericGenerator;
import run.halo.app.model.enums.OptionType; import run.halo.app.model.enums.OptionType;
import javax.persistence.*; import javax.persistence.*;
@ -25,7 +26,8 @@ import javax.persistence.*;
public class Option extends BaseEntity { public class Option extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Integer id; private Integer id;
/** /**
@ -44,7 +46,8 @@ public class Option extends BaseEntity {
/** /**
* option value * option value
*/ */
@Column(name = "option_value", length = 1023, nullable = false) @Column(name = "option_value", nullable = false)
@Lob
private String value; private String value;
public Option(String key, String value) { public Option(String key, String value) {

View File

@ -3,6 +3,7 @@ package run.halo.app.model.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*; import javax.persistence.*;
import java.util.Date; import java.util.Date;
@ -21,8 +22,8 @@ import java.util.Date;
public class Photo extends BaseEntity { public class Photo extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@Column(name = "id") @GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Integer id; private Integer id;
/** /**

View File

@ -2,6 +2,7 @@ package run.halo.app.model.entity;
import lombok.Data; import lombok.Data;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*; import javax.persistence.*;
import java.util.Objects; import java.util.Objects;
@ -20,7 +21,8 @@ import java.util.Objects;
public class PostCategory extends BaseEntity { public class PostCategory extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Integer id; private Integer id;
/** /**

View File

@ -2,6 +2,7 @@ package run.halo.app.model.entity;
import lombok.Data; import lombok.Data;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*; import javax.persistence.*;
import java.util.Objects; import java.util.Objects;
@ -21,8 +22,8 @@ import java.util.Objects;
public class PostTag extends BaseEntity { public class PostTag extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@Column(name = "id") @GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Integer id; private Integer id;
/** /**

View File

@ -3,6 +3,7 @@ package run.halo.app.model.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*; import javax.persistence.*;
@ -20,8 +21,8 @@ import javax.persistence.*;
public class Tag extends BaseEntity { public class Tag extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@Column(name = "id") @GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Integer id; private Integer id;
/** /**

View File

@ -3,6 +3,7 @@ package run.halo.app.model.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*; import javax.persistence.*;
@ -22,7 +23,8 @@ import javax.persistence.*;
public class ThemeSetting extends BaseEntity { public class ThemeSetting extends BaseEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "custom-id")
@GenericGenerator(name = "custom-id", strategy = "run.halo.app.model.entity.support.CustomIdGenerator")
private Integer id; private Integer id;
/** /**
@ -35,6 +37,7 @@ public class ThemeSetting extends BaseEntity {
* Setting value * Setting value
*/ */
@Column(name = "setting_value", nullable = false) @Column(name = "setting_value", nullable = false)
@Lob
private String value; private String value;
/** /**

View File

@ -0,0 +1,22 @@
package run.halo.app.model.entity.support;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentityGenerator;
import run.halo.app.utils.ReflectionUtils;
import java.io.Serializable;
/**
* @author ryanwang
* @date 2020-03-16
*/
public class CustomIdGenerator extends IdentityGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) {
Object id = ReflectionUtils.getFieldValue("id", object);
if (id != null) {
return (Serializable) id;
}
return super.generate(session, object);
}
}

View File

@ -9,9 +9,9 @@ package run.halo.app.model.enums;
public enum MigrateType implements ValueEnum<Integer> { public enum MigrateType implements ValueEnum<Integer> {
/** /**
* Halo version 0.4.4 * Halo
*/ */
OLD_VERSION(0), HALO(0),
/** /**
* WordPress * WordPress

View File

@ -60,7 +60,7 @@ public class AdminAuthenticationFilter extends AbstractAuthenticationFilter {
"/api/admin/login", "/api/admin/login",
"/api/admin/refresh/*", "/api/admin/refresh/*",
"/api/admin/installations", "/api/admin/installations",
"/api/admin/migrations/**", "/api/admin/migrations/halo",
"/api/admin/is_installed", "/api/admin/is_installed",
"/api/admin/password/code", "/api/admin/password/code",
"/api/admin/password/reset" "/api/admin/password/reset"

View File

@ -1,6 +1,6 @@
package run.halo.app.service; package run.halo.app.service;
import org.json.JSONObject; import com.alibaba.fastjson.JSONObject;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -19,29 +19,14 @@ import java.util.List;
public interface BackupService { public interface BackupService {
/** /**
* Backup posts and sheets * Import markdown content.
* *
* @param file file * @param file file
* @return post info * @return base post detail dto
* @throws IOException throws IOException
*/ */
BasePostDetailDTO importMarkdown(MultipartFile file) throws IOException; BasePostDetailDTO importMarkdown(MultipartFile file) throws IOException;
/**
* export posts by hexo formatter
*
* @return json object
*/
JSONObject exportHexoMDs();
/**
* Exports the specified articles to the specified dir path.
*
* @param posts
* @param path
*/
void exportHexoMd(List<JSONObject> posts, String path);
/** /**
* Zips work directory. * Zips work directory.
* *
@ -74,4 +59,21 @@ public interface BackupService {
*/ */
@NonNull @NonNull
Resource loadFileAsResource(@NonNull String fileName); Resource loadFileAsResource(@NonNull String fileName);
/**
* Export all database's data.
*
* @return data
*/
@NonNull
JSONObject exportData();
/**
* Import data
*
* @param file file
* @throws IOException throws IOException
*/
void importData(MultipartFile file) throws IOException;
} }

View File

@ -1,6 +1,8 @@
package run.halo.app.service; package run.halo.app.service;
import run.halo.app.model.entity.CommentBlackList;
import run.halo.app.model.enums.CommentViolationTypeEnum; import run.halo.app.model.enums.CommentViolationTypeEnum;
import run.halo.app.service.base.CrudService;
/** /**
* Comment BlackList Service * Comment BlackList Service
@ -8,7 +10,7 @@ import run.halo.app.model.enums.CommentViolationTypeEnum;
* @author Lei XinXin * @author Lei XinXin
* @date 2020/1/3 * @date 2020/1/3
*/ */
public interface CommentBlackListService { public interface CommentBlackListService extends CrudService<CommentBlackList, Long> {
/** /**
* *
* *

View File

@ -4,6 +4,7 @@ import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import run.halo.app.model.entity.ThemeSetting; import run.halo.app.model.entity.ThemeSetting;
import run.halo.app.service.base.CrudService;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -14,7 +15,7 @@ import java.util.Map;
* @author johnniang * @author johnniang
* @date 2019-04-08 * @date 2019-04-08
*/ */
public interface ThemeSettingService { public interface ThemeSettingService extends CrudService<ThemeSetting, Integer> {
/** /**

View File

@ -1,36 +1,31 @@
package run.halo.app.service.impl; package run.halo.app.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils; import org.springframework.context.ApplicationEventPublisher;
import org.json.JSONObject;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource; import org.springframework.core.io.UrlResource;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.yaml.snakeyaml.Yaml;
import run.halo.app.config.properties.HaloProperties; import run.halo.app.config.properties.HaloProperties;
import run.halo.app.event.options.OptionUpdatedEvent;
import run.halo.app.event.theme.ThemeUpdatedEvent;
import run.halo.app.exception.NotFoundException; import run.halo.app.exception.NotFoundException;
import run.halo.app.exception.ServiceException; import run.halo.app.exception.ServiceException;
import run.halo.app.model.dto.BackupDTO; import run.halo.app.model.dto.BackupDTO;
import run.halo.app.model.dto.post.BasePostDetailDTO; import run.halo.app.model.dto.post.BasePostDetailDTO;
import run.halo.app.model.entity.Post; import run.halo.app.model.entity.*;
import run.halo.app.model.entity.Tag;
import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.support.HaloConst; import run.halo.app.model.support.HaloConst;
import run.halo.app.security.service.OneTimeTokenService; import run.halo.app.security.service.OneTimeTokenService;
import run.halo.app.service.BackupService; import run.halo.app.service.*;
import run.halo.app.service.OptionService;
import run.halo.app.service.PostService;
import run.halo.app.service.PostTagService;
import run.halo.app.utils.HaloUtils; import run.halo.app.utils.HaloUtils;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -40,7 +35,8 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -59,26 +55,79 @@ public class BackupServiceImpl implements BackupService {
private static final String LINE_SEPARATOR = System.getProperty("line.separator"); private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private final AttachmentService attachmentService;
private final CategoryService categoryService;
private final CommentBlackListService commentBlackListService;
private final JournalService journalService;
private final JournalCommentService journalCommentService;
private final LinkService linkService;
private final LogService logService;
private final MenuService menuService;
private final OptionService optionService;
private final PhotoService photoService;
private final PostService postService; private final PostService postService;
private final PostCategoryService postCategoryService;
private final PostCommentService postCommentService;
private final PostMetaService postMetaService;
private final PostTagService postTagService; private final PostTagService postTagService;
private final OptionService optionService; private final SheetService sheetService;
private final SheetCommentService sheetCommentService;
private final SheetMetaService sheetMetaService;
private final TagService tagService;
private final ThemeSettingService themeSettingService;
private final UserService userService;
private final OneTimeTokenService oneTimeTokenService; private final OneTimeTokenService oneTimeTokenService;
private final HaloProperties haloProperties; private final HaloProperties haloProperties;
public BackupServiceImpl(PostService postService, private final ApplicationEventPublisher eventPublisher;
PostTagService postTagService,
OptionService optionService, public BackupServiceImpl(AttachmentService attachmentService, CategoryService categoryService, CommentBlackListService commentBlackListService, JournalService journalService, JournalCommentService journalCommentService, LinkService linkService, LogService logService, MenuService menuService, OptionService optionService, PhotoService photoService, PostService postService, PostCategoryService postCategoryService, PostCommentService postCommentService, PostMetaService postMetaService, PostTagService postTagService, SheetService sheetService, SheetCommentService sheetCommentService, SheetMetaService sheetMetaService, TagService tagService, ThemeSettingService themeSettingService, UserService userService, OneTimeTokenService oneTimeTokenService, HaloProperties haloProperties, ApplicationEventPublisher eventPublisher) {
OneTimeTokenService oneTimeTokenService, this.attachmentService = attachmentService;
HaloProperties haloProperties) { this.categoryService = categoryService;
this.postService = postService; this.commentBlackListService = commentBlackListService;
this.postTagService = postTagService; this.journalService = journalService;
this.journalCommentService = journalCommentService;
this.linkService = linkService;
this.logService = logService;
this.menuService = menuService;
this.optionService = optionService; this.optionService = optionService;
this.photoService = photoService;
this.postService = postService;
this.postCategoryService = postCategoryService;
this.postCommentService = postCommentService;
this.postMetaService = postMetaService;
this.postTagService = postTagService;
this.sheetService = sheetService;
this.sheetCommentService = sheetCommentService;
this.sheetMetaService = sheetMetaService;
this.tagService = tagService;
this.themeSettingService = themeSettingService;
this.userService = userService;
this.oneTimeTokenService = oneTimeTokenService; this.oneTimeTokenService = oneTimeTokenService;
this.haloProperties = haloProperties; this.haloProperties = haloProperties;
this.eventPublisher = eventPublisher;
} }
/** /**
@ -105,66 +154,6 @@ public class BackupServiceImpl implements BackupService {
return postService.importMarkdown(markdown, file.getOriginalFilename()); return postService.importMarkdown(markdown, file.getOriginalFilename());
} }
@Override
public JSONObject exportHexoMDs() {
final JSONObject ret = new JSONObject();
final List<JSONObject> posts = new ArrayList<>();
ret.put("posts", (Object) posts);
final List<JSONObject> passwords = new ArrayList<>();
ret.put("passwords", (Object) passwords);
final List<JSONObject> drafts = new ArrayList<>();
ret.put("drafts", (Object) drafts);
List<Post> postList = postService.listAll();
Map<Integer, List<Tag>> talMap = postTagService.listTagListMapBy(postList.stream().map(Post::getId).collect(Collectors.toList()));
for (Post post : postList) {
final Map<String, Object> front = new LinkedHashMap<>();
final String title = post.getTitle();
front.put("title", title);
final String date = DateFormatUtils.format(post.getCreateTime(), "yyyy-MM-dd HH:mm:ss");
front.put("date", date);
front.put("updated", DateFormatUtils.format(post.getUpdateTime(), "yyyy-MM-dd HH:mm:ss"));
final List<String> tags = talMap.get(post.getId()).stream().map(Tag::getName).collect(Collectors.toList());
if (tags.isEmpty()) {
tags.add("halo");
}
front.put("tags", tags);
front.put("permalink", "");
final JSONObject one = new JSONObject();
one.put("front", new Yaml().dump(front));
one.put("title", title);
one.put("content", post.getOriginalContent());
one.put("created", post.getCreateTime().getTime());
if (StringUtils.isNotBlank(post.getPassword())) {
passwords.add(one);
} else if (post.getStatus() == PostStatus.DRAFT) {
drafts.add(one);
} else {
posts.add(one);
}
}
return ret;
}
@Override
public void exportHexoMd(List<JSONObject> posts, String dirPath) {
posts.forEach(post -> {
final String filename = sanitizeFilename(post.optString("title")) + ".md";
final String text = post.optString("front") + "---" + LINE_SEPARATOR + post.optString("content");
try {
final String date = DateFormatUtils.format(post.optLong("created"), "yyyyMM");
final String dir = dirPath + File.separator + date + File.separator;
new File(dir).mkdirs();
FileUtils.writeStringToFile(new File(dir + filename), text, "UTF-8");
} catch (final Exception e) {
log.error("Write markdown file failed", e);
}
});
}
@Override @Override
public BackupDTO zipWorkDirectory() { public BackupDTO zipWorkDirectory() {
// Zip work directory to temporary file // Zip work directory to temporary file
@ -268,6 +257,106 @@ public class BackupServiceImpl implements BackupService {
} }
} }
@Override
public JSONObject exportData() {
JSONObject data = new JSONObject();
data.put("version", HaloConst.HALO_VERSION);
data.put("export_date", DateUtil.now());
data.put("attachments", attachmentService.listAll());
data.put("categories", categoryService.listAll());
data.put("comment_black_list", commentBlackListService.listAll());
data.put("journals", journalService.listAll());
data.put("journal_comments", journalCommentService.listAll());
data.put("links", linkService.listAll());
data.put("logs", logService.listAll());
data.put("menus", menuService.listAll());
data.put("options", optionService.listAll());
data.put("photos", photoService.listAll());
data.put("posts", postService.listAll());
data.put("post_categories", postCategoryService.listAll());
data.put("post_comments", postCommentService.listAll());
data.put("post_metas", postMetaService.listAll());
data.put("post_tags", postTagService.listAll());
data.put("sheets", sheetService.listAll());
data.put("sheet_comments", sheetCommentService.listAll());
data.put("sheet_metas", sheetMetaService.listAll());
data.put("tags", tagService.listAll());
data.put("theme_settings", themeSettingService.listAll());
data.put("user", userService.listAll());
return data;
}
@Override
public void importData(MultipartFile file) throws IOException {
String jsonContent = IoUtil.read(file.getInputStream(), StandardCharsets.UTF_8);
JSONObject data = JSONObject.parseObject(jsonContent);
List<Attachment> attachments = data.getJSONArray("attachments").toJavaList(Attachment.class);
attachmentService.createInBatch(attachments);
List<Category> categories = data.getJSONArray("categories").toJavaList(Category.class);
categoryService.createInBatch(categories);
List<Tag> tags = data.getJSONArray("tags").toJavaList(Tag.class);
tagService.createInBatch(tags);
List<CommentBlackList> commentBlackList = data.getJSONArray("comment_black_list").toJavaList(CommentBlackList.class);
commentBlackListService.createInBatch(commentBlackList);
List<Journal> journals = data.getJSONArray("journals").toJavaList(Journal.class);
journalService.createInBatch(journals);
List<JournalComment> journalComments = data.getJSONArray("journal_comments").toJavaList(JournalComment.class);
journalCommentService.createInBatch(journalComments);
List<Link> links = data.getJSONArray("links").toJavaList(Link.class);
linkService.createInBatch(links);
List<Log> logs = data.getJSONArray("logs").toJavaList(Log.class);
logService.createInBatch(logs);
List<Menu> menus = data.getJSONArray("menus").toJavaList(Menu.class);
menuService.createInBatch(menus);
List<Option> options = data.getJSONArray("options").toJavaList(Option.class);
optionService.createInBatch(options);
eventPublisher.publishEvent(new OptionUpdatedEvent(this));
List<Photo> photos = data.getJSONArray("photos").toJavaList(Photo.class);
photoService.createInBatch(photos);
List<Post> posts = data.getJSONArray("posts").toJavaList(Post.class);
postService.createInBatch(posts);
List<PostCategory> postCategories = data.getJSONArray("post_categories").toJavaList(PostCategory.class);
postCategoryService.createInBatch(postCategories);
List<PostComment> postComments = data.getJSONArray("post_comments").toJavaList(PostComment.class);
postCommentService.createInBatch(postComments);
List<PostMeta> postMetas = data.getJSONArray("post_metas").toJavaList(PostMeta.class);
postMetaService.createInBatch(postMetas);
List<PostTag> postTags = data.getJSONArray("post_tags").toJavaList(PostTag.class);
postTagService.createInBatch(postTags);
List<Sheet> sheets = data.getJSONArray("sheets").toJavaList(Sheet.class);
sheetService.createInBatch(sheets);
List<SheetComment> sheetComments = data.getJSONArray("sheet_comments").toJavaList(SheetComment.class);
sheetCommentService.createInBatch(sheetComments);
List<SheetMeta> sheetMetas = data.getJSONArray("sheet_metas").toJavaList(SheetMeta.class);
sheetMetaService.createInBatch(sheetMetas);
List<ThemeSetting> themeSettings = data.getJSONArray("theme_settings").toJavaList(ThemeSetting.class);
themeSettingService.createInBatch(themeSettings);
eventPublisher.publishEvent(new ThemeUpdatedEvent(this));
}
/** /**
* Builds backup dto. * Builds backup dto.
* *

View File

@ -30,7 +30,7 @@ public class ImageUtils {
} }
} }
public static ImageReader getImageReaderFromFile(InputStream is,String formatName) { public static ImageReader getImageReaderFromFile(InputStream is, String formatName) {
try { try {
Iterator<ImageReader> readerIterator = ImageIO.getImageReadersByFormatName(formatName); Iterator<ImageReader> readerIterator = ImageIO.getImageReadersByFormatName(formatName);
ImageReader reader = readerIterator.next(); ImageReader reader = readerIterator.next();

View File

@ -4,6 +4,7 @@ import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@ -11,6 +12,8 @@ import java.lang.reflect.Type;
* Reflection utilities. * Reflection utilities.
* *
* @author johnniang * @author johnniang
* @author ryanwang
* @date 2019-03-15
*/ */
public class ReflectionUtils { public class ReflectionUtils {
@ -89,4 +92,26 @@ public class ReflectionUtils {
return getParameterizedType(superClassType, extensionClass.getGenericSuperclass()); return getParameterizedType(superClassType, extensionClass.getGenericSuperclass());
} }
/**
* Gets field value from Object.
*
* @param fieldName fieldName must not be null
* @param object object must not be null.
* @return value
*/
public static Object getFieldValue(@NonNull String fieldName, @NonNull Object object) {
Assert.notNull(fieldName, "FieldName must not be null");
Assert.notNull(object, "Object type must not be null");
Object value = null;
try {
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getter = "get" + firstLetter + fieldName.substring(1);
Method method = object.getClass().getMethod(getter);
value = method.invoke(object);
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
} }

View File

@ -0,0 +1,7 @@
-- Migrate 1.3.0-beta.2 to 1.3.0-beta.3
-- Migrate options Table
alter table options modify option_value longtext not null;
-- Migrate theme_settings Table
alter table theme_settings modify setting_value longtext not null;