開発日誌

開発した時に躓いたこととかの記録

はてなブログからmicroCMSにデータ移行する方法

  • 既存CMSからmicroCMSに移行できるのか気になった
  • 個人でCMSを持ってないので、はてなブログをmicroCMSにデータ移行をやってみた
  • データ移行だけ試してみるだけなのでこのブログは引き続きはてなブログで運営する
  • 題材のブログは読書記録ブログの方

※諸注意

  • ブログとCMSは違うが、CMSでブログ運用できるので題材にした
  • Pythonは趣味レベルなので、あまり綺麗ではない
  • microCMS, Next.jsについての説明はしない

はてなブログからデータをexport

microCMSにデータをインポートする

悩みポイント

  • exportした本文がHTML形式の場合、リッチエディタにいれてしまうと、HTMLタグがそのまま出力されてしまう
  • microCMSデフォルトのデータであるpublishAtがimport日時になってしまう
    • publishAtはUIからは変更ができるが、インポートは不可
  • 近い悩みを抱える人を発見 WordPressからmicroCMSに記事を移行した時の覚え書き

解決方法の模索

  • 先人から『importデータを別枠で隔離した方がわかりやすくなる』とアドバイスをもらったので、カスタムスキーマにimportデータを隔離し、『importデータがあれば優先して表示、なければ新規データを読み込む』とした。
  • カスタムスキーマCSVでimportできないが、 先の記事 と同じくPOSTならデータを入れることができるので、複数回POSTを叩くことでデータの移行を実現する

CSVの追加作業

POSTの実行

  • POSTの実行の前にmicroCMSのAPI keyの権限をPOST許容に変更
    • 移行作業が終わったら不可に戻しておく
  • POSTのスクリプトはコチラ
    • 1回実行ごとに5秒sleep
      • 制限はないようだが、気持ち的に
    • 'fieldId' : 'importData',は必須
    • publishDateはmicroCMSはISO formatに変換。何もつけないとUTCになってしまうため、+09:00も追加。
    • 投稿の古い順からPOSTする
      • こうしておくとGETするときにpublishedAtでソートするだけで投稿順に取得できる

UI構築

【メモ】App RouterでmicroCMS構築

  • 公式の構築記事からの主な変更点は下記
  • Next.jsの公式を参照するときは、左上は青のApp Routerになっていることを確認
  • キャッシュが消えない時は.nextcacheを消してdev環境を再起動するといける

悩み

  • 新規記事の作成でもimportデータの欄が表示されてしまう
  • importデータと共存のために、本来ならrequiredにしたい本文をrequiredにできていない

おわり

  • リッチエディタ(HTML形式でexportされる)をリッチエディタに取り込めないのは移行を勧めるにあたってひっかかるポイントになってしまいそうなので手段を見つけたい
  • App Routerは今回はじめてじっくり触ったが、しっくりくる使い心地で良い。getStaticPropsをコンポーネント内でただのデータfetchのように書ける点がとても好き
  • App Routerの影響なのか、今回開発するときにdev環境のAPIのキャッシュがすごい強く、CMSのデータを更新してリロードしてもデータが変わらない事象に遭遇した。.nextcacheを消して再起動で解消したが、もっと良い方法がある気がする。
  • 【リマインド】microCMSのAPIキーのPOSTの権限をOFFを忘れずに!

VPSのSFTP接続設定をして、CalckeyをWindowsでPWAした時のアプリアイコンを変更したかった

【追記】

packagesの中を自分で置き換えるのは推奨されないようで、customディレクトリ内にファイルを置いてpnpm run gulpするのが推奨のやり方のようです。

が、Calckeyの最新のstableバージョンである13.1.4.1 ではassets配下のディレクトリの中身のコピーに対応しておらず、したがってhttps://yourinstance.tld/static-assets/icons/filename.extと1階層深堀したところにあるiconsファイルは置換ができません。

developでは当該箇所は修正されているので、新しいバージョンはstableになったらまた挑戦してみようかなと思います。

【追記ココマデ】

TLにて『AndroidでMisskeyのアイコンがfaviconにならないの……』という話を見てわたしも~~と思って参戦してみた。

iOSではfaviconがアイコンになりますが、Android/WindowsのPWAでは仕様が違うようです。

