Motomichi Works Blog

モトミチワークスブログです。その日学習したことについて書いている日記みたいなものです。

CursorでUser is unauthorizedとCan‘t verify the user is human. Please try again.になった

起こっていた問題

Cursorのプロンプトを入力して、送信をすると以下のエラーが出るようになりました。

User is unauthorized
(Request ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)

Cursorにログインし直そうと思ったら、ログインフォームで以下のエラーが出ました。

Can‘t verify the user is human. Please try again.

原因と解決

開発の都合で端末の「日付と時刻」を過去日時に設定していたため。

端末の日付と時刻を現在日時に戻したら直りました。

解決のために参照したページ

以下の書き込みがあり、そのおかげで解決しました。

I had the same issue on a new PC, but syncing the system time fixed it.

翻訳すると以下の通り

新しい PC でも同じ問題が発生しましたが、システム時間を同期すると解決しました。

Cursorでブログのシステムを作ってみる日記 その0001

はじめに

Cursorに慣れるために具体的に何か作ってみようと考え、markdownで記事を書けるブログのシステムを作ってみることにしました。

うまくいくかわかりませんが、自分のための日記です。

すごく長い記事です。

ChatGPTでどういう構成が良いか考える

ChatGPTに質問して、構成を考えました。

docker compose を使って、Next.js + API Routes + Prisma + Postgres という構成で、mdで記事を書けるブログシステムを構築することにしました。

Gitリポジトリの作成

next_js_md_blogリポジトリを作成してcloneしました。

Cursorで質問しながら仕様を考える

CursorでAIに相談したところ以下のような感じで提案されたので、これに沿って作ってみようと思う。

技術構成

機能要件

  • 記事(Post)はMarkdown本文・タイトル・作成者・作成日時などを持つ
  • 記事には複数のタグ(Tag)を付与できる
  • 記事とタグは多対多リレーション
  • ユーザー(User)はメールアドレス・パスワード・名前・管理者フラグ(isAdmin)を持つ
  • 管理者ユーザー(isAdmin=true)のみがユーザーの追加・編集ができる
  • 一般ユーザーは自分のマイページで記事の投稿・編集ができる
  • 初期管理者ユーザーはPrisma Studioやスクリプト等で手動作成

データベース設計(Prismaスキーマ

  • User(ユーザー)
  • Post(記事)
  • Tag(タグ)
  • PostTag(記事とタグの中間テーブル)

環境構築手順(推奨フロー)

  1. Next.jsアプリの初期化
  2. docker-compose.yml・Dockerfileの作成
  3. Prismaセットアップ・スキーマ作成
    1. Prismaのインストール
    2. Prismaの初期化
    3. prisma/schema.prismaスキーマを記述
  4. .envにDATABASE_URLを設定
  5. docker composeで起動
  6. Prismaマイグレーション
  7. 依存パッケージのインストール
    1. Markdown記事管理用
    2. 認証・パスワード管理用
    3. 型定義
  8. 初期管理者ユーザーの作成
  9. 各種API・フロントエンド実装(次回以降の記事で)
    1. 認証機能の実装
    2. 記事投稿・編集機能
    3. タグ付け機能
    4. APIルートやフロントエンドの開発

Next.jsアプリの初期化

next_js_md_blogディレクトリで、以下のコマンドを実行しました。

npx create-next-app@latest .

リポジトリ作成時に README.md を作成していたため、失敗しました。

とりあえず README.md を削除して再実行しました。

対話形式の選択は以下の通り選択しました。

user@mac next_js_md_blog % npx create-next-app@latest .                                
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … No
✔ Would you like your code inside a `src/` directory? … No
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to use Turbopack for `next dev`? … Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No

今回生成されたpackage.jsonの記述内容は以下の通りでした。

{
  "name": "next_js_md_blog",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "next": "15.3.4"
  },
  "devDependencies": {
    "typescript": "^5",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.3.4",
    "@eslint/eslintrc": "^3"
  }
}

package.json に "type": "module", を追記する

