Resolve #4928
This commit is contained in:
		
							parent
							
								
									3d8bbedf1b
								
							
						
					
					
						commit
						3f5b96bf62
					
				
					 12 changed files with 370 additions and 160 deletions
				
			
		|  | @ -6,8 +6,6 @@ mongodb: | ||||||
|   db: misskey |   db: misskey | ||||||
|   user: syuilo |   user: syuilo | ||||||
|   pass: '' |   pass: '' | ||||||
| drive: |  | ||||||
|   storage: 'db' |  | ||||||
| redis: | redis: | ||||||
|   host: localhost |   host: localhost | ||||||
|   port: 6379 |   port: 6379 | ||||||
|  |  | ||||||
|  | @ -6,8 +6,6 @@ mongodb: | ||||||
|   db: test-misskey |   db: test-misskey | ||||||
|   user: admin |   user: admin | ||||||
|   pass: '' |   pass: '' | ||||||
| drive: |  | ||||||
|   storage: 'db' |  | ||||||
| # __REDIS__ | # __REDIS__ | ||||||
| redis: | redis: | ||||||
|   host: localhost |   host: localhost | ||||||
|  |  | ||||||
|  | @ -78,61 +78,6 @@ redis: | ||||||
| #  port: 9200 | #  port: 9200 | ||||||
| #  pass: null | #  pass: null | ||||||
| 
 | 
 | ||||||
| #   ┌────────────────────────────────────┐ |  | ||||||
| #───┘ File storage (Drive) configuration └────────────────────── |  | ||||||
| 
 |  | ||||||
| drive: |  | ||||||
|   storage: 'fs' |  | ||||||
| 
 |  | ||||||
| # OR |  | ||||||
| 
 |  | ||||||
| #drive: |  | ||||||
| #  storage: 'minio' |  | ||||||
| #  bucket: |  | ||||||
| #  prefix: |  | ||||||
| #  config: |  | ||||||
| #    endPoint: |  | ||||||
| #    port: |  | ||||||
| #    useSSL: |  | ||||||
| #    accessKey: |  | ||||||
| #    secretKey: |  | ||||||
| 
 |  | ||||||
| # S3/GCS example |  | ||||||
| # |  | ||||||
| # * Replace <endpoint> to |  | ||||||
| #     S3: see https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region |  | ||||||
| #     GCS: use 'storage.googleapis.com' |  | ||||||
| # |  | ||||||
| # * Replace <region> to |  | ||||||
| #     S3: see https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region |  | ||||||
| #     GCS: not needed (just delete the region line) |  | ||||||
| # |  | ||||||
| #drive: |  | ||||||
| #  storage: 'minio' |  | ||||||
| #  bucket: bucket-name |  | ||||||
| #  prefix: files |  | ||||||
| #  baseUrl: https://bucket-name.<endpoint> |  | ||||||
| #  config: |  | ||||||
| #    endPoint: <endpoint> |  | ||||||
| #    region: <region> |  | ||||||
| #    useSSL: true |  | ||||||
| #    accessKey: XXX |  | ||||||
| #    secretKey: YYY |  | ||||||
| 
 |  | ||||||
| # S3/GCS example (with CDN, custom domain) |  | ||||||
| # |  | ||||||
| #drive: |  | ||||||
| #  storage: 'minio' |  | ||||||
| #  bucket: drive.example.com |  | ||||||
| #  prefix: files |  | ||||||
| #  baseUrl: https://drive.example.com |  | ||||||
| #  config: |  | ||||||
| #    endPoint: <endpoint> |  | ||||||
| #    region: <region> |  | ||||||
| #    useSSL: true |  | ||||||
| #    accessKey: XXX |  | ||||||
| #    secretKey: YYY |  | ||||||
| 
 |  | ||||||
| #   ┌───────────────┐ | #   ┌───────────────┐ | ||||||
| #───┘ ID generation └─────────────────────────────────────────── | #───┘ ID generation └─────────────────────────────────────────── | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1232,6 +1232,19 @@ admin/views/instance.vue: | ||||||
|   advanced-config: "その他の設定" |   advanced-config: "その他の設定" | ||||||
|   note-and-tl: "投稿とタイムライン" |   note-and-tl: "投稿とタイムライン" | ||||||
|   drive-config: "ドライブの設定" |   drive-config: "ドライブの設定" | ||||||
|  |   use-object-storage: "オブジェクトストレージを使用する" | ||||||
|  |   object-storage-base-url: "URL" | ||||||
|  |   object-storage-bucket: "バケット名" | ||||||
|  |   object-storage-prefix: "プレフィックス" | ||||||
|  |   object-storage-endpoint: "エンドポイント" | ||||||
|  |   object-storage-region: "リージョン" | ||||||
|  |   object-storage-port: "ポート" | ||||||
|  |   object-storage-access-key: "アクセスキー" | ||||||
|  |   object-storage-secret-key: "シークレットキー" | ||||||
|  |   object-storage-use-ssl: "SSLを使用" | ||||||
|  |   object-storage-s3-info: "Amazon S3をオブジェクトストレージとして使用する場合の「エンドポイント」と「リージョン」の設定については{0}をご確認ください。" | ||||||
|  |   object-storage-s3-info-here: "こちら" | ||||||
|  |   object-storage-gcs-info: "Google Cloud Storageをオブジェクトストレージとして使用する場合、「エンドポイント」は storage.googleapis.com に設定し、「リージョン」は空欄にします。" | ||||||
|   cache-remote-files: "リモートのファイルをキャッシュする" |   cache-remote-files: "リモートのファイルをキャッシュする" | ||||||
|   cache-remote-files-desc: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクするようになります。そのためサーバーのストレージを節約できますが、プライバシー設定で直リンクを無効にしているユーザーにはファイルが見えなくなったり、サムネイルが生成されないので通信量が増加します。通常はこの設定をオンにしておくことをおすすめします。" |   cache-remote-files-desc: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクするようになります。そのためサーバーのストレージを節約できますが、プライバシー設定で直リンクを無効にしているユーザーにはファイルが見えなくなったり、サムネイルが生成されないので通信量が増加します。通常はこの設定をオンにしておくことをおすすめします。" | ||||||
|   local-drive-capacity-mb: "ローカルユーザーひとりあたりのドライブ容量" |   local-drive-capacity-mb: "ローカルユーザーひとりあたりのドライブ容量" | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								migration/1557932705754-ObjectStorageSetting.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								migration/1557932705754-ObjectStorageSetting.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | import {MigrationInterface, QueryRunner} from "typeorm"; | ||||||
|  | 
 | ||||||
