ホーム 記事一覧 ブック DH週間トピックス 検索 このサイトについて
RSS English
Disable UI: DrupalをヘッドレスCMSとして使う

Disable UI: DrupalをヘッドレスCMSとして使う

DrupalをヘッドレスCMSとして使う機会があり、UIへのアクセスを管理者等に限定するDisable UIモジュールを試しました。 https://www.drupal.org/project/disable_ui 結果、トップページへのアクセス等は、以下のように表示されました。 一方、有効化したjson:apiなどについては、非ログインユーザもアクセスできました。 /jsonapi/ DrupalをヘッドレスCMSとして使用する際の参考になりましたら幸いです。

DrupalのJSON:APIの使用方法(includeと多言語対応)

DrupalのJSON:APIの使用方法(includeと多言語対応)

概要 DrupalのJSON:APIの使用方法に関する備忘録です。今回は、タクソノミーなどに対するincludeと多言語処理について記載します。 データ 以下のように、positionフィールドにタクソノミー「助教」を付与しています。 /node/5 また、コンテンツの多言語化を有効にしており、以下のように、タイトルとpositionの英語も表示されます。 /en/node/5 JSON:API 上記のコンテンツをコンテンツタイプ「faculty」に作成したので、以下のURLから、データの一覧を取得できます。 /jsonapi/node/faculty/ 以下は、linksフィールドを除く結果を表示しています。field_positionにタクソノミーのIDが含まれていますが、当該タクソノミーのラベル等はふくまれていません。 { "jsonapi": { "version": "1.0", "meta": { "links": { "self": { "href": "http://jsonapi.org/format/1.0/" } } } }, "data": [ { "type": "node--faculty", "id": "586ef1d9-b680-41f9-b54b-7ebcdc9d154f", "attributes": { "drupal_internal__nid": 5, "drupal_internal__vid": 13, "langcode": "ja", "revision_timestamp": "2023-06-08T01:01:43+00:00", "revision_log": null, "status": true, "title": "中村覚", "created": "2023-06-08T00:44:15+00:00", "changed": "2023-06-08T01:01:26+00:00", "promote": true, "sticky": false, "default_langcode": true, "revision_translation_affected": null, "content_translation_source": "und", "content_translation_outdated": false, "path": { "alias": null, "pid": null, "langcode": "ja" }, "body": null }, "relationships": { "node_type": { "data": { "type": "node_type--node_type", "id": "841962f7-91c8-47a1-b335-ea494efe467c", "meta": { "drupal_internal__target_id": "faculty" } } }, "revision_uid": { "data": { "type": "user--user", "id": "0b001e4d-ed29-4a53-960d-9cdcc3d3ad70", "meta": { "drupal_internal__target_id": 1 } } }, "uid": { "data": { "type": "user--user", "id": "0b001e4d-ed29-4a53-960d-9cdcc3d3ad70", "meta": { "drupal_internal__target_id": 1 } } }, "field_position": { "data": { "type": "taxonomy_term--position", "id": "6ce458e8-6d79-4ed1-9653-5ec178568b7a", "meta": { "drupal_internal__target_id": 5 } } } } } ] } includeを使う クエリに、?include=field_positionを追加します。結果、以下のように、includedフィールドが追加され、タクソノミータームのnameフィールドの値も得ることができました。 ...

Drupal Key authを用いたコンテンツの登録と多言語対応

Drupal Key authを用いたコンテンツの登録と多言語対応

概要 以下の記事で、Basic認証を使ったPythonによるコンテンツ登録を行いました。 今回は、以下の記事を参考に、API Key Authenticationを試しました。 https://designkojo.com/post-drupal-using-jsonapi-vuejs-front-end API Key Authentication 以下のモジュールを使用しました。 https://www.drupal.org/project/key_auth ユーザの編集画面に「Key authentication」というタブが表示され、APIキーを生成できました。 APIキーを使用する場合には、以下のようなプログラムで実行することができました。 import requests endpoint = 'http://{ipアドレス or ドメイン名}/jsonapi/node/article' key = '{APIキー}' headers = { 'Accept': 'application/vnd.api+json', 'Content-Type': 'application/vnd.api+json', "api-key": key } payload = { "data": { "type": "node--article", "attributes": { "title": "What's up from Python", "body": { "value": "Be water. My friends.", "format": "plain_text" } } } } r = requests.post(endpoint, headers=headers, json=payload) r.json() 多言語対応における注意点 注意点として、翻訳データの作成はできないようでした。 https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module/translations 作成済みの翻訳データの更新は可能ですが、翻訳データがないノードに対しては、以下のエラーが発生しました。 { "jsonapi": { "version": "1.0", "meta": { "links": { "self": { "href": "http://jsonapi.org/format/1.0/" } } } }, "errors": [ { "title": "Method Not Allowed", "status": "405", "detail": "The requested translation of the resource object does not exist, instead modify one of the translations that do exist: ja." } ] } この点について、既に対応策ができているかもしれません。引き続き調査したいと思います。 ...

