strapiとsupabaseを繋ぐ(storage編)

先日[こんな記事](https://qiita.com/ikea_shark_jk/items/a65cd6534ad2e9e2cba0)や[こんな記事](https://qiita.com/ikea_shark_jk/items/c7bfedb604b2b0735837)を投稿したので、今度はファイルのアップロード先もsupabaseにお任せしてしまおうかと思います。

## Supabaseにバケットを作成する

`strapi`という名前のバケットを作成し、Publicにしておきます。
作成後、Policyで画像拡張子のファイルがアップロードできるように設定をします。
わたしの環境では、SELECT, INSERT, DELETEができる権限を設定しておきました。SELECTなしでも行けるんじゃないかと思いましたが、removeの実行はDELETEだけでは権限不足なようです。

## Providerのインストール

StrapiとSupabaseのバケットをつなぐproviderをインストールします。
ただ、[それっぽいパッケージ](https://github.com/crubier/strapi-provider-upload-supabase)はあったのですがissueにstrapi v4に対応してないみたいなことが書いてあったのと、もう一つのv4に対応してそうなパッケージの方は存在自体が怪しすぎたので自作しました。

https://github.com/miya-fubu/strapi-provider-upload-supabase-strage

……記事書いてる途中に気が付いたんですが、パッケージ名がtypoしてますね。strageでなくてstorageです。strapiってずっと書いてたからかもしれません。

まあtypoはともかく、以下のコマンドでパッケージを取って来ます。

```
npm i strapi-provider-upload-supabase-strage
```

その後、プラグインファイルを追加してreadmeの内容をコピペ、環境変数に必要事項を追加します。

```config/plugins.ts
module.exports = ({ env }) => ({
  upload: {
    config: {
      provider: 'strapi-provider-upload-supabase-strage', // For community providers pass the full package name (e.g. provider: 'strapi-provider-upload-google-cloud-storage')
      providerOptions: {
        url: env("SUPABASE_URL"),
        apiKey: env("SUPABASE_KEY"),
        bucket: env("SUPABASE_BUCKET"),
        bucketPrefix: env("SUPABASE_BUCKET_PREFIX")
      },
    },
  },
});
```

```.env
SUPABASE_URL=<supabase API URL>
SUPABASE_KEY=<supabase API Key>
SUPABASE_BUCKET_PREFIX=hoge
```

SUPABASE_BUCKET_PREFIXはなくても良いですが、 SUPABASE_BUCKET_PREFIX == バケット内のディレクトリなので、ここを設定しておくといざという時にディレクトリごとファイルを一気に消し飛ばせるため楽です。

ひとまずこれでアップロードは可能になりました。
ただ、これだと上げた画像をstrapi上から閲覧できないので設定を修正する必要があります。

`config/middlewares.ts`を以下の内容に書き換えます。

```config/middlewares.ts

export default [
  "strapi::errors",
  {
    name: "strapi::security",
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          "connect-src": ["'self'", "https:"],
          "img-src": ["'self'", "data:", "blob:", process.env.SUPABASE_URL],
          "media-src": ["'self'", "data:", "blob:", process.env.SUPABASE_URL],
          upgradeInsecureRequests: null,
        },
      },
    },
  },
  "strapi::poweredBy",
  {
    name: "strapi::cors",
    config: {
      enabled: true,
      headers: "*",
      origin: [
        `http://localhost:${process.env.PORT}`,
        process.env.SUPABASE_URL,
      ],
    },
  },
  "strapi::logger",
  "strapi::query",
  "strapi::body",
  "strapi::session",
  "strapi::favicon",
  "strapi::public",
];
```

これで、`<img src="https://...supabase.io/hogehoge.png" />`みたいな要素で画像を閲覧できるようになります。

## おまけ: strapiのuppload-providerのつくりかた

もしこちらの内容が古かった場合は、こちらのstrapi公式ドキュメントを参考にしてください。
https://docs.strapi.io/developer-docs/latest/development/providers.html

というか古くなくても一読しておくとよいです。

### 置き場を作る

`provider/strapi-provider-<plugin>-<provider>`ディレクトリを作成します。
例えばsupabaseのファイルアップロードプラグインの場合は、`strapi-provider-upload-supabase`です。

その中で`npm init`したのち、`main`に指定したファイルに書き込んでいきます。

### 処理を記載する

strapiの公式に土台となるテンプレートがあるのでこちらを`main`で指定したファイルに転記して使用します。

```[main].js
module.exports = {
  init(providerOptions) {
    // init your provider if necessary

    return {
      upload(file) {
        // upload the file in the provider
        // file content is accessible by `file.buffer`
      },
      uploadStream(file) {
        // upload the file in the provider
        // file content is accessible by `file.stream`
      },
      delete(file) {
        // delete the file in the provider
      },
    };
  },
};

```

providerOptionsは、`config/plugins.ts`のproviderOptionsで指定した値です。

記載する内容は基本的に少なく、中の処理では該当のStorageにファイルを投げる処理と、Strapiのユーザー側から閲覧できる画像のURLをfile.urlに渡してあげるだけです。

イメージとしては以下のような感じですね。

```sample.js
module.exports = {
  init(providerOptions) {
    return {
      upload(file) {
        const { publickImageFileURL } = somethingStorage
          .upload(
            'bucketname',
            new Blob([file.buffer], { type: file.mime })
          );
        file.url = publickImageFileURL;
      },
      uploadStream(file) {
        const { publickImageFileURL } = somethingStorage
          .upload(
            'bucketname',
            streamToBlob(file.stream)
          );
        file.url = publickImageFileURL;      },
      delete(file) {
        somethingStorage.bucket("bucketname").remove(file.name)
      },
    };
  },
};
```

こうして書き終わったら、strapi側にあるpackage.jsonに以下を追記します。

```package.json
{
  ...
  "dependencies": {
    ...
    "strapi-provider-<plugin>-<provider>": "file:providers/strapi-provider-<plugin>-<provider>",
    ...
  }
}

```

これにて追加完了です。`npm i`しましょう。