feat: support deploy static page in netlify.

pull/471/head^2
ruibaby 2019-12-26 20:17:03 +08:00
parent 83e87553b7
commit c37f36f61e
14 changed files with 185 additions and 14 deletions

View File

@ -35,6 +35,7 @@ public class Application extends SpringBootServletInitializer {
// Run application
context = SpringApplication.run(Application.class, args);
}
/**

View File

@ -1,26 +1,55 @@
package run.halo.app.controller.admin.api;
import cn.hutool.core.io.FileUtil;
import io.swagger.annotations.ApiOperation;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import run.halo.app.model.properties.NetlifyStaticDeployProperties;
import run.halo.app.model.support.StaticPageFile;
import run.halo.app.service.OptionService;
import run.halo.app.service.StaticPageService;
import java.io.FileNotFoundException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
/**
* @author ryan0up
* @date 2019/12/25
* Static page controller.
*
* @author ryanwang
* @date 2019-12-25
*/
@RestController
@RequestMapping("/api/admin/static_page")
public class StaticPageController {
private final static String DEPLOY_API = "https://api.netlify.com/api/v1/sites/%s/deploys";
private final OptionService optionService;
private final RestTemplate httpsRestTemplate;
private final StaticPageService staticPageService;
public StaticPageController(StaticPageService staticPageService) {
public StaticPageController(StaticPageService staticPageService,
OptionService optionService,
RestTemplate httpsRestTemplate) {
this.staticPageService = staticPageService;
this.optionService = optionService;
this.httpsRestTemplate = httpsRestTemplate;
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
this.httpsRestTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
}
@GetMapping
@ -35,9 +64,29 @@ public class StaticPageController {
staticPageService.generate();
}
@GetMapping("deploy")
@PostMapping("deploy")
@ApiOperation("Deploy static page to remove platform")
public void deploy() {
staticPageService.deploy();
}
@GetMapping("netlify")
public void testNetlify() throws FileNotFoundException {
String domain = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_DOMAIN).toString();
String siteId = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_SITE_ID).toString();
String token = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_TOKEN).toString();
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/zip");
headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + token);
Path path = staticPageService.zipStaticPagesDirectory();
byte[] bytes = FileUtil.readBytes(path.toFile());
HttpEntity<byte[]> httpEntity = new HttpEntity<>(bytes, headers);
ResponseEntity<Object> responseEntity = httpsRestTemplate.postForEntity(String.format(DEPLOY_API, siteId), httpEntity, Object.class);
}
}

View File

