ken_nogi/ClaudePleasanter/★マスターシート/.claude/js/apply-to-site-496626.js
Kenichiro NOGI ed33892f08 chore: 作業中の変更を整理しコミット(複数プロジェクト分)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-02 11:09:50 +09:00

212 lines
9.1 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.

/**
* apply-to-site-496626.js
* ------------------------------------------------------------
* configs/site-480111_updated.json のSiteSettingsを、SiteId 496626へ
* まるごと上書き適用するupdatesite / Mode:"full")。
*
* 通常このプロジェクトの apply-site-config.js はドライラン専用(実送信しない)だが、
* 本スクリプトは今回のタスクに限りユーザーが明示的に許可した「専用の直接実行スクリプト」
* であり、--execute 指定時のみ実際にAPIへPOSTするcurlは使わない
*
* Permissionsは送信ボディに含めない。496626は権限をInheritPermissionで
* 継承しているため、直接の書き込み対象ではない(権限変更は apply-permission-grant.js
* を参照。Title/ReferenceType/ParentId/InheritPermissionは496626自身の
* 現状値configs/site-496626_latest.jsonから取得し、SiteSettingsのみ
* 480111由来の変更後内容で置き換える。
*
* 差分プレビュー・送信結果は、SiteId 496626 の modify/{リクエストラベル}/ フォルダへ保存する
* restructure-site-480111.js が作成したフォルダを --request= で指定するか、省略時は
* 最新のmodifyフォルダを自動的に使う
*
* 使い方:
* node apply-to-site-496626.js … 差分表示・アーティファクト生成のみ(送信なし)
* node apply-to-site-496626.js --execute … 上記に加えて実際に送信する
* node apply-to-site-496626.js --request=ラベル … 対象のmodifyフォルダを明示指定
* ------------------------------------------------------------
*/
const fs = require("fs");
const path = require("path");
const { findSiteDir, latestJsonPath, newModifyDir } = require("./site-paths");
const TARGET_SITE_ID = 496626;
const baseDir = path.join(__dirname, "..", "..");
const configsDir = path.join(baseDir, "configs");
const execute = process.argv.includes("--execute");
const requestArg = process.argv.find((a) => a.startsWith("--request="));
const requestLabel = requestArg ? requestArg.split("=")[1] : null;
function loadJson(p, required = true) {
if (!fs.existsSync(p)) {
if (required) {
console.error(`[エラー] ファイルが見つかりません: ${p}`);
process.exit(1);
}
return null;
}
return JSON.parse(fs.readFileSync(p, "utf-8"));
}
function maskApiKey(key) {
if (!key) return key;
return key.length > 8 ? key.slice(0, 4) + "****" + key.slice(-4) : "****";
}
function timestamp() {
return new Date().toISOString().replace(/[:.]/g, "-");
}
function diffTopLevel(before, after) {
const lines = [];
const afterKeys = Object.keys(after || {});
let changed = false;
for (const key of afterKeys) {
const b = JSON.stringify(before?.[key]);
const a = JSON.stringify(after?.[key]);
if (b !== a) {
changed = true;
const bLen = Array.isArray(before?.[key]) ? `(${before[key].length}件)` : "";
const aLen = Array.isArray(after?.[key]) ? `(${after[key].length}件)` : "";
lines.push(` [変更] SiteSettings.${key} ${bLen}${aLen}`);
}
}
if (!changed) lines.push(" 差分なし");
return lines;
}
// 対象のmodifyフォルダを解決する--request指定があればそれ、無ければ最新のものを使う
const targetSiteDir = findSiteDir(configsDir, TARGET_SITE_ID);
if (!targetSiteDir) {
console.error(`[エラー] SiteId ${TARGET_SITE_ID} のフォルダが見つかりません。先に node get-site-config.js を実行してください。`);
process.exit(1);
}
function resolveModifyDir() {
if (requestLabel) return newModifyDir(targetSiteDir, requestLabel);
const modifyRoot = path.join(targetSiteDir, "modify");
if (!fs.existsSync(modifyRoot)) {
console.error(`[エラー] modifyフォルダがまだありません。先に node restructure-site-480111.js を実行してください。`);
process.exit(1);
}
const dirs = fs.readdirSync(modifyRoot).filter((n) => fs.statSync(path.join(modifyRoot, n)).isDirectory());
if (dirs.length === 0) {
console.error(`[エラー] modifyフォルダの中身が空です: ${modifyRoot}`);
process.exit(1);
}
dirs.sort();
return path.join(modifyRoot, dirs[dirs.length - 1]);
}
const modifyDir = resolveModifyDir();
console.log(`[INFO] modifyフォルダ: ${modifyDir}`);
const config = loadJson(path.join(baseDir, "config.json"));
const updatedPath = path.join(modifyDir, "site-480111_updated.json");
const updatedJson = loadJson(updatedPath);
const modifiedSiteSettings = updatedJson?.Response?.Data?.SiteSettings;
if (!modifiedSiteSettings) {
console.error(`[エラー] ${updatedPath} からSiteSettingsを取得できませんでした。先に node restructure-site-480111.js を実行してください。`);
process.exit(1);
}
const currentPath = latestJsonPath(configsDir, TARGET_SITE_ID);
const currentJson = loadJson(currentPath, false);
const currentData = currentJson?.Response?.Data;
if (!currentData) {
console.error(`[エラー] ${currentPath} が見つかりません。先に node get-site-config.jsSiteIdに${TARGET_SITE_ID}を含めて)を実行してください。`);
process.exit(1);
}
const url = `${config.BaseUrl.replace(/\/+$/, "")}/api/items/${TARGET_SITE_ID}/updatesite`;
const body = {
ApiVersion: config.ApiVersion || "1.1",
ApiKey: config.ApiKey,
SiteId: TARGET_SITE_ID,
Title: currentData.Title,
ReferenceType: currentData.ReferenceType,
ParentId: currentData.ParentId,
InheritPermission: currentData.InheritPermission,
SiteSettings: modifiedSiteSettings,
};
console.log("========================================");
console.log(` SiteId ${TARGET_SITE_ID} への適用${execute ? "(実行)" : "(プレビューのみ)"}`);
console.log("========================================");
console.log(`URL : ${url}`);
console.log(`Title : ${currentData.Title}`);
console.log(`InheritPermission: ${currentData.InheritPermission}(権限は今回変更しません)`);
console.log("");
console.log("--- SiteSettings 差分496626現状 → 480111由来の変更後 ---");
const diffLines = diffTopLevel(currentData.SiteSettings, modifiedSiteSettings);
diffLines.forEach((l) => console.log(l));
console.log("");
const newItem = (modifiedSiteSettings.Columns || []).find(
(c) => c.ColumnName === "Date031" && c.GridLabelText === "解体紹介料申請"
);
console.log(`新規追加項目: ${newItem ? "解体紹介料申請 (Date031, 担当:" + newItem.Description + ")" : "(見つかりません)"}`);
console.log(`Columns件数 : ${(currentData.SiteSettings?.Columns || []).length}${modifiedSiteSettings.Columns.length}`);
console.log(`Sections件数: ${(currentData.SiteSettings?.Sections || []).length}${modifiedSiteSettings.Sections.length}`);
console.log("");
const maskedBody = { ...body, ApiKey: maskApiKey(body.ApiKey) };
console.log("--- 送信されるBodyAPIキーはマスク表示、SiteSettingsは件数のみ表示 ---");
console.log(
JSON.stringify(
{ ...maskedBody, SiteSettings: `(省略: Columns ${modifiedSiteSettings.Columns.length}件 等)` },
null,
2
)
);
const ts = timestamp();
const diffOutPath = path.join(modifyDir, `site-${TARGET_SITE_ID}_apply_diff_${ts}.txt`);
const diffText = [
`SiteId ${TARGET_SITE_ID} 適用差分プレビュー (${ts})`,
`URL: ${url}`,
`Title: ${currentData.Title}`,
`InheritPermission: ${currentData.InheritPermission}(今回は権限変更なし)`,
"",
"--- SiteSettings差分 ---",
...diffLines,
"",
`新規追加項目: ${newItem ? "解体紹介料申請 (Date031, 担当:" + newItem.Description + ")" : "(見つかりません)"}`,
`Columns件数 : ${(currentData.SiteSettings?.Columns || []).length}${modifiedSiteSettings.Columns.length}`,
`Sections件数: ${(currentData.SiteSettings?.Sections || []).length}${modifiedSiteSettings.Sections.length}`,
].join("\n");
fs.writeFileSync(diffOutPath, diffText, "utf-8");
console.log(`\n[OK] 差分プレビューを保存しました: ${diffOutPath}`);
if (!execute) {
console.log("\n[注意] --execute が指定されていないため、送信は行っていません。");
console.log("内容を確認の上、問題なければ次を実行してください: node apply-to-site-496626.js --execute");
process.exit(0);
}
(async () => {
console.log("\n実際に送信します...");
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const text = await res.text();
const resultOutPath = path.join(modifyDir, `site-${TARGET_SITE_ID}_apply_result_${ts}.json`);
fs.writeFileSync(resultOutPath, text, "utf-8");
console.log(`HTTP ${res.status} ${res.statusText}`);
console.log(`[OK] 応答を保存しました: ${resultOutPath}`);
try {
const json = JSON.parse(text);
if (json.StatusCode === 200) {
console.log("[OK] 適用に成功しました。");
} else {
console.error("[エラー] 適用に失敗した可能性があります。応答内容を確認してください。");
}
} catch {
console.log(text);
}
})();