0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

読後感で本を選ぶ、エンジニア・知的好奇心層向けの電子書籍ストア。
「泣ける」「知性が磨かれる」今の気分にぴったりの一冊を AI が提案する。

OCI
Vector
Go
Next.js
Auth0
Stripe
Terraform


コアコンセプト

01 · 読後感ベクトル推薦

書籍のあらすじを OCI Generative AI(Cohere embed-multilingual-v3.0) で 1024 次元のベクトルに変換。「泣ける」「高揚感」「深い洞察」などの気分プリセットとの Cosine Similarity で上位 10 件をリアルタイム推薦する。プリセットは管理画面から追加・編集が可能。

02 · Kubernetes ゼロ運用

Container Instances + OCI Functions のサーバーレス構成でインフラ管理を最小化。PDF 抽出バッチは常駐 Worker(Container Instances)、Embedding 生成はイベント駆動の OCI Functions と用途別に使い分けることで、コスト効率と応答速度を両立する。

03 · PDF URL 完全隠蔽(BFF パターン)

Object Storage の Pre-signed URL(有効期限 5 分)は BFF 内部でのみ使用し、フロントエンドに一切露出しない。全 PDF はブラウザの PDF ビューア経由でストリーム配信し、ダウンロードを抑制する。

04 · Auth0 で認証をゼロ実装

Google / GitHub SSO・MFA・不審ログイン検知(Anomaly Detection)を Auth0 に完全委譲。JWKS 検証は MicahParks/keyfunc で RSA 公開鍵パースと自動更新を行う。アプリはコア機能の開発に集中できる設計判断。


アーキテクチャー全体図

oci.jpg


技術スタック

技術 備考
Database OCI Autonomous DB 23ai ECPU ベース・自動スケール・プライベートエンドポイント
AI / Embedding OCI Generative AI Cohere embed-multilingual-v3.0(日本語対応)
Vector Search HNSW Index + COSINE VECTOR_DISTANCE() で上位 10 件を高速取得
Backend API Go + Gin Instance Principal 認証・Graceful Shutdown
Frontend / BFF Next.js 14 App Router / Server Components / ISR(60s)
PDF Worker Node.js + pdf-lib 先頭 10 ページ抽出・ウォーターマーク付与
Auth Auth0 SSO(Google / GitHub)/ MFA / Anomaly Detection
Payment Stripe Checkout Webhook 署名検証・冪等処理
Storage OCI Object Storage PDF / プレビュー / 書影(3 バケット)
Gateway OCI API Gateway レートリミット・JWT 検証
IaC Terraform Container Instances / Functions / API Gateway

書籍登録から推薦までの流れ

1. 管理者が PDF をアップロード
   POST /admin/books → Object Storage に保存 → DB に PENDING レコード作成

2. Worker が先頭 10 ページを自動抽出
   Container Instances 内の常駐 Worker が FOR UPDATE SKIP LOCKED で検知
   pdf-lib でプレビュー PDF を生成しウォーターマーク付与 → preview バケットへ保存

3. OCI Functions が Embedding を生成
   あらすじを Cohere embed-multilingual-v3.0 で 1024 次元ベクトルに変換
   Books.experience_vector に UPDATE

4. ユーザーが気分を選んで推薦を受け取る
   MoodPresets のプリセットベクトルと VECTOR_DISTANCE(COSINE) で上位 10 件を返す
   Next.js ISR でキャッシュ済みのため高速レスポンス

セキュリティ設計

対象 設計
PDF 配信 Pre-signed URL をフロントに露出しない。BFF が Object Storage からストリームして Content-Disposition: inline で返す
Stripe Webhook stripe-signature ヘッダを必ず検証。stripe_session_id の UNIQUE 制約で二重処理を防ぐ
JWT 検証 API Gateway と Go API の二段階で検証。keyfunc が JWKS を自動更新し kid ローテーションに追従
DB アクセス Autonomous DB はプライベートエンドポイント + mTLS 接続のみ許可
Object Storage PDF / Preview バケットは NoPublicAccess。書影バケットのみ公開読み取り

ディレクトリ構成

contextual-bookshelf-oci/
├── apps/
│   ├── api/          # Go + Gin バックエンド API
│   ├── bff/          # Next.js 14 BFF(App Router)
│   └── worker/       # Node.js PDF Worker(pdf-lib)
└── infra/
    ├── terraform/    # OCI インフラ定義(10 ファイル)
    └── scripts/      # deploy.sh / init_db.sh / rotate_secrets.sh

クイックスタート

# 1. 変数ファイルの準備
cd infra/terraform
cp terraform.tfvars.example terraform.tfvars
# terraform.tfvars を編集(OCI 認証情報・Auth0・Stripe を設定)

# 2. 機密情報を環境変数で設定
export TF_VAR_adb_admin_password='your-strong-password'
export TF_VAR_stripe_webhook_secret='whsec_xxxxx'
export OCI_TENANCY_NAMESPACE='your-namespace'
export OCI_COMPARTMENT_OCID='ocid1.compartment.oc1..xxxxx'
export OCI_REGION='ap-tokyo-1'

# 3. インフラ構築
terraform init && terraform apply

# 4. DB スキーマ初期化
./infra/scripts/init_db.sh

# 5. 全サービスをビルド・デプロイ
./infra/scripts/deploy.sh

インフラ概要

リソース 仕様
Region ap-tokyo-1
Container Instances 2 OCPU / 4 GB RAM(BFF / API / Worker 各 1 台)
Autonomous DB ECPU ベース・自動スケール / 1 TB
OCI Functions Embedding バッチ(256 MB / 120 s タイムアウト)
API Gateway Public エンドポイント / JWT 検証 / レートリミット

このプロジェクトは OCI のサーバーレス・AI ネイティブ構成の実験を兼ねている。
認証(Auth0)・決済(Stripe)・AI(OCI GenAI)をすべて外部サービスに委譲し、
アプリケーション層はコア機能である「読後感推薦」の実装に集中する設計判断を行った。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?