Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide backup dto fetch api #1278

Merged
merged 3 commits into from
Feb 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions src/main/java/run/halo/app/config/SwaggerConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,16 @@ private Docket buildApiDocket(@NonNull String groupName, @NonNull String basePac

private List<SecurityScheme> adminApiKeys() {
return Arrays.asList(
new ApiKey("Token from header", ADMIN_TOKEN_HEADER_NAME, In.HEADER.name()),
new ApiKey("Token from query", ADMIN_TOKEN_QUERY_NAME, In.QUERY.name())
new ApiKey(ADMIN_TOKEN_HEADER_NAME, ADMIN_TOKEN_HEADER_NAME, In.HEADER.name()),
new ApiKey(ADMIN_TOKEN_QUERY_NAME, ADMIN_TOKEN_QUERY_NAME, In.QUERY.name())
);
}

private List<SecurityContext> adminSecurityContext() {
final PathMatcher pathMatcher = new AntPathMatcher();
return Collections.singletonList(
SecurityContext.builder()
.securityReferences(defaultAuth())
.securityReferences(adminApiAuths())
.operationSelector(operationContext -> {
var requestMappingPattern = operationContext.requestMappingPattern();
return pathMatcher.match("/api/admin/**/*", requestMappingPattern);
Expand All @@ -166,16 +166,16 @@ private List<SecurityContext> adminSecurityContext() {

private List<SecurityScheme> contentApiKeys() {
return Arrays.asList(
new ApiKey("Access key from header", API_ACCESS_KEY_HEADER_NAME, In.HEADER.name()),
new ApiKey("Access key from query", API_ACCESS_KEY_QUERY_NAME, In.QUERY.name())
new ApiKey(API_ACCESS_KEY_HEADER_NAME, API_ACCESS_KEY_HEADER_NAME, In.HEADER.name()),
new ApiKey(API_ACCESS_KEY_QUERY_NAME, API_ACCESS_KEY_QUERY_NAME, In.QUERY.name())
);
}

private List<SecurityContext> contentSecurityContext() {
final PathMatcher pathMatcher = new AntPathMatcher();
return Collections.singletonList(
SecurityContext.builder()
.securityReferences(contentApiAuth())
.securityReferences(contentApiAuths())
.operationSelector(operationContext -> {
var requestMappingPattern = operationContext.requestMappingPattern();
return pathMatcher.match("/api/content/**/*", requestMappingPattern);
Expand All @@ -184,18 +184,18 @@ private List<SecurityContext> contentSecurityContext() {
);
}

private List<SecurityReference> defaultAuth() {
private List<SecurityReference> adminApiAuths() {
AuthorizationScope[] authorizationScopes =
{new AuthorizationScope("Admin api", "Access admin api")};
return Arrays.asList(new SecurityReference("Token from header", authorizationScopes),
new SecurityReference("Token from query", authorizationScopes));
return Arrays.asList(new SecurityReference(ADMIN_TOKEN_HEADER_NAME, authorizationScopes),
new SecurityReference(ADMIN_TOKEN_QUERY_NAME, authorizationScopes));
}

private List<SecurityReference> contentApiAuth() {
private List<SecurityReference> contentApiAuths() {
AuthorizationScope[] authorizationScopes =
{new AuthorizationScope("content api", "Access content api")};
return Arrays.asList(new SecurityReference("Access key from header", authorizationScopes),
new SecurityReference("Access key from query", authorizationScopes));
return Arrays.asList(new SecurityReference(API_ACCESS_KEY_HEADER_NAME, authorizationScopes),
new SecurityReference(API_ACCESS_KEY_QUERY_NAME, authorizationScopes));
}

private ApiInfo apiInfo() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package run.halo.app.controller.admin.api;

import static run.halo.app.service.BackupService.BackupType.JSON_DATA;
import static run.halo.app.service.BackupService.BackupType.MARKDOWN;
import static run.halo.app.service.BackupService.BackupType.WHOLE_SITE;

import io.swagger.annotations.ApiOperation;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -21,6 +26,7 @@
import org.springframework.web.multipart.MultipartFile;
import run.halo.app.annotation.DisableOnCondition;
import run.halo.app.config.properties.HaloProperties;
import run.halo.app.exception.NotFoundException;
import run.halo.app.model.dto.BackupDTO;
import run.halo.app.model.dto.post.BasePostDetailDTO;
import run.halo.app.model.params.PostMarkdownParam;
Expand Down Expand Up @@ -48,6 +54,29 @@ public BackupController(BackupService backupService, HaloProperties haloProperti
this.haloProperties = haloProperties;
}

@GetMapping("work-dir/fetch")
public BackupDTO getWorkDirBackup(@RequestParam("filename") String filename) {
return backupService.getBackup(Paths.get(haloProperties.getWorkDir(), filename), WHOLE_SITE)
.orElseThrow(() ->
new NotFoundException("备份文件 " + filename + " 不存在或已删除!").setErrorData(filename));
}

@GetMapping("data/fetch")
public BackupDTO getDataBackup(@RequestParam("filename") String filename) {
return backupService
.getBackup(Paths.get(haloProperties.getDataExportDir(), filename), JSON_DATA)
.orElseThrow(() ->
new NotFoundException("备份文件 " + filename + " 不存在或已删除!").setErrorData(filename));
}

@GetMapping("markdown/fetch")
public BackupDTO getMarkdownBackup(@RequestParam("filename") String filename) {
return backupService
.getBackup(Paths.get(haloProperties.getBackupMarkdownDir(), filename), MARKDOWN)
.orElseThrow(() ->
new NotFoundException("备份文件 " + filename + " 不存在或已删除!").setErrorData(filename));
}

@PostMapping("work-dir")
@ApiOperation("Backups work directory")
@DisableOnCondition
Expand All @@ -61,16 +90,16 @@ public List<BackupDTO> listBackups() {
return backupService.listWorkDirBackups();
}

@GetMapping("work-dir/{fileName:.+}")
@GetMapping("work-dir/{filename:.+}")
@ApiOperation("Downloads a work directory backup file")
@DisableOnCondition
public ResponseEntity<Resource> downloadBackup(@PathVariable("fileName") String fileName,
public ResponseEntity<Resource> downloadBackup(@PathVariable("filename") String filename,
HttpServletRequest request) {
log.info("Try to download backup file: [{}]", fileName);
log.info("Trying to download backup file: [{}]", filename);

// Load file as resource
Resource backupResource =
backupService.loadFileAsResource(haloProperties.getBackupDir(), fileName);
backupService.loadFileAsResource(haloProperties.getBackupDir(), filename);

String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
// Try to determine file's content type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ private boolean isSufficientOneTimeToken(HttpServletRequest request) {

// Get allowed uri
String allowedUri = oneTimeTokenService.get(oneTimeToken)
.orElseThrow(() -> new BadRequestException("The one-time token does not exist")
.orElseThrow(() -> new BadRequestException(
"The one-time token does not exist or has been expired")
.setErrorData(oneTimeToken));

// Get request uri
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package run.halo.app.security.service.impl;

import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.springframework.stereotype.Service;
Expand All @@ -16,10 +17,9 @@
@Service
public class OneTimeTokenServiceImpl implements OneTimeTokenService {

/**
* One-time token expired day. (unit: day)
*/
public static final int OTT_EXPIRED_DAY = 1;
private static final String tokenPrefix = "OTT-";

private static final Duration OTT_EXPIRATION_TIME = Duration.ofMinutes(5);

private final AbstractStringCacheStore cacheStore;

Expand All @@ -32,7 +32,7 @@ public Optional<String> get(String oneTimeToken) {
Assert.hasText(oneTimeToken, "One-time token must not be blank");

// Get from cache store
return cacheStore.get(oneTimeToken);
return cacheStore.get(tokenPrefix + oneTimeToken);
}

@Override
Expand All @@ -43,7 +43,10 @@ public String create(String uri) {
String oneTimeToken = HaloUtils.randomUUIDWithoutDash();

// Put ott along with request uri
cacheStore.put(oneTimeToken, uri, OTT_EXPIRED_DAY, TimeUnit.DAYS);
cacheStore.put(tokenPrefix + oneTimeToken,
uri,
OTT_EXPIRATION_TIME.getSeconds(),
TimeUnit.SECONDS);

// Return ott
return oneTimeToken;
Expand All @@ -54,6 +57,6 @@ public void revoke(String oneTimeToken) {
Assert.hasText(oneTimeToken, "One-time token must not be blank");

// Delete the token
cacheStore.delete(oneTimeToken);
cacheStore.delete(tokenPrefix + oneTimeToken);
}
}
34 changes: 34 additions & 0 deletions src/main/java/run/halo/app/service/BackupService.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package run.halo.app.service;

import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import org.springframework.core.io.Resource;
import org.springframework.lang.NonNull;
import org.springframework.web.multipart.MultipartFile;
Expand Down Expand Up @@ -44,6 +46,16 @@ public interface BackupService {
@NonNull
List<BackupDTO> listWorkDirBackups();

/**
* Get backup data by backup file name.
*
* @param backupFileName backup file name must not be blank
* @param type backup type must not be null
* @return an optional of backup data
*/
@NonNull
Optional<BackupDTO> getBackup(@NonNull Path backupFileName, @NonNull BackupType type);

/**
* Deletes backup.
*
Expand Down Expand Up @@ -116,4 +128,26 @@ public interface BackupService {
* @param fileName file name
*/
void deleteMarkdown(@NonNull String fileName);

/**
* Backup type.
*
* @author johnniang
*/
enum BackupType {
WHOLE_SITE("/api/admin/backups/work-dir"),
JSON_DATA("/api/admin/backups/data"),
MARKDOWN("/api/admin/backups/markdown/export"),
;

private final String baseUri;

BackupType(String baseUri) {
this.baseUri = baseUri;
}

public String getBaseUri() {
return baseUri;
}
}
}
Loading