Pusher
デバッグコンソールでお試し送信できる
どうやらチャンネルにはライフタイムがあるらしい。30秒?
確かにちゃんと30秒でdisconnectedになった。state_changeで観測可能。
公式ドキュメントには秒数は書いていないが、タイムアウトされる旨は記載されていた
If you have this feature enabled and a connection does not subscribe to at least one private- or presence- channel, it will be kicked off after a timeout.
以下で見つけた
Pusher ダッシュボードのチャンネル アプリ設定で、誤って有効にしていませんかEnable authorized connections? これを有効にすると、30 秒以内にプライベート チャネルへのアクセスが許可されない場合、クライアントは切断されます。
チャンネルの暗号化
- 悪意のあるユーザーに同時接続数を埋められないようにするため
- チャンネルに誰が接続しているのかを把握するため
- private-
- presence-
presenceはprivateの拡張版で、ユーザーを「登録」することで誰が今接続しているのかを把握できるようになる。
公開APP_KEYとチャンネル名を知っていれば誰でもチャンネルをサブスクライブできる。なので例えば他のアプリでpusherを初期化して接続することも可能なはず。
そうなるとクラッカーに同時接続数を消費されるおそれがある。
privateでチャンネル作成すれば、signinしたユーザーだけが同時接続数にカウントされる?
private、presenceチャンネルの認証方法
import Pusher from 'pusher-js'
const pusher = new Pusher(process.env.NEXT_PUBLIC_PUSHER_KEY, {
cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER,
userAuthentication: {
endpoint: '/api/pusher/user-auth',
transport: 'ajax',
params: { hoge: 'fofo' },
headers: { cussus: 'kasutamudayo' },
paramsProvider: null,
headersProvider: null,
},
channelAuthorization: {
endpoint: '/api/pusher/auth',
transport: 'ajax',
params: { hoge: 'channel-fofo' },
headers: { cussus: 'channel-kasutamudayo' },
paramsProvider: null,
headersProvider: null,
},
})
export default pusher
ユーザー認証とチャンネル認証のそれぞれの設定がある。
presenceチャンネルの場合はチャンネル認証のみ(/api/pusher/auth)、privateチャンネルの場合はユーザー認証のみ(/api/pusher/user-auth)が走る
src/pages/api/pusher/auth.ts
import type { NextApiRequest, NextApiResponse } from 'next'
import { getServerSession } from 'next-auth'
import { authOptions } from '../auth/[...nextauth]'
import pusher from '@/libs/pusher/server'
import { auth } from '@/libs/pusher/util'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await getServerSession(req, res, authOptions)
if (!session) {
res.status(401).json({ result: false })
}
const { socket_id, channel_name } = req.body
const userId = session.user.id
const isAuth = auth(userId, channel_name)
if (isAuth) {
const presenceData = {
user_id: userId,
user_info: { name: session.user.name, image: session.user.image },
}
const authResponse = pusher.authorizeChannel(
socket_id,
channel_name,
presenceData
)
return res.send(authResponse)
}
return res.status(403).json({ result: false })
}
ユーザー認証の場合はクライアント側でpusher.signin()をすると/pusher/user-authにリクエストが飛ぶ。んでpresenceDataに{id: string, user_info: object}を加えてauthResponseを返せば認証完了。
認証したユーザーにメッセージを送りたい場合は、クライアント側でpusher.user.bind
をしておけばそこで受け取れる。なのでユーザー認証の場合はチャンネルいらん。
複数タブを開いてpusher-jsでnew Pusherをして、pusher.allChannels()をしても同一タブで作成したチャンネルしか表示されない。しかし、同名のpresence-チャンネルを作成し参加し、メンバー情報を見てみると他のタブでも情報は共有される。
当たり前だが、pusherプラットフォーム上で管理されているからである。
別タブでも1つのpusher(pusherプラットフォーム上のグローバルなpusher)を参照したいときはある。シンプルにチャットルーム機能を実装しようとしたら欲しくなる。
サーバー側で下記のようにグローバルpusherの状態を取得できる。
const c = await pusher
.get({
path: '/channels',
params: { info: 'user_count', filter_by_prefix: 'presence-' },
})
.then((res) => res.json())
console.log(c)
/*
{
channels: {
'presence-1': { user_count: 1 },
'presence-12': { user_count: 1 },
'presence-12k': { user_count: 2 },
'presence-12kj': { user_count: 1 },
'presence-12kjd': { user_count: 1 },
'presence-12kjdfff': { user_count: 1 },
'presence-clmhu3oxp000aa6qzy1mzcl50': { user_count: 1 },
'presence-fd': { user_count: 1 },
'presence-fdffff': { user_count: 1 },
'presence-hoge': { user_count: 1 },
'presence-hoge2': { user_count: 1 }
}
}
*/
タブを閉じても、そのタブで作ったチャンネルは削除されない。
ただしチャンネルに参加している人数が1人だけ(そのタブの人のみ)だった場合、チャンネルは削除される。
過去のメッセージは取得できない