Teruhiro Komaki's Diary (Archive)

author
Teruhiro Komaki komaki.dev
2024年 05月 30日 木曜日
09時00分

Google Search Consoleで「重大なスパムの問題があります」と通知がきましたが、無事に再審査リクエストが承認されました

この日記diary.komaki.devの前にblog.teruhiro.jpというドメインで、ブログを書いていました。

以前blog.teruhiro.jpで運用していた際に、#Google Search Consoleから「重大なスパムの問題がある」という通知が届きました。

そのような通知を受け取る背景などは、以下のリンク先にまとめています。

タイトルの通りですが、ブログを teruhiro.jp/blog/yyyy/mm/title から blog.teruhiro.jp/yyyy/mm/title に移行したら、Google Search Consoleで「悪質なスパム」と判定されてしまいました。
Google Search Consoleで 2024/02/07 に「teruhiro.jp に重大なスパムの問題があります」という通知が届きました。

ブログをサブドメインに移行したら、Google Search Consoleで「悪質なスパム」と判定されて困っている話 | Teruhiro Komaki

2回目の再審査リクエスト

この通知を2024年02月07日に受け取り、翌日の2024年02月08日には理由や背景を説明して再審査リクエストを申請をしました。

しかし、1ヶ月ほど経過しても、なんの連絡もないので、改めて以下の内容で2024年04月09日に、2回目の再審査のリクエストをしました。

2024年02月07日に、重大なスパムの問題として、通知が届きました。
翌日の、2024年02月08日に、以下の内容を返信をしましたが、その後なんの通知や連絡もなく、2ヶ月ほど経過している状況です。
ヘルプには「連絡を待つように。」との記載がありましたが、なんの連絡もないため、改めてご連絡いたしました。
-----
今まで、以下のURLのように /blog/ 配下に記事を作成していました。
https://teruhiro.jp/blog/yyyy/mm/slug(例:https://teruhiro.jp/blog/2024/01/google-discover-line-investment-scam) 今回、blogをサブドメインに移行したいと思い、以下のURLに変更しました。
https://blog.teruhiro.jp/yyyy/mm/slug(例:https://blog.teruhiro.jp/2024/01/google-discover-line-investment-scam) 移行作業をしている間、全く同じブログが上記の2通り違うURLで存在していました。
https://teruhiro.jp/sitemap.xmlとhttps://blog.teruhiro.jp/sitemap.xmlも上記と同じURLを含む状況でした。
移行作業が終わりましたので、変更前の以下のURLにアクセスがあった場合、ステータスコード301を返すように修正しました。
https://teruhiro.jp/blog/2024/01/google-discover-line-investment-scam そのため、現在は重複したコンテンツはない状態になっていると思います。
ご確認お願いします。

再審査リクエストの承認結果

その後も、しばらくなんの連絡もなかったので、teruhiro.jpというドメインを使うのを辞めてkomaki.devというドメインを使うことにしようと思い、この日記サイトを作りdiary.komaki.devというドメインで公開しました。

さすがに、もうダメかな…と思っていたら、2024年05月15日に、#Google Search Consoleから以下の通知が届きました。

お送りいただいた再審査リクエストを承認し、貴サイトに対する手動による対策を解除しました。
ただし、今回の手動による対策の解除はサイトの掲載順位が上昇することを保証するものではありませんのでご了承ください。

再審査リクエストの承認通知
再審査リクエストの承認通知

複数のブログをまとめたい

昔に作ったブログも残っているため、現在では複数のブログを運営している状態です。これらのブログも、どこかのタイミングで、1箇所にまとめたいと思います。

ちなみに、Cloudflareの_redirectsを使うことで、容易にリダイレクトの設定ができるので、リダイレクト方法を模索している方は、ドキュメントをみて頂くと良いと思います。

To use redirects on Cloudflare Pages, declare your redirects in a plain text file called _redirects without a file extension, in the output folder of your project.
The build output folder is project-specific, so the _redirects file should not always be in the root directory of the repository. Changes to redirects will be updated to your website at build time so make sure you commit and push the file to trigger a new build each time you update redirects.

Google翻訳
Cloudflareページでリダイレクトを使用するには、プロジェクトの出力フォルダーにあるファイル拡張子のない_redirectsというプレーンテキストファイルでリダイレクトを宣言します。
ビルド出力フォルダーはプロジェクト固有であるため、_redirects ファイルは常にリポジトリのルート ディレクトリにある必要はありません。
リダイレクトへの変更はビルド時に Web サイトに更新されるため、リダイレクトを更新するたびにファイルをコミットしてプッシュして新しいビルドをトリガーするようにしてください。

Redirects · Cloudflare Pages docs
author
Teruhiro Komaki komaki.dev
2024年 04月 05日 金曜日
08時00分

Eleventy(11ty)のCanaryバージョンのインストール

Eleventy(11ty)公式のImageプラグインを確認したところ、非常に便利そうなので、この日記サイトに導入をしようと思い、早速インストールしました。

npm i -D @11ty/eleventy-img

.eleventy.jsを編集し、buildしたら、エラーが表示されました。

ドキュメントをみると、Canaryバージョンのインストールが必要とのことで、v3.0.0-alpha.5をインストールしました。

npm i @11ty/eleventy@canary --save-exact -D

Calling all courageous canary testers for Eleventy v3.0 — Eleventy

公式のImageプラグインについて