Wagtailを試す

Wagtailを試す

概要 Wagtailを試してみましたので、躓いた点などの備忘録です。 基本的には、以下のチュートリアルを参考に進めました。 https://docs.wagtail.org/en/v5.0.1/getting_started/tutorial.html 検索機能 「はじめての記事」という日本語のタイトルを持つページを追加した際、以下ではヒットしませんでした。 http://localhost:8000/admin/pages/search/?q=はじめて 一方、以下ではヒットしました。日本語の部分一致検索はデフォルトではできないようでした。 http://localhost:8000/admin/pages/search/?q=はじめての記事 Wagtail API APIについては、以下に記載がありました。 https://docs.wagtail.org/en/v5.0.1/advanced_topics/api/index.html 上記のサイトを参考に、rest_frameworkも追加することで、以下のように結果を得ることができました。 ただし、localhost:8000で立ち上げているアプリに対して、得られる結果のホスト名がlocalhostになっていました。 この点については、以下の記事を参考に、管理画面から修正できました。 https://stackoverflow.com/questions/52540254/edit-approved-email-points-to-localhost 具体的には、以下の/admin/sites/のページで、ポート番号を変更しました。 ?searchパラメータ 先の検索機能と同様、日本語については完全一致が必要なようでした。 http://localhost:8000/api/v2/pages/?search=はじめての記事 Elasticsearch Elasticsearchとの連携を試みました。 https://docs.wagtail.org/en/v5.0.1/topics/search/backends.html 今回はawsのopensearchを試してみましたが、以下のようなエラーが出てしまいました。 elasticsearch.exceptions.UnsupportedProductError: The client noticed that the server is not Elasticsearch and we do not support this unknown product 以下で同様のissueが上がっていましたが、現時点ではまだ未対応のようでした。 https://github.com/wagtail/wagtail/issues/7920 まとめ 誤った内容も含まれているかもしれませんが、Wagtailの利用にあたり、参考になりましたら幸いです。

ArchivematicaでBrowseがうまくできない場合の原因と対応

ArchivematicaでBrowseがうまくできない場合の原因と対応

概要 ArchivematicaでBrowseを押してもフォルダやファイルが閲覧できない不具合に遭遇しました。この原因と対策について紹介します。 /transfer/ 事象 /administration/storage/ Error retrieving locations: is the storage server running? Please contact an administrator. 以下にアクセスすると、以下のjsonが得られました。 /transfer/locations/ { "message": "Error retrieving source directories", "status": "Failure" } 対応 以下にアクセスすると、Storage Seviceにアクセスできないことが示されていました。 /administration/general/ Storage Sevice URLを修正した結果、エラーが解消しました。 まとめ 同様の事象でお困りの方の参考になりましたら幸いです。

Amazon ECRのリポジトリを一括削除する

Amazon ECRのリポジトリを一括削除する

概要 Amazon ECRのリポジトリを一括削除する機会がありましたので、その備忘録です。ご利用される際は注意して実行してください。 リポジトリの一覧を作成 以下の記事を参考にしました。 https://qiita.com/fk_2000/items/bffd3b1ad6f3ab109766 以下を実行します。 aws ecr describe-repositories --output json | jq -re ".repositories[].repositoryName" > repository.list macの場合でjqコマンドがない場合、brew install jqなどを実行してください。 削除 以下を実行します。--forceを使って、imageがあっても削除を行います。 for X in `awk '{print $1}' repository.list` ; do aws ecr delete-repository --repository-name $X --force ; done まとめ ご利用される際は十分注意の上、実行してください。参考になりましたら幸いです。

Django REST framework JSON:API(DJA)に独自のモデルのビューをカスタマイズする

