ken_nogi/Pleasanter/.claude/js/site-scripts/ケアセブン(リハ)/add-checkg-column.js
Kenichiro NOGI ce58cb4be4 初回コミット: dev配下(NodeSrv/Pleasanter等)をGitea管理下に統合
GitHub(nextgroup2706/ken_nogi)は今後使わず自社Gitea運用に切替え。
NodeSrvは旧リポジトリの履歴を破棄しファイルのみ統合(Dokploy用サービスアカウントは
別途mygit-admin/NodeSrv.gitに履歴あり)。notepmエクスポート(12GB)とPleasanter
インストーラzip(208MB)はサイズが大きいため.gitignoreで除外。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 15:37:06 +09:00

191 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.

/**
* add-checkg-column.js
* ------------------------------------------------------------
* ケアセブン(リハ) プロジェクト専用。
* 周回予定・個別予定・例外予定日 計6サイト【A】482696/482701/482700、
* 【B】505010/505011/505012のSiteSettings.Columnsへ「CheckG」列
* A由来フラグを新規追加し、updatesiteMode:"full")で適用する。
*
* デフォルト値による自動ON/OFFは行わない移行コピー実行時にAPIで明示的に
* CheckG=trueをセットするのみで足りるため。列は非表示・NoWrapで統一する。
*
* SiteId 482696【A】周回予定はユーザーが本番管理画面から手動で追加済み
* (実際の設定値: DefaultInput:"false", Hide:true, NoWrap:true
* 本スクリプトはその実物の設定に合わせて残り5サイトへ同一定義を追加する。
* 482696は既存列チェックによりスキップされる冪等
*
* 使い方:
* node add-checkg-column.js … 差分表示・アーティファクト生成のみ(送信なし)
* node add-checkg-column.js --execute … 上記に加えて実際に送信する
* ------------------------------------------------------------
*/
const fs = require("fs");
const path = require("path");
const { findSiteDir, latestJsonPath, newModifyDir } = require("../../site-paths");
const { REPO_ROOT, loadServerConfig } = require("../../resolve-project");
const PROJECT_NAME = "ケアセブン(リハ)";
const ENV = "production";
const baseDir = path.join(REPO_ROOT, PROJECT_NAME);
const configsDir = path.join(baseDir, "configs", ENV);
const execute = process.argv.includes("--execute");
const REQUEST_LABEL = "checkg-column";
const TARGETS = [
{ siteId: 482696, name: "【A】周回予定" },
{ siteId: 482701, name: "【A】個別予定" },
{ siteId: 482700, name: "【A】例外予定日" },
{ siteId: 505010, name: "【B】周回予定" },
{ siteId: 505011, name: "【B】個別予定" },
{ siteId: 505012, name: "【B】例外予定日" },
];
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 buildUpdatedSiteSettings(currentSiteSettings) {
const ss = JSON.parse(JSON.stringify(currentSiteSettings));
ss.Columns = ss.Columns || [];
if (ss.Columns.some((c) => c.ColumnName === "CheckG")) {
console.log(" [注意] CheckG列は既に存在します。変更なしとして扱います。");
return { siteSettings: ss, alreadyExists: true };
}
// SiteId 482696へユーザーが本番管理画面から手動追加した実物と同一の定義
const newColumn = { ColumnName: "CheckG", LabelText: "A由来フラグ", DefaultInput: "false", NoWrap: true, Hide: true };
ss.Columns.push(newColumn);
return { siteSettings: ss, alreadyExists: false };
}
async function processSite(target) {
const { siteId, name } = target;
console.log("========================================");
console.log(` SiteId ${siteId} (${name}) ${execute ? "(実行)" : "(プレビューのみ)"}`);
console.log("========================================");
const siteDir = findSiteDir(configsDir, siteId);
if (!siteDir) {
console.error(`[エラー] SiteId ${siteId} のフォルダが見つかりません。先に node get-site-config.js を実行してください。`);
process.exit(1);
}
const currentPath = latestJsonPath(configsDir, siteId);
const currentJson = loadJson(currentPath, false);
const currentData = currentJson?.Response?.Data;
if (!currentData) {
console.error(`[エラー] ${currentPath} が見つかりません。`);
process.exit(1);
}
const modifyDir = newModifyDir(siteDir, REQUEST_LABEL);
const ts = timestamp();
// 変更前スナップショット保存
fs.writeFileSync(
path.join(modifyDir, `before_site-${siteId}_latest.json`),
JSON.stringify(currentJson, null, 2),
"utf-8"
);
const { siteSettings: updatedSiteSettings, alreadyExists } = buildUpdatedSiteSettings(currentData.SiteSettings);
if (alreadyExists) {
console.log(` [スキップ] SiteId ${siteId} は既にCheckG列を持つため送信しません。`);
return { modifyDir, ts, skipped: true };
}
const config = loadServerConfig(ENV);
const url = `${config.BaseUrl.replace(/\/+$/, "")}/api/items/${siteId}/updatesite`;
const body = {
ApiVersion: config.ApiVersion || "1.1",
ApiKey: config.ApiKey,
SiteId: siteId,
Title: currentData.Title,
ReferenceType: currentData.ReferenceType,
ParentId: currentData.ParentId,
InheritPermission: currentData.InheritPermission,
SiteSettings: updatedSiteSettings,
};
const updatedOutPath = path.join(modifyDir, `site-${siteId}_updated.json`);
fs.writeFileSync(
updatedOutPath,
JSON.stringify({ Response: { Data: { ...currentData, SiteSettings: updatedSiteSettings } } }, null, 2),
"utf-8"
);
const maskedBody = { ...body, ApiKey: maskApiKey(body.ApiKey) };
const diffText = [
`SiteId ${siteId} (${name}) CheckG列追加 差分プレビュー (${ts})`,
`URL: ${url}`,
`Title: ${currentData.Title}`,
`Columns件数: ${(currentData.SiteSettings.Columns || []).length}${updatedSiteSettings.Columns.length}`,
`追加列: CheckG (A由来フラグ) DefaultInput="false", Hide=true, NoWrap=true482696の手動追加実物と同一定義`,
"",
"--- 送信BodyAPIキーはマスク ---",
JSON.stringify({ ...maskedBody, SiteSettings: `(省略: Columns ${updatedSiteSettings.Columns.length}件)` }, null, 2),
].join("\n");
const diffOutPath = path.join(modifyDir, `site-${siteId}_apply_diff_${ts}.txt`);
fs.writeFileSync(diffOutPath, diffText, "utf-8");
console.log(diffText);
console.log(`\n[OK] 差分プレビューを保存しました: ${diffOutPath}`);
if (!execute) {
console.log("\n[注意] --execute が指定されていないため、送信は行っていません。");
return { modifyDir, ts };
}
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-${siteId}_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);
}
return { modifyDir, ts };
}
(async () => {
for (const target of TARGETS) {
await processSite(target);
}
console.log("\n==== 全サイト処理完了 ====");
if (!execute) {
console.log("内容を確認の上、問題なければ次を実行してください: node add-checkg-column.js --execute");
}
})();