Low level utility to perform build-time image transformations for both vector and raster images.
Output multiple sizes, save multiple formats, cache remote images locally.
Uses the sharp image processor.

Google翻訳
ベクター画像とラスター画像の両方に対してビルド時の画像変換を実行する低レベルのユーティリティ。
複数のサイズを出力し、複数の形式で保存し、リモート画像をローカルにキャッシュします。
シャープ製画像処理エンジンを採用。

Image — Eleventy

どのように便利になったのか?

導入前

<figure>
  <img src="{{ page.url }}2024-04-05_03-51-38.png" alt="Snagit 2024 の新機能" width="2400" height="3138">
  <figcaption>Snagit 2024 の新機能</figcaption>
</figure>

導入後

<figure>
  {% image page , "2024-04-05_03-51-38.png", "Snagit 2024 の新機能" %}
  <figcaption>Snagit 2024 の新機能</figcaption>
</figure>

build後のHTML

<figure>
  <picture>
    <source type="image/webp" srcset="/2024/04/04/1500/2024-04-05_03-51-38-400w.webp 400w, /2024/04/04/1500/2024-04-05_03-51-38-800w.webp 800w, /2024/04/04/1500/2024-04-05_03-51-38-1280w.webp 1280w, /2024/04/04/1500/2024-04-05_03-51-38-1500w.webp 1500w, /2024/04/04/1500/2024-04-05_03-51-38-2000w.webp 2000w, /2024/04/04/1500/2024-04-05_03-51-38-2400w.webp 2400w" sizes="100vw">
    <img alt="Snagit 2024 の新機能" loading="lazy" decoding="async" src="/2024/04/04/1500/2024-04-05_03-51-38-400w.png" width="2400" height="3138" srcset="/2024/04/04/1500/2024-04-05_03-51-38-400w.png 400w, /2024/04/04/1500/2024-04-05_03-51-38-800w.png 800w, /2024/04/04/1500/2024-04-05_03-51-38-1280w.png 1280w, /2024/04/04/1500/2024-04-05_03-51-38-1500w.png 1500w, /2024/04/04/1500/2024-04-05_03-51-38-2000w.png 2000w, /2024/04/04/1500/2024-04-05_03-51-38-2400w.png 2400w" sizes="100vw">
  </picture>
  <figcaption>Snagit 2024 の新機能</figcaption>
</figure>

Shortcodeについては、別で投稿したいと思います。

author
Teruhiro Komaki komaki.dev
2024年 04月 04日 木曜日
01時00分

Snagit 2024 (24.2.3)にアップデートしたらクラッシュするようになってしまった

以下のスクリーンショットのように、#Snagitが起動できなくなってしまった。

Snagit クラッシュレポート
Snagit クラッシュレポート
Snagit 異常終了
Snagit 異常終了

私は、スクリーンショットを管理したり、編集するツールとして#Snagitを使っています。

以下の通り、2024-04-02にリリースされたバージョンの24.2.3をインストールしました。

Snagit (Mac)
Version: 24.2.3
Released: 2024 Apr 02
Operating System Compatibility: Check Now
File Size: 261.74 MB

ダウンロードリンクを確認すると、24.2.3の場合は、以下のようなので、一つ前のバージョンを指定してダウンロードできるか試みます。

ダウンロードリンク

https://download.techsmith.com/snagitmac/releases/2423/Snagit.dmg

一つ前のバージョンを確認しようと、サポートを見たらバージョン履歴というページがありましが、2024年分のバージョンについては、反映されていないようです。

Snagit(Mac)バージョン履歴 – TechSmith サポート

Snagit バージョン履歴
Snagit バージョン履歴

ひとつ前のバージョンが何だったのか、覚えていないため、分かりませんが、2423から推測し、2422を指定して、ダウンロードしてみることにしました。

無事ダウンロードでき、一つ前のバージョン24.2.2でアプリを置き換えました。

しばらく様子をみて、アップデートを試みたいと思います。

author
Teruhiro Komaki komaki.dev
2024年 04月 03日 水曜日
16時00分

AWS Lambdaの`event`を確認

API GatewayのGETメソッドでAWS Lambda関数を統合している場合に、eventで受け取る値を確認しました。

以下のような#AWS Lambdaを実行しました。

export const handler = async (event) => {

  console.log(JSON.stringify(event));

  const response = {
    statusCode: 200,
    body: JSON.stringify('Hello from Lambda!'),
  };
  return response;
};

API Gatewayからテスト

実行結果