@ -1,5 +1,7 @@
package run.halo.app.handler.migrate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import run.halo.app.model.enums.MigrateType;
@ -9,6 +11,8 @@ import run.halo.app.model.enums.MigrateType;
* @author ryanwang
* @date 2019-10-30
*/
@Slf4j
@Component
public class CnBlogsMigrateHandler implements MigrateHandler {
@Override

View File

@ -31,7 +31,7 @@ public class MigrateHandlers {
public MigrateHandlers(ApplicationContext applicationContext) {
// Add all migrate handler
addFileHandlers(applicationContext.getBeansOfType(MigrateHandler.class).values());
addMigrateHandlers(applicationContext.getBeansOfType(MigrateHandler.class).values());
}
@NonNull
@ -56,7 +56,7 @@ public class MigrateHandlers {
* @return current migrate handlers
*/
@NonNull
private MigrateHandlers addFileHandlers(@Nullable Collection<MigrateHandler> migrateHandlers) {
private MigrateHandlers addMigrateHandlers(@Nullable Collection<MigrateHandler> migrateHandlers) {
if (!CollectionUtils.isEmpty(migrateHandlers)) {
this.migrateHandlers.addAll(migrateHandlers);
}

View File

@ -1,9 +1,18 @@
package run.halo.app.handler.staticdeploy;
import cn.hutool.core.io.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import run.halo.app.model.enums.StaticDeployType;
import run.halo.app.model.properties.NetlifyStaticDeployProperties;
import run.halo.app.service.OptionService;
import run.halo.app.service.StaticPageService;
import java.nio.file.Path;
/**
* Netlify deploy handler.
@ -15,15 +24,40 @@ import run.halo.app.service.OptionService;
@Component
public class NetlifyStaticDeployHandler implements StaticDeployHandler {
private final static String DEPLOY_API = "https://api.netlify.com/api/v1/sites/%s/deploys";
private final OptionService optionService;
public NetlifyStaticDeployHandler(OptionService optionService) {
private final RestTemplate httpsRestTemplate;
private final StaticPageService staticPageService;
public NetlifyStaticDeployHandler(OptionService optionService,
RestTemplate httpsRestTemplate,
StaticPageService staticPageService) {
this.optionService = optionService;
this.httpsRestTemplate = httpsRestTemplate;
this.staticPageService = staticPageService;
}
@Override
public void deploy() {
String domain = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_DOMAIN).toString();
String siteId = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_SITE_ID).toString();
String token = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_TOKEN).toString();
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/zip");
headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + token);
Path path = staticPageService.zipStaticPagesDirectory();
byte[] bytes = FileUtil.readBytes(path.toFile());
HttpEntity<byte[]> httpEntity = new HttpEntity<>(bytes, headers);
ResponseEntity<Object> responseEntity = httpsRestTemplate.postForEntity(String.format(DEPLOY_API, siteId), httpEntity, Object.class);
}
@Override

View File

@ -26,8 +26,8 @@ public class StaticDeployHandlers {
private final Collection<StaticDeployHandler> staticDeployHandlers = new LinkedList<>();
public StaticDeployHandlers(ApplicationContext applicationContext) {
// Add all file handler
addFileHandlers(applicationContext.getBeansOfType(StaticDeployHandler.class).values());
// Add all static deploy handler
addStaticDeployHandlers(applicationContext.getBeansOfType(StaticDeployHandler.class).values());
}
@ -42,6 +42,7 @@ public class StaticDeployHandlers {
for (StaticDeployHandler staticDeployHandler : staticDeployHandlers) {
if (staticDeployHandler.supportType(staticDeployType)) {
staticDeployHandler.deploy();
return;
}
}
@ -55,7 +56,7 @@ public class StaticDeployHandlers {
* @return current file handlers
*/
@NonNull
public StaticDeployHandlers addFileHandlers(@Nullable Collection<StaticDeployHandler> staticDeployHandlers) {
public StaticDeployHandlers addStaticDeployHandlers(@Nullable Collection<StaticDeployHandler> staticDeployHandlers) {
if (!CollectionUtils.isEmpty(staticDeployHandlers)) {
this.staticDeployHandlers.addAll(staticDeployHandlers);
}

View File

@ -4,7 +4,7 @@ package run.halo.app.model.enums;
* Migrate type.
*
* @author ryanwang
* @date : 2019-03-12
* @date 2019-03-12
*/
public enum MigrateType implements ValueEnum<Integer> {

View File

@ -11,12 +11,12 @@ public enum StaticDeployType implements ValueEnum<Integer> {
/**
* Deploy static pages in remote git repository, such as github pages,gitee pages,coding pages.etc.
*/
GIT(1),
GIT(0),
/**
* Deploy static pages in netlify.
*/
NETLIFY(2);
NETLIFY(1);
private Integer value;

View File

@ -30,6 +30,11 @@ public class HaloConst {
*/
public final static String HALO_BACKUP_PREFIX = "halo-backup-";
/**
* Static pages pack prefix.
*/
public final static String STATIC_PAGE_PACK_PREFIX = "static-pages-";
/**
* Default theme name.
*/

View File

@ -16,6 +16,8 @@ import java.util.List;
@ToString
public class StaticFile implements Comparator<StaticFile> {
private String id;
private String name;
private String path;

View File

@ -14,6 +14,8 @@ import java.util.List;
@Data
public class StaticPageFile implements Comparator<StaticPageFile> {
private String id;
private String name;
private Boolean isFile;

View File

@ -2,8 +2,13 @@ package run.halo.app.service;
import run.halo.app.model.support.StaticPageFile;
import java.nio.file.Path;
import java.util.List;
import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR;
import static run.halo.app.model.support.HaloConst.TEMP_DIR;
import static run.halo.app.utils.HaloUtils.ensureSuffix;
/**
* Static Page service interface.
*
@ -17,6 +22,11 @@ public interface StaticPageService {
*/
String PAGES_FOLDER = "static_pages";
String STATIC_PAGE_PACK_DIR = ensureSuffix(TEMP_DIR, FILE_SEPARATOR) + "static-pages-pack" + FILE_SEPARATOR;
String[] USELESS_FILE_SUFFIX = {"ftl", "md", "yaml", "yml", "gitignore"};
/**
* Generate pages.
*/
@ -27,6 +37,13 @@ public interface StaticPageService {
*/
void deploy();
/**
* Zip static pages directory.
*
* @return zip path
*/
Path zipStaticPagesDirectory();
/**
* List file of generated static page.
*

View File

@ -1,6 +1,8 @@
package run.halo.app.service.impl;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.PageUtil;
import freemarker.template.Template;
import freemarker.template.TemplateException;
@ -42,6 +44,8 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Stream;
@ -123,6 +127,7 @@ public class StaticPageServiceImpl implements StaticPageService {
pagesDir = Paths.get(haloProperties.getWorkDir(), PAGES_FOLDER);
FileUtils.createIfAbsent(pagesDir);
Files.createDirectories(Paths.get(STATIC_PAGE_PACK_DIR));
}
@Override
@ -158,6 +163,22 @@ public class StaticPageServiceImpl implements StaticPageService {
staticDeployHandlers.deploy(type);
}
@Override
public Path zipStaticPagesDirectory() {
try {
String staticPagePackName = HaloConst.STATIC_PAGE_PACK_PREFIX +
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss-")) +
IdUtil.simpleUUID().hashCode() + ".zip";
Path staticPageZipPath = Files.createFile(Paths.get(STATIC_PAGE_PACK_DIR, staticPagePackName));
FileUtils.zip(pagesDir, staticPageZipPath);
return staticPageZipPath;
} catch (IOException e) {
throw new ServiceException("Failed to zip static pages directory", e);
}
}
@Override
public List<StaticPageFile> listFile() {
return listStaticPageFileTree(pagesDir);
@ -176,6 +197,7 @@ public class StaticPageServiceImpl implements StaticPageService {
pathStream.forEach(path -> {
StaticPageFile staticPageFile = new StaticPageFile();
staticPageFile.setId(IdUtil.fastSimpleUUID());
staticPageFile.setName(path.getFileName().toString());
staticPageFile.setIsFile(Files.isRegularFile(path));
if (Files.isDirectory(path)) {
@ -708,9 +730,41 @@ public class StaticPageServiceImpl implements StaticPageService {
*/
private void copyThemeFolder() throws IOException {
ThemeProperty activatedTheme = themeService.getActivatedTheme();
Path path = Paths.get(pagesDir.toString(), activatedTheme.getId());
Path path = Paths.get(pagesDir.toString(), activatedTheme.getFolderName());
FileUtils.createIfAbsent(path);
FileUtils.copyFolder(Paths.get(activatedTheme.getThemePath()), path);
cleanThemeFolder(Paths.get(pagesDir.toString(), activatedTheme.getFolderName()));
}
private void cleanThemeFolder(Path themePath) {
if (!Files.isDirectory(themePath)) {
return;
}
try (Stream<Path> pathStream = Files.list(themePath)) {
pathStream.forEach(path -> {
if (!Files.isDirectory(path)) {
for (String suffix : USELESS_FILE_SUFFIX) {
if (suffix.contains(FileUtil.extName(path.toFile()))) {
try {
Files.delete(path);
} catch (IOException e) {
e.printStackTrace();
}
}
}
} else {
if (path.getFileName().toString().contains(".git")) {
FileUtils.deleteFolderQuietly(path);
} else {
cleanThemeFolder(path);
}
}
});
} catch (IOException e) {
throw new ServiceException("Failed to list sub files", e);
}
}
/**

View File

@ -1,5 +1,6 @@
package run.halo.app.service.impl;
import cn.hutool.core.util.IdUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
@ -59,6 +60,7 @@ public class StaticStorageServiceImpl implements StaticStorageService {
pathStream.forEach(path -> {
StaticFile staticFile = new StaticFile();
staticFile.setId(IdUtil.fastSimpleUUID());
staticFile.setName(path.getFileName().toString());
staticFile.setPath(path.toString());
staticFile.setRelativePath(StringUtils.removeStart(path.toString(), staticDir.toString()));