やること

  • 公式にやり方が書いてある
  • manifest.jsonに書いてあるファイルを置き換えに行けばOK。
  • manifest.jsonを確認すると/static-assets/icons/192.png/static-assets/icons/512.pngと置き換えればよさそう
  • 私のサーバーはSSH接続しかできないので、SFTP接続できるようにして、ファイルを配置できるようにしていく

SFTP接続をできるようにする

前提

手順

メモ

  • 最初FFFTPで試行錯誤してたのだが、FFFTPだとSSH鍵認証できないのでダメっぽい

サーバー内のファイルを置き換えて反映!

  • ……できればよかったのですが、calckey 13.1.4.1では現状推奨される手段では置換ができません。developではそれなりに前に修正されているので、次のバージョンでは反映されると思います。
  • その時にきっと動くようになる手順を打消しつつおいておきます。

手順

  • /home/[calckey実行ユーザー名前]/calckey/custom/icons192.png512.pngを配置する
  • calckeyディレクトリでpnpm run gulpを叩く
  • キャッシュクリア or https://[鯖のドメイン]/static-assets/icons/192.pnghttps://[鯖のドメイン]/static-assets/icons/512.pngにアクセスして何度か更新して画像を反映!!
  • Windowsの場合、右の『・・・』からアプリを選択してインストール!
  • 🎉!!!

  • 下の画像の右上、インストールされているアプリの文言の左が元々Calckeyロゴにしかならなかった感じ。

おわり

  • SFTP接続できるようにするのってどうやるの??で3時間くらい使った気がする
    • インフラエンジニア偉大すぎる
  • 画面のことだからclientディレクトリだろうと思ったら違ったのでぴえんってなった
    • でも確かに『バックエンドに置くもの(クライアント側の持ち物ではない)』と言われたらそうかも。
  • サーバー再起動、VPSかりてから初だったのでちょっとドキドキした
    • サーバー再起動したらKAGOYAのサイトが落ちててちょっと焦った。すぐなおったようで良かった。
  • 実はWindowsでPWAできるのをこの時はじめて知った。スマホだけだと思ってた。

Sassを利用しているNext.jsをStorybook6から7に移行する

  • Storybookのv7がでてたのでアップデートしてみる。2023/5/18時点の7.0.12にアプデする。
  • リポジトリ

道のり

6 -> 7に移行し、起動できるようになるまで

  • 公式の Migration guide for Storybook 7.0 のupgradeコマンドをたたくも、何故かupgradeされない
  • 仕方ないのでpackage.jsonを自分で編集
  • Storybookをバージョン6から7への移行 の記事を参考2点実施
    • storybookコマンドを変更
    • @storybook/nextjsをinstall
    • main.jsを編集
    • storyを編集
  • ここでstorybook起動するも動かなかったので、再度upgradeコマンドを叩いく。質問が変わり、起動はするようになった。

起動後エラーの修正

  • 下記のエラーが出た。
ModuleBuildError: Module build failed (from ./node_modules/@storybook/nextjs/node_modules/sass-loader/dist/cjs.js):
SassError: expected "{".
  ╷
2 │       import API from "!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js";
  │                                                                                                     ^
  ╵
  src\components\frame\frame.module.scss 2:101  root stylesheet
  • 2022/9に同じ悩みを抱えた人がいた
  • 完成形は下記。v6の頃よりかなりシンプルになっていい感じ。
const path = require('path')

module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    {
      name: '@storybook/addon-styling',
      options: {},
    },
  ],
  framework: {
    name: '@storybook/nextjs',
    options: {},
  },
  features: {
    buildStoriesJson: true,
  },
  docs: {
    autodocs: true,
  },
}

おわり

  • 今までstorybook起因のvulnerabilitiesが結構あってちょっと気になってたのだが、v7になって一気に解消されてvulnerabilitiesがゼロになった。嬉しい。

Next.jsのAPIでnodemailerを使ってメールを送る

  • メール通知を作ってくれ。Next.jsのAPI Routesで。と言われて『できるんだ……』ってなったのでやってみるなど。
  • Routing: API Routes | Next.js

ざっくり仕様

  • メールはGmailを使う
  • ライブラリはnodemailerというやつを使う
  • FWはもちろんNext.js

結果

import type { NextApiRequest, NextApiResponse } from 'next'
import { createTransport } from 'nodemailer'