|  | export class ObjectStorageSetting1557932705754 implements MigrationInterface { | ||||||
|  | 
 | ||||||
|  |     public async up(queryRunner: QueryRunner): Promise<any> { | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" ADD "useObjectStorage" boolean NOT NULL DEFAULT false`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageBucket" character varying(512)`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" ADD "objectStoragePrefix" character varying(512)`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageBaseUrl" character varying(512)`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageEndpoint" character varying(512)`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageRegion" character varying(512)`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageAccessKey" character varying(512)`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageSecretKey" character varying(512)`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" ADD "objectStoragePort" integer`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageUseSSL" boolean NOT NULL DEFAULT true`); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public async down(queryRunner: QueryRunner): Promise<any> { | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageUseSSL"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStoragePort"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageSecretKey"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageAccessKey"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageRegion"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageEndpoint"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageBaseUrl"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStoragePrefix"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageBucket"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "useObjectStorage"`); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -53,6 +53,32 @@ | ||||||
| 
 | 
 | ||||||
| 	<ui-card> | 	<ui-card> | ||||||
| 		<template #title><fa icon="cloud"/> {{ $t('drive-config') }}</template> | 		<template #title><fa icon="cloud"/> {{ $t('drive-config') }}</template> | ||||||
|  | 		<section> | ||||||
|  | 			<ui-switch v-model="useObjectStorage">{{ $t('use-object-storage') }}</ui-switch> | ||||||
|  | 			<template v-if="useObjectStorage"> | ||||||
|  | 				<ui-info> | ||||||
|  | 					<i18n path="object-storage-s3-info"> | ||||||
|  | 						<a href="https://docs.aws.amazon.com/general/latest/gr/rande.html" target="_blank">{{ $t('object-storage-s3-info-here') }}</a> | ||||||
|  | 					</i18n> | ||||||
|  | 				</ui-info> | ||||||
|  | 				<ui-info>{{ $t('object-storage-gcs-info') }}</ui-info> | ||||||
|  | 				<ui-input v-model="objectStorageBaseUrl" :disabled="!useObjectStorage">{{ $t('object-storage-base-url') }}</ui-input> | ||||||
|  | 				<ui-horizon-group inputs> | ||||||
|  | 					<ui-input v-model="objectStorageBucket" :disabled="!useObjectStorage">{{ $t('object-storage-bucket') }}</ui-input> | ||||||
|  | 					<ui-input v-model="objectStoragePrefix" :disabled="!useObjectStorage">{{ $t('object-storage-prefix') }}</ui-input> | ||||||
|  | 				</ui-horizon-group> | ||||||
|  | 				<ui-input v-model="objectStorageEndpoint" :disabled="!useObjectStorage">{{ $t('object-storage-endpoint') }}</ui-input> | ||||||
|  | 				<ui-horizon-group inputs> | ||||||
|  | 					<ui-input v-model="objectStorageRegion" :disabled="!useObjectStorage">{{ $t('object-storage-region') }}</ui-input> | ||||||
|  | 					<ui-input v-model="objectStoragePort" type="number" :disabled="!useObjectStorage">{{ $t('object-storage-port') }}</ui-input> | ||||||
|  | 				</ui-horizon-group> | ||||||
|  | 				<ui-horizon-group inputs> | ||||||
|  | 					<ui-input v-model="objectStorageAccessKey" :disabled="!useObjectStorage"><template #icon><fa icon="key"/></template>{{ $t('object-storage-access-key') }}</ui-input> | ||||||
|  | 					<ui-input v-model="objectStorageSecretKey" :disabled="!useObjectStorage"><template #icon><fa icon="key"/></template>{{ $t('object-storage-secret-key') }}</ui-input> | ||||||
|  | 				</ui-horizon-group> | ||||||
|  | 				<ui-switch v-model="objectStorageUseSSL" :disabled="!useObjectStorage">{{ $t('object-storage-use-ssl') }}</ui-switch> | ||||||
|  | 			</template> | ||||||
|  | 		</section> | ||||||
| 		<section> | 		<section> | ||||||
| 			<ui-switch v-model="cacheRemoteFiles">{{ $t('cache-remote-files') }}<template #desc>{{ $t('cache-remote-files-desc') }}</template></ui-switch> | 			<ui-switch v-model="cacheRemoteFiles">{{ $t('cache-remote-files') }}<template #desc>{{ $t('cache-remote-files-desc') }}</template></ui-switch> | ||||||
| 		</section> | 		</section> | ||||||
|  | @ -65,69 +91,6 @@ | ||||||
| 		</section> | 		</section> | ||||||
| 	</ui-card> | 	</ui-card> | ||||||
| 
 | 
 | ||||||
| 	<ui-card> |  | ||||||
| 		<template #title><fa :icon="farEnvelope"/> {{ $t('email-config') }}</template> |  | ||||||
| 		<section> |  | ||||||
| 			<ui-switch v-model="enableEmail">{{ $t('enable-email') }}<template #desc>{{ $t('email-config-info') }}</template></ui-switch> |  | ||||||
| 			<ui-input v-model="email" type="email" :disabled="!enableEmail">{{ $t('email') }}</ui-input> |  | ||||||
| 			<ui-horizon-group inputs> |  | ||||||
| 				<ui-input v-model="smtpHost" :disabled="!enableEmail">{{ $t('smtp-host') }}</ui-input> |  | ||||||
| 				<ui-input v-model="smtpPort" type="number" :disabled="!enableEmail">{{ $t('smtp-port') }}</ui-input> |  | ||||||
| 			</ui-horizon-group> |  | ||||||
| 			<ui-switch v-model="smtpAuth">{{ $t('smtp-auth') }}</ui-switch> |  | ||||||
| 			<ui-horizon-group inputs> |  | ||||||
| 				<ui-input v-model="smtpUser" :disabled="!enableEmail || !smtpAuth">{{ $t('smtp-user') }}</ui-input> |  | ||||||
| 				<ui-input v-model="smtpPass" type="password" :with-password-toggle="true" :disabled="!enableEmail || !smtpAuth">{{ $t('smtp-pass') }}</ui-input> |  | ||||||
| 			</ui-horizon-group> |  | ||||||
| 			<ui-switch v-model="smtpSecure" :disabled="!enableEmail">{{ $t('smtp-secure') }}<template #desc>{{ $t('smtp-secure-info') }}</template></ui-switch> |  | ||||||
| 		</section> |  | ||||||
| 		<section> |  | ||||||
| 			<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> |  | ||||||
| 		</section> |  | ||||||
| 	</ui-card> |  | ||||||
| 
 |  | ||||||
| 	<ui-card> |  | ||||||
| 		<template #title><fa :icon="faGhost"/> {{ $t('proxy-account-config') }}</template> |  | ||||||
| 		<section> |  | ||||||
| 			<ui-info>{{ $t('proxy-account-info') }}</ui-info> |  | ||||||
| 			<ui-input v-model="proxyAccount"><template #prefix>@</template>{{ $t('proxy-account-username') }}<template #desc>{{ $t('proxy-account-username-desc') }}</template></ui-input> |  | ||||||
| 			<ui-info warn>{{ $t('proxy-account-warn') }}</ui-info> |  | ||||||
| 		</section> |  | ||||||
| 		<section> |  | ||||||
| 			<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> |  | ||||||
| 		</section> |  | ||||||
| 	</ui-card> |  | ||||||
| 
 |  | ||||||
| 	<ui-card> |  | ||||||
| 		<template #title><fa :icon="faBolt"/> {{ $t('serviceworker-config') }}</template> |  | ||||||
| 		<section> |  | ||||||
| 			<ui-switch v-model="enableServiceWorker">{{ $t('enable-serviceworker') }}<template #desc>{{ $t('serviceworker-info') }}</template></ui-switch> |  | ||||||
| 			<ui-info>{{ $t('vapid-info') }}<br><code>npm i web-push -g<br>web-push generate-vapid-keys</code></ui-info> |  | ||||||
| 			<ui-horizon-group inputs class="fit-bottom"> |  | ||||||
| 				<ui-input v-model="swPublicKey" :disabled="!enableServiceWorker"><template #icon><fa icon="key"/></template>{{ $t('vapid-publickey') }}</ui-input> |  | ||||||
| 				<ui-input v-model="swPrivateKey" :disabled="!enableServiceWorker"><template #icon><fa icon="key"/></template>{{ $t('vapid-privatekey') }}</ui-input> |  | ||||||
| 			</ui-horizon-group> |  | ||||||
| 		</section> |  | ||||||
| 		<section> |  | ||||||
| 			<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> |  | ||||||
| 		</section> |  | ||||||
| 	</ui-card> |  | ||||||
| 
 |  | ||||||
| 	<ui-card> |  | ||||||
| 		<template #title><fa :icon="faShieldAlt"/> {{ $t('recaptcha-config') }}</template> |  | ||||||
| 		<section class="fit-bottom"> |  | ||||||
| 			<ui-switch v-model="enableRecaptcha">{{ $t('enable-recaptcha') }}</ui-switch> |  | ||||||
| 			<ui-info>{{ $t('recaptcha-info') }}</ui-info> |  | ||||||
| 			<ui-horizon-group inputs> |  | ||||||
| 				<ui-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><template #icon><fa icon="key"/></template>{{ $t('recaptcha-site-key') }}</ui-input> |  | ||||||
| 				<ui-input v-model="recaptchaSecretKey" :disabled="!enableRecaptcha"><template #icon><fa icon="key"/></template>{{ $t('recaptcha-secret-key') }}</ui-input> |  | ||||||
| 			</ui-horizon-group> |  | ||||||
| 		</section> |  | ||||||
| 		<section> |  | ||||||
| 			<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> |  | ||||||
| 		</section> |  | ||||||
| 	</ui-card> |  | ||||||
| 
 |  | ||||||
| 	<ui-card> | 	<ui-card> | ||||||
| 		<template #title><fa :icon="faThumbtack"/> {{ $t('pinned-users') }}</template> | 		<template #title><fa :icon="faThumbtack"/> {{ $t('pinned-users') }}</template> | ||||||
| 		<section class="fit-top"> | 		<section class="fit-top"> | ||||||
|  | @ -138,34 +101,109 @@ | ||||||
| 		</section> | 		</section> | ||||||
| 	</ui-card> | 	</ui-card> | ||||||
| 
 | 
 | ||||||
|  | 	<ui-card> | ||||||
|  | 		<template #title><fa :icon="faGhost"/> {{ $t('proxy-account-config') }}</template> | ||||||
|  | 		<section> | ||||||
|  | 			<ui-info>{{ $t('proxy-account-info') }}</ui-info> | ||||||
|  | 			<ui-input v-model="proxyAccount"><template #prefix>@</template>{{ $t('proxy-account-username') }}<template #desc>{{ $t('proxy-account-username-desc') }}</template></ui-input> | ||||||
|  | 			<ui-info warn>{{ $t('proxy-account-warn') }}</ui-info> | ||||||
|  | 		</section> | ||||||
|  | 		<section> | ||||||
|  | 			<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> | ||||||
|  | 		</section> | ||||||
|  | 	</ui-card> | ||||||
|  | 
 | ||||||
|  | 	<ui-card> | ||||||
|  | 		<template #title><fa :icon="farEnvelope"/> {{ $t('email-config') }}</template> | ||||||
|  | 		<section> | ||||||
|  | 			<ui-switch v-model="enableEmail">{{ $t('enable-email') }}<template #desc>{{ $t('email-config-info') }}</template></ui-switch> | ||||||
|  | 			<template v-if="enableEmail"> | ||||||
|  | 				<ui-input v-model="email" type="email" :disabled="!enableEmail">{{ $t('email') }}</ui-input> | ||||||
|  | 				<ui-horizon-group inputs> | ||||||
|  | 					<ui-input v-model="smtpHost" :disabled="!enableEmail">{{ $t('smtp-host') }}</ui-input> | ||||||
|  | 					<ui-input v-model="smtpPort" type="number" :disabled="!enableEmail">{{ $t('smtp-port') }}</ui-input> | ||||||
|  | 				</ui-horizon-group> | ||||||
|  | 				<ui-switch v-model="smtpAuth">{{ $t('smtp-auth') }}</ui-switch> | ||||||
|  | 				<ui-horizon-group inputs> | ||||||
|  | 					<ui-input v-model="smtpUser" :disabled="!enableEmail || !smtpAuth">{{ $t('smtp-user') }}</ui-input> | ||||||
|  | 					<ui-input v-model="smtpPass" type="password" :with-password-toggle="true" :disabled="!enableEmail || !smtpAuth">{{ $t('smtp-pass') }}</ui-input> | ||||||
|  | 				</ui-horizon-group> | ||||||
|  | 				<ui-switch v-model="smtpSecure" :disabled="!enableEmail">{{ $t('smtp-secure') }}<template #desc>{{ $t('smtp-secure-info') }}</template></ui-switch> | ||||||
|  | 			</template> | ||||||
|  | 		</section> | ||||||
|  | 		<section> | ||||||
|  | 			<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> | ||||||
|  | 		</section> | ||||||
|  | 	</ui-card> | ||||||
|  | 
 | ||||||
|  | 	<ui-card> | ||||||
|  | 		<template #title><fa :icon="faBolt"/> {{ $t('serviceworker-config') }}</template> | ||||||
|  | 		<section> | ||||||
|  | 			<ui-switch v-model="enableServiceWorker">{{ $t('enable-serviceworker') }}<template #desc>{{ $t('serviceworker-info') }}</template></ui-switch> | ||||||
|  | 			<template v-if="enableServiceWorker"> | ||||||
|  | 				<ui-info>{{ $t('vapid-info') }}<br><code>npm i web-push -g<br>web-push generate-vapid-keys</code></ui-info> | ||||||
|  | 				<ui-horizon-group inputs class="fit-bottom"> | ||||||
|  | 					<ui-input v-model="swPublicKey" :disabled="!enableServiceWorker"><template #icon><fa icon="key"/></template>{{ $t('vapid-publickey') }}</ui-input> | ||||||
|  | 					<ui-input v-model="swPrivateKey" :disabled="!enableServiceWorker"><template #icon><fa icon="key"/></template>{{ $t('vapid-privatekey') }}</ui-input> | ||||||
|  | 				</ui-horizon-group> | ||||||
|  | 			</template> | ||||||
|  | 		</section> | ||||||
|  | 		<section> | ||||||
|  | 			<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> | ||||||
|  | 		</section> | ||||||
|  | 	</ui-card> | ||||||
|  | 
 | ||||||
|  | 	<ui-card> | ||||||
|  | 		<template #title><fa :icon="faShieldAlt"/> {{ $t('recaptcha-config') }}</template> | ||||||
|  | 		<section :class="enableRecaptcha ? 'fit-bottom' : ''"> | ||||||
|  | 			<ui-switch v-model="enableRecaptcha">{{ $t('enable-recaptcha') }}</ui-switch> | ||||||
|  | 			<template v-if="enableRecaptcha"> | ||||||
|  | 				<ui-info>{{ $t('recaptcha-info') }}</ui-info> | ||||||
|  | 				<ui-horizon-group inputs> | ||||||
|  | 					<ui-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><template #icon><fa icon="key"/></template>{{ $t('recaptcha-site-key') }}</ui-input> | ||||||
|  | 					<ui-input v-model="recaptchaSecretKey" :disabled="!enableRecaptcha"><template #icon><fa icon="key"/></template>{{ $t('recaptcha-secret-key') }}</ui-input> | ||||||
|  | 				</ui-horizon-group> | ||||||
|  | 			</template> | ||||||
|  | 		</section> | ||||||
|  | 		<section> | ||||||
|  | 			<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> | ||||||
|  | 		</section> | ||||||
|  | 	</ui-card> | ||||||
|  | 
 | ||||||
| 	<ui-card> | 	<ui-card> | ||||||
| 		<template #title><fa :icon="faShieldAlt"/> {{ $t('external-service-integration-config') }}</template> | 		<template #title><fa :icon="faShieldAlt"/> {{ $t('external-service-integration-config') }}</template> | ||||||
| 		<section> | 		<section> | ||||||
| 			<header><fa :icon="['fab', 'twitter']"/> {{ $t('twitter-integration-config') }}</header> | 			<header><fa :icon="['fab', 'twitter']"/> {{ $t('twitter-integration-config') }}</header> | ||||||
| 			<ui-switch v-model="enableTwitterIntegration">{{ $t('enable-twitter-integration') }}</ui-switch> | 			<ui-switch v-model="enableTwitterIntegration">{{ $t('enable-twitter-integration') }}</ui-switch> | ||||||
|  | 			<template v-if="enableTwitterIntegration"> | ||||||
| 				<ui-horizon-group> | 				<ui-horizon-group> | ||||||
| 					<ui-input v-model="twitterConsumerKey" :disabled="!enableTwitterIntegration"><template #icon><fa icon="key"/></template>{{ $t('twitter-integration-consumer-key') }}</ui-input> | 					<ui-input v-model="twitterConsumerKey" :disabled="!enableTwitterIntegration"><template #icon><fa icon="key"/></template>{{ $t('twitter-integration-consumer-key') }}</ui-input> | ||||||
| 					<ui-input v-model="twitterConsumerSecret" :disabled="!enableTwitterIntegration"><template #icon><fa icon="key"/></template>{{ $t('twitter-integration-consumer-secret') }}</ui-input> | 					<ui-input v-model="twitterConsumerSecret" :disabled="!enableTwitterIntegration"><template #icon><fa icon="key"/></template>{{ $t('twitter-integration-consumer-secret') }}</ui-input> | ||||||
| 				</ui-horizon-group> | 				</ui-horizon-group> | ||||||
| 				<ui-info>{{ $t('twitter-integration-info', { url: `${url}/api/tw/cb` }) }}</ui-info> | 				<ui-info>{{ $t('twitter-integration-info', { url: `${url}/api/tw/cb` }) }}</ui-info> | ||||||
|  | 			</template> | ||||||
| 		</section> | 		</section> | ||||||
| 		<section> | 		<section> | ||||||
| 			<header><fa :icon="['fab', 'github']"/> {{ $t('github-integration-config') }}</header> | 			<header><fa :icon="['fab', 'github']"/> {{ $t('github-integration-config') }}</header> | ||||||
| 			<ui-switch v-model="enableGithubIntegration">{{ $t('enable-github-integration') }}</ui-switch> | 			<ui-switch v-model="enableGithubIntegration">{{ $t('enable-github-integration') }}</ui-switch> | ||||||
|  | 			<template v-if="enableGithubIntegration"> | ||||||
| 				<ui-horizon-group> | 				<ui-horizon-group> | ||||||
| 					<ui-input v-model="githubClientId" :disabled="!enableGithubIntegration"><template #icon><fa icon="key"/></template>{{ $t('github-integration-client-id') }}</ui-input> | 					<ui-input v-model="githubClientId" :disabled="!enableGithubIntegration"><template #icon><fa icon="key"/></template>{{ $t('github-integration-client-id') }}</ui-input> | ||||||
| 					<ui-input v-model="githubClientSecret" :disabled="!enableGithubIntegration"><template #icon><fa icon="key"/></template>{{ $t('github-integration-client-secret') }}</ui-input> | 					<ui-input v-model="githubClientSecret" :disabled="!enableGithubIntegration"><template #icon><fa icon="key"/></template>{{ $t('github-integration-client-secret') }}</ui-input> | ||||||
| 				</ui-horizon-group> | 				</ui-horizon-group> | ||||||
| 				<ui-info>{{ $t('github-integration-info', { url: `${url}/api/gh/cb` }) }}</ui-info> | 				<ui-info>{{ $t('github-integration-info', { url: `${url}/api/gh/cb` }) }}</ui-info> | ||||||
|  | 			</template> | ||||||
| 		</section> | 		</section> | ||||||
| 		<section> | 		<section> | ||||||
| 			<header><fa :icon="['fab', 'discord']"/> {{ $t('discord-integration-config') }}</header> | 			<header><fa :icon="['fab', 'discord']"/> {{ $t('discord-integration-config') }}</header> | ||||||
| 			<ui-switch v-model="enableDiscordIntegration">{{ $t('enable-discord-integration') }}</ui-switch> | 			<ui-switch v-model="enableDiscordIntegration">{{ $t('enable-discord-integration') }}</ui-switch> | ||||||
|  | 			<template v-if="enableDiscordIntegration"> | ||||||
| 				<ui-horizon-group> | 				<ui-horizon-group> | ||||||
| 					<ui-input v-model="discordClientId" :disabled="!enableDiscordIntegration"><template #icon><fa icon="key"/></template>{{ $t('discord-integration-client-id') }}</ui-input> | 					<ui-input v-model="discordClientId" :disabled="!enableDiscordIntegration"><template #icon><fa icon="key"/></template>{{ $t('discord-integration-client-id') }}</ui-input> | ||||||
| 					<ui-input v-model="discordClientSecret" :disabled="!enableDiscordIntegration"><template #icon><fa icon="key"/></template>{{ $t('discord-integration-client-secret') }}</ui-input> | 					<ui-input v-model="discordClientSecret" :disabled="!enableDiscordIntegration"><template #icon><fa icon="key"/></template>{{ $t('discord-integration-client-secret') }}</ui-input> | ||||||
| 				</ui-horizon-group> | 				</ui-horizon-group> | ||||||
| 				<ui-info>{{ $t('discord-integration-info', { url: `${url}/api/dc/cb` }) }}</ui-info> | 				<ui-info>{{ $t('discord-integration-info', { url: `${url}/api/dc/cb` }) }}</ui-info> | ||||||
|  | 			</template> | ||||||
| 		</section> | 		</section> | ||||||
| 		<section> | 		<section> | ||||||
| 			<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> | 			<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> | ||||||
|  | @ -261,6 +299,16 @@ export default Vue.extend({ | ||||||
| 			swPrivateKey: null, | 			swPrivateKey: null, | ||||||
| 			pinnedUsers: '', | 			pinnedUsers: '', | ||||||
| 			hiddenTags: '', | 			hiddenTags: '', | ||||||
|  | 			useObjectStorage: false, | ||||||
|  | 			objectStorageBaseUrl: null, | ||||||
|  | 			objectStorageBucket: null, | ||||||
|  | 			objectStoragePrefix: null, | ||||||
|  | 			objectStorageEndpoint: null, | ||||||
|  | 			objectStorageRegion: null, | ||||||
|  | 			objectStoragePort: null, | ||||||
|  | 			objectStorageAccessKey: null, | ||||||
|  | 			objectStorageSecretKey: null, | ||||||
|  | 			objectStorageUseSSL: false, | ||||||
| 			faHeadset, faShieldAlt, faGhost, faUserPlus, farEnvelope, faBolt, faThumbtack, faPencilAlt, faSave, faHashtag | 			faHeadset, faShieldAlt, faGhost, faUserPlus, farEnvelope, faBolt, faThumbtack, faPencilAlt, faSave, faHashtag | ||||||
| 		}; | 		}; | ||||||
| 	}, | 	}, | ||||||
|  | @ -315,6 +363,16 @@ export default Vue.extend({ | ||||||
| 			this.swPrivateKey = meta.swPrivateKey; | 			this.swPrivateKey = meta.swPrivateKey; | ||||||
| 			this.pinnedUsers = meta.pinnedUsers.join('\n'); | 			this.pinnedUsers = meta.pinnedUsers.join('\n'); | ||||||
| 			this.hiddenTags = meta.hiddenTags.join('\n'); | 			this.hiddenTags = meta.hiddenTags.join('\n'); | ||||||
|  | 			this.useObjectStorage = meta.useObjectStorage; | ||||||
|  | 			this.objectStorageBaseUrl = meta.objectStorageBaseUrl; | ||||||
|  | 			this.objectStorageBucket = meta.objectStorageBucket; | ||||||
|  | 			this.objectStoragePrefix = meta.objectStoragePrefix; | ||||||
|  | 			this.objectStorageEndpoint = meta.objectStorageEndpoint; | ||||||
|  | 			this.objectStorageRegion = meta.objectStorageRegion; | ||||||
|  | 			this.objectStoragePort = meta.objectStoragePort; | ||||||
|  | 			this.objectStorageAccessKey = meta.objectStorageAccessKey; | ||||||
|  | 			this.objectStorageSecretKey = meta.objectStorageSecretKey; | ||||||
|  | 			this.objectStorageUseSSL = meta.objectStorageUseSSL; | ||||||
| 		}); | 		}); | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -382,6 +440,16 @@ export default Vue.extend({ | ||||||
| 				swPrivateKey: this.swPrivateKey, | 				swPrivateKey: this.swPrivateKey, | ||||||
| 				pinnedUsers: this.pinnedUsers.split('\n'), | 				pinnedUsers: this.pinnedUsers.split('\n'), | ||||||
| 				hiddenTags: this.hiddenTags.split('\n'), | 				hiddenTags: this.hiddenTags.split('\n'), | ||||||
|  | 				useObjectStorage: this.useObjectStorage, | ||||||
|  | 				objectStorageBaseUrl: this.objectStorageBaseUrl ? this.objectStorageBaseUrl : null, | ||||||
|  | 				objectStorageBucket: this.objectStorageBucket ? this.objectStorageBucket : null, | ||||||
|  | 				objectStoragePrefix: this.objectStoragePrefix ? this.objectStoragePrefix : null, | ||||||
|  | 				objectStorageEndpoint: this.objectStorageEndpoint ? this.objectStorageEndpoint : null, | ||||||
|  | 				objectStorageRegion: this.objectStorageRegion ? this.objectStorageRegion : null, | ||||||
|  | 				objectStoragePort: this.objectStoragePort ? this.objectStoragePort : null, | ||||||
|  | 				objectStorageAccessKey: this.objectStorageAccessKey ? this.objectStorageAccessKey : null, | ||||||
|  | 				objectStorageSecretKey: this.objectStorageSecretKey ? this.objectStorageSecretKey : null, | ||||||
|  | 				objectStorageUseSSL: this.objectStorageUseSSL, | ||||||
| 			}).then(() => { | 			}).then(() => { | ||||||
| 				this.$root.dialog({ | 				this.$root.dialog({ | ||||||
| 					type: 'success', | 					type: 'success', | ||||||
|  |  | ||||||
|  | @ -27,13 +27,6 @@ export type Source = { | ||||||
| 		port: number; | 		port: number; | ||||||
| 		pass: string; | 		pass: string; | ||||||
| 	}; | 	}; | ||||||
| 	drive?: { |  | ||||||
| 		storage: string; |  | ||||||
| 		bucket?: string; |  | ||||||
| 		prefix?: string; |  | ||||||
| 		baseUrl?: string; |  | ||||||
| 		config?: any; |  | ||||||
| 	}; |  | ||||||
| 
 | 
 | ||||||
| 	autoAdmin?: boolean; | 	autoAdmin?: boolean; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -288,4 +288,61 @@ export class Meta { | ||||||
| 		nullable: true | 		nullable: true | ||||||
| 	}) | 	}) | ||||||
| 	public feedbackUrl: string | null; | 	public feedbackUrl: string | null; | ||||||
|  | 
 | ||||||
|  | 	@Column('boolean', { | ||||||
|  | 		default: false, | ||||||
|  | 	}) | ||||||
|  | 	public useObjectStorage: boolean; | ||||||
|  | 
 | ||||||
|  | 	@Column('varchar', { | ||||||
|  | 		length: 512, | ||||||
|  | 		nullable: true | ||||||
|  | 	}) | ||||||
|  | 	public objectStorageBucket: string | null; | ||||||
|  | 
 | ||||||
|  | 	@Column('varchar', { | ||||||
|  | 		length: 512, | ||||||
|  | 		nullable: true | ||||||
|  | 	}) | ||||||
|  | 	public objectStoragePrefix: string | null; | ||||||
|  | 
 | ||||||
|  | 	@Column('varchar', { | ||||||
|  | 		length: 512, | ||||||
|  | 		nullable: true | ||||||
|  | 	}) | ||||||
|  | 	public objectStorageBaseUrl: string | null; | ||||||
|  | 
 | ||||||
|  | 	@Column('varchar', { | ||||||
|  | 		length: 512, | ||||||
|  | 		nullable: true | ||||||
|  | 	}) | ||||||
|  | 	public objectStorageEndpoint: string | null; | ||||||
|  | 
 | ||||||
|  | 	@Column('varchar', { | ||||||
|  | 		length: 512, | ||||||
|  | 		nullable: true | ||||||
|  | 	}) | ||||||
|  | 	public objectStorageRegion: string | null; | ||||||
|  | 
 | ||||||
|  | 	@Column('varchar', { | ||||||
|  | 		length: 512, | ||||||
|  | 		nullable: true | ||||||
|  | 	}) | ||||||
|  | 	public objectStorageAccessKey: string | null; | ||||||
|  | 
 | ||||||
|  | 	@Column('varchar', { | ||||||
|  | 		length: 512, | ||||||
|  | 		nullable: true | ||||||
|  | 	}) | ||||||
|  | 	public objectStorageSecretKey: string | null; | ||||||
|  | 
 | ||||||
|  | 	@Column('integer', { | ||||||
|  | 		nullable: true | ||||||
|  | 	}) | ||||||
|  | 	public objectStoragePort: number | null; | ||||||
|  | 
 | ||||||
|  | 	@Column('boolean', { | ||||||
|  | 		default: true, | ||||||
|  | 	}) | ||||||
|  | 	public objectStorageUseSSL: boolean; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -357,7 +357,47 @@ export const meta = { | ||||||
| 			desc: { | 			desc: { | ||||||
| 				'ja-JP': 'フィードバックのURL' | 				'ja-JP': 'フィードバックのURL' | ||||||
| 			} | 			} | ||||||
| 		} | 		}, | ||||||
|  | 
 | ||||||
|  | 		useObjectStorage: { | ||||||
|  | 			validator: $.optional.bool | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		objectStorageBaseUrl: { | ||||||
|  | 			validator: $.optional.nullable.str | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		objectStorageBucket: { | ||||||
|  | 			validator: $.optional.nullable.str | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		objectStoragePrefix: { | ||||||
|  | 			validator: $.optional.nullable.str | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		objectStorageEndpoint: { | ||||||
|  | 			validator: $.optional.nullable.str | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		objectStorageRegion: { | ||||||
|  | 			validator: $.optional.nullable.str | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		objectStoragePort: { | ||||||
|  | 			validator: $.optional.nullable.num | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		objectStorageAccessKey: { | ||||||
|  | 			validator: $.optional.nullable.str | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		objectStorageSecretKey: { | ||||||
|  | 			validator: $.optional.nullable.str | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		objectStorageUseSSL: { | ||||||
|  | 			validator: $.optional.bool | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -560,6 +600,46 @@ export default define(meta, async (ps) => { | ||||||
| 		set.feedbackUrl = ps.feedbackUrl; | 		set.feedbackUrl = ps.feedbackUrl; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if (ps.useObjectStorage !== undefined) { | ||||||
|  | 		set.useObjectStorage = ps.useObjectStorage; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ps.objectStorageBaseUrl !== undefined) { | ||||||
|  | 		set.objectStorageBaseUrl = ps.objectStorageBaseUrl; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ps.objectStorageBucket !== undefined) { | ||||||
|  | 		set.objectStorageBucket = ps.objectStorageBucket; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ps.objectStoragePrefix !== undefined) { | ||||||
|  | 		set.objectStoragePrefix = ps.objectStoragePrefix; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ps.objectStorageEndpoint !== undefined) { | ||||||
|  | 		set.objectStorageEndpoint = ps.objectStorageEndpoint; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ps.objectStorageRegion !== undefined) { | ||||||
|  | 		set.objectStorageRegion = ps.objectStorageRegion; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ps.objectStoragePort !== undefined) { | ||||||
|  | 		set.objectStoragePort = ps.objectStoragePort; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ps.objectStorageAccessKey !== undefined) { | ||||||
|  | 		set.objectStorageAccessKey = ps.objectStorageAccessKey; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ps.objectStorageSecretKey !== undefined) { | ||||||
|  | 		set.objectStorageSecretKey = ps.objectStorageSecretKey; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ps.objectStorageUseSSL !== undefined) { | ||||||
|  | 		set.objectStorageUseSSL = ps.objectStorageUseSSL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	await getConnection().transaction(async transactionalEntityManager => { | 	await getConnection().transaction(async transactionalEntityManager => { | ||||||
| 		const meta = await transactionalEntityManager.findOne(Meta, { | 		const meta = await transactionalEntityManager.findOne(Meta, { | ||||||
| 			order: { | 			order: { | ||||||
|  |  | ||||||
|  | @ -153,7 +153,7 @@ export default define(meta, async (ps, me) => { | ||||||
| 			globalTimeLine: !instance.disableGlobalTimeline, | 			globalTimeLine: !instance.disableGlobalTimeline, | ||||||
| 			elasticsearch: config.elasticsearch ? true : false, | 			elasticsearch: config.elasticsearch ? true : false, | ||||||
| 			recaptcha: instance.enableRecaptcha, | 			recaptcha: instance.enableRecaptcha, | ||||||
| 			objectStorage: config.drive && config.drive.storage === 'minio', | 			objectStorage: instance.useObjectStorage, | ||||||
| 			twitter: instance.enableTwitterIntegration, | 			twitter: instance.enableTwitterIntegration, | ||||||
| 			github: instance.enableGithubIntegration, | 			github: instance.enableGithubIntegration, | ||||||
| 			discord: instance.enableDiscordIntegration, | 			discord: instance.enableDiscordIntegration, | ||||||
|  | @ -182,6 +182,16 @@ export default define(meta, async (ps, me) => { | ||||||
| 		response.smtpUser = instance.smtpUser; | 		response.smtpUser = instance.smtpUser; | ||||||
| 		response.smtpPass = instance.smtpPass; | 		response.smtpPass = instance.smtpPass; | ||||||
| 		response.swPrivateKey = instance.swPrivateKey; | 		response.swPrivateKey = instance.swPrivateKey; | ||||||
|  | 		response.useObjectStorage = instance.useObjectStorage; | ||||||
|  | 		response.objectStorageBaseUrl = instance.objectStorageBaseUrl; | ||||||
|  | 		response.objectStorageBucket = instance.objectStorageBucket; | ||||||
|  | 		response.objectStoragePrefix = instance.objectStoragePrefix; | ||||||
|  | 		response.objectStorageEndpoint = instance.objectStorageEndpoint; | ||||||
|  | 		response.objectStorageRegion = instance.objectStorageRegion; | ||||||
|  | 		response.objectStoragePort = instance.objectStoragePort; | ||||||
|  | 		response.objectStorageAccessKey = instance.objectStorageAccessKey; | ||||||
|  | 		response.objectStorageSecretKey = instance.objectStorageSecretKey; | ||||||
|  | 		response.objectStorageUseSSL = instance.objectStorageUseSSL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return response; | 	return response; | ||||||
|  |  | ||||||
|  | @ -8,7 +8,6 @@ import * as sharp from 'sharp'; | ||||||
| 
 | 
 | ||||||
| import { publishMainStream, publishDriveStream } from '../stream'; | import { publishMainStream, publishDriveStream } from '../stream'; | ||||||
| import delFile from './delete-file'; | import delFile from './delete-file'; | ||||||
| import config from '../../config'; |  | ||||||
| import { fetchMeta } from '../../misc/fetch-meta'; | import { fetchMeta } from '../../misc/fetch-meta'; | ||||||
| import { GenerateVideoThumbnail } from './generate-video-thumbnail'; | import { GenerateVideoThumbnail } from './generate-video-thumbnail'; | ||||||
| import { driveLogger } from './logger'; | import { driveLogger } from './logger'; | ||||||
|  | @ -37,7 +36,9 @@ async function save(file: DriveFile, path: string, name: string, type: string, h | ||||||
| 	// thunbnail, webpublic を必要なら生成
 | 	// thunbnail, webpublic を必要なら生成
 | ||||||
| 	const alts = await generateAlts(path, type, !file.uri); | 	const alts = await generateAlts(path, type, !file.uri); | ||||||
| 
 | 
 | ||||||
| 	if (config.drive && config.drive.storage == 'minio') { | 	const meta = await fetchMeta(); | ||||||
|  | 
 | ||||||
|  | 	if (meta.useObjectStorage) { | ||||||
| 		//#region ObjectStorage params
 | 		//#region ObjectStorage params
 | ||||||
| 		let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) || ['']); | 		let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) || ['']); | ||||||
| 
 | 
 | ||||||
|  | @ -47,11 +48,11 @@ async function save(file: DriveFile, path: string, name: string, type: string, h | ||||||
| 			if (type === 'image/webp') ext = '.webp'; | 			if (type === 'image/webp') ext = '.webp'; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		const baseUrl = config.drive.baseUrl | 		const baseUrl = meta.objectStorageBaseUrl | ||||||
| 			|| `${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? `:${config.drive.config.port}` : '' }/${ config.drive.bucket }`; | 			|| `${ meta.objectStorageUseSSL ? 'https' : 'http' }://${ meta.objectStorageEndpoint }${ meta.objectStoragePort ? `:${meta.objectStoragePort}` : '' }/${ meta.objectStorageBucket }`; | ||||||
| 
 | 
 | ||||||
| 		// for original
 | 		// for original
 | ||||||
| 		const key = `${config.drive.prefix}/${uuid.v4()}${ext}`; | 		const key = `${meta.objectStoragePrefix}/${uuid.v4()}${ext}`; | ||||||
| 		const url = `${ baseUrl }/${ key }`; | 		const url = `${ baseUrl }/${ key }`; | ||||||
| 
 | 
 | ||||||
| 		// for alts
 | 		// for alts
 | ||||||
|  | @ -68,7 +69,7 @@ async function save(file: DriveFile, path: string, name: string, type: string, h | ||||||
| 		]; | 		]; | ||||||
| 
 | 
 | ||||||
| 		if (alts.webpublic) { | 		if (alts.webpublic) { | ||||||
| 			webpublicKey = `${config.drive.prefix}/${uuid.v4()}.${alts.webpublic.ext}`; | 			webpublicKey = `${meta.objectStoragePrefix}/${uuid.v4()}.${alts.webpublic.ext}`; | ||||||
| 			webpublicUrl = `${ baseUrl }/${ webpublicKey }`; | 			webpublicUrl = `${ baseUrl }/${ webpublicKey }`; | ||||||
| 
 | 
 | ||||||
| 			logger.info(`uploading webpublic: ${webpublicKey}`); | 			logger.info(`uploading webpublic: ${webpublicKey}`); | ||||||
|  | @ -76,7 +77,7 @@ async function save(file: DriveFile, path: string, name: string, type: string, h | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (alts.thumbnail) { | 		if (alts.thumbnail) { | ||||||
| 			thumbnailKey = `${config.drive.prefix}/${uuid.v4()}.${alts.thumbnail.ext}`; | 			thumbnailKey = `${meta.objectStoragePrefix}/${uuid.v4()}.${alts.thumbnail.ext}`; | ||||||
| 			thumbnailUrl = `${ baseUrl }/${ thumbnailKey }`; | 			thumbnailUrl = `${ baseUrl }/${ thumbnailKey }`; | ||||||
| 
 | 
 | ||||||
| 			logger.info(`uploading thumbnail: ${thumbnailKey}`); | 			logger.info(`uploading thumbnail: ${thumbnailKey}`); | ||||||
|  | @ -194,7 +195,15 @@ export async function generateAlts(path: string, type: string, generateWeb: bool | ||||||
|  * Upload to ObjectStorage |  * Upload to ObjectStorage | ||||||
|  */ |  */ | ||||||
| async function upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) { | async function upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) { | ||||||
| 	const minio = new Minio.Client(config.drive!.config); | 	const meta = await fetchMeta(); | ||||||
|  | 
 | ||||||
|  | 	const minio = new Minio.Client({ | ||||||
|  | 		endPoint: meta.objectStorageEndpoint!, | ||||||
|  | 		port: meta.objectStoragePort ? meta.objectStoragePort : undefined, | ||||||
|  | 		useSSL: meta.objectStorageUseSSL, | ||||||
|  | 		accessKey: meta.objectStorageAccessKey!, | ||||||
|  | 		secretKey: meta.objectStorageSecretKey!, | ||||||
|  | 	}); | ||||||
| 
 | 
 | ||||||
| 	const metadata = { | 	const metadata = { | ||||||
| 		'Content-Type': type, | 		'Content-Type': type, | ||||||
|  | @ -203,7 +212,7 @@ async function upload(key: string, stream: fs.ReadStream | Buffer, type: string, | ||||||
| 
 | 
 | ||||||
| 	if (filename) metadata['Content-Disposition'] = contentDisposition('inline', filename); | 	if (filename) metadata['Content-Disposition'] = contentDisposition('inline', filename); | ||||||
| 
 | 
 | ||||||
| 	await minio.putObject(config.drive!.bucket!, key, stream, undefined, metadata); | 	await minio.putObject(meta.objectStorageBucket!, key, stream, undefined, metadata); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function deleteOldFile(user: IRemoteUser) { | async function deleteOldFile(user: IRemoteUser) { | ||||||
|  |  | ||||||
|  | @ -1,9 +1,9 @@ | ||||||
| import * as Minio from 'minio'; | import * as Minio from 'minio'; | ||||||
| import config from '../../config'; |  | ||||||
| import { DriveFile } from '../../models/entities/drive-file'; | import { DriveFile } from '../../models/entities/drive-file'; | ||||||
| import { InternalStorage } from './internal-storage'; | import { InternalStorage } from './internal-storage'; | ||||||
| import { DriveFiles, Instances, Notes } from '../../models'; | import { DriveFiles, Instances, Notes } from '../../models'; | ||||||
| import { driveChart, perUserDriveChart, instanceChart } from '../chart'; | import { driveChart, perUserDriveChart, instanceChart } from '../chart'; | ||||||
|  | import { fetchMeta } from '../../misc/fetch-meta'; | ||||||
| 
 | 
 | ||||||
| export default async function(file: DriveFile, isExpired = false) { | export default async function(file: DriveFile, isExpired = false) { | ||||||
| 	if (file.storedInternal) { | 	if (file.storedInternal) { | ||||||
|  | @ -17,16 +17,24 @@ export default async function(file: DriveFile, isExpired = false) { | ||||||
| 			InternalStorage.del(file.webpublicAccessKey!); | 			InternalStorage.del(file.webpublicAccessKey!); | ||||||
| 		} | 		} | ||||||
| 	} else if (!file.isLink) { | 	} else if (!file.isLink) { | ||||||
| 		const minio = new Minio.Client(config.drive!.config); | 		const meta = await fetchMeta(); | ||||||
| 
 | 
 | ||||||
| 		await minio.removeObject(config.drive!.bucket!, file.accessKey!); | 		const minio = new Minio.Client({ | ||||||
|  | 			endPoint: meta.objectStorageEndpoint!, | ||||||
|  | 			port: meta.objectStoragePort ? meta.objectStoragePort : undefined, | ||||||
|  | 			useSSL: meta.objectStorageUseSSL, | ||||||
|  | 			accessKey: meta.objectStorageAccessKey!, | ||||||
|  | 			secretKey: meta.objectStorageSecretKey!, | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		await minio.removeObject(meta.objectStorageBucket!, file.accessKey!); | ||||||
| 
 | 
 | ||||||
| 		if (file.thumbnailUrl) { | 		if (file.thumbnailUrl) { | ||||||
| 			await minio.removeObject(config.drive!.bucket!, file.thumbnailAccessKey!); | 			await minio.removeObject(meta.objectStorageBucket!, file.thumbnailAccessKey!); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (file.webpublicUrl) { | 		if (file.webpublicUrl) { | ||||||
| 			await minio.removeObject(config.drive!.bucket!, file.webpublicAccessKey!); | 			await minio.removeObject(meta.objectStorageBucket!, file.webpublicAccessKey!); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue