private Mono<Void> setBucketLifecycle(String bucket, int expireAfterDays) {
        if (expireAfterDays <= 0) {
            return Mono.empty();
        }
        return PublisherUtil
                .fromFuture(() -> client.setBucketLifecycle(SetBucketLifecycleArgs.builder()
                        .bucket(bucket)
                        .config(new LifecycleConfiguration(
                                List.of(new LifecycleRule(
                                        Status.ENABLED,
                                        null,
                                        new Expiration((ZonedDateTime) null, expireAfterDays, null),
                                        new RuleFilter(""),
                                        null,
                                        null,
                                        null,
                                        null))))
                        .build()))
                .onErrorMap(t -> new RuntimeException(
                        "Failed to set a lifecycle configuration to the bucket: \""
                                + bucket
                                + "\"",
                        t))
                .then();
    }
@implNote 1. We use HMAC(key, message) instead of a HASH(key + message) to avoid the length
          extension attack. To put simply, if a hacker knows the signature of the resource
          "1", and he can also know the signature of resource "12", "13", "123", and so on
          without knowledge of the key.
          <p>
          2. Use MD5 because its output size (128 bits) is small, and it is a 22-character
          Base62-encoded string.
turms-plugins/turms-plugin-minio/src/main/java/im/turms/plugin/minio/MinioStorageServiceProvider.java