const transporter = createTransport({
  service: process.env.MAIL_SERVICE,
  secure: true,
  auth: {
    user: process.env.MAIL_AUTH_USER,
    pass: process.env.MAIL_AUTH_PASS,
  },
})

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const method = req.method
  switch (method) {
    case 'POST': {
      const { title, body } = req.body
      try {
        await transporter.sendMail({
          from: process.env.MAIL_FORM_USER,
          to: process.env.MAIL_TO_DEFAULT,
          subject: title,
          text: body,
        })
        res.status(200).end()
      } catch (error) {
        res.status(500).end()
      }
      res.status(500).end()
      break
    }
    default: {
      res.status(404).end()
    }
  }
}
  • src/pages/index.tsx
import Head from 'next/head'
import styles from '@/styles/Home.module.scss'
import { useState } from 'react'

export default function Home() {
  const [text, setText] = useState('')
  const [result, setResult] = useState<string>()

  const onClick = () => {
    setResult('メールを送ってるよ……。')
    fetch('/api/mail', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        title: 'テストメール',
        body: text,
      }),
    })
      .then((res) => {
        setResult('メールを送ったよ')
      })
      .catch((err) => {
        setResult('メールを送るのに失敗したよ……。')
      })
  }

  const onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setText(e.target.value)
  }

  return (
    <>
      <Head>
        <title>Mail Test App</title>
        <meta name='description' content='Generated by create next app' />
        <meta name='viewport' content='width=device-width, initial-scale=1' />
        <link rel='icon' href='/favicon.ico' />
      </Head>
      <main>
        <div className={`${styles.send}`}>
          {result === undefined ? (
            <>
              <textarea onChange={(e) => onChange(e)} />
              <button onClick={() => onClick()}>めーるおくるよー</button>
            </>
          ) : (
            <p>{result}</p>
          )}
        </div>
      </main>
    </>
  )
}

ひっかかったこと

Googleのログインにひっかかる

メモ

  • ts環境だとnodemailerがエラーになるのは @types/nodemailerをinstallしておけばOK
  • 同一オリジンポリシーがあるので、同じリポジトリのサイトから呼ぶだけならプラスで何かやる必要はなさそう

参考にしたサイト

おわり

  • 意外と簡単にできる!そして意外とメール送る処理って実装したことない。
  • メール送りまくったのでGoogleに不審に思われてないかちょっと不安
  • デプロイして動作確認もやったが、BASIC認証つけたとはいえインターネットに残しておくのは不安なので削除した。みんなもきをつけよう。
  • おわり。

個人サイトをポコポコ作るには

  • 最近個人サイトをポコポコ生やしてるので書く。
    • 更新間に合ってないが作った主な個人サイトは https://manasas.dev に適宜まとめている)。

前提

  • ギャラリー・ブログ・jsのみで作れる簡易なツールのサイトを作る想定
  • アプリケーション的なサイトも近い技術で作れるとは思う

使うもの

Github

  • 基本はmainブランチ1本構成
  • 大きめのもの作る時はブランチ切ることもある

ホスティングサービス

  • 基本Vercelを使ってるが、FirebaseやCloudflareも利用している

Firebase

  • ポートフォリオで利用
    • 制作時、Firestoreに激ハマりしていた名残
  • Github Actionsを組んで利用
  • ホスティングのみに利用するにはtoo muchな感じがあるので新規ではあまり採用していない

Vercel

Cloudflare

  • mana's toolsで利用
    • Calckeyの鯖立てした時に『Coudflareはhostingもできる』を知ったのがキッカケ
  • これもGithubリポジトリつなぐだけ。普通にホスティングする分にはVercelと大きな違いはないかも。

ホスティングサービス、どれを選ぶ?

やり方

  1. 何らかのフロントエンドフレームワークを使ってサイトを作る
    • Next.js, Astro, Svleteらへんが今の推し
  2. Githubに上げる
  3. ホスティングサービスでポチポチでリポジトリつないでホスティング
  4. サブドメインを作成。ホスティングサービスでポチポチで独自ドメインに変更。ドメイン変更が反映されるまでちょっとまつ。
  5. 完成🎉

おわり

  • ねっ、簡単でしょ?
  • 野良のHTML + CSS直打ち民を増やしたい気持ちがある。
  • サーバー借りるのは勇気がいるし、このへんのホスティングサービスならタダから小さくはじめられるよ!と、思ったのだが、Githubとフロントエンドフレームワークはそれなりに大きな壁になってしまうだろうか……
    • コンポーネント使わなければAstro/Svelteは個人サイト作るのに手軽だと思うのでちょいちょい言語化していきたい