API Gatewayからテスト
API Gatewayからテスト
{
  "resource": "/",
  "path": "/",
  "httpMethod": "GET",
  "headers": {
    "header2": "value2",
    "header1": "value1"
  },
  "multiValueHeaders": {
    "header2": [
      "value2"
    ],
    "header1": [
      "value1"
    ]
  },
  "queryStringParameters": {
    "param1": "value1",
    "param2": "value2"
  },
  "multiValueQueryStringParameters": {
    "param1": [
      "value1"
    ],
    "param2": [
      "value2"
    ]
  },
  "pathParameters": null,
  "stageVariables": null,
  "requestContext": {
    "resourceId": "zbkgq2150i",
    "resourcePath": "/",
    "httpMethod": "GET",
    "extendedRequestId": "Vo6HgFCYoAMFchg=",
    "requestTime": "03/Apr/2024:07:50:07 +0000",
    "path": "/",
    "accountId": "123456789123",
    "protocol": "HTTP/1.1",
    "stage": "test-invoke-stage",
    "domainPrefix": "testPrefix",
    "requestTimeEpoch": 1712130607770,
    "requestId": "05f4bb29-e600-4968-aeb8-12e062caa698",
    "identity": {
      "cognitoIdentityPoolId": null,
      "cognitoIdentityId": null,
      "apiKey": "test-invoke-api-key",
      "principalOrgId": null,
      "cognitoAuthenticationType": null,
      "userArn": "arn:aws:iam::123456789123:user/xxx-xxx",
      "apiKeyId": "test-invoke-api-key-id",
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
      "accountId": "123456789123",
      "caller": "AIDAQGIBVCFDQW6HXLIGK",
      "sourceIp": "test-invoke-source-ip",
      "accessKey": "ASIAQGIBVCFDRFRRWRO5",
      "cognitoAuthenticationProvider": null,
      "user": "AIDAQGIBVCFDQW6HXLIGK"
    },
    "domainName": "testPrefix.testDomainName",
    "apiId": "vjs9lsoti0"
  },
  "body": null,
  "isBase64Encoded": false
}

自分用のメモでした。

author
Teruhiro Komaki komaki.dev
2024年 04月 03日 水曜日
14時00分

AWS Lambdaの`process.env`を確認

以下のような#AWS Lambdaを実行しました。

export const handler = async (event) => {

  console.log(JSON.stringify(process.env));

  const response = {
    statusCode: 200,
    body: JSON.stringify('Hello from Lambda!'),
  };
  return response;
};

実行結果

{
  "AWS_LAMBDA_FUNCTION_VERSION": "$LATEST",
  "AWS_EXECUTION_ENV": "AWS_Lambda_nodejs20.x",
  "AWS_DEFAULT_REGION": "us-east-1",
  "AWS_LAMBDA_LOG_STREAM_NAME": "2024/04/03/[$LATEST]066a5738b9ba4cc09d6e0c0a70f7e466",
  "AWS_REGION": "us-east-1",
  "PWD": "/var/task",
  "_HANDLER": "index.handler",
  "TZ": ":UTC",
  "LAMBDA_TASK_ROOT": "/var/task",
  "LANG": "en_US.UTF-8",
  "AWS_SECRET_ACCESS_KEY": "DZQ9rrfVi0oz9Nn4/iyZr81r4P3U9jH6K9bVt75q",
  "AWS_LAMBDA_LOG_GROUP_NAME": "/aws/lambda/tmp-20240403_143239",
  "AWS_LAMBDA_RUNTIME_API": "127.0.0.1:9001",
  "AWS_LAMBDA_FUNCTION_MEMORY_SIZE": "128",
  "LAMBDA_RUNTIME_DIR": "/var/runtime",
  "_AWS_XRAY_DAEMON_ADDRESS": "169.254.79.129",
  "AWS_XRAY_DAEMON_ADDRESS": "169.254.79.129:2000",
  "SHLVL": "0",
  "AWS_ACCESS_KEY_ID": "ASIAQGIBVCFD7EZXTHMZ",
  "LD_LIBRARY_PATH": "/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib",
  "NODE_PATH": "/opt/nodejs/node20/node_modules:/opt/nodejs/node_modules:/var/runtime/node_modules:/var/runtime:/var/task",
  "AWS_LAMBDA_FUNCTION_NAME": "tmp-20240403_143239",
  "PATH": "/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin",
  "AWS_LAMBDA_INITIALIZATION_TYPE": "on-demand",
  "AWS_SESSION_TOKEN": "IQoJb3JpZ2luX2VjEEYaCXVzLWVhc3QtMSJHMEUCIHaOAVEBa8742bDc8NqIX1ecHNJi3rTajSttzvVk4XvaAiEAvFoY1nR+GX3DuMgtRYEZ0QRyLbgG8lRCo/pvMbz2WMYq+gIIbxADGgwwMTM0MjUyNTA2MzEiDFYK7zAtz02PVUTQ5yrXAjp2DzTSnmE7mljD2vvM92SKKAVkGz4UtWfCsCbLaGIwPGsf/WHPTcMV/vJRF1VbOiMlEtbYOh4FVfn7Vhr/+GoiV9lIsOf7aILN7R/bvHVSTjZ0TdoraYLr6b5unKImPWMwwvTnbxQ4HyNitcc/DzfyGxbbtRsyAru8OUZJAc4knxhX7+M7yOe4Ula8678RoOD6RTQ4yyeH6zA6E/OrgReMJBG+hfthSRWeP1UACesfjiUOZMwvvSLUZHwuESgpbsvq8sBsv+UQ+Lc0tc8l9g6+bOijtQgDwITEerHmxQCf5qRTEirQbitkFxmMlOjyKGtZ8THbztV4/gc9XOyhXOrw0LpX6MywCr+xHhC3PD7ValT882pNRQ8I+5twQpfjVzVZybHlvU7VlL9B96DsmQfbzxEKz883MNb9yjqrNX9tJ5k0ofCfKfVPQ1QAR47phy5wAvDXqMYw3NSzsAY6ngGoft73ap2zAoTMCEvIxGP6eLyZQdcBhzKrSzFURSU9o3oZ5qTrP6huGuNKC+2P49rvsqp1Djqu3WtniNSXpBOFPWz4FnleByQOz6+x64SuXqKKtqTG2n1KIBZ9Yu5uUHSC8eaK4YyGY00AqRex/kpA5yN7wSGghvAOyPXlUo7pyZQ7uWNHiybypN9+zpnjY2Gl+aSCLPwEZThzUV4n9g==",
  "AWS_XRAY_CONTEXT_MISSING": "LOG_ERROR",
  "_AWS_XRAY_DAEMON_PORT": "2000",
  "_X_AMZN_TRACE_ID": "Root=1-660cea5c-31b5dc97257d60084e97c6d4;Parent=1dd3fa1526589d56;Sampled=0;Lineage=bf0b63a4:0"
}

