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>
194 lines
7.5 KiB
JavaScript
194 lines
7.5 KiB
JavaScript
/**
|
||
* apply-9item-phase2-496626.js
|
||
* ------------------------------------------------------------
|
||
* site-496626「新・着工要因(東京)v2」9項目改修のitem8
|
||
* (「引越し」工程削除+後続20工程の内部カラム番号一括リネーム)の
|
||
* SiteSettings構造変更(Sections/Columns/EditorColumnHash/GridColumns)を反映する。
|
||
*
|
||
* 対象: Date028(引越し)を削除し、Date029〜048(20工程)を Date028〜047 へ、
|
||
* 対応する実績列(Date1xx)・備考列(Class0xx)・無しチェック列(Check1xx)・Section Idも
|
||
* 全て1つずつ前へリネームする。工程名(LabelText/GridLabelText)自体は変更しない。
|
||
*
|
||
* ★このスクリプトはSiteSettings構造のみを変更する。既存レコードのデータ
|
||
* (Date029等の実際の値)はこの反映だけでは移行されない。データ移行は
|
||
* migrate-process028-rename-496626.js を別途実行すること。構造変更を先に
|
||
* 反映し、その直後にデータ移行を実行する運用を想定している。
|
||
*
|
||
* 使い方:
|
||
* node apply-9item-phase2-496626.js … 差分表示のみ(送信なし)
|
||
* node apply-9item-phase2-496626.js --execute … 上記に加えて実際に送信する
|
||
* ------------------------------------------------------------
|
||
*/
|
||
|
||
const fs = require("fs");
|
||
const path = require("path");
|
||
const { findSiteDir, newModifyDir, latestModifyDir } = require("../../site-paths");
|
||
const { REPO_ROOT, loadServerConfig } = require("../../resolve-project");
|
||
|
||
const PROJECT_NAME = "新・着工要因システム";
|
||
const ENV = "production";
|
||
const SITE_ID = 496626;
|
||
const baseDir = path.join(REPO_ROOT, PROJECT_NAME);
|
||
const configsDir = path.join(baseDir, "configs", ENV);
|
||
const execute = process.argv.includes("--execute");
|
||
|
||
function loadJson(p) {
|
||
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, "-");
|
||
}
|
||
|
||
const config = loadServerConfig(ENV);
|
||
const siteDir = findSiteDir(configsDir, SITE_ID);
|
||
|
||
// ---- リネームマッピング構築 ----
|
||
const REMOVE_COLUMNS = new Set(["Date028", "Date128", "Class028", "Check128"]);
|
||
const RENAME_MAP = {};
|
||
const SECTION_ID_MAP = {};
|
||
for (let n = 29; n <= 48; n++) {
|
||
const newN = n - 1;
|
||
const pad = (x) => String(x).padStart(3, "0");
|
||
RENAME_MAP["Date" + pad(n)] = "Date" + pad(newN);
|
||
RENAME_MAP["Date" + (n + 100)] = "Date" + (newN + 100);
|
||
RENAME_MAP["Class" + pad(n)] = "Class" + pad(newN);
|
||
RENAME_MAP["Check" + (n + 100)] = "Check" + (newN + 100);
|
||
SECTION_ID_MAP[n] = newN;
|
||
}
|
||
|
||
function renameGridDesign(text) {
|
||
if (!text) return text;
|
||
return text.replace(/\[([A-Za-z]+\d+)\]/g, (m, colName) => {
|
||
return RENAME_MAP[colName] ? `[${RENAME_MAP[colName]}]` : m;
|
||
});
|
||
}
|
||
|
||
function renameTokenArray(arr) {
|
||
return arr
|
||
.filter((t) => t !== "_Section-28" && !REMOVE_COLUMNS.has(t))
|
||
.map((t) => {
|
||
if (t.startsWith("_Section-")) {
|
||
const id = parseInt(t.replace("_Section-", ""), 10);
|
||
return SECTION_ID_MAP[id] ? `_Section-${SECTION_ID_MAP[id]}` : t;
|
||
}
|
||
return RENAME_MAP[t] || t;
|
||
});
|
||
}
|
||
|
||
function transform(ss) {
|
||
const log = [];
|
||
|
||
// ---- Columns ----
|
||
ss.Columns = ss.Columns.filter((c) => !REMOVE_COLUMNS.has(c.ColumnName));
|
||
ss.Columns.forEach((c) => {
|
||
if (RENAME_MAP[c.ColumnName]) {
|
||
c.ColumnName = RENAME_MAP[c.ColumnName];
|
||
}
|
||
if (c.GridDesign) {
|
||
c.GridDesign = renameGridDesign(c.GridDesign);
|
||
}
|
||
});
|
||
log.push("Columns: Date028系4項目削除、Date029〜048系80項目をリネーム(GridDesign内参照含む)");
|
||
|
||
// ---- Sections ----
|
||
ss.Sections = ss.Sections.filter((s) => s.Id !== 28);
|
||
ss.Sections.forEach((s) => {
|
||
if (SECTION_ID_MAP[s.Id]) {
|
||
s.Id = SECTION_ID_MAP[s.Id];
|
||
}
|
||
});
|
||
log.push("Sections: Id28削除、Id29〜48をId28〜47へリネーム(LabelTextは変更なし)");
|
||
|
||
// ---- EditorColumnHash(5タブ全て) ----
|
||
["_Tab-1", "_Tab-2", "_Tab-3", "_Tab-4", "_Tab-5"].forEach((tab) => {
|
||
if (ss.EditorColumnHash[tab]) {
|
||
const before = ss.EditorColumnHash[tab].length;
|
||
ss.EditorColumnHash[tab] = renameTokenArray(ss.EditorColumnHash[tab]);
|
||
log.push(`EditorColumnHash.${tab}: ${before}トークン -> ${ss.EditorColumnHash[tab].length}トークン`);
|
||
}
|
||
});
|
||
|
||
// ---- GridColumns(メイン + View2/5/6) ----
|
||
ss.GridColumns = renameTokenArray(ss.GridColumns);
|
||
ss.Views.forEach((v) => {
|
||
if (v.GridColumns) {
|
||
v.GridColumns = renameTokenArray(v.GridColumns);
|
||
}
|
||
});
|
||
log.push("GridColumns: メイン・View2・View5・View6の4箇所をリネーム");
|
||
|
||
return log;
|
||
}
|
||
|
||
(async () => {
|
||
console.log("[INFO] 現在のサイト情報を取得しています...");
|
||
const getUrl = `${config.BaseUrl.replace(/\/+$/, "")}/api/items/${SITE_ID}/getsite`;
|
||
const getRes = await fetch(getUrl, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ ApiVersion: config.ApiVersion || "1.1", ApiKey: config.ApiKey }),
|
||
});
|
||
const getJson = await getRes.json();
|
||
if (getJson.StatusCode !== 200) {
|
||
console.error("[エラー] getsiteに失敗しました:", JSON.stringify(getJson).slice(0, 500));
|
||
process.exit(1);
|
||
}
|
||
const data = getJson.Response.Data;
|
||
console.log(`[OK] 取得しました(現在のVer: ${data.Ver}, UpdatedTime: ${data.UpdatedTime})`);
|
||
|
||
const siteSettings = JSON.parse(JSON.stringify(data.SiteSettings));
|
||
|
||
console.log("========================================");
|
||
console.log(` 9項目改修 Phase2(item8)引越し削除+20工程番号リネーム(updatesite全体更新)${execute ? "(実行)" : "(プレビューのみ)"}`);
|
||
console.log("========================================");
|
||
|
||
const log = transform(siteSettings);
|
||
log.forEach((l) => console.log(" " + l));
|
||
|
||
const body = {
|
||
ApiVersion: config.ApiVersion || "1.1",
|
||
ApiKey: config.ApiKey,
|
||
SiteId: SITE_ID,
|
||
Title: data.Title,
|
||
ReferenceType: data.ReferenceType,
|
||
ParentId: data.ParentId,
|
||
InheritPermission: data.InheritPermission,
|
||
Permissions: data.Permissions,
|
||
SiteSettings: siteSettings,
|
||
};
|
||
|
||
const modifyDir = latestModifyDir(siteDir) || newModifyDir(siteDir, "9item-revision");
|
||
const ts = timestamp();
|
||
const maskedBody = { ...body, ApiKey: maskApiKey(body.ApiKey) };
|
||
const previewPath = path.join(modifyDir, `site-${SITE_ID}_phase2_update_preview_${ts}.json`);
|
||
fs.writeFileSync(previewPath, JSON.stringify(maskedBody, null, 2), "utf-8");
|
||
console.log(`\n[OK] 送信予定内容を保存しました: ${previewPath}`);
|
||
|
||
if (!execute) {
|
||
console.log("\n[注意] --execute が指定されていないため、送信は行っていません。");
|
||
console.log("内容を確認の上、問題なければ次を実行してください: node apply-9item-phase2-496626.js --execute");
|
||
process.exit(0);
|
||
}
|
||
|
||
console.log("\n実際に送信します(updatesite)...");
|
||
const url = `${config.BaseUrl.replace(/\/+$/, "")}/api/items/${SITE_ID}/updatesite`;
|
||
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-${SITE_ID}_phase2_update_result_${ts}.json`);
|
||
fs.writeFileSync(resultOutPath, text, "utf-8");
|
||
|
||
console.log(`HTTP ${res.status} ${res.statusText}`);
|
||
console.log(`[OK] 応答を保存しました: ${resultOutPath}`);
|
||
console.log(text);
|
||
})();
|