@h-ai/serv 1.0.0-beta.2 → 1.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +172 -124
- package/README.ja.md +172 -124
- package/README.md +172 -124
- package/dist/{chunk-TWMOZPGU.js → chunk-CZ6VA2EN.js} +3 -3
- package/dist/{chunk-TWMOZPGU.js.map → chunk-CZ6VA2EN.js.map} +1 -1
- package/dist/{chunk-I3F7RN4I.js → chunk-PLGCHDZM.js} +30 -13
- package/dist/chunk-PLGCHDZM.js.map +1 -0
- package/dist/features/ai.d.ts +9 -8
- package/dist/features/ai.js +34 -13
- package/dist/features/ai.js.map +1 -1
- package/dist/features/feedback.d.ts +20 -19
- package/dist/features/feedback.js +1 -1
- package/dist/features/feedback.js.map +1 -1
- package/dist/features/iam.d.ts +4 -3
- package/dist/features/iam.js +4 -2
- package/dist/features/iam.js.map +1 -1
- package/dist/features/storage.d.ts +4 -3
- package/dist/features/storage.js +34 -9
- package/dist/features/storage.js.map +1 -1
- package/dist/index.d.ts +97 -68
- package/dist/index.js +338 -222
- package/dist/index.js.map +1 -1
- package/dist/{serv-context-DE1JH5bd.d.ts → serv-context-DSsS_ksL.d.ts} +4 -60
- package/dist/{serv-router-BJk04-oS.d.ts → serv-router-DWjYC0zD.d.ts} +2 -18
- package/package.json +29 -29
- package/dist/chunk-I3F7RN4I.js.map +0 -1
package/README.ja.md
CHANGED
|
@@ -4,29 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
[フレームワークのホーム](../../README.ja.md) · 関連モジュール:[@h-ai/ai](../ai/README.ja.md) · [@h-ai/api-contract](../api-contract/README.ja.md) · [@h-ai/core](../core/README.ja.md) · [@h-ai/crypto](../crypto/README.ja.md) · [@h-ai/iam](../iam/README.ja.md) · [@h-ai/storage](../storage/README.ja.md)
|
|
6
6
|
|
|
7
|
-
## 概要
|
|
8
|
-
|
|
9
7
|
hai-framework の API サービスランタイム。公開 HTTP App 抽象を通じて `@h-ai/api-contract` を複数プラットフォームから使える HTTP API として提供します。Hono は内部実装です。
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- `serv.createApp(...)` → `serv.listen(app, { port })` で起動。アプリは Hono を import する必要がありません。
|
|
14
|
-
- フラットな API:`serv.createApp / serv.implement / serv.listen / serv.toFetch / serv.generateSpec / ...`。
|
|
15
|
-
- procedure のチェーン:`implement().context().route().auth/permission/role().handle().build()`。
|
|
16
|
-
- 独自パイプライン:`createApp({ middlewares })` で HTTP middleware を接続し、context ファクトリーでリクエストごとの値を注入。
|
|
17
|
-
- 読み取り専用 Storage:`serv.storageAssets(...)` で安全な key、MIME 許可リスト、GET/HEAD、ETag、キャッシュヘッダーを管理。
|
|
18
|
-
- 既定の機能 procedures:`createIamProcedures()`、`createStorageProcedures()`、`createAiProcedures()`。
|
|
19
|
-
- `build()` は通常の router を同期的に返すため、標準 feature のネストした procedures を直接選択できます。接続する contract と procedures を一致させ、未選択の入口は公開しません。
|
|
20
|
-
- 存在しないルートは翻訳された HaiResult JSON と HTTP 404 を返します。通信暗号化でも 404 を保ち、非 JSON 応答による 500 にしません。
|
|
21
|
-
- 安全な応答ヘッダー・ヘルスチェックを内蔵し、OpenAPI JSON・Scalar 文書・内部 RPC endpoint は任意。
|
|
22
|
-
- 任意の通信暗号化:`serv.createApp({ transport: { crypto, serverKeyRing, keyStore } })` で鍵交換とリクエスト・レスポンスの暗号化を自動接続。
|
|
23
|
-
- 任意の音声入口:`serv.createApp({ audio: { ai, verifyTicket, authorize } })` が共通 WebSocket を接続します。接続直後に単回 ticket で認証(`{ session, grant? }`、独立した事前認証 timeout)。各フレームを Zod と厳密な状態機械で検証し、入力・キュー・送信バッファの背圧と連鎖キャンセルを管理します。サーバーの認可処理で操作・モデル・声・割当量・同時使用数を確認します。
|
|
24
|
-
|
|
25
|
-
> ライフサイクル:`@h-ai/serv` は状態を持たない HTTP App の構成機能で、`init()` / `close()` は不要です。app 作成前に `iam` / `storage` / `ai` / `crypto` などを初期化し、`serv.listen(..., { onClose })` で逆順に解放します。`serv.createApp()` は transport 管理器の生成失敗など起動設定のエラーを即座に throw します。 onClose は SIGINT/SIGTERM 用の終了コールバックです。返された handle.close の直接呼出しは HTTP だけを閉じ、業務モジュールはホストが別途逆順に解放します。
|
|
9
|
+
実行時の操作は `serv.xx` を使います。名前空間の型注釈や再エクスポートにはルート入口の `ServFunctions` を利用できます。context ファクトリーと汎用 `implement` の公開シグネチャーは利用側の宣言生成に対応し、内部ビルドパスや独立した helper は不要です。
|
|
26
10
|
|
|
27
|
-
##
|
|
11
|
+
## クイックスタート
|
|
28
12
|
|
|
29
|
-
|
|
13
|
+
以下の組立て例は、アプリで初期化済みの iam・storage・ai・core・logger と、アプリ側の closeApp を前提とします。初期化スクリプトではありません。健康確認だけなら空の contract/procedures を使えます。serv に init/close はなく、終了時はリスナーと業務モジュールを逆順に閉じます。
|
|
30
14
|
|
|
31
15
|
```ts
|
|
32
16
|
import { apiContract } from '@h-ai/api-contract'
|
|
@@ -53,6 +37,7 @@ const procedures = {
|
|
|
53
37
|
const app = serv.createApp({
|
|
54
38
|
contract,
|
|
55
39
|
procedures,
|
|
40
|
+
iam,
|
|
56
41
|
http: {
|
|
57
42
|
apiPrefix: '/api/v1',
|
|
58
43
|
openapi: { path: '/openapi.json' },
|
|
@@ -80,7 +65,47 @@ AI procedures は省略された `objectId` を信頼できる `context.session.
|
|
|
80
65
|
|
|
81
66
|
`chats.createCompletion`(`POST /ai/chats/completions`)は非ストリーミングの `HaiResult<AiChatCompletionData>` を一度返します。function `tools` のネストした JSON Schema と `strict`、`tool_choice`(`auto` / `none` / `required` / 提供した関数名)をモデルへ渡し、役割、`name`、ユーザー画像、ツール呼び出しを保持します。ツール結果には `role: 'tool'` と空でない `tool_call_id` が必須で、指定名は今回のツール定義に含まれる必要があります。procedure はツールを実行しません。アプリ側で認可・実行後、助手の `tool_calls` と対応する結果を送信します。入力に `stream` はなく、`true` / `false` とも Provider 呼び出し前に HTTP 400 で拒否します。ストリーミングには `ai.llm.chatStream()` と対応する出力型を使う専用 endpoint を定義します。
|
|
82
67
|
|
|
83
|
-
|
|
68
|
+
## 機能
|
|
69
|
+
|
|
70
|
+
### 用途・入力・出力
|
|
71
|
+
|
|
72
|
+
呼出し断片は本ページで初期化したサービスを使います。変数はアプリ側で用意する要求・Schema・リソース定義・設定を表します。初期化と失敗処理は各機能の例を参照し、HaiResult.success の確認と非同期処理の await を行ってください。
|
|
73
|
+
|
|
74
|
+
| 入口 | 用途 | 入力 | 出力 | 呼出し断片 |
|
|
75
|
+
| ---------------------------------- | -------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
76
|
+
| `serv.createApp` | API アプリを組立て | `contract, procedures, http?, iam?, middlewares?, transport?` | `ServHttpApp (fetch/request)` | `serv.createApp({ contract, procedures })` |
|
|
77
|
+
| `serv.listen` | Node リスナーを起動 | `app, { host?, port?, onClose?, onListening? }` | `ServNodeServer { server, address, close(): Promise<void> }` | `serv.listen(app, { host: "127.0.0.1", port: 3000 })` |
|
|
78
|
+
| `serv.toFetch` | Fetch ホストへ接続 | `ServHttpApp` | `(Request) => Promise<Response>` | `serv.toFetch(app)` |
|
|
79
|
+
| `serv.implement` | 領域ルートを実装 | `typed contract` | `ServImplementer; context/route/auth/permission/role/handle/build` | `serv.implement(contract)` |
|
|
80
|
+
| `serv.parseRequestContext` | 要求情報を解析 | `{ request, platform? }` | `ServContext with requestId/locale/accessToken; no verified session` | `serv.parseRequestContext({ request })` |
|
|
81
|
+
| `serv.buildAuthContextFactory` | 認証済み文脈を作成 | `(accessToken) => Promise<HaiResult<ServSession>>` | `CreateServContext; invalid credentials leave session absent` | `serv.buildAuthContextFactory(token => iam.auth.verifyToken(token))` |
|
|
82
|
+
| `serv.cors` | CORS を制限 | `{ origin?, methods?, allowedHeaders?, exposedHeaders?, credentials? }` | `ServMiddleware` | `serv.cors({ origin: "https://app.example.com", credentials: true })` |
|
|
83
|
+
| `serv.createRuntimeSecurityPolicy` | 実行時の安全方針 | `{ environment, corsOrigin, nativeOrigins? }` | `allowOrigin, secureRefreshCookie, exposeApiDocs` | `serv.createRuntimeSecurityPolicy({ environment: "production", corsOrigin: "https://app.example.com" })` |
|
|
84
|
+
| `serv.storageAssets` | 認可済み読取専用資源 | `ServStorageAssetsConfig with reader/key/MIME policy` | `GET/HEAD middleware with ETag` | `serv.storageAssets(assetOptions)` |
|
|
85
|
+
| `serv.generateSpec` | OpenAPI を生成 | `contract, { title?, version?, description?, apiPrefix? }` | `Promise<OpenAPI.Document>` | `serv.generateSpec(contract, { title: "Orders", version: "1.0.0" })` |
|
|
86
|
+
| `serv.validateInputOrFail` | 業務入力を検証 | `Zod schema, input, locale?` | `HaiResult<z.infer<Schema>>; failure contains safe field errors` | `serv.validateInputOrFail(InputSchema, input, context.locale)` |
|
|
87
|
+
| `serv.resolveRequestLocale` | 要求言語を解決 | `Headers` | `normalized supported locale` | `serv.resolveRequestLocale(request.headers)` |
|
|
88
|
+
| `serv.m` | モジュール文言を翻訳 | `message key, { locale?, ...messageOptions }` | `string` | `serv.m("serv_errorForbidden", { locale: "zh-CN" })` |
|
|
89
|
+
| `serv.WILDCARD_PERMISSION` | 全権限定数 | `none` | `string constant; only use in trusted permission policy` | `serv.WILDCARD_PERMISSION` |
|
|
90
|
+
| `serv.WILDCARD_ROLE` | 全ロール定数 | `none` | `string constant; does not authorize arbitrary requests` | `serv.WILDCARD_ROLE` |
|
|
91
|
+
|
|
92
|
+
- `serv.createApp(...)` → `serv.listen(app, { port })` で起動。アプリは Hono を import する必要がありません。
|
|
93
|
+
- フラットな API:`serv.createApp / serv.implement / serv.listen / serv.toFetch / serv.generateSpec / ...`。
|
|
94
|
+
- procedure のチェーン:`implement().context().route().auth/permission/role().handle().build()`。
|
|
95
|
+
- 独自パイプライン:`createApp({ middlewares })` で HTTP middleware を接続し、context ファクトリーでリクエストごとの値を注入。
|
|
96
|
+
- 読み取り専用 Storage:`serv.storageAssets(...)` で安全な key、MIME 許可リスト、GET/HEAD、ETag、キャッシュヘッダーを管理。
|
|
97
|
+
- 既定の機能 procedures:`createIamProcedures()`、`createStorageProcedures()`、`createAiProcedures()`。
|
|
98
|
+
- `build()` は通常の router を同期的に返すため、標準 feature のネストした procedures を直接選択できます。接続する contract と procedures を一致させ、未選択の入口は公開しません。
|
|
99
|
+
- 存在しないルートは翻訳された HaiResult JSON と HTTP 404 を返します。通信暗号化でも 404 を保ち、非 JSON 応答による 500 にしません。
|
|
100
|
+
- 安全な応答ヘッダー・ヘルスチェックを内蔵し、OpenAPI JSON・Scalar 文書・
|
|
101
|
+
- 任意の通信暗号化:`serv.createApp({ transport: { crypto, serverKeyRing, keyStore } })` で鍵交換とリクエスト・レスポンスの暗号化を自動接続。
|
|
102
|
+
- 任意の音声入口:`serv.createApp({ audio: { ai, verifyTicket, authorize } })` が共通 WebSocket を接続します。接続直後に単回 ticket で認証(`{ session, grant? }`、独立した事前認証 timeout)。各フレームを Zod と厳密な状態機械で検証し、入力・キュー・送信バッファの背圧と連鎖キャンセルを管理します。サーバーの認可処理で操作・モデル・声・割当量・同時使用数を確認します。
|
|
103
|
+
|
|
104
|
+
> ライフサイクル:`@h-ai/serv` は状態を持たない HTTP App の構成機能で、`init()` / `close()` は不要です。app 作成前に `iam` / `storage` / `ai` / `crypto` などを初期化し、`serv.listen(..., { onClose })` で逆順に解放します。`serv.createApp()` は transport 管理器の生成失敗など起動設定のエラーを即座に throw します。 onClose は SIGINT/SIGTERM 用の終了コールバックです。返された handle.close の直接呼出しは HTTP リスナーと音声接続を閉じ、業務モジュールはホストが別途逆順に解放します。
|
|
105
|
+
|
|
106
|
+
開始直後の close は未完了のバインドを取り消し、繰り返しの close は同じ結果を共有します。アドレス使用中など実際のリッスン失敗は正常終了として扱わず、close の拒否理由として保持します。
|
|
107
|
+
|
|
108
|
+
### host / port 設定
|
|
84
109
|
|
|
85
110
|
host/port は `config/_serv.yml` の `server` で指定し、`HOST` / `PORT` 環境変数 > 設定ファイル > `127.0.0.1:3000` の順です。既定の `127.0.0.1` はローカル専用で、コンテナー外に公開する場合は `0.0.0.0` にします:
|
|
86
111
|
|
|
@@ -93,7 +118,7 @@ server:
|
|
|
93
118
|
|
|
94
119
|
解決済みの `server` を `serv.listen` に渡します。`host` / `port` の明示的な指定で上書きすることもできます。
|
|
95
120
|
|
|
96
|
-
|
|
121
|
+
### 標準 fetch シグネチャのアダプター
|
|
97
122
|
|
|
98
123
|
toFetch は Node 互換ホストやテスト注入用の標準 fetch シグネチャです。パッケージルートは Node API に依存しており、それらを持たない Workers・Deno・エッジへの対応を保証しません。
|
|
99
124
|
|
|
@@ -102,11 +127,11 @@ const handler = serv.toFetch(app)
|
|
|
102
127
|
export default { fetch: handler }
|
|
103
128
|
```
|
|
104
129
|
|
|
105
|
-
Node `serv.listen()` は socket の接続元を自動で渡し、既定では転送ヘッダーを無視します。`http.trustedProxies` の IP/CIDR のみ信頼し、XFF を右から左へ調べ、最初の非信頼アドレスで停止します。その左側は無視します。信頼チェーンが不正・長すぎる場合は IP 未定義となり、代理をクライアントとして扱いません。代理は転送ヘッダーを清掃・追加してください。Fetch は `app.fetch(request, { remoteAddress: platformPeerAddress })` で信頼できる接続元を渡します。メタデータなしは IP 未定義です。任意のヘッダーをコピーしないでください。独自 `createContext(input)` は input 全体を再利用し、解決済み `input.ip`
|
|
130
|
+
Node `serv.listen()` は socket の接続元を自動で渡し、既定では転送ヘッダーを無視します。`http.trustedProxies` の IP/CIDR のみ信頼し、XFF を右から左へ調べ、最初の非信頼アドレスで停止します。その左側は無視します。信頼チェーンが不正・長すぎる場合は IP 未定義となり、代理をクライアントとして扱いません。代理は転送ヘッダーを清掃・追加してください。Fetch は `app.fetch(request, { remoteAddress: platformPeerAddress })` で信頼できる接続元を渡します。メタデータなしは IP 未定義です。任意のヘッダーをコピーしないでください。独自 `createContext(input)` は input 全体を再利用し、解決済み `input.ip` を保持します。
|
|
106
131
|
|
|
107
132
|
toFetch は Request・URL・文字列を標準 Request(input, init) で構築し、入力フィールドを変更せず method・headers・body・signal などを上書きします。消費済み・ロック中の本文や不正な本文指定は返却 Promise を reject します。派発前の取消しは handler を呼ばず、実行中の取消しは呼出側の待機を終了し、有効な signal をアプリへ伝えます。終了・失敗・取消しで listener を外し、取消し後に遅れて返る本文も cancel します。開始済みの外部効果は巻き戻せず、Response 返却後のストリーム終了はアプリが request.signal を消費して管理します。
|
|
108
133
|
|
|
109
|
-
|
|
134
|
+
### Audio WebSocket:ticket とサーバー認可
|
|
110
135
|
|
|
111
136
|
音声接続には短期・単回 ticket が必要です。認証済み HTTP endpoint で発行(推奨:`iam.ticket.issue`)し、`verifyTicket` でアトミックに消費します(推奨:`iam.ticket.consume`)。接続直後に独立 timeout で認証し、認証前に業務フレームを処理しません。返された `ServSession` と任意の `grant` を保存してから `authorize` で有料パラメーターを確認します:
|
|
112
137
|
|
|
@@ -132,7 +157,7 @@ const app = serv.createApp({
|
|
|
132
157
|
- `onSessionEnd` は成功・失敗・切断後に最大 1 回呼び、同時実行枠の解放に使用。
|
|
133
158
|
- ブラウザーの `createAudioClient({ url, getTicket })` は単回 `ticket` のみを URL に付けます。`getTicket(request)` は `operation` / `model` の概要から今回の操作専用 ticket を発行できます。通常の IAM access token は渡さないでください。
|
|
134
159
|
|
|
135
|
-
|
|
160
|
+
### 通信暗号化(`@h-ai/crypto` と共通)
|
|
136
161
|
|
|
137
162
|
serv 独自の暗号化ファクトリーはなく、`crypto.transport` から管理器を作成します。利用側はトップレベルの `crypto` のみを渡します:
|
|
138
163
|
|
|
@@ -199,14 +224,16 @@ const app = serv.createApp({
|
|
|
199
224
|
contract,
|
|
200
225
|
procedures,
|
|
201
226
|
http: servConfig.http,
|
|
202
|
-
middlewares: [
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
227
|
+
middlewares: [
|
|
228
|
+
{
|
|
229
|
+
middleware: serv.cors({
|
|
230
|
+
origin: servConfig.cors.origin,
|
|
231
|
+
credentials: servConfig.cors.credentials,
|
|
232
|
+
allowedHeaders: [...servConfig.cors.allowedHeaders],
|
|
233
|
+
exposedHeaders: [...servConfig.cors.exposedHeaders],
|
|
234
|
+
}),
|
|
235
|
+
}
|
|
236
|
+
],
|
|
210
237
|
transport: servConfig.transport === false
|
|
211
238
|
? undefined
|
|
212
239
|
: {
|
|
@@ -223,17 +250,15 @@ const app = serv.createApp({
|
|
|
223
250
|
|
|
224
251
|
> **安全方針**:`transport` 有効時は `excludePaths` と鍵交換以外で有効な `X-Client-Id` が必要です。不足・未知の clientId は 400、暗号化不能・非 JSON 応答・上限超過はエラーになり、平文の業務データは渡しません。
|
|
225
252
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
空の POST も空平文の認証封装を送ります。GET/HEAD は X-Hai-Transport ヘッダー、204/304/HEAD など本文なし応答も同じヘッダーを使います。他の要求・応答は JSON 本文と X-Encrypted: true を使います。クライアントは元の要求コンテキストと HTTP status を照合し、マーカー欠落、旧形式、認証失敗では reject します。平文 endpoint は Serv/Kit の `encryption: disabled` ルールまたはホストの `excludePaths` で明示的に許可し、呼出側も通常の要求を選びます。API Client/Kit のラッパーは自身の `excludePaths` を使用します。CORS では X-Hai-Transport を許可・公開してください。鍵交換の公開鍵認証は TLS に依存し、IAM の業務認可は別に必要です。
|
|
253
|
+
転送は認証済み v2 封装を使い、署名と MAC を確認してから復号し、方法・パス・時刻・応答状態を束縛します。共有 registry が nonce を原子的に消費します。TLS・信頼する代理・業務認可・冪等性はホストの責任です。独自 CORS 入口は X-Hai-Transport を許可・公開してください。詳細は[転送プロトコル](../crypto/TRANSPORT-PROTOCOL.md)を参照してください。
|
|
229
254
|
|
|
230
255
|
`transport.maxPlaintextBytes` でリクエストとレスポンスの JSON 平文容量を共通設定します。単位は UTF-8 バイト、既定値は 1 MiB、有効範囲は 1 KiB~64 MiB の整数です。クライアントは鍵交換で上限を取得し、`resolveTransportBodyLimits()` が暗号文とプロキシ容量(平文の 2 倍 + 32 KiB)を算出します。鍵交換、公開鍵、認証ヘッダー、防リプレイ制約は独立です。超過時は元ストリームを停止して reader を解放します。要求は 413、応答は暗号化された 500 容量エラーとなり、`TransportBodyTooLargeError` / `hai:crypto:070` で識別できます。 [Transport capacity and upgrade](../crypto/TRANSPORT-CAPACITY.md)。
|
|
231
256
|
|
|
232
257
|
通常業務は `transport.maxPlaintextBytes`(auto/省略は 1 MiB)を維持します。Feedback の受理・証拠・添付・ZIP ダウンロードは `routePlaintextBytes` と `feedback.budget.maxTransportBytes` で別途宣言します。`*` は完全な1パス区間のみ一致し、ホストの明示値が優先です。ハンドシェイク、adapter-node、プロキシは経路の最大値を使用します。HTTP アダプターは個別宣言された 1 MiB 超の資料経路を最大2件、通常業務を別枠で16件まで許可し、応答完了・失敗時に解放します。混雑は503 / `hai:crypto:072` を返し、業務書込は自動再送しません。
|
|
233
258
|
|
|
234
|
-
|
|
259
|
+
Serv/Kit は要求の結合、署名、MAC を検証した後、時刻の許容範囲を超えた要求に暗号化した HTTP 400 を返します。クライアントは `TransportTimeError`(`hai:crypto:071`)をスローし、検証時のサーバー UTC 時刻を表示して、自動時刻同期を有効にするか手動で時刻を設定してから再読み込みするよう案内します。`serverTime` は Unix ミリ秒です。旧サーバーが値を返さない場合も時刻調整の案内を表示します。`api-client` は `VALIDATION_FAILED` に変換し、`cause.transportCode` と `cause.serverTime` を保持します。
|
|
235
260
|
|
|
236
|
-
|
|
261
|
+
### Token 認証(`iam` / `refreshCookie`)
|
|
237
262
|
|
|
238
263
|
`serv.createApp` にトップレベルの `iam` を渡すと、次を自動構成します:
|
|
239
264
|
|
|
@@ -277,7 +302,7 @@ const app = serv.createApp({
|
|
|
277
302
|
|
|
278
303
|
標準 `auth.logout` は input.accessToken または Authorization Bearer の完全な Token を取り消し、有効な access 認証を前提にしません。userId による権限や取消対象は受け付けません。Token 不足は失敗、不存在 Token は冪等成功です。共有ストア障害は失敗 HaiResult を保ち、Cookie middleware は成功時だけ refresh Cookie を消去します。通常 procedure の HTTP 200 / HaiResult 契約は同じなので、クライアントは HTTP status だけでなく success を確認します。
|
|
279
304
|
|
|
280
|
-
|
|
305
|
+
### 独自 procedure(認証 + 権限)
|
|
281
306
|
|
|
282
307
|
```ts
|
|
283
308
|
const procedures = serv
|
|
@@ -291,7 +316,7 @@ const procedures = serv
|
|
|
291
316
|
.build()
|
|
292
317
|
```
|
|
293
318
|
|
|
294
|
-
|
|
319
|
+
### 高度なパイプライン(HTTP middleware + context + route guard)
|
|
295
320
|
|
|
296
321
|
`@h-ai/serv` のパイプラインは 3 層で、それぞれの拡張入口があります:
|
|
297
322
|
|
|
@@ -299,7 +324,7 @@ const procedures = serv
|
|
|
299
324
|
2. **context 層**:`verifyToken` / `createContext` / `serv.buildAuthContextFactory()` で拡張。
|
|
300
325
|
3. **route guard 層**:`.auth()` / `.permission()` / `.role()` で認証・認可を宣言。
|
|
301
326
|
|
|
302
|
-
|
|
327
|
+
#### 1) 独自 HTTP middleware
|
|
303
328
|
|
|
304
329
|
ログ・trace・レート制限・CORS・テナントヘッダーなどの **HTTP 共通処理**、または JSON/oRPC contract に適さない WebSocket・ファイル・バイナリー endpoint には `middlewares` を使います。一般的な CORS は `serv.cors(...)` を再利用できます:
|
|
305
330
|
|
|
@@ -355,7 +380,7 @@ const app = serv.createApp({
|
|
|
355
380
|
2. 組み込み CORS 応答処理;鍵交換と preflight のアクセス判定。
|
|
356
381
|
3. 組み込み `transport`(有効時)と各リクエストのアクセス判定。
|
|
357
382
|
4. 独自の `middlewares`。
|
|
358
|
-
5. health / refresh-cookie / OpenAPI /
|
|
383
|
+
5. health / refresh-cookie / OpenAPI / docs / oRPC routes
|
|
359
384
|
|
|
360
385
|
補足:
|
|
361
386
|
|
|
@@ -368,7 +393,7 @@ const app = serv.createApp({
|
|
|
368
393
|
- ブラウザーが `X-Encrypted` などを読む場合は `serv.cors({ exposedHeaders: [...] })` で明示的に公開。
|
|
369
394
|
- この層は HTTP middleware context を操作し、`HaiResult` ではなく **HTTP Response** を返します。
|
|
370
395
|
|
|
371
|
-
|
|
396
|
+
#### 2) 独自 context パイプライン
|
|
372
397
|
|
|
373
398
|
`Bearer token -> session` を再利用し、テナント・workspace・送信元などの項目を追加する場合は、
|
|
374
399
|
`serv.buildAuthContextFactory()` で独自の `createContext` を構成してください:
|
|
@@ -395,7 +420,7 @@ const app = serv.createApp({
|
|
|
395
420
|
- 既定の認証 context に項目を追加:`buildAuthContextFactory(...) + createContext`。
|
|
396
421
|
- context 生成全体を置換:直接 `createContext`。
|
|
397
422
|
|
|
398
|
-
|
|
423
|
+
#### 3) Route guard
|
|
399
424
|
|
|
400
425
|
公開 procedure は `.route(path, handler)` を使い、ガードが必要な場合は次を使います:
|
|
401
426
|
`.route(path).auth/permission/role().handle(handler)`:
|
|
@@ -419,6 +444,7 @@ const procedures = serv
|
|
|
419
444
|
|
|
420
445
|
- `.permission()` と `.role()` は認証も要求します。失敗時の HaiResult の `error.httpStatus` は未認証で 401、権限不足で 403 ですが、通常の procedure の HTTP 応答は既定で 200 です。
|
|
421
446
|
- 同じルートの複数 permission/role はすべて満たす必要があります。`serv.WILDCARD_PERMISSION` / `serv.WILDCARD_ROLE` は各分類を許可。
|
|
447
|
+
- 権限は IAM/Kit と同じコロン接頭辞にも対応します。`users:*` は `users:read` に一致し、`users2:read` やドット区切りの権限には一致しません。
|
|
422
448
|
- 未処理の handler 例外は INTERNAL_ERROR の HaiResult に変換。予期する業務失敗は直接 `err(...)` を返します。
|
|
423
449
|
- path・input・output は contract から推論。重複・未知のパスを拒否し、完全に実装するまで `.build()` は呼べません。
|
|
424
450
|
|
|
@@ -426,9 +452,9 @@ const procedures = serv
|
|
|
426
452
|
|
|
427
453
|
- `src/pipelines/*` は内部の既定実装で、公開サブパス API ではありません。
|
|
428
454
|
- アプリ向けの安定した入口はルートの `@h-ai/serv`。
|
|
429
|
-
- `securityHeaders` / `
|
|
455
|
+
- `securityHeaders` / `buildHaiErrorBody` などの内部 helper は独立サブパスとして公開しません。
|
|
430
456
|
|
|
431
|
-
|
|
457
|
+
### httpOnly Cookie 認証(本番推奨)
|
|
432
458
|
|
|
433
459
|
httpOnly cookie モードは refresh token をサーバー管理の Cookie に保存し、ブラウザー JS から読めなくして XSS による漏洩を抑えます。
|
|
434
460
|
|
|
@@ -485,13 +511,16 @@ await apiClient.init({
|
|
|
485
511
|
})
|
|
486
512
|
```
|
|
487
513
|
|
|
488
|
-
>
|
|
514
|
+
> リフレッシュ Cookie には `HttpOnly` と `SameSite=Strict` が付き、パスは `{apiPrefix}/auth/refresh` に限定されます。本番では既定で `Secure` も付きます。
|
|
515
|
+
|
|
516
|
+
更新失敗にも公開エラー処理を適用します。5xx と欠落・不正なステータスは安全なメッセージと traceId を返し、内部原因はサーバーログだけに残します。更新 Cookie を消去するのは認証失効の 401 のみです。レート制限やバックエンド障害では再試行できるよう資格情報を保持します。自動監査にも同じ正規化済み失敗ステータスを使います。
|
|
489
517
|
|
|
490
518
|
### API 一覧
|
|
491
519
|
|
|
492
|
-
- `serv.createApp(options)`:独自 middleware、ヘルスチェック、OpenAPI
|
|
520
|
+
- `serv.createApp(options)`:独自 middleware、ヘルスチェック、OpenAPI、任意の文書を備えた HTTP App を作成。
|
|
493
521
|
- `serv.parseRequestContext({ request })`:Bearer token + requestId を抽出し、session は設定しません。
|
|
494
522
|
- `serv.listen(app, options)`:Node.js HTTP サービスを起動し、`{ server, address, close }` を返す。
|
|
523
|
+
返された `close()` は冪等です。新規接続を止め、Audio WebSocket を閉じ、応答しない音声接続を 1 秒後に強制終了して HTTP 終了を待ちます。直接呼出しでもプロセスのシグナルリスナーを解除します。業務モジュールは引き続きホストが逆順に解放します。
|
|
495
524
|
- `serv.toFetch(app)`:標準の `fetch(Request)` handler に変換。
|
|
496
525
|
- `serv.generateSpec(contract, options)`:contract から OpenAPI 3.1 spec を生成。
|
|
497
526
|
- `serv.storageAssets(config)`:key/MIME 許可リスト、GET/HEAD、ETag 付きの読み取り専用 Storage middleware。
|
|
@@ -524,7 +553,81 @@ await apiClient.init({
|
|
|
524
553
|
|
|
525
554
|
`http.trustedProxies` は既定 `auto` です。通常のホストでは転送ヘッダーを無視し、管理対象 Compose の非公開バックエンド構成だけでコンテナのネットワークを採用します。独自の代理は実際の IP/CIDR を指定し、`[]` は信頼を無効化します。代理はヘッダーを清掃し、Node・Fetch は信頼できる接続元情報を提供します。独立ファイル・外側の代理・復旧は[リクエスト制御手引き](../../docs/usage/configuration/request-access.md)を参照してください。
|
|
526
555
|
|
|
527
|
-
###
|
|
556
|
+
### 設定による認証画面
|
|
557
|
+
|
|
558
|
+
規約は設定の userAgreementMarkdown/privacyPolicyMarkdown または URL を使用できます。同じ規約は Markdown 本文を優先して安全な Modal で描画します。
|
|
559
|
+
|
|
560
|
+
createIamProcedures({ iam }) は公開設定、LDAP/OTP/パスワード、登録、復旧を直接実装します。LDAP も既存の Cookie/ネイティブ Token 方針を使用し、自助登録が無効でも管理者のユーザー作成は権限検証の下で利用できます。
|
|
561
|
+
|
|
562
|
+
[完整 IAM 配置 / IAM configuration](../iam/README.ja.md)
|
|
563
|
+
|
|
564
|
+
### リクエスト言語の分離
|
|
565
|
+
|
|
566
|
+
Node リクエストと SSR は `core.i18n.runWithLocale(locale, () => handleRequest())` で囲みます。SvelteKit は `kit.i18n.runWithLocale(locale, () => resolve(event))` を使えます。serv はリクエストヘッダーから自動設定します。非同期の子処理も言語を継承し、明示的な `MessageOptions.locale`、リクエスト言語、起動時の既定値の順で優先します。コンテキスト内の `setGlobalLocale` はそのリクエストだけを変更します。コンテキスト外の設定は起動時に限定し、リクエスト hook から呼びません。
|
|
567
|
+
|
|
568
|
+
ブラウザーはアプリ全体の `setGlobalLocale` / `createLocaleStore` を使います。SSR の UI store 初期化と setter は自身の状態のみ変更し、メッセージはリクエストコンテキストを参照します。`runWithLocale` は Node 入口専用です。
|
|
569
|
+
|
|
570
|
+
### 入力制約と HTTP 機能
|
|
571
|
+
|
|
572
|
+
キーは UTF-8 で最大 1024 バイトで、絶対パス、バックスラッシュ、制御文字、ドットセグメントを拒否します。公開 HTTP 一括削除は一度に 1–1000 件です。共通規則はブラウザー対応の `@h-ai/storage/schemas` から参照できます。署名有効期限は整数 1–604800 秒(Node 既定 3600、HTTP 既定 900)。アップロード署名の入力は key/contentType/expiresIn のみで、contentLength/maxSize/metadata/checksum は未対応です。不正入力は HTTP 400、typed client では VALIDATION_FAILED です。FileUpload の制限は選択時の補助であり、実際のオブジェクトはサーバーで検証してください。Node のストリーム/checksum/条件付き書き込みには既定の HTTP エンドポイントがありません。[機能と認可の対応表(中国語)](../storage/README.md#存储输入约束与跨端能力)を参照してください。
|
|
573
|
+
|
|
574
|
+
### オプションのフィードバックサービスと信頼範囲
|
|
575
|
+
|
|
576
|
+
`@h-ai/serv/features/feedback` の `createFeedbackProcedures` で `apiContract.feedback` を接続します。通常のランタイム入口は feedback サービスを読み込みません。ホストで reldb/storage/reach/feedback を初期化し、検証済みセッションと配置ポリシーから範囲を作ります。null またはユーザー不一致は拒否し、ドメイン側も範囲と権限を検証します。
|
|
577
|
+
|
|
578
|
+
ヘルスチェック・404・中間件の早期応答を含む全 HTTP 応答に上限付き X-Request-Id を付けます。通常の業務ログでは既存 createContext の省略可能な scopeId を信頼できるホストから設定します。クライアントのヘッダーからは取得しません。HTTP 境界で古い認証情報を消去し、認証後の非同期処理に現在の ID とユーザーを伝播します。
|
|
579
|
+
|
|
580
|
+
```ts
|
|
581
|
+
import { apiContract } from '@h-ai/api-contract'
|
|
582
|
+
import { feedback } from '@h-ai/feedback'
|
|
583
|
+
import { iam } from '@h-ai/iam'
|
|
584
|
+
import { serv } from '@h-ai/serv'
|
|
585
|
+
import { createFeedbackProcedures } from '@h-ai/serv/features/feedback'
|
|
586
|
+
|
|
587
|
+
const contract = apiContract.create({ feedback: apiContract.feedback })
|
|
588
|
+
const authenticate = serv.buildAuthContextFactory(token => iam.auth.verifyToken(token))
|
|
589
|
+
const procedures = {
|
|
590
|
+
feedback: createFeedbackProcedures({
|
|
591
|
+
feedback,
|
|
592
|
+
resolveContext: context => context.scopeId
|
|
593
|
+
? { scopeId: context.scopeId, userId: context.session.userId, permissions: context.session.permissions }
|
|
594
|
+
: null,
|
|
595
|
+
}),
|
|
596
|
+
}
|
|
597
|
+
const app = serv.createApp({
|
|
598
|
+
contract,
|
|
599
|
+
procedures,
|
|
600
|
+
createContext: async input => ({
|
|
601
|
+
...await authenticate(input),
|
|
602
|
+
scopeId: 'configured-product'
|
|
603
|
+
}),
|
|
604
|
+
})
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
### 任意の対話チャレンジ
|
|
608
|
+
|
|
609
|
+
標準 IAM procedure は operation を受けて公開 ALTCHA チャレンジを返す POST /auth/captcha を含みます。有効時はパスワード/OTP/LDAP、登録、通知要求、匿名の旧パスワード更新で IAM が単用証明を検証し、captcha を保持してクライアント判断に依存しません。
|
|
610
|
+
|
|
611
|
+
方針・既定値・復旧範囲は次を参照: [IAM](../iam/README.ja.md)。
|
|
612
|
+
|
|
613
|
+
### ページ離脱時のセッション方針
|
|
614
|
+
|
|
615
|
+
`session.endOnPageHide` の既定値は `false` です。ホストは `actions.bindPageExit({ enabled: config.session.endOnPageHide })` を呼び、破棄時に戻り値の解除関数を実行します。標準 admin/H5/fullstack テンプレートは接続済みです。BFCache 以外の `pagehide`(再読み込み、文書全体の移動を含む)で POST `/auth/session/close` を最善努力で送信し、保持する access と関連 refresh を失効します。既存の CSRF/暗号化転送を使用し、`keepalive` を付け、自動更新しません。古い応答が新しいログインを上書きしないよう Cookie/ローカル資格情報を消去しません。明示的ログアウトは通常どおり失効と消去を行います。ブラウザーが離脱要求を送らない場合もあるため、業務に応じてサーバーの `idleTimeout`/`absoluteTimeout` を併用してください。閉じた瞬間の失効は保証できません。
|
|
616
|
+
|
|
617
|
+
## 設定
|
|
618
|
+
|
|
619
|
+
### 用途に応じた設定入口
|
|
620
|
+
|
|
621
|
+
| 設定入口 | 用途 | 入力 | 効果 | 例 |
|
|
622
|
+
| ------------------------------------------------------ | ------------------ | ------------------------------------------------------------------------ | -------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
623
|
+
| `server` | Node リスナー | host/port; HOST and PORT take precedence | options passed to serv.listen; default 127.0.0.1:3000 | `server: { host: 127.0.0.1, port: 3000 }` |
|
|
624
|
+
| `http.apiPrefix` | API 接頭辞 | absolute validated prefix, default /api/v1 | OpenAPI/REST mounting prefix | `http: { apiPrefix: /api/v1 }` |
|
|
625
|
+
| `http.health` | 生存と準備確認 | path, readyPath | health/readiness routes | `health: { path: /health, readyPath: /ready }` |
|
|
626
|
+
| `http.openapi / http.docs` | API 文書 | false or explicit path/authorization settings | spec/Scalar routes; production exposure controlled by host policy | `openapi: false` |
|
|
627
|
+
| `http.trustedProxies / http.access` | 信頼する入口 | proxy list/auto and access policy | trusted address and per-request entry policy | `trustedProxies: []` |
|
|
628
|
+
| `cors` | CORS 方針 | origin/nativeOrigins/credentials/allowedHeaders/exposedHeaders | serv.cors middleware input; explicit production allowlist | `cors: { origin: https://app.example.com, credentials: true }` |
|
|
629
|
+
| `transport` | 認証付き転送 | false or public paths/limits; serverKeyRing/keyStore injected separately | handshake and authenticated request/response handling | `transport: { maxPlaintextBytes: auto }` |
|
|
630
|
+
| `createApp.iam / refreshCookie / nativeTokenTransport` | ホスト認証の組立て | initialized IAM, secure Cookie policy and native transport rule | trusted session/token channel; frontend receives only allowed fields | `serv.createApp({ contract, procedures, iam })` |
|
|
528
631
|
|
|
529
632
|
`@h-ai/serv` 自体は YAML を走査しません。`@h-ai/core` で `config/_serv.yml` を読み、解析結果を `serv.createApp()` に渡してください。
|
|
530
633
|
|
|
@@ -574,7 +677,6 @@ http:
|
|
|
574
677
|
health:
|
|
575
678
|
path: /health
|
|
576
679
|
readyPath: /ready
|
|
577
|
-
rpc: false
|
|
578
680
|
|
|
579
681
|
cors:
|
|
580
682
|
origin: https://app.example.com
|
|
@@ -601,11 +703,10 @@ transport:
|
|
|
601
703
|
|
|
602
704
|
`ServHttpConfigInput`:
|
|
603
705
|
|
|
604
|
-
- `apiPrefix`:既定 `/api/v1
|
|
706
|
+
- `apiPrefix`:既定 `/api/v1`。`/somepath/api/v1` などの絶対接頭辞を指定できます。英数字・アンダースコア・ハイフン・点を使用し、末尾のスラッシュ、空区間、単独の `.`/`..`、クエリとフラグメントは拒否します。ヘルス・文書・公開ストレージのパスも同じディレクトリに別途設定します。
|
|
605
707
|
- `health`:既定 `{ path: '/health', readyPath: '/ready' }`。
|
|
606
708
|
- `openapi`:既定 `false`。`{ path: '/openapi.json' }` で有効化。
|
|
607
|
-
- `docs`:既定 `false`。`{ path: '/docs' }`
|
|
608
|
-
- `rpc`:既定 `false`。`{ prefix: '/rpc', access: 'loopback' | 'private-network' | 'gateway-only', gatewayHeader?, gatewaySecret? }` で有効化。
|
|
709
|
+
- `docs`:既定 `false`。`{ path: '/docs' }` で有効化。文書ディレクトリ内の `_hai/scalar.js`(既定 `/_hai/scalar.js`)で Scalar UI を配信し、外部 CDN は不要です。スクリプトと spec の URL は相対指定です。ゲートウェイが接頭辞を保持する場合は docs/openapi も転送範囲内に配置し、接頭辞を除去する構成にも対応します。`requireAuth` は後端で検証します。
|
|
609
710
|
- `cors`:origin・credentials・allowedHeaders・exposedHeaders を設定し、`serv.cors(...)` で接続。
|
|
610
711
|
- `transport`:`config/_serv.yml` のトップレベル設定。`servConfig.transport` を `createApp({ transport })` に渡し、既定の鍵交換サブパスは `/_hai/key-exchange`。
|
|
611
712
|
|
|
@@ -626,33 +727,34 @@ const app = serv.createApp({
|
|
|
626
727
|
openapi: security.exposeApiDocs ? servConfig.http.openapi : false,
|
|
627
728
|
docs: security.exposeApiDocs ? servConfig.http.docs : false,
|
|
628
729
|
},
|
|
730
|
+
iam,
|
|
629
731
|
refreshCookie: { secure: security.secureRefreshCookie },
|
|
630
|
-
middlewares: [
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
732
|
+
middlewares: [
|
|
733
|
+
{
|
|
734
|
+
middleware: serv.cors({
|
|
735
|
+
origin: security.allowOrigin,
|
|
736
|
+
credentials: servConfig.cors.credentials,
|
|
737
|
+
allowedHeaders: [...servConfig.cors.allowedHeaders],
|
|
738
|
+
exposedHeaders: [...servConfig.cors.exposedHeaders],
|
|
739
|
+
}),
|
|
740
|
+
}
|
|
741
|
+
],
|
|
638
742
|
})
|
|
639
743
|
```
|
|
640
744
|
|
|
641
|
-
本番では `_serv.yml` の `cors.origin`
|
|
642
|
-
|
|
643
|
-
### 設定による認証画面
|
|
745
|
+
本番では `_serv.yml` の `cors.origin` に許可するオリジンを明示し、OpenAPI と docs は無効になります。Web と Capacitor/Tauri のオリジンは、正規化した値全体で照合します。
|
|
644
746
|
|
|
645
|
-
|
|
747
|
+
管理されたプライベートネットワークや VPN の本番 HTTP 入口では、バックエンドのプロセス環境に `HAI_SERV_REFRESH_COOKIE_SECURE=false` を設定できます。再起動してログインし直すと、ログイン応答と更新応答の Cookie から `Secure` が外れ、HTTP でリフレッシュ認証情報が平文で送信されます。`true` は `Secure` を強制し、空欄や未対応の値では環境の既定値が使われます。`serv.createApp` を直接呼ぶ場合は、明示した `refreshCookie.secure`、この環境変数、`NODE_ENV` または `HAI_CORE_ENV` による本番判定の順に適用されます。
|
|
646
748
|
|
|
647
|
-
|
|
749
|
+
### 転送容量とモジュール予算
|
|
648
750
|
|
|
649
|
-
|
|
751
|
+
設定 transport.maxPlaintextBytes は 1 KiB~64 MiB の明示整数または auto を受け付けます。auto/省略は undefined に正規化され、要件なしの既定は 1 MiB、フィードバックのホストでは crypto.resolveTransportBodyLimits の requiredPlaintextBytes で統合します。明示上限の不足は起動/梱包時の失敗とし、上書きしません。プロキシ・adapter-node・ハンドシェイク・実行時の予算を一致させ、添付・展開・行数の制限は独立して検証します。
|
|
650
752
|
|
|
651
|
-
|
|
753
|
+
## エラー処理
|
|
652
754
|
|
|
653
755
|
通常の procedure は認証・認可・業務の失敗と捕捉した handler 例外を HaiResult として既定で HTTP 200 で返します。`error.httpStatus` はドメインエラーのメタデータで、HTTP 応答ステータスを変更しません。クライアントは `success` と `error.code` を確認してください。未認証では `error.httpStatus=401`、DB クエリー失敗や INTERNAL_ERROR では 500 です。
|
|
654
756
|
|
|
655
|
-
oRPC contract の入力検証は procedure の前に失敗すると HTTP 400 となり、ローカライズ済み body に `error.httpStatus=400` とフィールドエラーを含みます。procedure 内の `serv.validateInputOrFail` の失敗は HTTP 200 のままです。HTTP middleware、refresh-cookie、OpenAPI
|
|
757
|
+
oRPC contract の入力検証は procedure の前に失敗すると HTTP 400 となり、ローカライズ済み body に `error.httpStatus=400` とフィールドエラーを含みます。procedure 内の `serv.validateInputOrFail` の失敗は HTTP 200 のままです。HTTP middleware、refresh-cookie、OpenAPI のアクセス確認、transport のプロトコルエラーは各自の HTTP 応答契約に従います。api-client は実際の HTTP 401、または HTTP 200 の有効な失敗 HaiResult(`success=false`、文字列の `error.code/message`、`error.httpStatus=401`)で自動更新し、成功後に一度だけ再試行します。組み込みのログイン・登録と設定された更新パスは対象外で、403/500 でも更新しません。transport 有効時は更新と再試行も同じ暗号化 fetch を使用します。
|
|
656
758
|
|
|
657
759
|
- 既定 procedures はすべて `HaiResult<T>` を返し、クライアントで `result.success` を確認。
|
|
658
760
|
- 認証失敗 → `HaiCommonError.UNAUTHORIZED`。`.auth()` が `context.locale` でローカライズ。
|
|
@@ -697,49 +799,9 @@ const procedures = serv
|
|
|
697
799
|
.build()
|
|
698
800
|
```
|
|
699
801
|
|
|
700
|
-
>
|
|
701
|
-
|
|
702
|
-
### リクエスト言語の分離
|
|
703
|
-
|
|
704
|
-
Node リクエストと SSR は `core.i18n.runWithLocale(locale, () => handleRequest())` で囲みます。SvelteKit は `kit.i18n.runWithLocale(locale, () => resolve(event))` を使えます。serv はリクエストヘッダーから自動設定します。非同期の子処理も言語を継承し、明示的な `MessageOptions.locale`、リクエスト言語、起動時の既定値の順で優先します。コンテキスト内の `setGlobalLocale` はそのリクエストだけを変更します。コンテキスト外の設定は起動時に限定し、リクエスト hook から呼びません。
|
|
705
|
-
|
|
706
|
-
ブラウザーはアプリ全体の `setGlobalLocale` / `createLocaleStore` を使います。SSR の UI store 初期化と setter は自身の状態のみ変更し、メッセージはリクエストコンテキストを参照します。`runWithLocale` は Node 入口専用です。
|
|
707
|
-
|
|
708
|
-
## 入力制約と HTTP 機能
|
|
709
|
-
|
|
710
|
-
キーは UTF-8 で最大 1024 バイトで、絶対パス、バックスラッシュ、制御文字、ドットセグメントを拒否します。公開 HTTP 一括削除は一度に 1–1000 件です。共通規則はブラウザー対応の `@h-ai/storage/schemas` から参照できます。署名有効期限は整数 1–604800 秒(Node 既定 3600、HTTP 既定 900)。アップロード署名の入力は key/contentType/expiresIn のみで、contentLength/maxSize/metadata/checksum は未対応です。不正入力は HTTP 400、typed client では VALIDATION_FAILED です。FileUpload の制限は選択時の補助であり、実際のオブジェクトはサーバーで検証してください。Node のストリーム/checksum/条件付き書き込みには既定の HTTP エンドポイントがありません。[機能と認可の対応表(中国語)](../storage/README.md#存储输入约束与跨端能力)を参照してください。
|
|
711
|
-
|
|
712
|
-
## オプションのフィードバックサービスと信頼範囲
|
|
802
|
+
> 下流の 4xx メッセージは秘匿処理後に公開します。5xx と欠落・不正なステータスは traceId 付きの安全な内部エラーメッセージに統一し、完全な原因はサーバーログだけに記録します。`HaiError` は `messageKey/params` を持たないため、ドメインメッセージの i18n は生成元のモジュールでリクエストの locale を受け取って行います。
|
|
713
803
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
ヘルスチェック・404・中間件の早期応答を含む全 HTTP 応答に上限付き X-Request-Id を付けます。通常の業務ログでは既存 createContext の省略可能な scopeId を信頼できるホストから設定します。クライアントのヘッダーからは取得しません。HTTP 境界で古い認証情報を消去し、認証後の非同期処理に現在の ID とユーザーを伝播します。
|
|
717
|
-
|
|
718
|
-
```ts
|
|
719
|
-
import { apiContract } from '@h-ai/api-contract'
|
|
720
|
-
import { feedback } from '@h-ai/feedback'
|
|
721
|
-
import { iam } from '@h-ai/iam'
|
|
722
|
-
import { serv } from '@h-ai/serv'
|
|
723
|
-
import { createFeedbackProcedures } from '@h-ai/serv/features/feedback'
|
|
724
|
-
|
|
725
|
-
const contract = apiContract.create({ feedback: apiContract.feedback })
|
|
726
|
-
const authenticate = serv.buildAuthContextFactory(token => iam.auth.verifyToken(token))
|
|
727
|
-
const procedures = {
|
|
728
|
-
feedback: createFeedbackProcedures({
|
|
729
|
-
feedback,
|
|
730
|
-
resolveContext: context => context.scopeId
|
|
731
|
-
? { scopeId: context.scopeId, userId: context.session.userId, permissions: context.session.permissions }
|
|
732
|
-
: null,
|
|
733
|
-
}),
|
|
734
|
-
}
|
|
735
|
-
const app = serv.createApp({
|
|
736
|
-
contract,
|
|
737
|
-
procedures,
|
|
738
|
-
createContext: async input => ({ ...await authenticate(input), scopeId: 'configured-product' }),
|
|
739
|
-
})
|
|
740
|
-
```
|
|
741
|
-
|
|
742
|
-
## エラー表示とログの関連付け
|
|
804
|
+
### エラー表示とログの関連付け
|
|
743
805
|
|
|
744
806
|
ネイティブ Audio WebSocket も安全なエラー境界を使います。Provider/SDK の内部情報やスタックはマスク済みログだけに残し、クライアントには安定コード、安全な表示、traceId を返します。独自 CORS 設定でも機密操作の X-Hai-Reauthentication を許可してください。Serv/Kit の既定設定には含まれます。
|
|
745
807
|
|
|
@@ -749,20 +811,6 @@ HTTP/UI 境界には core.error.toPublic(error, traceId?)、再翻訳後の表
|
|
|
749
811
|
|
|
750
812
|
audit の初期化後、procedure は検証済み主体・信頼できる接続元・要求/範囲 ID を関連付け、業務 audit.log に継承します。書込と失敗/拒否は request.execute、入力検証・プロトコル・ミドルウェア・ルート未検出の HTTP エラーは http.reject を記録します。操作・メソッド・状態・理由コードのみを記録し、要求本文・動的 URL・トークン・内部エラーは記録しません。capture.enabled で自動記録を無効化、capture.includeReads で成功読取を有効化できます。明示的業務 log は維持し、業務の完了/失敗はアプリが記録します。
|
|
751
813
|
|
|
752
|
-
## 任意の対話チャレンジ
|
|
753
|
-
|
|
754
|
-
標準 IAM procedure は operation を受けて公開 ALTCHA チャレンジを返す POST /auth/captcha を含みます。有効時はパスワード/OTP/LDAP、登録、通知要求、匿名の旧パスワード更新で IAM が単用証明を検証し、captcha を保持してクライアント判断に依存しません。
|
|
755
|
-
|
|
756
|
-
方針・既定値・復旧範囲は次を参照: [IAM](../iam/README.ja.md)。
|
|
757
|
-
|
|
758
|
-
## ページ離脱時のセッション方針
|
|
759
|
-
|
|
760
|
-
`session.endOnPageHide` の既定値は `false` です。ホストは `actions.bindPageExit({ enabled: config.session.endOnPageHide })` を呼び、破棄時に戻り値の解除関数を実行します。標準 admin/H5/fullstack テンプレートは接続済みです。BFCache 以外の `pagehide`(再読み込み、文書全体の移動を含む)で POST `/auth/session/close` を最善努力で送信し、保持する access と関連 refresh を失効します。既存の CSRF/暗号化転送を使用し、`keepalive` を付け、自動更新しません。古い応答が新しいログインを上書きしないよう Cookie/ローカル資格情報を消去しません。明示的ログアウトは通常どおり失効と消去を行います。ブラウザーが離脱要求を送らない場合もあるため、業務に応じてサーバーの `idleTimeout`/`absoluteTimeout` を併用してください。閉じた瞬間の失効は保証できません。
|
|
761
|
-
|
|
762
|
-
## 転送容量とモジュール予算
|
|
763
|
-
|
|
764
|
-
設定 transport.maxPlaintextBytes は 1 KiB~64 MiB の明示整数または auto を受け付けます。auto/省略は undefined に正規化され、要件なしの既定は 1 MiB、フィードバックのホストでは crypto.resolveTransportBodyLimits の requiredPlaintextBytes で統合します。明示上限の不足は起動/梱包時の失敗とし、上書きしません。プロキシ・adapter-node・ハンドシェイク・実行時の予算を一致させ、添付・展開・行数の制限は独立して検証します。
|
|
765
|
-
|
|
766
814
|
## 開発とテスト
|
|
767
815
|
|
|
768
816
|
```bash
|