自分用のメモでした。

author
Teruhiro Komaki komaki.dev
2024年 04月 03日 水曜日
01時00分

Webstormの文法(Grammar)の間違い

#Webstormが好きで、コードを書くときはもちろん、マークダウンを始め色々なケースで便利に使っています。

ふとしたときに、スクリーンショットのように、テキスト(確認することができます。)に下線(ハイライト)が表示されていることに気づきました。

文法 | WebStorm ドキュメント

テキストに下線(ハイライト)
テキストに下線(ハイライト)

選択肢のignoreを選択すると、次回から無視してくれるようになります。

なにかの拍子で、ignoreを選択してしまったのですが、その後、どこでignoreしたリストを管理しているのか気になったので、確認しました。

文法間違いをignoreしたリスト(Exceptions)

Settings->Editor->Natural Languages->Grammar and Style

文法間違いをignoreしたリスト(Exceptions)
文法間違いをignoreしたリスト(Exceptions)

万が一、間違ってignoreした場合もExceptionsのリストから削除することで、改めて文法間違いの設定を元に戻せます。

Cloudflare D1 Database GA: Production Ready

#CloudflareのD1が、GAになりました。D1をあまり素振りできていませんが、隙間を見つけて、素振りしたいと思います。

author
Teruhiro Komaki komaki.dev
2024年 04月 02日 火曜日
01時00分

<code>タグにコードを貼り付けるためのミニアプリを作りました

個人の日記や、会社のブログを投稿するうえで、<code>タグにHTMLや、様々なコードを貼り付けるケースが多く、適切にエスケープする必要があります。

今までは、以下のサイトのように、文字列をエスケープしてくれるサイトを使っていました。

マークアップとして間違って評価されないように、文字列をエスケープしてくれるサイトです。

Free Online HTML Escape / Unescape Tool - FreeFormatter.com

HTMLのエスケープをしてくれるサイト
エスケープをしてくれるサイト

しかし、このようなサイトは、バックグラウンドでどのようなことが起こっているか分からないので、あまり積極的に使いたくないな…と思っていました。

そこで、隙間時間を使い、上記のサイトの代替となりうる自分専用のミニアプリを作りました。

Code Escape for <code> tag

リリースしたミニアプリ
リリースしたミニアプリ

自分がほしいミニアプリが作れて、満足しています。

今回、初めてVite(Vite + Bootstrap + PostCSS)を使いました。

以下のコードを貼る際に、早速ミニアプリを使っています。

自分のメモのために、コードを貼っておきます。

vite.config.js

const path = require('node:path');
import viteCompression from 'vite-plugin-compression';
import {defineConfig} from 'vite';

/** @type {import('vite').UserConfig} */
export default defineConfig({
  root: path.resolve(__dirname, 'src'),
  base: '/',
  build: {
    outDir: '../dist',
  },
  server: {
    port: 8080,
  },
  plugins: [viteCompression()],
});

postcss.config.js

const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const purgeCss = require('@fullhuman/postcss-purgecss');

/** @type {import('postcss-load-config').Config} */
const config = {
  plugins: [
    autoprefixer(),
    purgeCss({
      content: ['src/**/*.html'],
      variables: true,
    }),
    cssnano(),
  ],
};
module.exports = config;

src/js/main.js

// Import our custom CSS
import '../scss/styles.scss';

const DEFAULT_PATTERN = `&<>"{}#$%'()*+-\/`;

const CodeElement = document.getElementById('code');
const PatternElement = document.getElementById('pattern');
const EscapedElement = document.getElementById('escaped');
const CopyButtonElement = document.getElementById('copy');

// CodeElement
CodeElement.placeholder = `For example...\n<tag></tag>\n{{ template }}`;
CodeElement.addEventListener('input', (e) => {
  updateEscapedElement();
});
CodeElement.addEventListener('input', (e) => {
  EscapedElement.value = escapeHtml(CodeElement.value);
  CopyButtonElement.disabled = !Boolean(CodeElement.value);
});

function updateEscapedElement() {
  EscapedElement.value = escapeHtml(CodeElement.value);
  CopyButtonElement.disabled = !Boolean(CodeElement.value);
}

// PatternElement
PatternElement.value = getPattern() ? getPattern() : DEFAULT_PATTERN;
PatternElement.placeholder = DEFAULT_PATTERN;
PatternElement.addEventListener('input', (e) => {
  setPattern(PatternElement.value);
  updateEscapedElement();
});

// CopyButtonElement
CopyButtonElement.addEventListener('click', (e) => {
  onClickCopy();
});

export function onClickCopy() {
  if (EscapedElement.value) {
    navigator.clipboard.writeText(EscapedElement.value);
  }
}

function escapeHtml(text) {
  const pattern = `[${PatternElement.value}]`;
  const regexPattern = new RegExp(pattern, 'g');
  return text.replace(regexPattern, (character) => {
    return '&#' + character.charCodeAt(0) + ';';
  });

}

