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>
131 lines
5.3 KiB
JavaScript
131 lines
5.3 KiB
JavaScript
/**
|
||
* rotate-tabs-507570.js
|
||
* ------------------------------------------------------------
|
||
* SiteId 507570(事業計画v2)専用のワンオフスクリプト。
|
||
* 期首を4月→10月に変更したことに伴い、画面タブ「1Q/2Q/3Q/4Q」(Tabs配列のラベルはそのまま)に
|
||
* 割り当てられている中身(EditorColumnHashの_Tab-1〜_Tab-4配列)をローテーションする。
|
||
*
|
||
* 変更前: _Tab-1(1Q)=4-6月, _Tab-2(2Q)=7-9月, _Tab-3(3Q)=10-12月, _Tab-4(4Q)=1-3月
|
||
* 変更後: _Tab-1(1Q)=10-12月, _Tab-2(2Q)=1-3月, _Tab-3(3Q)=4-6月, _Tab-4(4Q)=7-9月
|
||
* (新1Q=旧3Q、新2Q=旧4Q、新3Q=旧1Q、新4Q=旧2Qの中身をそのまま入れ替えるだけ。
|
||
* Section Id等も含めて配列ごと移動するので、タブ内の受注/着工/完成セクション構造は保持される)
|
||
*
|
||
* SiteSettings.Linksは省略厳禁のため、取得済み生JSONをそのままベースにコピーし、
|
||
* EditorColumnHashのみ差し替える。Permissionsは対象サイト自身が実体を持つ
|
||
* (InheritPermission === SiteId自身)ためそのまま含める。
|
||
*
|
||
* 使い方:
|
||
* node rotate-tabs-507570.js … プレビューのみ(差分表示、送信なし)
|
||
* node rotate-tabs-507570.js --execute … 実際にupdatesiteへ送信
|
||
* ------------------------------------------------------------
|
||
*/
|
||
|
||
const fs = require("fs");
|
||
const path = require("path");
|
||
const https = require("https");
|
||
|
||
const PROJECT_NAME = "NEXTGROUP経営計画";
|
||
const ENV = "production";
|
||
const SITE_ID = 507570;
|
||
|
||
const REPO_ROOT = path.join(__dirname, "..", "..", "..", "..");
|
||
const baseDir = path.join(REPO_ROOT, PROJECT_NAME);
|
||
const siteDir = path.join(baseDir, "configs", ENV, `site-${SITE_ID}_事業計画v2`);
|
||
const latestJsonPath = path.join(siteDir, "sitesettings", `site-${SITE_ID}_latest.json`);
|
||
const modifyDir = path.join(siteDir, "modify", "rotate-tabs");
|
||
|
||
const configPath = path.join(REPO_ROOT, `config_${ENV}.json`);
|
||
const serverConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
||
|
||
const execute = process.argv.includes("--execute");
|
||
|
||
const raw = JSON.parse(fs.readFileSync(latestJsonPath, "utf-8"));
|
||
const data = raw.Response.Data;
|
||
const ss = data.SiteSettings;
|
||
|
||
const eh = ss.EditorColumnHash;
|
||
const before = {
|
||
"_Tab-1": eh["_Tab-1"],
|
||
"_Tab-2": eh["_Tab-2"],
|
||
"_Tab-3": eh["_Tab-3"],
|
||
"_Tab-4": eh["_Tab-4"],
|
||
};
|
||
|
||
const newEh = {
|
||
...eh,
|
||
"_Tab-1": before["_Tab-3"], // 1Q <- 旧3Q(10-12月)
|
||
"_Tab-2": before["_Tab-4"], // 2Q <- 旧4Q(1-3月)
|
||
"_Tab-3": before["_Tab-1"], // 3Q <- 旧1Q(4-6月)
|
||
"_Tab-4": before["_Tab-2"], // 4Q <- 旧2Q(7-9月)
|
||
};
|
||
|
||
console.log("========================================");
|
||
console.log(" タブ内容ローテーション(プレビュー)");
|
||
console.log("========================================");
|
||
console.log(`SiteId: ${SITE_ID}\n`);
|
||
for (const key of ["_Tab-1", "_Tab-2", "_Tab-3", "_Tab-4"]) {
|
||
const descBefore = before[key].filter((x) => typeof x === "string" && x.startsWith("Description")).slice(0, 3);
|
||
const descAfter = newEh[key].filter((x) => typeof x === "string" && x.startsWith("Description")).slice(0, 3);
|
||
console.log(`${key}: [${descBefore.join(",")}...] -> [${descAfter.join(",")}...]`);
|
||
}
|
||
|
||
const body = {
|
||
ApiVersion: serverConfig.ApiVersion || "1.1",
|
||
ApiKey: serverConfig.ApiKey,
|
||
SiteId: SITE_ID,
|
||
Title: data.Title,
|
||
ReferenceType: data.ReferenceType,
|
||
ParentId: data.ParentId,
|
||
InheritPermission: data.InheritPermission,
|
||
Permissions: data.Permissions,
|
||
SiteSettings: {
|
||
...ss,
|
||
EditorColumnHash: newEh,
|
||
},
|
||
};
|
||
|
||
fs.mkdirSync(modifyDir, { recursive: true });
|
||
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
||
const beforeSnapshotPath = path.join(modifyDir, `before_site-${SITE_ID}_latest_${ts}.json`);
|
||
const previewPath = path.join(modifyDir, `site-${SITE_ID}_rotate_tabs_preview_${ts}.json`);
|
||
fs.writeFileSync(beforeSnapshotPath, JSON.stringify(raw, null, 2));
|
||
fs.writeFileSync(previewPath, JSON.stringify({ ...body, ApiKey: "***masked***" }, null, 2));
|
||
console.log(`\n[OK] 変更前スナップショット保存: ${beforeSnapshotPath}`);
|
||
console.log(`[OK] 送信予定内容(APIキーマスク)保存: ${previewPath}`);
|
||
|
||
if (!execute) {
|
||
console.log("\n[注意] --execute が指定されていないため、送信は行っていません。");
|
||
console.log("内容を確認の上、問題なければ次を実行してください: node rotate-tabs-507570.js --execute");
|
||
process.exit(0);
|
||
}
|
||
|
||
console.log("\n実際に送信します(updatesite)...");
|
||
const url = new URL(`${serverConfig.BaseUrl}/api/items/${SITE_ID}/updatesite`);
|
||
const payload = JSON.stringify(body);
|
||
|
||
const req = https.request(
|
||
{
|
||
hostname: url.hostname,
|
||
path: url.pathname,
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"Content-Length": Buffer.byteLength(payload),
|
||
},
|
||
},
|
||
(res) => {
|
||
let chunks = "";
|
||
res.on("data", (c) => (chunks += c));
|
||
res.on("end", () => {
|
||
console.log(`HTTP ${res.statusCode}`);
|
||
console.log(chunks);
|
||
const resultPath = path.join(modifyDir, `site-${SITE_ID}_rotate_tabs_result_${ts}.json`);
|
||
fs.writeFileSync(resultPath, chunks);
|
||
console.log(`[OK] 応答を保存しました: ${resultPath}`);
|
||
});
|
||
}
|
||
);
|
||
req.on("error", (e) => console.error("[エラー]", e));
|
||
req.write(payload);
|
||
req.end();
|