ai-girlfriend-shared-lib
v1.0.2
Published
Shared library for AI girlfriend chat workers
Maintainers
Readme
共有ライブラリ(shared-lib)仕様書
🎯 概要
全Workerで使用する共通ライブラリ
R2認証ゲートウェイ経由でのセキュアなR2アクセスと、共通ユーティリティを提供します。
📚 提供ライブラリ
1. SecureR2Client
R2認証ゲートウェイ経由でのR2アクセスクライアント
基本使用法
import { SecureR2Client } from '../shared-lib/secure-r2-client.js';
const r2 = new SecureR2Client(
'auth-worker', // Worker名
env.AUTH_WORKER_R2_KEY, // 認証キー
env.R2_GATEWAY_URL // ゲートウェイURL
);
// ユーザーデータアクセス
const profile = await r2.getUserData('user_123', 'profile.json');
await r2.putUserData('user_123', 'profile.json', profileData);
await r2.deleteUserData('user_123', 'old_data.json');
// 共有データアクセス
const sharedData = await r2.getSharedData('gallery/character_456.json');
await r2.putSharedData('templates/template_789.json', templateData);2. 共通ユーティリティ
- バリデーション: メール、パスワード、ID等の検証
- 暗号化: パスワードハッシュ、JWT処理
- エラーハンドリング: 統一エラー形式
- ログ処理: 構造化ログ出力
🔧 SecureR2Client 詳細仕様
初期化
new SecureR2Client(workerName, authKey, gatewayUrl, options)パラメータ:
workerName: 呼び出し元Worker名(必須)authKey: Worker固有の認証キー(必須)gatewayUrl: R2認証ゲートウェイのURL(必須)options: オプション設定(オプション)
メソッド一覧
ユーザーデータ操作
// GET: ユーザーデータ取得
async getUserData(userId, path, options = {})
// 例: await r2.getUserData('user_123', 'profile.json')
// PUT: ユーザーデータ保存
async putUserData(userId, path, data, options = {})
// 例: await r2.putUserData('user_123', 'profile.json', profileData)
// DELETE: ユーザーデータ削除
async deleteUserData(userId, path, options = {})
// 例: await r2.deleteUserData('user_123', 'old_session.json')
// LIST: ユーザーデータ一覧
async listUserData(userId, prefix = '', options = {})
// 例: await r2.listUserData('user_123', 'characters/')共有データ操作
// GET: 共有データ取得
async getSharedData(path, options = {})
// 例: await r2.getSharedData('gallery/character_456.json')
// PUT: 共有データ保存
async putSharedData(path, data, options = {})
// 例: await r2.putSharedData('templates/new_template.json', data)
// DELETE: 共有データ削除
async deleteSharedData(path, options = {})
// 例: await r2.deleteSharedData('gallery/old_character.json')
// LIST: 共有データ一覧
async listSharedData(prefix = '', options = {})
// 例: await r2.listSharedData('gallery/')低レベルAPI
// 直接パス指定でのアクセス
async get(path, options = {})
async put(path, data, options = {})
async delete(path, options = {})
async list(prefix = '', options = {})パス自動生成
各Workerに応じて適切なパスプレフィックスを自動生成:
const PATH_PREFIXES = {
'auth-worker': 'auth',
'character-worker': 'characters',
'chat-worker': 'conversations',
'media-worker': 'images',
'billing-worker': 'billing'
};
// 例: auth-workerで getUserData('user_123', 'profile.json')
// → 実際のパス: 'auth/users/user_123/profile.json'エラーハンドリング
try {
const data = await r2.getUserData('user_123', 'profile.json');
} catch (error) {
if (error instanceof R2AuthError) {
// 認証エラー
console.error('Authentication failed:', error.message);
} else if (error instanceof R2NotFoundError) {
// データが存在しない
console.log('Profile not found for user_123');
} else if (error instanceof R2ForbiddenError) {
// アクセス権限なし
console.error('Access denied:', error.message);
} else {
// その他のエラー
console.error('R2 operation failed:', error.message);
}
}レスポンス形式
成功時
// GET操作
{
success: true,
data: {...}, // JSONデータ(自動パース済み)
metadata: {
etag: "abc123",
size: 1024,
lastModified: "2024-01-15T10:00:00Z"
}
}
// PUT操作
{
success: true,
path: "auth/users/user_123/profile.json",
etag: "def456",
size: 512
}
// DELETE操作
{
success: true,
path: "auth/users/user_123/old_data.json",
deleted: true
}
// LIST操作
{
success: true,
objects: [
{
key: "auth/users/user_123/profile.json",
size: 512,
lastModified: "2024-01-15T10:00:00Z"
}
],
truncated: false
}エラー時
{
success: false,
error: "Access denied to path: characters/users/user_123/",
code: "FORBIDDEN",
workerName: "auth-worker",
path: "characters/users/user_123/character.json",
requestId: "req_abc123"
}🛠️ 共通ユーティリティ
ValidationUtils
import { ValidationUtils } from '../shared-lib/validation-utils.js';
// メールアドレス検証
ValidationUtils.validateEmail('[email protected]'); // true
// パスワード検証
ValidationUtils.validatePassword('SecurePass123!'); // true
// ユーザーID検証
ValidationUtils.validateUserId('user_123'); // true
// 複合バリデーション
const result = ValidationUtils.validateUserRegistration({
email: '[email protected]',
password: 'SecurePass123!',
name: '山田太郎'
});
// { valid: true, errors: [] }CryptoUtils
import { CryptoUtils } from '../shared-lib/crypto-utils.js';
// パスワードハッシュ化
const hashedPassword = await CryptoUtils.hashPassword('plaintext');
// パスワード検証
const isValid = await CryptoUtils.verifyPassword('plaintext', hashedPassword);
// JWT生成
const token = await CryptoUtils.generateJWT({
userId: 'user_123',
exp: Date.now() + 86400000
}, secret);
// JWT検証
const payload = await CryptoUtils.verifyJWT(token, secret);LogUtils
import { LogUtils } from '../shared-lib/log-utils.js';
// 構造化ログ
LogUtils.info('User registered', {
userId: 'user_123',
email: '[email protected]',
timestamp: Date.now()
});
// エラーログ
LogUtils.error('R2 access failed', {
workerName: 'auth-worker',
path: 'auth/users/user_123/profile.json',
error: error.message
});
// パフォーマンスログ
LogUtils.perf('R2 operation', {
operation: 'GET',
path: 'auth/users/user_123/profile.json',
duration: 45
});ErrorUtils
import { ErrorUtils } from '../shared-lib/error-utils.js';
// 統一エラーレスポンス生成
const errorResponse = ErrorUtils.createErrorResponse(
400,
'VALIDATION_ERROR',
'Invalid email format',
{ field: 'email' }
);
// エラー分類
if (ErrorUtils.isRetryableError(error)) {
// リトライ可能なエラー
await retryOperation();
}
// エラーログ出力
ErrorUtils.logError(error, {
workerName: 'auth-worker',
operation: 'getUserData',
context: { userId: 'user_123' }
});📋 設定とカスタマイズ
デフォルト設定
const DEFAULT_OPTIONS = {
timeout: 10000, // 10秒タイムアウト
retries: 3, // 3回リトライ
retryDelay: 1000, // 1秒リトライ間隔
cacheEnabled: true, // レスポンスキャッシュ有効
cacheTTL: 300, // 5分キャッシュ
logLevel: 'info' // ログレベル
};Worker別カスタマイズ
// Worker固有の設定上書き
const r2 = new SecureR2Client('auth-worker', authKey, gatewayUrl, {
timeout: 5000, // 認証は高速にしたい
retries: 1, // 認証エラーはリトライしない
cacheEnabled: false // 認証データはキャッシュしない
});🧪 テスト用モック
モッククライアント
import { MockR2Client } from '../shared-lib/mock-r2-client.js';
// テスト環境用
const r2 = new MockR2Client();
// インメモリデータで動作
await r2.putUserData('user_123', 'profile.json', mockProfile);
const profile = await r2.getUserData('user_123', 'profile.json');テストヘルパー
import { TestHelpers } from '../shared-lib/test-helpers.js';
// テストデータ生成
const mockUser = TestHelpers.createMockUser();
const mockCharacter = TestHelpers.createMockCharacter();
// R2モック設定
TestHelpers.setupR2Mock({
'auth/users/user_123/profile.json': mockUser,
'characters/users/user_123/characters/char_456.json': mockCharacter
});📦 パッケージ構成
shared-lib/
├── src/
│ ├── secure-r2-client.js # メインクライアント
│ ├── validation-utils.js # バリデーション
│ ├── crypto-utils.js # 暗号化処理
│ ├── log-utils.js # ログ処理
│ ├── error-utils.js # エラー処理
│ ├── test-helpers.js # テストヘルパー
│ └── mock-r2-client.js # モッククライアント
├── tests/
│ ├── secure-r2-client.test.js
│ ├── validation-utils.test.js
│ ├── crypto-utils.test.js
│ └── integration.test.js
├── package.json
└── README.md🔄 使用例(Worker別)
Auth Worker
const r2 = new SecureR2Client('auth-worker', env.AUTH_WORKER_R2_KEY, env.R2_GATEWAY_URL);
// ユーザープロファイル操作
const profile = await r2.getUserData(userId, 'profile.json');
await r2.putUserData(userId, 'profile.json', updatedProfile);
// セッション管理
await r2.putUserData(userId, `sessions/${sessionId}.json`, sessionData);
const sessions = await r2.listUserData(userId, 'sessions/');Character Worker
const r2 = new SecureR2Client('character-worker', env.CHARACTER_WORKER_R2_KEY, env.R2_GATEWAY_URL);
// キャラクター管理
const character = await r2.getUserData(userId, `characters/${characterId}.json`);
await r2.putUserData(userId, `characters/${characterId}.json`, characterData);
// ギャラリー操作
await r2.putSharedData(`gallery/${sharedId}.json`, sharedCharacter);
const gallery = await r2.listSharedData('gallery/');Chat Worker
const r2 = new SecureR2Client('chat-worker', env.CHAT_WORKER_R2_KEY, env.R2_GATEWAY_URL);
// 会話履歴
const conversation = await r2.getUserData(userId, `conversations/${characterId}/${conversationId}.json`);
await r2.putUserData(userId, `conversations/${characterId}/${conversationId}.json`, conversationData);
// キャラクター情報(読み取り専用)
const character = await r2.get(`characters/users/${userId}/characters/${characterId}.json`);この共有ライブラリにより、全Workerで統一された安全なR2アクセスが実現できます。
