1.5.7.0 の変更点
リリース日: 2026年8月12日。1.5.6.1 からの変更です。機能追加・仕様変更と不具合修正を分けて記載します。
機能追加・仕様変更
- その他 — マルチテナント機能が加わった。
- 運用・設定 — キューイング機能にレコードのインポート機能が加わった。
- 利用者・権限 — SAML連携で、ユーザの拡張項目を取り込む機能が加わった。
- その他 — サーバのウォームアップ機能が加わった。
- 実行環境・DB — ASP.NET Core Data Protection キーをKVSに保存できるようになった。
- 運用・設定 — バイナリデータを Azure Blob に保存できるようになった。
- API・拡張 — スクリプト、スタイル、HTMLのドラフト出力を利用できるようになった。
- その他 — リッチテキストエディタのライブラリを更新した。
- 運用・設定 — 信頼するプロキシ認証を有効化した場合、Security.jsonのKnownNetworksまたはKnownProxiesを必須とするよう変更に関する仕様を更新した。
- 利用者・権限 — ログイン画面右上のPleasanter.netリンクを取り除いた。
不具合修正
- 運用・設定 — Mail.jsonのSendGrid.ApiKeyの設定が適用されない不具合を修正。
- その他 — BackgroundServerScriptがDeploymentEnvironmentを参照せず待機系サーバでも実行される不具合を修正。
- データ入出力 — CSP違反レポート有効時に syslogs ファイル出力が停止する不具合を修正。
- 利用者・権限 — BackgroundServerScriptが実行ユーザーのタイムゾーンを反映しない不具合を修正。
- API・拡張 — サーバスクリプトのitems.GetSiteで取得したサイトのapiModelでCreate関数を実行するとエラーが発生する不具合を修正。
- 通知・メール — APIによるメール送信時にエラー原因が特定しにくい不具合を修正。
- その他 — サブディレクトリ構成でFormのサンクスメッセージが表示できない不具合を修正。
実装を読む
1.5.6.1 との差分です。新規追加ファイルは 60 本以上あります。
| 分類 | 追加された主なもの | 概要 | ライセンス |
|---|---|---|---|
| マルチテナント管理 API | api/tenants 系エンドポイント | テナントの払い出し・停止・削除を API 化 | 必要(トライアル不可) |
| バイナリストレージ | AzureBlob プロバイダ | 添付ファイルを Blob Storage に直接置ける | 不要 |
| 起動ウォームアップ | WarmupGateMiddleware | 初期化完了までリクエストを受け付けない | 不要 |
| 分散ロック | DistributedLock | 複数インスタンスでの起動処理の競合を防ぐ | 不要 |
| ヘルスチェック | ウォームアップ状態のチェック | 初期化中はロードバランサから外れる | 不要 |
| データ保護キー | Redis への保存 | インスタンス間で鍵を共有できる | 不要 |
| バックグラウンドジョブ | ImportJobHandler | インポートをジョブとして裏で流せる | 必要(Queue) |
| 一覧のエラー処理 | GridDataTimeoutException | 一覧のタイムアウトを識別して返す | 不要 |
| 下書き出力 | DraftOutputMode | スタイル・スクリプト・HTML を下書き時だけ出し分け | 不要 |
マルチテナント管理 API
Implem.Pleasanter/Controllers/Api/TenantsController.cs が新設され、テナントを API で操作できるようになりました(ソース)。
| メソッド | パス | 用途 |
|---|---|---|
| POST | api/tenants/Get | テナント一覧の取得 |
| POST | api/tenants/Create | テナントと初期ユーザーの作成 |
| POST | api/tenants/{id}/Suspend | テナントの利用停止 |
| POST | api/tenants/{id}/Resume | 停止したテナントの再開 |
| POST | api/tenants/{id}/Delete | テナントの削除依頼 |
前提条件
すべてのメソッドで、Parameters.AllowMultiTenants()(マルチテナントのライセンス)と context.HasPrivilege(Security.json の PrivilegedUsers に含まれる特権ユーザー)がチェックされます。
トライアルライセンスでは使えない
他の Allow*() は TrialLicense?.Check() ?? ... の形で、トライアルライセンスがあればオプションを見ずに通ります。AllowMultiTenants() だけはこの形をとっていないため、トライアル環境ではマルチテナント API を試せません。
public static bool AllowQueue()
{
return TrialLicense?.Check() ?? License.Check() && HasQueue();
}
public static bool AllowMultiTenants()
{
return License.Check() && HasMultiTenants(); // トライアルのフォールバックなし
}ライセンスは認証にも影響します。1.5.7.0 で Context に次の判定が追加されました。
if (!Parameters.MultiTenant.IsProtectedTenant(TenantId) && !Parameters.AllowMultiTenants())
{
Authenticated = false;
}MultiTenant.json の DefaultTenantId で指定した保護テナント以外は、マルチテナントのライセンスがないとログインできません。ライセンスなしの環境で 2 つ目以降のテナントを作っていた場合は、1.5.7.0 へ上げる前に確認してください。
テナントの作成
リクエストボディは CreateTenantApiModel にデシリアライズされます(ソース)。
{
"ApiKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"TenantName": "サンプル商事",
"Title": "サンプル商事 プリザンター",
"LoginId": "sample-admin",
"Name": "サンプル管理者",
"NotifyMailAddress": "admin@example.com",
"Language": "ja"
}TenantName・LoginId・Name・NotifyMailAddressは必須で、未指定だとPleaseInputDataエラーになります。NotifyMailAddressはメールアドレスの形式もチェックされます。- テナントと同時に、
LoginId/Nameのテナント管理者(TenantManager = true)が 1 人作られます。パスワードはService.jsonのDefaultPasswordです。Security.jsonのPasswordExpirationPeriodが有効なら有効期限も設定されます。 - 作成後、
NotifyMailAddress宛に案内メールが送られます。送信元はMail.jsonのSupportFromで、未設定だとメールは送られません(テナントは作成されます)。本文テンプレートはApp_Data/Displays/TenantCreatedMailBody.jsonにあり、多言語定義と同じ仕組みで差し替えられます。
停止と再開
Suspend は Tenants テーブルの ContractDeadline(契約期限)に日付を書き込むだけの実装です。SuspendDate を省略すると当日、将来日を指定すれば予約停止になります。Resume は ContractDeadline を null に戻します。期限切れでログインを止める判定も 1.5.7.0 で追加されており、保護テナントは対象外です。
削除は 2 段階
Delete は物理削除ではなく、1.5.7.0 で追加された Tenants.DeleteRequestTime 列に削除依頼日時を書き込むだけです。実際の削除はバックグラウンドサービスが行います。
図を読み込み中…
{
"DeleteTenant": true,
"DeleteTenantTime": [ "04:00" ],
"DeleteTenantRetentionPeriod": 30,
"DeleteTenantChunkSize": 1000,
"DeleteTenantBinariesChunkSize": 10
}ChunkSize 系は、テナント配下のレコードや添付ファイルを分割しながら削除して DB の負荷を抑えるための設定です。
WARNING
1.5.8.0 で DeleteTenant の既定値が false に変わりました。1.5.8.0 の「既定値の変更(DeleteTenant)」を参照してください。
保護テナント
新しいパラメータファイル MultiTenant.json が追加されました。
{
"DefaultTenantId": 1
}DefaultTenantId に指定したテナントは Suspend / Resume / Delete を受け付けず、バックグラウンドの物理削除でもスキップされます(SysLogs に警告が残ります)。API の操作ミスで基盤テナントごと消える事故を防ぐためのガードです。
添付ファイルを Azure Blob Storage に置ける
添付ファイルの格納方式にインターフェース(IBinaryStorageProvider)が切られ、プロバイダを差し替えられる構造になりました(ソース)。
public static IBinaryStorageProvider Create(string provider)
{
return provider switch
{
BinaryStorageProviderNames.LocalFolder => new LocalFolderBinaryStorageProvider(),
BinaryStorageProviderNames.Local => new LocalFolderBinaryStorageProvider(),
BinaryStorageProviderNames.AzureBlob => new AzureBlobBinaryStorageProvider(),
_ => null
};
}Rdsの場合はプロバイダがnullになり、従来どおりBinaries.Bin列を読み書きします。IBinaryStorageProviderはUpload/Download/OpenRead/Delete/Exists/LastWriteTimeを同期版とAsync版で定義しています。独自ストレージに対応させるには、このインターフェースを実装してファクトリに 1 行足します。BinaryStorage.jsonにAzureBlobStorageAccountUriとAzureBlobContainerNameが追加されました。どちらも環境変数で上書きできます。- 認証には
DefaultAzureCredentialを使うため、接続文字列やアカウントキーを設定ファイルに書く必要はありません。App Service や Container Apps のマネージド ID にストレージの BLOB データ共同作成者ロールを付与すれば動きます。ローカル開発時は Azure CLI のログイン情報が使われます。 - 添付ファイルは
Attachments/{Guid}というオブジェクト名で保存されます。 - サーキットブレーカーを備えています。接続・認証に失敗すると 30 秒 → 2 分 → 10 分と段階的に遮断時間が延び、遮断中は即座に
IOExceptionを返します。1 回でも成功すれば失敗カウントはリセットされます。失敗内容はSysLogsに記録されます。
一時ファイルが外部ストレージへ移送されるようになった
ロードバランサ配下で、アップロードとレコード保存が別インスタンスに振り分けられる問題を避けるため、TemporaryBinaryStorageProvider を Rds にして一時ファイルだけ DB に置く運用があります。1.5.6.0 以前はこの構成だと保存後もファイルが Binaries.Bin に残り続けていましたが、1.5.7.0 では保存時に外部ストレージへ移送されます(ソース)。移送に失敗した場合は SysLogs に記録して例外を投げるため、DB 側だけ NULL になって実体を見失うことはありません。
起動ウォームアップ
起動時の初期化(テナント・拡張機能・ユーザー・アイテム・マイグレーション・ステータス・通知・サイト情報)がホステッドサービス ApplicationWarmupHostedService に切り出され、完了するまでリクエストをゲートで止めるようになりました(ソース)。状態は WarmupStatus(NotStarted / InProgress / Completed / Failed / Canceled / TimedOut)で管理されます。
Completed 以外のときは、WarmupGateMiddleware がリクエストの種類ごとに応答を振り分けます(ソース)。
図を読み込み中…
- 静的ファイルとエラーページを通すのは、リダイレクト先の
/errors/warmup自体を表示できるようにするためです。 - IIS の Application Initialization からのリクエストだけは、ウォームアップが終わるまで待ってから 200 を返します。
- 初期化に失敗した場合はアプリケーション自体が停止します。
タイムアウトと停止までの猶予は BackgroundService.json で調整できます。サイト数やユーザー数が多い環境では、既定の 180 秒で足りるか実測して確認してください。
{
"WarmupTimeoutSeconds": 180,
"WarmupFailureShutdownDelaySeconds": 60
}分散ロック
複数インスタンスを同時に起動したときに DB のマイグレーションが並行して走らないよう、DistributedLock が追加されました(ソース)。Redis などではなく DB のロック機構を使うため、追加のインフラは不要です。
| DBMS | 使用する仕組み |
|---|---|
| SQL Server | sp_getapplock(@LockOwner = 'Session') |
| PostgreSQL | pg_advisory_lock() + lock_timeout |
| MySQL | get_lock() |
| その他 | 従来どおり排他制御を行わずに処理を続ける |
起動時のマイグレーションは startup-migrations という名前のロックで保護され、ロックを取れなかったインスタンスは先行インスタンスの完了を待ってから次に進みます。
ヘルスチェック
/healthz は以前からありますが、1.5.7.0 で次の点が変わりました。
- 登録処理が
Startup.csからHealthCheckExtensions.csに切り出された - ウォームアップ状態を返すチェック(
warmup)が追加された(ソース) Security.jsonのHealthCheck.Enabledの既定値がfalseからtrueに変わった
| WarmupStatus | 結果 | HTTP |
|---|---|---|
Completed | Healthy | 200 |
NotStarted / InProgress | Degraded | 503 |
Failed / Canceled / TimedOut | Unhealthy | 503 |
ウォームアップ中は 503 になるため、ロードバランサの振り分け対象から自動的に外れます。
"HealthCheck": {
- "Enabled": false,
+ "Enabled": true,
"EnableDatabaseCheck": false,
"HealthQuery": null,
"RequireHosts": null,
"EnableDetailedResponse": false
},WARNING
/healthz は既定で応答するようになりました。RequireHosts が null のままだとホスト制限なしで公開されるため、監視元を限定したい場合は設定してください。
データ保護キーを Redis に置ける
複数インスタンスで動かす場合、ASP.NET Core のデータ保護キーを共有しないと Cookie の復号に失敗してログインが維持できません。従来の Azure Blob Storage に加え、Redis が選べるようになりました(ソース)。
"AspNetCoreDataProtection": {
"BlobContainerUri": null,
"KeyIdentifier": null,
"KeyFileName": "Keys.xml",
- "XmlAesKey": null
+ "XmlAesKey": null,
+ "KeyValueStoreConnectionString": null,
+ "KeyValueStoreKeyName": null
},KeyValueStoreConnectionString を指定すると PersistKeysToStackExchangeRedis() が使われます。優先順位は Blob(BlobContainerUri と KeyIdentifier の両方が設定されている場合)→ Redis → ローカル(既定)です。セッションの共有(Kvs.json の ConnectionStringForSession)で Redis を使っている環境なら、同じインスタンスにキーも置けます。
バックグラウンドジョブでインポートを実行できる
バックグラウンドジョブは以前からエクスポートに対応していましたが、インポート(BackgroundJobTypes.Import)が追加されました。ImportJobHandler がジョブに紐づく CSV を読み込み、通常のインポートと同じ処理を裏で実行します。結果は ImportResultData にまとめられ、件数やエラー内容がジョブの結果として残ります。
{
"BackgroundQueue": false,
"BackgroundJobDispatcherInterval": 60,
"BackgroundJobTimeout": 3600,
"FallbackLanguage": "ja",
"RecoverAction": "Failed",
"OutputFilePath": null,
"InputFilePath": null
}BackgroundQueue を true にし、InputFilePath に読み込み元のディレクトリを指定して使います。
WARNING
バックグラウンドジョブ機能そのものに上位ライセンス(Queue オプション)が必要です。BackgroundQueue を true にしても、ライセンスがなければキューは有効になりません。判定は AllowQueue() なのでトライアルライセンスでも有効になります。ライセンスの条件は 1.5.6.0 から変わっていません。
一覧のタイムアウトを識別できる
一覧のデータ取得で例外が起きたとき、従来は原因によらず「ソートできません」になっていましたが、1.5.7.0 では 3 種類に分かれます(ソース)。
| 例外 | 条件 | ビュー |
|---|---|---|
GridDataTimeoutException | DB のタイムアウト(判定は DBMS ごと。SQL Server は SqlException.Number == -2) | リセットしない |
CanNotGridSortException | その他の DbException | リセットしてソートエラーを返す |
GridDataException | その他の例外 | — |
タイムアウトのときはビューがリセットされないため、重いクエリでタイムアウトしてもフィルタ条件が残ります。
スタイル・スクリプト・HTML の下書き出力
管理画面のスタイル・スクリプト・HTML に DraftOutputMode と DraftKey が追加されました。本番サイトで、一般利用者には見せずに新しいスクリプトを検証するための機能です。
DraftOutputMode | 挙動 |
|---|---|
0(既定) | 常に出力する |
1 | 下書き時のみ出力する |
2 | 下書き時は出力しない |
下書きかどうかは、クエリパラメータ Draft の値が DraftKey と一致するかで判定されます(ソース)。DraftKey を指定しなければ 1 が使われるため、?Draft=1 で切り替わります。
/items/123?Draft=newfeatureこの例では、DraftKey に newfeature を設定したスクリプト(DraftOutputMode が 1)がこの URL のときだけ出力されます。DraftOutputMode を 2 にしておけば、既存のスクリプトを下書き確認時だけ止められます。
WARNING
DraftKey は URL に平文で載るため、秘匿情報ではありません。