212 lines
9.1 KiB
JavaScript
212 lines
9.1 KiB
JavaScript
/**
|
||
* 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.js(SiteIdに${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("--- 送信されるBody(APIキーはマスク表示、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);
|
||
}
|
||
})();
|