SetsunaのREST APIおよびSSE(Server-Sent Events)エンドポイントの仕様を定義します。
| 環境 | URL |
|---|---|
| 開発 | http://localhost:3000/api |
| 本番 | https://your-app.vercel.app/api |
すべてのAPIはJSON形式でレスポンスを返します。
{
"success": true,
"data": { ... }
}{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "エラーメッセージ"
}
}| コード | HTTPステータス | 説明 |
|---|---|---|
ROOM_NOT_FOUND |
404 | ルームが存在しないまたは期限切れ |
ROOM_EXPIRED |
410 | ルームの有効期限が切れている |
INVALID_ROOM_CODE |
400 | ルームコードの形式が不正 |
CONTENT_TOO_LONG |
400 | メッセージが10,000文字を超過 |
CONTENT_EMPTY |
400 | メッセージが空 |
RATE_LIMIT_EXCEEDED |
429 | レート制限を超過 |
INVALID_PASSWORD |
400/401 | パスワードが無効または未指定 |
ADMIN_REQUIRED |
401 | 管理者認証が必要 |
INTERNAL_ERROR |
500 | サーバー内部エラー |
- 制限: 1分あたり30リクエスト
- 対象: IPアドレスごと
- レスポンスヘッダー:
X-RateLimit-Limit: 制限値X-RateLimit-Remaining: 残りリクエスト数X-RateLimit-Reset: リセット時刻(Unix timestamp)
新しいルームを作成し、ルームコードを取得します。
POST /api/rooms
ボディは不要です。
{
"success": true,
"data": {
"room": {
"code": "ABCD23",
"expiresAt": "2024-12-22T10:30:00.000Z"
}
}
}| フィールド | 型 | 説明 |
|---|---|---|
room.code |
string | 6文字のルームコード(A-HJ-NP-Z, 2-9のみ) |
room.expiresAt |
string (ISO 8601) | 有効期限(作成から24時間後) |
curl -X POST http://localhost:3000/api/rooms指定したルームコードのルーム情報を取得します。
GET /api/rooms/{code}
| パラメータ | 型 | 説明 |
|---|---|---|
code |
string | 6文字のルームコード |
{
"success": true,
"data": {
"room": {
"id": "clq1234567890",
"code": "ABCD23",
"createdAt": "2024-12-21T10:30:00.000Z",
"expiresAt": "2024-12-22T10:30:00.000Z",
"messageCount": 5
}
}
}{
"success": false,
"error": {
"code": "ROOM_NOT_FOUND",
"message": "指定されたルームは存在しないか、有効期限が切れています"
}
}curl http://localhost:3000/api/rooms/ABCD23ルーム内のメッセージ一覧を取得します。
GET /api/rooms/{code}/messages
| パラメータ | 型 | 説明 |
|---|---|---|
code |
string | 6文字のルームコード |
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
after |
string | No | このID以降のメッセージのみ取得 |
limit |
number | No | 取得件数(デフォルト: 50、最大: 100) |
{
"success": true,
"data": {
"messages": [
{
"id": "clq1234567890",
"content": "共有したいテキスト",
"createdAt": "2024-12-21T10:30:00.000Z"
},
{
"id": "clq1234567891",
"content": "もう一つのメッセージ",
"createdAt": "2024-12-21T10:31:00.000Z"
}
],
"hasMore": false
}
}| フィールド | 型 | 説明 |
|---|---|---|
messages |
array | メッセージの配列 |
messages[].id |
string | メッセージID |
messages[].content |
string | メッセージ内容 |
messages[].createdAt |
string (ISO 8601) | 作成日時 |
hasMore |
boolean | さらにメッセージが存在するか |
# 全件取得
curl http://localhost:3000/api/rooms/ABCD23/messages
# 特定ID以降を取得
curl "http://localhost:3000/api/rooms/ABCD23/messages?after=clq1234567890"ルームに新しいメッセージを送信します。
POST /api/rooms/{code}/messages
| パラメータ | 型 | 説明 |
|---|---|---|
code |
string | 6文字のルームコード |
{
"content": "共有したいテキスト"
}| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
content |
string | Yes | メッセージ内容(1〜10,000文字) |
{
"success": true,
"data": {
"message": {
"id": "clq1234567892",
"content": "共有したいテキスト",
"createdAt": "2024-12-21T10:32:00.000Z"
}
}
}{
"success": false,
"error": {
"code": "CONTENT_TOO_LONG",
"message": "メッセージは10,000文字以内で入力してください"
}
}curl -X POST http://localhost:3000/api/rooms/ABCD23/messages \
-H "Content-Type: application/json" \
-d '{"content": "共有したいテキスト"}'Server-Sent Eventsを使用してリアルタイムにメッセージを受信します。
GET /api/sse/{code}
| パラメータ | 型 | 説明 |
|---|---|---|
code |
string | 6文字のルームコード |
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
event: connected
data: {"roomCode":"ABCD23","timestamp":1703155800000}
event: message
data: {"id":"clq1234567892","content":"共有したいテキスト","createdAt":"2024-12-21T10:32:00.000Z"}
event: ping
data: {"timestamp":1703155830000}
30秒ごとに送信され、接続を維持します。
const eventSource = new EventSource('/api/sse/ABCD23');
eventSource.addEventListener('connected', (event) => {
console.log('接続しました:', JSON.parse(event.data));
});
eventSource.addEventListener('message', (event) => {
const message = JSON.parse(event.data);
console.log('新しいメッセージ:', message);
});
eventSource.addEventListener('error', () => {
console.log('接続エラー。再接続を試みます...');
});
// クリーンアップ
eventSource.close();期限切れのルームを削除します。Vercel Cron Jobsから1時間ごとに呼び出されます。
POST /api/cleanup
| ヘッダー | 値 |
|---|---|
Authorization |
Bearer {CRON_SECRET} |
または、Vercel Cronからの呼び出し:
| ヘッダー | 値 |
|---|---|
x-vercel-cron |
1 |
{
"success": true,
"data": {
"deletedRooms": 15,
"executedAt": "2024-12-21T10:00:00.000Z"
}
}管理ダッシュボード用のAPI。すべて/api/adminパス配下。Cookie認証が必要なエンドポイントはadmin_token Cookieを確認します。
パスワード認証を行い、JWTトークンをCookieに設定します。
POST /api/admin/auth/login
{
"password": "your-admin-password"
}{
"success": true,
"data": {
"expiresAt": "2024-12-22T10:30:00.000Z"
}
}Cookie: admin_token (HttpOnly, Secure, SameSite=Strict, 24時間有効)
{
"success": false,
"error": {
"code": "INVALID_PASSWORD",
"message": "Invalid password"
}
}セッションを終了し、Cookie admin_token を削除します。
POST /api/admin/auth/logout
{
"success": true,
"data": {
"message": "Logged out successfully"
}
}ダッシュボード表示用の統計情報を取得します。
GET /api/admin/stats
Cookie: admin_token 必須
{
"success": true,
"data": {
"activeRooms": 42,
"totalMessages": 1234,
"roomsCreatedToday": 5,
"messagesCreatedToday": 67,
"dailyStats": [
{ "date": "2024-12-21", "rooms": 10, "messages": 50 },
{ "date": "2024-12-20", "rooms": 8, "messages": 42 }
]
}
}管理用のルーム一覧を取得します。
GET /api/admin/rooms
Cookie: admin_token 必須
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
page |
number | No | ページ番号(デフォルト: 1) |
search |
string | No | ルームコード検索 |
filter |
string | No | active / expired / all |
{
"success": true,
"data": {
"rooms": [
{
"code": "ABCD23",
"createdAt": "2024-12-21T10:30:00.000Z",
"expiresAt": "2024-12-22T10:30:00.000Z",
"messageCount": 5,
"isExpired": false
}
],
"pagination": {
"page": 1,
"totalPages": 5,
"totalItems": 42
}
}
}メッセージを含むルーム詳細を取得します。
GET /api/admin/rooms/{code}
Cookie: admin_token 必須
| パラメータ | 型 | 説明 |
|---|---|---|
code |
string | 6文字のルームコード |
{
"success": true,
"data": {
"room": {
"code": "ABCD23",
"createdAt": "2024-12-21T10:30:00.000Z",
"expiresAt": "2024-12-22T10:30:00.000Z",
"isExpired": false
},
"messages": [
{
"id": "clq1234567890",
"content": "共有したいテキスト",
"createdAt": "2024-12-21T10:30:00.000Z"
}
]
}
}指定したルームを強制削除します。
DELETE /api/admin/rooms/{code}
Cookie: admin_token 必須
| パラメータ | 型 | 説明 |
|---|---|---|
code |
string | 6文字のルームコード |
{
"success": true,
"data": {
"message": "Room deleted successfully"
}
}期限切れルームを即時削除します。
POST /api/admin/cleanup
Cookie: admin_token 必須
{
"success": true,
"data": {
"deletedRooms": 15
}
}// types/api.ts
// ルーム
interface Room {
id: string;
code: string;
createdAt: string;
expiresAt: string;
}
// メッセージ
interface Message {
id: string;
content: string;
createdAt: string;
}
// API レスポンス
interface ApiResponse<T> {
success: boolean;
data?: T;
error?: {
code: string;
message: string;
};
}
// ルーム作成レスポンス
type CreateRoomResponse = ApiResponse<{
room: Pick<Room, 'code' | 'expiresAt'>;
}>;
// ルーム取得レスポンス
type GetRoomResponse = ApiResponse<{
room: Room & { messageCount: number };
}>;
// メッセージ一覧レスポンス
type GetMessagesResponse = ApiResponse<{
messages: Message[];
hasMore: boolean;
}>;
// メッセージ送信レスポンス
type CreateMessageResponse = ApiResponse<{
message: Message;
}>;
// SSEイベント
type SSEEvent =
| { type: 'connected'; data: { roomCode: string; timestamp: number } }
| { type: 'message'; data: Message }
| { type: 'ping'; data: { timestamp: number } };
// 管理者統計
interface AdminStats {
activeRooms: number;
totalMessages: number;
roomsCreatedToday: number;
messagesCreatedToday: number;
dailyStats: { date: string; rooms: number; messages: number }[];
}
// 管理者用ルーム
interface AdminRoom {
code: string;
createdAt: string;
expiresAt: string;
messageCount: number;
isExpired: boolean;
}
// 管理者用メッセージ
interface AdminMessage {
id: string;
content: string;
createdAt: string;
}
// ページネーション
interface Pagination {
page: number;
totalPages: number;
totalItems: number;
}
// 管理者ログインレスポンス
type AdminLoginResponse = ApiResponse<{
expiresAt: string;
}>;
// 統計情報レスポンス
type AdminStatsResponse = ApiResponse<AdminStats>;
// ルーム一覧レスポンス
type AdminRoomsResponse = ApiResponse<{
rooms: AdminRoom[];
pagination: Pagination;
}>;
// ルーム詳細レスポンス
type AdminRoomDetailResponse = ApiResponse<{
room: Omit<AdminRoom, 'messageCount'>;
messages: AdminMessage[];
}>;