mixi2 のポストをブログや任意の Web ページに埋め込めるスニペットを生成・配信するサービス。
mixi2-js (非公式 SDK) で mixi2 Developer Platform の API からポストを取得し、サンドボックス iframe としてレンダリングします。
Note
このプロジェクトは MIXI 社が公式に運営するものではありません。コミュニティのオープンソースプロジェクトです。
- mixi2 のポスト URL を貼り付けるだけで埋め込み HTML を生成
- 生成された
<blockquote>+<script>を任意の Web ページに貼るだけで動作 - リプライ・引用ポストも自動で展開(X / Twitter の web embed と同様に「返信先: @user」ヘッダーや引用カードを表示)
- 投稿テキスト中の URL は OGP プレビュー カードとして展開(最大 4 件)
- メディア(画像 / 動画)は最大 4 枚レイアウトで表示
- iframe は中身に合わせて自動でサイズ調整(
postMessageで高さを通知) - ダーク/ライトモード自動対応
- Discord / X / Slack / Telegram などに シェア用 URL を貼ると OGP プレビューが展開される(カード形式)
- フォーク → Vercel デプロイで 独自ドメインで運用可能(ドメインのハードコードなし)
- 取得済みポストはメモリ + CDN で多段キャッシュ
| パス | 役割 |
|---|---|
/ |
URL 貼り付け → プレビュー → コピー UI |
/embed.js |
埋め込み先ページに貼るローダースクリプト |
/api/debug/{post-id} |
デバッグ用 JSON(リプライ/引用判定の確認用、dev のみ / 本番は 404) |
ポスト本体は以下のすべての形式で同じ SSR ハンドラ (SnippetPage.astro) に繋がります。@ の有無、ユーザー部分の有無、/snippets/ プレフィックスの有無いずれにも対応。Discord などにペーストするとサイト側で生成された OGP メタを読んでリッチプレビューを表示します。
| パス | 用途 |
|---|---|
/snippets/{id} |
iframe canonical (embed.js から呼ばれる) |
/snippets/posts/{id} |
/snippets/ 配下 / ユーザー名省略形 |
/snippets/@{user}/posts/{id} |
/snippets/ 配下 / @ あり |
/snippets/{user}/posts/{id} |
/snippets/ 配下 / @ なし |
/posts/{id} |
短縮シェア URL / ユーザー名省略形 |
/@{user}/posts/{id} |
短縮シェア URL / @ あり(推奨) |
/{user}/posts/{id} |
短縮シェア URL / @ なし |
ルートページ / の UI から HTML か MDX (set:html) のどちらかをトグルで選んで生成・コピーします。
<blockquote
class="mixi2-embedder-embed"
data-mixi2-embedder-post-id="POST_UUID"
>
<a href="https://mixi.social/posts/POST_UUID">View on mixi2</a>
</blockquote>
<script
async
src="https://YOUR_DEPLOYMENT/embed.js"
charset="utf-8"
></script>Note
MDX 形式(Astro / Next.js MDX 等で <script> がパースエラーになる環境)
<div
set:html={`<blockquote class="mixi2-embedder-embed" data-mixi2-embedder-post-id="POST_UUID"><a href="https://mixi.social/posts/POST_UUID">View on mixi2</a></blockquote><script async src="https://YOUR_DEPLOYMENT/embed.js" charset="utf-8"></script>`}
/><blockquote>:data-mixi2-embedder-post-id属性付きのプレースホルダー。JS が無効な環境ではポストへのリンクとして機能します。<script>: ページ内の対応する blockquote を検出し、自動で iframe (このサイトの/snippets/[id]) に置き換えます。srcのオリジンを iframe URL のベースに使うため、複数ドメインを切り替えても自動追従します。
ルートページの「シェア用 URL」をコピーしてチャットに貼り付けると、リッチプレビューカードが展開されます。形式は以下のいずれか:
https://YOUR_DEPLOYMENT/@{username}/posts/{post-id}
https://YOUR_DEPLOYMENT/posts/{post-id}
@ の有無、ユーザー部分の有無いずれにも対応。
このリポジトリは Vercel の auto-detect でそのままデプロイできます。
- GitHub にフォーク
- Vercel で "Import Project"
- Environment Variables に以下を登録
MIXI2_CLIENT_IDMIXI2_CLIENT_SECRET
- Deploy
- (任意)ダッシュボードから独自ドメインを追加
ドメインはどこにもハードコードしていません。Vercel が割り当てた *.vercel.app でも、独自ドメインを後付けしても、embed.js がリクエスト元のオリジンを動的解決して iframe URL を組み立てます。
/embed.js などに対する CDN キャッシュと CORS / CORP ヘッダを vercel.json で明示しています。Vercel はこれを自動で読み込みます。
- mixi2-js は内部で gRPC (
@grpc/grpc-js) を使うため Node ランタイム必須(Edge では動きません) @astrojs/vercelがデフォルトで Node Serverless Functions として出力するので追加設定は不要engines.node: >=22.12.0を指定済みなので、Vercel は自動で Node 22 ランタイムを選択
- Node.js
>=22.12.0(Astro 6 / mixi2-js v1.5 の要件) - pnpm
>=11 - mixi2 Developer Platform の OAuth2 アプリ(
client_idとclient_secret)
pnpm install
cp .env.example .env
# .env に MIXI2_CLIENT_ID / MIXI2_CLIENT_SECRET を記入
pnpm dev # http://localhost:4321
pnpm check # 型チェック (astro check)
pnpm build # 本番ビルド(.vercel/output/ に出力)
pnpm preview # 本番ビルドをローカルで確認| 変数 | 必須 | 説明 |
|---|---|---|
MIXI2_CLIENT_ID |
✅ | Mixi Developer Platform で発行したクライアント ID |
MIXI2_CLIENT_SECRET |
✅ | 同じくクライアントシークレット |
MIXI2_TOKEN_URL |
– | デフォルトは mixi2-js/helpers の tokenUrl。差し替える場合のみ |
MIXI2_API_ADDRESS |
– | デフォルトは mixi2-js/helpers の apiAddress。同上 |
public/
embed.js # ブログに貼られるローダー(静的配信)
favicon.svg
src/
assets/
mixi2_Symbol_FullColor.png # mixi2 公式ロゴ(Astro asset import 経由でフィンガープリント化)
lib/
mixi2.ts # OAuth2Authenticator + Client シングルトン + メモリキャッシュ
mixi2-web.ts # mixi.social Web の HTML を scrape して quotePostId を取得
ogp.ts # 外部 URL の OGP/Twitter Card フェッチャ(SSRF 対策付き)
parse.ts # mixi2 ポスト URL から UUID / handle を抽出
text.ts # URL/メンション/ハッシュタグの簡易セグメンタ
components/
Snippet.astro # ポストカード本体
SnippetPage.astro # /snippets, /posts, /@user/posts, /user/posts で共有される完全ページ
PostText.astro
PostMedia.astro # 1〜4 枚レイアウト
QuotedPost.astro # 引用ポストの compact ネストカード
OgpCard.astro # 外部 URL の OGP カード
SnippetFallback.astro # 404/削除/エラー用フォールバック
EmbedBuilder.svelte # ルートページのインタラクティブ UI(Svelte island)
pages/
index.astro # ルートページ
snippets/[id].astro # iframe canonical
snippets/posts/[id].astro # iframe canonical / ユーザー省略形
snippets/@[user]/posts/[id].astro # iframe canonical / @ あり
snippets/[user]/posts/[id].astro # iframe canonical / @ なし
posts/[id].astro # 短縮シェア URL / ユーザー指定なし
@[user]/posts/[id].astro # 短縮シェア URL / @ あり
[user]/posts/[id].astro # 短縮シェア URL / @ なし
api/debug/[id].ts # 開発用デバッグ JSON
- Astro 6 (SSR モード)
- Svelte 5 (islands)
- mixi2-js
- @astrojs/vercel (Node Serverless)
- メモリキャッシュ: ポスト 5 分、ユーザー 5 分、OGP 1 時間、引用 ID scrape 1 時間
- CDN キャッシュ:
/snippets/{id}系のレスポンスにCache-Control: public, s-maxage=300, stale-while-revalidate=86400を付与し、Vercel Edge Network にもキャッシュ - 静的アセット:
/embed.jsと/_astro/*は immutable / 長期キャッシュ
API 呼び出しは「最初の 1 ポスト × 5 分」に抑えられ、以後は CDN/メモリでヒットします。
| 種類 | 検出方法 |
|---|---|
| リプライ | Post.inReplyToPostId を使用(公開 API がそのまま返す) |
| 引用 | 公開 Application API は返さないため、mixi.social の HTML を 1 回 scrape して quotePostId を抽出(src/lib/mixi2-web.ts)。テキストに https://mixi.social/posts/... が含まれる場合はそちらも引用扱い。 |
mixi2 公式 API の Post proto に quoted_post_id が追加された暁には mixi2-web.ts は不要になり、fetchPost から直接読めるようになります。
- カードの見た目: src/components/Snippet.astro の
<style>ブロックで CSS 変数(--color-fg等)を編集 - ルートページの見た目: src/pages/index.astro の
:rootブロック - キャッシュ TTL: src/lib/mixi2.ts の
POST_CACHE_TTL_MS、および src/components/SnippetPage.astro のs-maxage