function getPattern() {
  return window.localStorage.getItem('pattern');
}

function setPattern(value) {
  return window.localStorage.setItem('pattern', value);
}
よろしければ、使ってみてください。
author
Teruhiro Komaki komaki.dev
2024年 03月 31日 日曜日
16時00分

FileMakerなどのインストーラをダウンロードする速度が遅い場合の対応

FileMakerのインストーラをダウンロードする速度が遅かったので、どうにか方法を考えました。

GCPの無料枠の対象になるように、インスタンス(e2-micro/オレゴン:us-west1)を立てて、そのインスタンスから#curlでインストーラを取得するのはどうかと。

いまいち原因がわかりませんが、自宅 -> AWSの通信が異常に遅いこともあり、GCPでかつUSのリージョンの方が良いだろうと思いました。

現在は、以下の通り…

自宅 -(ブラウザ)-> ダウンロードURL

を、以下のようにしてみようと…

自宅 -(SSH)-> GCP(US) -(curl)-> ダウンロードURL

  • 1 つの非プリエンプティブル e2-micro VM インスタンス(1 か月あたり)。
    次の米国リージョンのいずれかで利用できます。
    • オレゴン: us-west1
    • アイオワ: us-central1
    • サウスカロライナ: us-east1
  • 30 GB-月の標準永続ディスク
  • 1 GB の北米から全リージョン宛ての下りネットワーク(1 か月あたり、中国とオーストラリアを除く)
Google Cloud の無料プログラム

curlでダウンロード

平均、90Mでダウンロードでき、一瞬でダウンロード完了しました。

teruhiro@instance-20240331-071923:~/tmp$ curl -O https://fmdl.filemaker.com/maint/107-85rel/xxx.dmg
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  443M  100  443M    0     0  90.8M      0  0:00:04  0:00:04 --:--:-- 90.9M

Cloud Storageにアップロード

gcloudコマンドでアップロードします。

gcloud storage cp xxx.dmg gs://your-bucket/

Compute EngineからCloud Storageにアップロードできない場合

以下のスクショのアクセススコープの設定を確認してください。

確認手順

  • インスタンスを停止する
  • インスタンスを編集する
  • アイデンティティと API へのアクセスの項目に移動する
  • アクセススコープの設定を確認する
  • デフォルトのアクセス権を許可が選択されている場合
  • API ごとにアクセス権を設定を選択する
  • ストレージ読み取り / 書き込みに変更する
  • 編集を保存する
Compute Engineの設定(アクセススコープ)
Compute Engineの設定(アクセススコープ)

せっかくなので、インスタンスは削除せずに、定期的に利用したいと思います。

参考情報

author
Teruhiro Komaki komaki.dev
2024年 03月 28日 木曜日
09時00分

metaタグのtheme-colorを設定しました

metaタグのtheme-colorで、ブラウザのカラーを設定できるということで、自分で運営しているブログなどに設定してみました。

もともと #ffffff だった値を #000000FF に変更しました。

<meta name="theme-color" content="#000000FF">
metaタグ theme-color
metaタグ theme-color
Safariで表示 変更前
Safariで表示 変更前
Safariで表示 変更後
Safariで表示 変更後

metaタグのtheme-colorについて

theme-color Meta Tag | Can I use... Support tables for HTML5, CSS3, etc

安定の CSS-TricksMDN を確認するのが良いですね。

Starting with Version 15, Safari supports the theme-color <meta> tag both on macOS and iOS.
That’s exciting news because now the first desktop browser supports this <meta> tag and it also supports the media attribute and the prefers-color-scheme media feature.
I never really took much note of the theme-color meta tag, but now is a good time to learn about its features and limitations and try to discover some interesting use cases.


Google翻訳
バージョン 15 以降、Safari は macOS と iOS の両方でテーマカラーの <meta> タグをサポートします。
これは興味深いニュースです。なぜなら、最初のデスクトップ ブラウザがこの <meta> タグをサポートし、メディア属性と優先カラー スキーム メディア機能もサポートしているからです。
私はテーマカラー メタ タグにあまり注目したことがありませんでしたが、その機能と制限について学び、いくつかの興味深い使用例を見つけてみる良い機会になりました。

Meta Theme Color and Trickery | CSS-Tricks - CSS-Tricks

theme-color の値は <meta> 要素の name 属性において、ユーザーエージェントがページやその周辺のユーザーインターフェイスの表示をカスタマイズするために使用すべき推奨色を示します。
指定された場合、 content 属性には有効な CSS の <color> が含まれていなければなりません。

theme-color - HTML: ハイパーテキストマークアップ言語 | MDN
author
Teruhiro Komaki komaki.dev
2024年 03月 27日 水曜日
06時00分

防犯カメラをつけました

少し前に、#防犯カメラを買ったので、自宅につけてみました。

アマゾンで、#Ringというカメラを買いました。

ドアベル&セキュリティカメラ | Ring

バッテリーが意外と長持ちで、驚いています。

アプリで、バッテリーの残量を確認できるものの、マメに確認することは難しくて…

「そろそろバッテリーが切れるかな…」と思っていたら、通知メールがきました。

スクリーンショット
スクリーンショット

通知メールが来たおかげで、無事バッテリーの交換ができました。

助かりました…

author
Teruhiro Komaki komaki.dev
2024年 03月 21日 木曜日
10時00分

