posimai-root/docs/synology/SYNOLOGY_PHASE1_DEPLOY.md

11 KiB
Raw Blame History

Phase 1: Brain 本文全文保存 — Synology 側 実施手順(コピペ用)

目的: Reader から送られた本文を Brain API が受け取り DB に保存し、AI 分析で本文を使うようにする。

前提: Synology NAS に SSH で接続できること。Brain API と PostgreSQL がすでに動いていること。

補足: Synology の「Container Manager」は従来の「Docker」パッケージの後継です。中身は同じなので、SSH では docker コマンドが使えます。


あなたの環境docker ps の結果)について

コンテナ名 イメージ 用途の目安
posimai_api node:20-slim (8090) Brain API の可能性が高い
gitea_db postgres:15 Gitea 用 PostgreSQLBrain 用ではない)
その他 gitea, mcp_server, uptime-kuma 等 -

Brain 専用の PostgreSQL コンテナは一覧にありません。

  • posimai_api が Brain API で、その中で SQLite別の DB を使っている場合は、手順 1 の「PostgreSQL に接続」はそのままでは使えません
  • まず posimai_apiBrain APIのコードや設定を確認し、「どの DB に接続しているか」を把握する必要があります。

進め方の目安

  1. Brain 用に PostgreSQL を別コンテナで用意している場合
    → そのコンテナ名で手順 1 の「1-3. PostgreSQL に接続」を実行し、手順 1 → 2 の順で実施。

  2. Brain が SQLite など PostgreSQL 以外を使っている場合
    手順 1 はいったん飛ばし、手順 2API のコード修正)だけ先に実施してください。
    保存処理で full_text を扱うようにし、使っている DB のスキーマに full_text カラム(または相当の項目)を後から追加する形になります。
    SQLite なら、posimai_api のプロジェクト内のマイグレーションや、DB ファイルを開いて ALTER TABLE する方法などがあります。)

  3. どこに DB があるか分からない場合
    posimai_api の設定ファイル(例: .env / config.js / 環境変数)や、ソース内の pool / createPool / sqlite などの記述を確認すると、接続先が分かります。


手順 1: データベースにカラムを追加するBrain が PostgreSQL を使っている場合のみ)

1-1. Synology に SSH 接続

ssh admin@posimai-lab.tail72e846.ts.net

(パスワードを聞かれたら入力。ユーザー名が mai の場合は ssh mai@posimai-lab.tail72e846.ts.net

1-2. コンテナ名を確認する(重要)

手順書の posimai-brain-postgresです。お使いの環境ではコンテナ名が違う場合があります。

方法A: コマンドで一覧を表示

docker ps

表示された一覧の「NAMES」列で、PostgreSQL のコンテナ名を確認してください(例: postgres / brain-postgres / synology-postgres など)。

方法B: DSM の Container Manager で確認

  1. DSM にログイン → Container Manager を開く
  2. 「コンテナ」一覧で、PostgreSQL または Brain 用 DB のコンテナを探す
  3. コンテナ名(名前の列)をメモする

次の 1-3 で使うコマンドの コンテナ名 を、上で確認した名前に置き換えてください。

1-3. PostgreSQL に接続

Container ManagerDockerで PostgreSQL を動かしている場合:

docker exec -it コンテナ名 psql -U brain_user -d brain_db
  • コンテナ名 を 1-2 で確認した名前に置き換えます。
  • 例: コンテナ名が postgres なら
    docker exec -it postgres psql -U brain_user -d brain_db
  • ユーザー名・DB 名(brain_user / brain_db)が違う場合は、実際の設定に合わせて変更してください。

PostgreSQL を Docker 以外で動かしている場合NAS に直接インストール等):

psql -U brain_user -d brain_db

1-4. 以下の SQL をそのままコピーして貼り付け、Enter で実行

-- full_text カラムを追加(本文全文)
ALTER TABLE articles ADD COLUMN IF NOT EXISTS full_text TEXT;

-- オプション: 画像用(将来用)
ALTER TABLE articles ADD COLUMN IF NOT EXISTS images TEXT[];

-- 更新日時用
ALTER TABLE articles ADD COLUMN IF NOT EXISTS updated_at TIMESTAMP DEFAULT NOW();

-- 確認articles の構造が表示されればOK
\d articles

\d articles の結果に full_text が含まれていれば成功です。

1-5. PostgreSQL を終了

\q

手順 2: Brain API のコードを修正する

posimai_api が Brain API の場合、そのコンテナで動いているコードNAS 上でマウントしているフォルダ内の server.js など)を編集します。

Brain API の server.js(またはルーターを定義しているファイル)を編集します。

編集する場所の目安:

  • POST /save または POST /brain/api/save を処理しているブロック
  • GET /articles または GET /brain/api/articles を処理しているブロック

2-1. POST /save の置き換え

