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>
185 lines
8.2 KiB
JavaScript
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");
|
|
}
|
|
})();
|