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>
191 lines
7.4 KiB
JavaScript
191 lines
7.4 KiB
JavaScript
/**
|
||
* add-checkg-column.js
|
||
* ------------------------------------------------------------
|
||
* ケアセブン(リハ) プロジェクト専用。
|
||
* 周回予定・個別予定・例外予定日 計6サイト(【A】482696/482701/482700、
|
||
* 【B】505010/505011/505012)のSiteSettings.Columnsへ「CheckG」列
|
||
* (A由来フラグ)を新規追加し、updatesite(Mode:"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=true(482696の手動追加実物と同一定義)`,
|
||
"",
|
||
"--- 送信Body(APIキーはマスク) ---",
|
||
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");
|
||
}
|
||
})();
|