Files
ai-code-app/etc/servers/work-server

Work Server

Fastify + Knex + PostgreSQL 기반의 범용 작업용 API 서버입니다.

추천 DB

  • PostgreSQL
  • 이유:
    • Node 생태계에서 검증된 조합
    • Knex로 CRUD와 DDL을 함께 다루기 편함
    • 운영/확장/마이그레이션 측면에서 무난함

실행

docker compose up -d
docker compose logs -f work-server

work-server는 HMR/watch 없이 빌드 산출물(dist)을 실행합니다. 컨테이너 재기동은 docker compose up -d --build --force-recreate --no-deps work-server 기준으로 최신 소스를 다시 빌드한 뒤 새 컨테이너를 띄웁니다.

호스트 프로젝트 루트와 동일한 문맥으로 서버 재기동을 처리하려면 별도 host runner를 사용합니다. 이 runner는 별도 명시적 요청이 있을 때만 수동으로 켜거나 재기동합니다.

cd /home/how2ice/project/ai-code-app
npm run server-command:runner

환경 변수

기본 실행은 .env.example 값으로도 가능합니다. 로컬 환경에 맞는 값을 덮어쓰려면 .env.example를 참고해서 .env를 추가하면 됩니다.

주요 항목:

  • APP_TIME_ZONE: Node 서버 런타임 기준 시간대. 기본값 Asia/Seoul
  • DB_TIME_ZONE: 앱이 여는 DB 세션 시간대. 기본값 Asia/Seoul
  • DB_*: PostgreSQL 접속 정보
  • PLAN_WORKER_ENABLED: Plan 자동화 worker 활성화 여부
  • PLAN_WORKER_INTERVAL_MS: Plan polling 주기
  • PLAN_GIT_REPO_PATH: 브랜치 생성/병합 대상 저장소 경로
  • PLAN_MAIN_PROJECT_REPO_PATH: main 반영 후 pull 받을 메인 루트 프로젝트 경로. 비우면 PLAN_GIT_REPO_PATH를 사용
  • PLAN_RELEASE_BRANCH: 자동 merge 대상 release 브랜치명
  • IOS_NOTIFICATION_ENABLED: iOS APNs 알림 활성화 여부
  • APNS_*: Apple Push Notification 인증 키 정보
  • SERVER_COMMAND_DOCKER_SOCKET: 서버 재기동 명령이 사용할 Docker Unix socket 경로. rootless Docker면 예: /run/user/1000/docker.sock
  • SERVER_COMMAND_API_BASE_URL: work-server가 서버 재기동 요청을 위임할 host runner 주소
  • SERVER_COMMAND_API_ACCESS_TOKEN: host runner 호출 토큰
  • SERVER_COMMAND_TEST_CHECK_URL, SERVER_COMMAND_REL_CHECK_URL, SERVER_COMMAND_PROD_CHECK_URL: 외부 공개 URL과 별개로 재기동 성공 판정에 사용할 내부 확인 URL. 비워 두면 각 SERVER_COMMAND_*_URL 값을 그대로 사용합니다.

서버 재기동 기능을 쓰려면 work-server 컨테이너가 Docker에 접근할 수 있어야 합니다. 기본값은 /var/run/docker.sock이며, rootless Docker 환경이면 .envSERVER_COMMAND_DOCKER_SOCKET 또는 DOCKER_HOST=unix:///run/user/<uid>/docker.sock를 맞춰 준 뒤 work-server를 다시 올려야 합니다.

기본 예시는 http://host.docker.internal:3211/api로 맞춰져 있어서, work-server 컨테이너가 아니라 호스트의 현재 프로젝트 루트에서 restart-*.sh를 실행합니다. 즉 Server > Command가 직접 CLI로 재기동한 것과 최대한 비슷한 문맥을 사용합니다.

Codex Live

Codex Live는 현재 프로젝트 환경에서 PLAN_MAIN_PROJECT_REPO_PATH로 지정된 main_project 저장소 루트를 기준으로 실행됩니다. 문서 예시와 기본 컨테이너 값은 PLAN_MAIN_PROJECT_REPO_PATH=/workspace/main-project이지만, 이것은 환경 변수 기본 예시일 뿐이며 실제 실행 루트는 현재 마운트된 프로젝트 경로를 따릅니다.

소스 수정이 필요하면 현재 실행 환경에서 실제로 연결된 main_project 저장소 루트의 AGENTS.md 규칙을 먼저 확인한 뒤, 그 작업 트리에서 바로 수정합니다.

현재 운영 기준에서는 Codex Live, 일반 채팅, 일반 작업메모 반영 요청은 현재 프로젝트 루트의 로컬 작업본을 직접 기준으로 처리합니다.

