NFT/トークン保有者向けの Web3 コミュニティポータルです。ウォレット署名ログイン、トークン保有判定、メンバープロフィール、管理画面、ポータルのお知らせ/ライブラリ管理、Faucet、Big Medal Token (BMT)、ログインボーナスをまとめて扱います。
- ウォレット署名によるメンバー/管理者ログイン
- ERC-20 / ERC-721 / ERC-1155 の保有条件によるメンバー認証
- 管理ウォレットによるトークン条件に依存しないポータルアクセス
- メンバープロフィールとプロフィール画像アップロード
- 管理画面でのメンバー管理、公開制御、セッション失効
- 管理画面でのポータル「お知らせ」「ライブラリ」コンテンツ作成、編集、公開切替
- Faucet の支給額、allowlist、送金履歴、送金元ウォレット確認
- BMT の mint、transfer、minter 権限、ログインボーナス設定
- Caddy による HTTPS 終端とセキュリティヘッダー付与
- Node.js 24 以上
- npm
- Docker / Docker Compose (Docker 運用する場合)
- Reown / WalletConnect v2 Project ID
- 管理者用ウォレット
- Faucet を使う場合は、少額だけ入れた Faucet 送信用ウォレット
.env.example を .env にコピーして設定します。
Copy-Item .env.example .env既存 Caddy proxy 配下で動かす場合は、.env.proxy.example を .env.proxy にコピーして設定します。
Copy-Item .env.proxy.example .env.proxy| Key | 用途 |
|---|---|
DOMAIN |
本番で Caddy が HTTPS を発行するドメイン。例: example.com |
ADMIN_WALLET_ADDRESS |
/admin に管理者としてログインでき、条件に関係なくポータルへ入れるウォレットアドレス |
SESSION_SECRET |
セッショントークンの HMAC に使う長いランダム文字列。本番では必ず変更する |
DATABASE_URL |
SQLite DB の保存先。Docker 本番の標準は /data/portal.sqlite |
UPLOAD_DIR |
プロフィール画像の保存先。Docker 本番の標準は /data/uploads |
EVM_RPC_URL |
メンバー認証の初期 RPC URL。未設定時は Soneium Mainnet RPC を使う |
MINATO_RPC_URL |
BMT デプロイ用 Hardhat network の RPC URL |
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID |
Reown / WalletConnect v2 の Project ID |
NEXT_PUBLIC_APP_NAME |
ウォレット署名メッセージや AppKit 表示に使うアプリ名 |
FAUCET_PRIVATE_KEY |
Faucet 自動送金用ウォレットの秘密鍵。少額運用に限定する |
DEPLOYER_PRIVATE_KEY |
BMT デプロイ用ウォレットの秘密鍵。アプリ実行環境には渡さない |
NEXT_PUBLIC_BMT_TOKEN_ADDRESS |
デプロイ済み BMT コントラクトアドレス |
本番では SESSION_SECRET を十分長い乱数にし、公開後に変更した場合は既存セッションを無効化してください。FAUCET_PRIVATE_KEY のウォレットには、失ってよい少額だけを入れてください。DEPLOYER_PRIVATE_KEY はデプロイ作業時だけ使い、本番アプリの runtime には置かない運用にします。
npm install
npm run dev開発サーバーは 0.0.0.0:3000 で起動します。
よく使う検証コマンド:
npm run typecheck
npm run build
npm audit --omit=devdocker compose -f docker-compose-dev.yml up --build- App:
http://localhost:8080 - HTTPS dev proxy:
https://localhost:8443 - ソースコードはコンテナへマウントされます。
- 開発用 compose は
.envを app/caddy に読み込ませます。
同じ Docker host で既存 Caddy container が 80 / 443 を公開している場合は、docker-compose.proxy.yml を使います。この compose は Web3 app だけを起動し、Caddy service は起動しません。
初回のみ、既存 Caddy と Web3 app が共有する external network を作成します。
docker network create proxy既存 Caddy container と Web3 app container の両方を proxy network に参加させます。既存 Caddyfile の新ドメイン block は、次の upstream に proxy します。
reverse_proxy web3comu-app:3000Web3 app は次のように起動します。
docker compose --env-file .env.proxy -f docker-compose.proxy.yml up --build -d起動前の設定確認:
docker compose --env-file .env.proxy -f docker-compose.proxy.yml configdocker-compose.proxy.ymlはportsを公開せず、expose: "3000"のみを使います。appはproxynetwork 上でweb3comu-appalias を持ちます。- SQLite DB とアップロード画像は
portal-datavolume の/dataに保存されます。 - 既存 Caddy 側には
reverse_proxy web3comu-app:3000を設定します。 .env.proxyは secret を含むため Git 管理しません。
アプリ専用の Docker host で、この compose set が 80 / 443 を直接公開できる場合は、従来どおり docker-compose.yml を使います。
docker compose up --build -d- Caddy が
80/443を公開し、Next.js app へ reverse proxy します。 - Caddy には
DOMAINだけを渡します。 - app には runtime に必要な環境変数だけを明示して渡します。
DEPLOYER_PRIVATE_KEYは本番 app コンテナに渡しません。- DB とアップロード画像は Docker volume の
/dataに保存されます。 - Docker runner は
npm prune --omit=dev後の production dependencies で動作し、非 root ユーザーで起動します。
.envのADMIN_WALLET_ADDRESSに管理者ウォレットを設定します。.envのNEXT_PUBLIC_WALLETCONNECT_PROJECT_IDを設定します。/adminを開き、管理者ウォレットで接続して署名します。- 管理画面の「メンバー条件」で対象チェーン、RPC URL、コントラクト、規格、保有条件を設定します。
- 管理画面の「コンテンツ管理」で「お知らせ」と「ライブラリ」を必要に応じて登録します。
- 条件を有効化すると、メンバーは
/からウォレット接続して/portalに入れるようになります。
管理ウォレットはメンバー条件が未設定、無効、または未達の場合でも /portal、/portal/news、/portal/library、/members、/mypage に入れます。Faucet の allowlist や BMT のオンチェーン条件は別管理です。
- メンバーが
/でウォレットを接続します。 - アプリが署名 nonce を発行し、ウォレット署名を検証します。
- 設定済みの ERC-20 / ERC-721 / ERC-1155 条件で保有判定します。
- 条件を満たすとメンバーセッションが作成され、
/portalに入れます。 /mypageでプロフィール、SNS、公開状態、プロフィール画像を設定できます。/membersでは公開済みで停止されていないメンバーのプロフィールを表示します。
プロフィール画像は jpg/png/webp のみ、3MB 以下です。アップロード時に MIME type と実ファイル内容を検証し、配信時は認証済みメンバーまたは管理者だけが取得できます。
管理画面の「コンテンツ管理」では、ポータルに表示する「お知らせ」と「ライブラリ」を管理できます。
- お知らせはタイトル、本文、URL を登録できます。本文または URL のどちらかが必須です。
- ライブラリは外部資料リンクを中心に扱います。タイトルと URL が必須で、説明文は任意です。
- URL は
http://またはhttps://のみ保存できます。 - 公開状態は「下書き」と「公開」から選びます。ポータルには公開済みコンテンツだけが表示されます。
- 「一覧の上部に固定する」を有効にすると、公開一覧で固定コンテンツが先に表示されます。
- 表示順は固定コンテンツが先、その後は公開日時の新しい順です。
- お知らせ本文中の URL はポータル表示時にリンク化されます。
ポータル上部のメニューバーから、ホーム、お知らせ、イベント、ライブラリ、メンバー、マイページへ移動できます。各ページ末尾の重複する戻りリンクは置かず、上部メニューを共通導線にしています。ライブラリのURLは互換性維持のため /portal/library のままです。
Faucet は Soneium Mainnet / Soneium Minato のガス代用 ETH を、allowlist 済みウォレットへ支給する機能です。
- 管理画面でチェーン別の有効/無効と支給額を設定します。
- 管理画面で受け取り対象ウォレットを allowlist に登録します。
- ユーザーは
/faucetで対象ウォレットを接続し、署名して請求します。 - 請求は JST 日付でウォレット/チェーンごとに 1 日 1 回です。
- nonce 発行と claim API には IP + wallet + chain 単位の短時間連打制限があります。
- Faucet の成功、失敗、レート制限は秘密情報なしで監査ログへ出力します。
本番では FAUCET_PRIVATE_KEY のウォレット残高を常に少額に保ってください。Mainnet faucet を有効化する場合は、支給額、allowlist、残高を特に厳格に管理してください。
BMT は Minato 上の ERC-20 トークンです。Hardhat でデプロイします。
npm run deploy:bmt:minatoデプロイ後、出力されたコントラクトアドレスを .env の NEXT_PUBLIC_BMT_TOKEN_ADDRESS に設定します。管理画面では次の操作ができます。
- BMT の name / symbol / owner / totalSupply / cap / 管理者残高の確認
- 管理者ウォレットによる mint
- 管理者ウォレットからの transfer
- minter 権限の付与/解除
- ログインボーナスの有効/無効、支給額、必要残高の設定
メンバーは /portal/login-bonus で接続中ウォレット自身から claimLoginBonus を実行します。受け取りには Minato のガス代が必要です。
- 状態変更 API は same-origin 検証を行い、外部 origin からの
POST/PUT/PATCH/DELETEを拒否します。 - セッション cookie は
httpOnly、production ではsecure、sameSite: "lax"です。 - 管理 API は
ADMIN_WALLET_ADDRESSに一致する admin session を要求します。 - ポータル閲覧とプロフィール更新は、認証済みメンバーまたは設定済み管理ウォレットを許可します。
- DB 操作は SQLite prepared statement を使います。
- ポータルコンテンツの URL は
http:/https:に制限し、外部リンクは別タブで開きます。 - Caddy は HSTS、
X-Content-Type-Options、X-Frame-Options、Referrer-Policy、Permissions-Policy、最小 CSP を付与します。 - 画像配信は
X-Content-Type-Options: nosniffを付けます。 - 本番 Docker image には Hardhat などの devDependencies を残さない構成です。
公開前に最低限以下を確認してください。
npm install
npm run typecheck
npm run build
npm audit --omit=dev手動確認項目:
- 通常ログインと管理ログインが成功する
- 管理ウォレットがトークン条件に関係なくポータルへ入れる
- 管理画面でメンバー条件を保存できる
- 管理画面でお知らせ/ライブラリの下書き作成、公開、編集、削除が動作する
- ポータルには公開済みのお知らせ/ライブラリだけが表示される
- メンバー停止/解除、強制非公開、セッション失効が動作する
- プロフィール更新、画像アップロード、画像表示が動作する
- Faucet allowlist、設定保存、通常請求が動作する
- 同一 wallet/IP で短時間に Faucet を連打すると
429になる - 外部 origin から状態変更 API を呼ぶと
403になる - BMT の mint / transfer / minter / ログインボーナス設定が想定どおり動作する
- 静的画像は
public/images/に配置します。 - サンプル素材は
samples/にあります。 - ライブラリカード画像は
public/images/items.pngを使います。元素材はsamples/items.pngです。 - Docker 本番では SQLite DB とアップロード画像は
portal-datavolume の/dataに保存されます。 .env、data/、node_modules/、.next/は Git 管理しません。