Astroでいにしえの個人サイトを作った

  • いにしえの個人サイトをつくった。

    • ここでいう『いにしえの個人サイト』とは『1ページ1ページ真心込めてHTML+CSSを凝り凝りしてるサイト』のことをさす。
  • サイトはコレ↓

やりたかったこと

  • いにしえの個人サイトをpushで自動デプロイで作りたい
  • できれば非ITの人でもできる構成にしたい

やったこと

仕様技術

選定理由

Astro
  • HTML+CSSができてできるだけシンプルなものをチョイス
  • いつかjsやりたくなった時は局所でReactが召喚できるのも高ポイント
Vercel
  • いつもの
Bing Image Creator
  • 日本語で画像生成依頼ができて、1回の依頼で4枚作ってくれて、かつ応答速度もそこそこ良い

思ったこと

  • やった後、非ITの人向けの記事を書こうとして『Githubの導入を非ITの人向けに説明するのめっちゃムズい』ということに気づいた
    • 世の中にある記事も非ITに伝わるかって思うとだいぶ悩む
    • HTML+CSSを書きたい時点である程度のハードルは越えてくれそうだが、悩む
    • でも非ITの人向けのCSSデコみたいな話も書いていきたいので、この点はまた今度、でもいいから書いていこうかなと思う。
      • 日誌に書くのかの点については悩んでるがカテわけで日誌に書くでいいかなと思ってる
  • Bing Image Creator、良い
    • ローカルで動作させるStableDiffusionとWaifuDiffusionくらいしか触ったことなかったが、どちらも1/2枚ずつが限界だったので、4枚ずつでこの速度なら満足。
  • Astro、良い。
    • Astroが良いというよりは、単純なマークアップであればcss, htmlが1ファイルにまとまってる方が管理が楽で好き
  • いにしえの個人サイトたーのしーーー

    • ガラケー時代の個人サイト民なので『ガラケーでも見れるレイアウト』をしたくなる
    • 背景を『上少しグラデ重ねることで切替の違和感を減らす』ようにしているが、こういうスタイル書くの楽しい
    • 若干画像が重い気配があるのではやめに調整したい
  • おわり。

    • 長年小説を人目に晒さなかったのでちょっと恥ずかしい気持ちがあるが、サイトのレイアウトに関しては個人的に神ってんなと思ってるのでレイアウトを見てください!!!
    • コンテンツ更新も続けていけるようにがんばりたい

Svelteの書き方の特徴をざっくり語る

コンポーネントのファイルの構成

  • HTML、CSSJavaScriptを1ファイルにまとめるタイプ
    • このタイプ、CSSを書く場所に悩まなくて良いので好き
<script lang="ts">
   export let value: number = 0;
   export let text: string;
   export let min: number = 0;
   export let max: number = 255;
   export let step: number = 1;
</script>

<div class="root">
    <div class="label">{text}</div>
    <div class="slider"><input type="range" bind:value {min} {max} {step} /></div>
    <div class="num"><input type="number" bind:value {min} {max} {step} /></div>
</div>

<style lang="scss">
    .root {
        display: flex;
        .label {
            width: 5rem;
        }
        .num {
            width: 7rem;
        }
        .slider {
            width: 10rem;
        }
        div {
            margin: 0.25rem 0;
        }
        input {
            border-radius: 8px;
        }
    }
</style>

コンポーネントの引数定義

  • 下記のexportしているものがSvelteのコンポーネントの引数。
    • 初期値ないものがrequired。
export let value: number = 0;
export let text: string;
export let min: number = 0;
export let max: number = 255;
export let step: number = 1;
  • これ以上なくシンプル。Svelteのwrite less codeの思想を表現している点のひとつだと思う。
  • typeとして定義してる方がまとまってて好みという印象があるが、慣れかも。

jsの変数をCSSに渡したい時

  • JavaScript -> HTMLのstyleでCSS variableを定義 -> CSS という形になる
  • JavaScriptの変数を利用したい部分が少なければstyleにスタイル書くのもアリかもしれない
<script lang="ts">
   import type { ImgOptions } from './type';

   export let imgSrc: string;
   export let text: ImgOptions;
   export let mask: ImgOptions;
</script>