Codex LivePlan 자동화는 별개입니다. Codex Live는 채팅 유형 context를 최우선 기준으로 읽고, 채팅방 공통 문맥과 전용 메모는 충돌하지 않는 범위의 보조 문맥으로만 사용합니다. 현재 화면 및 최근 대화 문맥도 그 아래 보조 문맥으로 사용합니다. 자동화 유형 context는 기본 문맥으로 섞지 않습니다.

브라우저 기준 화면 테스트, 소스 변경 검증, 최종 화면 확인은 https://preview.sm-home.cloud/ 기준으로 진행합니다. https://test.sm-home.cloud/는 운영 비교나 프록시 점검이 꼭 필요할 때만 보조적으로 확인합니다. 별도 요청이 없는 한 sm-home.cloudrel.sm-home.cloud는 기본 검증 대상으로 삼지 않습니다.

채팅에서 파일, 문서, 이미지, 코드 같은 리소스를 제공할 때의 기본 공개 경로는 public/.codex_chat/<chat-session-id>/resource/...입니다. Codex가 원본 파일 경로만 답해도 서버가 이 위치로 세션 전용 사본을 만들고, 채팅에는 공개 URL을 다시 적어 줍니다.

채팅 첨부 파일도 같은 기준을 사용하며 public/.codex_chat/<chat-session-id>/resource/uploads/... 아래에 저장됩니다.

Plan 자동화

Plan 게시판 항목을 작업 큐처럼 읽어 자동화할 수 있습니다.

현재 운영 기준에서 자동화 작업메모의 세부 Git 절차는 이 문서에 고정하지 않습니다. 상태 전이와 실제 처리 흐름은 worker 구현, 환경 변수, 현재 운영 정책을 함께 확인해야 합니다.

자동화 실행기는 선택된 자동화 유형의 description/context만 우선 참조합니다. Codex Live나 일반 채팅 문맥은 자동화 기본 context로 사용하지 않습니다.

안전 조건:

  • Git worktree가 깨끗해야 동작
  • release 브랜치가 실제로 존재해야 병합 가능
  • 실패 시 자동으로 이슈 상태와 오류 메시지를 남김

주요 API

  • GET /health
  • GET /api/schema/tables
  • POST /api/ddl/create-table
  • POST /api/ddl/drop-table
  • POST /api/ddl/add-column
  • POST /api/ddl/drop-column
  • POST /api/ddl/raw
  • POST /api/crud/:table/select
  • POST /api/crud/:table/insert
  • PATCH /api/crud/:table/update
  • DELETE /api/crud/:table/delete
  • GET /api/plan/statuses
  • POST /api/plan/setup
  • GET /api/plan/items
  • GET /api/plan/items/:id
  • POST /api/plan/items
  • PATCH /api/plan/items/:id
  • DELETE /api/plan/items/:id
  • POST /api/notifications/setup
  • GET /api/notifications/tokens
  • GET /api/notifications/subscriptions/web
  • PUT /api/notifications/tokens/ios
  • DELETE /api/notifications/tokens/ios
  • POST /api/notifications/send-test

iOS 알림 연동

  • 프론트에서 알림 OnPUT /api/notifications/tokens/ios로 APNs 토큰을 등록합니다.
  • 프론트에서 알림 Off 시 또는 토큰이 폐기되면 DELETE /api/notifications/tokens/ios로 토큰을 제거합니다.
  • Plan worker가 브랜치 준비, 자동 작업 완료/실패, release 반영 완료/실패, main 반영 완료/실패 시 등록된 iOS 토큰으로 APNs 알림을 전송합니다.

웹푸쉬 호출 메모

  • POST /api/notifications/sendtitle, body, data, threadId 외에 targetClientIds도 받을 수 있습니다.
  • targetClientIds를 넣으면 web_push_subscriptions.device_id 또는 PWA iOS 토큰의 device_id가 일치하는 클라이언트에게만 알림을 보냅니다.
  • 웹푸시 구독과 PWA iOS 토큰 등록 시 서버는 appOrigin, appDomain도 함께 저장합니다.
  • POST /api/notifications/sendtargetAppOrigins, targetAppDomains를 넣으면 해당 앱 도메인/오리진으로 등록된 구독에만 발송할 수 있습니다.
  • GET /api/notifications/subscriptions/web로 현재 저장된 웹푸시 구독의 deviceId, appOrigin, appDomain, enabled 상태를 확인할 수 있습니다.
  • 같은 알림을 교체하려면 DB 삭제 대신 data.notificationKey 또는 threadId를 고정값으로 보내세요. 서비스워커가 이 값을 브라우저 알림 tag로 사용해 이전 알림을 대체합니다.