mirror of
https://github.com/halo-dev/plugin-s3.git
synced 2025-10-16 07:19:45 +00:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
40f66ff665 | ||
![]() |
cb968de154 | ||
![]() |
bdd62cbe12 | ||
![]() |
2a26171fc4 | ||
![]() |
6b62ce7aa4 | ||
![]() |
c60e31a033 | ||
![]() |
7a9b0de0c6 |
@@ -10,6 +10,7 @@ export function getApisS3OsHaloRunV1Alpha1ObjectsByPolicyName(params: GetApisS3O
|
|||||||
continuationObject: params.continuationObject,
|
continuationObject: params.continuationObject,
|
||||||
pageSize: params.pageSize,
|
pageSize: params.pageSize,
|
||||||
unlinked: params.unlinked,
|
unlinked: params.unlinked,
|
||||||
|
filePrefix: params.filePrefix,
|
||||||
};
|
};
|
||||||
return request.get<DeepRequired<S3ListResult>>(`/apis/s3os.halo.run/v1alpha1/objects/${params.policyName}`, {
|
return request.get<DeepRequired<S3ListResult>>(`/apis/s3os.halo.run/v1alpha1/objects/${params.policyName}`, {
|
||||||
params: paramsInput,
|
params: paramsInput,
|
||||||
@@ -22,4 +23,5 @@ interface GetApisS3OsHaloRunV1Alpha1ObjectsByPolicyNameParams {
|
|||||||
continuationObject?: any;
|
continuationObject?: any;
|
||||||
pageSize: any;
|
pageSize: any;
|
||||||
unlinked?: any;
|
unlinked?: any;
|
||||||
|
filePrefix?: any;
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,9 @@ request.interceptors.response.use(
|
|||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
const { status } = errorResponse;
|
const { status } = errorResponse;
|
||||||
if (status !== 200) {
|
if (status === 400) {
|
||||||
|
Toast.error(errorResponse.data.detail);
|
||||||
|
} else if (status !== 200) {
|
||||||
Toast.error("status: " + status);
|
Toast.error("status: " + status);
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
|
@@ -14,6 +14,7 @@ import {
|
|||||||
VTag,
|
VTag,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
import CarbonFolderDetailsReference from "~icons/carbon/folder-details-reference";
|
import CarbonFolderDetailsReference from "~icons/carbon/folder-details-reference";
|
||||||
|
import IconErrorWarning from "~icons/ri/error-warning-line";
|
||||||
import {computed, onMounted, ref, watch} from "vue";
|
import {computed, onMounted, ref, watch} from "vue";
|
||||||
import {
|
import {
|
||||||
getApisS3OsHaloRunV1Alpha1ObjectsByPolicyName,
|
getApisS3OsHaloRunV1Alpha1ObjectsByPolicyName,
|
||||||
@@ -27,10 +28,14 @@ const policyName = ref<string>("");
|
|||||||
const page = ref(1);
|
const page = ref(1);
|
||||||
const size = ref(50);
|
const size = ref(50);
|
||||||
const policyOptions = ref<{ label: string; value: string; attrs: any }[]>([{
|
const policyOptions = ref<{ label: string; value: string; attrs: any }[]>([{
|
||||||
label: "请选择策略",
|
label: "请选择存储策略",
|
||||||
value: "",
|
value: "",
|
||||||
attrs: {disabled: true}
|
attrs: {disabled: true}
|
||||||
}]);
|
}]);
|
||||||
|
// update when fetch first page
|
||||||
|
const filePrefix = ref<string>("");
|
||||||
|
// update when user input
|
||||||
|
const filePrefixBind = ref<string>("");
|
||||||
const s3Objects = ref<S3ListResult>({
|
const s3Objects = ref<S3ListResult>({
|
||||||
objects: [],
|
objects: [],
|
||||||
hasMore: false,
|
hasMore: false,
|
||||||
@@ -58,15 +63,15 @@ const selectedLinkedStatusItem = ref<boolean | undefined>(linkedStatusItems[0].v
|
|||||||
|
|
||||||
const emptyTips = computed(() => {
|
const emptyTips = computed(() => {
|
||||||
if (isFetchingPolicies.value) {
|
if (isFetchingPolicies.value) {
|
||||||
return "正在加载策略";
|
return "正在加载存储策略";
|
||||||
} else {
|
} else {
|
||||||
if (policyOptions.value.length <= 1) {
|
if (policyOptions.value.length <= 1) {
|
||||||
return "没有可用的策略,请前往【附件】添加S3策略";
|
return "没有可用的存储策略,请前往【附件】添加S3存储策略";
|
||||||
} else {
|
} else {
|
||||||
if (!policyName.value) {
|
if (!policyName.value) {
|
||||||
return "请在左上方选择策略";
|
return "请在左上方选择存储策略";
|
||||||
} else {
|
} else {
|
||||||
return "该策略的 桶/文件夹 下没有文件";
|
return "该存储策略的 桶/文件夹 下没有文件";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,7 +97,7 @@ const fetchPolicies = async () => {
|
|||||||
const policiesData = await getApisS3OsHaloRunV1Alpha1PoliciesS3();
|
const policiesData = await getApisS3OsHaloRunV1Alpha1PoliciesS3();
|
||||||
if (policiesData.status == 200) {
|
if (policiesData.status == 200) {
|
||||||
policyOptions.value = [{
|
policyOptions.value = [{
|
||||||
label: "请选择策略",
|
label: "请选择存储策略",
|
||||||
value: "",
|
value: "",
|
||||||
attrs: {disabled: true}
|
attrs: {disabled: true}
|
||||||
}];
|
}];
|
||||||
@@ -139,6 +144,8 @@ const clearTokenAndObject = () => {
|
|||||||
s3Objects.value.nextContinuationObject = "";
|
s3Objects.value.nextContinuationObject = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// filePrefix will not be updated from user input
|
||||||
|
// if you want to update filePrefix, please call `handleFirstPage`
|
||||||
const fetchObjects = async () => {
|
const fetchObjects = async () => {
|
||||||
if (!policyName.value) {
|
if (!policyName.value) {
|
||||||
return;
|
return;
|
||||||
@@ -152,6 +159,7 @@ const fetchObjects = async () => {
|
|||||||
continuationToken: s3Objects.value.currentToken,
|
continuationToken: s3Objects.value.currentToken,
|
||||||
continuationObject: s3Objects.value.currentContinuationObject,
|
continuationObject: s3Objects.value.currentContinuationObject,
|
||||||
unlinked: selectedLinkedStatusItem.value,
|
unlinked: selectedLinkedStatusItem.value,
|
||||||
|
filePrefix: filePrefix.value
|
||||||
});
|
});
|
||||||
if (objectsData.status == 200) {
|
if (objectsData.status == 200) {
|
||||||
s3Objects.value = objectsData.data;
|
s3Objects.value = objectsData.data;
|
||||||
@@ -222,6 +230,7 @@ const handleFirstPage = () => {
|
|||||||
isFetching.value = true;
|
isFetching.value = true;
|
||||||
page.value = 1;
|
page.value = 1;
|
||||||
clearTokenAndObject();
|
clearTokenAndObject();
|
||||||
|
filePrefix.value = filePrefixBind.value;
|
||||||
fetchObjects();
|
fetchObjects();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -255,18 +264,26 @@ const handleModalClose = () => {
|
|||||||
<div class="flex w-full flex-1 items-center sm:w-auto">
|
<div class="flex w-full flex-1 items-center sm:w-auto">
|
||||||
<div
|
<div
|
||||||
v-if="!selectedFiles.length"
|
v-if="!selectedFiles.length"
|
||||||
class="flex items-center gap-2"
|
class="flex flex-wrap items-center gap-2"
|
||||||
>
|
>
|
||||||
策略:
|
<span class="whitespace-nowrap">存储策略:</span>
|
||||||
<FormKit
|
<FormKit
|
||||||
id="policyChoose"
|
id="policyChoose"
|
||||||
outer-class="!p-0 w-48"
|
outer-class="!p-0"
|
||||||
|
style="min-width: 10rem;"
|
||||||
v-model="policyName"
|
v-model="policyName"
|
||||||
name="policyName"
|
name="policyName"
|
||||||
type="select"
|
type="select"
|
||||||
:options="policyOptions"
|
:options="policyOptions"
|
||||||
@change="fetchObjects()"
|
@change="handleFirstPage"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
|
<icon-error-warning v-if="!policyName" class="text-red-500"/>
|
||||||
|
<SearchInput
|
||||||
|
v-model="filePrefixBind"
|
||||||
|
v-if="policyName"
|
||||||
|
placeholder="请输入文件名前缀搜索"
|
||||||
|
@update:modelValue="handleFirstPage"
|
||||||
|
></SearchInput>
|
||||||
</div>
|
</div>
|
||||||
<VSpace v-else>
|
<VSpace v-else>
|
||||||
<VButton type="primary" @click="handleLink">
|
<VButton type="primary" @click="handleLink">
|
||||||
|
@@ -28,13 +28,13 @@ public class S3LinkController {
|
|||||||
@RequestParam(name = "continuationToken", required = false) String continuationToken,
|
@RequestParam(name = "continuationToken", required = false) String continuationToken,
|
||||||
@RequestParam(name = "continuationObject", required = false) String continuationObject,
|
@RequestParam(name = "continuationObject", required = false) String continuationObject,
|
||||||
@RequestParam(name = "pageSize") Integer pageSize,
|
@RequestParam(name = "pageSize") Integer pageSize,
|
||||||
@RequestParam(name = "unlinked", required = false, defaultValue = "false")
|
@RequestParam(name = "unlinked", required = false, defaultValue = "false") Boolean unlinked,
|
||||||
Boolean unlinked) {
|
@RequestParam(name = "filePrefix", required = false) String filePrefix) {
|
||||||
if (unlinked) {
|
if (unlinked) {
|
||||||
return s3LinkService.listObjectsUnlinked(policyName, continuationToken,
|
return s3LinkService.listObjectsUnlinked(policyName, continuationToken,
|
||||||
continuationObject, pageSize);
|
continuationObject, pageSize, filePrefix);
|
||||||
} else {
|
} else {
|
||||||
return s3LinkService.listObjects(policyName, continuationToken, pageSize);
|
return s3LinkService.listObjects(policyName, continuationToken, pageSize, filePrefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,10 +10,10 @@ public interface S3LinkService {
|
|||||||
Flux<Policy> listS3Policies();
|
Flux<Policy> listS3Policies();
|
||||||
|
|
||||||
Mono<S3ListResult> listObjects(String policyName, String continuationToken,
|
Mono<S3ListResult> listObjects(String policyName, String continuationToken,
|
||||||
Integer pageSize);
|
Integer pageSize, String filePrefix);
|
||||||
|
|
||||||
Mono<LinkResult> addAttachmentRecords(String policyName, Set<String> objectKeys);
|
Mono<LinkResult> addAttachmentRecords(String policyName, Set<String> objectKeys);
|
||||||
|
|
||||||
Mono<S3ListResult> listObjectsUnlinked(String policyName, String continuationToken,
|
Mono<S3ListResult> listObjectsUnlinked(String policyName, String continuationToken,
|
||||||
String continuationObject, Integer pageSize);
|
String continuationObject, Integer pageSize, String filePrefix);
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package run.halo.s3os;
|
|||||||
import static run.halo.s3os.S3OsAttachmentHandler.OBJECT_KEY;
|
import static run.halo.s3os.S3OsAttachmentHandler.OBJECT_KEY;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -14,6 +15,7 @@ import java.util.stream.Collectors;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||||
@@ -53,12 +55,12 @@ public class S3LinkServiceImpl implements S3LinkService {
|
|||||||
@Override
|
@Override
|
||||||
public Flux<Policy> listS3Policies() {
|
public Flux<Policy> listS3Policies() {
|
||||||
return client.list(Policy.class, (policy) -> "s3os".equals(
|
return client.list(Policy.class, (policy) -> "s3os".equals(
|
||||||
policy.getSpec().getTemplateName()), null);
|
policy.getSpec().getTemplateName()), Comparator.naturalOrder());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<S3ListResult> listObjects(String policyName, String continuationToken,
|
public Mono<S3ListResult> listObjects(String policyName, String continuationToken,
|
||||||
Integer pageSize) {
|
Integer pageSize, String filePrefix) {
|
||||||
return client.fetch(Policy.class, policyName)
|
return client.fetch(Policy.class, policyName)
|
||||||
.flatMap((policy) -> {
|
.flatMap((policy) -> {
|
||||||
var configMapName = policy.getSpec().getConfigMapName();
|
var configMapName = policy.getSpec().getConfigMapName();
|
||||||
@@ -68,11 +70,11 @@ public class S3LinkServiceImpl implements S3LinkService {
|
|||||||
var properties = handler.getProperties(configMap);
|
var properties = handler.getProperties(configMap);
|
||||||
var finalLocation = FilePathUtils.getFilePathByPlaceholder(properties.getLocation());
|
var finalLocation = FilePathUtils.getFilePathByPlaceholder(properties.getLocation());
|
||||||
return Mono.using(() -> handler.buildS3Client(properties),
|
return Mono.using(() -> handler.buildS3Client(properties),
|
||||||
|
// 执行 listObjects
|
||||||
(s3Client) -> Mono.fromCallable(
|
(s3Client) -> Mono.fromCallable(
|
||||||
() -> s3Client.listObjectsV2(ListObjectsV2Request.builder()
|
() -> s3Client.listObjectsV2(ListObjectsV2Request.builder()
|
||||||
.bucket(properties.getBucket())
|
.bucket(properties.getBucket())
|
||||||
.prefix(StringUtils.isNotEmpty(finalLocation)
|
.prefix(buildPrefix(finalLocation, filePrefix))
|
||||||
? finalLocation + "/" : null)
|
|
||||||
.delimiter("/")
|
.delimiter("/")
|
||||||
.maxKeys(pageSize)
|
.maxKeys(pageSize)
|
||||||
.continuationToken(StringUtils.isNotEmpty(continuationToken)
|
.continuationToken(StringUtils.isNotEmpty(continuationToken)
|
||||||
@@ -81,14 +83,16 @@ public class S3LinkServiceImpl implements S3LinkService {
|
|||||||
S3Client::close)
|
S3Client::close)
|
||||||
.flatMap(listObjectsV2Response -> {
|
.flatMap(listObjectsV2Response -> {
|
||||||
List<S3Object> contents = listObjectsV2Response.contents();
|
List<S3Object> contents = listObjectsV2Response.contents();
|
||||||
|
// 过滤掉目录并转换为ObjectVo
|
||||||
var objectVos = contents
|
var objectVos = contents
|
||||||
.stream().map(S3ListResult.ObjectVo::fromS3Object)
|
.stream().map(S3ListResult.ObjectVo::fromS3Object)
|
||||||
.filter(objectVo -> !objectVo.getKey().endsWith("/"))
|
.filter(objectVo -> !objectVo.getKey().endsWith("/"))
|
||||||
.collect(Collectors.toMap(S3ListResult.ObjectVo::getKey, o -> o));
|
.collect(Collectors.toMap(S3ListResult.ObjectVo::getKey, o -> o));
|
||||||
|
// 获取已经关联的附件并标记
|
||||||
ListOptions listOptions = new ListOptions();
|
ListOptions listOptions = new ListOptions();
|
||||||
listOptions.setFieldSelector(
|
listOptions.setFieldSelector(
|
||||||
FieldSelector.of(QueryFactory.equal("spec.policyName", policyName)));
|
FieldSelector.of(QueryFactory.equal("spec.policyName", policyName)));
|
||||||
return client.listAll(Attachment.class, listOptions, null)
|
return client.listAll(Attachment.class, listOptions, Sort.unsorted())
|
||||||
.doOnNext(attachment -> {
|
.doOnNext(attachment -> {
|
||||||
S3ListResult.ObjectVo objectVo =
|
S3ListResult.ObjectVo objectVo =
|
||||||
objectVos.get(attachment.getMetadata().getAnnotations()
|
objectVos.get(attachment.getMetadata().getAnnotations()
|
||||||
@@ -130,7 +134,7 @@ public class S3LinkServiceImpl implements S3LinkService {
|
|||||||
ListOptions listOptions = new ListOptions();
|
ListOptions listOptions = new ListOptions();
|
||||||
listOptions.setFieldSelector(
|
listOptions.setFieldSelector(
|
||||||
FieldSelector.of(QueryFactory.equal("spec.policyName", policyName)));
|
FieldSelector.of(QueryFactory.equal("spec.policyName", policyName)));
|
||||||
return client.listAll(Attachment.class, listOptions, null)
|
return client.listAll(Attachment.class, listOptions, Sort.unsorted())
|
||||||
.filter(attachment -> StringUtils.isNotBlank(
|
.filter(attachment -> StringUtils.isNotBlank(
|
||||||
MetadataUtil.nullSafeAnnotations(attachment).get(S3OsAttachmentHandler.OBJECT_KEY))
|
MetadataUtil.nullSafeAnnotations(attachment).get(S3OsAttachmentHandler.OBJECT_KEY))
|
||||||
&& objectKeys.contains(
|
&& objectKeys.contains(
|
||||||
@@ -163,7 +167,7 @@ public class S3LinkServiceImpl implements S3LinkService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<S3ListResult> listObjectsUnlinked(String policyName, String continuationToken,
|
public Mono<S3ListResult> listObjectsUnlinked(String policyName, String continuationToken,
|
||||||
String continuationObject, Integer pageSize) {
|
String continuationObject, Integer pageSize, String filePrefix) {
|
||||||
// TODO 优化成查一次数据库
|
// TODO 优化成查一次数据库
|
||||||
return Mono.defer(() -> {
|
return Mono.defer(() -> {
|
||||||
List<S3ListResult.ObjectVo> s3Objects = new ArrayList<>();
|
List<S3ListResult.ObjectVo> s3Objects = new ArrayList<>();
|
||||||
@@ -172,7 +176,8 @@ public class S3LinkServiceImpl implements S3LinkService {
|
|||||||
|
|
||||||
return Flux.defer(() -> Flux.just(
|
return Flux.defer(() -> Flux.just(
|
||||||
new TokenState(null, currToken.get() == null ? "" : currToken.get())))
|
new TokenState(null, currToken.get() == null ? "" : currToken.get())))
|
||||||
.flatMap(tokenState -> listObjects(policyName, tokenState.nextToken, pageSize))
|
.flatMap(tokenState -> listObjects(policyName, tokenState.nextToken,
|
||||||
|
pageSize, filePrefix))
|
||||||
.flatMap(s3ListResult -> {
|
.flatMap(s3ListResult -> {
|
||||||
var filteredObjects = s3ListResult.getObjects();
|
var filteredObjects = s3ListResult.getObjects();
|
||||||
if (!continuationObjectMatched.get()) {
|
if (!continuationObjectMatched.get()) {
|
||||||
@@ -267,4 +272,18 @@ public class S3LinkServiceImpl implements S3LinkService {
|
|||||||
.flatMap(func);
|
.flatMap(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String buildPrefix(String finalLocation, String filePrefix) {
|
||||||
|
if (StringUtils.isBlank(finalLocation) && StringUtils.isBlank(filePrefix)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (StringUtils.isNotBlank(finalLocation)) {
|
||||||
|
sb.append(finalLocation).append("/");
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(filePrefix)) {
|
||||||
|
sb.append(filePrefix);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
package run.halo.s3os;
|
package run.halo.s3os;
|
||||||
|
|
||||||
import org.pf4j.PluginWrapper;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import run.halo.app.plugin.BasePlugin;
|
import run.halo.app.plugin.BasePlugin;
|
||||||
|
import run.halo.app.plugin.PluginContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author johnniang
|
* @author johnniang
|
||||||
@@ -11,8 +11,8 @@ import run.halo.app.plugin.BasePlugin;
|
|||||||
@Component
|
@Component
|
||||||
public class S3OsPlugin extends BasePlugin {
|
public class S3OsPlugin extends BasePlugin {
|
||||||
|
|
||||||
public S3OsPlugin(PluginWrapper wrapper) {
|
public S3OsPlugin(PluginContext pluginContext) {
|
||||||
super(wrapper);
|
super(pluginContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -46,9 +46,10 @@ metadata:
|
|||||||
name: role-template-s3os-policy-config-validation
|
name: role-template-s3os-policy-config-validation
|
||||||
labels:
|
labels:
|
||||||
halo.run/role-template: "true"
|
halo.run/role-template: "true"
|
||||||
|
halo.run/hidden: "true"
|
||||||
rbac.authorization.halo.run/aggregate-to-role-template-manage-configmaps: "true"
|
rbac.authorization.halo.run/aggregate-to-role-template-manage-configmaps: "true"
|
||||||
rules:
|
rules:
|
||||||
- apiGroups: ["s3os.halo.run"]
|
- apiGroups: ["s3os.halo.run"]
|
||||||
resources: ["policies/validation"]
|
resources: ["policies/validation"]
|
||||||
resourceNames: ["s3"]
|
resourceNames: ["s3"]
|
||||||
verbs: [ "create" ]
|
verbs: [ "create" ]
|
||||||
|
@@ -7,8 +7,5 @@ spec:
|
|||||||
- group: basic
|
- group: basic
|
||||||
label: 使用提示
|
label: 使用提示
|
||||||
formSchema:
|
formSchema:
|
||||||
- $formkit: text
|
- $el: p
|
||||||
help: 请前往 “附件 - 存储策略” 添加策略
|
children: 请前往 “附件 - 存储策略” 添加策略
|
||||||
label: 此处不用设置,请前往 “附件 - 存储策略” 添加策略
|
|
||||||
name: text
|
|
||||||
placeholder: 此处不用设置,请前往 “附件 - 存储策略” 添加策略
|
|
||||||
|
@@ -6,12 +6,14 @@ spec:
|
|||||||
enabled: true
|
enabled: true
|
||||||
requires: ">=2.14.0"
|
requires: ">=2.14.0"
|
||||||
author:
|
author:
|
||||||
name: Halo OSS Team
|
name: Halo
|
||||||
website: https://github.com/halo-dev
|
website: https://github.com/halo-dev
|
||||||
logo: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAABUdJREFUeF7tmmtoXFUQx3+zMYGi/RJRULRFsL6oim2klVK0CrVqKxjoJ0Vt1bR7t6lURagiFlGpFPogydklVo1i/VB84QMfqFijpflQrI+IYj7FF+0HMbUEI82O3NNtzG6y956bvXd3IXvgksCd+c/M/8w598ycFWb5kFkePw0CGhkwyxloLIFZngCNTbCxBBpLoI4Y0A6aydNKC62kaAXGSDHCv4zQzIh0MRa3uzVdAppmMSmWoVyL0IZyWUiA3wL7gI/FcCQOMmpCQCHwDpSOCoIYQOmWLK9WgFHdr4Bu4wyOsR14uBKni3SFdxhnl+T4fCaYVcsAzbCEPNsRbpiJow46u8TwkINcCX9RNWYgr2nWIrwAzJ2BehSVAVpYJbv5y1Up8QwoBL/f1aGY5JaJ4aALVqIEqMc24EkXRxKQmSeGX8JwEyNAPR4AesMcSPD9ECdpk15GgmwkQoB2spxxPgWaEwzQBfptMdxRfQI8PgJWuniYuIyyQbLlMzH2DFCPDUAu8cDcDRwWQ1s58VgJ0E7OYZyvgAXu/lVBMiAL4iXA4z5gbxVCimpiQAxLp1OKl4AMfSj3RPWuKvJ5FkmOr0ttxUuAx9/AWVUJKKoR5QnJ8nRsBOh65tJiy9nFYJ+fwB586nUcEsN1FROgHncDG2EqWIWR/wpcUCFGsPooc6SPfyYLOS8BzbAI5RlgVQJObrXdH9iZAPb/kCkul25+jEyAejboDxJyLiMG42Orx2fAioTs+LC3iOHDSARohs0oexJxStgtPWw5jZ34IUpJS7b4kBa4BDTDTSifJBI8vC+G1ZOxdQtzGGMIOD8hm1vF2I7UxAgmwLNpn8SaHyTPjZLjWGmg6tl9YCIrYiVCuVOyvOZEQGG3fzlWB06BjZJipXTbI/OUoWmuQvgmAbs+5HIxfOlKwCFgSQKOrBNDXxCuerwH3Ba77ZPMl16GQwnQjSwkxXexO6A8JdnwDpF6tANvxG1fzNQu+LR7QEKtrFfEuNcJ6vEzcHGMJEzZdH3scgRkC6e9eOwr/Qi3iuGEK6B6PAo85yofKid40oMfV9EoR8CbENxKCjVYvNCWSg8DUXQKn8TRKDqBstOs/6AMiLelVaYUDQtO0zyPcH+YnMP7d8Vw+3Ry5TLgJeBeB2BXEX8mDwDHEUZQWzafEBNcPca2FwlrpMd+WaaM6QnI8CyKX6AkN/KsmHyfZ9voQjfKUbDPOMoCxF6TVzLKzn75JZBmOcIXlVgN0y39JGka/yOVDtOL/D5g9ssS4L9Qz5aNl0Y26KZwRAzXTBZVj8PAIjd1Z6ntYoIzuWwtoBkeK9T/ztacBUurwA1cQRODzvpugvvEcFeYaHkCHuFMRvGPwwvDQCK+P0qexZLjt9N6mmE9am+P4xnKn5LlbBew4GowzTqEF12AnGXytEuOtyaC30aKY7YRcr0zRrDg92K40hUrtCWmGfajrHUFDJHzf9tzc8na9zu1j8eCL/RKj72Zch6hBPhI6tEFbHJGnV5wSEzxjVGMrbZBlB2SJXL57kRAgQS/G7wDODcyEUKX9LC5ZOb9+wO/lV5J9+c4sIsmdkoX/v+RhzMBloRNXESeB8E+4UNtSbtHsvSXCqt/1oB2xNYc88PBiiT8uuIgTeyVLn6IqFskHomAiY0rwyUoa1BWI3YGzyu8+wPld1K8Tp5+yeL/ri90aCdXM26JuBCYN/GcOjYPIwzbv3CYJg5IF/4dQixjRgTEYrlOQBoE1MlE1MyNRgbUjPo6MdzIgDqZiJq50ciAmlFfJ4YbGVAnE1EzN2Z9BvwH8mZfUOuuxhIAAAAASUVORK5CYII=
|
logo: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAABUdJREFUeF7tmmtoXFUQx3+zMYGi/RJRULRFsL6oim2klVK0CrVqKxjoJ0Vt1bR7t6lURagiFlGpFPogydklVo1i/VB84QMfqFijpflQrI+IYj7FF+0HMbUEI82O3NNtzG6y956bvXd3IXvgksCd+c/M/8w598ycFWb5kFkePw0CGhkwyxloLIFZngCNTbCxBBpLoI4Y0A6aydNKC62kaAXGSDHCv4zQzIh0MRa3uzVdAppmMSmWoVyL0IZyWUiA3wL7gI/FcCQOMmpCQCHwDpSOCoIYQOmWLK9WgFHdr4Bu4wyOsR14uBKni3SFdxhnl+T4fCaYVcsAzbCEPNsRbpiJow46u8TwkINcCX9RNWYgr2nWIrwAzJ2BehSVAVpYJbv5y1Up8QwoBL/f1aGY5JaJ4aALVqIEqMc24EkXRxKQmSeGX8JwEyNAPR4AesMcSPD9ECdpk15GgmwkQoB2spxxPgWaEwzQBfptMdxRfQI8PgJWuniYuIyyQbLlMzH2DFCPDUAu8cDcDRwWQ1s58VgJ0E7OYZyvgAXu/lVBMiAL4iXA4z5gbxVCimpiQAxLp1OKl4AMfSj3RPWuKvJ5FkmOr0ttxUuAx9/AWVUJKKoR5QnJ8nRsBOh65tJiy9nFYJ+fwB586nUcEsN1FROgHncDG2EqWIWR/wpcUCFGsPooc6SPfyYLOS8BzbAI5RlgVQJObrXdH9iZAPb/kCkul25+jEyAejboDxJyLiMG42Orx2fAioTs+LC3iOHDSARohs0oexJxStgtPWw5jZ34IUpJS7b4kBa4BDTDTSifJBI8vC+G1ZOxdQtzGGMIOD8hm1vF2I7UxAgmwLNpn8SaHyTPjZLjWGmg6tl9YCIrYiVCuVOyvOZEQGG3fzlWB06BjZJipXTbI/OUoWmuQvgmAbs+5HIxfOlKwCFgSQKOrBNDXxCuerwH3Ba77ZPMl16GQwnQjSwkxXexO6A8JdnwDpF6tANvxG1fzNQu+LR7QEKtrFfEuNcJ6vEzcHGMJEzZdH3scgRkC6e9eOwr/Qi3iuGEK6B6PAo85yofKid40oMfV9EoR8CbENxKCjVYvNCWSg8DUXQKn8TRKDqBstOs/6AMiLelVaYUDQtO0zyPcH+YnMP7d8Vw+3Ry5TLgJeBeB2BXEX8mDwDHEUZQWzafEBNcPca2FwlrpMd+WaaM6QnI8CyKX6AkN/KsmHyfZ9voQjfKUbDPOMoCxF6TVzLKzn75JZBmOcIXlVgN0y39JGka/yOVDtOL/D5g9ssS4L9Qz5aNl0Y26KZwRAzXTBZVj8PAIjd1Z6ntYoIzuWwtoBkeK9T/ztacBUurwA1cQRODzvpugvvEcFeYaHkCHuFMRvGPwwvDQCK+P0qexZLjt9N6mmE9am+P4xnKn5LlbBew4GowzTqEF12AnGXytEuOtyaC30aKY7YRcr0zRrDg92K40hUrtCWmGfajrHUFDJHzf9tzc8na9zu1j8eCL/RKj72Zch6hBPhI6tEFbHJGnV5wSEzxjVGMrbZBlB2SJXL57kRAgQS/G7wDODcyEUKX9LC5ZOb9+wO/lV5J9+c4sIsmdkoX/v+RhzMBloRNXESeB8E+4UNtSbtHsvSXCqt/1oB2xNYc88PBiiT8uuIgTeyVLn6IqFskHomAiY0rwyUoa1BWI3YGzyu8+wPld1K8Tp5+yeL/ri90aCdXM26JuBCYN/GcOjYPIwzbv3CYJg5IF/4dQixjRgTEYrlOQBoE1MlE1MyNRgbUjPo6MdzIgDqZiJq50ciAmlFfJ4YbGVAnE1EzN2Z9BvwH8mZfUOuuxhIAAAAASUVORK5CYII=
|
||||||
settingName: s3os-settings
|
settingName: s3os-settings
|
||||||
configMapName: s3os-configMap
|
configMapName: s3os-configMap
|
||||||
homepage: https://github.com/halo-dev/plugin-s3
|
homepage: https://www.halo.run/store/apps/app-Qxhpp
|
||||||
|
repo: https://github.com/halo-dev/plugin-s3
|
||||||
|
issues: https://github.com/halo-dev/plugin-s3/issues
|
||||||
displayName: "对象存储(Amazon S3 协议)"
|
displayName: "对象存储(Amazon S3 协议)"
|
||||||
description: "提供兼容 Amazon S3 协议的对象存储策略,兼容阿里云、腾讯云、七牛云等"
|
description: "提供兼容 Amazon S3 协议的对象存储策略,兼容阿里云、腾讯云、七牛云等"
|
||||||
license:
|
license:
|
||||||
|
Reference in New Issue
Block a user