FileUploadSetting.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. import type { ChangeEvent, JSX } from 'react';
  2. import React, { useCallback, useEffect } from 'react';
  3. import { useTranslation } from 'next-i18next';
  4. import { useForm } from 'react-hook-form';
  5. import AdminAppContainer from '~/client/services/AdminAppContainer';
  6. import { toastSuccess, toastError } from '~/client/util/toastr';
  7. import { FileUploadType } from '~/interfaces/file-uploader';
  8. import { withUnstatedContainers } from '../../UnstatedUtils';
  9. import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
  10. import { AwsSettingMolecule } from './AwsSetting';
  11. import type { AwsSettingMoleculeProps } from './AwsSetting';
  12. import { AzureSettingMolecule } from './AzureSetting';
  13. import type { AzureSettingMoleculeProps } from './AzureSetting';
  14. import { GcsSettingMolecule } from './GcsSetting';
  15. import type { GcsSettingMoleculeProps } from './GcsSetting';
  16. type FileUploadSettingMoleculeProps = {
  17. fileUploadType: string
  18. isFixedFileUploadByEnvVar: boolean
  19. envFileUploadType?: string
  20. onChangeFileUploadType: (e: ChangeEvent, type: string) => void
  21. register: ReturnType<typeof useForm>['register']
  22. } & Omit<AwsSettingMoleculeProps, 'register'> & Omit<GcsSettingMoleculeProps, 'register'> & Omit<AzureSettingMoleculeProps, 'register'>;
  23. export const FileUploadSettingMolecule = React.memo((props: FileUploadSettingMoleculeProps): JSX.Element => {
  24. const { t } = useTranslation(['admin', 'commons']);
  25. return (
  26. <>
  27. <p className="card custom-card bg-warning-subtle my-3">
  28. {t('admin:app_setting.file_upload')}
  29. <span className="text-danger mt-1">
  30. <span className="material-symbols-outlined">link_off</span>
  31. {t('admin:app_setting.change_setting')}
  32. </span>
  33. </p>
  34. <div className="row mb-3">
  35. <label className="text-start text-md-end col-md-3 col-form-label">
  36. {t('admin:app_setting.file_upload_method')}
  37. </label>
  38. <div className="col-md-6 py-2">
  39. {Object.values(FileUploadType).map((type) => {
  40. return (
  41. <div key={type} className="form-check form-check-inline">
  42. <input
  43. type="radio"
  44. className="form-check-input"
  45. name="file-upload-type"
  46. id={`file-upload-type-radio-${type}`}
  47. checked={props.fileUploadType === type}
  48. disabled={props.isFixedFileUploadByEnvVar}
  49. onChange={(e) => { props?.onChangeFileUploadType(e, type) }}
  50. />
  51. <label className="form-label form-check-label" htmlFor={`file-upload-type-radio-${type}`}>{t(`admin:app_setting.${type}_label`)}</label>
  52. </div>
  53. );
  54. })}
  55. </div>
  56. {props.isFixedFileUploadByEnvVar && (
  57. <p className="alert alert-warning mt-2 text-start offset-3 col-6">
  58. <span className="material-symbols-outlined">help</span>
  59. <b>FIXED</b><br />
  60. {/* eslint-disable-next-line react/no-danger */}
  61. <b dangerouslySetInnerHTML={{
  62. __html: t('admin:app_setting.fixed_by_env_var', {
  63. envKey: 'FILE_UPLOAD',
  64. envVar: props.envFileUploadType,
  65. }),
  66. }}
  67. />
  68. </p>
  69. )}
  70. </div>
  71. {props.fileUploadType === 'aws' && (
  72. <AwsSettingMolecule
  73. register={props.register}
  74. s3ReferenceFileWithRelayMode={props.s3ReferenceFileWithRelayMode}
  75. s3Region={props.s3Region}
  76. s3CustomEndpoint={props.s3CustomEndpoint}
  77. s3Bucket={props.s3Bucket}
  78. s3AccessKeyId={props.s3AccessKeyId}
  79. s3SecretAccessKey={props.s3SecretAccessKey}
  80. onChangeS3ReferenceFileWithRelayMode={props.onChangeS3ReferenceFileWithRelayMode}
  81. onChangeS3Region={props.onChangeS3Region}
  82. onChangeS3CustomEndpoint={props.onChangeS3CustomEndpoint}
  83. onChangeS3Bucket={props.onChangeS3Bucket}
  84. onChangeS3AccessKeyId={props.onChangeS3AccessKeyId}
  85. onChangeS3SecretAccessKey={props.onChangeS3SecretAccessKey}
  86. />
  87. )}
  88. {props.fileUploadType === 'gcs' && (
  89. <GcsSettingMolecule
  90. register={props.register}
  91. gcsReferenceFileWithRelayMode={props.gcsReferenceFileWithRelayMode}
  92. gcsUseOnlyEnvVars={props.gcsUseOnlyEnvVars}
  93. gcsApiKeyJsonPath={props.gcsApiKeyJsonPath}
  94. gcsBucket={props.gcsBucket}
  95. gcsUploadNamespace={props.gcsUploadNamespace}
  96. envGcsApiKeyJsonPath={props.envGcsApiKeyJsonPath}
  97. envGcsBucket={props.envGcsBucket}
  98. envGcsUploadNamespace={props.envGcsUploadNamespace}
  99. onChangeGcsReferenceFileWithRelayMode={props.onChangeGcsReferenceFileWithRelayMode}
  100. onChangeGcsApiKeyJsonPath={props.onChangeGcsApiKeyJsonPath}
  101. onChangeGcsBucket={props.onChangeGcsBucket}
  102. onChangeGcsUploadNamespace={props.onChangeGcsUploadNamespace}
  103. />
  104. )}
  105. {props.fileUploadType === 'azure' && (
  106. <AzureSettingMolecule
  107. register={props.register}
  108. azureReferenceFileWithRelayMode={props.azureReferenceFileWithRelayMode}
  109. azureUseOnlyEnvVars={props.azureUseOnlyEnvVars}
  110. azureTenantId={props.azureTenantId}
  111. azureClientId={props.azureClientId}
  112. azureClientSecret={props.azureClientSecret}
  113. azureStorageAccountName={props.azureStorageAccountName}
  114. azureStorageContainerName={props.azureStorageContainerName}
  115. envAzureStorageAccountName={props.envAzureStorageAccountName}
  116. envAzureStorageContainerName={props.envAzureStorageContainerName}
  117. envAzureTenantId={props.envAzureTenantId}
  118. envAzureClientId={props.envAzureClientId}
  119. envAzureClientSecret={props.envAzureClientSecret}
  120. onChangeAzureReferenceFileWithRelayMode={props.onChangeAzureReferenceFileWithRelayMode}
  121. onChangeAzureTenantId={props.onChangeAzureTenantId}
  122. onChangeAzureClientId={props.onChangeAzureClientId}
  123. onChangeAzureClientSecret={props.onChangeAzureClientSecret}
  124. onChangeAzureStorageAccountName={props.onChangeAzureStorageAccountName}
  125. onChangeAzureStorageContainerName={props.onChangeAzureStorageContainerName}
  126. />
  127. )}
  128. </>
  129. );
  130. });
  131. FileUploadSettingMolecule.displayName = 'FileUploadSettingMolecule';
  132. type FileUploadSettingProps = {
  133. adminAppContainer: AdminAppContainer
  134. }
  135. const FileUploadSetting = (props: FileUploadSettingProps): JSX.Element => {
  136. const { t } = useTranslation(['admin', 'commons']);
  137. const { adminAppContainer } = props;
  138. const {
  139. fileUploadType, isFixedFileUploadByEnvVar, envFileUploadType, retrieveError,
  140. s3ReferenceFileWithRelayMode,
  141. s3Region, s3CustomEndpoint, s3Bucket,
  142. s3AccessKeyId, s3SecretAccessKey,
  143. gcsReferenceFileWithRelayMode, gcsUseOnlyEnvVars,
  144. gcsApiKeyJsonPath, envGcsApiKeyJsonPath, gcsBucket,
  145. envGcsBucket, gcsUploadNamespace, envGcsUploadNamespace,
  146. azureReferenceFileWithRelayMode, azureUseOnlyEnvVars,
  147. azureTenantId, azureClientId, azureClientSecret,
  148. azureStorageAccountName, azureStorageContainerName,
  149. envAzureTenantId, envAzureClientId, envAzureClientSecret,
  150. envAzureStorageAccountName, envAzureStorageContainerName,
  151. } = adminAppContainer.state;
  152. const { register, handleSubmit, reset } = useForm();
  153. // Sync form with container state
  154. useEffect(() => {
  155. reset({
  156. s3Region,
  157. s3CustomEndpoint,
  158. s3Bucket,
  159. s3AccessKeyId,
  160. gcsApiKeyJsonPath,
  161. gcsBucket,
  162. gcsUploadNamespace,
  163. azureTenantId,
  164. azureClientId,
  165. azureClientSecret,
  166. azureStorageAccountName,
  167. azureStorageContainerName,
  168. });
  169. }, [
  170. reset,
  171. s3Region, s3CustomEndpoint, s3Bucket, s3AccessKeyId,
  172. gcsApiKeyJsonPath, gcsBucket, gcsUploadNamespace,
  173. azureTenantId, azureClientId, azureClientSecret,
  174. azureStorageAccountName, azureStorageContainerName,
  175. ]);
  176. const submitHandler = useCallback(async(data) => {
  177. try {
  178. // Update container state with form data
  179. await adminAppContainer.changeS3Region(data.s3Region ?? '');
  180. await adminAppContainer.changeS3CustomEndpoint(data.s3CustomEndpoint ?? '');
  181. await adminAppContainer.changeS3Bucket(data.s3Bucket ?? '');
  182. await adminAppContainer.changeS3AccessKeyId(data.s3AccessKeyId ?? '');
  183. await adminAppContainer.changeS3SecretAccessKey(data.s3SecretAccessKey ?? '');
  184. await adminAppContainer.changeGcsApiKeyJsonPath(data.gcsApiKeyJsonPath ?? '');
  185. await adminAppContainer.changeGcsBucket(data.gcsBucket ?? '');
  186. await adminAppContainer.changeGcsUploadNamespace(data.gcsUploadNamespace ?? '');
  187. await adminAppContainer.changeAzureTenantId(data.azureTenantId ?? '');
  188. await adminAppContainer.changeAzureClientId(data.azureClientId ?? '');
  189. await adminAppContainer.changeAzureClientSecret(data.azureClientSecret ?? '');
  190. await adminAppContainer.changeAzureStorageAccountName(data.azureStorageAccountName ?? '');
  191. await adminAppContainer.changeAzureStorageContainerName(data.azureStorageContainerName ?? '');
  192. await adminAppContainer.updateFileUploadSettingHandler();
  193. toastSuccess(t('toaster.update_successed', { target: t('admin:app_setting.file_upload_settings'), ns: 'commons' }));
  194. }
  195. catch (err) {
  196. toastError(err);
  197. }
  198. }, [adminAppContainer, t]);
  199. const onChangeFileUploadTypeHandler = useCallback((e: ChangeEvent, type: string) => {
  200. adminAppContainer.changeFileUploadType(type);
  201. }, [adminAppContainer]);
  202. // S3
  203. const onChangeS3ReferenceFileWithRelayModeHandler = useCallback((val: boolean) => {
  204. adminAppContainer.changeS3ReferenceFileWithRelayMode(val);
  205. }, [adminAppContainer]);
  206. const onChangeS3RegionHandler = useCallback((val: string) => {
  207. adminAppContainer.changeS3Region(val);
  208. }, [adminAppContainer]);
  209. const onChangeS3CustomEndpointHandler = useCallback((val: string) => {
  210. adminAppContainer.changeS3CustomEndpoint(val);
  211. }, [adminAppContainer]);
  212. const onChangeS3BucketHandler = useCallback((val: string) => {
  213. adminAppContainer.changeS3Bucket(val);
  214. }, [adminAppContainer]);
  215. const onChangeS3AccessKeyIdHandler = useCallback((val: string) => {
  216. adminAppContainer.changeS3AccessKeyId(val);
  217. }, [adminAppContainer]);
  218. const onChangeS3SecretAccessKeyHandler = useCallback((val: string) => {
  219. adminAppContainer.changeS3SecretAccessKey(val);
  220. }, [adminAppContainer]);
  221. // GCS
  222. const onChangeGcsReferenceFileWithRelayModeHandler = useCallback((val: boolean) => {
  223. adminAppContainer.changeGcsReferenceFileWithRelayMode(val);
  224. }, [adminAppContainer]);
  225. const onChangeGcsApiKeyJsonPathHandler = useCallback((val: string) => {
  226. adminAppContainer.changeGcsApiKeyJsonPath(val);
  227. }, [adminAppContainer]);
  228. const onChangeGcsBucketHandler = useCallback((val: string) => {
  229. adminAppContainer.changeGcsBucket(val);
  230. }, [adminAppContainer]);
  231. const onChangeGcsUploadNamespaceHandler = useCallback((val: string) => {
  232. adminAppContainer.changeGcsUploadNamespace(val);
  233. }, [adminAppContainer]);
  234. // Azure
  235. const onChangeAzureReferenceFileWithRelayModeHandler = useCallback((val: boolean) => {
  236. adminAppContainer.changeAzureReferenceFileWithRelayMode(val);
  237. }, [adminAppContainer]);
  238. const onChangeAzureTenantIdHandler = useCallback((val: string) => {
  239. adminAppContainer.changeAzureTenantId(val);
  240. }, [adminAppContainer]);
  241. const onChangeAzureClientIdHandler = useCallback((val: string) => {
  242. adminAppContainer.changeAzureClientId(val);
  243. }, [adminAppContainer]);
  244. const onChangeAzureClientSecretHandler = useCallback((val: string) => {
  245. adminAppContainer.changeAzureClientSecret(val);
  246. }, [adminAppContainer]);
  247. const onChangeAzureStorageAccountNameHandler = useCallback((val: string) => {
  248. adminAppContainer.changeAzureStorageAccountName(val);
  249. }, [adminAppContainer]);
  250. const onChangeAzureStorageContainerNameHandler = useCallback((val: string) => {
  251. adminAppContainer.changeAzureStorageContainerName(val);
  252. }, [adminAppContainer]);
  253. return (
  254. <form onSubmit={handleSubmit(submitHandler)}>
  255. <FileUploadSettingMolecule
  256. register={register}
  257. fileUploadType={fileUploadType}
  258. isFixedFileUploadByEnvVar={isFixedFileUploadByEnvVar}
  259. envFileUploadType={envFileUploadType}
  260. onChangeFileUploadType={onChangeFileUploadTypeHandler}
  261. s3ReferenceFileWithRelayMode={s3ReferenceFileWithRelayMode}
  262. s3Region={s3Region}
  263. s3CustomEndpoint={s3CustomEndpoint}
  264. s3Bucket={s3Bucket}
  265. s3AccessKeyId={s3AccessKeyId}
  266. s3SecretAccessKey={s3SecretAccessKey}
  267. onChangeS3ReferenceFileWithRelayMode={onChangeS3ReferenceFileWithRelayModeHandler}
  268. onChangeS3Region={onChangeS3RegionHandler}
  269. onChangeS3CustomEndpoint={onChangeS3CustomEndpointHandler}
  270. onChangeS3Bucket={onChangeS3BucketHandler}
  271. onChangeS3AccessKeyId={onChangeS3AccessKeyIdHandler}
  272. onChangeS3SecretAccessKey={onChangeS3SecretAccessKeyHandler}
  273. gcsReferenceFileWithRelayMode={gcsReferenceFileWithRelayMode}
  274. gcsUseOnlyEnvVars={gcsUseOnlyEnvVars}
  275. gcsApiKeyJsonPath={gcsApiKeyJsonPath}
  276. gcsBucket={gcsBucket}
  277. gcsUploadNamespace={gcsUploadNamespace}
  278. envGcsApiKeyJsonPath={envGcsApiKeyJsonPath}
  279. envGcsBucket={envGcsBucket}
  280. envGcsUploadNamespace={envGcsUploadNamespace}
  281. onChangeGcsReferenceFileWithRelayMode={onChangeGcsReferenceFileWithRelayModeHandler}
  282. onChangeGcsApiKeyJsonPath={onChangeGcsApiKeyJsonPathHandler}
  283. onChangeGcsBucket={onChangeGcsBucketHandler}
  284. onChangeGcsUploadNamespace={onChangeGcsUploadNamespaceHandler}
  285. azureReferenceFileWithRelayMode={azureReferenceFileWithRelayMode}
  286. azureUseOnlyEnvVars={azureUseOnlyEnvVars}
  287. azureTenantId={azureTenantId}
  288. azureClientId={azureClientId}
  289. azureClientSecret={azureClientSecret}
  290. azureStorageAccountName={azureStorageAccountName}
  291. azureStorageContainerName={azureStorageContainerName}
  292. envAzureTenantId={envAzureTenantId}
  293. envAzureClientId={envAzureClientId}
  294. envAzureClientSecret={envAzureClientSecret}
  295. envAzureStorageAccountName={envAzureStorageAccountName}
  296. envAzureStorageContainerName={envAzureStorageContainerName}
  297. onChangeAzureReferenceFileWithRelayMode={onChangeAzureReferenceFileWithRelayModeHandler}
  298. onChangeAzureTenantId={onChangeAzureTenantIdHandler}
  299. onChangeAzureClientId={onChangeAzureClientIdHandler}
  300. onChangeAzureClientSecret={onChangeAzureClientSecretHandler}
  301. onChangeAzureStorageAccountName={onChangeAzureStorageAccountNameHandler}
  302. onChangeAzureStorageContainerName={onChangeAzureStorageContainerNameHandler}
  303. />
  304. <AdminUpdateButtonRow type="submit" disabled={retrieveError != null} />
  305. </form>
  306. );
  307. };
  308. /**
  309. * Wrapper component for using unstated
  310. */
  311. const FileUploadSettingWrapper = withUnstatedContainers(FileUploadSetting, [AdminAppContainer]);
  312. export default FileUploadSettingWrapper;