Django REST framework JSON:API(DJA)に独自のモデルのビューをカスタマイズする

概要 以下の記事で追加したモデルのビューをカスタマイズしてみます。 sort ordering_fieldsを追加してみます。 ... class UserInfoViewset(ModelViewSet): ordering_fields = ("user_name", ) # ここを追加 queryset = UserInfo.objects.all() serializer_class = UserInfoSerializer def get_object(self): entry_pk = self.kwargs.get("entry_pk", None) if entry_pk is not None: return Entry.objects.get(id=entry_pk).blog return super().get_object() ... 結果、「Filters」の表示で、user_nameのみが選択できるようになりました。 例えば、ageでソートを行うと、validation errorが返却されました。 フィルタ ... class UserInfoViewset(ModelViewSet): queryset = UserInfo.objects.all() serializer_class = UserInfoSerializer ordering_fields = ("user_name", ) # ここから下を追加 # override the default filter backends in order to test QueryParameterValidationFilter # without breaking older usage of non-standard query params like `page_size`. filter_backends = ( QueryParameterValidationFilter, OrderingFilter, DjangoFilterBackend, SearchFilter, ) rels = ( "exact", "iexact", "contains", "icontains", "gt", "gte", "lt", "lte", "in", "regex", "isnull", ) filterset_fields = { "id": ("exact", "in"), "user_name": rels } search_fields = ("user_name", ) ... ... 上記により、以下のようなフィルタが可能になりました。 ...

Django REST framework JSON:API(DJA)に独自のモデルを追加する

Django REST framework JSON:API(DJA)に独自のモデルを追加する

概要 以下の記事で、Django REST framework JSON:API(DJA)の基本的な操作方法を確認しました。 本記事では、DJAに独自のモデルを追加してみます。 参考 以下の記事を参考に、UserInfoモデルを追加してみます。 https://tech-blog.rakus.co.jp/entry/20220329/python 手順 モデルを定義 以下を追記します。 # ユーザ情報を格納する class UserInfo(BaseModel): user_name = models.CharField(verbose_name='ユーザ名',max_length=32) # ユーザ名 birth_day = models.DateField(verbose_name='生年月日') # 生年月日 age = models.PositiveSmallIntegerField(verbose_name='年齢',null=True,unique=False) # 年齢 created_at = models.DateTimeField(verbose_name='作成日時',auto_now_add=True) データベースを構築 以下を実行します。 % django-admin makemigrations --settings=example.settings Migrations for 'example': example/migrations/0013_userinfo.py - Create model UserInfo % django-admin migrate --settings=example.settings Operations to perform: Apply all migrations: auth, contenttypes, example, sessions, sites Running migrations: Applying example.0013_userinfo... OK 参考までに、以下のようなファイルが作成されます。 # Generated by Django 4.1.8 on 2023-06-04 17:35 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("example", "0012_author_full_name"), ] operations = [ migrations.CreateModel( name="UserInfo", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("modified_at", models.DateTimeField(auto_now=True)), ("user_name", models.CharField(max_length=32, verbose_name="ユーザ名")), ("birth_day", models.DateField(verbose_name="生年月日")), ("age", models.PositiveSmallIntegerField(null=True, verbose_name="年齢")), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="作成日時"), ), ], options={ "abstract": False, }, ), ] コンポーネントの作成 Serializer 以下を追記します。 ...

Django REST framework JSON:API(DJA)を試す

Django REST framework JSON:API(DJA)を試す

概要 Django REST framework JSON:API(DJA)を試す機会がありましたので、その備忘録です。 https://django-rest-framework-json-api.readthedocs.io/en/stable/index.html インストール 以下のページに記載があるexample appを起動します。 https://django-rest-framework-json-api.readthedocs.io/en/stable/getting-started.html git clone https://github.com/django-json-api/django-rest-framework-json-api.git cd django-rest-framework-json-api python3 -m venv env source env/bin/activate pip install -Ur requirements.txt django-admin migrate --settings=example.settings django-admin loaddata drf_example --settings=example.settings django-admin runserver --settings=example.settings 結果、以下の画面などが得られました。 http://localhost:8000 for the list of available collections (in a non-JSON:API format!), http://localhost:8000/swagger-ui/ for a Swagger user interface to the dynamic schema view, or http://localhost:8000/openapi for the schema view’s OpenAPI specification document. ...