FileMaker Data API を実行ステップや、FileMaker Data APIの戻り値の日付フォーマットについて

FileMaker Data API で、ID による単一レコードの取得、レコードの取得、およびレコード検索のエンドポイントに FileMaker Data API によって返される日付およびタイムスタンプフィールドの形式を制御するオプションの dateformats 引数が含まれるようになりました: 米国は 0、ファイルロケールは 1、ISO8601 は 2 です。
指定されていない場合、デフォルト値は 0 です。

Claris FileMaker Server 19.6.1 リリースノート

For FileMaker Data API, the Get a Single Record by ID, Get Records, and Find Records endpoints now include the optional dateformats parameter, which controls the format of date and timestamp fields returned by FileMaker Data API: 0 for US, 1 for file locale, or 2 for ISO8601.
If not specified, the default value is 0.

Claris FileMaker Server 19.6.1 Release Notes

dateformats = 0 の場合は、米国

{
  "layouts": "blank",
  "query": [
    {
      "日付": "03/01/2024"
    }
  ],
  "dateformats": 0 <- 米国
}
{
  "response": {
    "dataInfo": {
      "database": "blank",
      "layout": "blank",
      "table": "blank",
      "totalRecordCount": 1,
      "foundCount": 1,
      "returnedCount": 1
    },
    "data": [
      {
        "fieldData": {
          "タイムスタンプ": "03/01/2024 09:10:20", <- 米国
          "日付": "03/01/2024" <- 米国
        },
        "portalData": {},
        "recordId": "1",
        "modId": "4"
      }
    ]
  },
  "messages": [
    {
      "code": "0",
      "message": "OK"
    }
  ]
}

dateformats = 1 の場合は、ファイルロケール

{
  "layouts": "blank",
  "query": [
    {
      "日付": "03/01/2024"
    }
  ],
  "dateformats": 1 <- ファイルロケール
}
{
  "response": {
    "dataInfo": {
      "database": "blank",
      "layout": "blank",
      "table": "blank",
      "totalRecordCount": 1,
      "foundCount": 1,
      "returnedCount": 1
    },
    "data": [
      {
        "fieldData": {
          "タイムスタンプ": "2024/03/01 09:10:20", <- ファイルロケール
          "日付": "2024/03/01" <- ファイルロケール
        },
        "portalData": {},
        "recordId": "1",
        "modId": "4"
      }
    ]
  },
  "messages": [
    {
      "code": "0",
      "message": "OK"
    }
  ]
}

dateformats = 2 の場合は、ISO8601

{
  "layouts": "blank",
  "query": [
    {
      "日付": "03/01/2024"
    }
  ],
  "dateformats": 2 <- ISO8601
}
{
  "response": {
    "dataInfo": {
      "database": "blank",
      "layout": "blank",
      "table": "blank",
      "totalRecordCount": 1,
      "foundCount": 1,
      "returnedCount": 1
    },
    "data": [
      {
        "fieldData": {
          "タイムスタンプ": "2024-03-01T09:10:20", <- ISO8601
          "日付": "2024-03-01" <- ISO8601
        },
        "portalData": {},
        "recordId": "1",
        "modId": "4"
      }
    ]
  },
  "messages": [
    {
      "code": "0",
      "message": "OK"
    }
  ]
}
author
Teruhiro Komaki komaki.dev
2024年 03月 19日 火曜日
17時00分

Claris IDの更新トークンを使ってFileMaker Cloud の Claris ID Tokenを更新する

ひとつまえの投稿でも伝えた通り、Claris IDのRefresh Tokenを使って、Claris ID Tokenを更新したいので、API周りを調べていました。

curlコマンドで更新する方法がわかったので、簡易的にHTMLで公開することにしました。

Generating a Claris ID Token with FileMaker Cloud | frudens Inc.

スクリーンショット
スクリーンショット
Claris ID Tokenを生成するミニアプリのサムネイル
Claris ID Tokenを生成するミニアプリのサムネイル
author
Teruhiro Komaki komaki.dev
2024年 03月 19日 火曜日
09時00分

Claris ID Tokenの有効期限について

Refresh Tokenは、1年間有効。Claris ID Tokenは、1時間有効。

スクリーンショット
スクリーンショット

FileMaker CloudのClaris ID Tokenを更新する

Amazon Cognito User Pools の API Reference InitiateAuth に記載があります。

InitiateAuth - Amazon Cognito User Pools

以下のcurlコマンドを実行すると、Claris ID Tokenを更新することができます。

json='
{
  "ClientId": "4l9rvl4mv5es1eep1qe97cautn",
  "AuthFlow": "REFRESH_TOKEN_AUTH",
  "AuthParameters": {
    "REFRESH_TOKEN": "eyJjdHkiOiJK...",
    "DEVICE_KEY": null
  }
}'
curl 'https://cognito-idp.us-west-2.amazonaws.com/' \
  -H 'authority: cognito-idp.us-west-2.amazonaws.com' \
  -H 'content-type: application/x-amz-json-1.1' \
  -H 'x-amz-target: AWSCognitoIdentityProviderService.InitiateAuth' \
  --data-raw "${json}"

戻り値は、以下のJSONになります。

{
  "AuthenticationResult": {
    "AccessToken": "eyJraWQiOiJ6a...",
    "ExpiresIn": 3600,
    "IdToken": "eyJraWQiOiIya1...",
    "TokenType": "Bearer"
  },
  "ChallengeParameters": {}
}

