From a4a418b22e9c922e91ccc3b34ed86f347708b018 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Fri, 4 Jul 2025 12:37:40 +0800 Subject: [PATCH] feat: add support for remote URL attachment downloads (#7602) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /area ui /kind feature /milestone 2.21.x #### What this PR does / why we need it: Add support for remote URL attachment downloads image #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/7017 #### Does this PR introduce a user-facing change? ```release-note 支持通过远程地址下载到附件库 ``` --- api-docs/openapi/v3_0/aggregated.json | 38 ++++++++-- .../v3_0/apis_console.api_v1alpha1.json | 17 +++-- .../v3_0/apis_extension.api_v1alpha1.json | 8 +- .../v3_0/apis_public.api_v1alpha1.json | 8 +- .../openapi/v3_0/apis_uc.api_v1alpha1.json | 37 ++++----- .../run/halo/app/core/extension/Theme.java | 2 - .../app/infra/utils/FileTypeDetectUtils.java | 2 +- .../halo/app/theme/router/PageUrlUtils.java | 2 +- .../endpoint/AttachmentEndpoint.java | 8 +- .../endpoint/uc/UcAttachmentEndpoint.java | 3 +- .../impl/DefaultAttachmentService.java | 5 +- .../DefaultReactiveUrlDataBufferFetcher.java | 37 +++++++-- .../infra/ReactiveUrlDataBufferFetcher.java | 4 +- .../endpoint/AttachmentEndpointTest.java | 4 +- .../components/AttachmentUploadModal.vue | 58 ++++++++++---- .../attachments/components/UploadFromUrl.vue | 75 +++++++++++++++++++ .../api-client/src/.openapi-generator/FILES | 1 + .../src/api/attachment-v1alpha1-uc-api.ts | 28 +++---- ui/packages/api-client/src/models/index.ts | 1 + .../src/models/uc-upload-from-url-request.ts | 36 +++++++++ .../src/models/upload-from-url-request.ts | 14 +++- ui/src/locales/_missing_translations_es.yaml | 10 +++ ui/src/locales/en.yaml | 13 +++- ui/src/locales/zh-CN.yaml | 9 +++ ui/src/locales/zh-TW.yaml | 9 +++ 25 files changed, 337 insertions(+), 92 deletions(-) create mode 100644 ui/console-src/modules/contents/attachments/components/UploadFromUrl.vue create mode 100644 ui/packages/api-client/src/models/uc-upload-from-url-request.ts diff --git a/api-docs/openapi/v3_0/aggregated.json b/api-docs/openapi/v3_0/aggregated.json index 85a8b22ff..22cbdcfe8 100644 --- a/api-docs/openapi/v3_0/aggregated.json +++ b/api-docs/openapi/v3_0/aggregated.json @@ -15420,7 +15420,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UploadFromUrlRequest" + "$ref": "#/components/schemas/UcUploadFromUrlRequest" } } }, @@ -20583,12 +20583,12 @@ }, "visible": { "type": "string", - "default": "PUBLIC", "enum": [ "PUBLIC", "INTERNAL", "PRIVATE" - ] + ], + "default": "PUBLIC" } } }, @@ -22355,12 +22355,12 @@ }, "visible": { "type": "string", - "default": "PUBLIC", "enum": [ "PUBLIC", "INTERNAL", "PRIVATE" - ] + ], + "default": "PUBLIC" } } }, @@ -23386,6 +23386,22 @@ } } }, + "UcUploadFromUrlRequest": { + "required": [ + "url" + ], + "type": "object", + "properties": { + "filename": { + "type": "string", + "description": "Custom file name" + }, + "url": { + "type": "string", + "format": "url" + } + } + }, "UcUploadRequest": { "required": [ "file" @@ -23445,12 +23461,22 @@ }, "UploadFromUrlRequest": { "required": [ + "policyName", "url" ], "type": "object", "properties": { "filename": { - "type": "string" + "type": "string", + "description": "Custom file name" + }, + "groupName": { + "type": "string", + "description": "The name of the group to which the attachment belongs" + }, + "policyName": { + "type": "string", + "description": "Storage policy name" }, "url": { "type": "string", diff --git a/api-docs/openapi/v3_0/apis_console.api_v1alpha1.json b/api-docs/openapi/v3_0/apis_console.api_v1alpha1.json index d3d6c3b36..4de16175c 100644 --- a/api-docs/openapi/v3_0/apis_console.api_v1alpha1.json +++ b/api-docs/openapi/v3_0/apis_console.api_v1alpha1.json @@ -5471,12 +5471,12 @@ }, "visible": { "type": "string", - "default": "PUBLIC", "enum": [ "PUBLIC", "INTERNAL", "PRIVATE" - ] + ], + "default": "PUBLIC" } } }, @@ -6007,12 +6007,12 @@ }, "visible": { "type": "string", - "default": "PUBLIC", "enum": [ "PUBLIC", "INTERNAL", "PRIVATE" - ] + ], + "default": "PUBLIC" } } }, @@ -6468,13 +6468,16 @@ "type": "object", "properties": { "filename": { - "type": "string" + "type": "string", + "description": "Custom file name" }, "groupName": { - "type": "string" + "type": "string", + "description": "The name of the group to which the attachment belongs" }, "policyName": { - "type": "string" + "type": "string", + "description": "Storage policy name" }, "url": { "type": "string", diff --git a/api-docs/openapi/v3_0/apis_extension.api_v1alpha1.json b/api-docs/openapi/v3_0/apis_extension.api_v1alpha1.json index d952ac689..1efb0c5a4 100644 --- a/api-docs/openapi/v3_0/apis_extension.api_v1alpha1.json +++ b/api-docs/openapi/v3_0/apis_extension.api_v1alpha1.json @@ -12903,12 +12903,12 @@ }, "visible": { "type": "string", - "default": "PUBLIC", "enum": [ "PUBLIC", "INTERNAL", "PRIVATE" - ] + ], + "default": "PUBLIC" } } }, @@ -14281,12 +14281,12 @@ }, "visible": { "type": "string", - "default": "PUBLIC", "enum": [ "PUBLIC", "INTERNAL", "PRIVATE" - ] + ], + "default": "PUBLIC" } } }, diff --git a/api-docs/openapi/v3_0/apis_public.api_v1alpha1.json b/api-docs/openapi/v3_0/apis_public.api_v1alpha1.json index efad029c1..d1dc75ba8 100644 --- a/api-docs/openapi/v3_0/apis_public.api_v1alpha1.json +++ b/api-docs/openapi/v3_0/apis_public.api_v1alpha1.json @@ -2609,12 +2609,12 @@ }, "visible": { "type": "string", - "default": "PUBLIC", "enum": [ "PUBLIC", "INTERNAL", "PRIVATE" - ] + ], + "default": "PUBLIC" } } }, @@ -3183,12 +3183,12 @@ }, "visible": { "type": "string", - "default": "PUBLIC", "enum": [ "PUBLIC", "INTERNAL", "PRIVATE" - ] + ], + "default": "PUBLIC" } } }, diff --git a/api-docs/openapi/v3_0/apis_uc.api_v1alpha1.json b/api-docs/openapi/v3_0/apis_uc.api_v1alpha1.json index a887732c0..db60c20b1 100644 --- a/api-docs/openapi/v3_0/apis_uc.api_v1alpha1.json +++ b/api-docs/openapi/v3_0/apis_uc.api_v1alpha1.json @@ -1420,7 +1420,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UploadFromUrlRequest" + "$ref": "#/components/schemas/UcUploadFromUrlRequest" } } }, @@ -2470,12 +2470,12 @@ }, "visible": { "type": "string", - "default": "PUBLIC", "enum": [ "PUBLIC", "INTERNAL", "PRIVATE" - ] + ], + "default": "PUBLIC" } } }, @@ -2911,6 +2911,22 @@ } } }, + "UcUploadFromUrlRequest": { + "required": [ + "url" + ], + "type": "object", + "properties": { + "filename": { + "type": "string", + "description": "Custom file name" + }, + "url": { + "type": "string", + "format": "url" + } + } + }, "UcUploadRequest": { "required": [ "file" @@ -2944,21 +2960,6 @@ } } }, - "UploadFromUrlRequest": { - "required": [ - "url" - ], - "type": "object", - "properties": { - "filename": { - "type": "string" - }, - "url": { - "type": "string", - "format": "url" - } - } - }, "UserConnection": { "required": [ "apiVersion", diff --git a/api/src/main/java/run/halo/app/core/extension/Theme.java b/api/src/main/java/run/halo/app/core/extension/Theme.java index 25721bf63..b862695fb 100644 --- a/api/src/main/java/run/halo/app/core/extension/Theme.java +++ b/api/src/main/java/run/halo/app/core/extension/Theme.java @@ -5,12 +5,10 @@ import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; -import java.util.Objects; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import org.apache.commons.lang3.ObjectUtils; -import org.springframework.lang.NonNull; import org.springframework.util.Assert; import run.halo.app.extension.AbstractExtension; import run.halo.app.extension.GVK; diff --git a/api/src/main/java/run/halo/app/infra/utils/FileTypeDetectUtils.java b/api/src/main/java/run/halo/app/infra/utils/FileTypeDetectUtils.java index 63663b070..794a792cc 100644 --- a/api/src/main/java/run/halo/app/infra/utils/FileTypeDetectUtils.java +++ b/api/src/main/java/run/halo/app/infra/utils/FileTypeDetectUtils.java @@ -73,7 +73,7 @@ public class FileTypeDetectUtils { /** *

Recommend to use this method to verify whether the file extension matches the file type * after matching the file type to avoid XSS attacks such as bypassing detection by polyglot - * file

+ * file.

* * @param mimeType file mime type,such as "image/png" * @param fileName file name,such as "test.png" diff --git a/api/src/main/java/run/halo/app/theme/router/PageUrlUtils.java b/api/src/main/java/run/halo/app/theme/router/PageUrlUtils.java index 0a1f114a3..b209b4ab9 100644 --- a/api/src/main/java/run/halo/app/theme/router/PageUrlUtils.java +++ b/api/src/main/java/run/halo/app/theme/router/PageUrlUtils.java @@ -1,12 +1,12 @@ package run.halo.app.theme.router; +import java.util.Objects; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.springframework.web.reactive.function.server.ServerRequest; import run.halo.app.extension.ListResult; import run.halo.app.infra.utils.PathUtils; -import java.util.Objects; /** * A utility class for template page url. diff --git a/application/src/main/java/run/halo/app/core/attachment/endpoint/AttachmentEndpoint.java b/application/src/main/java/run/halo/app/core/attachment/endpoint/AttachmentEndpoint.java index 6ecdd19e9..cbd93bfe8 100644 --- a/application/src/main/java/run/halo/app/core/attachment/endpoint/AttachmentEndpoint.java +++ b/application/src/main/java/run/halo/app/core/attachment/endpoint/AttachmentEndpoint.java @@ -118,9 +118,11 @@ public class AttachmentEndpoint implements CustomEndpoint { } public record UploadFromUrlRequest(@Schema(requiredMode = REQUIRED) URL url, - @Schema(requiredMode = REQUIRED) String policyName, - String groupName, - String filename) { + @Schema(requiredMode = REQUIRED, description = "Storage " + + "policy name") String policyName, + @Schema(description = "The name of the group to which the " + + "attachment belongs") String groupName, + @Schema(description = "Custom file name") String filename) { public UploadFromUrlRequest { if (Objects.isNull(url)) { throw new ServerWebInputException("Required url is missing."); diff --git a/application/src/main/java/run/halo/app/core/endpoint/uc/UcAttachmentEndpoint.java b/application/src/main/java/run/halo/app/core/endpoint/uc/UcAttachmentEndpoint.java index e3304d89d..c0b629556 100644 --- a/application/src/main/java/run/halo/app/core/endpoint/uc/UcAttachmentEndpoint.java +++ b/application/src/main/java/run/halo/app/core/endpoint/uc/UcAttachmentEndpoint.java @@ -333,8 +333,9 @@ public class UcAttachmentEndpoint implements CustomEndpoint { return GroupVersion.parseAPIVersion("uc.api.storage.halo.run/v1alpha1"); } + @Schema(name = "UcUploadFromUrlRequest") public record UploadFromUrlRequest(@Schema(requiredMode = REQUIRED) URL url, - String filename) { + @Schema(description = "Custom file name") String filename) { public UploadFromUrlRequest { if (Objects.isNull(url)) { throw new ServerWebInputException("Required url is missing."); diff --git a/application/src/main/java/run/halo/app/core/user/service/impl/DefaultAttachmentService.java b/application/src/main/java/run/halo/app/core/user/service/impl/DefaultAttachmentService.java index 525ed17e7..c79bdd934 100644 --- a/application/src/main/java/run/halo/app/core/user/service/impl/DefaultAttachmentService.java +++ b/application/src/main/java/run/halo/app/core/user/service/impl/DefaultAttachmentService.java @@ -147,14 +147,13 @@ public class DefaultAttachmentService implements AttachmentService { AtomicReference fileNameRef = new AtomicReference<>(filename); Mono> contentMono = dataBufferFetcher.head(uri) - .map(response -> { - var httpHeaders = response.getHeaders(); + .map(httpHeaders -> { if (!StringUtils.hasText(fileNameRef.get())) { fileNameRef.set(getExternalUrlFilename(uri, httpHeaders)); } MediaType contentType = httpHeaders.getContentType(); mediaTypeRef.set(contentType); - return response; + return httpHeaders; }) .map(response -> dataBufferFetcher.fetch(uri)); diff --git a/application/src/main/java/run/halo/app/infra/DefaultReactiveUrlDataBufferFetcher.java b/application/src/main/java/run/halo/app/infra/DefaultReactiveUrlDataBufferFetcher.java index c85f57a9d..996050aa6 100644 --- a/application/src/main/java/run/halo/app/infra/DefaultReactiveUrlDataBufferFetcher.java +++ b/application/src/main/java/run/halo/app/infra/DefaultReactiveUrlDataBufferFetcher.java @@ -2,10 +2,11 @@ package run.halo.app.infra; import java.net.URI; import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.ExchangeStrategies; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -21,6 +22,8 @@ import reactor.netty.http.client.HttpClient; public class DefaultReactiveUrlDataBufferFetcher implements ReactiveUrlDataBufferFetcher { private final HttpClient httpClient = HttpClient.create() .followRedirect(true); + private final ContentLengthFetcher contentLengthFetcher = new ContentLengthFetcher(); + private final WebClient webClient = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .build(); @@ -35,10 +38,32 @@ public class DefaultReactiveUrlDataBufferFetcher implements ReactiveUrlDataBuffe } @Override - public Mono> head(URI uri) { - return webClient.head() - .uri(uri) - .retrieve() - .toBodilessEntity(); + public Mono head(URI uri) { + return contentLengthFetcher.fetchContentLength(uri); + } + + static class ContentLengthFetcher { + + private final WebClient webClient; + + ContentLengthFetcher() { + this.webClient = WebClient.builder() + .exchangeStrategies(ExchangeStrategies.builder() + .codecs(config -> config.defaultCodecs().maxInMemorySize(1)) + .build()) + .build(); + } + + Mono fetchContentLength(URI url) { + return webClient.get() + .uri(url) + .exchangeToMono(response -> { + HttpHeaders headers = response.headers().asHttpHeaders(); + + return response.bodyToMono(byte[].class) + .onErrorResume(ex -> Mono.empty()) + .thenReturn(headers); + }); + } } } diff --git a/application/src/main/java/run/halo/app/infra/ReactiveUrlDataBufferFetcher.java b/application/src/main/java/run/halo/app/infra/ReactiveUrlDataBufferFetcher.java index 1d538f776..de6749286 100644 --- a/application/src/main/java/run/halo/app/infra/ReactiveUrlDataBufferFetcher.java +++ b/application/src/main/java/run/halo/app/infra/ReactiveUrlDataBufferFetcher.java @@ -2,7 +2,7 @@ package run.halo.app.infra; import java.net.URI; import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.http.ResponseEntity; +import org.springframework.http.HttpHeaders; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -28,5 +28,5 @@ public interface ReactiveUrlDataBufferFetcher { * @param uri uri to fetch * @return response entity */ - Mono> head(URI uri); + Mono head(URI uri); } diff --git a/application/src/test/java/run/halo/app/core/extension/attachment/endpoint/AttachmentEndpointTest.java b/application/src/test/java/run/halo/app/core/extension/attachment/endpoint/AttachmentEndpointTest.java index bd0f77d2b..83e350465 100644 --- a/application/src/test/java/run/halo/app/core/extension/attachment/endpoint/AttachmentEndpointTest.java +++ b/application/src/test/java/run/halo/app/core/extension/attachment/endpoint/AttachmentEndpointTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatusCode; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -303,10 +304,11 @@ class AttachmentEndpointTest { attachment.setMetadata(metadata); ResponseEntity response = new ResponseEntity<>(HttpStatusCode.valueOf(200)); + HttpHeaders headers = response.getHeaders(); DataBuffer dataBuffer = mock(DataBuffer.class); when(handler.upload(any())).thenReturn(Mono.just(attachment)); - when(dataBufferFetcher.head(any())).thenReturn(Mono.just(response)); + when(dataBufferFetcher.head(any())).thenReturn(Mono.just(headers)); when(dataBufferFetcher.fetch(any())).thenReturn(Flux.just(dataBuffer)); when(extensionGetter.getExtensions(AttachmentHandler.class)) .thenReturn(Flux.just(handler)); diff --git a/ui/console-src/modules/contents/attachments/components/AttachmentUploadModal.vue b/ui/console-src/modules/contents/attachments/components/AttachmentUploadModal.vue index d83f4d0ed..deda21efa 100644 --- a/ui/console-src/modules/contents/attachments/components/AttachmentUploadModal.vue +++ b/ui/console-src/modules/contents/attachments/components/AttachmentUploadModal.vue @@ -6,6 +6,8 @@ import { VDropdown, VDropdownItem, VModal, + VTabItem, + VTabs, } from "@halo-dev/components"; import { useLocalStorage } from "@vueuse/core"; import { onMounted, ref } from "vue"; @@ -18,6 +20,7 @@ import AttachmentGroupBadge from "./AttachmentGroupBadge.vue"; import AttachmentGroupEditingModal from "./AttachmentGroupEditingModal.vue"; import AttachmentPolicyBadge from "./AttachmentPolicyBadge.vue"; import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue"; +import UploadFromUrl from "./UploadFromUrl.vue"; const emit = defineEmits<{ (event: "close"): void; @@ -62,6 +65,8 @@ const onGroupEditingModalClose = async () => { await handleFetchGroups(); groupEditingModal.value = false; }; + +const activeTab = ref("upload"); - + +
+ + + + + + + + +
diff --git a/ui/console-src/modules/contents/attachments/components/UploadFromUrl.vue b/ui/console-src/modules/contents/attachments/components/UploadFromUrl.vue new file mode 100644 index 000000000..d80bf9d43 --- /dev/null +++ b/ui/console-src/modules/contents/attachments/components/UploadFromUrl.vue @@ -0,0 +1,75 @@ + + diff --git a/ui/packages/api-client/src/.openapi-generator/FILES b/ui/packages/api-client/src/.openapi-generator/FILES index 677de08db..b50839b39 100644 --- a/ui/packages/api-client/src/.openapi-generator/FILES +++ b/ui/packages/api-client/src/.openapi-generator/FILES @@ -322,6 +322,7 @@ models/thumbnail.ts models/totp-auth-link-response.ts models/totp-request.ts models/two-factor-auth-settings.ts +models/uc-upload-from-url-request.ts models/uc-upload-request-form-data.ts models/upgrade-from-uri-request.ts models/upload-from-url-request.ts diff --git a/ui/packages/api-client/src/api/attachment-v1alpha1-uc-api.ts b/ui/packages/api-client/src/api/attachment-v1alpha1-uc-api.ts index 0b192c36d..62fedb1fc 100644 --- a/ui/packages/api-client/src/api/attachment-v1alpha1-uc-api.ts +++ b/ui/packages/api-client/src/api/attachment-v1alpha1-uc-api.ts @@ -26,9 +26,9 @@ import { Attachment } from '../models'; // @ts-ignore import { AttachmentList } from '../models'; // @ts-ignore -import { UcUploadRequestFormData } from '../models'; +import { UcUploadFromUrlRequest } from '../models'; // @ts-ignore -import { UploadFromUrlRequest } from '../models'; +import { UcUploadRequestFormData } from '../models'; /** * AttachmentV1alpha1UcApi - axios parameter creator * @export @@ -100,14 +100,14 @@ export const AttachmentV1alpha1UcApiAxiosParamCreator = function (configuration? }, /** * Upload attachment from the given URL. - * @param {UploadFromUrlRequest} uploadFromUrlRequest + * @param {UcUploadFromUrlRequest} ucUploadFromUrlRequest * @param {boolean} [waitForPermalink] Wait for permalink. * @param {*} [options] Override http request option. * @throws {RequiredError} */ - externalTransferAttachment1: async (uploadFromUrlRequest: UploadFromUrlRequest, waitForPermalink?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'uploadFromUrlRequest' is not null or undefined - assertParamExists('externalTransferAttachment1', 'uploadFromUrlRequest', uploadFromUrlRequest) + externalTransferAttachment1: async (ucUploadFromUrlRequest: UcUploadFromUrlRequest, waitForPermalink?: boolean, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'ucUploadFromUrlRequest' is not null or undefined + assertParamExists('externalTransferAttachment1', 'ucUploadFromUrlRequest', ucUploadFromUrlRequest) const localVarPath = `/apis/uc.api.storage.halo.run/v1alpha1/attachments/-/upload-from-url`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -139,7 +139,7 @@ export const AttachmentV1alpha1UcApiAxiosParamCreator = function (configuration? setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(uploadFromUrlRequest, localVarRequestOptions, configuration) + localVarRequestOptions.data = serializeDataIfNeeded(ucUploadFromUrlRequest, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), @@ -303,13 +303,13 @@ export const AttachmentV1alpha1UcApiFp = function(configuration?: Configuration) }, /** * Upload attachment from the given URL. - * @param {UploadFromUrlRequest} uploadFromUrlRequest + * @param {UcUploadFromUrlRequest} ucUploadFromUrlRequest * @param {boolean} [waitForPermalink] Wait for permalink. * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async externalTransferAttachment1(uploadFromUrlRequest: UploadFromUrlRequest, waitForPermalink?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.externalTransferAttachment1(uploadFromUrlRequest, waitForPermalink, options); + async externalTransferAttachment1(ucUploadFromUrlRequest: UcUploadFromUrlRequest, waitForPermalink?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.externalTransferAttachment1(ucUploadFromUrlRequest, waitForPermalink, options); const localVarOperationServerIndex = configuration?.serverIndex ?? 0; const localVarOperationServerBasePath = operationServerMap['AttachmentV1alpha1UcApi.externalTransferAttachment1']?.[localVarOperationServerIndex]?.url; return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); @@ -372,7 +372,7 @@ export const AttachmentV1alpha1UcApiFactory = function (configuration?: Configur * @throws {RequiredError} */ externalTransferAttachment1(requestParameters: AttachmentV1alpha1UcApiExternalTransferAttachment1Request, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.externalTransferAttachment1(requestParameters.uploadFromUrlRequest, requestParameters.waitForPermalink, options).then((request) => request(axios, basePath)); + return localVarFp.externalTransferAttachment1(requestParameters.ucUploadFromUrlRequest, requestParameters.waitForPermalink, options).then((request) => request(axios, basePath)); }, /** * List attachments of the current user uploaded. @@ -438,10 +438,10 @@ export interface AttachmentV1alpha1UcApiCreateAttachmentForPostRequest { export interface AttachmentV1alpha1UcApiExternalTransferAttachment1Request { /** * - * @type {UploadFromUrlRequest} + * @type {UcUploadFromUrlRequest} * @memberof AttachmentV1alpha1UcApiExternalTransferAttachment1 */ - readonly uploadFromUrlRequest: UploadFromUrlRequest + readonly ucUploadFromUrlRequest: UcUploadFromUrlRequest /** * Wait for permalink. @@ -561,7 +561,7 @@ export class AttachmentV1alpha1UcApi extends BaseAPI { * @memberof AttachmentV1alpha1UcApi */ public externalTransferAttachment1(requestParameters: AttachmentV1alpha1UcApiExternalTransferAttachment1Request, options?: RawAxiosRequestConfig) { - return AttachmentV1alpha1UcApiFp(this.configuration).externalTransferAttachment1(requestParameters.uploadFromUrlRequest, requestParameters.waitForPermalink, options).then((request) => request(this.axios, this.basePath)); + return AttachmentV1alpha1UcApiFp(this.configuration).externalTransferAttachment1(requestParameters.ucUploadFromUrlRequest, requestParameters.waitForPermalink, options).then((request) => request(this.axios, this.basePath)); } /** diff --git a/ui/packages/api-client/src/models/index.ts b/ui/packages/api-client/src/models/index.ts index 525205e49..8483d201c 100644 --- a/ui/packages/api-client/src/models/index.ts +++ b/ui/packages/api-client/src/models/index.ts @@ -233,6 +233,7 @@ export * from './thumbnail-spec'; export * from './totp-auth-link-response'; export * from './totp-request'; export * from './two-factor-auth-settings'; +export * from './uc-upload-from-url-request'; export * from './uc-upload-request-form-data'; export * from './upgrade-from-uri-request'; export * from './upload-from-url-request'; diff --git a/ui/packages/api-client/src/models/uc-upload-from-url-request.ts b/ui/packages/api-client/src/models/uc-upload-from-url-request.ts new file mode 100644 index 000000000..921a70396 --- /dev/null +++ b/ui/packages/api-client/src/models/uc-upload-from-url-request.ts @@ -0,0 +1,36 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.21.0-SNAPSHOT + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface UcUploadFromUrlRequest + */ +export interface UcUploadFromUrlRequest { + /** + * Custom file name + * @type {string} + * @memberof UcUploadFromUrlRequest + */ + 'filename'?: string; + /** + * + * @type {string} + * @memberof UcUploadFromUrlRequest + */ + 'url': string; +} + diff --git a/ui/packages/api-client/src/models/upload-from-url-request.ts b/ui/packages/api-client/src/models/upload-from-url-request.ts index 3c9bb5774..0380f6378 100644 --- a/ui/packages/api-client/src/models/upload-from-url-request.ts +++ b/ui/packages/api-client/src/models/upload-from-url-request.ts @@ -21,11 +21,23 @@ */ export interface UploadFromUrlRequest { /** - * + * Custom file name * @type {string} * @memberof UploadFromUrlRequest */ 'filename'?: string; + /** + * The name of the group to which the attachment belongs + * @type {string} + * @memberof UploadFromUrlRequest + */ + 'groupName'?: string; + /** + * Storage policy name + * @type {string} + * @memberof UploadFromUrlRequest + */ + 'policyName': string; /** * * @type {string} diff --git a/ui/src/locales/_missing_translations_es.yaml b/ui/src/locales/_missing_translations_es.yaml index 4962aa319..cc13a275c 100644 --- a/ui/src/locales/_missing_translations_es.yaml +++ b/ui/src/locales/_missing_translations_es.yaml @@ -222,6 +222,16 @@ core: policy_editing_modal: toast: policy_name_exists: Storage policy name already exists + upload_modal: + upload_options: + local_upload: Upload + download: Download from url + download_form: + fields: + url: + label: URL + toast: + success: Downloaded successfully uc_attachment: empty: title: There are no attachments. diff --git a/ui/src/locales/en.yaml b/ui/src/locales/en.yaml index 691c4ee0a..05937d239 100644 --- a/ui/src/locales/en.yaml +++ b/ui/src/locales/en.yaml @@ -158,8 +158,8 @@ core: back: title: Layout not saved description: >- - The current layout has not been saved, if you leave, the current layout - will be lost, do you want to continue? + The current layout has not been saved, if you leave, the current + layout will be lost, do you want to continue? confirm_text: Leave change_breakpoint: tips_not_saved: Please save the current layout first @@ -754,6 +754,15 @@ core: title: No storage policy description: Before uploading, a new storage policy needs to be created. not_select: Please select a storage policy first. + upload_options: + local_upload: Upload + download: Download from url + download_form: + fields: + url: + label: URL + toast: + success: Downloaded successfully select_modal: title: Select attachment providers: diff --git a/ui/src/locales/zh-CN.yaml b/ui/src/locales/zh-CN.yaml index 55934a8bd..b5453f761 100644 --- a/ui/src/locales/zh-CN.yaml +++ b/ui/src/locales/zh-CN.yaml @@ -711,6 +711,15 @@ core: title: 没有存储策略 description: 在上传之前,需要新建一个存储策略 not_select: 请先选择存储策略 + upload_options: + local_upload: 本地上传 + download: 通过链接下载 + download_form: + fields: + url: + label: 链接地址 + toast: + success: 下载成功 select_modal: title: 选择附件 providers: diff --git a/ui/src/locales/zh-TW.yaml b/ui/src/locales/zh-TW.yaml index 22a81ce8d..4009f5a6e 100644 --- a/ui/src/locales/zh-TW.yaml +++ b/ui/src/locales/zh-TW.yaml @@ -696,6 +696,15 @@ core: title: 沒有存儲策略 description: 在上傳之前,需要新建一個存儲策略 not_select: 請先選擇存儲策略 + upload_options: + local_upload: 本地上傳 + download: 通過鏈接下載 + download_form: + fields: + url: + label: 鏈接地址 + toast: + success: 下載成功 select_modal: title: 選擇附件 providers: