변경 이력(감사)
오퍼레이터가 Operator API 로 바꾼 설정은 모두 플랫폼 감사 기록에 남습니다. 이 API 로 자기 조작 기록을 조회하고, 조작 전 값으로 되돌릴(복구) 수 있습니다.
| 메서드 | 경로 | 용도 |
|---|---|---|
| GET | /operator/v1/audit | 감사 기록 조회 |
| POST | /operator/v1/audit/{audit_id}/restore | 조작 하나를 조작 전 값으로 되돌리기 |
감사 기록이 남는 방식
POST /launch를 뺀 쓰기 요청(POST · PUT · PATCH · DELETE)은 응답 헤더X-Audit-Id: <정수>로 감사 기록 번호를 돌려줍니다. 관리 화면에서 조작 로그를 따로 남긴다면 이 번호를 함께 저장해 두세요. 나중에GET /audit?ids=로 상태를 보거나 복구할 때 씁니다.- 기록은 요청이 성공(2xx)했을 때만 저장됩니다. 실패한 요청에도
X-Audit-Id헤더가 실릴 수 있지만 그 번호의 기록은 없습니다. - 설정을 바꾸는 요청은 바뀌기 전 · 뒤 값(스냅숏)이 기록에 붙습니다. 스냅숏이 있는 기록만 되돌릴 수 있습니다.
- 요청에
X-Operator-Admin: <관리자 아이디>헤더(^[A-Za-z0-9_.@-]{1,40}$, 서명 대상 아님)를 넣으면 조작자가operator:<오퍼레이터 코드>/<관리자 아이디>로 기록됩니다. 없으면operator:<오퍼레이터 코드>입니다. 이 값은 누가 눌렀는지 적는 표시일 뿐 권한과는 무관합니다. - 오퍼레이터는 자기 오퍼레이터 코드로 남은 기록만 보고 되돌릴 수 있습니다. 본사가 바꾼 설정은 이 API 에 보이지 않습니다.
되돌릴 수 있는 조작
| 조작 | 되돌리면 |
|---|---|
PUT /games/{game_code}/bet-limits · PUT /games/{game_code}/lobby | 게임 베팅 한도 · 로비 표시 설정 |
PATCH /tables/{table_id} · POST /tables/{table_id}/close · PUT /tables/{table_id}/{settings, live, timers, rake-rule, card-design, currency} | 전용 테이블 설정 (닫은 테이블 다시 열기 포함) |
PUT /settings · PUT /card-design | 오퍼레이터 설정 |
PUT /rake-defaults | 수수료 기본값과, apply_all · rake_floor 로 함께 바뀐 전용 테이블들 |
POST /players/{external_id}/logout | 플레이어 차단 상태(block)만. 끊긴 세션은 되살아나지 않습니다 |
PATCH /dealers/{dealer_id} · PUT /dealers/{dealer_id}/photo · DELETE /dealers/{dealer_id}/photo | 딜러 이름 · 역할 · 사용 여부 · 사진 (PIN 은 되돌리지 않음) |
DELETE /private-tables/{private_id} | 프라이빗 예약 취소 |
복구(audit.restore) 자체 | 복구를 취소 — 복구 직전 값으로 |
만들기 요청(POST /tables · POST /dealers · POST /private-tables), 사원증 발급 · 회수, 로비 배너 그림 올리기는 기록만 남고 되돌릴 수 없습니다(restorable: false).
GET /operator/v1/audit
감사 기록을 최신순(id 내림차순)으로 조회합니다. 두 가지 방식이 있습니다.
쿼리 파라미터
방식 1 — 번호로 조회
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
ids | string | 예 | 쉼표로 구분한 감사 기록 번호, 최대 500개 (예: ids=1204,1207). 숫자가 아닌 값은 건너뜁니다 |
ids 를 주면 아래 필터 · 페이지 파라미터는 무시하고, 그중 자기 기록만 돌려줍니다(has_more 는 항상 false).
방식 2 — 필터로 조회
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
action | string | 아니오 | 조작 이름 앞부분 일치 (예: table. 이면 table.settings, table.close …) |
target | string | 아니오 | 대상 정확히 일치 (예: tbl_…, 딜러 ID, 오퍼레이터 코드) |
restorable | 1 | 아니오 | 스냅숏이 붙은 기록만 |
limit | 정수 | 아니오 | 페이지 크기. 기본 100, 최대 500 |
page | 정수 | 아니오 | 1부터 시작하는 페이지 번호 (기본 1) |
요청 예시
GET /operator/v1/audit?action=table.&restorable=1&limit=50&page=1 HTTP/1.1
Host: {API_HOST}
X-Operator: acme
X-Api-Key: ak_live_…
X-Timestamp: 1760839200123
X-Nonce: 8d0f2b4a6c8e0d2f4b6a8c0e
X-Signature: 5c1d…응답 예시
{
"entries": [
{
"id": 1207,
"admin": "operator:acme/jane",
"action": "table.settings",
"target": "tbl_0198c1d2e3f47a5b8c9d0e1f2a3b4c5d",
"detail": { "currency": "PHP", "active": true, "before": { "…": "…" }, "after": { "…": "…" } },
"ip": "198.51.100.7",
"created_at": "2026-10-19T03:40:12.88Z",
"changes": [
{
"kind": "table",
"key": "0198c1d2-e3f4-7a5b-8c9d-0e1f2a3b4c5d",
"op": "update",
"fields": {
"limits.sb": { "before": 100, "after": 200 },
"limits.bb": { "before": 200, "after": 400 }
}
}
],
"restore_of": null,
"restored_by": null,
"restorable": true
},
{
"id": 1204,
"admin": "operator:acme/jane",
"action": "operator.watch_limit",
"target": "acme",
"detail": { "before": 0, "after": 600 },
"ip": "198.51.100.7",
"created_at": "2026-10-19T03:31:02.4Z",
"changes": [
{ "kind": "operator", "key": "0198a0b1-c2d3-7e4f-8a5b-6c7d8e9f0a1b", "op": "update", "fields": { "watch_limit_sec": { "before": 0, "after": 600 } } }
],
"restore_of": null,
"restored_by": 1210,
"restorable": true
}
],
"has_more": false
}응답 필드
| 필드 | 타입 | 설명 |
|---|---|---|
entries[].id | int64 | 감사 기록 번호 (= 그 요청의 X-Audit-Id) |
entries[].admin | string | 조작자 — operator:<코드> 또는 operator:<코드>/<관리자 아이디> |
entries[].action | string | 조작 이름 (아래 표) |
entries[].target | string | 대상 (테이블 ID · 딜러 ID · 게임 코드 · 플레이어 external_id · 오퍼레이터 코드 등) |
entries[].detail | object | 조작 내용 (조작마다 모양이 다름, 보통 before · after) |
entries[].ip | string | null | 요청한 서버 IP |
entries[].created_at | 시각 | 기록 시각 |
entries[].changes | array | 바뀐 설정 요약 (스냅숏이 있을 때만) — 아래 표 |
entries[].restore_of | int64 | null | 이 기록이 복구라면 되돌린 원래 기록 번호 |
entries[].restored_by | int64 | null | 이 기록을 되돌린 복구 기록 번호 (되돌리지 않았으면 null) |
entries[].restorable | bool | 되돌릴 수 있는 기록인지 |
has_more | bool | 다음 페이지가 있는지 (page 를 1 늘려 조회) |
changes[]
| 필드 | 설명 |
|---|---|
kind | 바뀐 대상 종류 — operator · operator_game · table · dealer · dealer_photo · player · private_reservation |
key | 대상 행의 내부 키 |
op | update · create · delete |
fields | 바뀐 항목 → { before, after }. JSON 설정은 바뀐 칸만 점 경로(예: config.rake.rake_floor)로 펼칩니다. 비밀번호 같은 값은 가려서 보여 주고, 사진 바이트처럼 큰 값은 줄여 보여 줍니다 |
changes 와 detail 은 화면 표시용 요약입니다. 항목 이름은 플랫폼 내부 저장 이름이라 바뀔 수 있으니, 프로그램 판단에는 action · target · restorable 을 쓰세요.
조작 이름(action)
| 범위 | 이름 |
|---|---|
| 게임 | game.bet_limits · game.lobby |
| 테이블 | table.create · table.update · table.close · table.settings · table.currency · table.live · table.timers · table.rake_rule · table.card_design · table.private · table.private_cancel |
| 오퍼레이터 | operator.timezone · operator.multi_table · operator.watch_limit · operator.rake_defaults · operator.card_design |
| 플레이어 | player.logout |
| 딜러 | dealer.create · dealer.update · dealer.badge_issue · dealer.badge_reissue · dealer.badge_revoke · dealer.photo · dealer.photo_delete |
| 복구 | audit.restore |
| 그 밖 | api <METHOD> <경로 패턴> — 전용 이름이 없는 쓰기 요청 (예: api PUT /operator/v1/lobby-banners) |
한 요청이 여러 설정을 바꾸면(예: PUT /settings 로 시간대와 중복 게임을 함께) 기록이 여러 줄 남을 수 있습니다. 이때 스냅숏과 X-Audit-Id 는 첫 번째 기록에 붙습니다.
오류
| HTTP | 코드 | 상황 |
|---|---|---|
| 500 | INTERNAL_ERROR | 일시적인 서버 오류 |
POST /operator/v1/audit/{audit_id}/restore
감사 기록 하나의 조작을 조작 전 값으로 되돌립니다. 되돌린 결과도 새 감사 기록(audit.restore)으로 남고, 원래 기록의 restored_by 에 그 번호가 적힙니다. 복구 기록을 다시 복구하면 복구가 취소됩니다.
운영 중인 테이블 · 딜러툴 · 입장 설정에도 바로 반영됩니다.
경로 · 요청 필드
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
audit_id | int (경로) | 예 | 되돌릴 감사 기록 번호 |
force | bool (본문) | 아니오 | 그 조작 뒤에 같은 설정이 다시 바뀌었어도 덮어쓰고 되돌림. 기본 false |
본문은 생략할 수 있습니다(빈 본문 또는 {} 로 서명하면 force: false).
충돌 처리
되돌리기 전에 지금 값이 "그 조작 직후 값"과 같은지 확인합니다. 그 뒤에 누군가 같은 설정을 또 바꿨다면 아무것도 바꾸지 않고 409 RESTORE_CONFLICT 와 충돌 목록을 돌려줍니다. 이때는
- 나중 조작들을 최신 것부터 먼저 되돌리거나,
- 나중 변경을 무시하고 덮어쓰려면
force: true로 다시 요청합니다.
요청 예시
curl -X POST {API_BASE}/operator/v1/audit/1207/restore \
-H 'Content-Type: application/json; charset=utf-8' \
-H 'X-Operator: acme' -H 'X-Api-Key: ak_live_…' -H 'X-Operator-Admin: jane' \
-H 'X-Timestamp: 1760839200123' -H 'X-Nonce: 2f4b6a8c0e2d4f6b8a0c2e4d' -H 'X-Signature: b83a…' \
-d '{}'응답 예시
200 OK (응답 헤더 X-Audit-Id 도 복구 기록 번호)
{
"ok": true,
"audit_id": 1211,
"changes": [
{
"kind": "table",
"key": "0198c1d2-e3f4-7a5b-8c9d-0e1f2a3b4c5d",
"op": "update",
"fields": {
"limits.sb": { "before": 200, "after": 100 },
"limits.bb": { "before": 400, "after": 200 }
}
}
]
}| 필드 | 타입 | 설명 |
|---|---|---|
ok | bool | 항상 true |
audit_id | int64 | 새로 남은 복구 기록 번호 |
changes | array | 복구가 실제로 바꾼 설정 요약 (모양은 목록의 changes 와 같음) |
오류
| HTTP | 코드 | details | 상황 |
|---|---|---|---|
| 404 | NOT_FOUND | — | 형식이 틀린 번호, 없는 기록, 다른 오퍼레이터 · 본사의 기록 |
| 403 | NOT_RESTORABLE | — | 오퍼레이터 권한으로 되돌릴 수 없는 종류의 변경이 섞여 있음 |
| 409 | NOT_RESTORABLE | — | 되돌릴 스냅숏이 없음(만들기 요청 · 사원증 등) 또는 이 환경에서 복구 기능이 꺼져 있음 |
| 409 | RESTORE_BLOCKED | { "kind": "table", "key": "…" } | 지금 상태에서는 되돌릴 수 없음 — 예: 플레이어가 앉아 있는 테이블의 통화 되돌리기. message 에 이유가 담깁니다. 상황이 바뀐 뒤 다시 시도하세요 |
| 409 | RESTORE_CONFLICT | { "conflicts": [ { "kind": "table", "key": "…" } ] } | 그 조작 뒤에 같은 설정이 다시 바뀜 — 위 충돌 처리 |
| 500 | INTERNAL_ERROR | — | 복구 중 서버 오류 |
RESTORE_CONFLICT 응답 예:
{
"error": {
"code": "RESTORE_CONFLICT",
"message": "changed again after this action — restore the later changes first, or force",
"details": { "conflicts": [ { "kind": "table", "key": "0198c1d2-e3f4-7a5b-8c9d-0e1f2a3b4c5d" } ] }
}
}