mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
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:
1 parent
f5203ddb47
commit
6e3562b64b
3 files changed
+57
-5
No files matched your search
@@ -835,11 +835,21 @@
|
||||
Enter your Cloudflare R2 credentials. Your Account ID (from dashboard) is used to connect. For Public Access URL, use your custom domain or R2.dev subdomain. Ensure the token has "Object Read & Write" permissions.
|
||||
</div>
|
||||
|
||||
<!-- S3 Helper -->
|
||||
<div id="storageS3Helper" class="card border border-info/30 bg-info/10 p-3 text-xs text-info hidden">
|
||||
Enter your S3-compatible storage details. The Endpoint URL is your provider's API address (e.g., https://s3.amazonaws.com or your MinIO URL).
|
||||
</div>
|
||||
|
||||
<label class="block text-sm text-muted-strong hidden" for="storageEndpoint">
|
||||
<span class="mb-2 block font-medium">Endpoint</span>
|
||||
<input id="storageEndpoint" type="url" class="input" placeholder="https://..." />
|
||||
</label>
|
||||
|
||||
<label class="hidden items-center gap-2 text-sm text-muted-strong" id="storageForcePathStyleLabel">
|
||||
<input id="storageForcePathStyle" type="checkbox" class="checkbox" checked />
|
||||
<span>Force Path Style</span>
|
||||
</label>
|
||||
|
||||
<label class="block text-sm text-muted-strong" for="storageRegion">
|
||||
<span class="mb-2 block font-medium">Region</span>
|
||||
<input id="storageRegion" type="text" class="input" placeholder="auto" value="auto" />
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -134,7 +134,8 @@ describe("StorageService", () => {
|
||||
region: "us-east-1",
|
||||
bucket: "my-bucket",
|
||||
accessKeyId: "minio-key",
|
||||
secretAccessKey: "minio-secret"
|
||||
secretAccessKey: "minio-secret",
|
||||
forcePathStyle: true
|
||||
};
|
||||
const meta = {
|
||||
label: "Local Minio",
|
||||
@@ -151,6 +152,7 @@ describe("StorageService", () => {
|
||||
assert.strictEqual(retrieved.region, "us-east-1", "Region should be preserved");
|
||||
assert.strictEqual(retrieved.bucket, "my-bucket", "Bucket should be preserved");
|
||||
assert.strictEqual(retrieved.accessKeyId, "minio-key");
|
||||
assert.strictEqual(retrieved.forcePathStyle, true, "ForcePathStyle should be preserved");
|
||||
assert.strictEqual(retrieved.meta.defaultForUploads, true);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user