ken_nogi/ClaudePleasanter/MSS回覧板システム/.claude/js/get-site-config.js
Kenichiro NOGI ed33892f08 chore: 作業中の変更を整理しコミット(複数プロジェクト分)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-02 11:09:50 +09:00

189 lines
7.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* get-site-config.js
* ------------------------------------------------------------
* プリザンターの指定サイトの構成情報SiteSettings等を取得し、
* ./configs/ 配下にタイムスタンプ付きJSONとして保存します。
*
* 使い方:
* node get-site-config.js
* node get-site-config.js ./config.json ← 設定ファイルを明示指定する場合
*
* 事前準備:
* 1. config.example.json を config.json にコピーし、
* BaseUrl / SiteId / ApiKey を実環境の値に書き換えてください。
* SiteId はカンマ区切りで複数指定可能です(例: "480111,438166,96235")。
* 1件のみの場合は従来通り数値例: 480111でも構いません。
* 2. Node.js 18以降組み込みfetchを使用
*
* 参照した公式マニュアル:
* - 開発者向け機能APIサイト操作サイト更新
* https://pleasanter.org/manual/api-site-update
* - 開発者向け機能APIサイト操作サイト設定の更新部分追加/更新/削除)
* https://pleasanter.org/ja/manual/api-update-sitesettings
*
* 注意:
* プリザンターのバージョンにより get のレスポンス構造
* SiteSettingsが Response.Site 配下にあるか等)が異なる場合があります。
* 初回実行時はコンソールに出力されるレスポンスのトップレベルキーを確認し、
* 想定通りの構造か確認してください。
* ------------------------------------------------------------
*/
const fs = require("fs");
const path = require("path");
const { extractSiteConfig } = require("./extract-site-config");
const { ensureSiteDir, sitesettingsDir } = require("./site-paths");
// config.json の SiteId数値 / カンマ区切り文字列 / 配列)を
// 個々のSiteId文字列の配列に正規化する。
function parseSiteIds(rawSiteId) {
const list = Array.isArray(rawSiteId) ? rawSiteId : String(rawSiteId).split(",");
return list
.map((id) => String(id).trim())
.filter((id) => id.length > 0);
}
// 1サイト分の取得・保存・抽出処理
async function fetchAndSaveSite(siteId, { BaseUrl, ApiKey, ApiVersion }) {
const url = `${BaseUrl.replace(/\/+$/, "")}/api/items/${siteId}/getsite`;
const body = {
ApiVersion: ApiVersion || "1.1",
ApiKey: ApiKey,
};
console.log(`[INFO] 取得先: ${url}`);
console.log(`[INFO] SiteId: ${siteId}`);
let response;
try {
response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
} catch (err) {
console.error("[エラー] リクエスト送信に失敗しました:", err.message);
console.error("BaseUrlの疎通・ネットワーク設定を確認してください。");
return { ok: false };
}
const text = await response.text();
if (!response.ok) {
console.error(`[エラー] HTTP ${response.status} ${response.statusText}`);
console.error(text);
return { ok: false };
}
let json;
try {
json = JSON.parse(text);
} catch (err) {
console.error("[エラー] レスポンスがJSONとして解析できませんでした。");
console.error(text);
return { ok: false };
}
// レスポンス構造の確認用ログ(初回はここを見て構造を把握してください)
console.log("[INFO] レスポンス トップレベルキー:", Object.keys(json));
if (json.Response) {
console.log("[INFO] Response配下のキー:", Object.keys(json.Response));
}
// 保存: configs/site-{siteId}_{サイト名}/sitesettings/ 配下へ
const configsDir = path.join(path.join(__dirname, "..", ".."), "configs");
const title = json?.Response?.Data?.Title;
const siteDir = ensureSiteDir(configsDir, siteId, title);
const outDir = sitesettingsDir(siteDir);
const timestamp = new Date()
.toISOString()
.replace(/[:.]/g, "-");
const outFile = path.join(outDir, `site-${siteId}_${timestamp}.json`);
fs.writeFileSync(outFile, JSON.stringify(json, null, 2), "utf-8");
// 常に「最新」ファイルも別名で保持Claudeへのアップロード用に固定名があると便利
const latestFile = path.join(outDir, `site-${siteId}_latest.json`);
fs.writeFileSync(latestFile, JSON.stringify(json, null, 2), "utf-8");
console.log(`[OK] 保存しました: ${outFile}`);
console.log(`[OK] 最新版としても保存しました: ${latestFile}`);
// 取得と同時に Scripts/Styles/ServerScripts/Processes/ガイドHTML も抽出する
console.log("\n[INFO] 続けて設定の個別ファイル抽出を実行します...");
extractSiteConfig(latestFile);
// 権限継承先の追跡: InheritPermission が自サイト以外を指している場合、
// 実際の権限はそちらのサイトにあるため、そのサイトも同様に取得する。
const inheritPermission = json?.Response?.Data?.InheritPermission;
return { ok: true, inheritPermission };
}
async function main() {
const configPath = process.argv[2] || path.join(path.join(__dirname, "..", ".."), "config.json");
if (!fs.existsSync(configPath)) {
console.error(`[エラー] 設定ファイルが見つかりません: ${configPath}`);
console.error("config.example.json をコピーして config.json を作成してください。");
process.exit(1);
}
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
const { BaseUrl, SiteId, ApiKey, ApiVersion } = config;
if (!BaseUrl || !SiteId || !ApiKey) {
console.error("[エラー] config.json に BaseUrl / SiteId / ApiKey を設定してください。");
process.exit(1);
}
const siteIds = parseSiteIds(SiteId);
if (siteIds.length === 0) {
console.error("[エラー] config.json の SiteId から有効なサイトIDを取得できませんでした。");
process.exit(1);
}
// 権限継承先の追跡: 指定サイトの取得後、InheritPermission が自サイト以外を
// 指していれば、そのサイトも同じ要領でキューに追加して取得する
// (権限の実体は継承元サイド側にあるため)。
const queue = [...siteIds];
const visited = new Set();
const results = [];
while (queue.length > 0) {
const siteId = String(queue.shift());
if (visited.has(siteId)) continue;
visited.add(siteId);
console.log(`\n==== [${results.length + 1}] SiteId: ${siteId} ====`);
const { ok, inheritPermission } = await fetchAndSaveSite(siteId, { BaseUrl, ApiKey, ApiVersion });
results.push({ siteId, ok });
if (
ok &&
inheritPermission != null &&
String(inheritPermission) !== "0" &&
String(inheritPermission) !== siteId &&
!visited.has(String(inheritPermission))
) {
console.log(`[INFO] SiteId ${siteId} は権限をSiteId ${inheritPermission} から継承しています。継承元も取得します。`);
queue.push(String(inheritPermission));
}
}
console.log("\n==== 取得結果まとめ ====");
for (const { siteId, ok } of results) {
console.log(` SiteId ${siteId}: ${ok ? "OK" : "失敗"}`);
}
console.log("\n次のステップ: latest.jsonをClaudeのチャットにアップロードして仕様書化を依頼するか、");
console.log("configs/site-{SiteId}/ 配下のファイルを直接編集・指示して修正してください。");
if (results.some((r) => !r.ok)) {
process.exit(1);
}
}
main();