用 redux async thunk 替换 S3Client 中的模拟凭据

Replace mock credentials in S3Client with redux async thunk

我有一个带有 redux 的类似 S3 的反应应用程序。我正在为 JS 使用 AWS SDK v3,并像这样初始化我的客户端:

auth.js

export const s3Client = new S3Client({
  region: 'default',
  credentials: {
    accessKeyId: 'testTestAccess',
    secretAccessKey: 'testTestSecret'
  },
  endpoint: `${document.URL}s3/`
});

我的请求通过我们的代理,这就是为什么我可以像上面的代码一样留下凭据,所以它可以是任何字符串。但我使用的是 s3 签名 url,它在查询字符串中使用凭据。

这就是我使用 redux-thunk 发送请求的方式

authReducer.js

const initialState = {
  secretKey: 'initSecretKey',
  accessKey: 'initAccessKey',
  keysCreated: false
};

export const fetchKeys = createAsyncThunk(
  'auth/fetchKeys',
  async (_, { rejectWithValue }) => {
    try {
      const response = await axiosInstance.get();

      if (response.statusText !== 'OK') {
        throw new Error('Error!');
      }

      return response.data.secrets;
    } catch (error) {
      return rejectWithValue(error.message);
    }
  }
);

export const authSlice = createSlice({
  name: 'auth',
  initialState,
  extraReducers: {
    [fetchKeys.pending]: (state, action) => {
      console.log('PENDING...');
    },
    [fetchKeys.fulfilled]: (state, action) => {
      const { AccessKey, SecretKey } = action.payload;
      state.secretKey = SecretKey;
      state.accessKey = AccessKey;
      state.keysCreated = true;
    },
    [fetchKeys.rejected]: (state, action) => {
      console.log('ERROR');
    }
  },
  reducers: {
    setSecretKey: (state, action) => ({
      ...state,
      secretKey: action.payload
    }),
    setAccessKey: (state, action) => ({
      ...state,
      accessKey: action.payload
    }),
    setKeysCreated: (state, action) => ({
      ...state,
      keysCreated: action.payload
    })
  }
});

export const { setSecretKey, setAccessKey, setKeysCreated } =
  authSlice.actions;

export default authSlice.reducer;

MainPage.jsx

const MainPage = () => {
  const dispatch = useDispatch();
  const { keysCreated } = useSelector((state) => state.authReducer);

  useEffect(() => {
    dispatch(fetchKeys());
  }, [dispatch]);

  if (keysCreated) {
    return <Content />
  }

  return <Loader />
};

那么我想做什么:

  1. 当页面呈现时,我使用 axios 和 redux-thunk 向 keygen 发送一个异步请求
  2. 仅当我获得密钥时才显示页面
  3. (!) 当请求成功时,用来自 keygen 的新密钥替换 s3 客户端实例中的模拟密钥,这样我就可以签名 urls.

我该怎么做?我只从商店拿到钥匙一次,所以看起来我需要订阅更改

const { secretKey, accessKey } = store.getState().authReducer;

export const s3Client = new S3Client({
  credentials: {
    accessKeyId: accessKey,     // 'initAccessKey' from initial state
    secretAccessKey: secretKey  // 'initSecretKey' from initial state
  }
});

我用 hook 解决了我的问题。

auth.js

export const s3Client = new S3Client({
  region: 'default',
  credentials: {
    accessKeyId: 'initAccessKey',
    secretAccessKey: 'initSecretKey'
  }
});

export const useS3Client = () => {
  const { accessKey, secretKey, keysCreated } = useSelector(
    (state) => state.authReducer
  );

  const client = useMemo(() => {
    if (keysCreated) {
      return new S3Client({
        region: 'default',
        credentials: {
          accessKeyId: accessKey,
          secretAccessKey: secretKey
        }
      });
    }

    return s3Client;
  }, [accessKey, secretKey, keysCreated]);

  return [client, keysCreated];
};

我为 API 签名请求更改了一些功能:

// i added client as 1st argument
export const getUrl = async (client, object, expirationSec = 300) => {
    const { name, absolutePath, parentBucket } = object;

    const params = {
      Bucket: parentBucket,
      Key: absolutePath
    };

    return await getSignedUrl(client, new GetObjectCommand(params), {
      expiresIn: expirationSec
    }).catch((error) => setError(error, 'SIGNED URL GETTING FAILED'));
  }

然后我可以在我的组件中使用它:

MainPage.jsx

const MainPageContainer = () => {
  const dispatch = useDispatch();
  const [, keysCreated] = useS3Client();

  useEffect(() => {
    dispatch(fetchKeys());
  }, [dispatch]);

  if (!keysCreated) return <Loader />;

  return <MainPage />;
};

SignUrlForm.jsx

const SignUrlForm = () => {
  const [client] = useS3Client();

  const onGetSignedUrlClick = (object) => {
    getUrl(client, object, intevalSec).then((url) => {
      // some actions
    });
  };

  return <Button onClick={onGetSignedUrlClick}>Sign URL</Button>;
};