さくらレンタルサーバにOmeka Sをインストールする際のimagemagickのパス指定

さくらレンタルサーバにOmeka Sをインストールする際のimagemagickのパス指定

概要 さくらレンタルサーバにOmeka Sをインストールする際、imagemagickのパス指定が必要でした。 以下のように、設定ファイルを修正します。 <?php return [ 'logger' => [ 'log' => false, 'priority' => \Laminas\Log\Logger::NOTICE, ], 'http_client' => [ 'sslcapath' => null, 'sslcafile' => null, ], 'cli' => [ 'phpcli_path' => null, ], 'thumbnails' => [ 'types' => [ 'large' => ['constraint' => 800], 'medium' => ['constraint' => 200], 'square' => ['constraint' => 200], ], 'thumbnailer_options' => [ 'imagemagick_dir' => '/usr/local/bin', # ここを修正 ], ], 'translator' => [ 'locale' => 'en_US', ], 'service_manager' => [ 'aliases' => [ 'Omeka\File\Store' => 'Omeka\File\Store\Local', 'Omeka\File\Thumbnailer' => 'Omeka\File\Thumbnailer\ImageMagick', ], ], ];

OpenAPIとAWS CDKを用いてREST APIを作成する(Opensearch接続・カスタムドメイン)

OpenAPIとAWS CDKを用いてREST APIを作成する(Opensearch接続・カスタムドメイン)

概要 OpenAPIとAWS CDKを用いてREST APIを作成する機会がありましたので、その備忘録です。以下の記事がとても参考になりました。 https://zenn.dev/taroman_zenn/articles/91879cec40627c 今回作成したものは以下のリポジトリで公開しています。 https://github.com/nakamura196/CdkOpenapi Opensearchとの接続 以下のLambdaで実装しています。 https://github.com/nakamura196/CdkOpenapi/blob/main/lambda/search.ts Lambdaに環境変数を渡す必要があり、lib以下のtsファイルで以下のように記述しました。 ... const searchFn = new NodejsFunction(this, "search", { entry: path.join(__dirname, "../lambda/search.ts"), runtime: Runtime.NODEJS_18_X, handler: "handler", environment: { ELASTIC_HOST: process.env.ELASTIC_HOST || "", ELASTIC_USERNAME: process.env.ELASTIC_USERNAME || "", ELASTIC_PASSWORD: process.env.ELASTIC_PASSWORD || "", ELASTIC_INDEX_NAME: process.env.ELASTIC_INDEX_NAME || "", }, }); ... カスタムドメイン 以下のファイルを参考にしてください。間違いなどがあるかもしれませんが、カスタムドメインの登録からAPI Gatewayへの設定も行ってみました。 https://github.com/nakamura196/CdkOpenapi/blob/main/lib/cdk-openapi-stack.ts まとめ 色々と中途半端なリポジトリではありますが、参考になる部分があれば幸いです。

StrapiとAmazon OpenSearchを連携する

StrapiとAmazon OpenSearchを連携する

概要 StrapiとElasticsearchとの連携にあたり、以下の記事が参考になりました。 https://punits.dev/blog/integrating-elasticsearch-with-strapi/ ソースコードも公開されています。 https://github.com/geeky-biz/strapi-integrate-elasticsearch ここでは、上記の記事を参考にして、Amazon OpenSearchと連携するなど、一部カスタマイズした内容についてメモします。 カスタマイズしたソースコードは以下です。 https://github.com/nakamura196/strapi-integrate-opensearch 修正点 以下について、記事ではindexing_typeとなっていますが、indexing_request_typeとする必要がありました。 https://github.com/nakamura196/strapi-integrate-opensearch/blob/006c533d4d7882fc9779552db31a7b0e2ada5e57/elastic/cron-search-indexing.js#L16 またElasticsearchではなく、Amazon OpenSearchを使用するにあたり、以下のライブラリをインストールする必要があります。 npm install @opensearch-project/opensearch npm install @aws-sdk/credential-providers 認証の方法はいくつかあると思いますが、ここでは以下のように変更しました。 https://github.com/nakamura196/strapi-integrate-opensearch/blob/006c533d4d7882fc9779552db31a7b0e2ada5e57/elastic/elasticClient.js#L1C1-L30 インデックスへの登録および削除の処理についても、以下のように修正する必要がありました。 https://github.com/nakamura196/strapi-integrate-opensearch/blob/006c533d4d7882fc9779552db31a7b0e2ada5e57/elastic/elasticClient.js#L39-L44 上記のような修正の結果、StrapiとAmazon OpenSearchを接続させることができました。 elastic/elasticClient.js および src/api/search/controllers/search.js を変更することで、APIの出力結果もカスタマイズできました。これにより、ElasticsearchのAggregationsを使用することができるようになりました。(私の調査不足により、Strapi単体でも実現できるかもしれません。) まとめ StrapiとAmazon OpenSearchの連携にあたり、参考になりましたら幸いです。

StrapiのREST APIの設定など

StrapiのREST APIの設定など

概要 StrapiのREST APIの一部設定に関する備忘録です。 検索結果の上限を変更する 以下に記載がありました。 https://docs.strapi.io/dev-docs/api/rest/sort-pagination#pagination 具体的には、以下です。 The default and maximum values for pagination[limit] can be configured in the ./config/api.js file with the api.rest.defaultLimit and api.rest.maxLimit keys. module.exports = { rest: { defaultLimit: 25, maxLimit: 1000, // 100, withCount: true, }, }; STATEがDraftのものも含めて取得する デフォルトではSTATEがDraftのものは取得できませんでした。以下の記事が参考になりました。 https://forum.strapi.io/t/can-i-expose-an-collection-entry-in-draft-mode-in-the-api-for-a-certain-user/895/4 具体的には以下のようなクエリパラメータを追加することで取得できました。 ?publicationState=preview&filters[publishedAt][$null]=true まとめ Strapiの利用にあたり、参考になりましたら幸いです。

IIIFマニフェストファイルからPDFファイルを作成する

IIIFマニフェストファイルからPDFファイルを作成する

概要 IIIFマニフェストファイルからPDFファイルを作成する機会がありました。このソリューションとして、以下のリポジトリが見つかりましたが、うまく動かすことができませんでした。 https://github.com/jbaiter/pdiiif そこで、上記リポジトリはJavaScriptを使用していますが、今回はPythonを用いた変換ツールを作成しました。 使い方 以下のノートブックからお試しいただけます。 https://colab.research.google.com/github/nakamura196/ndl_ocr/blob/main/iiif2pdf.ipynb 初回インストール時に、img2pdfをインストールしますが、PILのバージョンの関係で、「RESTART RUNTIME」ボタンが表示されますので、クリックの上、再度同じセルを実行してください。 オプションとして、「IIIFマニフェストURLの指定」「画像が格納されているフォルダのパスの指定」「ローカルに存在するIIIFマニフェストファイルのパスの指定」の3種類を用意しています。 注意点として、2023年5月末時点では、IIIF Presentation API v2にのみ対応しています。v3へは今後の対応を検討しています。 まとめ IIIFからのPDFファイルの作成にあたり、参考になりましたら幸いです。

GitHubのGUIを使ったファイルアップロードおよびファイル更新の方法について

GitHubのGUIを使ったファイルアップロードおよびファイル更新の方法について

概要 GitHubファイルアップロードおよびファイル更新の方法について共有する機会がありました。 ログイン アカウントの新規作成を含めて、以下の記事などを参考にしてください。 https://reffect.co.jp/html/create_github_account_first_time ファイルアップロード リポジトリにアクセスします。 Add fileボタンをクリックして、Upload filesをクリックします。 choose your filesをクリックして、ローカルからファイルをアップロードして、Commit changesを押します。 ファイルがアップロードされます。同じ名前のファイルが既に存在する場合には、上書き更新されます。 ファイルの更新 更新対象のファイル名をクリックします。 鉛筆アイコンをクリックします。 テキストを修正し、Commit changesボタンを押します。 再度、Commit changesボタンを押します。 ファイルが更新されます。 まとめ GitHubのGUIの使い方の参考になりましたら幸いです。

Omeka SのImage Serverの設定について

Omeka SのImage Serverの設定について

概要 Omeka SのImage Serverは、IIIF Image APIに対応した画像配信を可能とするモジュールです。 https://omeka.org/s/modules/ImageServer/ IIIF Serverモジュールと組み合わせて使用することにより、IIIFマニフェストによる配信も可能になります。 Image Serverモジュールでは、タイル画像の作成方法を含めて、さまざまな設定が可能です。本記事では、これらの設定について、調査結果を共有します。 実験環境 今回は、Amazon LightsailのLAMPインスタンスを使用します。2 GB RAM, 1 vCPUの比較的低スペックな環境を用います。 Amazon Lightsailを用いたOmeka Sの構築方法は、以下などを参考にしてください。 また、今回は以下のスクリプトを利用しました。今回検証する「Image Server」モジュールの使用に必要な、関連モジュールを合わせてインストールします。 https://github.com/nakamura196/omeka_aws/blob/main/2023-05-25.sh タイル画像の作成に関する設定 ImageServerモジュールのインストール後、以下にアクセスすると、ImageServerの設定画面にアクセスできます。 /admin/module/configure?id=ImageServer 以下の画面の「Tiling service」の箇所で設定を行うことができます。 Image processor項目 本モジュールの説明ページでは、vipsのインストールが推奨されていました。上記のスクリプトでも、以下によって、vipsをインストールしています。 sudo apt install --no-install-recommends libvips-tools そのため、上記設定画面の「Image processor」項目の初期値に基づき、タイル画像の作成には、以後vipsが使用されます。 Tilling type項目 この項目では、「Deep Zoom Image」「Zoomify」「Jpeg 2000」「Tiled tiff」の4つの項目を選択することができます。これらについて、公式サイトでは以下のように記載されています。 Four format are proposed to create tiles: DeepZoom, Zoomify, Jpeg 2000 and pyramidal Tiff. The recommended format is DeepZoom. For Jpeg 2000 and pyramidal tiff, some other tools may be required. ...

AWS CDK x CloudFront x S3 x Basic認証 x index.html対応 x 独自ドメイン

AWS CDK x CloudFront x S3 x Basic認証 x index.html対応 x 独自ドメイン

概要 AWS CDKを用いて、CloudFront + S3による静的サイトの作成を行いました。合わせて、CloudFront Functionを用いて、Basic認証とURLにファイル名や拡張子を含まないリクエストにindex.htmlを追加する処理を加えています。さらに、独自ドメインの追加も行いましたので、その備忘録です。 色々と不完全ですが、以下のリポジトリでソースコードを公開しています。 https://github.com/nakamura196/staticBasic 以下のような.envファイルを用意してcdk deployを実行する想定です。 CERT_ARN=arn:aws:acm:xxxx RECORD_NAME=aaa.bbb.com BUCKET_NAME=aaa.bbb.com REGION=us-east-1 ACCOUNT=yyyy DOMAIN_NAME=bbb.com それぞれの説明は以下のとおりです。 項目 説明 例 CERT_ARN 証明書のARN arn:aws:acm:xxxx RECORD_NAME 設定したいドメイン名 aaa.bbb.com BUCKET_NAME ファイルを格納するS3バケット名 aaa.bbb.com REGION リージョン名 us-east-1 ACCOUNT AWSのアカウント名(12 桁の数値 ) 123456789012 DOMAIN_NAME ホストゾーン名 bbb.com Stack 以下のStackを作成しました。 import { Stack, StackProps, RemovalPolicy, aws_cloudfront, aws_cloudfront_origins, aws_iam, } from "aws-cdk-lib"; import { Construct } from "constructs"; import * as s3 from "aws-cdk-lib/aws-s3"; import * as iam from "aws-cdk-lib/aws-iam"; import * as cloudfront from "aws-cdk-lib/aws-cloudfront"; import * as route53 from "aws-cdk-lib/aws-route53"; import * as targets from "aws-cdk-lib/aws-route53-targets"; import { Certificate } from "aws-cdk-lib/aws-certificatemanager"; import * as dotenv from "dotenv"; dotenv.config(); export class StaticBasicStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const recordName = process.env.RECORD_NAME || ""; const domainName = process.env.DOMAIN_NAME || ""; const bucketName = process.env.BUCKET_NAME || ""; const cert = process.env.CERT_ARN || ""; // ホストゾーンIDを取得 const hostedZoneId = route53.HostedZone.fromLookup(this, "HostedZoneId", { domainName, }); // S3バケットを作成 const websiteBucket = new s3.Bucket(this, "WebsiteBucket", { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, bucketName, }); // CloudFront用のOrigin Access Identityを作成 const originAccessIdentity = new cloudfront.OriginAccessIdentity( this, "OriginAccessIdentity", { comment: `${bucketName}-identity`, } ); // S3バケットポリシーを設定 const webSiteBucketPolicyStatement = new iam.PolicyStatement({ actions: ["s3:GetObject"], effect: iam.Effect.ALLOW, principals: [ new aws_iam.CanonicalUserPrincipal( originAccessIdentity.cloudFrontOriginAccessIdentityS3CanonicalUserId ), ], resources: [`${websiteBucket.bucketArn}/*`], }); websiteBucket.addToResourcePolicy(webSiteBucketPolicyStatement); // CloudFront Functionの設定 const cfFunction = new aws_cloudfront.Function(this, "CloudFrontFunction", { code: aws_cloudfront.FunctionCode.fromFile({ filePath: "assets/redirect.js", }), }); // 証明書を取得 const certificate = Certificate.fromCertificateArn( this, "Certificate", cert ); // CloudFrontの設定 const distribution = new aws_cloudfront.Distribution(this, "distribution", { domainNames: [recordName ], certificate, comment: `${bucketName}-cloudfront`, defaultRootObject: "index.html", defaultBehavior: { allowedMethods: aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD, cachedMethods: aws_cloudfront.CachedMethods.CACHE_GET_HEAD, cachePolicy: aws_cloudfront.CachePolicy.CACHING_OPTIMIZED, viewerProtocolPolicy: aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, origin: new aws_cloudfront_origins.S3Origin(websiteBucket, { originAccessIdentity, }), functionAssociations: [ { function: cfFunction, eventType: aws_cloudfront.FunctionEventType.VIEWER_REQUEST, }, ], }, priceClass: aws_cloudfront.PriceClass.PRICE_CLASS_ALL, }); // Route53レコード設定 new route53.ARecord(this, "Route53RecordSet", { // ドメイン指定 recordName, // ホストゾーンID指定 zone: hostedZoneId, // エイリアスターゲット設定 target: route53.RecordTarget.fromAlias( new targets.CloudFrontTarget(distribution) ), }); } } まとめ 色々と考慮不足の点があるかと思いますが、AWS CDKの便利さを体感することができました。他の方の参考になる部分がありましたら幸いです。 ...

Omeka SでIIIF画像をメディアとして登録する方法

Omeka SでIIIF画像をメディアとして登録する方法

概要 Omeka SでIIIF画像をメディアとして登録する方法を紹介します。外部のIIIF画像サーバ等で公開されている画像をOmeka Sに登録します。 公式のマニュアルでは、以下に記載があります。 https://omeka.org/s/docs/user-manual/content/items/#media 具体的な方法 アイテムの編集画面のメディアの登録画面において、以下のような値を入力します。 /info.jsonで終わるURLを入力する必要があります。以下、国立国会図書館の校異源氏物語を例としています。 https://dl.ndl.go.jp/api/iiif/3437686/R0000001/info.json 結果、以下のように、OmekaからIIIF画像を利用できるようになります。 補足 Omeka S v4から外部のIIIFマニフェストファイルの取り込み機能も提供されています。 https://dl.ndl.go.jp/api/iiif/3437686/manifest.json まとめ IIIF画像の登録にあたり、末尾に/info.jsonを追加する点がポイントです。OmekaでIIIFを利用する際の参考になりましたら幸いです。

GoogleドライブとGoogle Apps Scriptを用いて匿名のファイルアップローダを作成する

GoogleドライブとGoogle Apps Scriptを用いて匿名のファイルアップローダを作成する

概要 GoogleドライブとGoogle Apps Scriptを用いて匿名のファイルアップローダを作成する機会がありましたので、その備忘録です。 以下の記事などを参考にさせていただきました。 https://qiita.com/v2okimochi/items/06ed1ce7c56a877a1e10 ウェブアプリの作成 まず、以下のURLから、Apps Scriptにアクセスします。 https://script.google.com/ 「新しいプロジェクト」をクリック。 以下のような画面が表示されます。 以下のコードをコピペします。2行目の<Google Driveのアップロード用フォルダのID>について、事前にGoogleドライブでアップロード用のフォルダを作成しておき、そのIDを取得しておいてください。 // 定数: Google Driveのアップロード用フォルダのID const FOLDER_ID = '<Google Driveのアップロード用フォルダのID>'; // doGet関数: index.htmlファイルを表示する function doGet() { return HtmlService.createHtmlOutputFromFile('index'); } // processForm関数: フォームオブジェクトを受け取り、Google Driveにファイルをアップロードする function processForm(formObject) { // フォームからファイルデータを取得 var formBlob = formObject.myFile; // アップロード用フォルダを取得 var uploadFolder = DriveApp.getFolderById(FOLDER_ID); // 現在の日時をフォルダ名に使用 var today = new Date(); const folderName = today.toString(); // アップロード用フォルダ内に新しいフォルダを作成 const customFolder = uploadFolder.createFolder(folderName); // 新しいフォルダ内にファイルをアップロード customFolder.createFile(formBlob); // 新しいフォルダ名を戻り値として返す return folderName; } 次に、画面左上の「+」ボタンを押して、HTMLを選択します。 ファイル名に「index」を与えます。 以下のコードをコピペします。 <!DOCTYPE html> <html> <head> <base target="_top"> <script> // フォームのデフォルトの送信動作を無効にする function preventFormSubmit() { var forms = document.querySelectorAll('form'); for (var i = 0; i < forms.length; i++) { forms[i].addEventListener('submit', function(event) { event.preventDefault(); }); } } window.addEventListener('load', preventFormSubmit); // アップロードボタンを最初は無効にする document.addEventListener("DOMContentLoaded", function () { document.getElementById("upload").disabled = true; }, false); // 制限サイズ以内のファイルが選択されたらアップロードボタンを有効にする function changeSubmitButton() { const len = document.getElementById("file").files.length; const size = document.getElementById("file").files[0].size; const maxSize = 1024 * 1024 * 10; // 10MB const uploadButton = document.getElementById("upload"); if (len > 0 && size < maxSize) { uploadButton.disabled = false; } else { uploadButton.disabled = true; } } // アップロードボタンが押されたらファイルをアップロード function handleFormSubmit(formObject) { document.getElementById("upload").disabled = true; const div = document.getElementById('progress'); div.innerHTML = 'アップロード中...'; // アップロード成功した場合はupdateView()実行 google.script.run.withSuccessHandler(updateView).processForm(formObject); } // アップロード完了画面に変える(動的) function updateView(id) { var div = document.getElementById('myform'); div.innerHTML = `<div>アップロードが完了しました。</div>`; } </script> </head> <body> <div id="myform" style="text-align:center;"> ファイルを選択してからアップロードしてください(10MBまで)<br><br> <form onsubmit="handleFormSubmit(this)"> <input id="file" name="myFile" type="file" onchange="changeSubmitButton()" /> <input id="upload" type="submit" value="アップロード" /> </form> <div id="progress"></div> </div> </body> </html> デプロイ 画面右上の「デプロイ」ボタンをクリック後、「新しいデプロイ」をクリックします。 ...

Google スプレッドシートの更新をGitHubに通知する

Google スプレッドシートの更新をGitHubに通知する

概要 Google Apps Scriptを用いて、Googleスプレッドシートが更新された際、GitHubに通知を送る方法を調べました。合わせて、StrapiやContentfulからGitHubに通知を送る方法も調べたので、備忘録として記録します。 Google Apps Script 以下のようなスクリプトを用意することで、スプレッドシートの更新をGitHubに通知できました。 const token = "ghp_xxx" const owner = "yyy" const repo = "zzz" const event_type = "aaa" function postSheetChange() { const headers = { "Accept": "application/vnd.github+json", "Authorization": `Bearer ${token}`, "Content-Type": "application/json" } var payload = JSON.stringify({ event_type }); var requestOptions = { method: 'POST', headers, payload, }; UrlFetchApp.fetch(`https://api.github.com/repos/${owner}/${repo}/dispatches`, requestOptions) } トリガーの設定方法などは以下の記事が参考になりました。 https://businesschatmaster.com/slack/spreadsheet-change-notification なお、Googleスプレッドシートの更新の都度、GitHubへの通知が行くので、GitHub Actionsのほうで、以下のようなconcurrencyを設定しておくほうがよさそうです。 または、トリガーを特定のセル(列)が更新された時だけ、などにすることも考えられます。 name: Build Test concurrency: cancel-in-progress: true on: repository_dispatch: types: - update_content jobs: build: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v3 ... Strapi Strapiのwebhookでは、bodyをカスタマイズできないため、以下のようなプロキシサーバを立てる必要があるようです。 ...