FileMaker CloudのClaris ID Tokenをサインアウト(ログアウト)する

同じく、Amazon Cognito User Pools の API Reference GlobalSignOut に記載があります。

GlobalSignOut - Amazon Cognito User Pools

json='
{
  "AccessToken": "eyJraWQiOiJ6a1VrW..."
}'
curl 'https://cognito-idp.us-west-2.amazonaws.com/' \
  -H 'authority: cognito-idp.us-west-2.amazonaws.com' \
  -H 'content-type: application/x-amz-json-1.1' \
  -H 'x-amz-target: AWSCognitoIdentityProviderService.GlobalSignOut' \
  --data-raw "${json}"

戻り値は、以下のJSONになります。

{}

curlから実行できるということは…

FileMakerのURLから挿入で、実行できるということになります。

Claris ID Tokenを取得するための簡易的なアプリを作成しましたので、追って会社のブログでリリースしたいと思います。

author
Teruhiro Komaki komaki.dev
2024年 03月 16日 土曜日
00時30分

Twitterのように短文を投稿する日記を開始した

このWebサイトは、2024年03月13日から投稿を開始しました。

2024年03月13日 16時00分 | 小巻の日記

日々の生活のなかで、気になったことや、思いついたことを、適当に書きなぐっているわけですが、気づいたら、毎日、投稿しています。

ちゃんとしたブログとなってしまうと、どうしても腰が重くなってしまいますが、TwitterやMastodonのように、気軽に発信できるような構成にした点が良かったのかもしれません。

Webサイトの構成は、ブログなのですが、#日記っぽく見えるように、苦手な#CSSを頑張りました。

本当は、Tailwindを使いたかったのですが、なかなか勉強する時間がとれず、やむなく普通の#CSSで書きました。


昨日は、次男がストライダーに乗って、すっごく嬉しそうにしていました。

長男も、まだストライダーに乗りたいって言っているので、取り合いになりそうな予感が…

ストライダーの2台目を買おうかどうか…

author
Teruhiro Komaki komaki.dev
2024年 03月 15日 金曜日
23時00分

ドメインを取得しても、安易にメールアドレスを作らない方がよいと思う

みなさん、自分のWebサイトを公開するために、#ドメインを取得すると思いますが、その際に気を付けていることがあります。

それは…

安易にメールアドレスを作らないことです。

自分が生きているうちは、基本的に、#ドメインの更新を忘れることはないと思います。

しかし、私たちは、いつどこでなにが起こるか分からない環境にいます。

外を歩いていたら、突然事故にあって亡くなる可能性だってあるわけです。もし、自分の身に万が一のことがあり、ドメインの更新ができなかった場合、どうなるでしょうか?

更新期限が過ぎてしまい、#ドメインは失効し、第三者に#ドメインが渡ってしまいます。

その第三者が、メールをキャッチオールするように設定すれば、その#ドメインのメールを全て受信できてしまいます。

たとえば、Cloudflareの場合、以下のように記載されている通り、管理画面から簡単に設定できます。

Cloudflare Email Routing
容易なアドレス作成とメール転送が無料で
ドメインのカスタムメールアドレスを作成します。
設定されていないアドレス用のキャッチオールアドレスをオプションで追加します。
お好みの受信箱へメールを転送します。
すべて、プライマリメールアドレスを決して露出することなく行います。

Email Routing | アドレス作成とメール転送が簡単に | Cloudflare

失効のリスクのほかに、メインのメールアドレスとして使ってしまうと、将来にわたり、#ドメインを捨てることが難しい状況になります。

そのため、#ドメインを取得した際に、安易にメールアドレスを作ると、万が一のリスクがあるので、十分に気をつけましょう…

gandi.netの以下のブログを見ると、近しいことが書いてありました。

Facebook や Twitter などのソーシャル メディアのプロフィールは、一部の金融機関と同様に、電子メール アドレスにリンクされています。
個人アカウントとは別に、電子メール アドレスは、(有効期限が切れていない) ドメイン名などのオンライン資産を管理するアカウントにリンクできます。
ユーザーまたはオンライン サービスが (場合によっては自動的に) ホストされているドメイン名に電子メールを送信しようとしたときに、そのドメインが存在しない場合、電子メールはバウンスされます。
その結果、ドメイン名の有効期限が切れると、そのドメイン名に送信される可能性のある電子メールにとっても魅力的なものになります。
再登録された期限切れドメインに設定されたキャッチオール電子メール アドレスは、あらゆる電子メールをキャプチャすることができ、ソーシャル メディア プロファイル、銀行口座、ドメイン名、IP アドレス ブロックへのアクセスを他人に与えてしまう可能性があります。

The domain name afterlife

私は、家族ができ、子供が大きくなるにつれて、自分の#死後のことを意識するようになりました。

少し大げさかもしれませんが、#ドメインやメールアドレスだけでなく、パスワードマネージャーなど、家族が困らないように、日々意識して準備しておいても良いのかもしれません…

1Passwordのファミリープランを検討しないと。

author
Teruhiro Komaki komaki.dev
2024年 03月 15日 金曜日
15時50分

Googleのリフレッシュトークンの有効期限について

#FileMakerから、#Google#APIを利用する際に、#リフレッシュトークン(更新トークン)をFileMakerの内部で管理します。