いまの post('/save', ...) そのブロックの終わりまで を、以下でまるごと置き換えしてください。

// POST /save — Reader から url, title, content, source を受け取り本文を保存
app.post('/save', authMiddleware, async (req, res) => {
    const { url, title, content, source } = req.body || {};
    if (!url || !title) {
        return res.status(400).json({ error: 'Missing required fields: url and title' });
    }
    try {
        let fullText = content || null;
        let meta = {};
        if (!fullText || fullText.trim().length === 0) {
            meta = await fetchOGP(url);  // 既存の OGP 取得関数
            fullText = meta.desc || '';
        } else {
            meta = await fetchOGP(url);
        }
        const ai = await analyzeWithGemini(title, fullText, url);  // 第2引数を fullText に変更
        const result = await pool.query(`
            INSERT INTO articles (user_id, url, title, full_text, summary, topics, source, reading_time, favicon, og_image, status, created_at, updated_at)
            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'inbox', NOW(), NOW())
            ON CONFLICT (user_id, url) DO UPDATE SET
                title = EXCLUDED.title,
                full_text = EXCLUDED.full_text,
                summary = EXCLUDED.summary,
                topics = EXCLUDED.topics,
                reading_time = EXCLUDED.reading_time,
                favicon = EXCLUDED.favicon,
                og_image = EXCLUDED.og_image,
                updated_at = NOW()
            RETURNING id
        `, [req.userId, url, title, fullText, ai.summary, ai.topics, source || 'reader', ai.readingTime, meta.favicon || null, meta.ogImage || null]);
        const articleId = result.rows[0].id;
        return res.json({ success: true, articleId, fullTextSaved: !!fullText, textLength: fullText?.length || 0 });
    } catch (error) {
        console.error('[Brain API] Save failed:', error);
        return res.status(500).json({ error: 'Failed to save article', message: error.message });
    }
});

あわせて確認すること:

  • analyzeWithGemini第2引数を、これまでの「短い説明」ではなく 本文全文fullText に変更する。
  • fetchOGP はそのまま利用して問題ありません。

2-2. GET /articles の置き換え

いまの get('/articles', ...) そのブロックの終わりまで を、以下でまるごと置き換えしてください。

// GET /articles — 一覧取得時に full_text も返すBrain UI で本文表示するため)
app.get('/articles', authMiddleware, async (req, res) => {
    try {
        const { rows } = await pool.query(`
            SELECT id, url, title, full_text, summary, topics, source, reading_time, favicon, og_image, status, created_at, updated_at
            FROM articles
            WHERE user_id = $1
            ORDER BY created_at DESC
        `, [req.userId]);
        return res.json({
            articles: rows.map(row => ({
                id: row.id,
                url: row.url,
                title: row.title,
                fullText: row.full_text,
                summary: row.summary,
                topics: row.topics,
                source: row.source,
                readingTime: row.reading_time,
                favicon: row.favicon,
                ogImage: row.og_image,
                status: row.status,
                createdAt: row.created_at,
                updatedAt: row.updated_at
            }))
        });
    } catch (error) {
        console.error('[Brain API] Failed to fetch articles:', error);
        return res.status(500).json({ error: 'Failed to fetch articles', message: error.message });
    }
});

注意: お使いのコードが app ではなく router の場合は、上記の app.post / app.getrouter.post / router.get に読み替えてください。


手順 3: API サーバーを再起動する

Docker で Brain API を動かしている場合:

docker restart posimai-brain-api
docker logs -f posimai-brain-api

ログにエラーが出ていなければ成功です。Ctrl+C でログ表示を終了できます。

Docker を使っていない場合:
Brain API を起動している方法systemd や手動実行など)に合わせて再起動してください。


手順 4: 動作確認

  1. Reader で記事を開き、「Brainに保存」を押す。
  2. ブラウザの開発者ツールF12の「ネットワーク」で、/save へのレスポンスを確認する。
    • fullTextSaved: true かつ textLength が数百以上なら、本文が送れて保存されています。
  3. Brain の一覧を開き、該当記事の「本文を読む」が出ていれば、API と DB の対応は問題ありません。

うまくいかないとき

  • 「column full_text does not exist」
    → 手順 1 の SQL が実行されていないか、別の DB を見ている可能性があります。もう一度 1-21-4 を実行してください。
  • 「analyzeWithGemini is not a function」
    analyzeWithGemini の定義で、第2引数が「本文」になるように変更してください従来の短い説明用引数を fullText に変更)。
  • Reader から保存しても Brain に反映されない
    → Synology の Brain API のログ(docker logs posimai-brain-api)でエラーが出ていないか確認してください。

次のステップ: Brain のフロントVercel などは、すでに「本文を読む」で保存済み本文を表示できるように修正済みです。Synology 側の上記対応が完了すれば Phase 1 は完了です。