"type": "module", を追記して以下のようにしました。

{
  "name": "next_js_md_blog",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "next": "15.3.4"
  },
  "devDependencies": {
    "typescript": "^5",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.3.4",
    "@eslint/eslintrc": "^3"
  }
}

セキュリティの観点から .gitignore に追加した方が良い項目があるか聞いてみる

CursorでAIに以下の質問をしてみました。

.gitignore ファイルの記述内容は適切?
セキュリティの観点から追加して置いた方が良いファイルやディレクトリはある?

Prisma や Docker を使う前提というのもあり、いくつか項目を追加した方が良いということだったので、Agentモードで .gitignore を編集してもらった。

  • PrismaやDocker関連の除外を追加
  • ログや環境変数ファイルの除外を整理
  • 不要な重複やパス表記を統一

目視確認をしつつ、手で調整した。

Next.jsアプリの初期ファイルをコミットする

.gitignoreファイルが編集できたので一旦コミットしました。

どこでコミットとか今後は書きませんが、差分を確認しながら理解しやすい単位でコミットして進めていきます。

Next.jsアプリの開発サーバーを起動してみる

以下のコマンドを実行しました。

npm install

以下のコマンドを実行しました。

npm run dev

http://localhost:3000 で開発環境が起動できることが確認できました。

とりあえず control + c で止めておきます。

これで手順1が終わりました。

docker-compose.yml・Dockerfileの作成

手順2に進みます。

CursorのAgentモードでAIに作ってもらいました。

あくまでも開発環境用です。

本番環境用のdocker-compose.ymlはこのような記述内容では良くないのでセキュリティ観点で配慮が必要です。

本番環境ではハードコーディングをしないとか、どのような設定、どのような管理手法が良いのか、改めてAIに聞こうと思います。

docker-compose.ymlの記述内容は以下の通りです。

version: '3.8'
services:
  db:
    image: postgres:15
    restart: always
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: blog
    ports:
      - "5432:5432"
    volumes:
      - db_data:/var/lib/postgresql/data

  app:
    build: .
    command: sh -c "npm install && npx prisma migrate deploy && npm run dev"
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://postgres:postgres@db:5432/blog
    volumes:
      - .:/app
      - /app/node_modules
    depends_on:
      - db

volumes:
  db_data: 

Dockerfileの記述内容は以下の通りです。

FROM node:18

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "run", "dev"] 

本番環境ではどうするのかAIに聞いてみる

要約すると以下のような返答だった。

  • 機密情報は環境変数や.envで管理し、Gitに含めない
  • DBポートは外部公開しない
  • .dockerignoreで不要なファイルを除外
  • 本番用のcomposeファイルを分ける

これで手順2が終わりました。

Prismaセットアップ・スキーマ作成

Prismaのインストール

next_js_md_blogディレクトリで、以下のコマンドを実行しました。

npm install prisma @prisma/client

Prismaの初期化

以下のコマンドを実行しました。

npx prisma init

以下のように表示されました。

user@mac next_js_md_blog % npx prisma init
Fetching latest updates for this subcommand...

✔ Your Prisma schema was created at prisma/schema.prisma
  You can now open it in your favorite editor.

warn You already have a .gitignore file. Don't forget to add `.env` in it to not commit any private information.

Next steps:
1. Run prisma dev to start a local Prisma Postgres server.
2. Define models in the schema.prisma file.
3. Run prisma migrate dev to migrate your local Prisma Postgres database.
4. Tip: Explore how you can extend the ORM with scalable connection pooling, global caching, and a managed serverless Postgres database. Read: https://pris.ly/cli/beyond-orm

More information in our documentation:
https://pris.ly/d/getting-started

prisma/schema.prismaスキーマを記述

CursorのAgentモードでAIに編集してもらいました。