その際に、お客様から「#リフレッシュトークンは、更新する必要がなく、ずっと使えるの?」と良く質問を頂きます。

ということで、リンクを張っておきます。

リフレッシュトークンの有効期限について

付与された更新トークンが機能しなくなる可能性を想定したコードを記述する必要があります。更新トークンが機能しなくなる理由は、次のいずれかです。

  • ユーザーがアプリのアクセス権を取り消した。
  • 更新トークンが 6 か月間使用されていません。
  • ユーザーがパスワードを変更し、更新トークンに Gmail スコープが含まれている。
  • ユーザーアカウントが付与されている(有効な)更新トークンの最大数を超えています。
  • 管理者がアプリのスコープでリクエストされたサービスのいずれかを制限付きに設定した場合(エラーは admin_policy_enforced)。
OAuth 2.0 を使用して Google API にアクセスする | Authorization | Google for Developers
スクリーンショット
スクリーンショット

ということで、基本的には一度取得した#リフレッシュトークンは、無効化されることがないという認識です。

author
Teruhiro Komaki komaki.dev
2024年 03月 15日 金曜日
13時00分

CSSの属性セレクターについて

#CSSは、必要に迫られたり、分からないことがあれば、適宜勉強するという程度なので、かなり素人です。

何かのきっかけで、属性セレクターを見つけました。

CSS の属性セレクター (attribute selector) は、指定された属性が存在するかどうか、またはその値に基づいて要素を選択します。

属性セレクター - CSS: カスケーディングスタイルシート | MDN
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>CSS</title>
  <style>
    :root {
      --color-primary: #cfe2ff;
      --color-success: #d1e7dd;
      --color-warning: #fff3cd;

      --bg-color: var(--color-primary);
    }

    [param=a] {
      --bg-color: var(--color-success);
    }

    [param=b] {
      --bg-color: var(--color-warning);
    }

    .color {
      background-color: var(--bg-color);
    }

  </style>
</head>
<body>
<div>
  <p class="color">test</p>
  <p class="color" param="">test</p>
  <p class="color" param="invalid">test</p>
  <p class="color" param="a">test</p>
  <p class="color" param="b">test</p>
</div>
</body>
</html>

スクショもはっておきます。

スクリーンショット
スクリーンショット

Bootstrapは、ダークモードを始めとするカラーモードをサポートするようになりました!
v5.3.0では、独自のカラーモード・トグラーを実装することができ(Bootstrapのドキュメントにある例は下記を参照)、適切と思われるさまざまなカラーモードを適用することができます。
ライトモード(デフォルト)とダークモードをサポートしています。
カラーモードは、<html>要素上でグローバルに切り替えることも、data-bs-theme属性のおかげで、特定のコンポーネントや要素上で切り替えることもできます。

カラーモード · Bootstrap v5.3
author
Teruhiro Komaki komaki.dev
2024年 03月 15日 金曜日
00時30分

EleventyのPaginationについて

このサイトは、#Eleventyを使って、HTMLを生成しています。

#EleventyPaginationという機能を使い、、以下のスクショのように、年、月、日、タグのページを動的に生成しています。

スクリーンショット
スクリーンショット

タグのページを動的に生成する書き方は、以下のページに丁寧に書かれています。

Quick Tip #004—Zero Maintenance Tag Pages for your Blog — Eleventy

以下のようなFront Matterを書けば、動的にタグのページが生成されます。

すばらしく簡単です!

---
pagination:
  data: collections
  size: 1
  alias: tag
permalink: /tags/{{ tag }}/
---
<h1>Tagged “{{ tag }}”</h1>

<ol>
{% set taglist = collections[ tag ] %}
  {% for post in taglist | reverse %}
    <li><a href="{{ post.url }}">{{ post.data.title }}</a></li>
  {% endfor %}
</ol>

実装するなかで、ひとつ課題がありました。

以下のようにcollectionApi.getAll()をしても、Paginationの結果がNページある場合においても、1ページしか取得できない問題に遭遇しました。

eleventyConfig.addCollection('allPost', (collectionApi) => {
    const posts = collectionApi.getAll()
    for (const post of posts) {
      console.log(post.data.title)
    }
    return posts
  });

調べてみると、以下のIssueに答えがありました。

Collections.all only lists first page created with pagination, not others · Issue #253 · 11ty/eleventy

以下のように、Paginationを設定しているファイルのFront MatterにaddAllPagesToCollections: trueを追加することで、すべてのPaginationのページが取得できました。

---
pagination:
  addAllPagesToCollections: true
---
author
Teruhiro Komaki komaki.dev
2024年 03月 14日 木曜日
19時00分

Data Matrixに対応している、jsQRを使ってブラウザでQRコードを読み取る

16時00分に、投稿をした際には#jsQRを使ってみたのですが、#QRコードに追加して、#Data Matrixも読めるようにしたいなと思いました。

2024年03月13日 16時00分 | 小巻の日記

探してみると、以下の#ZXingというライブラリが見つかりました。

zxing-js/library: Multi-format 1D/2D barcode image processing library, usable in JavaScript ecosystem.

以下のデモサイトにアクセスすると、カメラを使ってテストできます。

ZXing TypeScript | Decoding from camera stream

#Data Matrixのサンプルで確認しましたら、無事読み込めました。

スクリーンショット
スクリーンショット

こちらのライブラリを活用すると、#FileMaker Goなどのクライアントが不要になりそうな予感がします…

ZXing TypeScript | Demo & Examples