コンテンツにスキップ

signage-aws-nodejs

Runtime(Server / Middleware / Utils)

サーバ起動と配線をまとめる層です。.env 読込→Express 初期化→HTTP サーバ化→Socket.IO 初期化→ルート登録→共通エラーハンドラ→起動、という流れで HTTP と WebSocket を束ねます

主な構成

  • server.js.envexpress.json() / bodyParser.json()http.createServer(app)initSocket(server)app.use('/', routes(io))共通エラーハンドラserver.listen(PORT)(既定 3000
  • MiddlewarevalidationResultexpress-validator の検証結果を 400 で集約)
  • UtilsextractFileNameFromUrl(URL からファイル名抽出。失敗時は 'unknown'。アップロード系で利用)

設計の要点

  • Socket 初期化は 1 回のみinitSocketgetIO で参照共有)
  • エラー伝播next(err)err.status || 500。タイムアウトは 504 相当で扱う方針を推奨
  • 環境変数PORT(任意)、OpenAI 利用時は OPENAI_API_KEY / OPENAI_MODEL / OPENAI_MAX_TOKENS

Tip

ルーティングは routes/index.js をハブに、Socket は initSocket()単一初期化へ集約すると見通しが良く保てます。

ルーティング(HTTP API)

routes/* は Express で組んだ HTTP エンドポイント群です。/api の下にドメイン別ルータをマウントし、Socket 層(getIO / deviceSockets / requests)と連携して 端末操作・状態取得・アップロードを行います。

主要ルート(概要)

  • /api/commands:再生・停止・回転・更新・音量(ACK ありの toggleVolume を含む)
  • /api/images/api/videos:一覧・サムネ取得(ACK:*_ListResponse / thumbnailResponse
  • /api/playlist:追加/挿入/移動/更新/削除/ファイルクリア(ACK:playlistUpdateResponse
  • /api/uploads/api/delete:画像/動画のアップロード・削除(ACK:upload*Response / delete*Response
  • /api/version:端末ソフトのバージョン問い合わせ(ACK:versionsResponse
  • /api/patchMigState:パッチ/マイグレーション状態取得(ACK:patchMigStateResponse
  • /api/deviceSettings:端末設定の取得/更新(ACK:configResponse / configUpdated
  • /api/ip/api/mac/api/status:IP/MAC 登録・接続状態
  • /api/device/power:シャットダウン/再起動
  • /api/device-info:デバイス情報の登録/リアルタイム取得
  • /api/random:ユーティリティ(例:ランダム部屋名)
  • /api/openai:OpenAI 応答の配信(io.emit

設計の要点

  • 相関:すべての往復で requestId を一致確認(誤解決防止)
  • タイムアウト:一覧/サムネ/バージョン等は 5s、削除系は 1s など用途別に設定
  • エラー方針:400 入力不足 / 404 未接続 / 500 内部異常(タイムアウトは 504 相当で扱うことを推奨)
  • 検証:routes/*/validators.jsexpress-validator / Joi により事前バリデーション

Tip

メディア系はネットワーク負荷が大きくなりがちです。maxHttpBufferSize やボディサイズ制限の調整、再送/冪等設計を検討してください。

ソケット層(双方向通信)

socket/* は端末(Jetson/Raspberry Pi)とサーバ間の リアルタイム通信を担います。
接続登録・切断検知、イベント配線、ACK 応答管理、HTTP→Socket ブリッジ(例:音量トグル)を提供します。

主なコンポーネント

  • index.jsinitSocket(server) で Socket.IO 初期化/汎用 ACK を配線(getIO() を公開)
  • deviceRegistry.jsregisterDevice / disconnectdeviceId ⇢ socketId を管理
  • playlistHandlers.jsimageListResponse / videoListResponse を共通ハンドラで解決
  • commonRequests.js:ACK 共通処理(handleListResponse / handleVersionResponse / handlePatchMigResponse
  • toggleVolume.js:HTTP 経由の音量トグルを発火し、volumeStatusChanged で応答待ち
  • requestStores.js:共有 Map ストア
    deviceSocketsdeviceId → socketId
    requests(汎用 ACK 待ち)
    thumbnailRequests(サムネ用)

設計の要点

  • 相関管理:requestId を往復で一致確認(誤解決防止)
  • リソース管理:resolve/reject 時に必ず clearTimeoutdelete
  • セキュリティ:本番では CORS を特定ドメインに制限origin: '*' は開発向け)

Tip

同種イベントを並列に投げる場合でも、requestId 単位の待機者分離で衝突を防げます。

Services(サービス層)

services/*HTTP ルート/コントローラSocket 層(端末) の橋渡しを担います。
ACK の要否に応じて 単発送信(emitCommand)ACK 往復(emitWithAck) を使い分け、requestId による相関で安全に同期します。

主なコンポーネント

  • Command(emitCommand):ACK なしの単発イベント送信(到達性保証不要な UI 操作向け)
  • DeviceSettingsService:設定の取得/更新(getConfig / updateConfigconfigResponse / configUpdated、既定タイムアウト ~1s)
  • PlaylistService:一覧・更新・削除・サムネ取得(updatePlaylist 系、ACK は playlistUpdateResponse、既定タイムアウト ~5s)
  • FileDownloadService:外部 URL を Buffer で取得(axiosarraybuffer 利用)
  • Socket Helper(emitWithAck):ACK 付きイベント送信の共通実装(requestId 照合・確実なリスナ解除・タイムアウト処理)

共通設計の要点

  • 接続解決:deviceSockets: Map<deviceId, socketId>getIO()(Socket.IO サーバ)を利用
  • 相関管理:ACK 往復では payload.requestIdres.requestId を一致確認
  • エラー方針:未接続/実体なしは 404 相当、タイムアウトは 504 相当(上位で HTTP にマッピング)

Tip

「ACK 不要(非同期 UI)」なら emitCommand
「整合性が重要(設定・同期)」なら emitWithAck を選択すると設計が安定します。

CI / GitHub Actions

このパッケージの CI は fmt / lint / test を回す基本ワークフローに加え、依存ライセンス検査、Release 公開時の バッジ更新 を行います。Node は 22 を使用し、PR/Push をトリガに 失敗早期化で品質を担保します。Release 時は jqcurl で Gist の release.json を更新し、Shields.io の endpoint バッジに反映させます(GH_PATgist スコープに限定)。

内訳

  • AWS Node.js CIfmtlinttestpush/pull_request 対象:main
  • License Checknpm run check:license(依存の法的健全性を継続監視)
  • Update Release Badge:Release 公開時に Gist のバッジ JSON を更新(非プレリリースのみ)

Tip

CI の再現性と速度を両立させるには、npm ci+キャッシュの併用が有効です。GH_PAT最小権限(gist)で保管してください。