以下の記述になりました。(後でわかることですが generator client ブロックの output の行は不要でした。)

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
  provider = "prisma-client-js"
  output   = "../app/generated/prisma"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  password  String
  name      String?
  isAdmin   Boolean  @default(false)
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String   // Markdown本文
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
  tags      PostTag[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Tag {
  id    Int      @id @default(autoincrement())
  name  String   @unique
  posts PostTag[]
}

model PostTag {
  post   Post @relation(fields: [postId], references: [id])
  postId Int
  tag    Tag  @relation(fields: [tagId], references: [id])
  tagId  Int

  @@id([postId, tagId]) // 複合主キー
}

.envにDATABASE_URLを設定

.envファイルを編集して、環境変数 DATABASE_URL を定義しました。

記述内容は以下の通りです。

DATABASE_URL="postgres://postgres:postgres@localhost:5432/blog"

AIによると、本番環境では .env で管理するより、以下の方法が適切なようです。

注意:このブログ記事で紹介している設定やコードは、あくまで開発環境構築用のサンプルです。

データベースのユーザー名やパスワード、DATABASE_URL などはチュートリアルやローカル開発用の一般的な初期値を使用しています。

本番環境で運用する際は、必ず強固なパスワードや適切な設定に変更し、機密情報はクラウドシークレット管理サービスなどで安全に管理してください。

また、APIキーや本番用の接続情報など、機密性の高い情報は絶対に公開しないようご注意ください。

代表的なクラウドシークレット管理サービスは以下の通りです。

  • AWS Secrets Manager
  • Azure Key Vault
  • GCP Secret Manager

これらのサービスは、アクセス権限や監査ログの管理もでき、インフラやCI/CDとの連携も容易です。

Prismaマイグレーションの実行

まずマイグレーションの前にDBの起動が必要です。

以下のコマンドを実行しました。

docker compose build

以下のコマンドを実行しました。

docker compose up -d

マイグレーションします。

以下のコマンドを実行しました。

npx prisma migrate dev --name init

Prisma StudioでDBをGUI確認

以下のコマンドを実行します。

npx prisma studio

自動的にブラウザが起動して http://localhost:5555/ が開き、データベースの各テーブルが確認できます。

終了する

ターミナルで control + c を押すとPrisma Studioを終了できます。

docker compose down で docker compose のサービスを終了できますが、データベースはまだ使うのでここではまだ終了しないでください。

これで手順6が終わりました。

依存パッケージのインストール

今決まっている仕様を実現していくうえで必要なパッケージを予めインストールします。

Markdown記事管理用

以下のコマンドを実行しました。

npm install gray-matter remark remark-html

認証・パスワード管理用

以下のコマンドを実行しました。

npm install next-auth bcrypt

環境変数管理用

以下のコマンドを実行しました。

npm install dotenv

型定義

以下のコマンドを実行しました。

npm install -D @types/bcrypt

これで手順7が終わりました。

初期管理者ユーザーの作成

初期管理者ユーザー作成用の環境変数を定義する

ログインしてブログの記事を書くことになるので、ユーザーを作成するscriptを作成します。

まず .env に初期管理ユーザー作成用の以下の環境変数を手で追加します。

ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=initial_password
ADMIN_NAME=管理者

初期管理者ユーザー作成スクリプトを定義する

CursorのAgentモードで以下の内容で scripts/create-admin.js をAIに作ってもらいました。

import { PrismaClient } from '@prisma/client';
import bcrypt from 'bcrypt';
import dotenv from 'dotenv';

dotenv.config();

const prisma = new PrismaClient();

async function main() {
  const adminEmail = process.env.ADMIN_EMAIL || 'admin@example.com';
  const adminPassword = process.env.ADMIN_PASSWORD || 'admin';
  const adminName = process.env.ADMIN_NAME || '管理者';

  const password = await bcrypt.hash(adminPassword, 10);
  await prisma.user.create({
    data: {
      email: adminEmail,
      password,
      name: adminName,
      isAdmin: true,
    }
  });
  console.log('管理者ユーザーを作成しました');
}

main()
  .catch(e => {
    console.error(e);
    process.exit(1);
  })
  .finally(() => prisma.$disconnect()); 

スクリプトを実行してみるもエラーになる

node scripts/create-admin.js 

エラーは詳細に書きませんが、エラーをAIのチャットに丸ごとペーストしたら、トラブルシューティングの方法を提案してくれました。

Prisma Clientのコード生成をする

トラブルシューティングのひとつとして、以下のコマンドを実行しました。

npx prisma generate

以下の通り出力されました。

Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma

✔ Generated Prisma Client (v6.10.1) to ./app/generated/prisma in 87ms

Start by importing your Prisma Client (See: https://pris.ly/d/importing-client)

Tip: Interested in query caching in just a few lines of code? Try Accelerate today! https://pris.ly/tip-3-accelerate

これをAIのチャットに貼ったら ./app/generated/prisma にアウトプットされてるのがおかしい。とのこと。

prisma/schema.prisma のアウトプットパスを修正する

以下のようになっている箇所を

generator client {
  provider = "prisma-client-js"
  output   = "../app/generated/prisma"
}

以下の通り変更しました。

generator client {
  provider = "prisma-client-js"
}

Prisma Clientのコード生成をやり直す

もう一度以下のコマンドを実行しました。

npx prisma generate

以下の通り出力され、デフォルトの出力先である ./node_modules/@prisma/client に生成されました。

Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma

✔ Generated Prisma Client (v6.10.1) to ./node_modules/@prisma/client in 46ms

Start by importing your Prisma Client (See: https://pris.ly/d/importing-client)

Tip: Want to turn off tips and other hints? https://pris.ly/tip-4-nohints

私は Prisma を初めて使っているのですが、初めて使うツールをAIに言われるまま適当に設定しているとこのようなトラブルに見舞われる。

ただ、それはそれとして、問題が起こったときのトラブルシューティングも早かった。

スクリプトを実行し直す

以下のコマンドを実行しました。

node scripts/create-admin.js

以下の通り出力されました。

管理者ユーザーを作成しました

GUIでデータを確認する

以下のコマンドを再度実行して、ブラウザからユーザーテーブルのレコードを確認します。

npx prisma studio

ユーザーテーブルにレコードが追加されたことが確認できました。

control + c で、prisma studioを終了します。

ユーザー作成のために .env に定義した環境変数についての注意事項

AIによると以下の注意事項があります。

本番環境を構築・運用していく際は、データベースのパスワードや初期管理者ユーザーのパスワード、APIキーなどの機密情報を安全に管理することが非常に重要です。

.envファイルに機密情報を記載する方法は開発環境では便利ですが、本番環境では漏洩リスクや管理ミスの危険性があるため、よりセキュアな方法を選ぶことをおすすめします。

本番運用では、クラウドシークレット管理サービスを利用するのが最も安全で堅牢な方法です。
これらのサービスを使うことで、機密情報を暗号化して安全に保管し、必要なときだけアプリやスクリプトが安全に取得できます。

代表的なサービスは以下の通りです。

  • AWS Secrets Manager
  • Azure Key Vault
  • GCP Secret Manager

これらのサービスは、アクセス権限や監査ログの管理もでき、インフラやCI/CDとの連携も容易です。


まとめ

  • 本番環境では、クラウドシークレット管理サービスを利用して機密情報を安全に管理しましょう。
  • 代表的なサービスとして「AWS Secrets Manager」「Azure Key Vault」「GCP Secret Manager」などがあります。
  • .envファイルは開発用にとどめ、本番では極力使わない運用が推奨されます。

各種API・フロントエンド実装

ようやくAIから「環境構築手順(推奨フロー)」として提案された「手順9. 各種API・フロントエンド実装」まできました。

とても長くなりそうなのと、ますますごちゃごちゃしそうなので、続きは次回以降の記事で以下のようなことを順次やっていこうと思います。

  • 各種API・フロントエンド実装(次回以降の記事で)
    1. 認証機能の実装
    2. 記事投稿・編集機能
    3. タグ付け機能
    4. APIルートやフロントエンドの開発

まずは「認証機能の実装」ですね。

それにしてもAIに自然言語で日本語で質問して日本語で答えてもらうのは、自分でWeb検索して英語のドキュメントを翻訳しながら環境構築するより何倍も早い。

AIコーディングなどについて学習し始める

はじめに

適当に記事を読んではここに追加していこうかと思う。

最近の所感

  • Cursorで主にReactのコードを編集しているが、ファイル分割のような「手順によっては簡単に安全にできるタスク」については、手順を人間が考えたうえで、タスクを分割して順番にAIに与えると精度が上がる。雑に指示を出して上手くいく場合もあるが、その場合は制度は低い。
  • 「適切な単位でファイル分割してください。」とかだと、フック関数1つにつき1ファイルに分割してしまうことが多かったり、React.MutableRefObject型の変数を考慮してくれなかった。これも人間がある程度それを考慮して、何をどう切り出すかを明確に指示すれば、まあ上手くいき始めた。自分の手でやるよりはプロンプトを適切に書く方が早い。ルールを設定すれば、雑なプロンプトでも適切にやってくれるのかもしれない。

読んだ記事

総合的な記事

Cursor

Amazon Bedrock

リモートGitHub MCPサーバー

CursorでAIにコードレビューしてもらう

ChatGPTによると、以下のプロンプトでできるらしいけど試していない。

@git diff abc1234..def5678

この変更内容をレビューしてください。改善点や問題点があれば教えてください。

Playwrightのテストでブラウザのコンソールに表示されるメッセージをターミナルに表示する

はじめに

CIなどでheadlessでテストを実行している場合に、ブラウザのコンソールに表示されるメッセージを確認する方法です。

サンプルコード

以下のように 'console' イベントをリスニングします。

import { test, expect } from '@playwright/test';

test('コンソールログをキャプチャする', async ({ page }) => {
  // console イベントのリスニングを開始
  page.on('console', (msg) => {
    // コンソールメッセージをターミナルに表示
    console.log(`msg.type(): ${msg.type()}`);
    console.log(`msg.text(): ${msg.text()}`);
    console.log(`msg.args(): ${msg.args()}`);
    console.log(`msg.location(): ${msg.location()}`);
  });

  // テスト実行
  await page.goto('http://localhost:3000');
  await expect(page).toHaveTitle('Example');
});

PlaywrightのMock APIsでGETやPOSTなどのメソッド毎にレスポンスを分ける

参照したページ

サンプルコード

以下のように route.request().method() を呼び出すとメソッドが取得できます。

// Handle GET requests.
await page.route('**/*', async route => {
  if (route.request().method() !== 'GET') {
    await route.fallback();
    return;
  }
  // Handling GET only.
  // ...
});

// Handle POST requests.
await page.route('**/*', async route => {
  if (route.request().method() !== 'POST') {
    await route.fallback();
    return;
  }
  // Handling POST only.
  // ...
});

Playwrightで$や*が含まれるspecファイルのテストをする

はじめに

以下のような $ が含まれるpathのテストを実行しようとしたらエラーになりました。

tests/example.$id.spec.ts

表示されたエラー

以下のエラーメッセージが表示されました。

Error: No tests found.
Make sure that arguments are regular expressions matching test files.
You may need to escape symbols like "$" or "*" and quote the arguments.

翻訳すると以下の通りでした。

エラー: テストが見つかりません。
引数がテスト ファイルに一致する正規表現であることを確認してください。
「$」や「*」などの記号をエスケープし、引数を引用符で囲む必要がある場合があります。

実行する

エラーメッセージを読んだままですが、以下のように$をエスケープしたうえで、引用符で囲います。

yarn playwright test './tests/example.\$id.spec.ts'

環境変数を設定してnpm runする

コマンド

例えば以下のように実行します。

npm

HOGE=hoge npm run jest

yarn

HOGE=hoge yarn jest

環境変数を参照する

環境変数はコード内で以下のように参照できます。

console.log(process.env.HOGE);