<div
    class="root"
    style="
  --text-blend-mode: {text.blendMode}; 
  --text-top: {text.top}px; --text-left: {text.left}px; 
  --text-red: {text.red}; --text-green: {text.green}; --text-blue: {text.blue}; --text-alpha: {text.alpha}; 
  --text-z-index: {text.layer};
  --mask-blend-mode: {mask.blendMode}; 
  --mask-top: {mask.top}px; --mask-left: {mask.left}px; 
  --mask-red: {mask.red}; --mask-green: {mask.green}; --mask-blue: {mask.blue}; --mask-alpha: {mask.alpha};
  --mask-z-index: {mask.layer};"
>
    <div class="imgWrapper">
        <p class="text">Welcome</p>
        <img src={`https://source.unsplash.com/${imgSrc ? imgSrc : 'Ak81Vc-kCf4'}`} alt="画像" />
    </div>
</div>

<style lang="scss">
    .root {
        display: flex;
        justify-content: center;
        margin: 48px;
        margin-bottom: 500px;
        .imgWrapper {
            position: relative;
            margin: 24px;
            img {
                width: 500px;
            }
            .text {
                display: block;
                position: absolute;
                font-size: 88px;
                font-weight: 700;
                top: var(--text-top);
                left: var(--text-left);
                margin: 0;
                z-index: var(--text-z-index);
                line-height: 1;
                color: rgba(var(--text-red), var(--text-green), var(--text-blue), var(--text-alpha));
                mix-blend-mode: var(--text-blend-mode);
            }

            &::before {
                position: absolute;
                display: block;
                width: calc(100% + 48px);
                height: calc(100% + 48px);
                content: '';
                top: var(--mask-top);
                left: var(--mask-left);
                background: rgba(var(--mask-red), var(--mask-green), var(--mask-blue), var(--mask-alpha));
                z-index: var(--mask-z-index);
                mix-blend-mode: var(--mask-blend-mode);
            }
        }
    }
</style>
  • Reactはcss in jsを使えばいい。CSS modules使ってる時はちょっと大変そう。
  • Astroは未調査だけどぱっと調べた感じだとSvelteと同じようなことをすることになりそう
    • Astro(.astro)はあまり動的なコンテンツ向けではない感があるので、jsを多用する場合はAstroのアイランドアーキテクチャを利用してReactとかVue.jsを利用した方がいいかも
  • Vue.jsはv-bindでjsの変数をCSS内にバインドすることができるらしい

headの書き換え方

  • <svelte:head>という独自タグを使う
  • 地味にフレームワークごとに書き方が違って面白い点だと思う
<script lang="ts">
   export let title: string;
</script>

<svelte:head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <link rel="icon" type="image/svg+xml" href="/mylogo.ico" />
    <meta name="description" content="=まなさすが作ったツール集" />
    <meta property="og:site_name" content="manasandbox" />
    <meta property="og:title" content={title} />
    <meta property="og:url" content="https://tools.manasas.dev" />
    <meta property="og:description" content="まなさすが作ったツール集" />
    <meta property="og:image" content="https://tools.manasas.dev/aza166.png" />
    <title>{title}</title>
</svelte:head>

<body>
    <header>
        <h1><a href="/">Mana's tools</a></h1>
    </header>
    <main>
        <slot />
    </main>
    <footer><a href="https://manasas.dev/" rel="me">© 2022 Manami SASAKI</a></footer>
</body>
  • app.html%sveltekit.head%に反映されるのだろうな、と思う
    • app.htmlが存在するフレームワークもなんか珍しい気がする。Next.jsやastroはこういう類はないのでなんか懐かしい印象がある。
<!DOCTYPE html>
<html lang="ja">
    <head>
       <meta charset="utf-8" />
       <link rel="icon" href="%sveltekit.assets%/mylogo.ico" />
       <meta name="viewport" content="width=device-width" />
       %sveltekit.head%
   </head>
    <body data-sveltekit-preload-data="hover">
        <div style="display: contents">%sveltekit.body%</div>
    </body>
</html>

学習用コンテンツ

  • Svelteは公式から下記のような書き換えて実行して学べるサンプルがたくさん用意されていて学びやすい
  • REPLという『コード片を実行できる仕組み』らしい。プログラミング入門にはかなりよさそう。
  • Svelteの公式、ちょっと重くて中々遷移できない時があるのは私だけだろうか。

おわりに

  • lessさは確かなのでプロトタイプにはかなり良さそう
  • 先の通りjsからCSSに渡すのはやや面倒なので、jsとcssが密接な場合は別フレームワークを検討した方が良さそう
  • 本番プロダクトに使うかは自分はまだちょっと保留。