From c88902e640e6ac7f7c9ea4b66f8a6005273636ab Mon Sep 17 00:00:00 2001
From: MeiMei <30769358+mei23@users.noreply.github.com>
Date: Sat, 6 Feb 2021 11:48:57 +0900
Subject: [PATCH] s3ForcePathStyle (#7122)

Co-authored-by: ybw2016v <dogcraft@126.com>
---
 .../1611547387175-objectStorageS3ForcePathStyle.ts | 14 ++++++++++++++
 src/client/pages/instance/settings.vue             |  4 ++++
 src/models/entities/meta.ts                        |  5 +++++
 src/server/api/endpoints/admin/update-meta.ts      | 10 +++++++++-
 src/server/api/endpoints/meta.ts                   |  1 +
 src/services/drive/s3.ts                           |  4 +++-
 6 files changed, 36 insertions(+), 2 deletions(-)
 create mode 100644 migration/1611547387175-objectStorageS3ForcePathStyle.ts

diff --git a/migration/1611547387175-objectStorageS3ForcePathStyle.ts b/migration/1611547387175-objectStorageS3ForcePathStyle.ts
new file mode 100644
index 0000000000..1506a29007
--- /dev/null
+++ b/migration/1611547387175-objectStorageS3ForcePathStyle.ts
@@ -0,0 +1,14 @@
+import {MigrationInterface, QueryRunner} from "typeorm";
+
+export class objectStorageS3ForcePathStyle1611547387175 implements MigrationInterface {
+    name = 'objectStorageS3ForcePathStyle1611547387175'
+
+    public async up(queryRunner: QueryRunner): Promise<void> {
+        await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageS3ForcePathStyle" boolean NOT NULL DEFAULT true`);
+    }
+
+    public async down(queryRunner: QueryRunner): Promise<void> {
+        await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageS3ForcePathStyle"`);
+    }
+
+}
diff --git a/src/client/pages/instance/settings.vue b/src/client/pages/instance/settings.vue
index 761044b011..cea621ba2d 100644
--- a/src/client/pages/instance/settings.vue
+++ b/src/client/pages/instance/settings.vue
@@ -175,6 +175,7 @@
 				<MkSwitch v-model:value="objectStorageUseSSL" :disabled="!useObjectStorage">{{ $ts.objectStorageUseSSL }}<template #desc>{{ $ts.objectStorageUseSSLDesc }}</template></MkSwitch>
 				<MkSwitch v-model:value="objectStorageUseProxy" :disabled="!useObjectStorage">{{ $ts.objectStorageUseProxy }}<template #desc>{{ $ts.objectStorageUseProxyDesc }}</template></MkSwitch>
 				<MkSwitch v-model:value="objectStorageSetPublicRead" :disabled="!useObjectStorage">{{ $ts.objectStorageSetPublicRead }}</MkSwitch>
+				<MkSwitch v-model:value="objectStorageS3ForcePathStyle" :disabled="!useObjectStorage">s3ForcePathStyle</MkSwitch>
 			</template>
 		</div>
 		<div class="_footer">
@@ -325,6 +326,7 @@ export default defineComponent({
 			objectStorageUseSSL: false,
 			objectStorageUseProxy: false,
 			objectStorageSetPublicRead: false,
+			objectStorageS3ForcePathStyle: true,
 			enableTwitterIntegration: false,
 			twitterConsumerKey: null,
 			twitterConsumerSecret: null,
@@ -393,6 +395,7 @@ export default defineComponent({
 		this.objectStorageUseSSL = this.meta.objectStorageUseSSL;
 		this.objectStorageUseProxy = this.meta.objectStorageUseProxy;
 		this.objectStorageSetPublicRead = this.meta.objectStorageSetPublicRead;
+		this.objectStorageS3ForcePathStyle = this.meta.objectStorageS3ForcePathStyle;
 		this.enableTwitterIntegration = this.meta.enableTwitterIntegration;
 		this.twitterConsumerKey = this.meta.twitterConsumerKey;
 		this.twitterConsumerSecret = this.meta.twitterConsumerSecret;
@@ -547,6 +550,7 @@ export default defineComponent({
 				objectStorageUseSSL: this.objectStorageUseSSL,
 				objectStorageUseProxy: this.objectStorageUseProxy,
 				objectStorageSetPublicRead: this.objectStorageSetPublicRead,
+				objectStorageS3ForcePathStyle: this.objectStorageS3ForcePathStyle,
 				enableTwitterIntegration: this.enableTwitterIntegration,
 				twitterConsumerKey: this.twitterConsumerKey,
 				twitterConsumerSecret: this.twitterConsumerSecret,
diff --git a/src/models/entities/meta.ts b/src/models/entities/meta.ts
index 72a8b97978..f013169f86 100644
--- a/src/models/entities/meta.ts
+++ b/src/models/entities/meta.ts
@@ -399,4 +399,9 @@ export class Meta {
 		default: false,
 	})
 	public objectStorageSetPublicRead: boolean;
+
+	@Column('boolean', {
+		default: true,
+	})
+	public objectStorageS3ForcePathStyle: boolean;
 }
diff --git a/src/server/api/endpoints/admin/update-meta.ts b/src/server/api/endpoints/admin/update-meta.ts
index d3addaba8a..163d7a2519 100644
--- a/src/server/api/endpoints/admin/update-meta.ts
+++ b/src/server/api/endpoints/admin/update-meta.ts
@@ -438,7 +438,11 @@ export const meta = {
 
 		objectStorageSetPublicRead: {
 			validator: $.optional.bool
-		}
+		},
+
+		objectStorageS3ForcePathStyle: {
+			validator: $.optional.bool
+		},
 	}
 };
 
@@ -713,6 +717,10 @@ export default define(meta, async (ps, me) => {
 		set.objectStorageSetPublicRead = ps.objectStorageSetPublicRead;
 	}
 
+	if (ps.objectStorageS3ForcePathStyle !== undefined) {
+		set.objectStorageS3ForcePathStyle = ps.objectStorageS3ForcePathStyle;
+	}
+
 	await getConnection().transaction(async transactionalEntityManager => {
 		const meta = await transactionalEntityManager.findOne(Meta, {
 			order: {
diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts
index 81053b26a3..3b647e21cd 100644
--- a/src/server/api/endpoints/meta.ts
+++ b/src/server/api/endpoints/meta.ts
@@ -205,6 +205,7 @@ export default define(meta, async (ps, me) => {
 			response.objectStorageUseSSL = instance.objectStorageUseSSL;
 			response.objectStorageUseProxy = instance.objectStorageUseProxy;
 			response.objectStorageSetPublicRead = instance.objectStorageSetPublicRead;
+			response.objectStorageS3ForcePathStyle = instance.objectStorageS3ForcePathStyle;
 		}
 	}
 
diff --git a/src/services/drive/s3.ts b/src/services/drive/s3.ts
index abe3c166a5..f419f09377 100644
--- a/src/services/drive/s3.ts
+++ b/src/services/drive/s3.ts
@@ -13,7 +13,9 @@ export function getS3(meta: Meta) {
 		secretAccessKey: meta.objectStorageSecretKey!,
 		region: meta.objectStorageRegion || undefined,
 		sslEnabled: meta.objectStorageUseSSL,
-		s3ForcePathStyle: !!meta.objectStorageEndpoint,
+		s3ForcePathStyle: !meta.objectStorageEndpoint	// AWS with endPoint omitted
+			? false
+			: meta.objectStorageS3ForcePathStyle,
 		httpOptions: {
 			agent: getAgentByUrl(new URL(u), !meta.objectStorageUseProxy)
 		}