FileUploadSetting.tsx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import type { JSX } from 'react';
  2. import { useCallback } from 'react';
  3. import { useTranslation } from 'next-i18next';
  4. import { useController, useForm } from 'react-hook-form';
  5. import { toastError, toastSuccess } from '~/client/util/toastr';
  6. import { FileUploadType } from '~/interfaces/file-uploader';
  7. import { useGrowiAppIdForGrowiCloud, useGrowiCloudUri } from '~/states/global';
  8. import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
  9. import { AwsSettingMolecule } from './AwsSetting';
  10. import { AzureSettingMolecule } from './AzureSetting';
  11. import type { FileUploadFormValues } from './FileUploadSetting.types';
  12. import { GcsSettingMolecule } from './GcsSetting';
  13. import { useFileUploadSettings } from './useFileUploadSettings';
  14. const FileUploadSetting = (): JSX.Element => {
  15. const { t } = useTranslation(['admin', 'commons']);
  16. const growiCloudUri = useGrowiCloudUri();
  17. const growiAppIdForGrowiCloud = useGrowiAppIdForGrowiCloud();
  18. const isCloud = growiCloudUri != null && growiAppIdForGrowiCloud != null;
  19. const { data, isLoading, error, updateSettings } = useFileUploadSettings();
  20. const { register, handleSubmit, control, watch, formState } =
  21. useForm<FileUploadFormValues>({
  22. values: data
  23. ? {
  24. fileUploadType: data.fileUploadType,
  25. s3Region: data.s3Region,
  26. s3CustomEndpoint: data.s3CustomEndpoint,
  27. s3Bucket: data.s3Bucket,
  28. s3AccessKeyId: data.s3AccessKeyId,
  29. s3SecretAccessKey: data.s3SecretAccessKey,
  30. s3ReferenceFileWithRelayMode: data.s3ReferenceFileWithRelayMode,
  31. gcsApiKeyJsonPath: data.gcsApiKeyJsonPath,
  32. gcsBucket: data.gcsBucket,
  33. gcsUploadNamespace: data.gcsUploadNamespace,
  34. gcsReferenceFileWithRelayMode: data.gcsReferenceFileWithRelayMode,
  35. azureTenantId: data.azureTenantId,
  36. azureClientId: data.azureClientId,
  37. azureClientSecret: data.azureClientSecret,
  38. azureStorageAccountName: data.azureStorageAccountName,
  39. azureStorageContainerName: data.azureStorageContainerName,
  40. azureReferenceFileWithRelayMode:
  41. data.azureReferenceFileWithRelayMode,
  42. }
  43. : undefined,
  44. });
  45. // Use controller for fileUploadType radio buttons
  46. const { field: fileUploadTypeField } = useController({
  47. name: 'fileUploadType',
  48. control,
  49. });
  50. // Use controller for relay mode fields
  51. const { field: s3RelayModeField } = useController({
  52. name: 's3ReferenceFileWithRelayMode',
  53. control,
  54. });
  55. const { field: gcsRelayModeField } = useController({
  56. name: 'gcsReferenceFileWithRelayMode',
  57. control,
  58. });
  59. const { field: azureRelayModeField } = useController({
  60. name: 'azureReferenceFileWithRelayMode',
  61. control,
  62. });
  63. const fileUploadType = watch('fileUploadType');
  64. const onSubmit = useCallback(
  65. async (formData: FileUploadFormValues) => {
  66. try {
  67. await updateSettings(formData, formState.dirtyFields);
  68. toastSuccess(
  69. t('toaster.update_successed', {
  70. target: t('admin:app_setting.file_upload_settings'),
  71. ns: 'commons',
  72. }),
  73. );
  74. } catch (err) {
  75. toastError(err);
  76. }
  77. },
  78. [updateSettings, formState.dirtyFields, t],
  79. );
  80. if (isLoading) {
  81. return <div>Loading...</div>;
  82. }
  83. if (error || !data) {
  84. return <div>Error loading settings</div>;
  85. }
  86. return (
  87. <form onSubmit={handleSubmit(onSubmit)}>
  88. <p className="card custom-card bg-warning-subtle my-3">
  89. {t('admin:app_setting.file_upload')}
  90. <span className="text-danger mt-1">
  91. <span className="material-symbols-outlined">link_off</span>
  92. {t('admin:app_setting.change_setting')}
  93. </span>
  94. </p>
  95. <div className="row mb-3">
  96. <span className="text-start text-md-end col-md-3 col-form-label">
  97. {t('admin:app_setting.file_upload_method')}
  98. </span>
  99. {!isCloud && (
  100. <div className="col-md-6 py-2">
  101. {Object.values(FileUploadType).map((type) => {
  102. return (
  103. <div key={type} className="form-check form-check-inline">
  104. <input
  105. type="radio"
  106. className="form-check-input"
  107. name="file-upload-type"
  108. id={`file-upload-type-radio-${type}`}
  109. checked={fileUploadTypeField.value === type}
  110. disabled={data.isFixedFileUploadByEnvVar}
  111. onChange={() => fileUploadTypeField.onChange(type)}
  112. />
  113. <label
  114. className="form-label form-check-label"
  115. htmlFor={`file-upload-type-radio-${type}`}
  116. >
  117. {t(`admin:app_setting.${type}_label`)}
  118. </label>
  119. </div>
  120. );
  121. })}
  122. </div>
  123. )}
  124. {isCloud ? (
  125. <div className="alert alert-warning mt-2 text-start offset-3 col-6">
  126. <p>
  127. {t('admin:cloud_setting_management.storage_change_from_cloud', {
  128. fileUploadType: t(`admin:app_setting.${fileUploadType}_label`),
  129. })}
  130. </p>
  131. <a
  132. href={`${growiCloudUri}/my/apps/${growiAppIdForGrowiCloud}`}
  133. className="btn btn-outline-secondary"
  134. >
  135. <span className="material-symbols-outlined me-1">share</span>
  136. {t('admin:cloud_setting_management.to_cloud_settings')}
  137. </a>
  138. </div>
  139. ) : (
  140. data.isFixedFileUploadByEnvVar && (
  141. <p className="alert alert-warning mt-2 text-start offset-3 col-6">
  142. <span className="material-symbols-outlined">help</span>
  143. <b>FIXED</b>
  144. <br />
  145. <b
  146. // biome-ignore lint/security/noDangerouslySetInnerHtml: includes markup from i18n strings
  147. dangerouslySetInnerHTML={{
  148. __html: t('admin:app_setting.fixed_by_env_var', {
  149. envKey: 'FILE_UPLOAD',
  150. envVar: data.envFileUploadType,
  151. }),
  152. }}
  153. />
  154. </p>
  155. )
  156. )}
  157. </div>
  158. {fileUploadType === 'aws' && (
  159. <AwsSettingMolecule
  160. register={register}
  161. s3ReferenceFileWithRelayMode={s3RelayModeField.value}
  162. onChangeS3ReferenceFileWithRelayMode={s3RelayModeField.onChange}
  163. />
  164. )}
  165. {fileUploadType === 'gcs' && (
  166. <GcsSettingMolecule
  167. register={register}
  168. gcsReferenceFileWithRelayMode={gcsRelayModeField.value}
  169. gcsUseOnlyEnvVars={data.gcsUseOnlyEnvVars}
  170. envGcsApiKeyJsonPath={data.envGcsApiKeyJsonPath}
  171. envGcsBucket={data.envGcsBucket}
  172. envGcsUploadNamespace={data.envGcsUploadNamespace}
  173. onChangeGcsReferenceFileWithRelayMode={gcsRelayModeField.onChange}
  174. isCloud={isCloud}
  175. />
  176. )}
  177. {fileUploadType === 'azure' && (
  178. <AzureSettingMolecule
  179. register={register}
  180. azureReferenceFileWithRelayMode={azureRelayModeField.value}
  181. azureUseOnlyEnvVars={data.azureUseOnlyEnvVars}
  182. envAzureTenantId={data.envAzureTenantId}
  183. envAzureClientId={data.envAzureClientId}
  184. envAzureClientSecret={data.envAzureClientSecret}
  185. envAzureStorageAccountName={data.envAzureStorageAccountName}
  186. envAzureStorageContainerName={data.envAzureStorageContainerName}
  187. onChangeAzureReferenceFileWithRelayMode={azureRelayModeField.onChange}
  188. isCloud={isCloud}
  189. />
  190. )}
  191. {!isCloud && <AdminUpdateButtonRow type="submit" disabled={isLoading} />}
  192. </form>
  193. );
  194. };
  195. export default FileUploadSetting;