feat(ui): enhance custom S3 storage configuration with helper text and force path style option

- Add helper text to the S3 storage configuration form to guide users on Endpoint URL format.
- Add 'Force Path Style' checkbox to S3 storage configuration to support MinIO and other S3-compatible providers that require it.
- Update `ProfileModalController` to toggle visibility of these new fields based on the selected provider.
- Update `ProfileModalController` to save and load the `forcePathStyle` setting.
- Update `tests/ui/storageService.test.mjs` to verify persistence of `forcePathStyle`.
This commit is contained in:
google-labs-jules[bot] committed 2026-01-26 16:50:48 +00:00
1 parent f5203ddb47
commit 6e3562b64b
3 files changed
+57 -5

No files matched your search

+44 -4
View File
@@ -1776,6 +1776,9 @@ export class ProfileModalController {
this.storagePrefixWarning = document.getElementById("storagePrefixWarning") || null;
this.storageDefaultInput = document.getElementById("storageDefault") || null;
this.storageR2Helper = document.getElementById("storageR2Helper") || null;
this.storageS3Helper = document.getElementById("storageS3Helper") || null;
this.storageForcePathStyleInput = document.getElementById("storageForcePathStyle") || null;
this.storageForcePathStyleLabel = document.getElementById("storageForcePathStyleLabel") || null;
// Backwards-compatible aliases retained for application code that still
// mirrors DOM references from the controller. These should be removed once
@@ -9042,8 +9045,8 @@ export class ProfileModalController {
fillStorageForm(conn) {
if (!conn) return;
const { provider, accessKeyId, secretAccessKey, accountId: payloadAccountId, endpoint: payloadEndpoint } = conn;
const { endpoint, region, bucket, prefix, defaultForUploads, accountId } = conn.meta || {};
const { provider, accessKeyId, secretAccessKey, accountId: payloadAccountId, endpoint: payloadEndpoint, forcePathStyle: payloadForcePathStyle } = conn;
const { endpoint, region, bucket, prefix, defaultForUploads, accountId, forcePathStyle: metaForcePathStyle } = conn.meta || {};
if (this.storageProviderInput) this.storageProviderInput.value = provider || "cloudflare_r2";
@@ -9058,6 +9061,17 @@ export class ProfileModalController {
if (this.storagePrefixInput) this.storagePrefixInput.value = prefix || "";
if (this.storageDefaultInput) this.storageDefaultInput.checked = !!defaultForUploads;
if (this.storageForcePathStyleInput) {
if (typeof payloadForcePathStyle === "boolean") {
this.storageForcePathStyleInput.checked = payloadForcePathStyle;
} else if (typeof metaForcePathStyle === "boolean") {
this.storageForcePathStyleInput.checked = metaForcePathStyle;
} else {
// Default to true for S3 if unspecified
this.storageForcePathStyleInput.checked = true;
}
}
this.updateStorageFormVisibility();
this.handlePublicUrlInput();
}
@@ -9098,6 +9112,20 @@ export class ProfileModalController {
if (isR2) this.storageR2Helper.classList.remove("hidden");
else this.storageR2Helper.classList.add("hidden");
}
if (this.storageS3Helper) {
if (!isR2) this.storageS3Helper.classList.remove("hidden");
else this.storageS3Helper.classList.add("hidden");
}
if (this.storageForcePathStyleLabel) {
if (!isR2) this.storageForcePathStyleLabel.classList.remove("hidden", "flex");
else this.storageForcePathStyleLabel.classList.add("hidden");
if (!isR2) {
this.storageForcePathStyleLabel.classList.add("flex");
}
}
}
async handleUnlockStorage() {
@@ -9195,7 +9223,11 @@ export class ProfileModalController {
}
let publicBaseUrl = "";
let forcePathStyle = provider === PROVIDERS.GENERIC;
// For Generic S3, respect the user's checkbox selection.
let forcePathStyle = false;
if (provider === PROVIDERS.GENERIC) {
forcePathStyle = this.storageForcePathStyleInput?.checked ?? true;
}
const payload = {
provider,
@@ -9287,7 +9319,12 @@ export class ProfileModalController {
const accessKeyId = this.storageAccessKeyInput?.value?.trim() || "";
const secretAccessKey = this.storageSecretKeyInput?.value?.trim() || "";
const bucket = this.storageBucketInput?.value?.trim() || "";
const forcePathStyle = provider === PROVIDERS.GENERIC;
// Checkbox is only relevant for Generic S3
const forcePathStyle = provider === PROVIDERS.GENERIC
? (this.storageForcePathStyleInput?.checked ?? true)
: false;
const publicBaseUrl = this.storagePrefixInput?.value?.trim() || "";
if (!accessKeyId || !secretAccessKey || !endpointOrAccount) {
@@ -9348,6 +9385,9 @@ export class ProfileModalController {
this.storagePrefixInput.value = "";
this.storageDefaultInput.checked = false;
this.storageProviderInput.value = "cloudflare_r2";
if (this.storageForcePathStyleInput) {
this.storageForcePathStyleInput.checked = true;
}
this.updateStorageFormVisibility();
this.setStorageFormStatus("", "info");
}