ken_nogi/Pleasanter/.claude/js/site-scripts/org-master-sync代替マスタ/add-columns.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

185 lines
8.2 KiB
JavaScript

// org-master-sync代替マスタ: 組織(504736)/ユーザー(504737)/グループ(504738)へ
// Users/Depts/Groups相当の項目(Columns)を新規追加するワンショットスクリプト。
// 対象は真っさらな空サイト(SiteSettingsにColumns未設定)。updatesite(Mode:full)で送信する。
// --execute指定時のみ実際に送信。指定なければプレビュー(送信Body・diff)のみ出力。
"use strict";
const fs = require("fs");
const path = require("path");
const PROJECT_NAME = "org-master-sync代替マスタ";
const ENV = "staging";
const REPO_ROOT = path.join(__dirname, "..", "..", "..", "..");
const PROJECT_ROOT = path.join(REPO_ROOT, PROJECT_NAME);
function loadServerConfig() {
const configPath = path.join(REPO_ROOT, `config_${ENV}.json`);
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
if (!config.BaseUrl || !config.ApiKey) {
console.error(`[エラー] ${configPath} に BaseUrl / ApiKey を設定してください。`);
process.exit(1);
}
return config;
}
function siteDir(siteId, siteName) {
return path.join(PROJECT_ROOT, "configs", ENV, `site-${siteId}_${siteName}`);
}
function loadLatest(siteId, siteName) {
const p = path.join(siteDir(siteId, siteName), "sitesettings", `site-${siteId}_latest.json`);
const raw = JSON.parse(fs.readFileSync(p, "utf-8"));
return raw.Response.Data;
}
const TARGETS = [
{
// Depts(組織) API全フィールド対応。Title/Body/Commentsは使わず全部Class/Description列で表現
siteId: 504736,
siteName: "組織",
titleColumns: [],
columns: [
{ ColumnName: "ClassA", LabelText: "組織ID" },
{ ColumnName: "ClassB", LabelText: "組織名", FieldCss: "field-wide" },
{ ColumnName: "ClassC", LabelText: "組織コード" },
{ ColumnName: "Description001", LabelText: "説明" },
],
gridColumns: ["ResultId", "ClassA", "ClassB", "ClassC", "Updator", "UpdatedTime"],
editorGeneral: ["ResultId", "Ver", "ClassA", "ClassB", "ClassC", "Description001"],
},
{
// Users(ユーザー) sample CSV(ユーザ_2026_09_03 13_53_13.csv)の列順・内容に準拠(コメント列のみ除外)。
// Title/Body/Commentsは使わず全部Class/Num/Date/Description/Check列で表現
siteId: 504737,
siteName: "ユーザー",
titleColumns: [],
columns: [
{ ColumnName: "ClassA", LabelText: "ユーザID" },
{ ColumnName: "ClassB", LabelText: "ログインID", FieldCss: "field-wide" },
{ ColumnName: "ClassC", LabelText: "名前", FieldCss: "field-wide" },
{ ColumnName: "ClassD", LabelText: "ユーザコード" },
{ ColumnName: "DateA", LabelText: "生年月日" },
{ ColumnName: "ClassE", LabelText: "性別" },
{ ColumnName: "ClassF", LabelText: "言語" },
{ ColumnName: "ClassG", LabelText: "タイムゾーン" },
{ ColumnName: "ClassH", LabelText: "組織コード" },
{ ColumnName: "ClassI", LabelText: "組織" },
{ ColumnName: "ClassJ", LabelText: "管理者" },
{ ColumnName: "ClassK", LabelText: "テーマ" },
{ ColumnName: "Description001", LabelText: "説明" },
{ ColumnName: "DateB", LabelText: "最終ログイン日時" },
{ ColumnName: "DateC", LabelText: "パスワード有効期限" },
{ ColumnName: "DateD", LabelText: "パスワード変更日時" },
{ ColumnName: "NumA", LabelText: "ログイン回数" },
{ ColumnName: "NumB", LabelText: "ログイン失敗回数" },
{ ColumnName: "CheckA", LabelText: "テナント管理者" },
{ ColumnName: "CheckB", LabelText: "サイトトップへの作成を許可" },
{ ColumnName: "CheckC", LabelText: "グループの管理を許可" },
{ ColumnName: "CheckD", LabelText: "グループの作成を許可" },
{ ColumnName: "CheckE", LabelText: "APIを許可" },
{ ColumnName: "CheckF", LabelText: "サイトトップからの移動を許可" },
{ ColumnName: "CheckG", LabelText: "2段階認証の有効化" },
{ ColumnName: "CheckH", LabelText: "2段階認証の無効化" },
{ ColumnName: "CheckI", LabelText: "無効" },
{ ColumnName: "CheckJ", LabelText: "ロック" },
{ ColumnName: "NumC", LabelText: "ロックカウンター" },
{ ColumnName: "CheckK", LabelText: "秘密鍵有効" },
{ ColumnName: "DateE", LabelText: "ログイン有効期限" },
{ ColumnName: "NumD", LabelText: "無効化までの日数" },
{ ColumnName: "ClassL", LabelText: "メールアドレス", FieldCss: "field-wide" },
{ ColumnName: "ClassM", LabelText: "パスワード" },
],
gridColumns: ["ResultId", "ClassA", "ClassB", "ClassC", "ClassH", "CheckI", "CheckJ", "Updator", "UpdatedTime"],
editorGeneral: [
"ResultId", "Ver",
"ClassA", "ClassB", "ClassC", "ClassD",
"DateA",
"ClassE", "ClassF", "ClassG", "ClassH", "ClassI", "ClassJ", "ClassK",
"Description001",
"DateB", "DateC", "DateD",
"NumA", "NumB",
"CheckA", "CheckB", "CheckC", "CheckD", "CheckE", "CheckF", "CheckG", "CheckH", "CheckI", "CheckJ",
"NumC",
"CheckK",
"DateE",
"NumD",
"ClassL", "ClassM",
],
},
{
// Groups(グループ) API全フィールド対応。
// GroupMembersはUser以外にDept/Groupも混在する(実データで確認済み)ため、
// 選択肢型(Users限定)ではなくDescription列にAPI形式のテキスト("種別,キー,管理者フラグ"を改行区切り)で格納する
siteId: 504738,
siteName: "グループ",
titleColumns: [],
columns: [
{ ColumnName: "ClassA", LabelText: "グループID" },
{ ColumnName: "ClassB", LabelText: "グループ名", FieldCss: "field-wide" },
{ ColumnName: "Description001", LabelText: "説明" },
{ ColumnName: "Description002", LabelText: "グループメンバー(種別,キー,管理者フラグ 1行1メンバー)", FieldCss: "field-wide" },
],
gridColumns: ["ResultId", "ClassA", "ClassB", "Updator", "UpdatedTime"],
editorGeneral: ["ResultId", "Ver", "ClassA", "ClassB", "Description001", "Description002"],
},
];
function buildDesiredSiteSettings(current, target) {
const settings = JSON.parse(JSON.stringify(current.SiteSettings));
settings.TitleColumns = target.titleColumns;
settings.Columns = target.columns;
settings.GridColumns = target.gridColumns;
settings.EditorColumnHash = { General: target.editorGeneral };
settings.Links = current.SiteSettings.Links || [];
return settings;
}
function buildUpdateBody(apiKey, siteId, current, desiredSettings) {
return {
ApiVersion: "1.1",
ApiKey: apiKey,
SiteId: siteId,
Title: current.Title,
ReferenceType: current.ReferenceType,
ParentId: current.ParentId,
InheritPermission: current.InheritPermission,
SiteSettings: desiredSettings,
};
}
const execute = process.argv.includes("--execute");
const server = loadServerConfig();
(async () => {
for (const target of TARGETS) {
const current = loadLatest(target.siteId, target.siteName);
const desiredSettings = buildDesiredSiteSettings(current, target);
const body = buildUpdateBody(server.ApiKey, target.siteId, current, desiredSettings);
const outDir = siteDir(target.siteId, target.siteName);
const previewPath = path.join(outDir, "add-columns_preview.json");
fs.writeFileSync(previewPath, JSON.stringify(body, null, 2), "utf-8");
console.log(`\n==== SiteId ${target.siteId} (${target.siteName}) ====`);
console.log("追加する項目:");
for (const c of target.columns) console.log(` - ${c.ColumnName}: ${c.LabelText}`);
console.log(`プレビュー保存先: ${previewPath}`);
if (!execute) {
console.log("(--execute 未指定のためプレビューのみ。送信していません)");
continue;
}
const url = `${server.BaseUrl}api/items/${target.siteId}/updatesite`;
console.log(`送信先: ${url}`);
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const json = await res.json();
console.log(`[結果] HTTP ${res.status}:`, JSON.stringify(json));
const resultPath = path.join(outDir, `add-columns_result_${Date.now()}.json`);
fs.writeFileSync(resultPath, JSON.stringify(json, null, 2), "utf-8");
}
})();